1.目标文件生链接成可执行文件依赖的库和目标文件
使用 ld 链接目标文件生成可执行文件,用于取代命令 g++,仅仅用于学习 ld 命令的使用,不用于实际的项目编译。
首先看一个简单的 C++ 源文件 main.cpp。
//
//@file:main.cpp
//
#include <iostream>
using namespace std;
int main()
{
cout<<"hello world"<<endl;
}
当我使用命令g++ -c
将其编译为目标文件main.o时,使用命令ld main.o
会报如下错误。
[root@TENCENT64 ~]# ld main.o -o main.out
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400120
main.o: In function `main':
main.i:(.text+0xa): undefined reference to `std::cout'
main.i:(.text+0xf): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)'
main.i:(.text+0x14): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)'
main.i:(.text+0x1c): undefined reference to `std::ostream::operator<<(std::ostream& (*)(std::ostream&))'
main.o: In function `__static_initialization_and_destruction_0(int, int)':
main.i:(.text+0x4a): undefined reference to `std::ios_base::Init::Init()'
main.i:(.text+0x4f): undefined reference to `__dso_handle'
main.i:(.text+0x59): undefined reference to `std::ios_base::Init::~Init()'
main.i:(.text+0x5e): undefined reference to `__cxa_atexit'
ld: main.out: hidden symbol `__dso_handle' isn't defined
ld: final link failed: Bad value
使用ld进行链接,需要注意添加较长的命令选项,不然会报上面的链接错误。Linux系统,链接目标文件生成可执行文件比我们想象的要复杂许多,因为生成一个C++可执行文件,需要依赖很多系统库和相关的目标文件,比如C语言库libc.a。使用g++ -v
命令可以查看最后一行collect2使用的命令选项,进而了解生成可执行文所需的相关依赖。
[root@TENCENT64 ~]# g++ -v main.o
...
usr/libexec/gcc/x86_64-redhat-linux/4.8.5/collect2 --build-id --no-add-needed --eh-frame-hdr --hash-style=gnu -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crt1.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crti.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtbegin.o -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64 -L/lib/../lib64 -L/usr/lib/../lib64 -L/usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../.. -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-redhat-linux/4.8.5/crtend.o /usr/lib/gcc/x86_64-redhat-linux/4.8.5/../../../../lib64/crtn.o main.o
所以,使用上面的命令选项作用于ld命令,即可完成目标文件的链接。简单地总结,至少有如下的库和目标文件被链接到可执行文件中。
ld-linux-x86-64.so.2
crt1.o
crti.o
crtbegin.o
libstdc++.a
libm.a
libgcc_s.a
libgcc.a
libc.a
crtend.o
crtn.o
2.collect2是什么
从上面的链接过程可以看出,最终编译器 g++ 调用链接器 collect2 来完成链接工作。为什么是 collect2 而不是 ld 呢?实际上 collect2 是对 ld 的封装,最终还是要调用 ld 来完成链接工作。与 ld 不同的是在完成链接工作后,collect2 还会对链接结果做一些处理,主要是收集所有与程序初始化相关的信息并且构造初始化的结构。简单来说,可以把 collect2 看作是链接器 ld。
参考文献
[1] stackoverflow.How to link C++ object files with ld
[2] 余甲子,石凡,潘爱民.程序员的自我修养——链接、装载与库[M].北京:机械工业出版社,2009.C4.5静态库链接.P120-122