汇编基础(3) --X86-64

这篇具有很好参考价值的文章主要介绍了汇编基础(3) --X86-64。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

x86_64汇编语言是一种低级程序设计语言,用于控制x86_64架构的处理器执行特定的指令序列。它提供了对底层硬件的直接访问和控制,因此常用于编写性能关键的系统软件、驱动程序和嵌入式设备。对于熟悉高级编程语言(如C或C++)的开发者来说,了解和学习x86_64汇编语言有助于深入了解计算机的工作原理和优化代码的性能。

x86_64汇编语言使用助记符(mnemonics)来表示不同的机器指令。这些指令可以操作寄存器(Registers)、内存(Memory)和标志位(Flags)等系统资源。寄存器是一种高速存储器,用于暂存数据和执行算术和逻辑操作。x86_64架构包含一组通用寄存器,如RAX、RBX、RCX、RDX等,它们可以存储整数值和指针。此外,还有一些特殊目的寄存器,如程序计数器(Program Counter)和堆栈指针(Stack Pointer)。

x86_64汇编语言还支持各种内存访问模式,包括直接寻址、间接寻址和基于索引的寻址。通过这些寻址模式,可以读取和写入内存中的数据,这为处理复杂的数据结构和算法提供了便利性。

与高级编程语言相比,汇编语言更加底层和繁琐。它需要开发者手动管理寄存器和内存,并且需要详细了解处理器的指令集。此外,调试和维护汇编代码通常更加困难。因此,在大多数情况下,编写应用程序的大部分代码使用高级编程语言来实现,只有对性能要求极高或对底层硬件的直接控制有特殊需求时,才会使用汇编语言编写一小部分关键代码。

函数调用

  1. 寄存器使用:

    • 前6个整型参数(从左到右)通过寄存器传递,分别是:RDI、RSI、RDX、RCX、R8、R9。
    • 如果需要传递更多的整型参数,超出这6个寄存器的参数将通过栈传递。
    • 浮点数和矢量参数则使用XMM0至XMM7这8个XMM寄存器传递。
  2. 函数调用栈帧:

    • 函数调用时,返回地址会被压入栈中。
    • 当前函数将使用栈上的一部分空间来存储局部变量和临时数据。
    • RBP(基址指针)寄存器通常被用作指向当前函数的栈帧的基地址。
    • 函数参数也可能保存在栈上。
    • 返回值会通过RAX寄存器传递。
  3. 栈对齐:

    • 栈要求以16字节对齐,即栈指针的地址必须是16的倍数。
    • 在函数调用过程中,栈指针(RSP)会被调整以保持对齐。
  4. 寄存器保护:

    • 如果一个函数调用另一个函数,被调用函数必须保存并恢复使用的寄存器。
    • 一般情况下,RBP、RBX和R12至R15这些寄存器需要被调用者保存。
  5. 栈帧清理:

    • 调用者负责清理栈上的参数和临时数据。
    • 在函数调用结束后,通过调整栈指针(RSP)来释放栈帧。

通用寄存器 (general register)

通用寄存器(general-purpose registers, GPRs) 可能是读书是最早接触的寄存器。每一个用户空间的程序,或者内核程序都用到的,基本的寄存器。
因为X86-64是从32位的X86,甚至16位、8位演变而来的,为了软件可以向前兼容,所以,这些寄存器都有不同的版本。话不多说,看下表:

64-bit 32-bit 16-bit 8-bit (low)
RAX EAX AX AL
RBX EBX BX BL
RCX ECX CX CL
RDX EDX DX DL
RSI ESI SI SIL
RDI EDI DI DIL
RBP EBP BP BPL
RSP ESP SP SPL
R8 R8D R8W R8B
R9 R9D R9W R9B
R10 R10D R10W R10B
R11 R11D R11W R11B
R12 R12D R12W R12B
R13 R13D R13W R13B
R14 R14D R14W R14B
R15 R15D R15W R15B

其中,16位的寄存器中的AX, BX, CX, DX 可以高低位分别访问,所以又增加4个

16-bit 8-bit (high)
AX AH
BX BH
CX CH
DX DH

通用寄存器总共: 68.

到目前为止一共: 68.

