DOS → 8086 → IA32
8086 → 80286 → i386 → i486 → Pentium → PentiumPro → Pentium II → Pentium III → Pentium 4 → Pentium 4E
→ Core 2 → Core i7
两种抽象极为重要:
1.机器级程序的格式和行为
指令集体系结构(ISA),它定义了处理器状态、指令的格式以及每条指令对状态的影响。
大多数ISA,包括ISA32和x86-64,将程序的行为描述成每条指令是按顺序执行的。
2.机器级程序使用的存储器地址是虚拟抵制,提供的存储器模型看上去是一个非常打的字节数组。
实际实现是将多个硬件存储器和操作系统软件组合起来。
汇编代码有一个主要特点:
它用可读性更好的文本格式来表示。
1.**程序计数器**(在IA32中,通常成为“PC”,用%eip表示)指示将要执行的下一条指令在存储器中的地址。
2.**整数寄存器文件**包含8个命名的为止,分别存储32位的值。用于存储地址或整数数据。
3.**条件码寄存器**保存着最近执行的算术或者逻辑指令的状态信息。用于实现控制和数据流中的条件变化。
包含程序的可执行机器代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的存储器块。
用虚拟地址来寻址。操作系统负责管理虚拟地址空间,将虚拟抵制翻译成实际处理器存储器中的物理地址。
利用gcc -O1 -S xxx.c -o xxx.s将C语言代码编译成汇编代码
如下图,将code.c编译成code.s
由于本人使用的是Ubuntu 64位系统所以使用gcc -m32 -S xxx.c -o xxx.s所得到的汇编代码更接近教材所示。
也可以用objdump -d xxx进行反汇编
查看-S生成的汇编文件时,出现很多"."开头的语句,将其删除后便于阅读。
可以用od 命令查看,也可以用gdb的x命令查看。
Intel格式代码的生成:
gcc -S -masm=intel xxx.c
区别
IA32CPU包含一组8个存储32位值的寄存器,用于存储整数数据和指针,名称都以%e开头。
4个数据寄存器(EAX、EBX、ECX和EDX):
32位CPU有4个32位的通用寄存器EAX、EBX、ECX和EDX。对低16位数据的存取,不会影响高16位的数据。这些低16位寄存器分别命名
为:AX、BX、CX和DX,它和先前的CPU中的寄存器相一致。
4个16位寄存器又可分割成8个独立的8位寄存器(AX:AH-AL、BX:BH-BL、CX:CH-CL、DX:DH-DL),每个寄存器都有自己的名称,可
独立存取。程序员可利用数据寄存器的这种“可分可合”的特性,灵活地处理字/字节的信息。
2个变址和指针寄存器(ESI和EDI)
32位CPU有2个32位通用寄存器ESI和EDI。其低16位对应先前CPU中的SI和DI,对低16位数据的存取,不影响高16位的数据。
寄存器ESI、EDI、SI和DI称为变址寄存器(Index Register),它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储
器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。
变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。它们可作一般的存储器指针使用。
在字符串操作指令的执行过程中,对它们有特定的要求,而且还具有特殊的功能。
2个重要的指针寄存器(ESP和EBP)
ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部
注意%ebp和%esp保存着指向程序栈中重要位置的指针。
字节操作可以对%eax/%ecx/%edx/%ebx的2个低位字节进行读写操作。
格式表示:Imm (Eb,Ei,s) *其中Imm为立即数偏移,Eb位基址寄存器,Ei为变址寄存器,s为比例因子,s必须位1、2、4或8.
传送指令MOV(Move)把一个字节、字或双字的操作数从源位置传送到目的位置,源操作数的内容不变。
格式:MOV DST,SRC
实例:
应该注意的是:
p117代码展示:
一元操作、二元操作、移位操作均会影响条件码
SUB S2,S1 改变标志位,但相减的结果会赋予S1
典型用法:testl %eax,%eax用来检查%eax是负数、零,还是正数。
基于set的不同组合
C语言中的if-else语句通用模版:
if(test-expr)
then-statement
else
else-statement
汇编相对应的通用形式
t = test-expr;
if(!t)
goto false;
then-statement
goto done;
false:
else-statement
done:
汇编为then-statement 和 else-statement产生了各自的代码块。
通用形式:
do
body-statment
while(test-expr);
对应的goto语句:
loop:
body-statement
t = test-expr;
if(t)
goto loop;
body-statemnt至少会执行一次
通用形式:
while(test-expr)
body-statement
对应的goto语句
t = test-expr;
if(!t)
goto done;
loop:通过条件才执行依次。
通用形式:
for(init-expr;test-expr;update-expr)
body-statement
对应的goto语句
init-expr;
t = test-expr;
if(!t)
goto done;
loop:
body-statement
update-expr
t = test-expr;
if(t)
goto loop;
done:
运用到了跳转表(jump table),跳转表为一个数组,表项i是一个代码段的地址,这个代码段实现打那个开关索引值等于i时程序应采取的措施
编译后去除.开头的语句得到以下语句:
add:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $15, %eax
popl %ebp
function:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call add
leave
ret
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $15, (%esp)
call function
addl $8, %eax
leave
ret
1.在加载有效地址 %edx=x leal 7(%edx,%edx,4),%eax对此并不理解为什么表示的为将设置寄存器%eax的值为5x+7
解决:通过网络的查询,得知此为AT&T语法,base(offset, index, i),就是 base+offset+index*i
相对应的则是7+x+x*4=5*x+7。
2.在查阅CMP与SUB之间的区别时,发现百度百科上CMP对于操作数的执行顺序与教材中的不同。
解决:询问老师,得知在Linux系统中CMP S2,S1表示S1-S2,而在Windows系统中 CMP S2,S1 表示S2-S1。