Android 15 之如何快速适配 16K Page Size
在此之前,我们通过 《Android 15 上 16K Page Size 为什么是最坑》 介绍了:
- 什么是16K Page Size
- 为什么它对于 Android 很坑
- 如何测试
如果你还没了解,建议先去了解下前文,然后本篇主要是提供适配的思路,因为这类适配更多工作量在于实际的执行调整和编译跟踪,而非功能上的适配,本质不复杂,但可能量大且繁琐。
是的,想要适配你首选需要有 C/C++ 等 so 库的源码。
适配的开始,我们可以先粗暴的全局搜索 「4096」关键字,看看是否有将 4096 搭配 mmap 、sysconf 等相关的地方,因为 Android 上的 4K 这么多年已经「深入人心」,可以说不少代码都将 Android 默认为「4096」。


对于这些地方,我们可以通过类似 getpagesize() 等方式来进行调整,例如通过 ALOGV("####### %d", getpagesize()); 可以看到在 Android15 上输出的是 16384 。

接着我们可以运行项目到 Android 15 的模拟器上(如果运行不起来看前文),看 so 是否能正常被加载执行,一般情况下你可能会看到类似:
java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH ···· (new hash type from the future?)

这类问题基本都是 so 文件没有 16K 编译对齐的原因,此时,根据你的 CMakeList 版本,可以使用 target_link_options 或者 target_link_libraries 进行调整。

例如 3.13 之前:
target_link_libraries(a4ijkplayer "-Wl,-z,max-page-size=16384")
例如 3.13 之后:
target_link_options(a4ijkplayer PRIVATE "-Wl,-z,max-page-size=16384")
注意 CMakeList 的版本还需要 SDK Manager 里有下载安装。
如果是 Android.mk , 则添加 LOCAL_LDFLAGS 也可以配置 16K Page :
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384
编译后,我们通过 readelf 工具,可以对比编译前后两个 so 的 elf 对齐情况,工具一般位于 /Users/guoshuyu/Library/Android/sdk/ndk/21.4.7075529/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin,通过以下命令可以输出对应参数:
./aarch64-linux-android-readelf -l /Users/guoshuyu/workspace/android/******/libs/arm64-v8a/libijkffmpeg.so
如下两种图所示:
- 图 1 LOAD 段在 Align 栏目显示 1000 (16进制,即 4096) ,也就是还没增加 16K 对齐的状态
- 图 2 LOAD 段在 Align 栏目显示 4000 (16进制,即 16384) ,也就是修改编译后,已经增加 16K 对齐的状态


这里我们只关心 LOAD 段,因为一般只有 LOAD 段需要加载到内存中。
所以简单情况下,你也可以通过 readelf 工具查看 so 的对齐状态。
另外,如果你在低版本 NDK (低于 r27) 上使用动态链接到 C++ 标准库,那么可以也会遇到 1ibc++_shared.so 的相关报错,解决的思路还是:
- 升级到 NDK r27
- 将 C++ 标准库静态链接到 so 库里面,例如
-DANDROID_STL=c++_static
externalNativeBuild {cmake {abiFilters 'armeabi-v7a', 'arm64-v8a'arguments '-DANDROID_ARM_NEON=TRUE', '-DANDROID_STL=c++_static'}}

之后,如果 so 运行过程中还存在异常问题, 可以通过地址信息来进行代码定位,例如使用常见的:addr2line 。

而 addr2line 的可执行文件位置,一般位于以下位置:
/Users/guoshuyu/Library/Android/sdk/ndk/21.4.7075529/toolchains/aarch64-linux-android-4.9/prebuilt/darwin-x86_64/bin
注意 ndkVersion 的版本还需要 SDK Manager 里有下载安装。
其中 ndk 版本为项目 build.gradle 下配置的 ndk 版本,例如 ndkVersion '21.4.7075529' ,之后你只需要通过执行命令输出,即可看到出现问题的文件和行数:
./aarch64-linux-android-addr2line -e /Users/guoshuyu/workspace/android/··········/build/intermediates/merged_native_libs/debug/out/lib/arm64-v8a/????.so 000000000007e0a0

当然,也可以增加 -f,通过 ./aarch64-linux-android-addr2line -f -e 来查看出问题的函数是什么:

例如,对于 C++ 里静态存储区域分配,内存在程序编译的时候就已经分配好,而 static 这块内存变量上,如果 so 对齐和地址存在问题,也可能会导致访问地址异常。
如果上述在使用 addr2line 输出时看到都是 ?? ,那么你可能需要修改下编译的调试支持和优化级别,例如 Application.mk 上降低优化等级为 APP_CFLAGS := -O0。
当然,很多时候存在许多「玄学」的问题,例如前段时间就遇到了一个神奇的 Bug ,甚至也不知道为什么,但是又能改好它。
举个不严谨的例子,通过地址定位,可以定位到报错的地方是这个 static 的全局变量报错,但是为什么会出现 Fatal signal 11(SIGSEGV),code 2(SEGV_ACCERR) 让人费解。


