ldr [pc, #value]的奇怪行为

11

我在调试一些C++代码(在ARM平台上使用WinCE 6),发现了一些奇怪的行为:

    4277220C    mov         r3, #0x93, 30
    42772210    str         r3, [sp]
    42772214    ldr         r3, [pc, #0x69C]
    42772218    ldr         r2, [pc, #0x694]
    4277221C    mov         r1, #0
    42772220    ldr         r0, [pc, #0x688]

代码中的42772214 ldr r3, [pc, #0x69C]这一行用来从.DATA节获取一些常量,至少我是这么认为的。

奇怪的是,根据代码,r2应该填充地址为 pc=0x42772214 + 0x69C = 0x427728B0 的内存,但据内存内容显示,它是从0x427728B8(+8字节)加载的,其他ldr使用也会发生同样的情况。

这是调试器的错误还是我的ldr/pc理解有误呢?另外一个问题是:为什么访问.data部分与执行的代码相关呢?我觉得有点奇怪。

还有一个问题:我找不到第一个mov命令的语法(任何人都可以指向我Thumb(1C2)的操作类型规范吗?)

抱歉我的描述比较初级,但我正在熟悉汇编语言。


这看起来不像是thumb代码,而更像是ARM代码。所有指令的地址相差4个字节 - 只有少数几个4字节的thumb指令。 - Aidan Cully
在我看过的大多数架构中,程序计数器在指令执行之前被递增。在指令执行期间,程序计数器将包含当前指令结尾的地址。 - Mehrdad Afshari
1
为什么ARM PC寄存器指向下一条即将执行的指令后面的那个指令? - Ciro Santilli
1个回答

23

这是正确的。当在ARM模式下使用pc进行读取时,偏移量为8个字节,在Thumb模式下则为4个字节。

从ARM-ARM中可以找到:

当指令读取PC值时,所读取的值取决于它来自哪个指令集:

  • 对于ARM指令,读取的值是指令地址加上8字节。该值的[1:0]位始终为零,因为ARM指令始终是按字对齐的。
  • 对于Thumb指令,读取的值是指令地址加上4字节。该值的[0]位始终为零,因为Thumb指令始终是按半字对齐的。

这种读取PC的方式主要用于快速、位置无关的附近指令和数据寻址,包括程序内的位置无关分支。

进行相对于PC的寻址有两个原因:

  1. 位置无关代码,这是您的情况。
  2. 获取一些复杂的常量,这些常量不能用一个简单的指令写出,例如mov r3,#0x12345678不可能在1个指令中完成,因此编译器可能会将此常量放在函数末尾,并使用ldr r3,[pc,#0x50]来加载它。

我不知道mov r3,#0x93,30是什么意思。可能是mov r3,#0x93,rol 30(得到0xC0000024)吗?


4
mov r3, #0x93, 30 实际上是 mov r3, #0x93, ror 30,得到的结果是 0x24c - Mike Seymour
2
@Mike - 解释得很好,引用ARM ARM也很好。在ARM的3级流水线中,PC指向被获取的指令,PC-4指向正在解码的指令,而PC-8是“当前指令”,即正在执行的指令。这也是异常在返回之前必须调整LR值的原因。正如你所指出的那样,这适用于ARM(32位)指令,因此每个流水线阶段都要进行4字节的调整。 - Dan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接