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

2023-10-18 10:49:11 浏览数 (2)

现象

今天在为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命令进行排序,才能够确保结果升序。

0 人点赞