2018/12/3 C++程序链接过程简介 杨森 2018/12/3
2018/12/3 大纲 编译器与链接器 目标文件 静态链接 动态链接 常用工具介绍 2018/12/3
2018/12/3 大纲 编译器与链接器 目标文件 静态链接 动态链接 常用工具介绍 2018/12/3
被隐藏的过程 hello.cc a.out GCC #include <stdio.h> int main() { printf(“hello world\n”); return 0; } hello.cc a.out GCC 2018/12/3
被隐藏的过程 Processing (cpp) Compilation (cc1plus) Assembly (as) Linking Source Code hello.c Processing (cpp) Preprocessed hello.i Compilation (cc1plus) Header Files stdio.h … Static Library libc.a Object File hello.o Assembly (as) Assembly hello.s Linking (ld) Executable a.out 2018/12/3
链接器的产生 很久很久以前,在一个非常遥远的银河系…… 人们编写程序时,将所有源代码都写在同一个文件中,发展到后来一个程序源代码的文件长达数百万行,以至于这个地方的人类已经没有能力维护这个程序了。人们开始寻找新的办法,一场新的软件开发革命即将爆发…… 2018/12/3
链接器的产生 jmp foo 0 0001 0100 1 … 2 … 3 … 4 1000 0111 5 … 0 0001 0101 1 … 2 … 3 … 4 … 5 1000 0111 6 … 重定位 符号 2018/12/3
链接器的产生 … xxxxxx … 地址和空间分配 符号决议 重定位 2018/12/3
2018/12/3 大纲 编译器与链接器 目标文件 静态链接 动态链接 常用工具介绍 2018/12/3
什么是目标文件 编译器编译源代码后生成的文件 (.obj .o) 目标文件和可执行文件的结构是一样的 常见格式:PE(Portable Executable), ELF(Executable Linkable Format),.COM 静态链接库 2018/12/3
ELF文件分类 ELF文件类型 说明 实例 可重定位文件 (Relocatable File) Linux的.o Windows的.obj 这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可以归为这一类 Linux的.o Windows的.obj 可执行文件 (Executable File) 这类文件包含了可以直接执行的程序,它的代表就是ELF可执行文件 /bin/bash Windows的.exe 共享目标文件 (Shared Object File) 这种文件包含了代码和数据,既可以链接成为新的目标文件,也可以链接成为可执行文件 Linux的.so Windows的DLL 核心转储文件 (Core Dump File) 当系统以外终止时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转储到核心转储文件 Linux下的core dump 2018/12/3
目标文件的结构 为什么要将数据和代码分开存放? File Header .text section .data section int global_init_var = 84; int global_uinit_var; File Header .text section .data section .bss section void func1(int i) { printf("%d\n", i); } int main(void) 为什么要将数据和代码分开存放? static int static_var = 85; static int static_var2; int a = 1; int b; func1(global_init_var + static_var + a + b); return 0; } 2018/12/3
目标文件的结构 文件头(/usr/include/elf.h):ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF重定位类型…… 段表(e_shoff):段名(sh_name),段类型(sh_type),标志位(sh_flag),链接信息(sh_link, sh_info)…… 重定位表 字符串表 偏移 +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 \0 h e l o w r +10 d M y v a i b +20 偏移 字符串 空字符串 1 helloworld 6 world 12 Myvariable 2018/12/3
链接的接口——符号 将函数和变量统称为符号 本地定义的全局符号、外部符号(External Symbol)、局部符号、段名、行号 符号修饰和函数签名 弱符号和强符号 extern “C” 2018/12/3
2018/12/3 大纲 编译器与链接器 目标文件 静态链接 动态链接 常用工具介绍 2018/12/3
空间与地址分配 File Header File Header .text section .text section Object File A Ouput File File Header .text section .data section .bss section File Header .text section .data section .bss section Object File A File Header .text section .data section .bss section Object File B File Header .text section .data section .bss section Object File B 2018/12/3
空间与地址分配 Object File A Ouput File File Header .text section .data section .bss section File Header .text Object File B .data File Header .text section .data section .bss section .bss 2018/12/3
符号解析与重定位 /* a.c */ extern int shared; int main() { int a = 100; swap(&a, &shared); } /* b.c */ int shared =1; void swap(int* a, int* b) *a ^= *b ^= *a ^= *b; $ ld a.o a.o: In function `main`: a.c: (.text+0x1c):undefined reference to `shared` a.c: (.text+0x27):undefined reference to `swap` $ readelf -s a.o Symbol table ‘.symtab’ contains 10 entries: Num : Value Size Type Bind Vis Ndx Name 8 : 00000000 0 NOTYPE GLOBAL DEFAULT UND shared 2018/12/3
符号解析与重定位 指令地址修正 /* a.c */ extern int shared; int main() { int a = 100; swap(&a, &shared); } /* b.c */ int shared =1; void swap(int* a, int* b) *a ^= *b ^= *a ^= *b; $ objdump –o a.o 26 : e8 fc ff ff ff call 27 <main+27> 2b : 指令地址修正 80480ba : e8 09 00 00 00 call 80480c8 <swap> 80480bf 2018/12/3
COMMON块 链接器无法处理多个符号定义类型不一致的情况(三种情况) 编译器将未初始化的全局变量定义作为弱符号处理 若有强符号,以强符号为准 若有弱符号空间大于强符号,有警告 ld : warning: alignment 4 of symbol `global` in a.o is smaller than 8 in b.o 2018/12/3
C++相关问题 重复代码消除 函数级别链接(VS:/Gy GCC:-ffunction-sections) 全局构造与析构(.init .fini) ABI:Application Binary Interface 2018/12/3
静态库链接 一组目标文件的集合(ar lib.exe) Linker libc.a printf.o hello.o vfprintf.o stdio.o Other .o files hello.o Linker Excutable Program 2018/12/3
静态库链接 一组目标文件的集合(ar lib.exe) C运行库 相关DLL 描述 Libcmt.lib 多线程静态库 Msvcrt.lib Msvcr90.dll 多线程动态库 Libcmtd.lib 多线程静态调试库 Msvcrtd.lib Msvcr90d.dll 多线程动态调试库 2018/12/3
2018/12/3 大纲 编译器与链接器 目标文件 静态链接 动态链接 常用工具介绍 2018/12/3
为什么要动态链接 内存和磁盘空间 程序开发和发布 程序可扩展性与兼容性(插件) DLL HELL ld-2.6.so 2018/12/3
地址无关代码 装载时重定位 (-shared):无法共享指令 地址无关代码(-fPIC):Position-independent Code 模块内部的函数调用、跳转 模块内部的数据访问 模块外部的函数调用、跳转 模块外部的数据访问 static int a; extern int b; extern void ext(); void bar() { a = 1; b = 2; } void foo() bar(); ext(); Type 2 Type 4 Type 1 Type 3 2018/12/3
GOT(Global offset table) 地址无关代码 .data 0x20002000 Int b = 100 .text … 0x20002000 … … .data GOT(Global offset table) .text 0x10000000 Process Virtual Space 2018/12/3
动态链接步骤 装载共享对象 重定位和初始化 Bootstrap 动态链接器不能依赖于任何共享对象 动态链接器所需全局和静态变量由自己重定位 全局符号表 遍历依赖 装载共享对象 .init .finit 重定位和初始化 2018/12/3
2018/12/3 大纲 编译器与链接器 目标文件 静态链接 动态链接 常用工具介绍 2018/12/3
常用工具介绍 gcc :GCC编译器 ld :GNU链接器 objdump readelf: GNU目标文件可执行文件查看器 cl :MSVC编译器 link :MSVC链接器 dumpbin :MSVC的PE文件查看器 ar lib.exe :静态库查看器 2018/12/3
谢 谢! 2018/12/3