x86-64 汇编:寄存器和过程调用约定

通用目的寄存器(Generous Purpose Registers,GPRs)

GPRs 的作用

x86-64 指令架构要求至少1664Bit 的通用寄存器。

寄存器全称用途
%raxregister a extended存储过程调用返回值(return value)
%rbxregister b extended/
%rcxregister c extended存储过程调用的第个参数
%rdxregister d extended存储过程调用的第个参数
%rbpregister base pointer存储当前栈帧的基地址
%rspregister stack pointer存储栈顶地址
%rsiregister source index存储过程调用的第个参数
%rdiregister destination index存储过程调用的第个参数
%r8register 8存储过程调用的第个参数
%r9register 9存储过程调用的第个参数
%r10-%r11register 10 ~ register 11/
%r12-%r15register 12 ~ register 15/

再提一个特殊的寄存器:指令寄存器。在 x86-64 汇编中,用 %rip 表示。它永远指向下一条即将执行的地址。

GPRs 的访问方式

寄存器也能以另一种方式去访问特定位,总结如下。

  • e 替换前缀 r 或者添加后缀 d,表示仅使用该寄存器的低 32Bit。如 %eax%ebp%esp%r8d%r15d
  • 移除前缀 r 或者添加后缀 d,表示仅使用该寄存器的低 16Bit。如 %ax%bp%r8w%r15w
  • 移除前缀 r,并且用 h 替换后缀 x,表示仅使用该寄存器的低 16Bit 中的高 8Bits。如 %ah%bh
  • 移除前缀 r,并且用 l 替换后缀 x(或者添加后缀 l),表示仅使用该寄存器的低。16Bit 中的高 8Bit。如:%al%bl%bpl%spl%sil%dil%r8b%r15b
    General Purpose Registers in 64-Bit Mode
    图 1 General Purpose Registers in 64-Bit Mode

注意:

  1. %rbp%rsp%rsi%rdi%r8 ~ %r15 的低 16Bit 中的高 8Bit 未提供符号用于单独访问。

过程调用(Procedure Call)

过程调用,可以简单理解为函数调用,涉及到:

  • 传递控制:指令执行权的转移控制。
  • 传递数据:过程参数的传入,过程结果的返回。
  • 内存管理:过程中,局部内存的申请和释放。

传递控制

假设过程 P 调用 过程 Q,

  • 进入过程 Q 前,P 必须将 %rip 设为 Q 的地址;
  • 然后 Q 执行结束时,必须将 %rip 设为 P 调用 Q 前的下一条地址。

寄存器 %rip 的值,仅能通过有限的几条指令更改。最常见的是无条件跳转指令 call

0100 Q:
// ......
01fe  ret

1000  call Q
1005  mov %rax, -16(%rbp)

以上述汇编代码片段为例:执行到地址 0x1000 处,

  • call 指令执行完,并且还未开始执行下一条指令,%rip 等于 0x0100
  • 然后等到 Q 过程执行到 0x01fe 处时,ret 指令执行完,并且还未开始执行下一条指令,%rip 等于 0x1005

传递参数

寄存器用途约定

用于存储传入参数:%rdi%rsi%rdx%rcx%r8~%r9,共计 6 个。
用于存放返回结果:%rax

如果参数个数超过 6 个,那必须借助栈来传递参数。这部分空间由调用者分配管理。

寄存器使用约定

因为寄存器是共用的,所以过程调用中,寄存器中的值可能被不同过程修改。

比如 P 过程调用 Q 过程,Q 过程执行过程中可能修改 P 过程中寄存器所存放的值,因此需要有一个约定让不同过程可以放心的操作寄存器。

这种分类方式下,寄存器可分为两组:

  • 调用者保护寄存器:由调用者(caller)保护的寄存器。在被调用的过程执行结果后,调用者恢复这些寄存器的值,然后再使用。
  • 被调用者保护寄存器:由被调用者(callee)保护的寄存器。在被调用的过程结束前,被调用者应当恢复这些寄存器原来的值(即进入调用前的初始值)。