如果按照正常场景来看,C 和 C++ 中的静态变量通常存储在 “data” 区域,但是对于任何未初始化或初始化为零的全局数据,都是存放在 “bss” :

正常情况下,bss 段属于静态内存分配,通常是用来存放未初始化的全局变量和未初始化的局部静态变量,所以 bss 段只是给未初始化的全局变量和未初始化的局部静态变量预留位置而已。
我们可以通过前面的 readelf 工具看一下,如下命令所示 ,通过 -s | grep _errMsg 输出,我们可以看到 _errMsg 确实存在 [23] 的 bss 的 NOBITS 位置。
./aarch64-linux-android-readelf -S /Users/guoshuyu/workspace/*****.so -s | grep _errMsg


但是从输出看,貌似也没什么问题,如果从另外的 .text 和 .rodata 的 Address 上看, 165670 - 071fa0 = F36D0 ,也正好是 4000 的 3C 倍数 ,所以分页应该也没问题。

而通过 objdump 输出的结果看,也和没看出异常,所以问题就变得很玄学,根据猜测,可能是以下某个之一的问题:
./aarch64-linux-android-objdump -h /Users/guoshuyu/workspace/android/****.so
- 某个过程编译的对齐有问题
- NDK 的 gcc 优化存在一些奇怪问题, 例如 gcc 不仅观察变量如何定义,还观察了变量如何在代码中使用
- 赋值的寻址有问题没对齐
- 模拟器系统问题
在偶然之下,屏蔽掉某个使用
_errMsg的 static function 的static声明后,它居然就正常运行了,就是偶尔删掉了某个函数的 static 声明,它突然就好了···所以我也不知道是编译的问题还是系统的问题,也许后面有空再深入研究下,不过在适配过程中,真的是「运气好」的情况下,你只需要改一下配置,编译完就可以立即使用,运气不好的情况下,真的就很「玄学」。
最后,如果你需要升级到 NDK r27,你可能还需要对代码的编排方式做一些调整,例如类似 ISO C99 and later do not support implicit function declarations 等小细节问题,因为 NDK r27 目前是 clang-r522817 的版本:


根据 r27 目前的 clang_source_info.md ,其 clang 应该会是 18.1.0 ,也就是包含 C++ 20/23/2C 等的支持,如果升级跨度较大,其实可能会是改了旧坑来新坑的节奏。
例如腾讯目前的 MMKV,在 issue#1353 就提到了兼容的评估问题,虽然对于使用者来说,可能就是几个简单配置就可以重新编译支持,但是对于平台方来说,升级环境是一个“高风险”行为,所以还需要谨慎而行。
最后,这个适配的基础还是你有源码,如果你连源码都没有,那么基本就没希望了,对于 android 环境下,基于 linux 的 4k/16k 内核是不支持混用(详细原因见前文),所以如果你没做适配,很大可能 so 是无法正常运行,不过好消息是,OEM 厂商可以不启用,坏消息是,Google 表示明年在 Google Play 上会强制要求。

相关文章:
Android 15 之如何快速适配 16K Page Size
在此之前,我们通过 《Android 15 上 16K Page Size 为什么是最坑》 介绍了: 什么是16K Page Size为什么它对于 Android 很坑如何测试 如果你还没了解,建议先去了解下前文,然后本篇主要是提供适配的思路,因为这类适配…...
学习unity官方的网络插件Netcode【一】
对bool值的个人理解: using Unity.Netcode; using UnityEngine; //个人理解:通过Rpc完成了一次客户端给服务端发消息,服务端再向所有客户端广播消息 public class RpcTest : NetworkBehaviour {public override void OnNetworkSpawn(){if (!…...
QT写一个mainWindow
切换风格的写法: 先看看样式效果: mian_window.h文件 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>class MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent nullptr);~MainWindow();void Ini…...
Java查找算法练习(2024.7.23)
顺序查找 package SearchExercise20240723; import java.util.Scanner; public class SearchExercise {public static void main(String[] args) {Scanner sc new Scanner(System.in);System.out.println("需要多大的数组?");int size sc.nextInt();int[] array …...
洗地机哪个牌子好?四款口碑最好的洗地机排名推荐
随着“懒人经济”的出现,越来越多的人开始使用洗地机。洗地机哪个牌子好?为了帮助大家在这个琳琅满目的市场中做出明智决策,本文特别整理了四款口碑最好的洗地机排名推荐,它们凭借出色的清洁效果、智能化的操作体验以及用户的高度…...
如何提升短视频的曝光量和获客效能?云微客来解决
在流量至上的当下,短视频凭借其优势,迅速成为了众多企业获客引流的核心营销手段。进入短视频赛道后,如何提升短视频的曝光量和获客效能,就成为了众多企业亟待解决的焦点。 如果你不想投入大量的广告预算,还想在短视频平…...
SpringBoot开发中如何缓存数据, 减少数据库的访问频率?
一:自定义是否开启缓存 方法一: 在不同环境的配置文件中如application-dev.yml、application-test.yml、application-prod.yml,修改 spring.cache.type none; spring:cache:type: none 方法二: 自定义配置 application.yml&…...
PostgreSQL如何在windows/linux开启归档
linux开启归档: archive_mode onarchive_command test ! -f /mnt/pg12/archivedir/%f && cp %p /mnt/pg12/archivedir/%fwindows开启归档: archive_mode onarchive_command copy "%p" "C:\\server\\pg12\\archivedir\\%f&q…...
【启明智显分享】基于国产Model3芯片的7寸触摸屏助力智慧医疗,电子床头屏提升护理交互
未来医院必然是以信息化为基础,以物联网为特征,以医疗为核心的服务型医院。病房作为医院的重要服务场所,成为智慧医院建设的重要一环。 为提高医护人员与患者的互动交流,给医疗注入智慧元素,让患者享受智能服务&#…...
从理论到实践:如何用 TDengine 打造完美数据模型
在用 TDengine 进行数据建模之前,我们需要回答两个关键问题:建模的目标用户是谁?他们的具体需求是什么?在一个典型的时序数据管理方案中,数据采集和数据应用是两个主要环节。如下图所示: 对于数据采集工程师…...
可以免费合并pdf的软件 合并pdf文件的软件免费 合并pdf的软件免费
在数字化办公的今天,pdf格式因其稳定性和跨平台兼容性被广泛使用。然而,当我们需要将多个 pdf 文件合并为一个时,却往往感到力不从心。本文将为你介绍几款强大的pdf文件合并软件,让你轻松管理文档。 方法一、使用pdf转换器 步骤1…...
【排序 滑动窗口 】1498. 满足条件的子序列数目
本文涉及至知识点 排序 C算法:滑动窗口总结 LeetCode1498. 满足条件的子序列数目 给你一个整数数组 nums 和一个整数 target 。 请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。 由于答案可能很大,…...
RabbitMQ普通集群搭建指南
RabbitMQ普通集群搭建指南 本文已经完全迁移至,www.geekery.cn 后续不在此更新 目标架构 本次搭建的目标是构建一个由三个节点组成的RabbitMQ集群,节点信息如下: rabbit02: IP地址 192.168.10.132rabbit03: IP地址 192.168.10.133rabbit04:…...
AGV平面坐标系变换公式及实例
1、AGV坐标系简介 如上图,小车前后对角是有激光雷达的,其坐标系称为激光坐标系,采用极坐标系体现。中间为车体坐标系,激光坐标系相对于车体坐标系关系不变;左下角是地图坐标系,小车扫图后,建立的…...
es切片和集群
解决单点故障 支持高并发 解决海量数据 1.cluster 集群:包含多个节点,每个节点属于哪个集群是通过一个集群名称(集群名称,默认是elasticsearch)来决定的,对于中小型应用来说,刚开始一个集群就…...
IEEE官方列表会议 | 第三届能源与环境工程国际会议(CFEEE 2024)
会议简介 Brief Introduction 2024年第三届能源与环境工程国际会议(CFEEE 2024) 会议时间:2024年12月2日-4日 召开地点:澳大利亚凯恩斯 大会官网:CFEEE 2024-2024 International Conference on Frontiers of Energy and Environment Engineer…...
深度学习中的正则化技术 - Dropout篇
序言 在深度学习的浩瀚领域中,模型过拟合一直是研究者们面临的挑战之一。当模型在训练集上表现得近乎完美,却难以在未见过的数据(测试集)上保持同样优异的性能时,过拟合现象便悄然发生。为了有效缓解这一问题…...
《昇思 25 天学习打卡营第 18 天 | 扩散模型(Diffusion Models) 》
《昇思 25 天学习打卡营第 18 天 | 扩散模型(Diffusion Models) 》 活动地址:https://xihe.mindspore.cn/events/mindspore-training-camp 签名:Sam9029 扩散模型(Diffusion Models) 扩散模型概述 扩散模…...
【Django+Vue3 线上教育平台项目实战】Elasticsearch实战指南:从基础到构建课程搜索与数据同步接口
文章目录 前言一、Elasticsearch倒排索引 二、Docker 搭建 ESDocker 安装Docker 搭建 ES 三、ES基础语法创建索引查看索引删除索引添加数据查询数据修改数据删除数据条件查询分页查询排序 多条件查询andor 范围查询 四、ES在项目中的应用示例 前言 在数据驱动的时代,…...
libtins初探-抓包嗅探
libtin 一、概述1. 可移植性2. 特性 二、基础知识1. PDU2. 地址类3. 地址范围类4. 网络接口5. 写pcap文件 三、嗅探1.嗅探基础2. 嗅探器配置3. 循环嗅探4. 使用迭代器嗅探6. 包对象7. 读取pcap文件8. 包的解析 四、发送包1. 发送网络层pdu2. 发送链路层pdu3. 发送和接收响应校验…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
【深尚想】TPS54618CQRTERQ1汽车级同步降压转换器电源芯片全面解析
1. 元器件定义与技术特点 TPS54618CQRTERQ1 是德州仪器(TI)推出的一款 汽车级同步降压转换器(DC-DC开关稳压器),属于高性能电源管理芯片。核心特性包括: 输入电压范围:2.95V–6V,输…...
