有些时候 使用gdb 或者 crash 工具 分析诊断 问题的时候需要某段代码的汇编指令去排查问题,或者在调试一些不含调试信息的 release 版程序时,只能通过反汇编代码去定位问题,这个时候了解一些汇编语言的基本的知识,对排查问题是很有帮助的
下面记录介绍一下:
一、汇编语言概述
汇编语言是一种低级编程语言,它使用助记符来代表机器语言指令,与特定的计算机架构紧密相关。每种CPU架构都有自己的汇编语言。
二、寄存器分类
1. x86架构寄存器分类
通用寄存器(32位)
EAX - 累加器(Accumulator)
EBX - 基址寄存器(Base)
ECX - 计数器(Counter)
EDX - 数据寄存器(Data)
ESI - 源变址寄存器(Source Index)
EDI - 目的变址寄存器(Destination Index)
EBP - 基址指针(Base Pointer)
ESP - 栈指针(Stack Pointer)
段寄存器
CS - 代码段(Code Segment)
DS - 数据段(Data Segment)
SS - 栈段(Stack Segment)
ES - 附加段(Extra Segment)
FS - 附加段
GS - 附加段
控制寄存器
EIP - 指令指针(Instruction Pointer)
EFLAGS - 标志寄存器
2. x86-64架构寄存器(64位扩展)
RAX, RBX, RCX, RDX - 64位通用寄存器
R8-R15 - 新增的8个通用寄存器
RSI, RDI, RBP, RSP - 64位版本
RIP - 64位指令指针
三、汇编指令分类
1. 数据传送指令
MOV - 传送数据
PUSH - 入栈
POP - 出栈
LEA - 装载有效地址
XCHG - 交换数据
2. 算术运算指令
ADD - 加法
SUB - 减法
MUL - 无符号乘法
IMUL - 有符号乘法
DIV - 无符号除法
IDIV - 有符号除法
INC - 加1
DEC - 减1
NEG - 求负
3. 逻辑运算指令
AND - 逻辑与
OR - 逻辑或
XOR - 逻辑异或
NOT - 逻辑非
TEST - 测试(与运算但不保存结果)
4. 移位指令
SHL/SAL - 逻辑/算术左移
SHR - 逻辑右移
SAR - 算术右移
ROL - 循环左移
ROR - 循环右移
5. 控制转移指令
JMP - 无条件跳转
JE - 相等时跳转
JNE - 不相等时跳转
JG - 大于时跳转
JL - 小于时跳转
CALL - 调用子程序
RET - 返回
6. 比较指令
CMP - 比较两个操作数
TEST - 测试(逻辑与运算)
四、完整示例程序
示例1:计算两数之和(NASM语法,Linux x86-64)
section .data
msg db 'Result: ', 0
num1 dq 25
num2 dq 35
section .text
global _start
_start:
; 加载第一个数到 rax
mov rax, [num1]
; 加上第二个数
add rax, [num2]
; 结果在 rax 中 (60)
; 退出程序
mov rax, 60 ; 系统调用号 (sys_exit)
xor rdi, rdi ; 退出状态码 0
syscall
示例2:循环计算阶乘(MASM语法,Windows x86)
.386
.model flat, stdcall
.stack 4096
.data
number DWORD 5 ; 计算5的阶乘
result DWORD ?
.code
main PROC
mov ecx, number ; 将5加载到ECX(循环计数器)
mov eax, 1 ; 初始化结果为1
factorial_loop:
mul ecx ; EAX = EAX * ECX
loop factorial_loop ; ECX减1并循环,直到ECX=0
mov result, eax ; 保存结果(120)
ret
main ENDP
END main
示例3:字符串处理(AT&T语法,Linux x86)
.section .data
source: .string "Hello"
dest: .space 10
.section .text
.global _start
_start:
# 设置寄存器
movl $source, %esi # 源地址
movl $dest, %edi # 目标地址
movl $5, %ecx # 字符串长度
copy_loop:
movb (%esi), %al # 从源读取一个字节
movb %al, (%edi) # 写入目标
incl %esi # 源指针加1
incl %edi # 目标指针加1
loop copy_loop # 循环直到ECX=0
# 退出
movl $1, %eax # 系统调用号
xorl %ebx, %ebx # 退出码0
int $0x80
五、不同汇编语言的差异
1. 语法差异
特性 | Intel语法 (NASM/MASM) | AT&T语法 (GAS) |
---|---|---|
操作数顺序 | mov dest, src | mov %src, %dest |
寄存器前缀 | 无前缀 | % 前缀 |
立即数前缀 | 无前缀 | $ 前缀 |
内存访问 | [ebx] | (%ebx) |
大小后缀 | byte ptr , word ptr | b , w , l , q |
2. 架构差异
x86 vs ARM
; x86 - CISC架构,复杂指令集
mov eax, [ebx+ecx*4+10h] ; 复杂寻址模式
; ARM - RISC架构,精简指令集
ldr r0, [r1, r2, lsl #2] ; 相对简单的寻址
add r0, r0, #16 ; 立即数需要单独加
x86 vs MIPS
; x86
add eax, ebx ; 两操作数,结果存入第一个
; MIPS
add $t0, $t1, $t2 ; 三操作数,结果存入第一个
3. 汇编器差异
汇编器 | 平台 | 语法风格 | 特点 |
---|---|---|---|
NASM | 跨平台 | Intel | 纯汇编器,语法简洁 |
MASM | Windows | Intel | 微软官方,功能丰富 |
GAS | Unix/Linux | AT&T | GNU工具链一部分 |
FASM | 跨平台 | Intel | 自举汇编器 |
4. 平台相关差异
系统调用差异
; Linux x86 系统调用
mov eax, 1 ; sys_exit
mov ebx, 0 ; 退出码
int 0x80 ; 软中断
; Linux x86-64 系统调用
mov rax, 60 ; sys_exit
mov rdi, 0 ; 退出码
syscall ; 系统调用指令
; Windows x86 API调用
push 0 ; 参数
call ExitProcess; Windows API
本文版权归原作者zhaofujian所有,采用 CC BY-NC-ND 4.0 协议进行许可,转载请注明出处。