源码 test.c
#include <stdio.h>
int main()
{
printf("hello world!");
return 0;
}
gcc -S test.c 生成test.s
.file "test.c"
.text
.def __main; .scl 2; .type 32; .endef
.section .rdata,"dr"
.LC0:
.ascii "hello world!\0"
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
pushq %rbp
.seh_pushreg %rbp
movq %rsp, %rbp
.seh_setframe %rbp, 0
subq $32, %rsp
.seh_stackalloc 32
.seh_endprologue
call __main
leaq .LC0(%rip), %rcx
call printf
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (GNU) 7.3.0"
.def printf; .scl 2; .type 32; .endef
注意汇编程序由三个不同的元素组成:
- 指示(Directives) 以点号开始,用来指示对编译器,连接器,调试器有用的结构信息。指示本身不是汇编指令。例如,.file 只是记录原始源文件名。.data表示数据段(section)的开始地址, 而 .text 表示实际程序代码的起始。.string 表示数据段中的字符串常量。 .globl main指明标签main是一个可以在其它模块的代码中被访问的全局符号 。至于其它的指示你可以忽略。
- 标签(Labels) 以冒号结尾,用来把标签名和标签出现的位置关联起来。例如,标签.LC0:表示紧接着的字符串的名称是 .LC0. 标签main:表示指令 pushq %rbp是main函数的第一个指令。按照惯例, 以点号开始的标签都是编译器生成的临时局部标签,其它标签则是用户可见的函数和全局变量名称。
-
指令(Instructions) 实际的汇编代码 (pushq %rbp), 一般都会缩进,以便和指示及标签区分开来。
==================================网络资料=======================================
寄存器和数据类型
x86-64有大约十六个通用64位整数寄存器:
我们说”大约十六个通用”是因为早期版本的处理器每个寄存器都有其特殊用途,并不是所有指令都可以应用到每一个寄存器。随着设计的进展,新的指令和寻址模式被添加进来,使得很多寄存器变成了等同的。少数留下来的指令,特别是和字符串处理相关的,要求使用%rsi 和%rdi。另外,两个寄存器被保留下来分别作为栈指针 (%rsp) 和基址指针 (%rbp)。最后的8个寄存器是编号的并且没有特殊限制。
多年来,体系结构从8位扩展到16位,32位,因此每个寄存器都有一些内部结构:
%rax的低8位是8位寄存器%al, 仅靠的8位是%ah。低16位是 %ax, 低32位是 %eax,整个64位是%rax。
寄存器%r8-%r15也有相同结构,但命名方式稍有不同:
为了简单点,我们只关注64位寄存器。不过大多数编译器产品混合使用32和64位模式。32位寄存器用来做整数计算,因为大多数程序不需要大于 2^32 的整数值。64位一般用来保存内存地址(指针),使得可以寻址到16EB虚拟内存。
寻址模式
你最先应该了解的是MOV指令,它在寄存器和内存之间移动数据。X86-64 使用复杂指令集 (CISC),所以MOV指令有很多不同的变种以便在不同的存储单元之间移动不同的数据类型。
MOV和其他指令一样,有一个决定移动多大数据的单字母前缀:
不同数据有不同的寻址模式。全局值(全局变量和函数)的引用直接使用名字,例如x或者printf。
常数使用带有美元符号的立即数,例如$56。寄存器值的引用使用寄存器的名称,例如 %rbx.
间接寻址则是使用与寄存器中保存的地址值对应的内存中的值,例如,(%rsp) 表示%rsp指向的内存中的值。相对基址寻址,则是把一个常数加到寄存器值上,例如 -16(%rcx)表示把%rcx指向的地址前移16个字节后对应的内存值。寻址模式对于管理栈,局部变量,函数参数很重要。相对基址寻址有很多复杂变种,例如-16(%rbx,%rcx,8)表示-16+%rbx+%rcx*8对应的地址的内存值,这种寻址模式在访问元素大小特殊的数组时很有用。
如下是使用各种寻址模式加载一个64位值到%rax:
在大多数情况下,相同的寻址模式可用于将数据存储到寄存器和内存中。但是,并非所有模式都支持。例如,不可能对MOV的两个参数使用相对基址寻址:MOVQ -8(%rbx), -8(%rbx)
。要查看是否支持寻址模式的组合,您需要阅读手册的相关说明。