排查Android编译错误时的技巧及常见Android编译错误
1.排查技巧
编译Android时,我们常用命令
make flashfiles -j8
-j8代表用系统的8个线程去编译
但是这样出来的log会直接打印在标准输出里,会存不全;而且因为是8个线程,会导致log的顺序是乱的
所以当我们编译过一次之后(大部分文件已经被编译了,下次编译会跳过,这样节省下大部分时间)
选择下面的命令编译比较好:
nohup make flashfiles -j1
log会存储在当前目录下的"nohup"文件中,并且-j1参数指定只用1个线程去编译,log顺序不会乱,这样方便我们排查编译错误。
补充:
一般情况下,我们需要看log里的error信息,如果步骤没错,一般出现的错误都是缺少依赖,这样我们就缺什么就安装什么。
补充:关于nohup,这是一个可以让当前shell语句在系统后台执行的command。标准的输出全部被重定向到当前文件夹的nohup中。
1.nohup
用途:不挂断地运行命令。
语法:nohup Command [ Arg … ] [ & ]
无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。
如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。
如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。
2.&
用途:在后台运行
一般两个一起用
nohup command &
eg: nohup /usr/local/node/bin/node /www/im/chat.js >> /usr/local/node/output.log 2>&1 &
这条命令的意思是调用路径“/usr/local/node/bin/”下的“node”来运行“/www/im/”下的“chat.js”,让程序的运行过程不在前台显示,而是后台运行,最终将输出重定向到“ /usr/local/node/”下的“output.log”文件。
查看运行的后台进程
(1)jobs -l
jobs命令只看当前终端生效的,关闭终端后,在另一个终端jobs已经无法看到后台跑得程序了,此时利用ps(进程查看命令)
(2)ps -ef
a:显示所有程序
u:以用户为主的格式来显示
x:显示所有程序,不以终端机来区分
(3)如果某个进程起不来,可能是某个端口被占用
查看使用某端口的进程
lsof -i:8090
netstat -ap|grep 8090
查看到进程id之后,使用netstat命令查看其占用的端口
netstat -nap|grep 7779
(4)终止后台运行进程
kill -9 进程号
linux下我们如果想一个任务或者程序还后台执行可以使用&,实际上linux还提供了其他任务调度的命令。
bg
将一个在后台暂停的命令,变成继续执行
fg
将后台中的命令调至前台继续运行
jobs
查看当前有多少在后台运行的命令
ctrl + z
可以将一个正在前台执行的命令放到后台,并且暂停
nohup 命令
用途:不挂断地运行命令。
&的意思是在后台运行, 什么意思呢? 意思是说, 当你在执行 ./a.out & 的时候, 即使你用ctrl C, 那么a.out照样运行(因为对SIGINT信号免疫)。 但是要注意, 如果你直接关掉shell后, 那么, a.out进程同样消失。 可见, &的后台并不硬(因为对SIGHUP信号不免疫)。
nohup的意思是忽略SIGHUP信号, 所以当运行nohup ./a.out的时候, 关闭shell, 那么a.out进程还是存在的(对SIGHUP信号免疫)。 但是, 要注意, 如果你直接在shell中用Ctrl C, 那么, a.out进程也是会消失的(因为对SIGINT信号不免疫)
所以, &和nohup没有半毛钱的关系, 要让进程真正不受shell中Ctrl C和shell关闭的影响, 那该怎么办呢? 那就用nohua ./a.out &吧, 两全其美。
如果你懂守护进程, 那么nohup ./a.out &颇有点让a.out成为守护进程的感觉。
2.Android编译错误
2.1 root_elements issue
报错:Missing -r/--root option: please specify the names of root elements. In Android.bp, use 'root_elements' property to set root elements. Possible root elements are: "feature", "ip_capabilities", "capabilities
解决办法:在对应的Android.bp中的xsd_config中加入提示的root_elements的内容:
xsd_config { name: "capabilities_xxx_type", srcs: ["capabilities_xxx_type.xsd"], package_name: "capabilities_xxx_type", root_elements: [ "capabilities", "feature", "ip_capabilities", ], }
2.2 llvm-strip error
报错:llvm-strip : error: 'xxxxx/xxxx/xxxx/xxxx/xxx.sh' : the file was not recognized as a valid object file
分析:llvm-strip是用来去除目标文件中的一些符号表、调试符号表信息,以减小程序的大小。这里是把xxx.sh这个文件当成ELF文件(binary文件的格式)去做strip操作了。其实并不影响,这个error只是表示想减少这个shell文件的size,结果失败了。
这里的error message只是llvm-strip自己打印的log信息,并非Android编译系统出的error。
修复也比较简单,可以在Android.bp中加入以下字段来skip掉strip的操作:
strip: { none: true, },
2.3 invalid escape sequence '\0' in an unevaluated string "Extension name exceeds the permitted length of 64 including '\0' terminator"
修复:
- "Extension name exceeds the permitted length of 64 including '\0' terminator"); + "Extension name exceeds the permitted length of 64 including '\\0' terminator");
2.3 sepolicy编译错误: xxxx is not defined
out/soong/.intermediates/system/sepolicy/contexts/vendor_service_contexts_test/android_common/79ee1e9077a85cf0a7efae415df2b097/timestamp # hash of input list: c846b504469b87be61d57f02d93b5ba3a1ff35595e86220e4280962f2be84092 libsepol.context_from_record: type hal_mali_platform_service is not defined libsepol.context_from_record: could not create context structure
这个错误信息表明,在尝试运行vendor_service_contexts_test测试时,SELinux策略编译器遇到了问题。具体来说,它无法加载vendor_service_contexts文件,因为该文件的第一行包含了一个未定义的上下文hal_mali_platform_service。
要解决这个问题,请按照以下步骤操作:
检查SELinux策略文件:
- 确保在SELinux策略文件中定义了
hal_mali_platform_service类型。 - 检查该类型的定义是否正确,包括它的属性、角色和敏感级别。
而sepolicy文件中是定义 hal_mali_platform_service的:
type hal_mali_platform_service, service_manager_type, hal_service_type, vendor_service; add_service(hal_mali_platform_default, hal_mali_platform_service); allow { coredomain -init -vold -installd -app_zygote -webview_zygote -isolated_app } hal_mali_platform_service:service_manager find; 在这个SELinux策略片段中,定义了几个类型(type),包括hal_mali_platform_service、service_manager_type、hal_service_type和vendor_service。 然后,用add_service宏将hal_mali_platform_default服务添加到hal_mali_platform_service类型中。 最后,使用allow语句来允许特定的域(domain)对hal_mali_platform_service类型的service_manager类进行find操作。
最终是发现Android升级后,sepolicy.mk中没有添加上述sepolicy文件,导致hal_mali_platform_service未定义,加入Android15的定义就可以了如下:
ifeq ($(PLATFORM_VERSION), 15) BOARD_VENDOR_SEPOLICY_DIRS += $(THIS_DIR)/public/sepolicy-v15 endif ifeq ($(PLATFORM_VERSION), VanillaIceCream) BOARD_VENDOR_SEPOLICY_DIRS += $(THIS_DIR)/public/sepolicy-v15 endif
2.4 Depends on multiple versions of the same aidl_interface
Depends on multiple versions of the same aidl_interface: android.hardware.graphics.common-V4-ndk, android.hardware.graphics.common-V5-ndk
一般该问题出现在Android升级时,是由于NDK的更新导致的。
解决办法:
diff --git a/android/build.bp b/android/build.bp index exxxxxxx4 --- a/android/build.bp +++ b/android/build.bp @@ -46,6 +46,10 @@ bob_external_shared_library { name: "android.hardware.graphics.common-V4-ndk", } +bob_external_shared_library { + name: "android.hardware.graphics.common-V5-ndk", +} + bob_external_shared_library { name: "arm.mali.platform-V2-ndk", } @@ -199,9 +203,12 @@ bob_static_library { "{{if eq .android_api_level \"33\"}}" + "android.hardware.graphics.common-V3-ndk" + "{{end}}", - "{{if ge .android_api_level \"34\"}}" + + "{{if eq .android_api_level \"34\"}}" + "android.hardware.graphics.common-V4-ndk" + "{{end}}", + "{{if ge .android_api_level \"35\"}}" + + "android.hardware.graphics.common-V5-ndk" + + "{{end}}", ], diff --git a/android/usages/build.bp b/android/usages/build.bp index xxxxxxxxx100644 --- a/android/usages/build.bp +++ b/android/usages/build.bp @@ -39,9 +39,12 @@ bob_static_library { "{{if eq .android_api_level \"33\"}}" + "android.hardware.graphics.common-V3-ndk" + "{{end}}", - "{{if ge .android_api_level \"34\"}}" + + "{{if eq .android_api_level \"34\"}}" + "android.hardware.graphics.common-V4-ndk" + "{{end}}", + "{{if ge .android_api_level \"35\"}}" + + "android.hardware.graphics.common-V5-ndk" + + "{{end}}", ], android: {
2.5 not normalized
升级Android15时遇到:
编译Android镜像时报错,单独编译xxx component可以pass
但是编译整个Android镜像时就报错:
打包vendor.img时报错
xxxxxx/./libxxxxx.so: not normalized
本质原因就是对应的xxxx.mk里写了“LOCAL_MODULE_RELATIVE_PATH = .”导致多了一级无效的路径。 最终导致生成了 xxxx/./libxxxx.so 这种不太规范的路径,使得Android15打包vendor.img时报错(A15报错,A14未报错,可能是因为A15的编译系统打包系统对错误检查更加严格)
LOCAL_MODULE_RELATIVE_PATH这个变量就是用来指定模块(比如一个库或者可执行文件)在构建系统中的输出目录的相对路径的,这里指指定成“ . ”其实就是当前目录,没有任何额外效果,直接注释掉就行。
2.6 CANNOT LINK EXECUTABLE "./XYZ": cannot locate symbol "abc_abcxxxx" referenced by "/system/lib64/libbinder_ndk.so"
Android升级完成后,运行某个可执行文件时遇到该报错
用readelf -a命令查看该XYZ依赖于哪些so文件:
$ readelf -a XYZ |grep NEEDED
发现该XYZ文件除了报出来的libbinder_ndk.so,还依赖于libbiner.so
用readelf -Ws查看对应的.so文件中符号abc_abcxxxx的情况
$ readelf -Ws libbinder.so
$ readelf -Ws libbinder_ndk.so
发现符号abc_abcxxxx在libbinder_ndk.so中是UND(undefined),在libbinder.so中可以找到定义
这里就发生特别奇怪的事情了。按道理,如果是某个符号找不到定义的话,肯定在编译的link阶段会报错,但是我们这里编译时未报错,而是执行的时候报错无法定位该symbol,那么就应该是运行XYZ时的环境变量设置的有问题。
去检查执行XYZ前设置环境变量发现:
export LD_LIBRARY_PATH=/vendor/lib64:/vendor/lib64/egl:/system/lib64
我们未把/system/lib64放在最前,该路径既是报错中提示的路径,也是我们通过readelf -Ws能确切找到abc_abcxxxx这个符号的.so所在路径,考虑到这里,我们把环境变量的设置更改为:
export LD_LIBRARY_PATH=/system/lib64:/vendor/lib64:/vendor/lib64/egl
即把/system/lib64放在最前面,再运行./XYZ,此时这个报错消失了。
回头复盘,为什么会导致这个问题?
发现/vendor/lib64路径下面也有一个libbinder.so,并且readelf -Ws找不到abc_abcxxxx这个符号,推测是优先搜索了/vendor/lib64/这个路径,发现有libbinder.so,没有再去找/system/lib64下面的,结果找不到该符号。
浙公网安备 33010602011771号