特殊寄存器 (Special registers)

  • 指针寄存器(instruction pointer, RIP.)
    X86-64的RIP可以切分成32位的EIP 和 16位IP,但他们不可以同时使用,所以这里不重复计数。
  • 状态寄存器(status register, RFLAGS)
    就像上面的RIP一样,RFLAGS也有32位和16位版本,分别是EFLAGS 和 FLAGS,但不同的是PUSHF 和 PUSHFQ可以在长模式下同时使用,以及LAHF 和 SAHF可以同时在一个CPU上使用,这里计数。

本部分寄存器总共: 4.

到目前为止一共: 72.

段寄存器 (Segment registers)

X86-64一共有6个段寄存器: CS, SS, DS, ES, FS, and GS

  • 除了长模式以外的所有CPU运行模式里,都有一个段选择器 selector, 表示当前使用GDT 还是 LDT。 同时,还需要一个段描述符descriptor, 提供了段的基址和范围。
  • 长模式中,除了 FS 和 GS 之外的所有内容都被视为在一个具有零基地址和64位范围的平面地址空间中。 FS 和 GS 作为特殊情况保留,但不再使用段描述符表,取而代之的是,访问保存在 FSBASE 和 GSBASE 中的MSR寄存器中的基地址。

本部分寄存器总共: 6.

到目前为止一共: 78.

单指令多数据SIMD 和浮点运算指令FP

X86家族经历了几代 SIMD 和浮点指令,每一代都引入、扩展或重新定义各种各样的指令:

  • x87
  • MMX
  • SSE (SSE2, SSE3, SSE4, SSE4, …)
  • AVX (AVX2, AVX512)
  • AMX

x87

X87最初是一个独立的协处理器,有自己的指令集和寄存器,从80486开始,x87指令就经常被植入x86内核本身。
由于其协处理器的历史,x87定义了正常的寄存器(类似于GPR)和控制FPU状态所需的各种特殊寄存器。

  • ST0到ST7:8个80位浮点寄存器
  • FPSW, FPCW, FPTW 7:控制、状态和标签字寄存器
  • “数据操作数指针Data operand pointer”。我不知道这个是做什么的,但英特尔SDM规定了它
  • 指令指针Instruction pointer:x87状态机显然持有它自己的当前x87指令的拷贝。
  • 最后一条指令的操作码Last instruction opcode:与x87操作码不同,并且有它自己的寄存器。

本部分寄存器总共: 14.

到目前为止一共: 92.

MMX

MMX是Intel在X86芯片上添加SIMD指令的第一次尝试,发布于1997年。MMX寄存器实际上是x87 STn寄存器的子集。每个64位MMn占用其相应STn的尾数部分。因此,x86(和x86-64)CPU不能同时执行MMX和x87指令。
MMX定义了MM0到MM7,8个寄存器,另外还有一个新的状态寄存器(MXCSR),以及用于操作它的加载/存储指令对(LDMXCSR和STMXCSR)。

本部分寄存器总共: 9.

到目前为止一共: 101.

SSE and AVX

为了简单起见,我打算把SSE和AVX包成一个部分:它们使用与GPR和x87/MMX相同的子寄存器模式,所以放在一个表中。

AVX-512 (512-bit) AVX-2 (256-bit) SSE (128-bit)
ZMM0 YMM0 XMM0
ZMM1 YMM1 XMM1
ZMM2 YMM2 XMM2
ZMM3 YMM3 XMM3
ZMM4 YMM4 XMM4
ZMM5 YMM5 XMM5
ZMM6 YMM6 XMM6
ZMM7 YMM7 XMM7
ZMM8 YMM8 XMM8
ZMM9 YMM9 XMM9
ZMM10 YMM10 XMM10
ZMM11 YMM11 XMM11
ZMM12 YMM12 XMM12
ZMM13 YMM13 XMM13
ZMM14 YMM14 XMM14
ZMM15 YMM15 XMM15
ZMM16 YMM16 XMM16
ZMM17 YMM17 XMM17
ZMM18 YMM18 XMM18
ZMM19 YMM19 XMM19
ZMM20 YMM20 XMM20
ZMM21 YMM21 XMM21
ZMM22 YMM22 XMM22
ZMM23 YMM23 XMM23
ZMM24 YMM24 XMM24
ZMM25 YMM25 XMM25
ZMM26 YMM26 XMM26
ZMM27 YMM27 XMM27
ZMM28 YMM28 XMM28
ZMM29 YMM29 XMM29
ZMM30 YMM30 XMM30
ZMM31 YMM31 XMM31

