标签(空格分隔): 未分类
如何编译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
对于可执行程序相对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位的动态链接库。
这部分是C语言的问题,所有的c程序都需要面对的问题,这里推荐几个网址 32bit-64bit porting work注意事项
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:
64位的一个pdf 64-bit and Data Size Neutrality
[^data-model2]: Porting Linux applications to 64-bit systems