静态连接时undefindereference错误原因及解决方法

在说明本问题的产生原理是先简单说明一下gnu的连接器在静态链接时的解析过程。

连接器在静态链接时的过程

在符号解析阶段,连接器会从左到右的按照他们在命令行上出现的顺序去扫描可重定位的目标文件和存档文件。在扫描的过程中,链接器维持了一个可重定位的目标文件的集合E(这个集合中的文件会被合并成可执行文件),一个未解析的符号(即引用了但尚未定义的符号)的集合U,以及一个在前面输入文件中已定义的符号集(D)。初始时,E,U和D都是空的。

  • 对于每个输入文件,链接器会去判断他是一个目标文件还是一个存档文件
  • 如果是目标文件,则将f添加到E中,并去修改U和D来反映f中的符号定义
  • 如果是存档文件,则会匹配U中未解析的符号和存档文件中定义的符号。如在存到文件的某个成员m中找到U中的符号,就将m加入到E中,并修改U和D来反映m中的成员符号和定义。如果没有找到匹配的,则将存档文件中的成员文件抛弃。
  • 当链接器完成命令行的扫描后,如果U是非空的,则抛出一个undefinedreference错误,并停止。如果为空,则构建可执行文件

具体的流程图如下:

问题产生原因

libfun.a,其中有函数fun()的定义。在main.c调用到了在libfun.a中定义的fun()文件。当使用如下命令进行连接时:

gcc–static –o main libfun.a main.c

undefinedreference错误。因为在扫描到libfun.a动态库时,此时集合U为空,libfun.a中没有匹配U中的任何符号,则libfun.a文件将会被抛弃。而当解析到main.c文件时,调用了libfun.a中fun函数,而libfun.a已经被抛弃,则会找不到fun函数而报错。

解决方案

上面错误的例子中由于只有一个静态库,所以可以简单的将libfun.a放到main.c文件后面即可。这也算是使用静态库的一个准则吧。那就是将静态库放置到最后边。

并不是简单的遵守了上面的准则就能一定避免这个问题的,因为在一些复杂的系统中可能会出现静态库相互引用的情况。这时候可以将静态库重复或者将所有的静态库打包成一个静态库放置到命令行的最后边。