Android 15 之如何快速适配 16K Page Size

在此之前,我们通过 《Android 15 上 16K Page Size 为什么是最坑》 介绍了:

  • 什么是16K Page Size
  • 为什么它对于 Android 很坑
  • 如何测试

如果你还没了解,建议先去了解下前文,然后本篇主要是提供适配的思路,因为这类适配更多工作量在于实际的执行调整和编译跟踪,而非功能上的适配,本质不复杂,但可能量大且繁琐

是的,想要适配你首选需要有 C/C++ 等 so 库的源码。

适配的开始,我们可以先粗暴的全局搜索 「4096」关键字,看看是否有将 4096 搭配 mmapsysconf 等相关的地方,因为 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 上会强制要求

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/49199.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Facebook Dating:社交平台的约会新体验

随着社交媒体的普及和技术的发展,传统的社交方式正在经历革新,尤其是在约会这个领域。Facebook作为全球领先的社交平台,推出了Facebook Dating,旨在为用户提供一个全新的约会体验。本文将探讨Facebook Dating如何重新定义社交平台…

43 华三AC登录Web页面

一 无线上WEB页面 1 创建vlan 56 [AC-KongZhi]vlan 56 2 退出 [AC-KongZhi-vlan56]quit 3 进入vlan三层口 配置IP地址 [AC-KongZhi]interface Vlan-interface 56 [AC-KongZhi-Vlan-interface56]ip address 192.168.56.55 24 4 在AC控制器与Host主机的接口上能通关vlan 5…

高等数学重难点突破:高阶导数的计算

写在最前 文章目录 写在最前方法一:找规律方法二:牛顿莱布尼茨公式方法三:泰勒公式方法四: 数学归纳法 本文重点讨论总结面对高阶导数,我们可以使用哪些方法(工具)来解决计算高阶导数问题 方法概述&#xf…

【入门教程一】基于DE2-115的My First FPGA 工程

1.1. 概述 这是一个简单的练习, 可以帮助初学者开始了解如何使用Intel Quartus 软件进行 FPGA 开发。 在本章节中,您将学习如何编译 Verilog 代码,进行引脚分配,创建时序约束,然后对 FPGA 进行编程,驱动开…

【Redis】主从复制分析-基础

1 主从节点运行数据的存储 在主从复制中, 对于主节点, 从节点就是自身的一个客户端, 所以和普通的客户端一样, 会被组织为一个 client 的结构体。 typedef struct client {// 省略 } client;同时无论是从节点, 还是主节点, 在运行中的数据都存放在一个 redisServer 的结构体中…

求职学习day8

7/21回顾: 用面试鸭的意义可能就在于将知识点用问答的形式具象化在脑海,不然可能只停留在听说过的感觉 7.21 玩了一天。一个很不好的信号。今天下午要试试把 mall 项目的代码运行过一遍。 项目运行问题: 问题 1 :两个门服务器…

有序充电在新型电力系统下的解决方案

摘要:近年来,新能源汽车的销量快速增长,相应的充电桩数量也急剧增加,这一现象可能会给电网和变压器造成负担,与此同时,新型电力系统下以光伏为主的分布式发电系统占比也在逐渐提高,新能源的不稳定性叠加充电需求的不确定性会给电网带来严峻的…

Unity发布XR中用于worldbuilding的全新电子书

通过身临其境的虚拟领域开始旅程,在维度之间传送,或将数字奇迹与现实世界融合——虚拟现实(VR)和混合现实(MR)的千万种可能性将邀请创作者把他们的想象力带入生活。 Unity发布的最新版综合指南将帮助有抱负的创作者和经验丰富的开发者深入研究和理解构建…

洛谷看不了别人主页怎么办

首先,我们先点进去 可以看到,看不了一点 那我们看向上方,就可以发现,我们那有个URL,选中 把光标插到n和/中间 把.cn删了,变成国际服 我们就可以看了 但是国际服还没搭建完,跳转的时候可能503&a…

融云 2024 ChinaJoy | 你有一个必逛的展位 @W4 馆 B786

惊喜闪现👇 打开抖音搜索【融云】 关注融云官抖,可到展位领取好礼一份 更有惊喜内容不定时掉落~

Python从0到100(四十六):实现管理员登录及测试功能

前言: 零基础学Python:Python从0到100最新最全教程。 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Pyth…

“微软蓝屏”事件暴露了网络安全哪些问题?

网络安全的隐性威胁:从“微软蓝屏”事件看全球IT基础设施的脆弱性 ---------------------------------------------------------------------------------------------------------------------------- 一、事件回顾 近日,由微软视窗系统软件更新引发的…

Python设计模式:巧用元类创建单例模式!

✨ 内容: 今天我们来探讨一个高级且实用的Python概念——元类(Metaclasses)。元类是创建类的类,它们可以用来控制类的行为。通过本次练习,我们将学习如何使用元类来实现单例模式,确保某个类在整个程序中只…

Adobe Dimension(DN)安装包软件下载

目录 一、软件简介 二、软件下载 三、注意事项 四、软件功能 五、常用快捷键 快捷键: 一、软件简介 Adobe Dimension(简称DN)是Adobe公司推出的一款三维设计和渲染软件。与一般的3D绘图软件相比,DN在操作界面和功能上有所不…

国产JS库(js-tool-big-box)7月度总结

js-tool-big-box开发已经有3个月了,团队内的小伙伴进行了热烈的讨论,持续做了功能迭代。小伙伴们也做了艰苦卓绝的文档分享,有纯功能分享类的,有带有小故事的,有朋友们利用自己独自网站分发分享的。7月份快要结束了&am…

sip六大头域深度解析 - Max-Forwards头域

SIP(Session Initiation Protocol,会话初始协议)中的Max-Forwards头域是一个用于限制SIP请求消息在SIP网络中能够经过的实体(如代理服务器、gateway)的最大数目的头域。 基本概念 功能:Max-Forwards头域用…

FPGA DNA 获取 DNA_PORT

FPGA DNA DNA 是 FPGA 芯片的唯一标识, FPGA 都有一个独特的 ID ,也就是 Device DNA ,这个 ID 相当于我们的身份证,在 FPGA 芯片生产的时候就已经固定在芯片的 eFuse 寄存器中,具有不可修改的属性。在 xilinx 7series…

基于java 精品课程教学网站的设计与实现

1 引言 当今时代是飞速发展的信息时代。在各行各业中离不开信息处理,计算机被广泛应用于B/S系统环境。计算机的好处在于它能够进行信息管理。使用计算机进行信息控制,不仅提高了工作效率,而且大大的提高了安全性。 对于复杂的信息管理&…

算法——滑动窗口(day7)

904.水果成篮 904. 水果成篮 - 力扣(LeetCode) 题目解析: 根据题意我们可以看出给了我们两个篮子说明我们在开始采摘到结束的过程中只能有两种水果的种类,又要求让我们返回收集水果的最大数目,这不难让我们联想到题目…

Stateflow中的状态转换表

状态转换表是表达顺序模态逻辑的另一种方式。不要在Stateflow图表中以图形方式绘制状态和转换,而是使用状态转换表以表格格式表示模态逻辑。 使用状态转换表的好处包括: 易于对类列车状态机进行建模,其中模态逻辑涉及从一个状态到其邻居的转换…