Android App Native 64位适配


标签(空格分隔): 未分类


目录


如何编译64位对应的so

如何编译64位对应的so 修改jni/Application.mk

#APP_ABI := armeabi armeabi-v7a x86
APP_ABI := arm64-v8a
APP_OPTIM := release
APP_PLATFORM := android-21 #android-9
APP_STL     := stlport_static
#APP_STL := gnustl_static
SPECIAL_FLAGS   := -fexceptions

实际在安装时,android会选择适合当前手机的so文件,只在安装的时候选择一次

数据模型

从32位到64位,数据模型(C基本数据类型)发生了变化 ^data-model

linux的64位上采用了LP64数据模型,相对于原来的ILP32具体差异如下 [^data-model2]

可见long和指针的字节数变为了64位,其他数据类型保持了32位^data-model3

由于在jni的数据类型中没有指针类型,习惯上c的指针在java侧保存都是通过jint类型传递的,正是这个习惯在兼容64位时是最大的问题,从上图可以看出,所有的指针类型必须用jlong来替代,这种替代是兼容32位系统的(用jlong在jni中保存指针是个好习惯)。 问题的来源清楚了,在看下可能出问题的位置,见下图^data-model4

  • java部分平台无关无任何问题
  • jni部分这部分是重灾区,几乎是必须改
  • native部分,这部分可能会有问题,不过必须要深入看代码才可以发现,一般不用改就可以用

so文件的适配

从代码修改看下具体的工作 ![](http://7u2oj8.com1.z0.glb.clouddn.com/android64-java-modify-1.png) 对应的c端JNI的修改 ![](http://7u2oj8.com1.z0.glb.clouddn.com/android64-c-modify-1.png) 放大图 ![](http://7u2oj8.com1.z0.glb.clouddn.com/android64-c-modify-1-large.png) ![](http://7u2oj8.com1.z0.glb.clouddn.com/android64-c-modify-newobject.png) 这种部分的一个模式是,java端保存了在c代码中创建的一个对象(指针),后续的JNI调用都通过这个保存的对象实现,在用完之后会销毁。 有些JNI部分是不需要修改就可以直接适配64位的,因为没有在c和java之间传递指针。 ![](http://7u2oj8.com1.z0.glb.clouddn.com/android64-on-need-modify.png) ------

可执行程序的升级(Athena)

对于可执行程序相对so多了一个选择,可以考虑把可执行程序采用64位编译,也可以采用64位兼容32位的特点以32位执行。 android系统在升级64位时也采用这种策略,一个系统的关键进程mediaserver是以32位运行的。据我所知mediaserver中部分是使用汇编实现的,audioflinger中的重采样混音部分即有汇编代码,可能升级难度比较大。 从上图中也可看出一个特定,64位上的可执行程序都是share object类型,不管是32位或是64位,32位的可执行程序直接在64位机器执行可能就会报错,抱怨说不是位置无关可执行程序,针对之中情况需要在Android.mk中添加如下的编译选项 make LOCAL_CFLAGS += -pie -fPIE LOCAL_LDFLAGS += -pie -fPIE 重点说下Athena在适配时遇到的问题 athena位于apk中的assets目录下,这就面临两个选择,一是,把Athena编译两个,一个是64位一个是32位,在apk的java代码中在运行时根据ABI选择不同的文件执行;二是,只保留32位的Athena在64位上采用兼容32位的方式运行。考虑到方案二的优势比较明显,首先尝试的是该方案。

tmsdk--ProcessBuild-->sh--execute-->athena--fork-->script process

编译完直接执行,发现athena和script进程无法启动,进一步加log 添加catch CHILD信号函数,发现是Athena fork子进程后,子进程很快就退出了,导致athena在通过pipe和script进程通信是出现breaken pipe的报错 。log中没有任何关于子进程出错的log,进一步分析代码,

        if (dup2(sPIPE_p2c[0], 0) < 0 || dup2(sPIPE_c2p[1], 1) < 0 || dup2(sPIPE_c2p[1], 2) < 0) {
            LogXX(sTAG, "dup2 error, (%s)", strerror(errno));
            goto error;
        }

        int retcode = execle("/system/bin/sh", "sh", NULL, env);
        if (retcode != 0) {
            LogXX(sTAG, "execle err(%d, %s)", retcode, strerror(errno));
        }

        // 管道出错的话,将父进程kill掉然后退出
        error:
        printfInfo("dup2()");

在fork子进程后,重定向的子进程的标准输入输出错误信息到管道。这是如果有错误在标准错误输出也看不到。尝试注释掉重定向,编译后在直接在shell中命令行执行。

        if (dup2(sPIPE_p2c[0], 0) < 0 || dup2(sPIPE_c2p[1], 1) < 0 /*|| dup2(sPIPE_c2p[1], 2) < 0*/) {
            LogXX(sTAG, "dup2 error, (%s)", strerror(errno));
            goto error;
        }

        int retcode = execle("/system/bin/sh", "sh", NULL, env);
        if (retcode != 0) {
            LogXX(sTAG, "execle err(%d, %s)", retcode, strerror(errno));
        }

        // 管道出错的话,将父进程kill掉然后退出
        error:
        printfInfo("dup2()");

果然在shell窗口出现报错,提示sh需要加载64位libc,实际找到的只有32位的libc,继续尝试在子进程中打印出其LDLIBRARYPATH环境变量,在添加log过程中无意发现代码中有对该环境变量的使用。

#define CTS_SOCKET_PATH         "/dev/socket/script_socket"

#define PATH                    "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin"
#define LD_LIBRARY_PATH         "LD_LIBRARY_PATH=/vendor/lib:/system/lib"

这样问题就大白了, tmsdk--ProcessBuild-->sh----execute-->athena--fork------------------->script process 64位---ProcessBuild-->64位--execute-->32位------fork-32位-execle sh-->64位 fork出的子进程是32位的使用/system/lib没有问题,但执行execle之后进程会成为64位进程,需要使用lib64的对应的libc。最终的修改是把指定LDLIBRARYPATH的代码注释掉让系统自动选择64位和32位的动态链接库。

补充两个可能会遇到的问题

  1. extern c C++ name mangling遇到这种问题很难定位,介绍一种方式,奇怪的是同样的代码原始的32位没问题 添加extern c之后
  2. 使用android-21编译出的32位so,无法在老版本(<5.0)版本上运行,运行时保存动态链接的系统函数找不到,一般是random sigsetempty sigaddset等函数。网上也有很多,从代码看是android-21 修改了函数接口导致的。解决办法是32位的so要是<21的参数编译。

native部分可能存在的问题

这部分是C语言的问题,所有的c程序都需要面对的问题,这里推荐几个网址 32bit-64bit porting work注意事项

  • 截断问题
  • 常量有效性问题
  • 参数问题
  • 扩充问题(指针范围越界)
  • 符号扩展问题
  • 联合体问题(Union)
  • 对齐问题
  • 内存分配问题以及指针跳转问题
  • 内存消耗问题与性能
  • 字节序问题

20 issues of porting C++ code on the 64-bit platform

Porting from 32-bit to 64-bit systems

This section shows you how to correct common trouble spots:

  • Declarations
  • Expressions
  • Assignments
  • Numeric constants
  • Endianism
  • Type definitions
  • Bit shifting
  • Formatting strings
  • Function parameters

64位的一个pdf 64-bit and Data Size Neutrality

[^data-model2]: Porting Linux applications to 64-bit systems


Copyright © FengGuangtu 2017