当前位置: 首页 > news >正文

x86_64汇编

1.CPU分类

目前主要就是两种架构,分别是x86架构和arm架构。

  • x86架构:

    • x86、x86_64,分别是x86架构的32位版本和64位版本,后者也兼容32位

    • amd64,跟x86_64一样

  • arm架构

    • arm64架构

    • aarch64架构

2.x86_64架构

2.1寄存器

寄存器是计算机中央处理器(CPU)内部的一种高速存储部件,用于临时存放数据、指令和地址等信息,是 CPU 进行运算和控制的核心组件之一。

2.1.1通用寄存器

x86_64 共有 16 个 64 位通用寄存器,每个寄存器可根据操作数长度(64 位、32 位、16 位、8 位)使用不同的名称(通过寄存器名后缀区分)。这些寄存器既可以用于数据运算,也可用于地址存储,部分寄存器还保留了特殊用途。

64 位名称32 位名称16 位名称低 8 位名称主要用途(通用 + 特殊)
raxeaxaxal累加器,默认用于乘法 / 除法结果;函数返回值(ABI 约定)
rbxebxbxbl基址寄存器,可作为内存访问的基地址;被调用者保存寄存器(ABI 约定)
rcxecxcxcl计数器,用于循环(如rep指令)和移位操作(cl存放移位位数)
rdxedxdxdl数据寄存器,乘法 / 除法时存放高半部分结果;I/O 指令中作为端口地址
rsiesisisil源索引寄存器,字符串操作中指向源数据;函数参数传递(ABI 约定)
rdiedididil目的索引寄存器,字符串操作中指向目标地址;函数第一个参数(ABI 约定)
rbpebpbpbpl基址指针,传统上用于栈帧基地址(栈回溯);被调用者保存寄存器
rspespspspl栈指针,始终指向栈顶,用于栈操作(压栈 / 弹栈),不可随意修改
r8-r15r8d-r15dr8w-r15wr8b-r15b新增 64 位寄存器,功能通用,r8-r11为调用者保存寄存器,r12-r15为被调用者保存寄存器(ABI 约定)

通用寄存器的特殊用途说明:

  1. ABI 约定:在 Linux/UNIX 系统的函数调用中,寄存器的使用遵循 System V AMD64 ABI 标准:

    1. 函数参数传递:rdi(第 1 个)、rsi(第 2 个)、rdx(第 3 个)、rcx(第 4 个)、r8(第 5 个)、r9(第 6 个),超出部分通过栈传递。

    2. 返回值:rax(64 位)、rdx:rax(128 位,如乘法结果)。

    3. 寄存器保存规则:rbxrbprspr12-r15由被调用者保存(需恢复原值),其余由调用者自行保存。

  2. 字符串操作:rsi(源地址)、rdi(目标地址)、rcx(操作长度)配合movs(移动)、cmps(比较)等指令实现高效字符串处理。

  3. 栈操作:rsp是栈顶指针,每次push/pop指令会自动修改其值;rbp可选作为栈帧基地址,用于定位函数参数和局部变量。

2.1.2标志位寄存器

标志位寄存器(64 位为rflags)是一个特殊寄存器,用于存储运算结果的状态和 CPU 的控制信息,共包含 19 个标志位(部分为保留位)。每个标志位占 1 位(0 或 1),主要分为状态标志和控制标志两类。

  1. 状态标志(Status Flags)

记录算术 / 逻辑运算的结果特征,用于条件跳转等指令判断:

