AT&T汇编指令

我们知道,LinuxUnix家族的一员,尽管Linux的历史不长,但与其相关的很多事情都发源于Unix。就Linux所使用的386汇编语言而言,它也是起源于UnixUnix最初是为PDP-11开发的,曾先后被移植到VAX68000系列的处理器上,这些处理器上的汇编语言都采用的是AT&T的指令格式。当Unix被移植到i386时,自然也就采用了AT&T的汇编语言格式,而不是Intel的格式。尽管这两种汇编语言在语法上有一定的差异,但所基于的硬件知识是相同的,因此,如果你非常熟悉Intel的语法格式,那么你也可以很容易地把它“移植“到AT&T来。下面我们通过对照IntelAT&T的语法格式,以便于你把过去的知识能很快地“移植”过来。

GAS中每个操作都是有一个字符的后缀,表明操作数的大小。

C声明 GAS后缀 大小(字节)
char b 1
short w 2
(unsigned) int / long / char* l 4
float s 4
double l 8
long double t 10/12

注意:GAL使用后缀“l”同时表示4字节整数和8字节双精度浮点数,这不会产生歧义因为浮点数使用的是完全不同的指令和寄存器。

操作数格式:

格式 操作数值 名称 样例(GAS = C语言)
$Imm Imm 立即数寻址 $1 = 1
Ea R[Ea] 寄存器寻址 %eax = eax
Imm M[Imm] 绝对寻址 0x104 = *0x104
(Ea) M[R[Ea]] 间接寻址 (%eax)= *eax
Imm(Ea) M[Imm+R[Ea]] (基址+偏移量)寻址 4(%eax) = *(4+eax)
(Ea,Eb) M[R[Ea]+R[Eb]] 变址 (%eax,%ebx) = *(eax+ebx)
Imm(Ea,Eb) M[Imm+R[Ea]+R[Eb]] 寻址 9(%eax,%ebx)= *(9+eax+ebx)
(,Ea,s) M[R[Ea]*s] 伸缩化变址寻址 (,%eax,4)= *(eax * 4)
Imm(,Ea,s) M[Imm+R[Ea]*s] 伸缩化变址寻址 0xfc(,%eax,4)= *(0xfc+eax * 4)
(Ea,Eb,s) M(R[Ea]+R[Eb]*s) 伸缩化变址寻址 (%eax,%ebx,4) = *(eax+ebx * 4)
Imm(Ea,Eb,s) M(Imm+R[Ea]+R[Eb]*s) 伸缩化变址寻址 8(%eax,%ebx,4) = *(8+eax+ebx * 4)

注:M[xx]表示在存储器中xx地址的值,R[xx]表示寄存器xx的值,这种表示方法将寄存器、内存都看出一个大数组的形式。

数据传送指令:

指令 效果 描述
movl S,D D ← S 传双字
movw S,D D ← S 传字
movb S,D D ← S 传字节
movsbl S,D D ← 符号扩展S 符号位填充(字节->双字)
movzbl S,D D ← 零扩展S 零填充(字节->双字)
pushl S R[%esp] ← R[%esp] – 4; M[R[%esp]] ← S 压栈
popl D D ← M[R[%esp]]; R[%esp] ← R[%esp] + 4; 出栈

注:均假设栈往低地址扩展。

算数和逻辑操作地址:

指令 效果 描述
leal S,D D = &S movl S地址入D,D仅能是寄存器
incl D D++ 加1
decl D D– 减1
negl D D = -D 取负
notl D D = ~D 取反
addl S,D D = D + S
subl S,D D = D – S
imull S,D D = D*S
xorl S,D D = D ^ S 异或
orl S,D D = D | S
andl S,D D = D & S
sall k,D D = D << k 左移
shll k,D D = D << k 左移(同sall)
sarl k,D D = D >> k 算数右移
shrl k,D D = D >> k 逻辑右移

特殊算术操作:

