gcc(g++)编译的顺序问题 下载本文

内容发布更新时间 : 2024/5/3 2:43:21星期一 下面是文章的全部内容请认真阅读。

今天编译遇到一个奇怪的问题, main.cpp中有main函数,main.cpp中需要用到xxx库 g++ -o main -lxxx main.cpp

这样编译不过,报错main.cpp中某行有undefined reference(xxx中的)。

把顺序调整一下:

g++ -o main main.cpp -lxxx

能编过了.

原因是gcc/g++ 编译是,源文件不能在关联的库后面。 编译器从左到右扫描,需要两个表A,B

A:里面记录了所有没有被resolve的symbol B: 里面记录了所有需要链接的库

一开始A,B两个表都是空的 从左到右扫描的时候的过程如下

1.如果是一个obj文件,则将不能被resolve的symbol加入到A表里面

2.如果是一个库文件,则查找其中是否有A表中的symbol,如果有,则从A表中去掉该symbol(因为已经被当前库resolve了),同时将该库加入到B表中

扫描结束后如果A表非空,则会报错

现在的编译器默认都是进行一次扫描的,所以你需要将.o文件放在前面,否则你这个A表直到最后才生成,但这个时候所有的库已经扫描完了会造成A表非空

具体的可能会有些不一样,但是大体原理基本就是这样的。

摘录别人的分析:

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的帮助