diff --git a/notes/pics/compile-pipeline.png b/notes/pics/compile-pipeline.png new file mode 100644 index 00000000..2af854c6 Binary files /dev/null and b/notes/pics/compile-pipeline.png differ diff --git a/notes/pics/dynamic-link.png b/notes/pics/dynamic-link.png new file mode 100644 index 00000000..f0cf19c8 Binary files /dev/null and b/notes/pics/dynamic-link.png differ diff --git a/notes/pics/static-link.png b/notes/pics/static-link.png new file mode 100644 index 00000000..e264f3b7 Binary files /dev/null and b/notes/pics/static-link.png differ diff --git a/notes/计算机操作系统 - 链接.md b/notes/计算机操作系统 - 链接.md index a06caa1b..4a86458d 100644 --- a/notes/计算机操作系统 - 链接.md +++ b/notes/计算机操作系统 - 链接.md @@ -31,7 +31,7 @@ gcc -o hello hello.c 这个过程大致如下: -

+

- 预处理阶段:处理以 # 开头的预处理命令; - 编译阶段:翻译成汇编文件; @@ -45,7 +45,7 @@ gcc -o hello hello.c - 符号解析:每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析的目的是将每个符号引用与一个符号定义关联起来。 - 重定位:链接器通过把每个符号定义与一个内存位置关联起来,然后修改所有对这些符号的引用,使得它们指向这个内存位置。 -

+

## 目标文件 @@ -65,4 +65,4 @@ gcc -o hello hello.c - 在给定的文件系统中一个库只有一个文件,所有引用该库的可执行目标文件都共享这个文件,它不会被复制到引用它的可执行文件中; - 在内存中,一个共享库的 .text 节(已编译程序的机器代码)的一个副本可以被不同的正在运行的进程共享。 -

+

diff --git a/tools/diagram/src/compile-pipeline.svg b/tools/diagram/src/compile-pipeline.svg new file mode 100644 index 00000000..81cc14c1 --- /dev/null +++ b/tools/diagram/src/compile-pipeline.svg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + gcc -o hello hello.c 的四个阶段 + + + + + + hello.c + 源程序 + + 预处理器 cpp + gcc -E + + + + + + hello.i + 预处理后的源程序 + + 编译器 cc1 + gcc -S + + + + + + hello.s + 汇编程序 + + 汇编器 as + gcc -c + + + + + + hello.o + 可重定位目标文件 + + 链接器 ld + + + + + + hello + 可执行目标文件 + + + + + + printf.o 等 + 库中的目标文件 + + 前三步各自可以用 gcc 的选项单独执行;.i、.s 是文本文件,.o 和 hello 是二进制的 ELF 文件。 + diff --git a/tools/diagram/src/dynamic-link.svg b/tools/diagram/src/dynamic-link.svg new file mode 100644 index 00000000..f30c7836 --- /dev/null +++ b/tools/diagram/src/dynamic-link.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + 动态链接:链接时只记录依赖,运行时再加载共享库 + + 构建时 + + 运行时 + + + + + + main.o + + + + + + libmath.so + 只读取符号表 + + ld + + + + + + + + prog + + prog 中只记下依赖 libmath.so 和 libc.so, + 不包含它们的代码 + + execve 加载 prog + + + 动态链接器 ld-linux.so + 把 libmath.so、libc.so + 映射进内存,完成重定位 + 多个进程共享同一份库的代码(.text), + 库更新后不用重新链接程序 + 用 ldd prog 可以查看程序依赖的共享库;找不到某个库时程序启动失败,报 cannot open shared object file。 + diff --git a/tools/diagram/src/static-link.svg b/tools/diagram/src/static-link.svg new file mode 100644 index 00000000..4bdb8ea4 --- /dev/null +++ b/tools/diagram/src/static-link.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + 静态链接:符号解析和重定位 + + + + + + // main.c + int sum(int *a, int n); + int main() { return sum(arr, 2); } + + + + + + // sum.c + int sum(int *a, int n) { +   ... } + + + + + + main.o + 引用 sum,地址未知 + + + + + + sum.o + 定义 sum + + + + 链接器 ld + + + + + + + + + prog + 可执行目标文件 + ① 符号解析:把 main.o 中 + 对 sum 的引用对应到 + sum.o 中的定义 + ② 重定位:合并代码后确定 + sum 的地址,填回 main 中 + 调用指令的地址字段 + 找不到定义时报 undefined reference,出现两个同名的强定义时报 multiple definition,都是在符号解析这一步发现的。 +