随着CPU体系结构的发展,寄存器大小从8扩展到16、32和最终是64位。我想知道是否有任何方法可以访问寄存器的较高部分。
下面是有关
rax
64位寄存器及其后续除法示例:6 3 1
4 2 6 8 1
+------------------------------+---------------+-------+-------+
| rax |
+------------------------------+---------------+-------+-------+
(???) | eax |
+---------------+-------+-------+
(???) | ax |
+-------+-------+
| ah | al |
+-------+-------+
分别引用
ax
和ah
可以轻松访问al
的上部和下部。但是对于rax
和eax
更高的部件,我找不到任何参考。 (用(???)
表示)是否可以访问32位和64位寄存器的较高部分?如果是这样,哪个?
如果不可能,原因是什么?
注意:我在说直接访问这些字节,我不是在要求序列可以检索它们的指令集。
#1 楼
是否可以访问32位和64位寄存器的较高部分?如果是,是哪一个?
无法直接访问
EAX
和RAX
寄存器或任何其他32位和64位寄存器的较高部分。如果您有兴趣这样做,则必须使用间接指令序列。这是因为在任何指令中都没有编码来访问这些部分。假设不可能,这是什么原因造成的?
正如@nordwald在下面的评论中指出的那样,它在手册中的定义很简单。要获得更详细的官方答案,我们需要询问规范定义的成员。我们可以肯定地认为,核心原因是提供对所有可用寄存器分数的访问权的成本超出了收益。过去的t:
更多可用寄存器空间的好处
在16位寄存器期间,寄存器稀缺(由于芯片制造)费用),而芯片则吹嘘着可供用户使用的寄存器数量。需要更多的寄存器。公开半寄存器可以更好地利用可用的(宝贵和昂贵)寄存器房地产。随着时间的流逝,越来越多的寄存器可供使用,对更好地使用确切的寄存器大小的需求减少了。如今,您只能将64位寄存器用于其低位字节或字,而不必担心没有足够的寄存器。
增加指令集的成本
由于可用的指令集相对较小并且只有几个寄存器,因此有足够的位可用于编码寄存器的另一半,而不会增加指令集的大小。在最初的16位寄存器天中,使用了大多数可用的指令集空间。转移到32位(然后再转移到64位),而不是重新实现具有不同寄存器大小前缀的相同指令(例如,对于32位寄存器为
0x66
),其余指令保持不变。这使得在16位原始指令集之上支持32位寄存器变得微不足道,但是访问这些寄存器的较高部分需要更复杂的设计。您会注意到只有8个全尺寸寄存器(AX
,CX
,DX
,BX
,SP
,BP
,SI
和DI
)以及仅8个半尺寸寄存器(AL
,CL
,DL
,BL
,AH
,CH
,DH
,q4312079q ,因此只需要3位即可指定所需的寄存器。现在,对寄存器的所有部分进行编码会增加指令集和总体CPU复杂度。
支持遗留代码
当英特尔制造第一个16位处理器(8086处理器系列)时,他们希望保持所谓的源兼容性。这意味着可以将8位处理器(Z80 / 8080)代码组装为16位处理器,而无需更改代码/源代码,尽管可以更改指令的基本二进制表示形式。因此,即使完全重新设计了指令二进制表示并且新的CPU不支持8位模式(与32位和64位二进制模式不同),8位寄存器也必将保留在早期的8位处理器中向后兼容)。 Z80处理器系列具有六个8位寄存器,可以一起访问以形成16位字。
此外,从16位处理器过渡到32位处理器,再从32位处理器过渡到64位处理器,需要保持向后兼容性并支持传统执行模式。因此,通过对多个指令保留相同的指令集和相同的二进制编码,从而“保持”半寄存器作为可用指令集的一部分,可以保持相同的二进制编码。
寄存器访问同步的成本
随着流水线优化的日益流行,同步寄存器访问的负担(除其他外)也增加了,这使得CPU难以跟踪
在现代CPU中,半寄存器实际上并不与全寄存器重叠,而是当更改一个寄存器时,CPU在所有其他寄存器上设置一个特殊的无效标志,因此它将知道如果访问其他寄存器,则读取更新的寄存器。这样可以进行高级CPU级别的优化(例如,重命名寄存器),但是当半尺寸寄存器可互换使用(不再那么频繁)时,实际上会使执行速度变慢。
半尺寸寄存器仅部分可用以
开始,尽管您可能会假设并非如此,但并非总是可以访问或使用半尺寸寄存器。例如,您不能压入一半寄存器(没有“ Push AL”)。这样做是为了仅支持使半寄存器有用的最低要求,但不会将其视为全吹寄存器。 ,有些可能与原始决定无关,某些原始原因可能在此处缺失,等等。这些仅仅是有根据的猜测和YMMV
评论
对于第一部分,它仅在《英特尔指令手册》第3.4.1节“通用寄存器”中指定。我认为第1点和第2点很好地总结了这个问题。
–诺德瓦尔德
18年7月9日在10:42
谢谢。我相信您的回答,以及诺德瓦尔德(Nordwald)的评论,都会让您大跌眼镜。如果有人有其他解释,我将等待一两天再接受答案。
–百合子
18年7月9日在11:55
即使给出了OP的目标,声称“访问”这些目标还是有些误导。当然可以。只需使用移位指令即可。没有一个伪寄存器,它专门将较高部分编码为唯一操作数。
–克里斯·斯特拉顿(Chris Stratton)
18年7月9日在23:48
并非所有x86 CPU都将部分寄存器的名称与完整寄存器的名称分开。仅Intel P6系列和Sandybridge系列这样做,而不是AMD,不是Intel P4,不是Silvermont / Knight's Landing。请参阅为什么GCC不使用部分寄存器?总结不同君主的行为。 (Haswell和更高版本甚至没有与RAX分别重命名AL或AX,而仅重命名AH / BH / CH / DH)。当一个reg没有单独重命名时,写它就是对整个寄存器的读-修改-写操作。 (可能会创建错误的依赖关系,而不是以后合并成本。)
– Peter Cordes
18年7月10日在10:49
我接受了这个答案,因为它对我来说似乎更有意义,尽管MSalters的答案很有趣并且目前有更多的支持。
–百合子
18年7月11日在14:39
#2 楼
确实,这是一个错误的问题。AH
是例外。 现在真正的问题是,为什么
AH
是一个例外?它是8086年代的旧寄存器。它的存在是为了方便从8080移入代码。8080与8086的寄存器不同,因此您不能直接移入代码。特别是,它没有AL,AH或AX寄存器。它确实有一个8位的A累加器和一个8位的F标志寄存器,它们组合在一起形成一个16位的AF寄存器。
8086保留了16位累加器,但将标志移到了自己的寄存器中。但是,仍然有残留物。
LAHF
从AH
的低8位加载F
,而AL中的低8位保持不变。该指令简化了将8080代码移植到8086的过程。是的,在2018年Core i9中仍然提供对8080的支持。评论
谢谢您的回答。我发现答案可能不是,那是出于历史原因的例外。您的回答清楚地说明了这一点,并且使似乎有些奇怪的决定变得有意义。
–百合子
18年7月9日在13:24
#3 楼
更新:感谢Nirlzr,我发现我错过了帖子底部的重点。这个答案尽管不是OP所寻找的,但对于正在寻找访问这些位的方式的任何人可能有用。很遗憾缺少OP的歉意!几年前,我实际上写了一篇有关此主题的深入文章:访问和修改x86和x64寄存器中的高位
因此,虽然没有任何直接指令专门用于访问32位或64位寄存器的上半部分,但是您可以使用移位和旋转指令来获取该数据(选择哪种方式主要取决于您是否在意)关于保持寄存器下半部分中位的完整性)。
我会在这里零碎文章,以获得更肉体的答案,但最好只阅读这篇文章,是,它具有非常有意的流程,其中包含大量示例。
评论
一篇很好而详尽的文章,谢谢!但请注意,OP特别提到了这不是他在问题底部寻找的内容。
– NirIzr
18年7月9日在14:12
哎呀。你是对的。由于某种原因,我完全掩饰了这一点,哈哈。好吧,对于以后可能会发现该线程寻找如何访问这些位的任何人,也许这会找到目的。 =)
–dsasmblr
18年7月9日在14:14
是的,如果您问我,没有害处:)
– NirIzr
18年7月9日在14:16
@dsasmblr:确实,我想专注于直接访问。但是,您的答案仍然是对其他人的补充。稍后我会阅读您的文章。谢谢您的意见!
–百合子
18年7月9日在14:23
评论
跨站点重复:为什么没有包含更高字节EAX的寄存器?,为什么英特尔不提供其CPU寄存器的高位部分可用?,为什么不能访问EAX中的高16位名称(例如AX,AH和AL)?这是公然的话题,因为这是关于体系结构的根本问题,而不是与逆向工程有任何关系的事情。
@ChrisStratton:谢谢您的投入;我明白你的意思。随时在meta上提出问题,因为这对您而言确实是一个值得关注的问题。我个人认为,逆向工程(RE)涵盖(并适用于)多个领域。毫无疑问,这个问题可以在另一个SE站点上占有更大的基础,就像可以将与组装相关的问题重定向到StackOverflow,将与软件相关的问题重定向到SuperUser等一样。了解软件,语言和体系结构是如何工作的可以说是RE学习过程的一部分。
可以(应该)将此问题迁移到SO并作为重复项关闭。作为学习逆向工程的基础,您需要学习很多东西,但这并不意味着所有内容都是逆向工程或属于本网站。我不认为将asm / cpu-architecture问题分散在多个站点上不是一个好主意,除了专门针对逆向工程的站点之外。当通用ISA设计问题在此处和SO之间分开时,将更难找到相关问题,而不是更容易。