-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

  1. [root@localhost c]# gcc -fPIC -shared -Wl,-soname,libto.so. -o libto.so.1.2 to.c
  2. [root@localhost c]# ls -lh
  3. -rwxr-xr-x root root Jan : libto.so.1.2
  4. [root@localhost c]# ldconfig -n ./
  5. lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.2
  6. -rwxr-xr-x root root .2K Jan : libto.so.1.2
  1. [root@localhost c]# readelf -d libto.so.1.2
  2.  
  3. Dynamic section at offset 0x504 contains entries:
  4. Tag Type Name/Value
  5. 0x00000001 (NEEDED) Shared library: [libc.so.]
  6. 0x0000000e (SONAME) Library soname: [libto.so.]
  7. 0x0000000c (INIT) 0x2cc
  8. 0x0000000d (FINI) 0x4c4
  9. 0x6ffffef5 (GNU_HASH) 0xb4
  10. 0x00000005 (STRTAB) 0x1b4
  11. 0x00000006 (SYMTAB) 0xf4
  12. 0x0000000a (STRSZ) (bytes)
  13. 0x0000000b (SYMENT) (bytes)
  14. 0x00000003 (PLTGOT) 0x15d8
  15. 0x00000002 (PLTRELSZ) (bytes)
  16. 0x00000014 (PLTREL) REL
  17. 0x00000017 (JMPREL) 0x2b4
  18. 0x00000011 (REL) 0x294
  19. 0x00000012 (RELSZ) (bytes)
  20. 0x00000013 (RELENT) (bytes)
  21. 0x6ffffffe (VERNEED) 0x264
  22. 0x6fffffff (VERNEEDNUM)
  23. 0x6ffffff0 (VERSYM) 0x24a
  24. 0x6ffffffa (RELCOUNT)
  25. 0x00000000 (NULL) 0x0

2. 声明libto.so.1,并生成libto.so.1.3

  1. [root@localhost c]# gcc -fPIC -shared -Wl,-soname,libto.so. -o libto.so.1.3 to.c
  2. [root@localhost c]# ls -lh
  3. lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.2
  4. -rwxr-xr-x root root .2K Jan : libto.so.1.2
  5. -rwxr-xr-x root root .2K Jan : libto.so.1.3
  6. [root@localhost c]# ldconfig -n ./
  7. lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.3 #重新ldconfig,指向新的库文件
  8. -rwxr-xr-x root root .2K Jan : libto.so.1.2
  9. -rwxr-xr-x root root .2K Jan : libto.so.1.3
  1. [root@localhost c]# readelf -d libto.so.1.3
  2.  
  3. Dynamic section at offset 0x504 contains entries:
  4. Tag Type Name/Value
  5. 0x00000001 (NEEDED) Shared library: [libc.so.]
  6. 0x0000000e (SONAME) Library soname: [libto.so.]
  7. 0x0000000c (INIT) 0x2cc
  8. 0x0000000d (FINI) 0x4c4
  9. 0x6ffffef5 (GNU_HASH) 0xb4
  10. 0x00000005 (STRTAB) 0x1b4
  11. 0x00000006 (SYMTAB) 0xf4
  12. 0x0000000a (STRSZ) (bytes)
  13. 0x0000000b (SYMENT) (bytes)
  14. 0x00000003 (PLTGOT) 0x15d8
  15. 0x00000002 (PLTRELSZ) (bytes)
  16. 0x00000014 (PLTREL) REL
  17. 0x00000017 (JMPREL) 0x2b4
  18. 0x00000011 (REL) 0x294
  19. 0x00000012 (RELSZ) (bytes)
  20. 0x00000013 (RELENT) (bytes)
  21. 0x6ffffffe (VERNEED) 0x264
  22. 0x6fffffff (VERNEEDNUM)
  23. 0x6ffffff0 (VERSYM) 0x24a
  24. 0x6ffffffa (RELCOUNT)
  25. 0x00000000 (NULL) 0x0