标志位(位位置)名称含义
0 位CF(Carry Flag)进位 / 借位标志:无符号运算结果超出范围时置 1(如 32 位加法结果≥2³²);减法借位时置 1。
2 位PF(Parity Flag)奇偶标志:运算结果的低 8 位中 1 的个数为偶数时置 1(用于数据校验)。
4 位AF(Auxiliary Carry Flag)辅助进位标志:低 4 位向高 4 位进位 / 借位时置 1(主要用于 BCD 码运算)。
6 位ZF(Zero Flag)零标志:运算结果为 0 时置 1(如a - b = 0则ZF=1)。
7 位SF(Sign Flag)符号标志:结果为负数时置 1(以补码最高位为判断依据)。
8 位TF(Trap Flag)陷阱标志(控制标志):置 1 时 CPU 进入单步调试模式,每条指令执行后触发中断。
9 位IF(Interrupt Enable Flag)中断允许标志(控制标志):置 1 时允许 CPU 响应可屏蔽中断(如外部设备中断)。
10 位DF(Direction Flag)方向标志(控制标志):字符串操作中,DF=0时地址递增(从低到高),DF=1时地址递减。
11 位OF(Overflow Flag)溢出标志:有符号运算结果超出范围时置 1(如 32 位有符号数运算结果>2³¹-1 或<-2³¹)。
14 位NT(Nested Task Flag)嵌套任务标志:用于多任务切换,指示当前任务是否嵌套在另一个任务中(现代系统较少使用)。
16 位RF(Resume Flag)恢复标志:调试相关,置 1 时暂时忽略调试异常(避免调试断点重复触发)。
17 位VM(Virtual-8086 Mode)虚拟 8086 模式标志:置 1 时 CPU 运行在虚拟 8086 模式(兼容 16 位程序)。
18 位AC(Alignment Check)对齐检查标志:置 1 时检测内存访问的地址对齐错误(仅在特权级 3 有效)。
19 位VIF(Virtual Interrupt Flag)虚拟中断标志:与VIP配合,在虚拟 8086 模式下模拟IF标志。
20 位VIP(Virtual Interrupt Pending)虚拟中断挂起标志:指示虚拟中断是否挂起。
21 位ID(Identification Flag)识别标志:用于检测 CPU 是否支持 CPUID 指令(可通过指令修改)。
  1. 控制标志(Control Flags)

用于控制 CPU 的运行模式或行为,上述TFIFDF均属于控制标志,其值可通过指令(如sti/cli修改IFcld/std修改DF)主动修改。

