android 6 (API 23) 及更高版本 面向 NDK 开发者的 Android 变更
Android N 已经出来,有了好大的变化,对于我们开发者来说,最大的影响莫过于**NDK**相关东西。
以下是在中国谷歌开发者社区看到的。
里面有好多的变化,欢迎大家来讨论。
发布人:开发顾问 Dmitry Malykhanov
受 Android 平台其他改进的影响,为了方便加载本机代码,Android M 和 N 中的动态链接器对编写整洁且跨平台兼容的本机代码提出了更严格的要求。为了确保平滑过渡到近期发布的 Android 版本,应用的本机代码必须遵循这些规则和建议。
我们在下面详细说明了与加载本机代码有关的每一项变更及其影响,以及您可以采取哪些措施来避免出现问题。
所需工具:在 NDK 中,每一个架构都有一个 -linux-android-readelf 二进制文件(例如 arm-linux-androideabi-readelf 或 i686-linux-android-readelf)(位于 toolchains/ 下),但您可以将 readelf 用于任何架构,因为我们只进行基本检查。在 Linux 上,您需要为 readelf 安装 binutils 程序包,为 scanelf 安装 pax-utils 程序包。
私有 API(从 API 24 开始执行)
原生库必须仅使用公共 API,且不可以链接到非 NDK 平台库。该规则从 API 24 开始强制执行,自此以后,应用无法加载非 NDK 平台库。该规则由动态链接器执行,所以无论代码使用何种方式加载,都无法访问非公共库:System.loadLibrary(...)、DT_NEEDED 条目和直接调用 dlopen(...) 都将以完全相同的方式失败。在更新期间用户应获得一致的应用体验,开发者应不需要进行紧急应用更新来处理平台变更。因此,我们不推荐使用私有 C/C++ 符号。所有 Android 设备都必须通过的兼容性测试套件 (CTS) 不包含对私有符号进行的测试。它们可能不存在,或可能行为方式不同。因此,使用私有符号的应用很可能在某些设备上或在将来发布的版本中无法使用,在 Android 6.0 Marshmallow 从 OpenSSL 切换到 BoringSSL 时,很多开发者就发现了这种问题。
为了减少过渡期间对用户的影响,我们确定了在 Google Play 上安装最多的应用中颇为常用且我们在短期内仍可提供支持的一些库(包括 libandroid_runtime.so、libcutils.so、libcrypto.so 和 libssl.so)。为了给您更多的时间进行过渡,我们将暂时支持这些库;所以,如果您看到警告提示,这就意味着您的代码在将来发布的版本中将无法使用,请立即对其进行修复!
$ readelf --dynamic libBroken.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [libnativehelper.so]
0x00000001 (NEEDED) Shared library: [libutils.so]
0x00000001 (NEEDED) Shared library: [libstagefright_foundation.so]
0x00000001 (NEEDED) Shared library: [libmedia_jni.so]
0x00000001 (NEEDED) Shared library: [liblog.so]
0x00000001 (NEEDED) Shared library: [libdl.so]
0x00000001 (NEEDED) Shared library: [libz.so]
0x00000001 (NEEDED) Shared library: [libstdc++.so]
0x00000001 (NEEDED) Shared library: [libm.so]
0x00000001 (NEEDED) Shared library: [libc.so]潜在问题:从 API 24 开始,动态链接器将不再加载私有库,并会阻止应用加载。
解决方案:重新编写本机代码,以便仅依赖公共 API。作为短期的变通方案,可以将没有复杂依赖关系的平台库 (libcutils.so) 复制到项目。作为长期的解决方案,必须将相关代码复制到项目树。SSL/Media/JNI 内部/binder API 不可以从本机代码访问。在必要时,本机代码应调用合适的公共 Java API 方法。
NDK 的 platforms/android-API/usr/lib 下提供完整的公共库列表。
注:SSL/crypto 是特例,应用不得直接使用平台 libcrypto 和 libssl 库,即使在较早版本的平台上也不可以。所有应用都应使用 GMS 安全提供程序,以确保应用免遭已知漏洞攻击。
缺少节标题(从 API 24 开始执行)
每个 ELF 文件的节标题中都包含额外的信息。现在这些标题必须存在,因为动态链接器使用它们进行健全性检查。有些开发者尝试通过删除标题使二进制文件难以理解,防止遭到反向工程。(这样做事实上没有作用,因为可以利用随处可得的工具重建被删除的信息。)$ readelf --header libBroken.so | grep 'section headers'
Start of section headers: 0 (bytes into file)
Size of section headers: 0 (bytes)
Number of section headers: 0
$解决方案:在您的版本中取消删除节标题的步骤。
文本重定位(从 API 23 开始执行)
从 API 23 开始,共享对象将不得包含文本重定位。也就是说,代码必须按原样加载,不得对其进行修改。这种方法减少了加载时间,并提高了安全性。文本重定位的常见原因是使用了非定位独立手写汇编程序。这种情况并不常见。使用我们的文档中所述的 scanelf 工具进行进一步诊断:
$ scanelf -qT libTextRel.so
libTextRel.so: (memory/data?) [0x15E0E2] in (optimized out: previous simd_broken_op1) [0x15E0E0]
libTextRel.so: (memory/data?) [0x15E3B2] in (optimized out: previous simd_broken_op2) [0x15E3B0]
[skipped the rest]如果您没有可用的 scanelf 工具,您可以使用 readelf 进行基本检查,查找 TEXTREL 条目或 TEXTREL 标志。查找其中之一便已足够。(对应于 TEXTREL 条目的值无关紧要,且其通常为 0,存在 TEXTREL 条目即可表明 .so 包含文本重定位)。本例同时存在两个指标:
$ readelf --dynamic libTextRel.so | grep TEXTREL
0x00000016 (TEXTREL) 0x0
0x0000001e (FLAGS) SYMBOLIC TEXTREL BIND_NOW
$注:从技术上讲,是有可能存在带有 TEXTREL 条目/标志但没有任何实际文本重定位的共享对象。NDK 不会发生这种情况,但如果您自己生成 ELF 文件,请确保不要生成声明包含文本重定位的 ELF 文件,因为 Android 动态链接器信任该条目/标志。
潜在问题:重定位强制使代码页可写入,增加了内存中的脏页数量,因此非常浪费内存。从 Android K (API 19) 开始,动态链接器发布了有关文本重定位的警告,但在 API 23 及更高版本中,其拒绝加载带有文本重定位的代码。
解决方案:重新写入与位置无关的汇编程序,确保不需要文本重定位。查看 Gentoo 文档,了解相关详细信息。
无效的 DT_NEEDED 条目(从 API 23 开始执行)
虽然库依赖项(ELF 标题中的 DT_NEEDED 条目)可以是绝对路径,但这在 Android 平台上没有任何意义,因为您无法控制系统将在何处安装库。DT_NEEDED 条目应与所需库的 SONAME 相同,将在运行时查找库的任务留给动态链接器。在 API 23 之前,Android 的动态链接器在查找所需库时会忽略完整路径,仅使用基名(最后一个 ‘/’ 之后的部分)。从 API 23 开始,运行时链接器将完全服从 DT_NEEDED,所以,如果设备的特定位置不存在库,其将无法加载库。
更糟糕的是,有些编译系统存在漏洞,这会导致它们插入指向构建主机上的文件的 DT_NEEDED 条目,而在设备上将无法找到这种文件。
$ readelf --dynamic libSample.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [libm.so]
0x00000001 (NEEDED) Shared library: [libc.so]
0x00000001 (NEEDED) Shared library: [libdl.so]
0x00000001 (NEEDED) Shared library:
[C:\Users\build\Android\ci\jni\libBroken.so]
$潜在问题:在 API 23 之前使用 DT_NEEDED 条目的基名(即最后一个 / 后面的文件名),但从 API 23 开始,Android 运行时将尝试使用指定路径加载库,而该路径在设备上将不存在。有些损坏的第三方工具链/编译系统使用构建主机而非 SONAME 上的路径。
解决方案:确保所有所需的库仅通过 SONAME 引用。最好让运行时链接器查找和加载这些库,因为库的位置可能在不同的设备上有所不同。即确保当前库所依赖的库是通过 -soname libprebuilt.so 形式编译的,如下的使用示例:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS)
LOCAL_MODULE := libprebuilt
LOCAL_SRC_FILES := libprebuilt.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS) LOCAL_MODULE := myprogram
LOCAL_SRC_FILES := myprogram.c
LOCAL_SHARED_LIBRARIES := libprebuilt
include $(BUILD_SHARED_LIBRARY)myprogram 依赖 libprebuilt.so,我们需要确保编译 libprebuilt.so 的时候是使用 -soname libprebuilt.so 形式编译的。
一些特殊情况下(如没有源代码,只有库)可以使用如下方法来更改 DT_NEEDED 的某些项:
去 https://github.com/NixOS/patchelf 下载 patchelf, 编译后使用方法:$ ./patchelf --add-needed libhzh.so libupnpdmc.so # libhzh.so是要add的 DT_NEEDED 库, libupnpdmc.so 是要将 libhzh.so 加入其 DT_NEEDED的库。
缺少 SONAME(从 API 23 开始使用)
每个 ELF 共享对象(“原生库”)必须有一个 SONAME(共享对象名称)属性。NDK 工具链会默认添加此属性,若无此属性,则表明备用工具链或编译系统存在配置错误。缺少 SONAME 可能导致运行时问题,例如加载错误的库:缺少此属性时将使用文件名。$ readelf --dynamic libWithSoName.so | grep SONAME
0x0000000e (SONAME) Library soname: [libWithSoName.so]
$潜在问题:命名空间冲突可能导致在运行时加载错误的库,进而导致在未找到所需符号时或您尝试使用非预期的 ABI 不兼容库时系统崩溃。
解决方案:当前 NDK 默认情况下生成正确的 SONAME。确保使用最新版 NDK,且未将编译系统配置为生成不正确的 SONAME 条目(使用 -soname 链接器选项)。
android 6 (API 23) 及更高版本 面向 NDK 开发者的 Android 变更的更多相关文章
- 【转】NPAPI 插件无法在 Chrome 42 版及更高版本上正常运行
原文网址:https://support.google.com/chrome/answer/6213033 NPAPI 插件无法在 Chrome 42 版及更高版本上正常运行 您可以利用插件在浏览器中 ...
- System.Data.Oracleclient需要Oracle客户端软件Version8.1.7或更高版本问题
C#连接ORACLE报System.Data.Oracleclient需要Oracle客户端软件Version8.1.7或更高版本问题: 开始Webservice在32位系统ORACLE10g库中we ...
- phpMyAdmin - 错误 您应升级到 MySQL 5.5.0 或更高版本,解决办法。。。
折腾自己的个人网站,装了个数据库管理工具,遇到您应升级到 MySQL 5.5.0 或更高版本... 采用降级phpmyadmin版本的方法解决了: 查找phpmyadmin/libraries/com ...
- oracleclient连oracle库 报System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本
在iis下发布eworkflow+eform+ebiao的代码,访问oracle的数据库,用oracleClient或者oledb的方式连接,有时会报“System.Data.OracleClient ...
- 关于IIS部署时出现“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本”的问题解决摘要
系统环境:windows2008 X64 IIS版本:iis7 oracle客户端版本:11g,另外装了32位的客户端. 网站.net framework版本: 4.0 目前状况,IIS可以正常运行, ...
- MVC 基架不支持 Entity Framework 6 或更高版本
MVC 基架不支持 Entity Framework 6 或更高版本.有关详细信息,请访问 http://go.microsoft.com/fwlink/?LinkId=276833. PS:新做一个 ...
- 【2016-09-16】UbuntuServer14.04或更高版本安装问题记录
出于项目需要,我们的Qt程序需要运行在 1. Windows/Linux-X86平台(CPU为常见的桌面级CPU如G3220.I3等): 2. Windows/Linux-X86低功耗平台(CPU为I ...
- “System.Exception: System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本” 的解决方案
在项目部署过程中ORACLE客户端多次会遇"System.Exception: System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本&qu ...
- System.Data.OracleClient 需要 Oracle 客户端软件 version 8.1.7 或更高版本
说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息. 异常详细信息: System.ServiceModel.FaultEx ...
随机推荐
- Quotations中页面弹出的问题
- Ehcache2 的配置(不使用配置文件)
EhCache是一个开放源码的,基于标准的高速缓存系统. 网上关于EhCache的使用配置很多,但是一般是基于配置文件的.但是实际应用中.我们可能需要动态的管理缓存,这时候单纯配置文件就不够用了. 所 ...
- nginx php-fpm启用慢日志slowlog
php-fpm慢日志slowlog设置可以让我们很好的看见哪些php进程速度太慢而导致的网站问题. 可以让我们方便的找到问题的所在. 代码如下 1 vi /data1/server/php-cgi/ ...
- Hive学习笔记——保存select结果,Join,多重插入
1. 保存select查询结果的几种方式: 1.将查询结果保存到一张新的hive表中 create table t_tmp as select * from t_p; 2.将查询结果保存到一张已经存在 ...
- 使用AllocConsole()添加调试用控制台
AllocConsole 函数 为调用进程分配一个新的控制台. 使用步骤: 1. AllocConsole(); //分配控制台 2. HANDLE g_hOutput=GetStdHandle( ...
- Ubuntu14.04中安装Sublime_Text_3
Sublime Text 简介 Sublime Text 是一款流行的文本编辑器软件,有点类似于TextMate,跨平台,可运行在Linux.Windows和Mac OS X.也是许多程序员喜欢使用的 ...
- 【BZOJ】1661: [Usaco2006 Nov]Big Square 巨大正方形(暴力)
http://www.lydsy.com/JudgeOnline/problem.php?id=1661 暴力大法好... 枚举对角线(注意,一种对角线2种情况就行了,自己想...) 然后可以算出其它 ...
- datagrid加分组后的效果
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAAd8AAADdCAIAAAB13e+wAAAZgElEQVR4nO2d/28b533Hn7+APxnYgL ...
- 【PyQt】插入排序算法
# coding=utf-8 import sys from PyQt4.QtGui import * from PyQt4.QtCore import * class MainWindow(QMai ...
- WPF验证之——必填验证
要事先必填验证,首先要重写ValidationRule类的Validate方法,然后在Binding中指定对应的ValidationRule. 第一步:重写ValidationRule的Validat ...