admin 管理员组文章数量: 888526
汇编学习
汇编分类
汇编语言种类大致可以分为:8086汇编(16bit)、x86汇编(32bit)、x64汇编(64bit)以及嵌入式汇编
等。根据书写格式的不同可将汇编分为:Intel汇编
和AT&T汇编
。GCC编译器中默认使用的是AT&T汇编
,两种格式的差异如下:
寻址方式的差异如下:
寄存器
寄存器是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汇编码的,如下图所示:
在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_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6subq $16, %rspmovl $10, -4(%rbp)movl $2, -8(%rbp)movl -4(%rbp), %edxmovl -8(%rbp), %eaxaddl %edx, %eaxmovl %eax, -12(%rbp)movl -12(%rbp), %eaxmovl %eax, %esimovl $.LC0, %edimovl $0, %eaxcall printfnopleave.cfi_def_cfa 7, 8ret.cfi_endproc.LFE0:.size call_fun, .-call_fun.globl main.type main, @functionmain:.LFB1:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6movl $0, %eaxcall call_funmovl $0, %eaxpopq %rbp.cfi_def_cfa 7, 8ret.cfi_endproc.LFE1:.size main, .-main.ident "GCC: (GNU) 8.2.0".section .note.GNU-stack,"",@progbits
汇编指令call call_fun
和call printf
都表示函数调用,调用call_fun函数和调用printf函数打印结果。
汇编指令movl $10, -4(%rbp)
中,-4(%rbp)
等价于Intel汇编中的[rbp - 4]
,表示rbp寄存器减去4之后的地址值。整条汇编指令表示:将立即数10放到-4(%rbp)内存地址所在的存储空间。其中movl
中l
用于32位的长字值,与Intel汇编中的mov
不同之处在于多了一个数据元素的长度。
在call_fun和main之前都有pushq %rbp
、movq %rsp, %rbp
两条指令,做函数的序言(prologue)。在函数的结尾处都有popq %rbp
、ret
(epilogue)指令,它们组合形成维护函数的调用栈的作用。
假设有AT&T汇编指令:movl $3, (1122h)
,其Intel汇编为:mov dword ptr [1122h], 3
,表示将3放到1122h地址所在的存储空间,示意图如下:
如图所示,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, 4a:.long 10.globl b.align 4.type b, @object.size b, 4b:.long 2.section .rodata.LC0:.string "%d\n".text.globl call_fun.type call_fun, @functioncall_fun:.LFB0:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6subq $16, %rspmovl a(%rip), %edxmovl b(%rip), %eaxaddl %edx, %eaxmovl %eax, -4(%rbp)movl -4(%rbp), %eaxmovl %eax, %esimovl $.LC0, %edimovl $0, %eaxcall printfnopleave.cfi_def_cfa 7, 8ret.cfi_endproc.LFE0:.size call_fun, .-call_fun.globl main.type main, @functionmain:.LFB1:.cfi_startprocpushq %rbp.cfi_def_cfa_offset 16.cfi_offset 6, -16movq %rsp, %rbp.cfi_def_cfa_register 6movl $0, %eaxcall call_funmovl $0, %eaxpopq %rbp.cfi_def_cfa 7, 8ret.cfi_endproc.LFE1:.size main, .-main.ident "GCC: (GNU) 8.2.0".section .note.GNU-stack,"",@progbits
前面两个示例代码区别仅在于a、b两个变量是局部变量
还是全局变量
,但得到的汇编码有较大差异,如下图所示:
如图可知,局部变量的汇编码地址进行了减法操作,通过rbp寄存器的偏移地址获得,即局部变量地址值不是固定的,而全局变量地址是固定的。
movl (1122h),%eax,
指令表示将1122h地址空间中的值取出放到eax,而指令lea (1122h),%eax
表示直接将1122h这个地址值赋值给eax,类似于movl 1122h,%eax
。lea
表示装载有效的地址值,它和mov
两者是不同的操作。
注意:
- 汇编语言不区分大小写。
- 一般R开头的寄存器是64 bit,占8字节,E开头的寄存器是32 bit,占4字节。
- 小端模式:高字节放高地址,大端模式相反。
References:
- =25
本文标签: 汇编学习
版权声明:本文标题:汇编学习 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1699080332h326928.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论