汇编学习 - 汇编分类和mov、call指令

汇编分类

  汇编语言种类大致可以分为:8086汇编(16bit)、x86汇编(32bit)、x64汇编(64bit)以及嵌入式汇编等。根据书写格式的不同可将汇编分为:Intel汇编AT&T汇编。GCC编译器中默认使用的是AT&T汇编,两种格式的差异如下:
ass1
寻址方式的差异如下:
ass2

寄存器

  寄存器是cpu中的数据存储区域,cpu会先将内存中的数据存储到寄存器,再对寄存器中的数据进行运算。不同种类的汇编码中,寄存器也是不一样的,以最常用的几个通用寄存器为例:在64 bit汇编下的rax、rbx、rcx、rdx寄存器,在32 bit汇编下为eax、ebx、ecx、edx,在16 bit汇编下为ax、bx、cx、dx。64 bit汇编码是兼容32 bit和16 bit汇编码的,如下图所示:
ass3
在16位汇编下,AX寄存器由AH(高位)和AL(低位)组成,在32位汇编下AX寄存器则是EAX寄存器的低16位,同样在64位汇编下,EAX寄存器是RAX寄存器的低32位,示例如下:

movl    $0, %ax
movl    $11112222h, %eax

  以上汇编码执行完成之后,ax寄存器的值被改变了,不再是 0 ,而是 2222h,说明给eax寄存器赋值会改变低16位ax寄存器的值。对于rax和eax寄存器赋值也是如此。

mov和call指令

例如有以下示例代码:

#include <stdio.h>

void call_fun()
{
    int a = 10;
    int b = 2;
    int c = a +b;
    printf("%d\n",c);
}
int main()
{
    call_fun();
    return 0;
}

GCC编译器使用O0编译下得到汇编码如下:

call_fun:
 .LFB0:
     .cfi_startproc
     pushq   %rbp
     .cfi_def_cfa_offset 16
     .cfi_offset 6, -16
     movq    %rsp, %rbp
    .cfi_def_cfa_register 6
     subq    $16, %rsp
     movl    $10, -4(%rbp)
     movl    $2, -8(%rbp)
     movl    -4(%rbp), %edx
     movl    -8(%rbp), %eax
     addl    %edx, %eax
     movl    %eax, -12(%rbp)
     movl    -12(%rbp), %eax
     movl    %eax, %esi
     movl    $.LC0, %edi
     movl    $0, %eax
     call    printf
     nop
     leave
     .cfi_def_cfa 7, 8
     ret
     .cfi_endproc
 .LFE0:
     .size   call_fun, .-call_fun
     .globl  main
     .type   main, @function
 main:
 .LFB1:
     .cfi_startproc
     pushq   %rbp
     .cfi_def_cfa_offset 16
     .cfi_offset 6, -16
     movq    %rsp, %rbp
     .cfi_def_cfa_register 6
     movl    $0, %eax
     call    call_fun
     movl    $0, %eax
     popq    %rbp
     .cfi_def_cfa 7, 8
     ret
     .cfi_endproc
 .LFE1:
     .size   main, .-main
     .ident  "GCC: (GNU) 8.2.0"
     .section    .note.GNU-stack,"",@progbits 

  汇编指令call call_funcall printf都表示函数调用,调用call_fun函数和调用printf函数打印结果。
  汇编指令movl $10, -4(%rbp)中,-4(%rbp)等价于Intel汇编中的[rbp - 4],表示rbp寄存器减去4之后的地址值。整条汇编指令表示:将立即数10放到-4(%rbp)内存地址所在的存储空间。其中movll用于32位的长字值,与Intel汇编中的mov不同之处在于多了一个数据元素的长度。

  在call_fun和main之前都有pushq %rbpmovq %rsp, %rbp两条指令,做函数的序言(prologue)。在函数的结尾处都有popq %rbpret(epilogue)指令,它们组合形成维护函数的调用栈的作用。

  假设有AT&T汇编指令:movl $3, (1122h),其Intel汇编为:mov dword ptr [1122h], 3,表示将3放到1122h地址所在的存储空间,示意图如下:
ass4
如图所示,4字节大小空间如何存储数值3,根据小端模式,从低地址开始向高地址存储,而读取数据都是从低位地址开始往高地址读,在组合时从高地址开始组合到低地址,所以看到00000000 00000000 00000000 00000011在内存中的形式如上。

假设有如下示例代码:

int a = 10;
int b = 2;
void call_fun()
{
    int c = a + b;
    printf("%d\n",c);
}
int main()
{ 
   call_fun();
   return 0;
}

GCC编译器使用O0编译下得到汇编码如下:

     .file   "t.c"
     .text
     .globl  a
     .data
     .align 4
     .type   a, @object
     .size   a, 4
 a:
     .long   10
     .globl  b
     .align 4
     .type   b, @object
     .size   b, 4
 b:
     .long   2
     .section    .rodata
 .LC0:
     .string "%d\n"
     .text
     .globl  call_fun
     .type   call_fun, @function
 call_fun:
 .LFB0:
     .cfi_startproc
     pushq   %rbp
     .cfi_def_cfa_offset 16
     .cfi_offset 6, -16
     movq    %rsp, %rbp
     .cfi_def_cfa_register 6
     subq    $16, %rsp
     movl    a(%rip), %edx
     movl    b(%rip), %eax
     addl    %edx, %eax
     movl    %eax, -4(%rbp)
     movl    -4(%rbp), %eax
     movl    %eax, %esi
     movl    $.LC0, %edi
    movl    $0, %eax
     call    printf
     nop
     leave
     .cfi_def_cfa 7, 8
     ret
     .cfi_endproc
 .LFE0:
     .size   call_fun, .-call_fun
     .globl  main
     .type   main, @function
 main:
 .LFB1:
    .cfi_startproc
     pushq   %rbp
     .cfi_def_cfa_offset 16
     .cfi_offset 6, -16
     movq    %rsp, %rbp
     .cfi_def_cfa_register 6
     movl    $0, %eax
     call    call_fun
     movl    $0, %eax
     popq    %rbp
     .cfi_def_cfa 7, 8
     ret
     .cfi_endproc
 .LFE1:
     .size   main, .-main
     .ident  "GCC: (GNU) 8.2.0"
     .section    .note.GNU-stack,"",@progbits 

前面两个示例代码区别仅在于a、b两个变量是局部变量还是全局变量,但得到的汇编码有较大差异,如下图所示:
ass5
  如图可知,局部变量的汇编码地址进行了减法操作,通过rbp寄存器的偏移地址获得,即局部变量地址值不是固定的,而全局变量地址是固定的。
  movl (1122h),%eax,指令表示将1122h地址空间中的值取出放到eax,而指令lea (1122h),%eax表示直接将1122h这个地址值赋值给eax,类似于movl 1122h,%eaxlea表示装载有效的地址值,它和mov两者是不同的操作。

注意:

  • 汇编语言不区分大小写。
  • 一般R开头的寄存器是64 bit,占8字节,E开头的寄存器是32 bit,占4字节。
  • 小端模式:高字节放高地址,大端模式相反。

References:
  • https://www.bilibili.com/video/BV1HJ411z7ou?p=25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值