10 编译器原理与技术---编译系统和运行系统

这一节主要向我们介绍了编译器原理与技术中的编译系统和运行系统。首先向我们介绍了C语言编译系统,然后向我们介绍了Java语言的运行系统,然后是无用单元的收集,最后向我们介绍了多样编译以及反编译技术。
展开查看详情

1.第 10 章 编译 系统和运行系统 编译原理与技术

2.C 语言编译系统 预处理器、编译器、汇编器、连接器 目标文件的格式、静态库、动态连接 Java 运行系统 无用单元收集(垃圾收集) 多样化编译与反编译技术 引入 本章的目的 掌握从源程序到可执行目标程序的实际处理过程 对实际参与软件开发是直接有用的 10 编译 系统和运行系统 内容提要

3.10.1 C 语言编译系统 第 10 章 编译 系统和运行系统

4.C 源程序可以分成 若干 个 模块 ( 文件 ) 分别 进行预处理、 编译 和 汇编、形成可 重定位 的 目标文件 目标 文件和必要的库 文 件 连接成一个可执行 的 目标 文件 gcc 和 cc 是编译驱动 程 序 的名字 10.1 C 语言编译系统 预处理器 源程序 修改后的源程序 可重定位的目标程序 可重定位的 目标文件 库 编译器 汇编器 汇编程序 连接器 可执行的目标程序

5.1 0.1.1 预处理器 gcc 首先调用 预处理器 cpp , 将源程序文件翻译成一个 ASCII 中间文件,它是经修改后的源程序 cpp 实现以下功能 文件包含 宏展开 条件编译 10.1 C 语言编译系统

6.10.1 C 语言编译系统 main.c (1) #if 1 (2) int buf [2]; (3) #else (4) int buf [2] = {10,20}; (5) # endif (6) void swap(); (7) #define A buf [0] (8) int main() (9) { (10) scanf ("%d, %d", buf , buf+1); (11)swap(); (12) printf ("%d, %d ", A , buf [1]); (13)return 0; (14) } swap.c (1) extern int buf [2]; (2) int *bufp0 = buf ; (3) int *bufp1; (4) void swap() (5) { (6) int temp; (7)bufp1 = buf+1; (8)temp = *bufp0; (9)*bufp0 = *bufp1; (10)*bufp1 = temp; (11) }

7.1 0.1.2 汇编器 gcc 系统 的编译器 cc1 产生汇编代码 最简单的汇编器对输入进行两遍扫描 例 一段汇编代码 . L2 : cmpl $ 0 , - 4(% ebp ) jne .L6 jmp .L11 .L11 : cmpl $ 0 , - 8(% ebp ) jne .L6 jmp .L12 .L12: jmp .L5 .p2align 4 ,,7 .L6 : 10.1 C 语言编译系统 第一编扫描建立符号 表,包括 代码标号. L2, .L11 等 第二遍扫描依据符号 表中 的信息来产生可 重定位 代码

8.一 遍扫描完成汇编代码到可重定位目标代码的翻译也是完全可能的 例 一段汇编代码 . L2: cmpl $0, - 4(% ebp ) jne .L6 建 .L6 的回填链 jmp . L11 .L11 : cmpl $0, -8(% ebp ) jne .L6 加入 .L6 的回填链 jmp .L12 .L12: jmp .L5 .p2align 4 ,,7 .L6 : 顺 .L6 回填链进行 回填 10.1 C 语言编译系统

9.用 gcc  S main.c 可以得到汇编文件 main.s 用 as  o main.o main.s 可以将 main.s 汇编成可重定位目标文件 main.o 10.1 C 语言编译系统

10.1 0.1.3 连接器 目标模块或目标文件的形式 可重定位的目标文件 可执行的目标文件 共享目标文件 一种特殊的可重定位目标文件 在装入程序或运行程序时,动态地装入到内存并连接 10.1 C 语言编译系统

11.连接是一个收集、组织程序所需的不同代码和数据的过程,以便程序能被装入内存并被执行 连接的时机 编译时,装入时,或运行时 静态连接器 动态连接器 可重定位目标模块的组成?(回顾例子) 10.1 C 语言编译系统

