第1部分 简介 1
第1章 温故而知新 3
1.1 从Hello World说起 4
1.2 万变不离其宗 5
1.3 站得高,望得远 8
1.4 操作系统做什么 10
1.4.1 不要让CPU打盹 10
1.4.2 设备驱动 11
1.5 内存不够怎么办 14
1.5.1 关于隔离 15
1.5.2 分段(Segmentation) 15
1.5.3 分页(Paging) 17
1.6 众人拾柴火焰高 19
1.6.1 线程基础 19
1.6.2 线程安全 24
1.6.3 多线程内部情况 30
1.7 本章小结 33
第2部分 静态链接 35
第2章 编译和链接 37
2.1 被隐藏了的过程 38
2.1.1 预编译 39
2.1.2 编译 40
2.1.3 汇编 40
2.1.4 链接 41
2.2 编译器做了什么 41
2.2.1 词法分析 42
2.2.2 语法分析 43
2.2.3 语义分析 44
2.2.4 中间语言生成 45
2.2.5 目标代码生成与优化 47
2.3 链接器年龄比编译器长 48
2.4 模块拼装——静态链接 50
2.5 本章小结 53
第3章 目标文件里有什么 55
3.1 目标文件的格式 56
3.2 目标文件是什么样的 58
3.3 挖掘SimpleSection.o 61
3.3.1 代码段 64
3.3.2 数据段和只读数据段 65
3.3.3 BSS段 66
3.3.4 其他段 67
3.4 ELF文件结构描述 68
3.4.1 文件头 69
3.4.2 段表 74
3.4.3 重定位表 79
3.4.4 字符串表 80
3.5 链接的接口——符号 81
3.5.1 ELF符号表结构 82
3.5.2 特殊符号 85
3.5.3 符号修饰与函数签名 86
3.5.4 extern“C” 90
3.5.5 弱符号与强符号 92
3.6 调试信息 94
3.7 本章小结 95
第4章 静态链接 97
4.1 空间与地址分配 98
4.1.1 按序叠加 98
4.1.2 相似段合并 99
4.1.3 符号地址的确定 103
4.2 符号解析与重定位 103
4.2.1 重定位 103
4.2.2 重定位表 106
4.2.3 符号解析 108
4.2.4 指令修正方式 109
4.3 COMMON块 111
4.4 C++相关问题 112
4.4.1 重复代码消除 113
4.4.2 全局构造与析构 114
4.4.3 C++与ABI 115
4.5 静态库链接 117
4.6 链接过程控制 123
4.6.1 链接控制脚本 123
4.6.2 最“小”的程序 124
4.6.3 使用ld链接脚本 127
4.6.4 ld链接脚本语法简介 128
4.7 BFD库 131
4.8 本章小结 132
第5章 Windows PE/COFF 133
5.1 Windows的二进制文件格式PE/COFF 134
5.2 PE的前身——COFF 135
5.3 链接指示信息 139
5.4 调试信息 140
5.5 大家都有符号表 141
5.6 Windows下的ELF——PE 142
5.6.1 PE数据目录 145
5.7 本章小结 146
第3部分 装载与动态链接 147
第6章 可执行文件的装载与进程 149
6.1 进程虚拟地址空间 150
6.2 装载的方式 153
6.2.1 覆盖装入 153
6.2.2 页映射 155
6.3 从操作系统角度看可执行文件的装载 157
6.3.1 进程的建立 157
6.3.2 页错误 159
6.4 进程虚存空间分布 160
6.4.1 ELF文件链接视图和执行视图 160
6.4.2 堆和栈 166
6.4.3 堆的最大申请数量 168
6.4.4 段地址对齐 169
6.4.5 进程栈初始化 171
6.5 Linux内核装载ELF过程简介 173
6.6 Windows PE的装载 175
6.7 本章小结 177
第7章 动态链接 179
7.1 为什么要动态链接 180
7.2 简单的动态链接例子 184
7.3 地址无关代码 188
7.3.1 固定装载地址的困扰 188
7.3.2 装载时重定位 189
7.3.3 地址无关代码 190
7.3.4 共享模块的全局变量问题 197
7.3.5 数据段地址无关性 199
7.4 延迟绑定(PLT) 200
7.5 动态链接相关结构 202
7.5.1 “.interp”段 203
7.5.2 “.dynamic”段 204
7.5.3 动态符号表 206
7.5.4 动态链接重定位表 207
7.5.5 动态链接时进程堆栈初始化信息 211
7.6 动态链接的步骤和实现 214
7.6.1 动态链接器自举 214
7.6.2 装载共享对象 215
7.6.3 重定位和初始化 218
7.6.4 Linux动态链接器实现 219
7.7 显式运行时链接 221
7.7.1 dlopen() 222
7.7.2 dlsym() 223
7.7.3 dlerror() 224
7.7.4 dlclose() 224
7.7.5 运行时装载的演示程序 225
7.8 本章小结 228
第8章 Linux共享库的组织 229
8.1 共享库版本 230
8.1.1 共享库兼容性 230
8.1.2 共享库版本命名 232
8.1.3 SO-NAME 233
8.2 符号版本 235
8.2.1 基于符号的版本机制 236
8.2.2 Solaris中的符号版本机制 237
8.2.3 Linux中的符号版本 239
8.3 共享库系统路径 241
8.4 共享库查找过程 241
8.5 环境变量 242
8.6 共享库的创建和安装 245
8.6.1 共享库的创建 245
8.6.2 清除符号信息 246
8.6.3 共享库的安装 246
8.6.4 共享库构造和析构函数 247
8.6.5 共享库脚本 248
8.7 本章小结 248
第9章 Windows下的动态链接 249
9.1 DLL简介 250
9.1.1 进程地址空间和内存管理 250
9.1.2 基地址和RVA 251
9.1.3 DLL共享数据段 251
9.1.4 DLL的简单例子 251
9.1.5 创建DLL 252
9.1.6 使用DLL 253
9.1.7 使用模块定义文件 254
9.1.8 DLL显式运行时链接 256
9.2 符号导出导入表 257
9.2.1 导出表 257
9.2.2 EXP文件 261
9.2.3 导出重定向 261
9.2.4 导入表 261
9.2.5 导入函数的调用 265
9.3 DLL优化 266
9.3.1 重定基地址(Rebasing) 267
9.3.2 序号 270
9.3.3 导入函数绑定 271
9.4 C++与动态链接 273
9.5 DLL HELL 276
9.6 本章小结 279
第4部分 库与运行库 281
第10章 内存 283
10.1 程序的内存布局 284
10.2 栈与调用惯例 286
10.2.1 什么是栈 286
10.2.2 调用惯例 293
10.2.3 函数返回值传递 299
10.3 堆与内存管理 305
10.3.1 什么是堆 305
10.3.2 Linux进程堆管理 306
10.3.3 Windows进程堆管理 308
10.3.4 堆分配算法 311
10.4 本章小结 315
第11章 运行库 317
11.1 入口函数和程序初始化 318
11.1.1 程序从main开始吗 318
11.1.2 入口函数如何实现 319
11.1.3 运行库与I/O 327
11.1.4 MSVC CRT的入口函数初始化 329
11.2 C/C++运行库 335
11.2.1 C语言运行库 335
11.2.2 C语言标准库 336
11.2.3 glibc与MSVC CRT 340
11.3 运行库与多线程 350
11.3.1 CRT的多线程困扰 350
11.3.2 CRT改进 352
11.3.3 线程局部存储实现 353
11.4 C++全局构造与析构 357
11.4.1 glibc全局构造与析构 358
11.4.2 MSVC CRT的全局构造和析构 364
11.5 fread实现 368
11.5.1 缓冲 369
11.5.2 fread_s 370
11.5.3 fread_nolock_s 371
11.5.4 _read 376
11.5.5 文本换行 377
11.5.6 fread回顾 380
11.6 本章小结 381
第12章 系统调用与API 383
12.1 系统调用介绍 384
12.1.1 什么是系统调用 384
12.1.2 Linux系统调用 385
12.1.3 系统调用的弊端 387
12.2 系统调用原理 388
12.2.1 特权级与中断 388
12.2.2 基于int的Linux的经典系统调用实现 390
12.2.3 Linux的新型系统调用机制 399
12.3 Windows API 401
12.3.1 Windows API概览 402
12.3.2 为什么要使用Windows API 404
12.3.3 API与子系统 408
12.4 本章小结 410
第13章 运行库实现 411
13.1 C语言运行库 412
13.1.1 开始 413
13.1.2 堆的实现 417
13.1.3 IO与文件操作 420
13.1.4 字符串相关操作 425
13.1.5 格式化字符串 426
13.2 如何使用Mini CRT 429
13.3 C++运行库实现 433
13.3.1 new与delete 435
13.3.2 C++全局构造与析构 437
13.3.3 atexit实现 439
13.3.4 入口函数修改 441
13.3.5 stream与string 442
13.4 如何使用Mini CRT++ 446
13.5 本章小结 448
附录A 449
A.1 字节序(Byte Order) 450
A.2 ELF常见段 451
A.3 常用开发工具命令行参考 453
A.3.1 gcc,GCC编译器 453
A.3.2 ld,GNU链接器 454
A.3.3 objdump,GNU目标文件可执行文件查看器 454
A.3.4 cl,MSVC编译器 455
A.3.5 link,MSVC链接器 455
A.3.6 dumpbin,MSVC的COFF/PE文件查看器 456
索引 457