x86-64汇编:基础语言

文章目录

    • 访问信息
    • 操作数指示符
    • 数据传送指令
    • 压入和弹出栈数据
    • 加载有效地址(load effective address)指令
    • 算术和逻辑操作
    • 乘除运算

总结自《深入理解计算机系统》第三版


访问信息

x86-64的CPU包含一组16个存储64位值的通用目的寄存器,用于存放整数和指针。
x86-64汇编:基础语言_第1张图片
注意:

  • 生成1字节和2字节数字的指令会保持剩下的字节不变
  • 生成4字节数字会把高位4个字节置位0,作为从IA32的扩展部分

操作数指示符

操作数类型分为

  • 立即数(immediate):$后接整数,汇编器会自动选择最紧凑的方式进行数值编码。
  • 寄存器(register):ra表示寄存器a,寄存器组看做是一个数组R,将ra看做索引,R[ra]即表示寄存器的值
  • 内存引用:将内存看做一个数组Mb[Addr]

x86-64汇编:基础语言_第2张图片
常用形式Imm(rb,ri,s)
基址变址寄存器rb和ri必须是64位寄存器,s只能是1、2、4、8。有效地址的计算为Addr=Imm+R[rb]+R[ri]*s,所以以操作数形式出现的Imm(rb,ri,s)表示M[Imm(rb,ri,s)]。
其他形式都是此形式的变式,Imm表示M[Imm],(rb)表示以R[rb]为有效地址的M[R[rb]]。


数据传送指令

mov指令

x86-64汇编:基础语言_第3张图片

  • 后缀’b’,‘w’,‘l’,'q’分别代表字节、字、双字、四字
  • movabsq指令,将任意64位立即数送入寄存器
  • 源、目的寄存器字节大小相同
  • 传送指令的操作数不能都是内存,将值从一个位置到另一个位置需要两条指令——将源值加载到寄存器,将寄存器写入到目的位置
  • mov指令只更新指定的寄存器字节或内存位置,而movl指令以寄存器为目的是,会将高4字节设置为0
movabsq  $0x0011223344556677,%rax         ;%rax=0011223344556677
movb     $-1,%al                          ;%rax=00112233445566FF
movw     $-1,%ax                          ;%rax=001122334455FFFF
movl     $-1,%eax                         ;%rax=00000000FFFFFFFF
movq     $-1,%rax                         ;%rax=FFFFFFFFFFFFFFFF

movz指令(零扩展,将较小源值复制到较大目的)
x86-64汇编:基础语言_第4张图片
注意:在mov指令中源操作数为双字(后缀l)将高位设为0,因而movz指令缺少movzlq。

movs指令(符号扩展,将较小源值复制到较大目的)
x86-64汇编:基础语言_第5张图片

  • cltq只用于寄存器%eax和%rax,且它与movzlq %eax,%rax一致。
movabsq  $0x0011223344556677,%rax        ;%rax=0011223344556677
movb     $0xAA,%dl                       ;%dl=AA
movb     %dl,%al                         ;%rax=00112233445566AA
movzbq   %dl,%rax                        ;%rax=00000000000000AA
movsbq   %dl,%rax                        ;%rax=FFFFFFFFFFFFFFAA

压入和弹出栈数据

x86-64汇编:基础语言_第6张图片
将四字值压入栈,首先将栈指针减8,然后将值写入新栈顶地址。因此下式相等,区别在于pushq只有一个字节,而两条指令要8个字节。

pushq %rax   =   subq  $8,%rsp             ;Decrement stack pointer
                 movq  %rax,(%rsp)         ;Store %rax on stack
pop %rax     =    movq (%rsp),%rdx         ;Read %rdx from stack
                  addp $8,%rsp             ;Increment stack pointer

例子:
x86-64汇编:基础语言_第7张图片
注意:

  • 栈向低地址方向增长,所以压栈减小栈指针%rsp的值(%rsp中存有效地址,所以存值到内存加括号),并将数据存放在内存,出栈相反。
  • 栈和程序代码以及其他形式的程序数据都放在同一内存,所以程序可以用标准的内存寻址方式访问栈的任意位置,例如:movq 8(%rsp),%rax将第二个四字从栈中取出。

加载有效地址(load effective address)指令

形式是从内存读数据到寄存器,实际上没有引用内存,并不是从内存读数据带到寄存器,而是将有效位置带到寄存器,例如M[R[rb]]中的有效地址R[rb]。
两个用法:

  1. 为内存引用产生指针,类似C的取址符&
  2. 执行普通的算术操作(单纯将寄存器中的有效地址看做数),但这种方法比用算术操作方便得多

C程序代码

long scale(long x,long y,long z){
     long t=x+4*y+12*z;
     return t;
}

汇编代码

;long scale(long x,long y,long z)
;x in %rdi,y in %rsi,z in %rdx

scale:
   leaq (%rdi,%rsi,4),%rax   ;x+4*y
   leaq (%rdx,%rdx,2),%rdx   ;z+2*z=3*z
   leaq (%rax,%rdx,4),%rax   ;(x+4*y)+4*(3*z)=x+4*y+12*z
   ret

算术和逻辑操作

x86-64汇编:基础语言_第8张图片
注意:

  1. 第三组的二元操作数,第一个操作数可以是立即数、寄存器或内存,第二个是寄存器或内存(若为内存地址,则处理器必须从内存度读出值,执行操作,然后写回内存)。
  2. 移位操作的移位量可以是立即数或单字节寄存器%cl中的值。

C程序代码

long arith(long x,long y,long z){
    long t1=x^y;
    long t2=z*48;
    long t3=t1&0x0F0F0F0F;
    long t4=t2=t3;
}

汇编代码

;long arith(long x,long y,long z)
;x in %rdi,y in %rsi,z in %rdx

arith:
   xorq %rsi,%rdi               ;t1=x^y 
   leaq (%rdx,%rdx,2),%rax      ;3*z
   salq $4,%rax                 ;t2=16*(3*z)=48*z
   andl $252645135,%edi         ;t3=t1&0x0F0F0F0F
   subq %rdi,%rax               ;t2-t3
   ret  

上面代码可以看出

  1. 将乘法的一部分分解为逻辑运算
  2. 一个寄存器会存放多个程序值

乘除运算

x86-64汇编:基础语言_第9张图片
汇编器可通过计算操作数数目,分辨出哪条指令。

你可能感兴趣的:(反汇编,指针)