GCC选项_-Wl,-soname 及 DT_NEEDED 的解释
-Wl选项告诉编译器将后面的参数传递给链接器。
-soname则指定了动态库的soname(简单共享名,Short for shared object name)
soname的关键功能是它提供了兼容性的标准:
当要升级系统中的一个库时,并且新库的soname和老库的soname一样,用旧库链接生成的程序使用新库依然能正常运行。这个特性使得在Linux下,升级使得共享库的程序和定位错误变得十分容易。
在Linux中,应用程序通过使用soname,来指定所希望库的版本,库作者可以通过保留或改变soname来声明,哪些版本是兼容的,这使得程序员摆脱了共享库版本冲突问题的困扰。
可以通过readelf -d来查看每个动态库的SONAME
1. 声明libto.so.1,并生成libto.so.1.2
- [root@localhost c]# gcc -fPIC -shared -Wl,-soname,libto.so. -o libto.so.1.2 to.c
- [root@localhost c]# ls -lh
- -rwxr-xr-x root root Jan : libto.so.1.2
- [root@localhost c]# ldconfig -n ./
- lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.2
- -rwxr-xr-x root root .2K Jan : libto.so.1.2
- [root@localhost c]# readelf -d libto.so.1.2
- Dynamic section at offset 0x504 contains entries:
- Tag Type Name/Value
- 0x00000001 (NEEDED) Shared library: [libc.so.]
- 0x0000000e (SONAME) Library soname: [libto.so.]
- 0x0000000c (INIT) 0x2cc
- 0x0000000d (FINI) 0x4c4
- 0x6ffffef5 (GNU_HASH) 0xb4
- 0x00000005 (STRTAB) 0x1b4
- 0x00000006 (SYMTAB) 0xf4
- 0x0000000a (STRSZ) (bytes)
- 0x0000000b (SYMENT) (bytes)
- 0x00000003 (PLTGOT) 0x15d8
- 0x00000002 (PLTRELSZ) (bytes)
- 0x00000014 (PLTREL) REL
- 0x00000017 (JMPREL) 0x2b4
- 0x00000011 (REL) 0x294
- 0x00000012 (RELSZ) (bytes)
- 0x00000013 (RELENT) (bytes)
- 0x6ffffffe (VERNEED) 0x264
- 0x6fffffff (VERNEEDNUM)
- 0x6ffffff0 (VERSYM) 0x24a
- 0x6ffffffa (RELCOUNT)
- 0x00000000 (NULL) 0x0
2. 声明libto.so.1,并生成libto.so.1.3
- [root@localhost c]# gcc -fPIC -shared -Wl,-soname,libto.so. -o libto.so.1.3 to.c
- [root@localhost c]# ls -lh
- lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.2
- -rwxr-xr-x root root .2K Jan : libto.so.1.2
- -rwxr-xr-x root root .2K Jan : libto.so.1.3
- [root@localhost c]# ldconfig -n ./
- lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.3 #重新ldconfig,指向新的库文件
- -rwxr-xr-x root root .2K Jan : libto.so.1.2
- -rwxr-xr-x root root .2K Jan : libto.so.1.3
- [root@localhost c]# readelf -d libto.so.1.3
- Dynamic section at offset 0x504 contains entries:
- Tag Type Name/Value
- 0x00000001 (NEEDED) Shared library: [libc.so.]
- 0x0000000e (SONAME) Library soname: [libto.so.]
- 0x0000000c (INIT) 0x2cc
- 0x0000000d (FINI) 0x4c4
- 0x6ffffef5 (GNU_HASH) 0xb4
- 0x00000005 (STRTAB) 0x1b4
- 0x00000006 (SYMTAB) 0xf4
- 0x0000000a (STRSZ) (bytes)
- 0x0000000b (SYMENT) (bytes)
- 0x00000003 (PLTGOT) 0x15d8
- 0x00000002 (PLTRELSZ) (bytes)
- 0x00000014 (PLTREL) REL
- 0x00000017 (JMPREL) 0x2b4
- 0x00000011 (REL) 0x294
- 0x00000012 (RELSZ) (bytes)
- 0x00000013 (RELENT) (bytes)
- 0x6ffffffe (VERNEED) 0x264
- 0x6fffffff (VERNEEDNUM)
- 0x6ffffff0 (VERSYM) 0x24a
- 0x6ffffffa (RELCOUNT)
- 0x00000000 (NULL) 0x0
3. 声明libto.so.2,并生成libto.so.1.4
- [root@localhost c]# gcc -fPIC -shared -Wl,-soname,libto.so. -o libto.so.1.4 to.c
- [root@localhost c]# ls -lh
- lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.3
- -rwxr-xr-x root root .2K Jan : libto.so.1.2
- -rwxr-xr-x root root .2K Jan : libto.so.1.3
- -rwxr-xr-x root root .2K Jan : libto.so.1.4
- [root@localhost c]# ldconfig -n ./
- lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.3 #重新ldconfig,不指向新的库文件,因为新库(1.4)的soname为libto.so.
- -rwxr-xr-x root root .2K Jan : libto.so.1.2
- -rwxr-xr-x root root .2K Jan : libto.so.1.3
- -rwxr-xr-x root root .2K Jan : libto.so.1.4
- lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.4
- [root@localhost c]# readelf -d libto.so.1.4
- Dynamic section at offset 0x504 contains entries:
- Tag Type Name/Value
- 0x00000001 (NEEDED) Shared library: [libc.so.]
- 0x0000000e (SONAME) Library soname: [libto.so.]
- 0x0000000c (INIT) 0x2cc
- 0x0000000d (FINI) 0x4c4
- 0x6ffffef5 (GNU_HASH) 0xb4
- 0x00000005 (STRTAB) 0x1b4
- 0x00000006 (SYMTAB) 0xf4
- 0x0000000a (STRSZ) (bytes)
- 0x0000000b (SYMENT) (bytes)
- 0x00000003 (PLTGOT) 0x15d8
- 0x00000002 (PLTRELSZ) (bytes)
- 0x00000014 (PLTREL) REL
- 0x00000017 (JMPREL) 0x2b4
- 0x00000011 (REL) 0x294
- 0x00000012 (RELSZ) (bytes)
- 0x00000013 (RELENT) (bytes)
- 0x6ffffffe (VERNEED) 0x264
- 0x6fffffff (VERNEEDNUM)
- 0x6ffffff0 (VERSYM) 0x24a
- 0x6ffffffa (RELCOUNT)
- 0x00000000 (NULL) 0x0
gcc -shared -Wl,-soname,libfoo.so.major -o libfoo.so.major.minor
DT_NEEDED tag:
DT_NEEDED: 表示一个列表,列表里面以(NEEDED)为标志的项,就是当前库加载时要依赖的其它库。注意 DT_NEEDED 中的 DT 不是 DON'T 的意思。
1 gcc/g++链接时.o文件以及库的顺序问题
1.1 写在前面
最近换了xubuntu12.4,把原来的项目co出来编译的时候报“undefined reference to”。猜测是gcc的版本问题,用-v跟踪一下,发现gcc-4.6默认开 启了ld的–as-needed选项。关闭该选项(–no-as-needed)后编译正常。深 入挖掘发现还是一个比较有意思的问题。
1.2 几个名词
- gcc: 后面不特殊说明gcc代表gcc/g++。
- 主程序块: 只包含main的binary,可执行程序。
1.3 技术铺垫
1.3.1 编译动态库时的符号解析
有符号解析不了的时候,gcc不会直接报错而是假设load的时候地址重定位修正。
1.3.2 linux下查看一个可执行文件或动态库依赖哪些动态库的办法
你有一个library或者是可执行文件,你可以这样查看他的依赖关系:
- readelf -d
- ocaml@ocaml:~$ readelf -d PyGalaxy.so
- Dynamic section at offset 0x7dd8 contains 26 entries:
- Tag Type Name/Value
- 0x0000000000000001 (NEEDED) Shared library: [libGalaxyParser.so]
- ...
- 0x000000000000000c (INIT) 0x18e0
- 0x000000000000000d (FINI) 0x6398
- ldd工具,如下:
- ocaml@ocaml:~$ ldd PyGalaxy.so
- linux-vdso.so.1 => (0x00007fffc8ad3000)
- libGalaxyParser.so => /home/ocaml/lib/libGalaxyParser.so (0x00007f1736e6d000)
- ...
1.3.3 load 动态库过程
基本的说就是符号重定位,然后合并到全局符号表。
1.4 gcc/g++链接时对库的顺序要求
先看看gcc手册对-L和-l的描述
- -Ldir
- Add directory dir to the list of directories to be searched for -l.
- -llibrary
- -l library
- Search the library named library when linking. (The second
- alternative with the library as a separate argument is only for POSIX
- compliance and is not recommended.)
- It makes a difference where in the command you write this option;
- the linker searches and processes libraries and object files in
- the order they are specified. Thus, `foo.o -lz bar.o' searches
- library `z' after file foo.o but before bar.o. If bar.o refers to
- functions in `z', those functions may not be loaded.
- The linker searches a standard list of directories for the
- library, which is actually a file named liblibrary.a. The linker
- then uses this file as if it had been specified precisely by name.
- The directories searched include several standard system
- directories plus any that you specify with -L.
- Normally the files found this way are library files—archive files
- whose members are object files. The linker handles an archive file
- by scanning through it for members which define symbols that have
- so far been referenced but not defined. But if the file that is
- found is an ordinary object file, it is linked in the usual
- fashion. The only difference between using an -l option and
- specifying a file name is that -l surrounds library with `lib' and
- `.a' and searches several directories.
基本的意思就是从左向右查找,如果是链接成动态库会稍微不同一点。
1.4.1 对于library的查找
查找需要连接的符号名是从前向后找,根据-L指定的路径顺序查找;不同 目录下的同名的库,只取第一个(从左向右),后面同名库被忽略;
1.4.2 对于符号的查找
从左向右查找,如果是主程序块和静态库,不能定位地址就报错: ‘undefined reference to: xxx’如果是链接成动态库,则假设该符号在load 的时候地址重定位。如果找不到对应的动态库,则会在load的时候报:“undefined symbol: xxx“这样的错误。
1.5 –as-needed对链接动态库的影响
先看看与动态库链接相关的几个选项的man说明:
- --as-needed
- --no-as-needed
- This option affects ELF DT_NEEDED tags for dynamic libraries mentioned on the command line after the --as-needed
- option. Normally the linker will add a DT_NEEDED tag for each dynamic library mentioned on the command line,
- regardless of whether the library is actually needed or not. --as-needed causes a DT_NEEDED tag to only be emitted for
- a library that satisfies an undefined symbol reference from a regular object file or, if the library is not found in
- the DT_NEEDED lists of other libraries linked up to that point, an undefined symbol reference from another dynamic
- library. --no-as-needed restores the default behaviour.
- --add-needed
- --no-add-needed
- These two options have been deprecated because of the similarity of their names to the --as-needed and --no-as-needed
- options. They have been replaced by --copy-dt-needed-entries and --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 searched, following their DT_NEEDED tags to other
- libraries, in order to resolve symbols required by the output binary. With the default setting however the searching
- of dynamic libraries that follow it will stop with the dynamic library itself. No DT_NEEDED links will be traversed to
- resolve symbols.
再看看ld的帮助
- --add-needed Set DT_NEEDED tags for DT_NEEDED entries in following dynamic libs
- --no-add-needed Do not set DT_NEEDED tags for DT_NEEDED entries in following dynamic libs
- --as-needed Only set DT_NEEDED for following dynamic libs if used
- --no-as-needed Always set DT_NEEDED for following dynamic libs
关键的看–as-needed,意思是说:只给用到的动态库设置DT_NEEDED。比如:
- g++ -shared PyGalaxy.o -lGalaxyParser -lxxx -lrt -o PyGalaxy.so
像这样链接一个PyGalaxy.so的时候,假设PyGalaxy.so里面用到了libGalaxyParser.so但是没 有用到libxxx.so。查看依赖关系如下:
- ocaml@ocaml:~$ readelf -d PyGalaxy.so
- Dynamic section at offset 0x7dd8 contains 26 entries:
- Tag Type Name/Value
- 0x0000000000000001 (NEEDED) Shared library: [libGalaxyParser.so]
- 0x0000000000000001 (NEEDED) Shared library: [libxxx.so]
- ...
当开启–as-needed的时候,像
- g++ -shared -Wl,--as-needed PyGalaxy.o -lGalaxyParser -lxxx -lrt -o PyGalaxy.so
这样链接PyGalaxy.so的时候,查看依赖关系如下:
- ocaml@ocaml:~$ readelf -d PyGalaxy.so
- Dynamic section at offset 0x7dd8 contains 26 entries:
- Tag Type Name/Value
- 0x0000000000000001 (NEEDED) Shared library: [libGalaxyParser.so]
- ...
–as-needed就是忽略链接时没有用到的动态库,只将用到的动态库set NEEDED。
开启–as-needed的一些常见的问题:
1.5.1 链接主程序模块或者是静态库的时的‘undefined reference to: xxx’
- g++ -Wl,--as-needed -lGalaxyRT -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o mutex mutex.o
假设mutex依赖libGalaxyRT.so中的东西。想想,因为gcc对库的顺序要求 和–as-needed(因为libGalaxyRT.so在mutex.o的左边,所以gcc认为没有 用到它,–as-needed将其忽略),ld忽略libGalaxyRT.so,定位mutex.o的 符号的时候当然会找不到符号的定义!所以‘undefined reference to’这个 错误是正常地!
正确的链接方式是:
- g++ -Wl,--as-needed mutex.o -lGalaxyRT -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o mutex
1.5.2 编译动态库(shared library)的时候会导致一个比较隐晦的错误
编译出来的动态库的时候没有问题,但是加载的时候有“undefined symbol: xxx”这样的错误。假如像这也链接PyGalaxy.so
- g++ -shared -Wl,--as-needed -lGalaxyParser -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o PyGalaxy.so PyGalaxy.o
load PyGalaxy.so的时候会有上面的运行时错误!
简单分析原因:因为libGalaxyParser.so在mutex.o的左边,所以gcc认为没 有用到它,–as-needed将其忽略。但是前面说的动态库符号解析的特点导 致ld认为某些符号是加载的时候才去地址重定位的。但是 libGalaxyParser.so已经被忽略了。所以就算你写上了依赖的库,load的时 候也会找不到符号。但是为什么没有-Wl–as-needed的时候是正确的呢?没 有的话,ld会set NEEDED libGalaxyParser.so(用前面提到的查看动态库 依赖关系的办法可以验证)。load的时候还是可以找到符号的,所以正确。
正确的链接方式是:
- g++ -shared -Wl,--as-needed PyGalaxy.o -lGalaxyParser -lc -lm -ldl -lpthread -L/home/ocaml/lib/ -lrt -o PyGalaxy.so
1.6 对链接顺序导致问题的解决方案
1.6.1 在项目开发过层中尽量让lib是垂直关系,避免循环依赖;越是底层的库,越是往后面写!
例如:
- g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@
这样写可以避免很多问题,这个是在搭建项目的构建环境的过程中需要考虑 清楚地,在编译和链接上浪费太多的生命不值得!
1.6.2 通过-(和-)强制repeat
-(和-),它能够强制"The specified archives are searched repeatedly", 这就是我们要找的啦。比如:
- g++ -shared -Wl,--as-needed PyGalaxy.o Xlinker "-("-lGalaxyParser -lxxx -lrt"-)" -o PyGalaxy.so
简单解释一下,Xlinker是将后面的一个参数传给ld(这里就是 "-("-lGalaxyParser -lxxx -lrt"-)"),然后-(和-)强制repeat当然就 可以找到了。但是这样的repeat需要浪费一些时间。
GCC选项_-Wl,-soname 及 DT_NEEDED 的解释的更多相关文章
- 常用gcc选项
<Linux GCC常用命令> Makefile有三个非常有用的变量.分别是$@,$^,$<代表的意义分别是: $@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件 ...
- gcc选项-g与-rdynamic的异同
摘自http://www.tuicool.com/articles/EvIzUn gcc选项-g与-rdynamic的异同 gcc 的 -g ,应该没有人不知道它是一个调试选项,因此在一般需要进行程序 ...
- 迅为4412开发板Linux驱动教程——总线_设备_驱动注冊流程具体解释
视频下载地址: 驱动注冊:http://pan.baidu.com/s/1i34HcDB 设备注冊:http://pan.baidu.com/s/1kTlGkcR 总线_设备_驱动注冊流程具体解释 • ...
- gcc选项-g与-rdynamic的异同_转
转自:http://www.tuicool.com/articles/EvIzUn gcc 的 -g ,应该没有人不知道它是一个调试选项,因此在一般需要进行程序调试的场景下,我们都会加上该选项,并且根 ...
- GCC选项-Xlinker和-Wl区别
写下给自己备忘,在一次使用GCC的过程中发现了原来传递给链接器ld可以同时使用Xlinker和Wl两种命令,这两个命令都可以正确传递给ld作为使用,现在总结下两者的区别. Xlinker后面跟的参数第 ...
- 【转载】 GNU GCC 选项说明
GCC 1 Section: GNU Tools (1) Updated: 2003/12/05 Sponsor: GCC Casino Winning Content NAME gcc,g++-GN ...
- GCC选项
-g: Debugging Option. 提供给GDB的debugging信息的选项: -fno-omit-frame-pointer: Optimization Option: -Wstrict- ...
- Linux下动态链接库 与gcc 选项
-L 编译时查找动态链接库的路径 -lxxx(小写) e.g -lcudart = link libcudart.so , -I(大写) 头文件的路径 -rpath (-R), 编译时指定链接 ...
- [转]gcc -ffunction-sections -fdata-sections -Wl,–gc-sections 参数详解
背景 有时我们的程序会定义一些暂时使用不上的功能和函数,虽然我们不使用这些功能和函数,但它们往往会浪费我们的ROM和RAM的空间.这在使用静态库时,体现的更为严重.有时,我们只使用了静态库仅有的几个功 ...
随机推荐
- ubuntu 14.04下使用fcitx时将caps lock映射为ctrl
在~/.xprofile中加入 setxkbmap -option caps:ctrl_modifier 要弄成全局的就在 /etc/X11/Xsession.d/ 里面找个文件塞进去. archli ...
- Fence Repair(poj3253)
题目链接:http://poj.org/problem?id=3253 Description Farmer John wants to repair a small length of the fe ...
- spring aop 获取request、response对象
在网上看到有不少人说如下方式获取: 1.在web.xml中添加监听 <listener> <listener-class> org. ...
- A1052. Linked List Sorting
A linked list consists of a series of structures, which are not necessarily adjacent in memory. We a ...
- Linux下,根据FHS定义出来的每个目录的作用
(下表摘自<鸟哥的Linux的私房菜>) 在Linux下,根据FHS定义出来的每个目录应该放置的档案内容为: 目录 应放置档案内容 / 根目录 root (/),一般建议在根目录底下只接目 ...
- spring 项目分开发和生产环境
1.pom 文件修改 <profile> <!-- 本地开发环境 --> <id>dev</id> <properties> <pro ...
- CalISBN.java
/****************************************************************************** * Compilation: javac ...
- 在Linux上安装Elasticsearch Kibaba.md
在Linux上安装Elasticsearch Kibaba Kibana是一个开源为elasticsearch 引擎提供数据和数据分析 1.下载安装 切换到root账户,按顺序依次执行以下命令 rpm ...
- vue2.0 之文本渲染-v-html、v-text
vue2.0 之文本渲染-v-html.v-text 1.index.html代码 <!DOCTYPE html> <html> <head> <meta c ...
- Linux命令之rmdir
rmdir命令 用处:删除文件夹 用法:在终端中输入rmdir加上要删除的文件夹的名字 示例: (我要删除shuyunquan这个文件夹)