2.1.3指令指针寄存器

  1. 指令指针寄存器(rip

    1. 64 位指令指针,存放下一条要执行的指令的地址,CPU 执行完当前指令后自动根据指令长度递增rip

    2. 不可通过mov直接修改,需通过jmpcallretjcc(条件跳转)等指令间接更新,是程序流程控制的核心。

2.1.4段寄存器

段寄存器是 x86 架构历史遗留的重要组件,最初用于解决 16 位 CPU 的内存寻址限制(通过 “段基址 + 偏移量” 扩展地址空间)。在 64 位模式下,段寄存器的功能被大幅简化,但仍保留用于内存保护和线程局部存储(TLS)。

x86_64 有 6 个 16 位段寄存器:csdsesfsgsss,每个寄存器存储一个 “段选择子”(Segment Selector),用于索引全局描述符表(GDT)或局部描述符表(LDT),获取段的基地址、限长和权限信息。

各段寄存器的作用:

  1. cs(Code Segment,代码段寄存器)

    1. 指向当前执行代码所在的内存段,包含代码的基地址和执行权限(如特权级)。

    2. 在 64 位模式下,cs的特权级(CPL)决定 CPU 当前运行级别(0 为内核态,3 为用户态),是系统安全的核心控制之一。

    3. 不可通过mov指令直接修改(需通过jmpcallret等控制转移指令间接更新)。

  2. ss(Stack Segment,栈段寄存器)

    1. 指向当前栈所在的内存段,与rsp配合定义栈的地址范围和权限。

    2. 64 位模式下,栈段的基地址通常被忽略(视为 0),但段的权限检查仍有效(如防止用户态访问内核栈)。

  3. ds(Data Segment,数据段寄存器) es(Extra Segment,附加数据段寄存器)

    1. 传统上用于访问数据段内存,64 位模式下默认基地址为 0,主要用于兼容 16/32 位程序。

    2. 现代系统中,dses通常不主动修改,默认指向全局数据段。

  4. fsgs(额外段寄存器)

    1. 功能灵活,无固定用途,由操作系统自定义:

      • 在 Linux 中,fs用于用户态线程局部存储(TLS),通过fs:[0]可访问当前线程的局部变量;gs用于内核态(如存放当前进程描述符task_struct指针)。

      • 在 Windows 中,gs用于用户态 TLS 和异常处理(如gs:[0x30]指向线程信息块 TEB)。

2.2常见汇编指令

x86_64 架构的汇编指令集非常丰富,涵盖数据传输、算术运算、逻辑操作、控制流、栈操作等多个类别。以下是最常用的汇编指令及其功能说明,以 AT&T 语法(Linux 环境常用)为例:

2.2.1数据传输指令

用于在寄存器、内存和立即数之间传递数据。

指令示例功能说明
movmov %rax, %rbx将rax的值复制到rbx(寄存器→寄存器)
mov 0x10(%rbp), %rdx(内存→寄存器)
mov $0x20, %ecx(立即数→寄存器)
pushpush %rax将rax的值压入栈(栈顶指针rsp减 8)
poppop %rbx从栈顶弹出值到rbx(栈顶指针rsp加 8)
lealea 4(%rdi), %rsi计算内存地址(rdi+4)并存储到rsi(常用于地址计算,非数据加载)
xchgxchg %rax, %rbx交换rax和rbx的值

2.2.2算数与逻辑指令

用于数值计算和位操作。

  • 算数运算

指令示例功能说明
addadd $5, %eaxeax += 5(结果影响进位标志CF、零标志ZF等)
subsub %rbx, %raxrax -= rbx(结果影响借位标志CF等)
mulmul %rbx无符号乘法:rdx:rax = rax * rbx(64 位结果)
imulimul $3, %ecx有符号乘法:ecx *= 3
divdiv %rbx无符号除法:rax = (rdx:rax) / rbx,余数存rdx
idividiv %rcx有符号除法:rax = (rdx:rax) / rcx,余数存rdx
incinc %ediedi += 1(不影响进位标志CF)
decdec %edxedx -= 1(不影响进位标志CF)
  • 逻辑操作

指令示例功能说明
andand $0xFF, %alal &= 0xFF(位与,常用于清高位)
oror %rbx, %raxrax = rbx(位或,常用于置位)
xorxor %rax, %raxrax ^= rax(结果为 0,常用于清零寄存器)
notnot %rdxrdx = ~rdx(位取反)
shlshl $2, %eaxeax <<= 2(逻辑左移,低位补 0,影响进位标志CF)
shrshr $1, %ebxebx >>= 1(逻辑右移,高位补 0)
sarsar $3, %ecxecx >>= 3(算术右移,高位补符号位)

2.2.3控制流指令

用于改变程序执行顺序(分支、循环、函数调用)。

  • 无条件跳转

指令示例功能说明
jmpjmp label跳转到标签label处执行
  • 条件跳转

指令依赖标志跳转条件
je/jzZF=1相等(Equal)或零(Zero)
jne/jnzZF=0不相等或非零
jg/jnleZF=0且OF=SF有符号大于(Greater)
jl/jngeOF≠SF有符号小于(Less)
ja/jnbeCF=0且ZF=0无符号大于(Above)
jb/jnaeCF=1无符号小于(Below)
  • 函数调用和返回

指令示例功能说明
callcall func调用函数func:压入返回地址(rip)到栈,跳转到函数入口
retret函数返回:从栈弹出返回地址到rip,继续执行调用处后续指令
  • 循环控制

指令示例功能说明
looploop labelrcx -= 1,若rcx≠0则跳转到label(循环计数)
loope/loopzloope labelrcx -= 1,若rcx≠0且ZF=1则跳转
loopne/loopnzloopne labelrcx -= 1,若rcx≠0且ZF=0则跳转

2.2.4栈与帧的操作

用于函数调用时的栈帧管理。

指令示例功能说明
enterenter $0x10, $0创建栈帧:等价于push %rbp; mov %rsp, %rbp; sub $0x10, %rsp(分配 16 字节局部变量)
leaveleave销毁栈帧:等价于mov %rbp, %rsp; pop %rbp

2.2.5字符串操作指令

配合rsi(源)、rdi(目标)、rcx(计数)寄存器高效处理字符串

指令示例功能说明
movsbrep movsb复制字节:[rdi] = [rsi],rsi±1,rdi±1,rcx-1(rep重复直到rcx=0)
movsw/movsqrep movsq复制字(2 字节)/ 双字(8 字节),步长为 2/8
cmpsbrepe cmpsb比较字节:[rsi] - [rdi],更新标志位,repe重复直到rcx=0或ZF=0
stosbrep stosb存储字节:[rdi] = al,rdi±1,rcx-1(常用于填充内存)

2.3汇编相关的一些命令

Linux下

gcc 相关

  • 预处理操作:gcc -E main.c -o main.i

    直接cat或者more查看

    • 展开#include,将头文件内容插入当前文件中

    • 处理#define,替换宏定义

    • 条件编译,处理#if/#else/#endif等,根据条件保留删除代码块

    • 删除注释,将//或者/*注释删除

    • 添加行号和文件名表示,便于后面编译阶段报错定位

  • 编译操作:gcc -S main.i -masm=intel -o main.s  直接more、cat、vi查看

    • 词法分析,语法分析,语义分析

    • 代码优化:常量折叠,循环展开,根据优化级别决定

    • 生成汇编代码

  • 汇编操作:gcc -c main.s -o main.o

      (.o)文件是二进制文件,无法直接执行,缺少库依赖和入口地址。

    objdump -d main.o #反汇编目标文件,查看机器码对应的汇编命令。 
    readelf -S main.o #查看目标文件的段信息。
    • 汇编指令翻译:翻译为对应的机器码。

    • 符号表构建:记录变量函数的地址。

    • 段划分,将代码、数据分别放入text段、data段。

  • 链接操作:gcc main.o -o main

    objdump -d main # 反汇编可执行文件,查看完整机器码 
    readelf -h main # 查看ELF文件头(包含入口地址等信息)
    • 符号解析:将目标文件中未定义的符号与库文件中实现关联。

    • 重定位:修改目标文件中的相对地址为绝对地址。

    • 合并段:将所有目标文件中的段合并为统一的段。

    • 条件ELF头:包含程序入口地址main,运行时信息等。

段类型核心作用存储内容访问权限典型场景
.text存放可执行指令函数体的机器码(如main、printf的实现)、跳转指令、运算指令等只读、可执行int add(int a, int b) { return a+b; }的机器码
.data存放已初始化的全局 / 静态变量全局变量(如int g_var = 10;)、静态变量(如static int s_var = 20;)可读、可写程序启动时就有初始值的变量
.bss存放未初始化的全局 / 静态变量未初始化全局变量(如int g_uninit;)、未初始化静态变量(如static int s_uninit;)可读、可写初始值为 0 或未指定的变量
.rodata存放只读常量字符串常量(如"hello, world")、const修饰的全局变量(如const int MAX = 100;)只读printf("hello")中的字符串
.stack函数调用时的临时数据区局部变量(如int a = 5;)、函数参数、返回地址、栈帧信息可读、可写函数调用时的参数传递和局部变量存储
.heap动态内存分配区域程序运行时通过malloc/new申请的内存(如int* p = malloc(4);)可读、可写动态大小的数据(如链表、数组)
.symtab/.strtab符号表与字符串表函数名、变量名及其地址(如main函数的地址、g_var的地址)只读(调试用)调试器(如gdb)查找变量 / 函数位置
.rel.text重定位信息代码段中需要修正的地址(如call printf的地址在链接前是相对地址,需重定位)只读(链接用)链接阶段修正指令地址

http://www.dtcms.com/a/304420.html

相关文章:

  • 微信小程序私密消息
  • 实习日志111
  • ElementUI表格 el-table实现自动循环滚动
  • Rust:高效错误处理工具 anyhow
  • 大文档免费翻译方法分享
  • AbMole小课堂丨Blasticidin S(杀稻瘟菌素S)在构建稳转株、抗肿瘤、抗植物病害的跨界应用
  • 6、微服务架构常用十种设计模式
  • 随缘玩 一: 代理模式
  • 算法导论核心代码精粹
  • USRP X440 和USRP X410 直接RF采样架构的优势
  • 【51单片机静态1位数码管显示按键倒计时控制蜂鸣器】2022-9-28
  • Wndows Docker Desktop-Unexpected WSL error
  • AUTOSAR Mcal Dio - 模块介绍 + EB配置工具介绍
  • 【开源项目】轻量加速利器 HubProxy 自建 Docker、GitHub 下载加速服务
  • Doris中文检索效果调优
  • 自组织遗传算法(Self-Organizing Genetic Algorithm, SOGA)求解Rastrigin函数优化问题
  • 【Rust并发集合】如何在多线程中并发安全地使用集合
  • 【AI News | 20250728】每日AI进展
  • 接口自动化测试pytest框架
  • 网络原理--HTTPHTTPS
  • JAVA_TWENTY—ONE_单元测试+注解+反射
  • MySQL——MVCC
  • ftp加ssl,升级ftps
  • 解决Spring MVC中@PathVariable参数为null导致的404问题:全面解析与最佳实践
  • Spring MVC数据传递全攻略
  • 架构实战——互联网架构模板(“网络层”技术)
  • WINCC选项组配置
  • Spring Boot 请求限流实战:基于 IP 的高效防刷策略
  • Postgresql 查询使用正则
  • SQL158 每类视频近一个月的转发量/率