1

我有一个指令块,我想知道 pc 寄存器是如何工作的。它在 Wikipedia 上说,pc 寄存器保存要执行的下一条指令的值,但是,查看二进制 ninja 的反汇编图以获得相同的指令块,这似乎并不完全正确。

这是二进制 ninja 的反汇编图的一部分,其中在每个内存加载的前面都写入了加载发生的内存地址。
在此处输入图像描述

000080ec         ldr r3, [pc, #76] -> 0x813c = 0x80f0 + 0x4c -> pc = 80f0 ?? (shouldnt it be 80ee).
000080ee         cmp r3, #0
000080f0         it eq
000080f2         ldreq r3, [pc, #68] -> 0x8138 = 0x80f4 + 0x44 -> pc = 80f4 (this makes sense).
000080f4         mov sp, r3
000080f6         sub.w sl, r3, #65536 (edited) 

这也发生在代码的下方,PC并不总是保存下一条要执行的指令的地址。有什么我应该解释的吗?

4

2 回答 2

4

您缺少的关键是PCThumbldr Rd, [Pc, #imm]指令中的值在使用之前与 4 个字节对齐。来自 ARMv7 架构参考手册的略微删节的伪代码是:

t = UInt(Rt);
imm32 = ZeroExtend(imm8:’00’, 32);
base = Align(PC,4);
address = base + imm32;
data = MemU[address,4];
R[t] = data;

所以回到你的例子:

000080ec         ldr r3, [pc, #76]

我们知道PC在 Thumb 模式下读取为当前地址加上 4 个字节,因此 PC 读取为0x80f0. 该值已经对齐到 4 个字节,因此base具有相同的值。为此我们添加76(立即数总是四的倍数,两个最低有效位未存储)得到0x813c

对于第二个例子:

000080f2         ldreq r3, [pc, #68]

这与上面的指令相同ldr。反汇编程序为助记符添加eq后缀,因为该指令受前一个IT块的条件执行。但是,这不会以任何方式影响指令编码。 PC读取为0x80f6与 4 字节对齐的0x80f4. 为此,我们添加68,0x8138作为要加载的地址。

有关详细信息,请参阅 ARM 体系结构参考手册。

于 2021-12-30T15:23:15.253 回答
3

pc 相关 ldr 指令的拇指编码最近刚刚在 SO 上介绍过。当您查看指令集上的文档时,您会如我们所指出的那样知道 PC 从文档的角度来看,在拇指前 2 天的早期是两个字节,但现在对于拇指来说,它比指令地址提前 4 个字节. pc 偏移量以字为单位进行编码,因此使用的地址是

((instruction address + 4 ) & 0xFFFFFFFC) + (immed<<2)

消除对前面两个事情的所有困惑。

现实情况是有多个程序计数器,单个程序计数器用于实际获取内容和进行 pc 相对寻址的日子是旧的、更简单的体系结构的历史的一部分。

这两个领先的东西是过去的一部分,但出于兼容性的原因,已经从 acorn 延续到现在的 arm 产品,就像 x86 和其他人有不再是他们所说的那样的遗留东西(分支阴影/延迟插槽米)。

管道是不同的,人们会假设每个不同的 arm 产品(不是架构,而是产品 cortex-m0、cortex-m4、cortex-a7 等)的管道实现以及核心跟踪事物的方式各不相同。前面的两个是由某种形式的程序计数器合成的,它跟踪管道中的指令。同样,提取/预取/分支预测都是程序计数器的所有形式,但不假定为单个程序计数器。r15 本身也是来自寄存器文件的真实或假的或两者兼而有之(我希望不在寄存器文件中,为什么要烧掉这些周期而不增加任何价值)。

就像在软件中你可以有一个 reg[15] 数组项、一个 pc_fetch、一个 pc_current_inst、pc_execution、一个 pc_possible_branch、一个 pc_branch_prediction 变量集来跟踪处理器的模拟,逻辑也可以。而在什么时间使用哪一个取决于你在做什么。正如在指令操作中所描述的那样,作为 PC 的程序员,我们所理解的是一个地址,该地址比指令所在的地址“提前两个”。使用 thumb2 时,前面的两个不再有意义,因此对于拇指模式,它是 4 个字节,对于 arm 模式,它比指令地址提前 8 个字节。然后您按照文档了解在执行指令期间如何使用该 PC。

对于 BX 和其他能够切换模式的指令,成为“程序计数器”的地址的定义是不同的,lsbit 驱动模式切换到(并被它不在程序计数器中的分支剥离,有是一个 psr 来处理这个问题)。这些地址也是程序计数器的一种形式,至少暂时是要跳转到的指令的实际地址,而不是两个前面的地址。

在许多早期的处理器实现中,你有一个或一个程序计数器的想法,你一次获取、解码和执行一条指令,然后再继续下一条(并不意味着人们不再做这些设计,你可以小而高效的小控制器是老式的方式,人们仍然这样做,并且在我们使用的产品中)。在这种情况下,pc 用于获取可能超过一个字节的指令,一旦指令被完全获取,那么程序计数器至少暂时指向下一条指令。现在可以开始执行该指令,因为获取和解码已经完成。如果程序计数器用作该指令的输入,则它指向下一条指令,如果用作跳转或分支中的目的地,那么它会被修改,完成后下一次提取发生在它碰巧指向的任何地方。这些架构中有许多是可变长度指令集,因此,一条指令可能是一、二、三……字节长,因此在执行时相对于指令地址的 pc 地址是不同的。早期的 arm 来自具有固定大小指令的管道类型解决方案,因此,如果您有单个程序计数器,那么根据管道设计,如果您使用教科书式的,那么执行在管道含义中的固定深度当您执行时,程序计数器会提前获取那么多。字节长,因此执行时相对于指令地址的 pc 地址是不同的。早期的 arm 来自具有固定大小指令的管道类型解决方案,因此,如果您有单个程序计数器,那么根据管道设计,如果您使用教科书式的,那么执行在管道含义中的固定深度当您执行时,程序计数器会提前获取那么多。字节长,因此执行时相对于指令地址的 pc 地址是不同的。早期的 arm 来自具有固定大小指令的管道类型解决方案,因此,如果您有单个程序计数器,那么根据管道设计,如果您使用教科书式的,那么执行在管道含义中的固定深度当您执行时,程序计数器会提前获取那么多。

于 2021-12-30T14:46:49.783 回答