换句话说:每个ZMMn的低字节部分是YMMn,而每个YMMn的低字节部分是XMMn。没有直接的寄存器可以只访问YMMn的高字节部分,或者ZMMn的高128位或者256位字部分。
SSE还定义了一个新的状态寄存器MXCSR,它包含的标志与RFLAGS中的算术标志大致平行(与x87状态字中的浮点标志一起)。
AVX-512还引入了8个OPMask寄存器,k0到k7。k0是一个特殊的情况,它的行为很像一些RISC ISA上的 “零 “寄存器:它不能被存储到,而且从它的加载总是产生一个全部为1的位掩码。

本部分寄存器总共: 33.

到目前为止一共: 134.

边界寄存器(Bounds registers)

英特尔在MPX中加入了BR,其目的是提供硬件加速的边界检查。但好像名声并不是很大,暂且放这里把。
BND0 - BND3:单独的128位寄存器,每个都包含一对绑定的地址。
BNDCFG: 绑定配置,内核模式。
BNDCFU: 绑定配置,用户模式。
BNDSTATUS: 绑定状态,在一个#BR被提出后。

本部分寄存器总共: 7.

到目前为止一共: 141.

调试寄存器(Debug registers)

正如他们的名字那样,帮助和加速软件调试器的寄存器,如GDB。
有6个调试寄存器,分为两种类型:

  • DR0到DR3包含线性地址,每个地址都与一个断点条件相关。
  • DR6和DR7是调试状态和控制寄存器。DR6的低位表示遇到了哪些调试条件(在进入调试异常处理程序时),而DR7控制哪些断点地址被启用以及它们的断点条件(例如,当某一地址被写入时)。
  • 那么,DR4和DR5呢?不清楚! 但它们确实有编码,但分别被当做DR6和DR7,或者在CR4.DE[位3]=1时产生#UD异常。

本部分寄存器总共: 6.

到目前为止一共: 147.

控制寄存器 (Control registers)

x86-64 定义了一组控制寄存器,可用于管理和检查 CPU 的状态。有 16 个“主”控制寄存器,所有这些都可以通过MOV variant变体访问:

Name Purpose
CR0 Basic CPU operation flags
CR1 Reserved
CR2 Page-fault linear address
CR3 Virtual addressing state
CR4 Protected mode operation flags
CR5 Reserved
CR6 Reserved
CR7 Reserved
CR8 Task priority register (TPR)
CR9 Reserved
CR10 Reserved
CR11 Reserved
CR12 Reserved
CR13 Reserved
CR14 Reserved
CR15 Reserved

所有reserved的控制寄存器在访问时都会产生#UD,不把它们算在本文里。
除了 “主 “CRn控制寄存器之外,还有 “扩展 “控制寄存器,由XSAVE功能集引入。截至目前,XCR0是唯一指定的扩展控制寄存器。
扩展控制寄存器使用XGETBVXSETBV而不是MOV的变种。

本部分寄存器总共: 6.

到目前为止一共: 153.

“System table pointer registers”

这就是英特尔SDM对这些寄存器的称呼。这些寄存器保存着各种保护模式表的大小和指针。
据我所知,它们有四个:

  • GDTR:存放GDT的大小和基址。
  • LDTR:保存LDT的大小和基址。
  • IDTR:保存IDT的大小和基址。
  • TR:保存TSS的选择器和TSS的基址。

GDTR、LDTR和IDTR在64位模式下都是80位:16个低位是寄存器的表的大小,高64位是表的其实地址。
TR同样也是80位。16位用于选择器(其行为与段选择器相同),然后另外64位用于TSS的基本地址。

本部分寄存器总共: 4.

到目前为止一共: 157.

Memory-type-ranger registers

这些是有趣的例子:与到目前为止所涉及的所有其他寄存器不同,这些不是多核芯片中某个特定CPU所独有的,相反,它们是所有内核共享的。
MTTR的数量似乎因CPU型号而异,并且在很大程度上被页中的所取代page attribute table,该表是用MSR编程的。

本部分寄存器总共: ?.

到目前为止一共: >157.

MSR Model specific registers

