chenpengcong / blog

14 stars 3 forks source link

静态链接库的顺序问题 #7

Open chenpengcong opened 6 years ago

chenpengcong commented 6 years ago

首先看如下示例代码

//main.c
void foo();
int main()
{
    foo();
}

//foo.c
void bar();
void foo()
{
    bar();
}

//bar.c
void bar()
{

}

可以看到有三个文件main.c, foo.c, bar.c,main.c中的调用的foo函数定义在foo.c,而foo函数调用的bar函数定义在bar.c

接下来将foo.c和bar.c编译成静态库

$ gcc -c foo.c -o foo.o
$ ar cr libfoo.a foo.o
$ gcc -c bar.c -o bar.o
$ ar cr libbar.a bar.o

编译main.c,链接相应静态库生成可执行文件

$ gcc main.c -L. -lfoo -lbar 

成功

$gcc main.c -L. -lbar -lfoo
./libfoo.a(foo.o): In function `foo':
foo.c:(.text+0xa): undefined reference to `bar'
collect2: error: ld returned 1 exit status

失败,提示foo.o找不到符号bar的定义

为什么改变静态库的顺序会导致链接失败?我们需要了解链接器时怎么工作的

以链接成功的顺序为例,首先,链接器从左到右开始检索,记录下main.o中未解析的符号foo,然后从libfoo.a中的foo.o文件找到符号foo的解析,同时foo.o引入了未解析符号bar,继续往右检索,从libbar.a中的bar.o找到符号bar。

而如果将静态库顺序调整为libbar.a,libfoo.a,当链接器在向右检索符号foo的解析时,由于libbar.a不包含符号foo解析,因此被忽略跳过,而最后在libfoo.a中的foo.o中找到foo符号的解析时又引入了未解析符号bar,此时链接器不会重新往前进行检索,因此提示符号未定义错误。

了解了静态链接库的顺序问题,我们来介绍下静态链接库的另一个问题:循环依赖

看如下代码

//main.c
void foo();
int main()
{
    foo();
    return 0;
}

//foo.c
void bar();
void foo()
{
    bar();
}

//bar.c
void zar();
void bar()
{
    zar();
}

//zar.c
void zar()
{
}

将foo.o和zar.o一起打包成一个静态链接库,bar.o单独一个静态链接库

ar cr libfoo_zar.a foo.o zar.o
ar cr libbar.a bar.o

此时这两个库的依赖关系如下

image

成功链接指令如下

gcc main.c -L. -lfoo_zar -lbar -lfoo_zar

失败链接指令如下

$ gcc main.c -L. -lbar -lfoo_zar -lbar 
./libbar.a(bar.o): In function `bar':
bar.c:(.text+0xa): undefined reference to `zar'
collect2: error: ld returned 1 exit status

可以看到成功链接的指令使用了2次lfoo_zar,原因是foo.o依赖于bar.o,而bar.o又依赖于zar.o,这里容易误解的是当链接器检索到libfoo_zar.a时会解析符号zar,事实上并不会,链接器只会链接libfoo_zar.a中的foo.o文件用以解析符号foo,因此当链接libbar.a时引入了未解析符号zar,需要在-lbar后再次添加-lfoo_zar

关于链接的更细致描述推荐阅读这篇文章

为了避免由于静态链接库顺序造成的错误,可以使用--start-group … --end-group参数,该参数告诉linker在中间的...重复找寻smbol,直到没有新的未定义的symbol创建

执行命令如下: $ gcc main.c -L. -Wl, --start-group -lfoo_zar -lbar -Wl, --end-group

参考: https://stackoverflow.com/questions/45135/why-does-the-order-in-which-libraries-are-linked-sometimes-cause-errors-in-gcc/409470#409470 http://novus.pixnet.net/blog/post/32736521-%E9%97%9C%E6%96%BC-ld-%E7%9A%84%E9%80%A3%E7%B5%90%E9%A0%86%E5%BA%8F http://wen00072.github.io/blog/2013/12/24/c-archive-reference-link/ https://eli.thegreenplace.net/2013/07/09/library-order-in-static-linking