3.2程序编码

unix> gcc -01 -o p p1.c p2.c

-o用于指定输出(out)文件名。

-01,-02 告诉编译器使用第一级或第二级优化

3.2.1机器级代码

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

         1.ISA(指令集体系结构):机器级程序的格式和行为,叙述成按顺序执行,一条指令结束后,下一条再开始。

         2.机器级程序使用的储存器地址是虚拟地址,提供的储存器模型看上去是一个非常大的字节数组。

IA32

         1.程序计数器

         2.整数寄存器文件

         3.条件码寄存器保存着最近执行的算术或逻辑指令的状态信息。

         4.一组浮点寄存器存放浮点数据。

3.2.2代码示例

gcc  -S -o code.s code.c产生一个汇编文件code.svim code.s

 

objdump -d xxx.xx使用反汇编器查看目标代码文件的内容。

机器代码和它的反汇编表示的一些特性

1.IA32指令长度从115个字节不等

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

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

4.反汇编器使用的指令命名规则与GCC生成的汇编代码使用的有些差别

 

 

3.3数据格式

8 位:字节

16位:字

32位:双字

64位:四字

16位的机器体系结构,Intel用术语"(word)"表示16位数据类型。

32位机器体系结构是从16位扩展过来,因此称32位数为双字(double words,称64位数为四字(quad words

大部分指令都是针对字节和双字操作的。

          C声明               Intel数据类型                          GAS后缀                                      大小(字节)

          char                 字节(byte                             b                                                       1

          short                字(word                               w                                                       2

          int                    双字(double words            l                                                        4

          unsigned         双字(double words            l                                                        4

          long int           双字(double words             l                                                        4

          unsigned long    双字(double words         l                                                        4

          char *                  双字(double words         l                                                        4

          float                 单精度(signal                      s                                                        4

          double              双精度(double                  l                                                         8

          long double       扩展精度                                 t                                                     10/12

GAS中的每个操作指令都有一个字符后缀,用于表明操作数的大小。例如,mov有三种形式: movb(传送字节)、movw(传送字)、movl(传送双字)。其中float的后缀也是l,这不会与整数的混淆,因为浮点数使用的一组完全不同的指令和寄存器(浮点数寄存器)。

3.4访问信息

IA32中央处理单元(CPU)中,包含了832位整数寄存器。在每个32位寄存器的名字前面都会有一个%e,在这里可以把e理解成extended(扩展的),因为早期的8086寄存器是16位,所以加e之后就变成32位的了。

3.4.1操作数指示符

操作数指示符包括立即数,寄存器,存储器。

类型

格式

操作数值

名称

立即数

$Imm

Imm

立即数寻址

寄存器

Ea

R[Ea]

寄存器寻址

存储器

Imm

M[Imm]

绝对寻址

存储器

(Ea)

M[R[Ea]]

间接寻址

存储器

Imm(Eb)

M[Imm + R[Eb]]

(基址+偏移量)寻址

存储器

(Eb, Ei)

M[R[Eb] + R[Ei]]

变址寻址

存储器

Imm(Eb, Ei)

M[Imm + R[Eb] + R[Ei]]

变址寻址

存储器

(, Ei, s)

M[R[Ei] * s]

比例变址寻址

存储器

Imm(, Ei, s)

M[Imm + R[Ei] * s]

比例变址寻址

存储器

(Eb, Ei, s)

M[R[Eb] + R[Ei]*s]

比例变址寻址

寄存器

Imm (Eb, Ei, s)

M[Imm + R[Eb] + R[Ei]*s]

比例变址寻址

3.4.2数据传送指令

将数据从一个位置复制到另一个位置的指令是最频繁使用的指令

 

传送指令MOV

MOV reg/mem,imm           ;立即数送寄存器或是存储器

MOV reg/mem/seg,reg      ;寄存器送寄存器(包括段寄存器)或贮存

MOV reg/seg,mem            ;主存送寄存器(包括段寄存器)

MOV reg/mem,seg            ;段寄存器送主存或寄存器

特别说明:(1)立即数传送至通用寄存器(不包括段寄存器)或存储单元

        (2)IA32的限制:两个操作数都不能指向存储器。

3.5算术和逻辑操作

3.5.1加载有效地址    3.5.2一元和二元操作     3.5.3移位操作

加载有效地址指令leal 实际上是movl指令的变形,它的指令形式是从储存器读数据到寄存器,但实际上它没用引用储存器。

一元操作

      只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。

二元操作

      操作数既是源又是目的

·第一个操作数可以是立即数、寄存器或者存储器位置 第二个操作数可以是寄存器或者存储器位置 但是不能同时是存储器位置。

移位操作

SAL 算术左移

SHL 逻辑左移

SAR 算术右移(补符号位)

SHR 逻辑右移(补0

3.5.5特殊的算术操作

imull

      双操作数,从两个32位操作数产生一个32位的乘积。

mull
         无符号数乘法

要求一个参数必须在寄存器%eax中,另一个作为指令的源操作数给出。乘积的高32位在%edx中,低32位在%eax中。

idivl

    有符号除法

操作数

DX:AX中的64位数作为被除数,操作数中为除数,结果商在AX中,余数在DX中。

divl无符号除法

通常会事先设定寄存器%edx0.

3.6控制

3.6.1条件码

         条件语句、循环语句和分支语句,要求有条件的进行,根据数据测试的结果来决定操作执行的顺序。机器代码提供两种基本的低级机制来实现有条件的行为:测试数据值,然后根据测试的结果来改变控制流或者数据流

控制指令-条件码

CPU维护着一组单个位的条件码寄存器,他们描述了最近的算术或者逻辑操作的属性。可以检测这些寄存器来执行条件分支指令

最常用的条件码:

CF:进位标志。最近的操作使最高位产生了进位。可以用来检查无符号操作数的溢出

ZF:零标志。最近的操作得出的结果为0.

SF:符号标志。最经的操作得到的结果为负数。

OF:溢出标志。最近的操作导致一个补码溢出——正溢出或者负溢出

比较和测试指令

有两类指令只设置条件码而不改变任何其他寄存器。

CMP指令根据两个操作数之间的差来设置条件码。

TEST指令的行为同and一样。典型的用法是,testl %eax%eax用来检查%eax是负数、零还是正数

3.6.2访问条件码

三种方法

1.根据条件码的某个组合,将一个字节设置为0或者1

2.可以条件跳转到程序的某个其他的部分

3.可以有条件地传送数据

3.6.3、跳转指令及其编码

jump分为直接跳转和间接跳转

直接跳转:后面跟标号作为跳转目标

间接跳转:*后面跟一个操作数指示符

 

 

3.6.4翻译条件分支

将条件表达式和语句从c语言翻译成机器语言,最常用的方式就是结合有条件和无条件跳转

无条件跳转:例如 goto。书上的例子就是把if-else语句翻译成了goto形式,然后再由这个形式翻译成汇编语言。

3.6.5循环

1.Do-while循环

源代码

do{

         body-statement 

}while(test-expr)

翻译成汇编的伪代码

loop:    

         body-statement

t = test-expr;

if(t)        

goto loop;

2.While循环

源代码

while(test-expr)

         body-statement

翻译成汇编的伪代码

t = test-expr;

if(!t)

    goto done;

loop:

    body-statement;

    t = test-expr;

    if(t)

        goto loop;

done:

For循环

源代码:

for(init-expr;

     test-expr;

     update-expr)

         body-statement;

翻译成汇编的伪代码:

init-expr;

 t = test-expr;

if(!t)

    goto done;

loop:

    body-statement;

    update-expr;

    t = test-expr;

    if(t)

        goto loop;

done:

switch语句

根据一个整数索引值进行多重分支,执行switch语句的关键步骤是通过跳转表来访问代码位置,使结构更加高效。

3.7过程

数据传递、局部变量的分配和释放通过操纵程序栈来实现。

3.7.1栈帧结构

·      为单个过程分配的栈叫做栈帧,寄存器%ebp为帧指针,而寄存器指针%esp为栈指针,程序执行时栈指针移动,大多数信息的访问都是相对于帧指针。

·      栈向低地址方向增长,而栈指针%esp指向栈顶元素。

 

3.7.2转移控制

call:目标是指明被调用过程起始的指令地址,效果是将返回地址入栈,并跳转到被调用过程的起始处。

ret:从栈中弹出地址,并跳转到这个位置。

函数返回值存在%eax

3.7.3寄存器使用惯例

程序寄存器是唯一能被所有过程共享的资源,调用者保存寄存器 被调用者保存寄存器是分开的,对于哪一个寄存器保存函数调用过程中的返回值要有统一的约定。

3.11使用GDB调试器

 

 

 

命令

参数含义

说明

display

 

设置自动显示列表,不带参数时显示列表中所有表达式

delete display dnums…..

自动显示列表的ID

删除自动显示列表

disable dislay dnums…..

自动显示列表的ID

禁用自动显示列表

enable display dnums…..

自动显示列表的ID

使用自动显示列表

info display

 

显示自动显示列表中的表达式

show convenience

 

显示快捷变量

macro expand expression

包含宏定义的表达式

将表达式宏展示

info macro macroname

宏定义的名字

查看宏定义的信息

generatre-core file

 

产生转储文件

backtrace

最内层的n个函数栈帧

-n 最外层n个函数栈帧

栈帧回溯

frame

栈帧号或者内存地址

选定栈帧,不带参数时显示栈帧简要信息

up

栈帧号

选定栈帧上移

down

栈帧号

选定栈帧下移

info frame

 

显示栈帧详细信息

info agrs

 

显示当前选定栈帧的函数参数

info locals

 

显示当前选定栈帧中的所有局部变量

set variable expr

赋值表达式

给变量赋值或者修改变量的值

print

赋值表达式

给变量赋值或者修改变量的值

jump

 

使程序从另外的地址开始执行

signal sig

信号值或者信号和名称

向被调试程序发送信号

call expr

包含函数调用的表达式

调用函数

print expr

包含函数调用的表达式

调用函数

list

函数名 行号 地址 偏移等

列出程序源码

set listsize count

 

设置 list默认显示的行数

disassemble

 

反汇编被调试程序

set disassembly-flavor instruction-set

 

设置反汇编格式

 

 

命令

参数含义

说明

display

 

设置自动显示列表,不带参数时显示列表中所有表达式

delete display dnums…..

自动显示列表的ID

删除自动显示列表

disable dislay dnums…..

自动显示列表的ID

禁用自动显示列表

enable display dnums…..

自动显示列表的ID

使用自动显示列表

info display

 

显示自动显示列表中的表达式

show convenience

 

显示快捷变量

macro expand expression

包含宏定义的表达式

将表达式宏展示

info macro macroname

宏定义的名字

查看宏定义的信息

generatre-core file

 

产生转储文件

backtrace

最内层的n个函数栈帧

-n 最外层n个函数栈帧

栈帧回溯

frame

栈帧号或者内存地址

选定栈帧,不带参数时显示栈帧简要信息

up

栈帧号

选定栈帧上移

down

栈帧号

选定栈帧下移

info frame

 

显示栈帧详细信息

info agrs

 

显示当前选定栈帧的函数参数

info locals

 

显示当前选定栈帧中的所有局部变量

set variable expr

赋值表达式

给变量赋值或者修改变量的值

print

赋值表达式

给变量赋值或者修改变量的值

jump

 

使程序从另外的地址开始执行

signal sig

信号值或者信号和名称

向被调试程序发送信号

call expr

包含函数调用的表达式

调用函数

print expr

包含函数调用的表达式

调用函数

list

函数名 行号 地址 偏移等

列出程序源码

set listsize count

 

设置 list默认显示的行数

disassemble

 

反汇编被调试程序

set disassembly-flavor instruction-set

 

设置反汇编格式


实验楼实验五

要求

使用gcc –S –o main.s main.c -m32
命令编译成汇编代码,如下代码中的数字和函数名请自行修改以防与他人雷同

int g(int x) {   return x + 3}  int f(int x) {   return g(x); int main(void) {   return f(8) + 1; }

1.删除gcc产生代码中以"."开头的编译器指令,针对每条指令画出相应栈帧的情况
2.(选做)使用gdb的bt/frame/up/down 指令动态查看调用栈帧的情况

实验结果

 

 2.

 

参考资料:

 http://blog.csdn.net/21cnbao/article/details/7385161

http://blog.csdn.net/nabila/article/details/7786428