像XCR一样,MSR可以通过一对指令间接地(通过标识符)访问,RDMSR和WRMSR。
MSR本身是64位的,但起源于32位的时代,所以RDMSR和WRMSR从两个32位的寄存器中读取和写入。EDX和EAX。
举个例子:下面是访问IA32_MTRRCAP MSR的设置和RDMSR调用,其中包括(除其他外)系统上可用的MTRR的实际数量。

MOV ECX, 0xFE ; 0xFE = IA32_MTRRCAP
RDMSR  ; The bits of IA32_MTRRCAP are now in EDX:EAX

RDMSR和WRMSR是特权指令,所以Ring 3代码不能直接访问MSR。我知道的一个例外是时间戳计数器(TSC),它存储在IA32_TSC MSR中,但是可以通过RDTSC和RDTSCP从非特权上下文中读取。
另外两个是FSBASE和GSBASE,除非启用了 FSGSBASE 支持,否则可以直接从环 3 修改 FSBASE 和 GSBASE。Linux 在 5.9 中启用了 FSGSBASE。它们分别存储为IA32_FS_BASE和IA32_GS_BASE。正如在段寄存器部分所提到的,这些存储在x86-64 CPU上的FS和GS段基。这使得它们成为相对频繁使用的目标(根据MSR标准),所以它们有自己专门的R/W操作码。

  • RDFSBASE和RDGSBASE用于读取
  • WRFSBASE和WRGSBASE用于写入
    但回到本文主题:有多少个MSR? 我们从SDM 335592-sdm-vol-4中寻找答案。

许多MSRs已经从一代IA-32处理器延续到下一代,并延续到Intel 64处理器。MSR的一个子集和相关的位域,被认为是架构MSR。由于历史原因(从奔腾4处理器开始),这些 “架构性MSR “被赋予 “IA32_”的前缀。
根据SDM中的Table 2-2, 最高序号的MSR是6097/17D1H,即IA32_HW_FEEDBACK_CONFIG。
然而,在记录的MSR序号是存在着明显的洞。SDM直接从3506/DB2H(IA32_THREAD_STALL)跳到6096/17D0H(IA32_HW_FEEDBACK_PTR)。在空的范围之上,还有一些范围被明确地标记为保留的范围。
为了计算MSR的实际数量,只提取SDM第4卷中的表2-2中的值。

  • 解压缩 SDM

    $ pdfjam 335592-sdm-vol-4.pdf 19-67 -o 2-2.pdf
    
  • 利用pdftotext,把PDF转换为纯文本。

    $ pdftotext 2-2.pdf table.txt
    # edit table.txt by hand
    
  • 使用 IA32_ 前缀,筛选字符串

    $ tr -s '[:space:]' '\n' < table.txt \
        | grep 'IA32_' \
        | tr -d '.' \
        | sed 's/\[.*$//' \
        | sort | uniq | wc -l
    

最后得到400个MSR,这要比6096合理多了。

本部分寄存器总共: 400.

到目前为止一共: >557.

参考

  • sandpile.org MSR 可视化。
  • Vol. 3A § 8.7.1 (“State of the Logical Processors”) of the Intel SDM has a useful list of
    nearly all of the registers that are either unique to or shared between x86-64 cores.
  • The OSDev Wiki has collection of helpful pages on various x86-64
    registers, including a great page on the behavior of the segment
    base MSRs.

总而言之,在相对较新的x86-64 CPU内核上大约有557个寄存器。但一些我不确定的外设情况。
现代英特尔CPU使用集成的APICs作为其SMT实现的一部分。这些APICs有自己的寄存器库,可以被内存映射,以便由x86 CPU读取和潜在的修改。本文没有计算它们,因为(1)它们是内存映射的,因此表现得更像来自任意硬件的映射寄存器,而不是CPU寄存器
英特尔的SDM 说Last Branch Records 存储在离散的非MSR寄存器中,本文也没有单独计算这些。文章来源地址https://www.toymoban.com/news/detail-730599.html

