第三章 程序的转换与机器级表示 程序转换概述 IA-32 /x86-64指令系统 C语言程序的机器级表示 复杂数据类型的分配和访问 越界访问和缓冲区溢出、x86-64架构
程序的转换与机器级表示 主要教学目标 了解高级语言与汇编语言、汇编语言与机器语言之间的关系 掌握有关指令格式、操作数类型、寻址方式、操作类型等内容 了解高级语言源程序中的语句与机器级代码之间的对应关系 了解复杂数据类型(数组、结构等)的机器级实现 主要教学内容 介绍C语言程序与IA-32机器级指令之间的对应关系。 主要包括:程序转换概述、IA-32指令系统、C语言中控制语 句和过程调用等机器级实现、复杂数据类型(数组、结构等) 的机器级实现等。 本章所用的机器级表示主要以汇编语言形式表示为主。
程序的机器级表示 分以下五个部分介绍 第一讲:程序转换概述 机器指令和汇编指令 机器级程序员感觉到的属性和功能特性 高级语言程序转换为机器代码的过程 第二讲:IA-32 /x86-64指令系统 第三讲: C语言程序的机器级表示 过程调用的机器级表示 选择语句的机器级表示 循环结构的机器级表示 第四讲:复杂数据类型的分配和访问 数组的分配和访问 结构体数据的分配和访问 联合体数据的分配和访问 数据的对齐 第五讲:越界访问和缓冲区溢出 从高级语言程序出发,用其对应的机器级代码以及内存(栈)中信息的变化来说明底层实现 围绕C语言中的语句和复杂数据类型,解释其在底层机器级的实现方法
回顾:冯.诺依曼结构计算机模型 CPU 存储器 控制器 输入 GPRs 设备 ALU 输出 设备 地址 标 志 寄 控制 存 器 … … 数据 控制 控制器 PC MAR 输入 设备 GPRs 1 标 志 寄 存 器 ALU 2 1 3 … 4 输出 设备 K-1 5 … IR MDR N-1
回顾:冯.诺依曼结构计算机模型 你还记得冯.诺依曼计算机结构的特点吗? CPU 存储器 控制器 输入 GPRs 设备 ALU 输出 设备 1 2 3 4 5 6 7 地址 数据 控制 控制器 PC MAR 输入 设备 GPRs 1 2 3 标 志 寄 存 器 ALU 输出 设备 IR MDR 你能想到计算机相当于现实生活中的什么呢? 工厂、饭店? 计算机是如何工作的呢?
计算机是如何工作的? 先想象一下妈妈是怎样做一桌你喜欢(指定)的菜的? CPU 存储器 控制器 输入 GPRs 设备 ALU 输出 设备 1 2 3 4 5 6 7 地址 数据 控制 控制器 PC MAR 输入 设备 GPRs 1 2 3 标 志 寄 存 器 ALU 输出 设备 IR MDR
计算机是如何工作的? 类似“存储程序”工作方式 做菜前 原材料(数据)和菜谱(指令)都按序放在厨房外的架子(存储器)上, 每个架子有编号(存储单元地址)。 菜谱上信息:原料位置、做法、做好的菜放在哪里等 例如,把10、11号架上的原料一起炒,并装入3号盘 然后,我告诉妈妈从第5个架上(起始PC=5)指定菜谱开始做 开始做菜 第一步:从5号架上取菜谱(根据PC取指令) 第二步:看菜谱(指令译码) 第三步:从架上或盘中取原材料(取操作数) 第四步:洗、切、炒等具体操作(指令执行) 第五步:装盘或直接送桌(回写结果) 第六步:算出下一菜谱所在架子号6=5+1(修改PC的值) 继续做下一道菜(执行下一条指令)
计算机是如何工作的? 你能告诉我计算机是如何工作的吗? “存储程序”工作方式! CPU 存储器 控制器 输入 设备 GPRs ALU 输出 如果你知道你妈妈是如何做菜的,你就已经知道计算机是如何工作的! 你能告诉我计算机是如何工作的吗? “存储程序”工作方式! CPU 存储器 1 2 3 4 5 6 7 地址 数据 控制 控制器 PC MAR 输入 设备 GPRs 1 2 3 标 志 寄 存 器 ALU 输出 设备 IR MDR
计算机是如何工作的? 程序由指令组成(菜单由菜谱组成) 程序在执行前 数据和指令事先存放在存储器中,每条指令和每个数据都有地址,指令按序存放,指令由OP、ADDR字段组成,程序起始地址置PC (原材料和菜谱都放在厨房外的架子上, 每个架子有编号。妈妈从第5个架上指定菜谱开始做) 开始执行程序 第一步:根据PC取指令(从5号架上取菜谱) 第二步:指令译码(看菜谱) 第三步:取操作数(从架上或盘中取原材料) 第四步:指令执行(洗、切、炒等具体操作) 第五步:回写结果(装盘或直接送桌) 第六步:修改PC的值(算出下一菜谱所在架子号6=5+1) 继续执行下一条指令(继续做下一道菜)
指令和数据 程序启动前,指令和数据都存放在存储器中,形式上没有差别,都是0/1序列 采用”存储程序“工作方式: 程序由指令组成,程序被启动后,计算机能自动取出一条一条指令执行,在执行过程中无需人的干预。 指令执行过程中,指令和数据被从存储器取到CPU,存放在CPU内的寄存器中 指令中需给出的信息: 操作性质(操作码) 源操作数1 或/和 源操作数2 (立即数、寄存器编号、存储地址) 目的操作数地址 (寄存器编号、存储地址) 存储地址的描述与操作数的数据结构有关!
“指令”的概念 计算机中的指令有微指令、机器指令和伪(宏)指令之分 机器指令处于硬件和软件的交界面 相当于一个菜谱指定的一个完整做菜过程 本章中提及的指令都指机器指令 微指令是微程序级命令,属于硬件范畴 相当于洗、切、煮、炒等做菜“微过程“ 伪指令是由若干机器指令组成的指令序列,属于软件范畴 相当于由多个菜谱合成一个”大菜“的过程 汇编指令是机器指令的汇编表示形式,即符号表示 机器指令和汇编指令一一对应,它们都与具体机器结构有关,都属于机器级指令
程序的转换 temp = v[k]; v[k] = v[k+1]; v[k+1] = temp; 汇编指令 软件 lw $15, 0($2) sw $16, 0($2) sw $15, 4($2) 机器指令 100011 00010 01111 0000 0000 0000 0000 100011 00010 10000 0000 0000 0000 0100 101011 00010 10000 0000 0000 0000 0000 101011 00010 01111 0000 0000 0000 0100 硬件 … , EXTop=1,ALUSelA=1,ALUSelB=11,ALUop=add, IorD=1,Read,MemtoReg=1,RegWr=1,...... 微指令 … 1 1 11 100 1 0 1 1 …
机器级指令 mov [bx+di-6], cl 或 movb %cl, -6(%bx,%di) 机器指令和汇编指令一一对应,都是机器级指令 机器指令是一个0/1序列,由若干字段组成 汇编指令是机器指令的符号表示(可能有不同的格式) mov、movb、bx、%bx等都是助记符 指令的功能为:M[R[bx]+R[di]-6]←R[cl] 补码11111010的真值为多少? 操作码 寻址方式 寄存器编号 立即数(位移量) mov [bx+di-6], cl movb %cl, -6(%bx,%di) 或 Intel格式 AT&T 格式 R:寄存器内容 M:存储单元内容 寄存器传送语言 RTL(Register Transfer Language) 注:也有用(x)表示地址x中的内容
指令集体系结构ISA ISA(Instruction Set Architecture)位于软件和硬件之间 硬件的功能通过ISA提供出来 可执行的指令的集合,包括指令格式、操作种类以及每种操作对应的操作数的相应规定; 指令可以接受的操作数的类型; 操作数所能存放的寄存器组的结构,包括每个寄存器的名称、编号、长度和用途; 操作数所能存放的存储空间的大小和编址方式; 操作数在存储空间存放时按照大端还是小端方式存放; 指令获取操作数的方式,即寻址方式; 指令执行过程的控制方式,包括程序计数器、条件码定义等。 I/O CPU Compiler Operating System Application Digital Design Circuit Design Instruction Set Architecture MM Assembler
IA-32的体系结构是怎样的呢? CPU 存储器 控制器 输入 GPRs 设备 ALU 输出 设备 你妈会做的菜和厨师会做的菜不一样,同一个菜谱的做法也可能不同 如同 不同架构支持的指令集不同,同一种指令的实现方式和功能也可能不同 CPU 存储器 1 2 3 4 5 6 7 地址 数据 控制 控制器 PC MAR 输入 设备 GPRs 1 2 3 标 志 寄 存 器 ALU 输出 设备 IR MDR
高级语言程序转换为机器代码的过程 用GCC编译器套件进行转换的过程 预处理:在高级语言源程序中插入所有用#include命令指定的文件和用#define声明指定的宏。 编译:将预处理后的源程序文件编译生成相应的汇编语言程序。 汇编:由汇编程序将汇编语言源程序文件转换为可重定位的机器语言目标代码文件。 链接:由链接器将多个可重定位的机器语言目标文件以及库例程(如printf()库函数)链接起来,生成最终的可执行目标文件。
GCC使用举例 两个源程序文件test1.c和test2.c,最终生成可执行文件为test gcc -O1 test1.c test2.c -o test 选项-O1表示一级优化,-O2为二级优化,选项-o指出输出文件名 目标文件可用“objdump -d test.o” 反汇编为汇编语言程序 00000000 <add>: 0: 55 push %ebp 1: 89 e5 mov %esp, %ebp 3: 83 ec 10 sub $0x10, %esp 6: 8b 45 0c mov 0xc(%ebp), %eax 9: 8b 55 08 mov 0x8(%ebp), %edx c: 8d 04 02 lea (%edx,%eax,1), %eax f: 89 45 fc mov %eax, -0x4(%ebp) 12: 8b 45 fc mov -0x4(%ebp), %eax 15: c9 leave 16: c3 ret gcc -E test.c -o test.i gcc -S test.i -o test.s 位移量 机器指令 汇编指令 test.s gcc –S test.c –o test.s add: pushl %ebp movl %esp, %ebp subl $16, %esp movl 12(%ebp), %eax movl 8(%ebp), %edx leal (%edx, %eax), %eax movl %eax, -4(%ebp) movl -4(%ebp), %eax leave ret 编译得到的与反汇编得到的汇编指令形式稍有差异
两种目标文件 test.o:可重定位目标文件 test:可执行目标文件 “objdump -d test” 结果 “objdump -d test.o”结果 00000000 <add>: 0: 55 push %ebp 1: 89 e5 mov %esp, %ebp 3: 83 ec 10 sub $0x10, %esp 6: 8b 45 0c mov 0xc(%ebp), %eax 9: 8b 55 08 mov 0x8(%ebp), %edx c: 8d 04 02 lea (%edx,%eax,1), %eax f: 89 45 fc mov %eax, -0x4(%ebp) 12: 8b 45 fc mov -0x4(%ebp), %eax 15: c9 leave 16: c3 ret 080483d4 <add>: 80483d4: 55 push ... 80483d5: 89 e5 … 80483d7: 83 ec 10 … 80483da: 8b 45 0c … 80483dd: 8b 55 08 … 80483e0: 8d 04 02 … 80483e3: 89 45 fc … 80483e6: 8b 45 fc … 80483e9: c9 … 80483ea: c3 ret test.o中的代码从地址0开始,test中的代码从80483d4开始!
可执行文件的存储器映像 内核区 栈区 共享库的代码 堆区 程序(段)头表描述如何映射 brk 从可执行文件装入 Kernel virtual memory Memory-mapped region for shared libraries Run-time heap (created by malloc) User stack (created at runtime) Unused Read/write segment (.data, .bss) Read-only segment (.init, .text, .rodata) 内核区 0xC00000000 ELF header Segment header table .text section .data section .bss section .symtab .debug .rodata section .line .init section .strtab 栈区 %esp (栈顶) 共享库的代码 brk 堆区 从可执行文件装入 0x08048000
总结 高级语言程序总是转换为机器代码才能在机器上执行 转换过程:预处理、编译、汇编、链接 机器代码是二进制代码,可DUMP为汇编代码表示 ISA规定了一台机器的指令系统涉及到的所有方面 例如: 所有指令的指令格式、功能 通用寄存器的个数、位数、编号和功能 存储地址空间大小、编址方式、大/小端 指令寻址方式