Intel® 64 and IA-32 Architectures Software Developer’s Manual 文档官方地址:
https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf
很好的入门文档:https://www.ibm.com/developerworks/cn/linux/l-assembly/
汇编语言由三部分组成:
- 汇编指令:跟机器指令一一对应,实际就算一个个的助记符。有 AT&T 和 INTEL 两种语法
- 伪指令:给编译器看,用于指示编译器该如何做。不同编译器的语法不同,有 NASM、GNU as 和 MASM 三种常用编译器
- 运算符:±*/ 之类的符号
汇编指令
具体可以参考:https://blog.csdn.net/kennyrose/article/details/7575952
AT&T 和 INTEL 两种语法的主要差异有:
- | AT&T | INTEL |
---|---|---|
操作数方向 | 从左向右 | 从右向左 |
立即数表示方式 | $0x01 | 30h |
寄存器表示方式 | %eax | eax |
助记符指定操作数长度 | b8位,w16位,l32位, movl $lb, %eax | mov eax, dw ptr lb |
长跳转和调用 | ljmp $sect, $off | jmp sect:off |
内存单元 | 圆括号 mov 5(%ebx), %eax | 方括号 mov eax, [ebx + 5] |
间接寻址方式 | %segreg:disp(base,index,scale) | segreg:[base+index*scale+disp] |
注释 | 单行用 # 或 // 多行用 /* ... */
|
只有;
|
AT&T 语法比较难受,但是 UNIX 类系统的源码都在用,所以还是要学习。
x86 和 x86-64 平台
这两个平台,一个是 32 位,一个是 64 位,其系统调用编号完全不同。
汇编编译器
- NASM:INTEL 语法,跨 Linux 和 Windows 平台。
- GNU as:默认 ATT 语法,可以通过
.intel_syntax
伪指令声明使用 INTEL 语法。 - MASM:Windows 平台专用,不讨论。
NASM
NASM 编译器中,所有伪指令就是一般单词,没有用句点开头。
常用选项:
-
-f
选项:指定目标代码的平台,可以通过nasm -hf
查看所有支持的。linux 常用的是 ELF32 和 ELF64 -
-o
选项:指定输出文件名字
GNU as
Unix 系列的操作系统基本默认用这个。伪指令全部用句点开头。可以直接通过 --32/--64/--x32 generate 32bit/64bit/x32 code
选项生成指定位数的代码。
Hello world 示例
NASM 编译器 + INTEL 语法
源码:
section .text ; 声明代码段
global _start ; 声明程序入口
_start:
mov eax, 4 ; 参数一:系统调用号,4表示写
mov ebx, 1 ; 参数二:写的目的地,1表示 stdout
mov ecx, msg ; 参数三:要写的字符串首地址
mov edx, len ; 参数四:字符串长度
int 80h ; 开始系统调用
mov eax, 1
mov ebx, 0
int 80h
section .data ; 声明数据段
msg db "hello world!", 0xA
len equ $ - msg
编译链接执行,链接器 LD 用 -m elf_i386
指明链接的是 32 位的目标文件:
nasm hello.asm -o hello.o -f elf
ld hello.o -o hello -m elf_i386
./hello
GNU as 编译器 + AT&T 语法
.data # 数据段声明
msg : .string "Hello, world!\\n" # 要输出的字符串
len = . - msg # 字串长度
.text # 代码段声明
.global _start # 指定入口函数
_start: # 在屏幕上显示一个字符串
movl $len, %edx # 参数三:字符串长度
movl $msg, %ecx # 参数二:要显示的字符串
movl $1, %ebx # 参数一:文件描述符(stdout)
movl $4, %eax # 系统调用号(sys_write)
int $0x80 # 调用内核功能
# 退出程序
movl $0,%ebx # 参数一:退出代码
movl $1,%eax # 系统调用号(sys_exit)
int $0x80 # 调用内核功能
编译执行:
kika@kika-VirtualBox:~/test/gas$ as att.s -o att.o
kika@kika-VirtualBox:~/test/gas$ ld att.o -o att
kika@kika-VirtualBox:~/test/gas$ ./att
Hello, world!\n
GNU as 编译器 + INTEL 语法
这个太折腾了,建议不用
- .intel_syntax:使用 INTEL 语法,注意寄存器前面仍然需要 %
- .intel_syntax noprefix:使用 INTEL 语法,寄存器前面不需要 %