2-4 Assembly & Compilation
-
编译执行:将要执行的源程序(高级语言编写),先编译成可执行的程序(机器语⾔), 然后再执⾏。
(如C语言,C++ )
-
解释执行对要执行的源程序(高级语⾔编写)读取⼀句语句, 然后解释执行; 再读取下⼀句, 再解释执行, 直到程序结束。 (python,Java,matlab)
解释器和编译器
解释器
- 解释器比编译器容易实现
- 解释器更接近⾼级语⾔, 所以可以给出更好的错误信息便于调试。
- 解释型语⾔最⼤的优势之⼀是其平台独立性: 可以在任何机器上运行。
- 解释器更慢(10x )解释型应⽤占⽤更多的内存和CPU资源。 这是由于为了运⾏解释型语⾔编写的程序, 相关的解释器必须⾸先运⾏。 解释器是复杂的、 智能的、 ⼤量消耗资源的程序, 并且它们会占⽤很多CPU周期和内存。由于解释型应⽤的decode-fetch-execute(解码-抓取-执⾏) 的周期, 它们⽐编译型程序慢很多。
- 解释器也会做很多代码优化, 运⾏时安全性检查; 这些额外的步骤占⽤了更多的资源并进⼀步降低了应⽤的运⾏速度。
编译器
- 翻译/编译的代码更高效, 因此性能更⾼:执⾏速度对于许多应⽤程序很重要, 尤其是操作系统。
- 编译的代码做⼀次性的艰苦⼯作:在编译期间 。
- 编译型程序比解释型程序消耗的内存更少 。
- 编译型程序是⾯向特定平台的因⽽是平台依赖的。
- 编译器在调试程序时提供不了多少帮助。
- 可执行的编译型代码要比相同的解释型代码大许多。
- 编译器(Compiler)的输出可能会包含伪指令(Pseudo-instructions ),要由汇编器(Assembler)将其替换掉。
常见伪指令
汇编器
汇编器主要由三部分⼯作组成
- 数据存储分配及初始化
- 助记符转换成⼆进制机器指令
- 地址解析
解析地址
“Old-style” 2-pass assembler approach
1st Pass :第⼀道:数据/指令 被编码, 并在它们的段内分配偏移量, 同时构建符号表。未解析的地址引⽤设置为 0。
2nd Pass:在第⼆道, 如果可能,使⽤正确的值填充那些内存引⽤指令的相应字段
例子:
Modern Way: 单道汇编器
现代的单道汇编器 single-pass 将更多信息保留在符号表中, 以便⼀次性解析地址,也有第⼆步, 称作回填 “back filling”
- 知道的地址(back references) 可以⻢上解析
- 未知的地址(forward references) ⼀旦解析则 回填“back-filled”
- 对于每个符号, 引⽤列表 “reference list”被保留以跟踪哪些指令需要回填
汇编之后生成目标文件(object file).o
链接器
有些地址解析汇编器单独解决不了 • 对其他对象模块中的数据或例程的引⽤ • 内存中所有段的布局 • ⽀持可重复使⽤的代码模块 • ⽀持可重定位代码模块 这个最后的解析⼯作由链接器 LINKER 完成
过程:
-
Step 1: 从.o ⽂件中提取每个代码段,把==所有代码段放在⼀起==
-
Step 2: 从每个.o ⽂件提取数据段, ==所有数据段放在⼀起, 并将其连接到代码段的末尾==
-
Step 3: 解析地址引用:
- 遍历地址解析表Relocation Table; 处理每个⼊⼝地址
- I.e.,填充所有绝对地址 absolute addresses
组合几个目标⽂件(.o) 到⼀个可执行⽂件 (“linking”)
Static and Dynamic Libraries 静态和动态库
LIBRARIES 是存储为“目标文件”的子程序
• 为整个库维护⼀个全局符号表, 每个⼦程序都有⼊⼝点./ • 在库中的⼦程序可以由汇编器引⽤, ⼦程序的⼊⼝由链接器LINKER 解析, 并将适当的代码添加到可执⾏⽂件中。 这种链接称为静态链接 STATIC linking.
许多程序使用通用库
• 在多个可执⾏⽂件中包含相同的代码浪费内存和磁盘空间 • 静态链接的现代替代⽅案是允许加载器LOADER和程序本身解析库子程序的地址。 这种形式的衬⾥lining称为动态链接 (e.x. .dll).
MIPS 里的绝对地址
-
j/jal/sw/lw用的都是绝对地址
-
beq/bne用的是相对地址,PC=PC+4+4*offset
Loader Basics 加载器
可执⾏⽂件存储在硬盘⾥,加载器的⼯作是把要运⾏的程序加载到内存,开始运⾏,加载器属于操作系统的⼯作 (OS)
Loader 工作
- 读执⾏⽂件的头⽂件以决定代码和数据段的⼤⼩
- 给程序创建新的地址空间, 留出⾜够代码、 数据、 堆栈所需空间
- 从可执⾏⽂件中拷⻉指令和数据到新的地址空间
- 拷⻉传递参数到堆栈 stack
- 初始化寄存器内容
- 大部分寄存器清零, 所分配的堆栈空间的第⼀个位置赋给堆栈指针,跳转到程序的第⼀条执⾏指令位置
- 如果程序退出, 则调⽤ exit 系统调⽤终⽌程序