动态库依赖动态库,静态库依赖静态库,顺序

更新时间:2023-06-16 08:44:23 阅读: 评论:0

动态库依赖动态库,静态库依赖静态库,顺序
静态库依赖静态库,有顺序的问题,否则undefined reference
⾄于动态链接,链接器会对依赖进⾏整理,避免这个问题。动态库就不存在依赖顺序的问题。
如果库是相互独⽴的,则顺序不重要。如果不是相互独⽴,那么必须对它们进⾏排序
对于⽇常命令⾏编译命令,⼀般从左到右分别是可执⾏⽂件 ——> ⾼级库 ——> 底层库,避免循环依赖;越是底层的库,越是往后⾯写,可以参考下述命令通式:
g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@
静态库有顺序问题,并且要把⾃⼰的库所依赖的所有的第三⽅库都要显⽰的指定出来。
动态库⽆顺序问题,并且只需要显⽰的连接⾃⼰的动态库,⾃⼰的动态库依赖的第三⽅的动态库⽆需显⽰指定,⾃⼰会从rpath中⾃动连接第三⽅的动态库。
但必须把第三⽅库依赖的所有的库,包括路径都拷贝出来。
例如使⽤libevent.so, 此时需要把下⾯这⼀堆⽂件都拷贝出来:
lrwxrwxrwx. 1 root root    21 Mar 25 15:18 deps/so/libevent-2.1.so.7 -> libevent-2.1.so.7.0.1
-rwxr-xr-x. 1 root root 386024 Mar 25 15:18 deps/so/libevent-2.1.so.7.0.1
lrwxrwxrwx. 1 root root    26 Mar 25 15:17 deps/so/libevent_core-2.1.so.7 -> libevent_core-2.1.so.7.0.1
-rwxr-xr-x. 1 root root 241936 Mar 25 15:17 deps/so/libevent_core-2.1.so.7.0.1
lrwxrwxrwx. 1 root root    26 Mar 25 15:17 deps/so/libevent_core.so -> libevent_core-2.1.so.7.0.1
lrwxrwxrwx. 1 root root    21 Mar 25 15:18 deps/so/libevent.so -> libevent-2.1.so.7.0.1
把第三⽅静态库链接到⾃⼰的.so动态库,编译第三⽅静态库的时候需要加 -fPIC这个参数。
可以依赖第三⽅动态库⽣成⾃⼰的动态库
t(CMAKE_INSTALL_RPATH ${MY_RUNTIME_PATH})
add_library(my_dynamic SHARED SRC_list)
TARGET_LINK_LIBRARIES(my_dynamic ${other_so1_so2})
此时⽣成的libmy_dynamic.so动态库的rpath是${MY_RUNTIME_PATH},
别⼈利⽤我的libmy_dynamic.so
程序指定的rpath
两种解决办法:国画枇杷
1. 两处的rpath保持⼀致创业资源
2. 通过export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}: new_path
所有的动态库都拷贝到new_path路径下。
链接过程
我们在编译的过程中,有两种链接⽅式,动态与静态。
静态链接:即将依赖库与调⽤程序链接成⼀个完成的库或者可执⾏⽂件,运⾏的时候会将整个程序装到内存中,⽅便部署但是体积较⼤,依赖库升级的时候需要重新编译;
动态链接:即将依赖库与调⽤程序分离,不组装成单个⽂件,⽽是在运⾏的时候,当调⽤到动态库的库时,才会将依赖库装载到内存中,这样会⽅便与其它程序共享以及升级,可执⾏⽂件的体积⼩,但是不⽅便部署;
今天我们遇到的问题,发⽣在静态链接过程中,⽽链接过程的细节如下:
静态库中,包含着所有的 obj(*.o) ⽂件,连接器从左⾄右搜索,维护着⼀个 undefined 列表,⼀旦遇到没有定义的内容,就会将它加到列表中,如果搜索到了定义的内容,则抽取出 obj ⽂件,进⾏链接,并将 undefined 内容移出列表,
⽽其它 obj ⽂件就会被丢弃(为了减少最终的体积⼤⼩),于是⼀个静态库如果不能在搜索过程中被链接,它就会被丢弃,⽽在后⾯⼀旦遇到依赖它的库,就会造成引⽤⽆法被链接,⼀直留在undefined 列表中,最终导致编译错误。
静态库的环形链接:
然后我们再来个复杂点的:
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
int d;
$ cat d.cpp
extern int d;
extern int e;
int b = d + e;
$ cat e.cpp
int e
再运⾏上⾯的编译后:
$ g++ -c e.cpp -o e.o
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o e.o # 注意这⾥,把 b 与 e 在同⼀个静态库 libb.a ⾥⾯
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
会发现那两种⽅式都不能解决问题。
⽽下⾯这种⽅式却能解决
$ g++ a.cpp -L. -lb -ld -lb
人生经典
为什么?
四级英语试卷真题很简单,因为回顾下链接的过程就能发现,当链接器遇到第⼀个 lb 时,会将 b 加⼊ undefined 列表,⽽遇到 ld 时,会将 b 与 ld 链接,同时将 d 与 e 加⼊ undefined 列表,最后遇到第⼆个 lb 时,重复同样的过程,然后顺利链接。
但是,反过来:
$ g++ a.cpp -L. -ld -lb -ld
却会失败。
链接的库⽂件中⼜使⽤了另⼀个库⽂件
关于幸运的作文
这种问题⽐较隐蔽,也是我最近遇到的与⽹上⼤家讨论的不同的问题,举例说明如下,⾸先,还是看看测试代码。
从上图可以看出,main.c调⽤了test.c的函数,test.c中⼜调⽤了fun.c的函数。⾸先,我们先对fun.c,test.c,main.c进⾏编译,⽣成 .o ⽂件。
1. gcc -c func.c
2. gcc -c test.c
3. gcc -c main.c
然后,将test.c和func.c各⾃打包成为静态库⽂件。
1. ar –rc func.a func.o
2. ar –rc test.a test.o
这时,我们准备将main.o链接为可执⾏程序,由于我们的main.c中包含了对test()的调⽤,因此,应该在链接时将test.a作为我们的库⽂件,链接命令如下。
1. gcc -o main main.o test.a
这时,编译器仍然会报错,如下:
1. test.a(test.o): In function `test':
2. test.c:(.text+0x13): undefined reference to `func'
3. collect2: ld returned 1 exit status
就是说,链接的时候,发现我们的test.a调⽤了func()函数,找不到对应的实现。由此我们发现,原来我们还需要将test.a所引⽤到的库⽂件也加进来才能成功链接,因此命令如下。
1. gcc -o main main.o test.a func.a
ok,这样就可以成功得到最终的程序了。同样,如果我们的库或者程序中引⽤了第三⽅库(如pthread.a)则同样在链接的时候需要给出第三⽅库的路径和库⽂件,否则就会得到undefined reference的错误。
4 多个库⽂件链接顺序问题
这种问题也⾮常的隐蔽,不仔细研究你可能会感到⾮常地莫名其妙。我们依然回到第3⼩节所讨论的问题中,在最后,如果我们把链接的库的顺序换⼀下,看看会发⽣什么结果?
1. gcc -o main main.o func.a test.a
我们会得到如下报错.
1. test.a(test.o): In function `test':
2. test.c:(.text+0x13): undefined reference to `func'
3. collect2: ld returned 1 exit status
因此,我们需要注意,在链接命令中给出所依赖的库时,需要注意库之间的依赖顺序,依赖其他库的库⼀定要放到被依赖库的前⾯,这样才能真正避免undefined reference的错误,完成编译链接。
假设程序test依赖动态库b,⽽动态库b依赖动态库a。
在编译test的时候,我们希望的是只指定b,⽽不⽤指定a,因为我们不希望知道a的依赖库有哪些,只需关⼼b。那么我们采⽤这样的思路去编译test的时候,是会报错的。⽐如:
我们编译动态库a:gcc a.c -o liba.so -shared -fPIC
我们编译动态库b:gcc b.c -o libb.so -shared -fPIC -I../a/ -L../a/ -la
我们编译test:gcc main.c -o test -I../b -L../b -lb
程序会报错如下:
/usr/bin/ld: warning: liba.so, needed by ../b/libb.so, not found (try using -rpath or -rpath-link)
../b/libb.so: undefined reference to `a'
collect2: ld returned 1 exit status
错误的原因在于程序没能⾃动找到liba.so,因为liba.so不在ld的默认搜索路径⾥。解决⽅法是如下编译:
gcc main.c -o test -I../b -L../b -lb -Wl,-rpath=../a
【不显⽰指定第三⽅被依赖的动态库也⾏,只要指定了其rpath路径就可以,ldd libb.so可以看到依赖了liba.so】————————————————
libB.so的源码:
#include <stdio.h>
int funB1(){
printf("in funB1");
return 0;
}
int funB2(){
printf("in funB2");
return 0;
}
这⾥⾯有两个函数:funB1和funB2。
其中funB1函数会被libA调⽤,⽽funB2会被可执⾏⽂件调⽤。
编译libB.so:
$ gcc libB.cpp -fPIC -shared -o libB.so
1
libA.so的源码:
#include <stdio.h>
int funB1();
int funA1(){
printf("in funA1 \n");
funB1();消防案例
return 0;
}
该库中只有⼀个函数funA1,该函数在内部调⽤了libB中的funB1函数。且该函数会被可执⾏⽂件调⽤。
编译libA.so:
$ gcc libA.cpp -fPIC -shared -o libA.so -Wl,-rpath=./ -L./ -lB
1
main.cpp的源码:
int funA1();
int funB2();房产经纪人
int main(){
funA1();
funB2();
return 0;
}
编译main.cpp:(复现错误的编译⽅法)
gcc main.cpp -L./ -lA
1
当我们按照上⾯的指令编译main.cpp的时候,便报错了。
/
usr/bin/ld: /tmp/ccDQXTKy.o: undefined reference to symbol '_Z5funB2v'
.//libB.so: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
(问号.jpg)这,这GCC不是搞事吗,你明明知道我需要连接libB.so为啥就不帮我链接上去呢?难道我libA.so没有指明要使⽤libB.so?我们使⽤下⾯的指令来看⼀下
$ ldd libA.so
1
得到如下信息:
linux-vdso.so.1 => (0x00007ffd09def000)
libB.so => ./libB.so (0x00007fc513d7d000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc5139b3000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc514181000)
明明libA.so已经显式的指明我要依赖libB.so了,那为啥在编译main.cpp的时候链接了libA.so,GCC却还要我们显式的链接libB.so呢?
3 答案
答案很简单,那就是GCC就是想要你显式链接呗。(你是编译器,你⽜好吧。)那这是为啥呢?
官⽅⼀点的答案就是,⾃从binutils 2.22版本以后,如果你在程序中使⽤了你依赖的动态库所依赖的动态库中的函数时,你就必须显式的指定你依赖的动态库所依赖的动态库。
说那么多,我们更想知道的是,通过修改什么参数可以解决这个问题呢?因为你可能不想在编译程序的时候要把动态库所依赖的所有动态库都显⽰链接⼀遍。
4 究极答案
实际上,这是binutils在2.22版本以后,默认把--no-copy-dt-needed-entries这个选项打开了。当打开了这个选项的时候,编译器在链接的时候是不会递归的去获取依赖动态库的依赖项的,于是就会出现上述的问题。关于该配置项的详细说明如下:
考勤怎么做--copy-dt-needed-entries
--no-copy-dt-needed-entries
This option affects the treatment of dynamic libraries referred to by DT_NEEDED tags inside ELF dynamic libraries mentioned on the command line. Normally the linker won't add a DT_NEEDED
tag to the output binary for each library mentioned in a DT_NEEDED tag in an input dynamic library. With --copy-dt-needed-entries specified on the command line however any dynamic
libraries that follow it will have their DT_NEEDED entries added. The default behaviour can be restored with --no-copy-dt-needed-entries. This option also has an effect on the resolution of symbols in dynamic libraries. With --copy-dt-needed-entries dynamic libraries mentioned on the command line will be recursively
arched, following their DT_NEEDED tags to other libraries, in order to resolve symbols required by the output binary. With the default tting however the arching of dynamic
libraries that follow it will stop with the dynamic library itlf. No DT_NEEDED links will be traverd to resolve symbols.
⼤概意思就是,跟在--no-copy-dt-needed-entries它后⾯的库都不会遍历其依赖项,使⽤--copy-dt-nee
ded-entries则相反。也就是使⽤下⾯的指令来编译mian.cpp就可以避免该问题了。
$ gcc main.cpp -L./ -Wl,--copy-dt-needed-entries -lA
1
题外话
在Linux的ELF⽂件中,如果依赖于其他的动态库,那么改ELF⽂件会存在⼀个.dynamic的段,这个段⾥⾯会记录其依赖的动态库信息,其标志位为DT_NEEDED。

本文发布于:2023-06-16 08:44:23,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1040872.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:依赖   链接   动态   时候
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图