【CSAPP-02】从源程序到进程(一)
前言
内容分两篇完成
(1)程序的编译,汇编,链接:知识包括编译原理,cpu指令集架构(Y86-64),链接。
(2)程序的加载,执行:知识包括操作系统(进程,虚拟内存,文件)
一、编译
编译:将.c高级程序语言转化为.s汇编语言。
1.编译原理
【词法分析】
依照有限自动机的模型,将单词串与标识符对应
【语法分析】
使用“上下文无关文法”将符号表构成抽象语法树,选择合适的方法,如LR,LL方法进行分析
【语义分析】
将语法树与语义相对应,完成类型匹配,转化的工作
【中间代码生成】
将语法树使用顺序的三地址码形式表示
【目标代码生成】
对中间代码进行优化,翻译成汇编指令
2.汇编分析
-
指令:gcc -s hello.i -o hello.s
-
内存段:
【代码段(code segment)】
.text段:代码段,还保存只读的常量
【数据段(data segment)】
.data段:已初始化的全局变量,是静态内存分配
.rodata段:存放C中的字符串和#define定义的常量
.bss段:未初始化的全局变量,是静态内存分配
【栈stack】
程序运行时内存区,存放局部变量
【堆heap】
程序运行时动态分配的内存区
3. 机器级表示
注:机器级编程需要考虑程序计数器PC,寄存器,状态码
- 基础
【数据访问】
寄存器:%rsp(栈指针)
立即数:$Imm
内存引用:Imm(rb, ri, s):M[ Imm + rb + ri*s]
【常用指令】
数据传送:mov
栈操作:push, pop
地址操作:leap
算术运算:add, sub, mul
逻辑运算:and, xor, or - 控制
【条件码】
cmp S1, S2:不改变寄存器,重新设置条件码
set D
【跳转指令】
jmp:无条件跳转
jop:条件跳转
//条件控制
//存在不确定跳转,常用分支预测填满流水线,损失性能
cmpq %rsi, %rdi
jge .L1
PROC2
ret
.L1:
PROC1
ret
//条件传送
//可能存在计算错误
movq %rsi, %rax
subq %rdi, %rax //y-x
movq %rdi, %rdx
subq %rsi, %rdx //x-y
cmpq %rsi, %rdi
cmovge %rdx, %rax
ret
//循环:while, do while, for大同小异
fact_while:
movl $1, %eax
jmp .JUDGE
.L1:
imulq %rdi, %rax
subq $1, %rdi
.JUDGE:
cmpq $1, %rdi
jg .L1
rep; ret
- 函数
栈帧:函数被调用时,创建定长的栈帧管理局部数据和转移控制。
//step1: 创建栈帧
push %ebp ; //基址指针rbp
movl %esp, $ebp ;
movl -12(%esp), %esp ; //分配栈帧空间
//step2:code
//...
//step3: 返回调用函数
mov %ebp, %esp //返回调用函数的栈指针rsp
pop %ebp //弹栈
ret //返回rax
相关指令:call , ret
- 数据:数组与结构体
二、汇编
汇编:将.s汇编语言转化为二进制.o目标文件。
1.汇编分析
-
指令:as hello.s -o hello.o
-
可重定位目标elf格式:readelf -a hello.o > ./elf.txt
(1)ELF头(ELF header):包含生成该目标文件的系统的字大小和字节顺序、ELF头的大小、目标文件类型、机器类型、节头部表的文件偏移,以及节头部表中条目的大小和数目。
(2).text: 已编译程序的机器代码
(3).rodata: 只读数据,比如跳转表等等
(4).data: 保存已初始化的全局变量和静态变量(全局和局部)
(5).bss: 保存未初始化的静态变量(全局和局部),以及被初始化为0的全局变量和静态变量(全局和局部)
(6).symtab: 符号表,存放在程序中定义和引用的函数和变量的符号信息
注意:不包含局部非静态变量条目,因为该变量是由栈管理的
(7).rel.text:.text节的重定位信息,可执行目标文件中需要修改的指令地址
(8).rel.data: .data节的重定位信息,合并后的可执行目标文件中需要修改的指针数据的地址
(9).debug:调试符号表,其条目是程序中定义的局部变量和类型定义,程序汇总定义和引用的全局变量,以及原始的C源文件
(10).line: 原始C源程序中的行号和.text节中机器指令之间的映射
注意:只有以-g选项调用编译器驱动程序,才会出现.debug和.line
(11).strtab: 字符串表,包括.symtab和.debug节中的符号表,以及节头部中的节名字
(12)节头部表(Section Header Table):给出不同节的大小和位置等其他信息 -
反汇编:objdump -d -r hello.o > dump_hello.txt
(1)反汇编后数值为16进制,.s中为10进制
(2)跳转地址为重定位条目,.s中用L1等字段跳转
2.机器指令的执行细节
cpu采用流水线的方式分为五阶段执行:取值,译码,访存,写回,更新
详细内容参考:计算机组成原理与体系结构
三.链接
链接:合并目标文件, 重定位 + 符号解析。
1.链接分析
-
可执行ELF文件:
【对比】
目标elf: 地址为0,需要重定位,便于链接
可执行elf: 填充了虚拟地址,无重定位节,增添了库函数信息,便于进程装载
-
静态链接 和 动态链接
【静态链接】
链接器是一个独立程序,将一个或多个库或目标文件(先前由编译器或汇编器生成)链接到一块生成可执行程序。
这里的库指的是静态链接库,Windows下以.lib为后缀,Linux下以.a为后缀。
【动态链接】
链接过程推迟到运行时再进行,在可执行文件装载时或运行时,由操作系统的装载程序加载库。
这里的库指的是动态链接库,Windows下以.dll为后缀,Linux下以.so为后缀。
【对比】
静态链接可执行文件体积较大,包含相同的公共代码,造成浪费。
动态链接运行时依赖dll模块。 -
静态链接步骤
(1)空间与地址的分配。扫描所有的目标文件,合并相似段,收集当中所有的符号信息。
(2)符号解析与重定位。调整代码位置。