到了这里,关于汇编基础(3) --X86-64的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • (汇编) 基于VS的x86汇编基础指令

    visual studio 选择x86运行 示例代码 OV 溢出 超出表示范围为溢出 1,否则 0 UP 增量 1:以递减顺序对数据串处理;0:以递增顺序对数据串处理 EI 允许中断 CPU允许中断1,否则0 PL 正 运算结果为正则为1,否则0 ZR 零 运算结果为0则为1,否则0 AC 辅助进位 低4位向高位进位1,否则0 P

    2024年02月06日
    浏览(43)
  • 汇编基础(2) -- ARM64

    ARM架构中,ARM64(也称为AArch64)是一种64位处理器架构,它是ARMv8指令集的一部分。与之前的32位ARM架构相比,ARM64提供了更大的寄存器容量、更广阔的地址空间和更高的计算能力。 64位版本的指令集和32位版本的指令集有一些区别,这些区别主要涉及到以下几个方面: 寄存器

    2024年02月07日
    浏览(37)
  • ARM64汇编基础

    到目前为止,大部分的移动设备都是64位的arm架构,一直想抽个时间系统学习下,这个周末就专门来学习下。毕竟两天的时间,也只是简单的入门了解下,为后续工作和学习打下基础。 本次学习的主要内容包括寄存器、指令系统以及堆栈函数相关的知识,了解这些知识后,后

    2024年02月07日
    浏览(36)
  • 服务器基础知识:aarch64 arm64 arm x86有什么区别

    aarch64 和 arm64 是指基于ARM架构的64位处理器,而 arm 是指基于ARM架构的32位处理器。 x86 则是指基于x86架构的处理器。 架构: aarch64 、 arm64 和 arm 都属于ARM架构,而 x86 属于x86架构。 位数: aarch64 和 arm64 是64位处理器架构,能够使用64位的寄存器和指令集。 arm 是32位处理器架构

    2024年02月08日
    浏览(48)
  • 【CPU】关于x86、x86_64/x64、amd64和arm64/aarch64

    为什么叫x86和x86_64和AMD64? 为什么大家叫x86为32位系统? 为什么软件版本会注明 for amd64版本,不是intel64呢?     x86是指intel的开发的一种32位指令集,从386开始时代开始的,一直沿用至今,是一种cisc指令集,所有intel早期的cpu,amd早期的cpu都支持这种指令集,intel官方文档里面

    2024年02月05日
    浏览(59)
  • X86汇编语言:从实模式到保护模式--命令篇

    注:不能直接将内存赋值给内存,也不能将立即数直接赋值给段寄存器(CS DS ES SS),但是可以将内存直接赋值给段寄存器 div:使用操作数作为除数,除以被除数(无符号除法)。 如果除数的长度为8位,则只使用AX作为被除数,执行除法后商存储在AL中,余数存储在AH中。 如

    2024年02月05日
    浏览(37)
  • x86汇编_MUL/IMUL乘法指令_笔记52

    32位模式下整数乘法可以实现32、16或8位的操作,64位下还可以使用64位操作数。MUL执行无符号乘法,IMUL执行有符号乘法。 MUL指令:无符号数乘法 32 位模式下,MUL(无符号数乘法)指令有三种类型: 执行 8 位操作数与 AL 寄存器的乘法; 执行 16 位操作数与 AX 寄存器的乘法;

    2024年02月07日
    浏览(38)
  • CentOS(4)——关于Linux软件下载时:amd64、x86、x86_64、arm64 的说明

    目录 一、简介 二、常见的CPU架构 三、Linux查看CPU架构命令 ①arch命令 ②uname -a 命令 ③lscpu 在安装GitLab Runner的时候,去清华源下载RPM包时发现同一个软件有许多不同架构的安装包,常见的有amd64、x86、x86_64、arm64这些架构,这就类似于Windows下安装软件需要区分32bit和64bit。在

    2024年02月03日
    浏览(56)
  • arm64-v8a、armeabi-v7a、x86、x86_64

    当我们去GitHub下载应用的时候是不是经常很懵逼,就像下图一样,粗看一下如此多安装包到底要选择下载哪个且每种安装包到底有哪差别?毕竟因为自己一无所知,有时便随意下载一个后,安装时却报『此版本与你的系统不兼容』的错误,只得一个一个下载尝试,不但浪费时

    2024年04月26日
    浏览(57)
  • Linux上x86_64架构的动态链接器 ld-linux-x86-64.so.2

    /lib64/ld-linux-x86-64.so.2 是Linux操作系统上x86_64架构的动态链接器(也称为动态链接编辑器)。它负责加载和链接在运行时(即程序启动时或之后)被引用的动态库。现在,我们来深入了解其作用和重要性: 动态链接器的作用 : 当运行一个可执行程序时,该程序可能依赖于多个动

    2024年02月02日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包