12.10.1 C 语言编译系统 .data | .text .align 4 | .align 4 .type aa, @object | . globl func .size aa, 4 | func : aa: | . . . .long 10 | movw $40, -2(% ebp ) . globl bb | . . . .align 2 | .type bb, @object | .size bb, 2 bb: .value 20 .align 4 .type cc.2, @object .size cc.2, 4 cc.2: .long 30 static long aa = 10; short bb = 20; func ( ) { static long cc = 30; short dd = 40; }

13.一个重定位模块 M 可能 定义和引用的符号 全局符号 指那些在模块 M 中定义,可以被其它模块引用的符号 局部符号 指那些在模块 M 中定义,且只能在本模块中引用的符号 外部符号 指那些由模块 M 引用并由其它模块定义符号 符号解析 识别各个目标模块中定义和引用的符号,为每一个符号引用确定它所关联的一个同名符号的定义 重定位 10.1 C 语言编译系统

14.1 0.1.4 目标文件的格式 目标文件格式随系统不同而不同 介绍 Unix 的 ELF(Executable and Linkable Format) 格式 Linux、System V Unix 的后期版本、 BSD Unix 变体和 Sun Solaris, 都使用 Unix 的 ELF 格式 10.1 C 语言编译系统

15.ELF 头 描述了字的大小 产生此文件的系统的字节次序 目标文件的类型 机器类型 节头表的位置、条目多少 其它 节头表 描述目标文件中各节的位置和大小 处于目标文件的 末尾 10.1 C 语言编译系统 ELF 头 . text . rodata . data . bss . symtab . rel.text . rel.data . debug . line . strtab 节头表 0 描述 目标 文件 的节 节

16.. text 节 被 编译程序的机器代码 . rodata 节 诸如 printf 语句中的格式串 和 switch 语句 的跳转表等只 读数据 . data 节 已初始化 的 全局变量 . bss 节(. comm 节) 未 初始化的全局变量 在 目标文件中不占实际的 空间 10.1 C 语言编译系统 ELF 头 . text . rodata . data . bss . symtab . rel.text . rel.data . debug . line . strtab 节头表 0 描述 目标 文件 的节 节

17.. symtab 节 记录 在该模块中定义和 引用 的函数 和全局变量的信息 的 符号表 Name :指针 Value :偏移地址或 绝对地址 Size :字节 数 Type : FUNC 或 OBJECT Bind : GLOBAL 或 LOCAL 或 EXTERN 10.1 C 语言编译系统 ELF 头 . text . rodata . data . bss . symtab . rel.text . rel.data . debug . line . strtab 节头表 0 描述 目标 文件 的节 节

18.. rel.text 节 . text 节 中需要修改的 单元的 位置 列表 . rel.data 节 用于 被本模块引用或定义 的 全局变量 的重定位 信息 . debug 节 用于 调试程序的调试符号 表 10.1 C 语言编译系统 ELF 头 . text . rodata . data . bss . symtab . rel.text . rel.data . debug . line . strtab 节头表 0 描述 目标 文件 的节 节

19.. line 节 源文件 和. text 节中的机器 指 令 之间的行号映射 . strtab 一 组有空结束符的串构成 的 串 表 10.1 C 语言编译系统 ELF 头 . text . rodata . data . bss . symtab . rel.text . rel.data . debug . line . strtab 节头表 0 描述 目标 文件 的节 节

20.1 0.1.5 符号解析 将每个符号引用正确地与某可重定位模块的符号表中的一个符号定义相关联,从而确定各个符号引用的位置 在所有输入模块中都找不到被引用符号的定义,则打印错误消息并结束连接 需要定义解析规则 函数和已初始化的全局变量称为 强符号 ;未初始化的全局变量称为 弱符号 不 允许有多重的强符号定义 出现一个强符号定义和多个弱符号定义时,选择强符号的定义 出现多个弱符号定义时,选择任意一个弱符号的定义 10.1 C 语言编译系统

