您的位置:首页 > 其它

程序的机器级表示(1)--引入

2017-07-31 17:31 148 查看

GCC编译器在C语言中嵌入汇编语言

整个函数用汇编代码来写,然后在链接阶段与C语言函数结合起来

在C语言程序中直接利用GCC编译器对嵌入汇编语言的支持

机器级编程的两种重要抽象

机器级程序的格式和行为,定义为指令集体系结构(ISA),它定义了处理器状态,指令的格式,以及每条指令对状态的影响。大多数ISA,包括IA32和x86-64,将程序的行为秒速成好像每条指令是按顺序执行的,一套指令结束后,下一条再开始。

机器级程序使用的储存器地址是虚拟地址,提供的储存器模型看上去是一个非常大的字节数组。储存器系统的实际实现是将多个硬件储存器和操作系统软件结合起来。

IA32机器代码的不可见机器状态

程序计数器(PC,用%eip表示)指示将要执行的下一条指令在储存器的地址

整数寄存器文件包括8个命名的位置,分别储存32位的值。这些寄存器可以存储地址(C语言的指针)或整数数据。有的寄存器被用来记录某些重要的程序状态,其他的寄存器则用来保存临时数据,如过程的局部变量和函数的返回值。

条件码寄存器保存着最近执行的算数或逻辑指令的状态信息。他们用来实现控制或数据流中的条件变化,比如说用来实现if和while语句。

一组浮点寄存器存放浮点数据

程序储存器

程序储存器包含:程序的可执行代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的储存器快(比如说用malloc库函数分配的)

代码示例

有一个c语言代码文件code.c,程序如下:

int accum = 0;

int sum(int x,int y)
{
int t = x + y;
accum += t;
return t;
}


Linux下使用“-S”选项,就能得到该C语言代码对应的汇编代码,命令如下

linux>gcc -01 -S code.c


这会使GCC运行编译器,产生一个汇编文件code.s,该文件包含各种声明,包括下面几行:

sum:
pushl %ebp
movl %esp,%ebp
movl 12(%ebp),%eax
addl 8(%ebp),%eax
addl %eax,accum
poopl %ebp
ret


缩进的每一行都对应一条汇编指令。如果使用’-C’命令行选项,GCC会编译并汇编该代码:

linux>gcc -01 -c code.s


这样会产生目标代码文件code.o,这是一个二进制文件。该文件有一段17字节序列,十六进制表示为:

55 89 e5 8b 45 0c 03 45 08 01 05 00 00 00 00 5d c3


可以知道,机器实际执行的程序只是对一系列指令进行编码的字节序列。如果要查看该目标代码文件的内容,可以用反汇编器。反汇编器会根据目标代码产生一种类似于汇编大妈的格式。在linux下,可以用下面的命令行进行反汇编:

linux>objdump -d code.o


得到的结果如下:

00000000<sum>:
0: 55                                push %ebp
1: 89 e5                             mov %esp,%ebp
3: 8b 45 0c                          mov 12(%ebp),%eax
6: 03 45 08                          add 8(%ebp),%eax
9: 01 05 00 00 00 00                 add %eax,accum
f: 5d                                poop %ebp
10:c3                                ret


如上,左边的是目标代码,右边是汇编语言代码。

注意

IA32指令长度从1到15个字节不等。常用的指令以及操作数较少的指令所需的字节数少,而那些不太常用或操作数较多的指令所需的字节数较多

设计指令格式的方式是,从某个给定位置开始,可以将字节唯一地解码成机器指令。

反汇编器只是基于机器代码文件中的字节序列来确定汇编代码。它不需要访问程序的源代码或汇编代码。

反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有些细微的区别。如上面反汇编出来的代码,它省略了很多指令结尾的”l”,后缀是大小指示符,后面会提到。

Intel汇编语言格式

上述采用的的ATT(根据AT&T命名)格式的汇编语言。这是GCC,OBJDUMP和其他一些我们使用的工具的默认格式。但是有些编辑器会产生Intel格式的汇编代码,包括Microsofft的工具,以及Intel的文档。可以使用下述命令行,GCC产生sum的Intel格式代码。

linux>gcc -01 -S -masm-intel code.c


得到下面的汇编代码:

simple:
push ebp
mov ebp,esp
mov edx,DWORD PTR [ebp+8]
mov eax,DWORD PTR [ebp+12]
add eax,DWORD PTR [edx]
mov DWORD PTR [edx],eax
pop ebp
ret


Intel和ATT格式有一些不同:

Intel代码省略了指示大小的后缀,用的是mov,而不是movl。

Intel代码省略了寄存器名字前面的‘%’符号,用的是esp,而不是%esp

Intel代码使用不同的方式来描述储存器中位置。例如:DWORD PTR [ebp+8]表示‘8(%esp)’

在带有多个操作数的指令情况下,列出操作数的顺序相反
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: