汇编语言 基础

有些时候 使用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, srcmov %src, %dest
寄存器前缀无前缀% 前缀
立即数前缀无前缀$ 前缀
内存访问[ebx](%ebx)
大小后缀byte ptr, word ptrb, 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纯汇编器,语法简洁
MASMWindowsIntel微软官方,功能丰富
GASUnix/LinuxAT&TGNU工具链一部分
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 协议进行许可,转载请注明出处。

发表评论