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表示输入的参数,从前往后统一编号(如上例中*dest表示%0,reset是%1,x是%2,y是%3),”r”表示整数寄存器,”=”表示对其进行了赋值。%eax要写成%%eax,这是c语言字符串的规则,别忘了code-string就是一个c语言的字符串。