您的位置:首页 > 其它

程序的机器级表示

2015-10-11 10:30 701 查看
P104,p105:

X86,经历了一个长期的的不断发展的过程。开始时他是第一代单芯片和16位微处理器之一,由于当时集成电路技术水平十分有限,其中做了很多妥协。此后,他不断地成长,利用进步的技术满足更高性能和支持更高级的操作系统的需求。

8086(1978)

80286(1982)

i386(1985)

i486(1989)

Pentium(1993)

PentiumPro(1995)

PentiumII(1997)

PentiumIII(1999)

Pentium4(2000)

Pentium4E(2004)

Core2(2006)

Corei7(2008)

X86寻址方式经历三代:

1.DOS时代的平坦模式,不区分用户空间和内核空间,很不安全

2.8086的分段模式

3.IA32的带保护模式的平坦模式

摩尔定律:

随着Intel微处理器复杂性的复杂度提高,晶体管的数量不断增长。

P106:

ISA的定义:

计算级程序的格式和行为,定义为指令集体系结构,它定义了处理器状态指令的格式,以及每条指令对状态的影响。

一些对C语言程序员隐藏的处理器的状态是可见的:

程序计数器:

通常称为PC,指示将要执行的下一条指令在存储器的地址。

整数寄存器:

文件包含8个命名的位置,分别存储32位的值。

条件码寄存器:

保存最近执行的算术或者逻辑的状态和信息。它们用来实现控制或者数据流中的条件变化。

一组浮点寄存器:

用来存放浮点数据。

P107:

在命令行使用-s选项,就能得到C语言编译器产生的汇编代码:

unix>gcc-01-scode.c

如果我们使用-c命令行选项,GCC会编译并汇编该代码:

unix>gcc-01-ccode.c

gcc-Sxxx.c-oxxx.s获得汇编代码,也可以用objdump-dxxx反汇编。

注意:

64位机器上想要得到32代码:gcc-m32-Sxxx.cMACOS中没有objdump,有个基本等价的命令otoolUbuntu中gcc-Scode.c(不带-O1)产生的代码更接近教材中代码(删除"."开头的语句)

P108:

如何找到程序的字节表示:

在文件code.o上运行GNU调试工具GDB输入命令:

(gdb)x/17xbsum

查看目标代码文件的内容输入命令:

unix>objdump-dcode.o

生成可执行的文件prog:

unix>gcc-01-oprogcode.omain.c

反汇编文件prog:

unix>objdump-dprog

进制文件可以用od命令查看,也可以用gdb的x命令查看。

有些输出内容过多,我们可以使用more或less命令结合管道查看,也可以使用输出重定向来查看

odcode.o|more
odcode.o>code.txt
P109:
gcc-S产生的汇编中以“.”开始的语句都删除
所有是我以“.”开头的行都是知道汇编器和链接器的命令。
我们通常可以忽略这些行。另一方面,没有关于这些指令的用途以及它们与源代码之间的关系的解释说明。

P110:

了解Linux和Windows的汇编格式有点区别:ATT格式和Intel格式

GCC采用的是AT&T的汇编格式,也叫GAS格式(GnuASemblerGNU汇编器),而微软采用Intel的汇编格式.
一基本语法
语法上主要有以下几个不同.
1、寄存器命名原则


AT&TIntel说明
%eaxeaxIntel的不带百分号


2、源/目的操作数顺序


AT&TIntel说明
movl%eax,%ebxmovebx,eaxIntel的目的操作数在前,源操作数在后


3、常数/立即数的格式


AT&TIntel说明
movl$_value,%ebxmoveax,_valueIntel的立即数前面不带$符号
movl$0xd00d,%ebxmovebx,0xd00d规则同样适用于16进制的立即数


4、操作数长度标识


AT&TIntel说明
movw%ax,%bxmovbx,axIntel的汇编中,操作数的长度并不通过指令符号来标识


在AT&T的格式中,每个操作都有一个字符后缀,表明操作数的大小.例如:mov指令有三种形式:

movb传送字节

movw传送字

movl传送双字

因为在许多机器上,32位数都称为长字(longword),这是沿用以16位字为标准的时代的历史习惯造成的.
果没有指定操作数长度的话,编译器将按照目标操作数的长度来设置。比如指令“mov%ax,%bx”,由于目标操作数bx的长度为word,那么编译器将把此指令等同于“movw%ax,%bx”。同样道理,指令“mov$4,%ebx”等同于指令“movl$4,%ebx”,“push%al”等同于“pushb%al”。对于没有指定操作数长度,但编译器又无法猜测的指令,编译器将会报错,比如指令“push$4”。




5、寻址方式


AT&TIntel
imm32(basepointer,indexpointer,indexscale)[basepointer+indexpointer*indexscale+imm32)

两种寻址的实际结果都应该是


imm32+basepointer+indexpointer*indexscale

P111:
表中不同数据的汇编代码后缀




P112:

esiedi可以用来操纵数组,espebp用来操纵栈帧。

对于寄存器,特别是通用寄存器中的eax,ebx,ecx,edx,要理解32位的eax,16位的ax,8位的ah,al都是独立的,我们通过下面例子说明:


假定当前是32位x86机器,eax寄存器的值为0x8226,执行完addw$0x8266,%ax指令后eax的值是多少?
解析:0x8226+0x826=0x1044c,ax是16位寄存器,出现溢出,最高位的1会丢掉,剩下0x44c,不要以为eax是32位的不会发生溢出.