21.1 0.1.6 静态库 将相关的可重定位目标模块打包成一个文件,作为连接器的输入 连接器仅复制库中被应用程序引用的模块 gcc  c swap.c  编译 ar rcs mylib.a swap.o  建库 gcc  static  o swap1 main.c / usr /lib/ libc.a mylib.a  生成可执行文件 10.1 C 语言编译系统

22.和静态库连接 10.1 C 语言编译系统 printf.o 等 可重定位文件 翻译器 main.c 源文件 连接器 main.o mylib.a swap.o libc.a 静态 库 swap1 完全连接的可执行文件

23.1 0.1.7 可执行目标文件及装入 可执行目标文件 与可重定位目标文件格式类似 可 执行目标 文件 的 装入由 加载器 完成 这里 描述的 装入 过程 从概念 上来 说是 正确的 若需要了解 装入 过程 真正是 怎样 工作 的,必须 在 理解 了进程、 虚 拟 内存和内存 分 页 等概念以后 10.1 C 语言编译系统 读/ 写 内存 段 ELF 头 段头表 . init . text . rodata . data . bss . symtab . debug . line . strtab 节头表 只读 内存 段 符号表 和 调试 信息 , 不 装入内存 描述 目标 文件 的节 将下面的节映射到运行时的内存段 典型的 ELF 可执行目标 文件格式

24.Linux 运行时的内存映像 10.1 C 语言编译系统 内核 用户栈 (运行时创建) 共享库 的内存 区域 运行时的堆 (运行时用 malloc 创建) 读/写段 (. data, . bss ) 只读段 (. init, .text, .rodata) 未使用 0 xc0000000 0 x40000000 0 x08048000 对用户代码不可见 % esp (栈指针) brk 从可 执行 文件 装入 0

25.10.1.8 动态连接 静态库 周期性地被维护和更新 内存可能有多份 printf 和 scanf 的代码 共享库 在运行 时装 到任意的内存位置,被内存中的进程共享 共享库以两种不同的方式被共享 共享库的代码和数据被所有引用该库的可执行目标文件所共享 共享库的 . text 节在内存中的一个副本可以被正在运行的不同进程共享 10.1 C 语言编译系统

26.10.1 C 语言编译系统 可重定位文件 翻译器 ( cpp,cc1,as) main.c 源文件 连接器( ld) main.o libc.so mylib.so 重定位 和 符号 表信息 部分连接的 可 执行 目标文件 swap2 加载器( execve ) libc.so mylib.so 动态连接器( ld-linux.so) 代码和 数据 此时,动态连接器是内存中已完全连接的可执行代码

27.加载器通常装入和运行动态连接器 动态连接器接着完成连接任务 把 libc.so 的文本和数据装入内存并进行重定位 把 mylib.so 的文本和数据装入内存并进行重定位 重定位 swap2 中任何对 libc.so 或 mylib.so 定义的符号的引用 将控制传递给应用程序 10.1 C 语言编译系统

28.1 0.1.9 处理目标文件的一些工具 ar 创建 静态库,插入、删除、罗列和提取 成 员 strings 列出 包含在目标文件中的所有可打印串 strip 从 一个目标文件中删除符号表信息 nm 列出 一个目标文件的符号表中定义的符号 size 列出 目标文件中各段的名字和大小 readelf 显示 目标文件的完整结构,包括编码在 ELF 头 中 的 所有信息。它包括了 size 和 nm 的功能 objdump 可以 显示目标文件中的所有信息。其最有用的 功 能 是反汇编. text 节中的二进制指令 ldd 列出 可执行目标文件在运行时需要的共享库 10.1 C 语言编译系统

29.如果 cfile 是一个 C 语言源程序(注意,该 文件名没有 后缀),在 X86/Linux 机器上, 命令 cc cfile 的 结果是错误信息 / usr /bin/ ld : cfile : file format not recognized: treating as linker script / usr /bin/ ld : cfile : 1: parse error collect2: ld returned 1 exit status 请 解释为什么会是这样的 错误信息? cc 根据文件扩展名决定执行哪些步骤:预处理 (.c) 、编译 (. i ) 、汇编 (.s) 、连接装配 (.o) 无后缀时,会跳过 预处理、编译、 汇编,尝试进行连接装配,因而出错 10.1 C 语言编译系统 例题 1