AT&T 汇编常用指令
汇编常用指令
1、操作数
-
立即数:表示常数值
表示方法:"$"+标准C表示法表示的整数 例:$0x1F
-
寄存器:表示某个寄存器的内容
表示方法:"%"+寄存器的名称 例:%eax
-
内存引用:会根据计算出来的地址访问某个内存位置。
表示方法:这个表示方法比较多,可以利用寄存器进行间接寻址,也可以通过一个整数进行绝对寻址,也可以通过寄存器加减来寻址,例:0x104、(%rax)、4(%rax)
(tips:4(%rax)=(%rax+4))
2、数据传送指令
指令模板类一:MOV S,D(通过MOV类指令把数据从S复制到D)
MOV类由四条指令组成:movb、movw、movl、和movq。
- movb(传送字节)
- movw(传送字)
- movl(传送双字)
- movq(传送四字)
- movabsq(传送绝对的四字)
movq和movabsq的区别在于,movq指令只能以表示为32位补码数字的立即数作为源操作数,movabsq能够以任意64位立即数值作为源操作数,并且只能以寄存器为目的。
指令模板类二:MOVZ S,R(将较小的源值复制到较大的目的时使用)
该MOV类指令,在转移数据的时候,会把源值做零扩展后再转移到目的
零扩展:将目标的高位设置为0,通常针对无符号数字
- movzbw(将做了零扩展的字节传送到字)
- movzbl(将做了零扩展的字节传送到双字)
- movzwl(将做了零扩展的字传送到双字)
- movzbq(将做了零扩展的字节传送到四字)
- movzwq(将做了零扩展的字传送到四字)
指令模板类三:MOVS S,R (与MOVZ指令功能一致)
与MOV不同的点在于MOVS对源值做的是符号扩展。
符号扩展:保留数字的符号(正/负)和值的同时增加二进制数的位数,通常针对有符号数字
- movsbw(将做了符号扩展的字节传送到字)
- movsbl(将做了符号扩展的字节传送到双字)
- movswl(将做了符号扩展的字传送到双字)
- movsbq(将做了符号扩展的字节传送四字)
- movswq(将做了符号扩展的字传送到四字)
- movslq(将做了符号扩展的双字传送到四字)
3、数据传输实例
C语言代码:
long exchange(long *xp,long y)
{
long x = *xp;
*xp = y;
return x;
}
输入命令:
gcc -Og -S hello.c
生成汇编文件,查看文件内容:
long exchange(long *xp, long y)
xp in %rdi,y in %rsi
exchange:
.LFB11:
.cfi_startproc
movq (%rdi), %rax
movq %rsi, (%rdi)
ret
.cfi_endproc
关注第四行~第六行,第四行xp指向的内容被mov给rax寄存器,第五行y被mov给rdi寄存器储存的地址的内存,第六行ret,将%rax储存的内容返回,也就是xp指向的内容。
4、压入栈和弹出栈
-
pushq S
功能:将一个四字值压入栈中。
过程:先将栈指针减8,再将值写到栈顶地址
-
popq
功能:弹出一个四字,并从栈顶位置读出数据
过程:首先将栈顶的数据读出来,再将栈指针加8
5、算术逻辑操作
指令 | 描述 |
---|---|
leaq S,D | 将有效地址S读取到D寄存器,类似C中的& |
INC D | 对D加1 |
DEC D | 对D减1 |
NEG D | 对D取负 |
NOT D | 对D取补 |
ADD S,D | D+S,并将值储存在D中 |
SUB S,D | D-S,并将值储存在D中 |
IMUL S,D | D*S,并将值储存在D中 |
XOR S,D | S^D,并将值储存在D中 |
OR S,D | D|S,并将值储存在D中 |
AND S,D | D&S,并将值储存在D中 |
SAL k,D | D左移k位,并将值储存在D中 |
SHL k,D | 与SAL的含义一致 |
SAR k,D | 算数右移(填上0) |
SHR k,D | 逻辑右移(填上符号位) |
6、控制
6.1条件操作
除了寄存器,CPU维护着一组单个位的条件码寄存器,常用的条件码:
- CF:进位标志。最近的操作使最高位产生了进位,可用来检查无符号操作的溢出
- ZF:零标志。最近的操作得出的结果为0
- SF:符号标志。最近的操作得到的结果为负数
- OF:溢出标志,最近的操作导致了一个补码溢出
6.2 跳转指令
指令 | 跳转条件 | 描述 |
---|---|---|
jmp Label | 1 | 直接跳转 |
jmp *Operand | 1 | 间接跳转 |
je Label | ZF | 相等/零 |
jne Label | ~ZF | 不相等/非零 |
js Label | SF | 负数 |
jns Label | ~SF | 非负数 |
jg Label | (SF^OF)&ZF | 大于(有符号>) |
jge Label | ~(SF^OF) | 大于或等于(有符号>=) |
jl Label | SF^OF | 小于(有符号<) |
jle Label | (SF^OF)|ZF | 小于或等于(有符号<=) |
ja Label | CF&ZF | 超过(无符号>) |
jae Label | ~CF | 超过或相等(无符号>=) |
jb Label | CF | 低于(无符号<) |
jbe Label | CF|ZF | 低于或相等(无符号<=) |
上面表格中,Label一般表示的是一个标号,例如下面代码:
movq $0,%rax
jmp .L1 //此行代表直接跳过下行代码,从.L1处开始执行
movq (%rax),%rdx
.L1:
popq %rdx
而*Operand表示的是用Operand代表的值作为跳转目标,例如
jmp *%rax //用寄存器%rax中的值作为跳转目标
jmp *(%rax) //以%rax中的值作为读地址,在从内存中读出跳转目标
上面表格中除了jmp,还有许多jmp和条件码的组合的跳转指令,当满足跳转条件的时候,它们就会跳转到Label的位置。
6.3 循环
汇编没有相应的指令来表示循环,一般是将条件测试和跳转组合起来实现循环的效果,例如以下代码:
movl $1,%eax
loop:
imulq %rdi,%rax
subq $1,%rdi //每次循环将rdi寄存器的值减一
cmpq $1,%rdi //比较1和rdi寄存器的值
jg loop //若rdi>1,则保持循环
其循环等效于以下类C语言形式的代码:
long result = 1;
while(n>1)
{
result *= n;
n--;
}
6.4 转移控制
当在函数P中调用函数Q的时候,就需要用到转移控制指令CALL,CALL指令将控制从函数P转移到函数Q时,把程序计数器设置为Q的代码的起始位置,同时记录稍后要继续P的执行代码的位置,既然有控制转移,那就有返回,一般与CALL指令配合使用的返回指令为ret。下表给出CALL和RET的一般形式:
指令 | 描述 |
---|---|
call Label | 过程调用 |
call *Operand | 过程调用 |
ret | 从过程调用中返回 |