P113:
操作数指令符:
立即数:
在ATT格式的汇编代码中,立即数的书写方式$后面跟一个用标准C表示法表示的整数。任何能放进一个32位的字里的数值都可以用作立即数。
寄存器:
它表示某个寄存器的内容,对双操作数来说,可以是8个32位寄存器中的一个。
对于字操作数来说,可以是8个16位寄存器中的一个。
我们用Ea来表示任意的寄存器a,用引用R【Ea】来表示它的值
存储器:
它会根据计算出来的地址访问某个存储器的位置。

有效地址的计算方式Imm(Eb,Ei,s)=Imm+R[Eb]+R[Ei]*s

P114:

MOV相当于C语言的赋值”=“

注意ATT格式中的方向

另外注意不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下。
MOV:
传送
MOVS:
传送符号扩展字节
MOVZ:
传送零扩展的字节

掌握push,pop:

(1)进栈指令push
pushreg/mem/seg;sp<-sp-2,ss<-reg/mem/seg
进栈指令先使堆栈指令sp减2,然后把一个字操作数存入堆栈顶部。堆栈操作的对象只能是字操作数,进栈时底字节存放于低地址,高字节存放于高地址,sp相应向低地址移动两个字节单元。
pushAX
PUSH[2000H]
PUSHCS
(2)、出栈指令pop
popreg/seg/mem;reg/seg/mem<-ss:[sp],sp<-sp+2
出栈指令把栈顶的一个字传送至指定的目的操作数,然后堆栈指针sp加2。目的操作数应为字操作数,字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
popAX
POP[2000H]
POPSS堆栈可以用来临时存放数据,以便随时恢复它们。也常用于子程序见传递参数。

p115/p116:

push:

ax是把ax里的值压入堆栈。即当前esp-4出的值变为ax的值,ax本身的值不变。

pop:
dx是把当esp指向的栈中的值(即之前pushax进栈的ax的值)赋给dx
并且esp+4(dx的值改变,esp在pop之前指向的地方的值不变,还是之前ax进栈后的值,即堆栈里的那个值不会自动清零)
注意栈顶元素的地址是所有栈中元素地址中最低的。


p117:

指针就是地址;局部变量保存在寄存器中。

C语言中所谓的“指针”其实就是地址。

间接地引用指针就是将该指针放在一个寄存器中,然后在存储器中引用中使用这个寄存器。

其次,像X这样的局部变量通常是保存在寄存器中,而不是存储器中。寄存器访问比存储器访问要快的多。


p119:

结合表理解一下算术和逻辑运算

注意目的操作数都是什么类型:

加载有效地址:

指令leal实际上是movl指令的变形。它的指令形式是从存储器读数据到寄存器,但实际上它根本就没有引用存储器。

一元操作:

它只有一个操作数,既是源又是目的。

二元操作:

既是源又是目的。

移位:

先给出移位量,然后第二项给出的是要移位的位数。

特别注意:

1.减法是谁减去谁

2.移位操作移位量可以是立即数或%cl中的数


p123:

条件码:

CF:进位标志

ZF:零标志

SF:符号标志

OF:溢出标志

CF:(unsigned)t<(unsiged)a无符号溢出

ZF:(t==0)零

SF:(t<0)负数

OF:(a<0==b<0)&&(t<0!=a<0)有符号溢出

控制中最核心的是跳转语句:有条件跳转


p124:

有条件跳转的条件看状态寄存器(教材上叫条件码寄存器)

注意leal不改变条件码寄存器

思考一下:CMP和SUB用在什么地方

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

除了只设置条件码而不更新目标寄存器之外,CMP指令与SUB指令的行为是一样的。

p125:

条件码通常不会直接读取,常用的方法有三种:

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

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

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

setl和setb:

表示”小于时设置“和”低于时设置“

SET指令根据t=a-b的结果设置条件码


p127:

正常情况下执行下,指令按照它们出现的顺序一条一条的执行。

跳转指令会导致执行切换到程序中的一个全新的位置。

这些跳转的目的通常用一个标号指明。

p128:

(实现if,switch,while,for),

无条件跳转jmp(实现goto)


p130/p131:

if-else的汇编结构:

t=test-expr;

if(!t)

gotofalse;

then-statement

gotodone;

false:

else-statement

done:

汇编器为then-statement和else-statement产生各自的代码块。

它会插入条件和无条件分支,以保证执行正确的代码块。


p132/p133:

do-while:

loop:

body-statement

t=test-expr;

if(t)

gotoloop;

也就是说,每次循环,程序会执行循环体里的语句,然后执行测试表达式。

如果测试为真,则回去再执行一个循环。


p134/p135:

while:

if(!test-expr)

gotodone;

do

body-statement

while(test-expr);

done:

接下来,翻译成goto代码:

t=test-expr;

if(!t)

gotodone;

loop:

body-statement

t=test-expr;

if(t)

gotoloop;

done:


p137/p138:

for:

init-expr;

if(!test-expr)

gotodone;

do{

body-statement

update-expr;

}while(test-expr);

done:


p144/p145:

switch:




p149:

IA32通过栈来实现过程调用。掌握栈帧结构,注意函数参数的压栈顺序.


p150/p151:

转移控制:

call指令有一个指令目标,即指明呗调用过程起始的指令地址。同跳转一样,调用可以是直接的,也可以是间接的。

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

call/ret;函数返回值存在%eax中


p174:

bt/frame/up/down:关于栈帧的gdb命令



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
章节导航