X86-64汇编基本语法

.LC0:
.string “Helloworld”

这里的汇编是GNU汇编,即使用AT&T语法的汇编,请不要和别的汇编器混淆。

指令助记符号与伪操作

伪操作

在汇编程序中,以’.’开头的名称并不是指令助记符,不会被翻译为机器码,而是给汇编器的特殊指令,叫做伪操作(实际上是汇编会执行的操作,同时也辅助人们阅读),常见伪操作及其含义如下:

.section及常用段

.section 用于把代码划分为若干段,每个段会被加载至不同地址,同时不同的页面会有相应的操作权限,常见段包括:

名称 作用 读写/可执行性
.text 保存代码 只读可执行
.data 全局数据段 可读可写
.rodata 常量数据段 只读

.globl

该伪操作的作用是声明全局变量/标号,可供其他源文件在链接时访问,当汇编器编译汇编代码时,会将改变量进行记录,知道其是一个全局变量,当其他文件使用该全局变量时即可提供访问,类似于C语言中的extern语句作用。

.code*

这个操作表示程序要在位模式下运行,\可以是16、32或64,当使用GDB进行调试时,遇到.code会发生架构的切换

1
2
3
(gdb) si
The target arghitecture is set to "i386" # .code32
=> 0x7c32: mov &0x10, %ax

.set

设置变量,例如下面的语句将PROT_MODE_CSEG的值设置为0x8

1
.set PROT_MODE_CSEG, 0x8

.com

分配空间,在数据段中分配指定空间

1
.comm stack, KSTACKSIZE

在数据段中分配KSTACKSIZE长度的空间,这段空间用符号stack来表示。

.align

内存对齐操作

1
2
.align 2;       /* align function definition 令函数对齐,即其起始地址为2的倍数*/     \ 
name: /* function starts here */ \

函数的起始地址就变成了2的倍数,中间部分会用null补齐。操作数必须是2的整数倍。

常用寄存器及功能

此部分请参考关于寄存器的讲解

常用命令

数据移动类

mov

一下列举了一些mov的使用方法

1
mov    0x10(%ebp),%edi  # ebp+10 --> edi
mov的变种

根据mov数据长度的不同,mov还有如下变种:

命令名 功能 备注
movw 移动2字节(一个word) mov word

push

只要有PUSH的动作,ESP寄存器自动减小,push命令使用格式如下:

1
push %eax

等价于

1
2
subl $4, %esp 
movl %eax, (%esp)

先减小esp,再移动数据。

push的变种

根据push数据长度的不同,push还有如下的变种:

命令名 功能 备注
pushl 压入2字节(一个word) push long
pushal push EAX,EBX,ECX,EDX,ESP,EBP,ESI,EDI,一次性压入上述寄存器

下面给出针对push的一个测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int test_pushl()
{
asm volatile ("movl %esp, %eax\n"
"pushl %ds\n"
"subl %esp, %eax\n"
"popl %ds\n");
}

void main()
{
printf("It used %d bytes\n", test_pushl());
}

pop

计算类

add

sub

1
sub $0x1c, %esp  #  %esp = %esp - 0x1c

这个语句用于为栈分配空间

test

shl

控制类

转移命令

ljmp

长转移命令,使用格式为:

1
ljmp addr16
call

函数调用命令,使用方式为:

1
call addr

等价于

1
2
pushl %eip          # 先将当前eip保存
movl $addr, %eip # 然后将新地址移至eip

所以每次调用call,会导致esp减小,因为要将返回地址进行保存。x86架构规定栈向下生长,一个使用call指令的例子如下:

Example instruction What it does
pushl %eax subl $4, %esp
movl %eax, (%esp)
popl %eax movl (%esp), %eax
addl $4, %esp
call 0x12345 pushl %eip
movl $0x12345, %eip
ret popl %eip
ret

函数返回命令,使用方式为:

1
ret

该指令等价于

1
popl %eip

系统相关

iret

int

IO类

inb

从一个端口读数,读至寄存器

1
inb $0x64, %al

从0x64 port读取一个数,然后读至al寄存器中。

附录

符号及功能

符号 功能 例子
$ 常量符号,所有常量都必须以$开头 mov $100, %bx,将100移植bx
取地址符号 movl $values, %edi,将value所在地址移至edi
% 寄存器标志,所有寄存器必须以%开头 mov %ax, %bx

参考文献

0%