ubuntu 12.04编译链接库顺序问题
1. 现象描述
在ubuntu 12.04的编译程序过程中,经常发现原来在ubuntu9.10可以编译过的程序在12.04编译不通过,报“undefined reference toxxxxx”的错误,如下图所示:
2. 问题分析
经过简单尝试,发现使用以下的方式就可以编译过去:
gcc -o xpointer xpointer.c -lX11
那么:“gcc -lX11 -o xpointer xpointer.c”与”gcc -o xpointer xpointer.c -lX11”到底有什么区别呢?从下面两张图可以看出:
编译过与不过,就主要看“collect2”这个程序的参数顺序,
“collect2 … -lX11xxx.o -lgcc…”就不通过,“collect2 …xxx.o -lX11-lgcc…”就可以编译通过。
那么collect2这个程序的作用是什么呢?
gcc安装的各个部分:
| g++ | c++编译器,链接时使用c++库 |
| gcc | c编译器,链接时使用c库 |
| cc1 | 实际的c编译器 |
| cc1plus | 实际的c++编译器 |
| collect2 | 使用collect2产生特定的全局初始化代码,后台处理是传递参数给ld完成实际的链接工作。 |
| crt0.o | 初始化和结束代码 |
| libgcc | 平台相关的库 |
也就是说,后台真正处理的是ld,collect2只是个传递参数的。从<AnIntroduction to GCC >(http://www.network-theory.co.uk/docs/gccintro/gccintro_18.html)中有这样一句话“Thetraditional behavior of linkers is to search for external functions from leftto right in the libraries specified on the command line. This means that alibrary containing the definition of a function should appear after any sourcefiles or object files which use it.”
从上面的描述我们能够比较清晰的了解到库的先后顺序对链接的影响,但遗留一个问题,就是为什么在ubuntu 9.10上编译没有问题呢?有没有办法去掉库的先后顺序对连接的影响呢?当然,最终还得回到ld才能解决问题,从man ld可以看出有几个选项影响这个:
--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. --as-needed causes a DT_NEEDED tag to only be emitted for a library that satisfies a symbol reference from regular objects which is undefined at the point that the library was linked, or, if the library is not found in the DT_NEEDED lists of other libraries linked up to that point, a reference from another dynamic library. --no-as-needed restores the default behaviour.
--add-needed --no-add-needed This option affects the treatment of dynamic libraries from ELF DT_NEEDED tags in dynamic libraries mentioned on the command line after the --no-add-needed option. Normally, the linker will add a DT_NEEDED tag for each dynamic library from DT_NEEDED tags. --no-add-needed causes DT_NEEDED tags will never be emitted for those libraries from DT_NEEDED tags. --add-needed restores the default behaviour. |
大体的意思是:
--add-needed Set DT_NEEDED tags forDT_NEEDED entries in following dynamiclibs
--no-add-needed Do not set DT_NEEDED tags forDT_NEEDED entries in following dynamiclibs
--as-needed Only set DT_NEEDED forfollowing dynamic libs if used
--no-as-needed Always set DT_NEEDED forfollowing dynamic libs
关键的看--as-needed,意思是说:只给用到的动态库设置DT_NEEDED。(详细的分析可以参考:http://blog.csdn.net/littlegrizzly/article/details/7599868)
回到我们之前的问题,“collect2 …-lX11 xxx.o -lgcc…”就不通过,“collect2 …xxx.o -lX11 -lgcc…”就可以编译通过?因为gcc对库的顺序要求和--as-needed(因为libX11.so在xxx.o的左边,所以gcc认为没有用到它,--as-needed将其忽略),ld忽略libX11.so,定位xxx.o的符号的时候当然会找不到符号的定义!所以‘undefined reference to’这个错误是正常地!为了验证上述的想法,我们需要看一下ubuntu 9.10及12.04对--as-needed的使用方式:
Ubuntu9.10
/usr/lib/gcc/i486-linux-gnu/4.4.1/collect2--build-id --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker/lib/ld-linux.so.2 -o xpointer -z relro/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/crt1.o/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/crti.o/usr/lib/gcc/i486-linux-gnu/4.4.1/crtbegin.o-L/usr/lib/gcc/i486-linux-gnu/4.4.1 -L/usr/lib/gcc/i486-linux-gnu/4.4.1-L/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib -L/lib/../lib-L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.4.1/../../..-L/usr/lib/i486-linux-gnu -lX11 /tmp/ccUZ403F.o -lgcc --as-needed -lgcc_s--no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed/usr/lib/gcc/i486-linux-gnu/4.4.1/crtend.o/usr/lib/gcc/i486-linux-gnu/4.4.1/../../../../lib/crtn.o
Ubuntu12.04
/usr/lib/gcc/i686-linux-gnu/4.6/collect2--sysroot=/ --build-id --no-add-needed --as-needed --eh-frame-hdr -melf_i386 --hash-style=gnu -dynamic-linker /lib/ld-linux.so.2 -z relro -oxpointer /usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crt1.o/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crti.o/usr/lib/gcc/i686-linux-gnu/4.6/crtbegin.o -L/usr/lib/gcc/i686-linux-gnu/4.6-L/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu -L/usr/lib/gcc/i686-linux-gnu/4.6/../../../../lib-L/lib/i386-linux-gnu -L/lib/../lib -L/usr/lib/i386-linux-gnu -L/usr/lib/../lib-L/usr/lib/gcc/i686-linux-gnu/4.6/../../.. -lX11 /tmp/ccvH7BLd.o -lgcc--as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed/usr/lib/gcc/i686-linux-gnu/4.6/crtend.o/usr/lib/gcc/i686-linux-gnu/4.6/../../../i386-linux-gnu/crtn.o
可见,Ubuntu 12.04对链接的顺序有要求是因为gcc在传递参数给ld时,默认加了--no-add-needed --as-needed两个参数。
分析到这里已经基本结束,但是还有一个问题,就是有没有办法去掉ubuntu 12.04 gcc的这种默认行为呢,也就是说能否给ld传递参数,加上--no-as-needed的呢?Google一下很快就能够找到答案: