编译相关问题整理
本文整理一下遇到的编译问题,最常见的就是编译时缺符号,重定义
但是一般来说cmake生成的make语句会隐藏实际的gcc语句,如果手写的makefile,也有可能是目标规则没配置正确
make的debug
verbose = 1
查看实际的gcc语句
make -n
只输出规则不运行
make -d
将打印 Make 为每个目标尝试的所有规则(包括内置规则)
单步编译链接语句debug
拿到gcc语句以后,就可以只运行这一句gcc语句来进行debug了
编译搜索顺序问题
最常见的是因为头文件include顺序,导致的报错的符号和预期的不一致:
此时
gcc -v -E
拼接实际的gcc语句输出搜索的头文件路径,-v输出详细信息,-E是预处理截断链接缺符号/重定义
nm libA.a
或者reaelf -s libA.a
看缺什么符号或者多了什么符号,对应的预期的库有没有这些符号- 缺符号的报错
- 如果预期的库有这些符号,那就是链接搜索顺序问题
- 如果没有这个符号,那就是有bug
- 重定义的报错
- 如果预期的库没有这些符号,那就是链接搜索顺序问题
- 如果有这些符号,那就是有bug
- 缺符号的报错
链接搜索顺序问题
最常见的是因为库文件路径的搜索顺序有问题,导致引入了非预期的库文件:
gcc -Wl,--verbose拼接实际的gcc语句,输出搜索库文件路径的顺序
执行阶段动态库错误debug
由于动态链接阶段的GOT表覆盖规则,因此不会发生重定义的问题
动态链接缺符号
ldd -r看缺什么库和符号,对应的预期的库有没有这些符号
- 如果有的话,那就是链接搜索顺序问题
- 如果没有的话,那就是预期的库不对
动态链接搜索顺序问题
执行时库文件路径的搜索顺序有问题,导致动态链接器引入了非预期的库文件
LD_DEBUG=libs ./xxx输出搜索动态库文件路径的顺序,看看是否和预期一致
其他编译系统
bazel
得到单条gcc语句
bazel -s
例如
bazel --output_user_root=/tmp/cache build :libtest.so -s
这时输出.o文件是怎么编译的,这里面可能会缺一些头文件,需要自己
-I
一下也有如何链接的,但是链接语句是
/usr/bin/gcc @bazel-out/k8-fastbuild/bin/libtest.so-2.params
的形式,cat出来就可以看到了,但是同样的,有可能会缺一些库文件,需要自己改下libtest.so-2.params
的语句
实战
在mysql静态库编译报错,提示重定义
背景
3.4.5.9开始taf框架引入ssl依赖,为了20.04能直接使用16.04的编译结果,我把ssl静态编译入taf框架
解析
libcrypto.a(sha256.o)和libmysqlclient.a(my_sha2.cc.o)的SHA224重定义
这是因为mysql库和ssl库本来就有冲突
mysqlclient
1
2
3
4
5
6
7
8$ dpkg -S /usr/lib/x86_64-linux-gnu/libmysqlclient.a
libmysqlclient-dev: /usr/lib/x86_64-linux-gnu/libmysqlclient.a
$ apt-cache madison libmysqlclient-dev
libmysqlclient-dev | 5.7.33-0ubuntu0.16.04.1 | http://mirrors.aliyun.com/ubuntu xenial-security/main amd64 Packages
$ nm /usr/lib/x86_64-linux-gnu/libmysqlclient.a |grep SHA224
00000000000001e0 T SHA224ssl
静态库
1
2
3
4
5
6
7
8$ dpkg -S /usr/lib/x86_64-linux-gnu/libcrypto.a
libssl-dev:amd64: /usr/lib/x86_64-linux-gnu/libcrypto.a
$ apt-cache madison libssl-dev
libssl-dev | 1.0.2g-1ubuntu4.20 | http://mirrors.aliyun.com/ubuntu xenial-security/main amd64 Packages
$ nm /usr/lib/x86_64-linux-gnu/libcrypto.a|grep SHA224
0000000000000510 T SHA22动态库:
1
2
3
4
5
6
7
8$ dpkg -S /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
libssl1.0.0:amd64: /lib/x86_64-linux-gnu/libcrypto.so.1.0.0
$ apt-cache madison libssl1.0.0
libssl1.0.0 | 1.0.2g-1ubuntu4.20 | http://mirrors.aliyun.com/ubuntu xenial-security/main amd64 Packages
$ nm -D /lib/x86_64-linux-gnu/libcrypto.so.1.0.0|grep SHA224
000000000006b230 T SHA224
如上,5.7.33的mysqlclient和1.0.2g的ssl(不管是动态库还是静态库),都存在SHA224这个符号
都进行静态编译的时候,小部分业务在mysqlclient用到了SHA224符号的时候才会出问题,这也是为什么我没测出来问题
当ssl动态编译的时候,根据got表的覆盖规则,可执行文件的同名符号会覆盖动态链接的符号,由于mysqlclient作为静态库已经被链接到可执行文件里面了,所以会覆盖ssl的符号从而不会发生问题
- PS:20.04的mysqlclient修复了这个问题
1 | $ apt-cache madison libmysqlclient-dev |
小结
20.04的mysqlclient不再有兼容性问题,此时ssl可以静态编译,但是不可能为20.04单独维护一个ssl静态编译的版本
因此ssl必须动态编译,每个ubuntu版本需要单独编译一次taf框架
在libkafka静态库链接报错,提示缺符号
背景
3.4.5.9引入ssl依赖的时候,我使用submodle的方式clone了openssl,然后通过口口相传的知识,使用了1.0.0的ssl版本
解析
1 | /usr/local/sbin/ld: /home/tafjce/HUYA/KafkaHelper/librdkafka/librdkafka.a(rdkafka_ssl.o): in function 'rd_kafka_transport_ssl_set_endpoint_id': |
这是因为librdkafka这个静态库编译的时候,依赖的ssl版本(1.0.2g)和我clone的版本(1.0.0)不一致
librdkafka依赖的ssl符号,在我的版本里面是没有的
经过研究,16.04的开发环境和运行环境统一使用了1.0.2g版本的ssl,我测试checkout到1.0.2g版本的静态库和动态库,都不会出现该报错
所以这里的主要问题还是版本不匹配的问题