《程序员的自我修养 — 链接,装载与库》这本书被很多人推荐,翻了一下,好像确实是一本很有意思的书,值的推荐。我会陆陆续续把读书笔记写在这里。
目录:
- ELF文件结构的描述
- 静态链接的过程
- 链接静态库
1. ELF文件结构的描述
什么是ELF ?
ELF ( Executable Linkable Format 的缩写),简单的来讲 ELF 文件包括: 目标文件(.o 文件) + 可执行文件 (a.out) + 静态库/动态库(.so文件) 。
ELF的结构是什么?
ELF 里面应该存放在二进制的代码(因为它已经是被编译和汇编过的代码了),这个代码应该包括着 代码 + 数据。在ELF文件结构里面,他们分别被存放到不同的段里面。
- ELF 里面几个重要的段 :
.text (存放 : 代码 和 局部变量)
.bss (存放 : 未初始化的数据)
.data (存放:初始化的数据)
.rodata(read only data) (存放 : 只读数据)
.rel.text (存放 : 重定位数据)
对于一段代码,不同的部分放在ELF文件里面的不同部分。如下图所示:
- 查看目标文件的符号表
使用nm命令查看
nm 命令查看#include <stdio.h> int val2=100; int main(){ int val=100; printf("hello world \n "); return 0; }
nm test1-2.o 0000000000000000 T main U printf 0000000000000000 D val2
2. 静态链接的过程
静态链接的一般会涉及到多个文件(多个.c 文件),所以静态链接的过程一般有两步:
-
空间和地址分配
1.1 合并ELF文件
对于每个.c 文件,编译成 目标文件之后,都会对应一个ELF 文件。链接器的第一步就是合并ELF文件。
1.2 分配空间
对于一个合并好的ELF文件,下一步就是分配空间(这个空间指的是虚拟内存空间)。因为只有分配了空间之后,对应的变量才会有自己的地址。分配空间如下图所示:
1.3 符号地址的确定
在分配好空间之后,对应的符号才有了自己的地址。那么,链接器就知道了各个符号的地址(这个地址指的是虚拟地址)。
-
符号解析与重定位
在知道了符号真实的地址之后,就要对原来符号的地址进行修改。
是因为这样的,对于如下的代码:extern int shared; int main(){ int a=100; swap(&a,&shared); }
在刚开始编译的时候,生成的汇编代码里面,其实并不知道变量shared和外部定义的函数swap的地址。所以,此时的汇编代码的地址是一个相对的地址。
所以,符号解析的第一步就是找到所有的未定义的变量
nm xxx.o
然后,把他们的原来的相对地址修改为在1.3 步骤里面找到的虚拟地址。这样编译器的工作就顺利完成了。
3. 静态库链接
#include <stdio.h>
int main(){
int val=100;
printf("hello world \n ");
return 0;
}
对于上面的代码,虽然没有调用自定义的函数,但是使用了printf函数。这个函数是库函数,也就是链接的时候,需要把这个函数的定义链接进来。(这个过程不需要我们自己来做,是系统替我完成的)
静态库可以理解为一堆目标文件(.o 文件)的集合。
将链接的需要的目标文件 输出出来:
gcc -static --verbose -fno-builtin test1-1.c
输出:
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.9' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
COLLECT_GCC_OPTIONS='-static' '-v' '-fno-builtin' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/5/cc1 -quiet -v -imultiarch x86_64-linux-gnu test1-1.c -quiet -dumpbase test1-1.c -mtune=generic -march=x86-64 -auxbase test1-1 -version -fno-builtin -fstack-protector-strong -Wformat -Wformat-security -o /tmp/cc3R93V6.s
GNU C11 (Ubuntu 5.4.0-6ubuntu1~16.04.9) version 5.4.0 20160609 (x86_64-linux-gnu)
compiled by GNU C version 5.4.0 20160609, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3
GGC heuristics: --param ggc-min-expand=96 --param ggc-min-heapsize=124704
ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/5/../../../../x86_64-linux-gnu/include"
#include "..." search starts here:
#include <...> search starts here:
/usr/lib/gcc/x86_64-linux-gnu/5/include
/usr/local/include
/usr/lib/gcc/x86_64-linux-gnu/5/include-fixed
/usr/include/x86_64-linux-gnu
/usr/include
End of search list.
GNU C11 (Ubuntu 5.4.0-6ubuntu1~16.04.9) version 5.4.0 20160609 (x86_64-linux-gnu)
compiled by GNU C version 5.4.0 20160609, GMP version 6.1.0, MPFR version 3.1.4, MPC version 1.0.3
GGC heuristics: --param ggc-min-expand=96 --param ggc-min-heapsize=124704
Compiler executable checksum: d079eab342c322d6be59e8628e10ae67
COLLECT_GCC_OPTIONS='-static' '-v' '-fno-builtin' '-mtune=generic' '-march=x86-64'
as -v --64 -o /tmp/cc5siGfj.o /tmp/cc3R93V6.s
GNU assembler version 2.26.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Ubuntu) 2.26.1
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/5/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib/:/lib/x86_64-linux-gnu/:/lib/../lib/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/5/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-static' '-v' '-fno-builtin' '-mtune=generic' '-march=x86-64'
/usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccTObSEv.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=/ --build-id -m elf_x86_64 --hash-style=gnu --as-needed -static -z relro /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbeginT.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. /tmp/cc5siGfj.o --start-group -lgcc -lgcc_eh -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o