指令 效果 描述
imull S R[%edx]:R[%eax] = S * R[%eax] 无符号64位乘
mull S R[%edx]:R[%eax] = S * R[%eax] 有符号64位乘
cltd S R[%edx]:R[%eax] = 符号位扩展R[%eax] 转换为4字节
idivl S R[%edx] = R[%edx]:R[%eax] % S; R[%eax] = R[%edx]:R[%eax] / S; 有符号除法,保存余数和商
divl S R[%edx] = R[%edx]:R[%eax] % S; R[%eax] = R[%edx]:R[%eax] / S; 无符号除法,保存余数和商

注:64位数通常存储为,高32位放在edx,低32位放在eax。

条件码:

条件码寄存器描述了最近的算数或逻辑操作的属性。

CF:进位标志,最高位产生了进位,可用于检查无符号数溢出。

OF:溢出标志,二进制补码溢出——正溢出或负溢出。

ZF:零标志,结果为0。

SF:符号标志,操作结果为负。

比较指令:

指令 基于 描述
cmpb S2,S1 S1 – S2 比较字节,差关系
testb S2,S1 S1 & S2 测试字节,与关系
cmpw S2,S1 S1 – S2 比较字,差关系
testw S2,S1 S1 & S2 测试字,与关系
cmpl S2,S1 S1 – S2 比较双字,差关系
testl S2,S1 S1 & S2 测试双字,与关系

访问条件码指令:

指令 同义名 效果 设置条件
sete D setz D = ZF 相等/零
setne D setnz D = ~ZF 不等/非零
sets D D = SF 负数
setns D D = ~SF 非负数
setg D setnle D = ~(SF ^OF) & ZF 大于(有符号>)
setge D setnl D = ~(SF ^OF) 小于等于(有符号>=)
setl D setnge D = SF ^ OF 小于(有符号<)
setle D setng D = (SF ^ OF) | ZF 小于等于(有符号<=)
seta D setnbe D = ~CF & ~ZF 超过(无符号>)
setae D setnb D = ~CF 超过或等于(无符号>=)
setb D setnae D = CF 低于(无符号<)
setbe D setna D = CF | ZF 低于或等于(无符号<=)

跳转指令:

指令 同义名 跳转条件 描述
jmp Label 1 直接跳转
jmp *Operand 1 间接跳转
je Label jz ZF 等于/零
jne Label jnz ~ZF 不等/非零
js Label SF 负数
jnz Label ~SF 非负数
jg Label jnle ~(SF^OF) & ~ZF 大于(有符号>)
jge Label jnl ~(SF ^ OF) 大于等于(有符号>=)
jl Label jnge SF ^ OF 小于(有符号<)
jle Label jng (SF ^ OF) | ZF 小于等于(有符号<=)
ja Label jnbe ~CF & ~ZF 超过(无符号>)
jae Label jnb ~CF 超过或等于(无符号>=)
jb Label jnae CF 低于(无符号<)
jbe Label jna CF | ZF 低于或等于(无符号<=)

转移控制指令:(函数调用):

指令 描述
call Label 过程调用,返回地址入栈,跳转到调用过程起始处,返回地址是call后面那条指令的地址
call *Operand
leave 为返回准备好栈,为ret准备好栈,主要是弹出函数内的栈使用及%ebp

用GCC在C中潜入汇编代码:

asm( code-string [:output-list [ : input-list [ :overwrite-list]]]);

注意,后面的参数(如overwrite-list)如果为空则不要相应的“:”,而如果前面参数(如output-list)为空则需要用“:”占位。

如:

asm ("..."
    :                    //output需要占位
    : "r" (src)       //后面的Overwrites不能写,我测试的结果是写了编译不过
};

如:

int ok_umul(unsigned x,unsigned y,unsigned *dest) {
  int result;
asm(“movl %2 , %%eax; mull %3; movl %%eax,%0;\
           setae %dl; movzbl %%dl,%1”
           :  “=r” (*dest)  ,  “=r” (result)         //output
           :  “r” (x)  ,  “r” (y)                         //inputs
           :  “%ebx”  , “%edx”                        //Overwrites
);
return result;
}

我们用%0--%n表示输入的参数,r表示整数寄存器,=表示对其进行了赋值。%eax要写成%%eax,这是c语言字符串的规则,别忘了code-string就是一个c语言的字符串。