【故障分析+解决】解决链接程序时,由于链接crt*.o的顺序问题导致的bug

December 09, 2023
测试
测试
测试
测试
1 分钟阅读

现象

今天在为DragonOS编译http服务器程序时,遇到了一个神奇的bug:

程序在一台机器A上能够正常编译、正常运行,但是,换了机器B编译之后,就无法运行,会报错:

两台编译用的机器,操作系统都是Ubuntu22.04. 我一开始以为是机器B上面的编译器/链接器有问题,于是重新安装了编译工具链。但是无法解决问题。

对有故障的程序使用objdump -D命令进行反编译,发现其_init段变成了两个:

按照之前的开发经验可以知道,_init段是存在于crt*.o这几个文件内的,链接器会把这几个文件的_init段,按照顺序拼接起来。

然后再看正常运行的程序,反汇编之后的结果:

对比可以发现,异常程序把_init段的后半部分,加到了_init-0x2这个段内。这是错误的现象。

排错

由于正常机器A、异常机器B的操作系统、编译器、链接器版本相同,我首先怀疑问题出在编译出来的libc的文件上。于是,我把A编译出来的文件,在B上进行链接,发现问题仍然存在。把B编译出来的文件在A上链接,发现结果正常。

因此排除编译结果的问题,接下来把问题聚焦在链接过程上。

怀疑是机器B的系统自带的链接器有问题,因此我将A的链接器拷贝到B上,然后进行链接。发现问题依旧。重复对比实验,排除链接器问题。

于是,考虑链接参数问题。由于链接的时候使用了find命令查找crt*.o文件,并存储到一个数组中。因此把最终调用链接器的命令打出来,发现B机器上,输入链接器的文件参数顺序如下:

  • main.o
  • crt1.o
  • crtn.o
  • crti.o
  • crt0.o
  • libc.a

而正常的A机器上,输入链接器的文件参数顺序如下:

  • main.o
  • crt0.o
  • crt1.o
  • crti.o
  • crtn.o
  • libc.a

观察发现,机器A上,输入的crt*.o文件的顺序是按照升序排列的,而有问题的B机器则不是按照升序的。因此尝试在find命令后面加上” | sort” ,使得crt*.o按照升序排列。经过测试,调整之后,在B机器上面,编译出来的程序能够正常运行。

结论

链接器的链接顺序与文件输入顺序有关,并且crt*.o的链接顺序必须按照文件名升序排序。错误的顺序会导致程序无法运行,而链接器不会报任何错误。

并且,我们不能假设find命令输出的结果是按照升序排列的,必须使用sort命令进行排序,才能够确保结果升序。

继续阅读

更多来自我们博客的帖子

如何安装 BuddyPress
由 测试 December 17, 2023
经过差不多一年的开发,BuddyPress 这个基于 WordPress Mu 的 SNS 插件正式版终于发布了。BuddyPress...
阅读更多
Filter如何工作
由 测试 December 17, 2023
在 web.xml...
阅读更多
如何理解CGAffineTransform
由 测试 December 17, 2023
CGAffineTransform A structure for holding an affine transformation matrix. ...
阅读更多