BusyBox是TWRP的依赖组件,也是用户在TWRP中必不可少的工具箱。但是,由于Android 9相比于上一个版本Android 8.1,在编译系统上进行了一些意想不到的调整,使得理论上可以正常编译而不会出问题的BusyBox源码,在OmniROM 9.0上频频出错。鉴于网上并没有有效的解决方案,故只能自己摸索和总结。探究过程记述如下。
注意:以下记述的前提是已经运行过
source build/envsetup.sh
。
问题一:警告当错误而导致无法继续
一条运行出错的编译指令
编译TWRP时,在其中一条命令下出错:
1 |
|
然而观察错误输出可以发现,报错的原因是“Unused variable xxxx
”,即编译器检测到了只定义不使用的变量。这当然不会影响程序运行,因此不难得知,这条所谓的“错误”其实是在一个被视为错误的警告。给GCC编译器加上-Werror
参数,那么所有的警告,无论是否严重,都会被当成错误。
追溯过程
笔者发现,上述命令中,
-Wall
被调用了两次,而且第二次调用时前面有不止一个空格,后面跟着-Werror
。这显然是后期被追加上去的。使用
mgrep
于全编译系统检索-Werror
,结果在build/core/binary.mk
中发现了如下代码。最为醒目的是“WARNING_ALLOWED
”这个关键词:1
2
3
4
5
6
7# Add -Wall -Werror unless the project is in the WARNING_ALLOWED project list.
ifeq (,$(strip $(call find_warning_allowed_projects,$(LOCAL_PATH))))
my_cflags := -Wall -Werror $(my_cflags)
else
$(eval MODULES_ADDED_WALL := $(MODULES_ADDED_WALL) $(LOCAL_MODULE_MAKEFILE):$(LOCAL_MODULE))
my_cflags := -Wall $(my_cflags)
endif检索“
WARNING_ALLOWED
”(或“find_warning_allowed_projects
”),在build/core/config.mk
中发现如下函数定义:1
2
3
4# ANDROID_WARNING_ALLOWED_PROJECTS is generated by build/soong.
define find_warning_allowed_projects
$(filter $(ANDROID_WARNING_ALLOWED_PROJECTS),$(1)/)
endef顺藤摸瓜,去到
build/soong
,检索变量ANDROID_WARNING_ALLOWED_PROJECTS
,它出现在cc/makevars.go
中:1
ctx.Strict("ANDROID_WARNING_ALLOWED_PROJECTS", makeStringOfWarningAllowedProjects())
查找函数
makeStringOfWarningAllowedProjects()
的定义,它就在makevars.go
当中,如下所示:1
2
3
4
5
6
7
8
9
10
11func makeStringOfWarningAllowedProjects() string {
allProjects := append([]string{}, config.WarningAllowedProjects...)
allProjects = append(allProjects, config.WarningAllowedOldProjects...)
sort.Strings(allProjects)
// Makefile rules use pattern "path/%" to match module paths.
if len(allProjects) > 0 {
return strings.Join(allProjects, "% ") + "%"
} else {
return ""
}
}可以看出,这个函数的作用,是把
config.WarningAllowedProjects
这个列表中的所有项目连缀成一个字符串。继续顺藤摸瓜,检索
WarningAllowedProjects
,最终在cc/config/global.go
中看到了它的定义:1
2
3
4
5
6
7
8
9
10
11var (
......
// Directories with warnings from Android.bp files.
WarningAllowedProjects = []string{
"device/",
"vendor/",
}
// Directories with warnings from Android.mk files.
WarningAllowedOldProjects = []string{}
)可以看出这是一个数组,其中存放的值为Manifest中的项目路径。
解决办法
把BusyBox和TWRP的项目路径加到上述WarningAllowedProjects
列表中,就像这样:
1 |
|
然后make clean
,重新编译。
问题二:链接错误
链接错误日志
最后关头,发生以下错误:
1 |
|
上述错误信息的内容是:链接器ld
检测到了重复定义的符号。这是因为每个符号在Busybox和Android的libc库中都有定义。事实上可让链接器在链接时允许符号重定义,只需给gcc
加上参数-Wl,-z,muldefs
,告诉链接器应该这么做,即可解决问题。
进行测试
一旦某条编译指令运行出错,编译系统就会把它完整地打印出来。在本文中,Busybox最后的链接指令如下(只有一行,但会很长),笔者在最后加入了-Wl,-z,muldefs
:
1 |
|
经测试,链接成功。
找出上述命令的原型
那么,上面的这条链接命令是怎么工作的?如何找出它的原型?这需要分析最后链接这一步调用了哪些参数,然后选出其中一个有代表性的参数,使用mgrep
在所有的Makefile中检索。
按空格分割上述日志,目光转向最上面的参数,就属-Wl,--gc-sections
有代表性。在源码根目录进行检索:
1 |
|
果然在build/make/core/definitions.mk
发现了四个匹配:
1 |
|
结合日志中的其他参数,对检索结果进行比对,终于在1939行发现了生成Busybox可执行文件的代码本体:
1 |
|
注意上方的PRIVATE_LDFLAGS
变量,该变量可以给这个链接过程传入额外的链接器参数。它的值来自每个项目(project)的Android.mk
中所指定的变量LOCAL_LDFLAGS
,正如其名——“private”指的是项目相对于编译系统的“private”。
补救方法
修改external/busybox/Android.mk
,在生成可执行文件的地方加上LOCAL_LDFLAGS
即可。具体做法见下方diff补丁:
1 |
|
直接重新编译即可生效。
参考资料
- 本文作者: 爱拼安小匠
- 本文链接: https://anclark.github.io/2019/07/10/Android_Adapting_Note/追根溯源解决Busybox编译错误/
- 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 3.0(署名-非商用-禁止演绎 3.0) 许可协议。转载请注明出处!