参考资料

  1. AMD64 Architecture Programmer’s Manual Volume 1: Application Programming
  2. Computer Systems: A Programmer’s Perspective, 3/E (CS:APP3e)
  • 5
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PeachPy 是一个用于编写高性能汇编内核的 Python 框架,可在汇编中编写模块。 它自动化了一些细节,并允许使用 Python 生成重复的汇编代码序列。PeachPy 旨在简化编写优化的汇编内核,同时保留传统的汇编所有优化机会。一些特性:用于 Windows,Unix 和 Golang 程序集的通用汇编语法.PeachPy 可以直接生成 ELF,MS COFF 和 Mach-O 对象文件以及 Golang 工具链的汇编列表自动适应不同的调用约定和 ABIs用于不同平台的功能可以从汇编相同的源生成支持 Microsoft x64 ABI, System V x86-64 ABI (Linux 和 OS X), Linux x32 ABI, Native Client x86-64 SFI ABI, Golang AMD64 ABI, Golang AMD64p32 ABI自动分配寄存器PeachPy 是灵活的,而且允许在同一代码中混合自动分配寄存器和硬编码寄存器汇编编程中常规任务的自动化:函数 prolog 和 epilog 由 PeachPy 生成数据常量的重复数据删除 (e.g. Constant.float32x4(1.0))分析在函数中使用的 ISA 扩展支持 x86-64 指令,最高可达 AVX-512 和 SHA包含 3dnow! , XOP, FMA3, FMA4, TBM 和 BMI2.不包括 x87 FPU 和大多数系统指令使用自动生成的测试 auto-generated tests 进行严格测试,以生成与 binutils 相同的操作码自动生成元数据文件具有模块依赖性的Makefile (-MMD 和-MF 选项)C 头文件生成的函数JSON 格式的函数元数据基于 Python 的元编程和代码生成多个指令流的复用(有助于软件流水线)兼容 Python 2 和 Python 3,CPython 和 PyPy在线 DEMO: PeachPy.IO 标签:PeachPy
本书是作者十余年编程生涯中的技术和经验的总结。内容涵盖了从认识CPU、Windows运行机理、编程语言的运行机理,到代码的规范和风格、分析方法、调试方法和内核优化,内有作者对许多问题的认知过程和透彻的分析,以及优秀和精彩的编程经验。 性思维(2) 第2章认识CPU 2.1 8位微处理器回顾/2.2 16位微处理 器(1) 2.2 16位微处理器(2) 2.3 32位微处理器(1) 2.3 32位微处理器(2) 2.3 32位微处理器(3) 2.4 【实例】:在DOS实模式下读取4GB内 存(1) 2.4 【实例】:在DOS实模式下读取4GB内 存(2) 第3章 Windows运行机理 3.1 内核分析(1) 3.1 内核分析(2) 3.1 内核分析(3) 3.1 内核分析(4) 3.1 内核分析(5) 3.1 内核分析(6) 3.1 内核分析(7) 3.1 内核分析(8) 3.1 内核分析(9) 3.1 内核分析(10) 3.1 内核分析(11) 3.1 内核分析(12) 3.3 GDI的结构和组成(1) 3.3 GDI的结构和组成(2) 3.4 线程的机制(1) 3.4 线程的机制(2) 3.4 线程的机制(3) 3.4 线程的机制(4) 3.4 线程的机制(5) 3.4 线程的机制(6) 3.4 线程的机制(7) 3.5 PE结构分析(1) 3.5 PE结构分析(2) 3.5 PE结构分析(3) 编程高手箴言 file:///d|/Administrator/Desktop/编程高手箴言/编程高手箴言/index.htm[2009-5-13 22:18:51] 3.1 内核分析(13) 3.2 消息的运行方式(1) 3.2 消息的运行方式(2) 3.2 消息的运行方式(3) 3.5 PE结构分析(4) 3.5 PE结构分析(5) 3.5 PE结构分析(6) 3.5 PE结构分析(7) 第4章编程语言的运行机理 第5章代码的规范和风格 5.1 环境的设置 5.1.1 集成环境的设置 5.1.2 TAB值的设置 5.1.3 编译环境的设置 5.1.4 设置herosoft.dsm宏 5.2 变量定义的规范 5.2.1 变量的命名规则 5.2.2 变量定义的地方规定 5.2.3 变量的对齐规定 5.3 代码对齐方式、分块、换行的规范 5.4 快速的代码整理方法 5.5 注释的规范 5.6 头文件的规范 5.7 建议采用的一些规则 5.8 可灵活运用的一些规则 5.9 标准化代码示例 5.10 成对编码规则 5.10.1 成对编码的实现方法 5.10.2 成对编码中的几点问题 5.11 正确的成对编码的工程编程方法 5.11.1 编码前的工作 5.11.2 成对编码的工程方法 5.11.3 两个问题的解释 第6章分析方法 6.1 分析概要 6.1.1 分析案例一:软件硬盘阵列 6.1.2 分析案例之二:游戏内存修改工具 6.2 接口的提炼 6.2.1 分离接口 6.2.2 参数分析 6.3 主干和分支 6.3.1 主干和分支分析举例 6.3.2 程序检验 6.4 是否对象化 6.5 是否DLL化 6.5.1 DLL的建立和调用 6.5.2 DLL动态与静态加载的比较 6.5.3 DLL中函数的定义 6.6 COM的结构 6.7 几种软件系统的体系结构分析 6.7.1 播放器的解码组成分析 6.7.2 豪杰大眼睛的体系结构 6.7.3 Windows 9x体系结构 编程高手箴言 file:///d|/Administrator/Desktop/编程高手箴言/编程高手箴言/index.htm[2009-5-13 22:18:51] 第7章调试方法 7.1 调试要点 7.1.1 调试和编程同步 7.1.2 汇编代码确认 7.1.3 Win32的Debug实现方法 7.2 基本调试实例分析 7.3 多线程应用的调试 7.4 非固定错误的调试 7.4.1 激活调试环境 7.4.2 正确区分错误的类型 7.4.3 常见的偶然错误 第8章内核优化 8.1 数据类型的认识 8.2 X86优化编码准则 8.2.1 通用的X86优化技术 8.2.2 通用的AMD-K6处理器x86代码优化 8.2.3 AMD-K6处理器整数x86代码优化 8.3 MMX指令的优化 8.3.1 MMX的寄存器介绍 8.3.2 MMX的工作原理 8.3.3 MMX的检测 8.3.4 MMX指令的介绍 8.4 MMX的实例一:图像的淡入淡出 8.4.1 目的 8.4.2 解决方法 8.4.3 分析 8.4.4 初步实现 8.4.5 MMX的优化实现 8.5 MMX的实例二:MMX类的实现方法 8.5.1 实现方法分析 8.5.2 实现步骤 8.5.3 检测过程 8.5.4 总结
为了使用汇编语言,必须熟悉CPU和寄存器,还需要掌握CPU的寻址方式。 为了利用NASM进行PC机的汇编语言编程,需要熟悉x86的指令集和NASM汇编程序的若干附加指令。 在使用汇编语言编写的低级代码中,所有的I/O操作(如显示字符和字符串、读入键盘按键、读写磁盘数据等)都是通过调用系统的BIOS(Basic Input Output System,基本输入输出系统)来完成的。 计算机在启动时,因为系统自动装入的引导模块太小(只有一个扇区,512个字节),一般只能包含装载操作系统启动程序的代码,而启动程序本身的代码,则必须放在磁盘的其他地方。至于操作系统的大量代码,一般用C语言和汇编语言混合编写,保存为磁盘文件后,再由启动程序装载入内存。 DOS和Windows的COM可执行文件简单小巧,可用于引导代码的运行测试。 为了调试我们编写的汇编语言程序,可以使用传统小巧的实模式调试工具debug。 这次实验,我们先介绍CPU及其寄存器和寻址方法;再给出常用的x86指令和NASM汇编程序的常用附加指令;接着列出几个常用的BIOS中断及其调用方法;然后编写一个读入按键并回显字符到屏幕的小汇编程序MY-OS,并将其作为另一个引导程序;其后,把MY-OS的代码放到第二个物理扇区,再编写装载此代码块的汇编程序放到引导扇区;接着介绍COM可执行文件的生成和使用,最后简介实模式调试工具debug的基本使用方法
函数调用过程是程序中常见的一种操作,它通常涉及到参数传递、栈帧的建立与销毁、返回值的传递等多个方面。从汇编的角度来看,函数调用过程可以分为以下几个步骤: 1. 将函数的参数压入栈中。在调用函数时,需要将函数所需的参数传递给它。这些参数通常以一定的顺序压入栈中,以便在函数内部使用。在 x86 架构中,参数的传递是通过将参数压入栈顶实现的。 2. 调用函数。函数调用的指令通常是 CALL 指令。在调用函数前,需要将函数的入口地址压入栈中,以便在函数执行完毕后返回到调用位置。CALL 指令会将当前的程序计数器(PC)压入栈中,并将函数的入口地址作为新的 PC。 3. 建立栈帧。在函数被调用时,需要为函数建立一个独立的栈帧,以便在函数内部使用局部变量和临时变量。栈帧通常包括以下几个部分:返回地址、旧的基址指针、局部变量和临时变量。在 x86 架构中,栈帧的建立是通过将 ESP 寄存器减去一个固定的值实现的。 4. 执行函数。在函数被调用后,CPU 会跳转到函数的入口地址并开始执行函数。函数内部可以通过栈中的参数和局部变量完成相应的计算和操作。 5. 返回值传递。在函数执行完毕后,需要将函数的返回值传递给调用者。在 x86 架构中,函数的返回值通常通过 EAX 寄存器传递。 6. 销毁栈帧。在函数执行完毕后,需要将栈帧销毁,以便释放栈空间。栈帧的销毁通常是通过将 ESP 寄存器还原到旧的基址指针处实现的。 7. 返回到调用位置。在函数执行完毕后,需要返回到函数被调用的位置。在 x86 架构中,返回指令通常是 RET 指令。RET 指令会将栈顶的返回地址弹出,并将其作为新的 PC。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值