汇编存在多种形式,目前主流的有AT&T汇编和INTEL汇编,这两种形式极其容易混淆,其中AT&T汇编语法被Linux和GCC广泛支持,而INTEL则由Windows支持,故而对于长期涉及到两种操作系统编程的人而言,这两种汇编语法虽本质相同,但是语法形式的差异常常让人混淆。这里便介绍了两者的主要区别。
1. 赋值方向不同
INTEL汇编语法第一个操作数表示目的操作数,第二个才是源操作数,赋值方向为从右向左; mov eax , ebx //eax = eax + ebx
AT&T汇编语法则反过来,第一个为源操作数,第二个为目的操作数,故而看着更舒服。 movl %ebx, %eax //eax = eax + ebx
2. 前缀修饰符不同
其实从上面已经可以看到,除了赋值方向不同,INTEL的寄存器似乎是直接存取的,而AT&T则需要加上“%”前缀。其实INTEL语法中寄存器和立即数都不需要前缀修饰,而AT&T则均需要修饰
### INTEL ###
mov eax, 1 //eax = 1,将立即数1赋值填充到eax寄存器中
### AT&T ###
movl $1, %eax //寄存器前缀加"%", 立即数前缀加"$"
3. 后缀修饰符位置不同
AT&T
在汇编时经常会碰到mov
, movl
,movb
等组合出现,很是让人费解,其实mov
后接的后缀是反应当前操作式的操作数字长。
‘b’ : byte 一字节; ‘w’: word 两字节; ‘l’: long 四字节
当前计算机分为32位,64位,也存在着32位寄存器和64位寄存器的区别,而操作数字节数存在word\dword\long\ long long的区别,寄存器资源是很宝贵的,所以同一个寄存器可能高位存放着一个操作数a, 低位存放着另一个操作数b,所以在操作指令中明确指出当前操作数的字长是很必要的。
/*下面指令针对的是32位操作系统*/
1. movb %bl, %al //声明当前操作数为1字节长,则取ebx寄存器的低8位(1字节)作为操作数
2. movw %bx, %ax //声明操作数为2字节长,取ebx寄存器的低16位(2字节)作为操作数
3. movl (%ebx), %eax //声明操作数为4字节长,ebx寄存器的32位全部空间全取
所以如果在编写程序时,在程序中人工插入了汇编码,并且是按照32位的分布格式来设计代码的,则需要在使用GCC编译器显示申明按照32位ELF格式编辑-m32
。
涉及到多个操作数,则必然涉及到类型转换。如果两个操作数的字长不一样,显然此时用mov[b,w,l]
是不够的,这时便需要使用扩展指令movs
,后接bl\ bw\ wl
三种合法扩展后缀中的一种
‘bl’ : 单字节->4字节 ; ‘bw’: 单字节->2字节; ‘wl’: 2字节->4字节
可以看到只存在低到高的扩展,不存在wb
这种反向的缩减,因为寄存器存放时会自动截断。
应用扩展指令movs
的例子如
movsbl %al, %ebx //将eax寄存器的低8位(1字节)先扩展成4字节数,然后塞进ebx寄存器中
INTEL
INTEL则和AT&T不同的是,并不是把后缀符加在mov
后面,除非是申明内存地址存取时,才有相对应的语法:byte ptr\ word ptr\ dword ptr
/*同样是针对32位操作系统,并和上面的汇编代码一一对应*/
1. mov al, bl
2. mov ax, bx
3. mov eax, dword ptr[ebx] //INTEL语法中基地址是用[]包括的,外部才是偏移量或修饰符
还有一个后缀符号,用于跳转命令的方向修饰,如jmp
,跳转需要给出跳转步长,和跳转方向,则存在如下
1. jmp 10f //向前forwad跳10步
2. jmp 10b //后退back 10步
4. 间接寻址语法
其实前面提到过,AT&T语法中采用()圈定基地址,而INTEL采用[]圈定基地址,所以直接给出两者例子的对比
AT&T: movl 8(%ebx), %eax
INTEL: mov eax, dword ptr[ebx+8]
而其实AT&T的寻址语法还是蛮有意思的,其间接寻址语法格式如下
偏移量M ( 基地址base,间隔步长step,步长数n ) = * (base+step * n + M)
使用案例如下
1. 0xfc(, %eax, 4) = *(eax*4 + 0xfc)
2. 8(%eax,%ebx,4) = *(eax + ebx*4 + 8)
3. 9(%eax, %ebx) = *(eax + ebx*1 + 9) //步长数如果省略,默认为1
INTEL的寻址语法则直接比较粗暴了,间接寻址语法格式如下
修饰符 [ 基地址base + 步长*步长数 + 偏移量]
5. AT&T指令集
INTEL语法的指令集合AT&T指令集基本相同,由于AT&T更好看点,所以下面便介绍AT&T的指令集。
常见操作指令
64位乘除操作指令
对于32位系统而言,32位寄存器内做加减运算倒是简单,但是如果做32位乘除很容易跳出了32位限制,所以需要用两个寄存器联合使用来保存乘除运算的操作数或结果。一般而言都是将64位数的高32位放在edx,低32位放在eax寄存器中。
比较指令
条件码寄存器相关指令
条件码寄存器存放着最近一次运算的属性,说明如下
1. CF: 进位标志,最高位产生了进位
2. OF: 溢出标志,补码溢出
3. ZF: 零标志,上次结果是否为0
4. SF: 符号标志, 上次结果符号位
跳转指令
至此,关于基本汇编的内容收集结束。