x86_64汇编之一:AT&T汇编语法

汇编语法主要有两大派系:AT&T语法 和 Intel语法。

GAS (GNU Assembler) 编译器默认是基于AT&T语法;MASM、NASM等编译器默认基于Intel语法。

需要说明的是,GAS汇编器除了支持AT&T语法之外,自己也定义了一些额外的directives,用于辅助完成汇编操作。关于GAS汇编器及其语法可以参考GAS的官方文档:https://sourceware.org/binutils/docs/as/

由于AT&T语法在Linux平台上使用比较广泛,本文将主要介绍AT&T语法。下面的文档对AT&T汇编语法作出了详细的总结,本文就不再赘述:

https://gist.github.com/lvhlvh/f6c456b95182d99385f2694257ac77e2

https://cs61.seas.harvard.edu/site/2018/Asm1/

下面仅针对上述文档做些补充和强调

1. 指令的长度后缀

AT&T语法要求在指令后面加上后缀表示长度,根据操作的是1字节、2字节、4字节、8字节,分别对应后缀b, w, l, q。例如movq $1, %rax

2. 跳转和函数调用指令的语法

AT&T语法中的跳转函数调用指令(jmp, jcc, call等)比较特殊,如下表所示:

在这里插入图片描述

上表给出了如果跳转和函数调用的操作数分别是寄存器、立即数、内存,该如何表示地址:

  • 寄存器:如果想要跳到寄存器%rax内容对应的地址,不是写成jmpq %rax,而是写成jmpq *%rax
  • 立即数:如上表所示,不用加*
  • 内存:如果想要跳转到%rip + 0x10对应的地址,不是写成jmpq 0x10(%rip),而是写成jmpq *0x10(%rip)

3. 指令寄存器相对寻址 (%rip-relative)

在x86_64汇编中,我们可以使用指令寄存器相对寻址这种寻址方式来定位全局变量和函数

例如,C代码中有一个全局变量a,我们可以使用a(%rip)来定位到该变量,而不是使用a来定位。

使用这种寻址方式能帮助我们编写出 position-independent code (PIC) 的代码,进而获取 position-independent executables (PIEs) 的执行文件。关于PIC和PIE的概念,涉及程序链接的细节,这里暂时不讲。

注意:汇编器在解析汇编代码时,不是将a(%rip)中的a解析成a的地址,而是会将a解析成变量a的地址和当前%rip的偏移,这也是为什么这种语法寻址a可行的原因。

参考:https://cs61.seas.harvard.edu/site/2018/Asm1/

4. 一个AT&T汇编文件代码分为哪些部分

一个AT&T汇编文件分为多个section,在文档https://gist.github.com/lvhlvh/f6c456b95182d99385f2694257ac77e2中有说明,这些section包括:

  • .text:存放代码对应的指令
  • .bss:存放未初始化的初始化的全局和静态变量,在运行时该区域初始是全0。.bss区域也可以用.comm/.lcomm和声明(https://sourceware.org/binutils/docs/as/bss.html#bss
  • .rodata:存放只读数据和变量,例如字符串字面量
  • .data:存放余下的数据和变量,可读可写

下面是一个C语言程序的内存布局,和汇编代码有一定的对应关系。
在这里插入图片描述

5. 如何编译和运行AT&T汇编代码

使用as命令对汇编文件进行汇编生成目标文件:as xxx.s -o xxx.o

然后使用ld命令对目标文件进行链接生成可执行文件:ld xxx.o -o xxx

注意,ld命令进行链接要求目标文件的.text段必须有一个入口点,ld默认认为_start标签对应的代码是入口点。

例如下面的汇编代码:

.section .data
output:
	.string "Hello\n"

.text
.globl _start
_start:
	movq $1, %rax
	movq %rax, %rsp # stack alignment

	movq $1, %rdi
	movq $output, %rsi
	movq $6, %rdx
	syscall

	movq $60, %rax
	xor %rdi, %rdi
	syscall

上面的汇编代码可以正常汇编和链接,但是,如果将代码中的_start标签去掉,或者改成其他名称,在执行ld命令之后就会报告一个警告信息:

ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0

ld会默认给我们找一个入口点,有可能这个默认入口点是错误的,这样就会导致运行可执行文件出现错误。

猜你喜欢

转载自blog.csdn.net/qq_29328443/article/details/107242121