3. 声明libto.so.2,并生成libto.so.1.4

  1. [root@localhost c]# gcc -fPIC -shared -Wl,-soname,libto.so. -o libto.so.1.4 to.c
  2. [root@localhost c]# ls -lh
  3. lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.3
  4. -rwxr-xr-x root root .2K Jan : libto.so.1.2
  5. -rwxr-xr-x root root .2K Jan : libto.so.1.3
  6. -rwxr-xr-x root root .2K Jan : libto.so.1.4
  7. [root@localhost c]# ldconfig -n ./
  8. lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.3 #重新ldconfig,不指向新的库文件,因为新库(1.4)的soname为libto.so.
  9. -rwxr-xr-x root root .2K Jan : libto.so.1.2
  10. -rwxr-xr-x root root .2K Jan : libto.so.1.3
  11. -rwxr-xr-x root root .2K Jan : libto.so.1.4
  12. lrwxrwxrwx root root Jan : libto.so. -> libto.so.1.4
  1. [root@localhost c]# readelf -d libto.so.1.4
  2.  
  3. Dynamic section at offset 0x504 contains entries:
  4. Tag Type Name/Value
  5. 0x00000001 (NEEDED) Shared library: [libc.so.]
  6. 0x0000000e (SONAME) Library soname: [libto.so.]
  7. 0x0000000c (INIT) 0x2cc
  8. 0x0000000d (FINI) 0x4c4
  9. 0x6ffffef5 (GNU_HASH) 0xb4
  10. 0x00000005 (STRTAB) 0x1b4
  11. 0x00000006 (SYMTAB) 0xf4
  12. 0x0000000a (STRSZ) (bytes)
  13. 0x0000000b (SYMENT) (bytes)
  14. 0x00000003 (PLTGOT) 0x15d8
  15. 0x00000002 (PLTRELSZ) (bytes)
  16. 0x00000014 (PLTREL) REL
  17. 0x00000017 (JMPREL) 0x2b4
  18. 0x00000011 (REL) 0x294
  19. 0x00000012 (RELSZ) (bytes)
  20. 0x00000013 (RELENT) (bytes)
  21. 0x6ffffffe (VERNEED) 0x264
  22. 0x6fffffff (VERNEEDNUM)
  23. 0x6ffffff0 (VERSYM) 0x24a
  24. 0x6ffffffa (RELCOUNT)
  25. 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
  1. ocaml@ocaml:~$ readelf -d PyGalaxy.so
  2.  
  3. Dynamic section at offset 0x7dd8 contains 26 entries:
  4. Tag Type Name/Value
  5. 0x0000000000000001 (NEEDED) Shared library: [libGalaxyParser.so]
  6. ...
  7. 0x000000000000000c (INIT) 0x18e0
  8. 0x000000000000000d (FINI) 0x6398
  • ldd工具,如下:
  1. ocaml@ocaml:~$ ldd PyGalaxy.so
  2. linux-vdso.so.1 => (0x00007fffc8ad3000)
  3. libGalaxyParser.so => /home/ocaml/lib/libGalaxyParser.so (0x00007f1736e6d000)
  4. ...

1.3.3 load 动态库过程

基本的说就是符号重定位,然后合并到全局符号表。

1.4 gcc/g++链接时对库的顺序要求

先看看gcc手册对-L和-l的描述

  1. -Ldir
  2. Add directory dir to the list of directories to be searched for -l.
  3.  
  4. -llibrary
  5. -l library
  6. Search the library named library when linking. (The second
  7. alternative with the library as a separate argument is only for POSIX
  8. compliance and is not recommended.)
  9.  
  10. It makes a difference where in the command you write this option;
  11. the linker searches and processes libraries and object files in
  12. the order they are specified. Thus, `foo.o -lz bar.o' searches
  13. library `z' after file foo.o but before bar.o. If bar.o refers to
  14. functions in `z', those functions may not be loaded.
  15.  
  16. The linker searches a standard list of directories for the
  17. library, which is actually a file named liblibrary.a. The linker
  18. then uses this file as if it had been specified precisely by name.
  19.  
  20. The directories searched include several standard system
  21. directories plus any that you specify with -L.
  22.  
  23. Normally the files found this way are library filesarchive files
  24. whose members are object files. The linker handles an archive file
  25. by scanning through it for members which define symbols that have
  26. so far been referenced but not defined. But if the file that is
  27. found is an ordinary object file, it is linked in the usual
  28. fashion. The only difference between using an -l option and
  29. specifying a file name is that -l surrounds library with `lib' and
  30. `.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说明:

  1. --as-needed
  2. --no-as-needed
  3. This option affects ELF DT_NEEDED tags for dynamic libraries mentioned on the command line after the --as-needed
  4. option. Normally the linker will add a DT_NEEDED tag for each dynamic library mentioned on the command line,
  5. regardless of whether the library is actually needed or not. --as-needed causes a DT_NEEDED tag to only be emitted for
  6. a library that satisfies an undefined symbol reference from a regular object file or, if the library is not found in
  7. the DT_NEEDED lists of other libraries linked up to that point, an undefined symbol reference from another dynamic
  8. library. --no-as-needed restores the default behaviour.
  9.  
  10. --add-needed
  11. --no-add-needed
  12. These two options have been deprecated because of the similarity of their names to the --as-needed and --no-as-needed
  13. options. They have been replaced by --copy-dt-needed-entries and --no-copy-dt-needed-entries.
  14.  
  15. --copy-dt-needed-entries
  16. --no-copy-dt-needed-entries
  17. This option affects the treatment of dynamic libraries referred to by DT_NEEDED tags inside ELF dynamic libraries
  18. mentioned on the command line. Normally the linker won't add a DT_NEEDED tag to the output binary for each library
  19. mentioned in a DT_NEEDED tag in an input dynamic library. With --copy-dt-needed-entries specified on the command line
  20. however any dynamic libraries that follow it will have their DT_NEEDED entries added. The default behaviour can be
  21. restored with --no-copy-dt-needed-entries.
  22.  
  23. This option also has an effect on the resolution of symbols in dynamic libraries. With --copy-dt-needed-entries
  24. dynamic libraries mentioned on the command line will be recursively searched, following their DT_NEEDED tags to other
  25. libraries, in order to resolve symbols required by the output binary. With the default setting however the searching
  26. of dynamic libraries that follow it will stop with the dynamic library itself. No DT_NEEDED links will be traversed to
  27. resolve symbols.

再看看ld的帮助

  1. --add-needed Set DT_NEEDED tags for DT_NEEDED entries in following dynamic libs
  2. --no-add-needed Do not set DT_NEEDED tags for DT_NEEDED entries in following dynamic libs
  3.  
  4. --as-needed Only set DT_NEEDED for following dynamic libs if used
  5. --no-as-needed Always set DT_NEEDED for following dynamic libs

关键的看–as-needed,意思是说:只给用到的动态库设置DT_NEEDED。比如:

  1. g++ -shared PyGalaxy.o -lGalaxyParser -lxxx -lrt -o PyGalaxy.so

像这样链接一个PyGalaxy.so的时候,假设PyGalaxy.so里面用到了libGalaxyParser.so但是没 有用到libxxx.so。查看依赖关系如下:

  1. ocaml@ocaml:~$ readelf -d PyGalaxy.so
  2.  
  3. Dynamic section at offset 0x7dd8 contains 26 entries:
  4. Tag Type Name/Value
  5. 0x0000000000000001 (NEEDED) Shared library: [libGalaxyParser.so]
  6. 0x0000000000000001 (NEEDED) Shared library: [libxxx.so]
  7. ...

当开启–as-needed的时候,像

  1. g++ -shared -Wl,--as-needed PyGalaxy.o -lGalaxyParser -lxxx -lrt -o PyGalaxy.so

这样链接PyGalaxy.so的时候,查看依赖关系如下:

  1. ocaml@ocaml:~$ readelf -d PyGalaxy.so
  2.  
  3. Dynamic section at offset 0x7dd8 contains 26 entries:
  4. Tag Type Name/Value
  5. 0x0000000000000001 (NEEDED) Shared library: [libGalaxyParser.so]
  6. ...

–as-needed就是忽略链接时没有用到的动态库,只将用到的动态库set NEEDED。

开启–as-needed的一些常见的问题:

1.5.1 链接主程序模块或者是静态库的时的‘undefined reference to: xxx’

  1. 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’这个 错误是正常地!

正确的链接方式是:

  1. 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

  1. 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的时候还是可以找到符号的,所以正确。

正确的链接方式是:

  1. 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是垂直关系,避免循环依赖;越是底层的库,越是往后面写!

例如:

  1. g++ ... obj($?) -l(上层逻辑lib) -l(中间封装lib) -l(基础lib) -l(系统lib) -o $@

这样写可以避免很多问题,这个是在搭建项目的构建环境的过程中需要考虑 清楚地,在编译和链接上浪费太多的生命不值得!

1.6.2 通过-(和-)强制repeat

-(和-),它能够强制"The specified archives are searched repeatedly", 这就是我们要找的啦。比如:

  1. 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 的解释的更多相关文章

  1. 常用gcc选项

    <Linux GCC常用命令> Makefile有三个非常有用的变量.分别是$@,$^,$<代表的意义分别是: $@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件 ...

  2. gcc选项-g与-rdynamic的异同

    摘自http://www.tuicool.com/articles/EvIzUn gcc选项-g与-rdynamic的异同 gcc 的 -g ,应该没有人不知道它是一个调试选项,因此在一般需要进行程序 ...

  3. 迅为4412开发板Linux驱动教程——总线_设备_驱动注冊流程具体解释

    视频下载地址: 驱动注冊:http://pan.baidu.com/s/1i34HcDB 设备注冊:http://pan.baidu.com/s/1kTlGkcR 总线_设备_驱动注冊流程具体解释 • ...

  4. gcc选项-g与-rdynamic的异同_转

    转自:http://www.tuicool.com/articles/EvIzUn gcc 的 -g ,应该没有人不知道它是一个调试选项,因此在一般需要进行程序调试的场景下,我们都会加上该选项,并且根 ...

  5. GCC选项-Xlinker和-Wl区别

    写下给自己备忘,在一次使用GCC的过程中发现了原来传递给链接器ld可以同时使用Xlinker和Wl两种命令,这两个命令都可以正确传递给ld作为使用,现在总结下两者的区别. Xlinker后面跟的参数第 ...

  6. 【转载】 GNU GCC 选项说明

    GCC 1 Section: GNU Tools (1) Updated: 2003/12/05 Sponsor: GCC Casino Winning Content NAME gcc,g++-GN ...

  7. GCC选项

    -g: Debugging Option. 提供给GDB的debugging信息的选项: -fno-omit-frame-pointer: Optimization Option: -Wstrict- ...

  8. Linux下动态链接库 与gcc 选项

    -L 编译时查找动态链接库的路径 -lxxx(小写)  e.g -lcudart   = link libcudart.so  , -I(大写) 头文件的路径 -rpath (-R), 编译时指定链接 ...

  9. [转]gcc -ffunction-sections -fdata-sections -Wl,–gc-sections 参数详解

    背景 有时我们的程序会定义一些暂时使用不上的功能和函数,虽然我们不使用这些功能和函数,但它们往往会浪费我们的ROM和RAM的空间.这在使用静态库时,体现的更为严重.有时,我们只使用了静态库仅有的几个功 ...

随机推荐

  1. ubuntu 14.04下使用fcitx时将caps lock映射为ctrl

    在~/.xprofile中加入 setxkbmap -option caps:ctrl_modifier 要弄成全局的就在 /etc/X11/Xsession.d/ 里面找个文件塞进去. archli ...

  2. Fence Repair(poj3253)

    题目链接:http://poj.org/problem?id=3253 Description Farmer John wants to repair a small length of the fe ...

  3. spring aop 获取request、response对象

    在网上看到有不少人说如下方式获取: 1.在web.xml中添加监听 <listener>          <listener-class>              org. ...

  4. A1052. Linked List Sorting

    A linked list consists of a series of structures, which are not necessarily adjacent in memory. We a ...

  5. Linux下,根据FHS定义出来的每个目录的作用

    (下表摘自<鸟哥的Linux的私房菜>) 在Linux下,根据FHS定义出来的每个目录应该放置的档案内容为: 目录 应放置档案内容 / 根目录 root (/),一般建议在根目录底下只接目 ...

  6. spring 项目分开发和生产环境

    1.pom 文件修改 <profile> <!-- 本地开发环境 --> <id>dev</id> <properties> <pro ...

  7. CalISBN.java

    /****************************************************************************** * Compilation: javac ...

  8. 在Linux上安装Elasticsearch Kibaba.md

    在Linux上安装Elasticsearch Kibaba Kibana是一个开源为elasticsearch 引擎提供数据和数据分析 1.下载安装 切换到root账户,按顺序依次执行以下命令 rpm ...

  9. vue2.0 之文本渲染-v-html、v-text

    vue2.0 之文本渲染-v-html.v-text 1.index.html代码 <!DOCTYPE html> <html> <head> <meta c ...

  10. Linux命令之rmdir

    rmdir命令 用处:删除文件夹 用法:在终端中输入rmdir加上要删除的文件夹的名字 示例: (我要删除shuyunquan这个文件夹)