我正在查看这段C代码的反汇编代码:
#define GPIO_PORTF_DATA_R (*((volatile unsigned long *)0x400253FC))
int main(void){
// Initialization code
while(1) {
SW1 = GPIO_PORTF_DATA_R&0x10; // Read PF4 into SW1
// Other code
SW2 = GPIO_PORTF_DATA_R&0x01;
}
}该SW1=行的程序集为(对不起不能复制代码):
以下是我的问题:
我对此做了一些研究,发现PC实际上指向了下一个要执行的指令。
当pc用于读取时,ARM模式有8字节偏移,拇指模式有4字节偏移。
然而,0x00000AB4-0x00000A56= 0x5E = 94,它也不匹配92+8或92+4。
参考文献
Strange behaviour of ldr [pc, #value]
Why does the ARM PC register point to the instruction after the next one to be executed?
发布于 2021-12-27 06:21:01
您忽略了拇指模式规则的一个关键部分,引用在您链接的一个问题(Why does the ARM PC register point to the instruction after the next one to be executed?)中:
对于所有使用标签的其他指令,PC的值是当前指令的地址加上4个字节,与bit1的结果清除为0,使其字对齐。
(0xA56 + 4) & -4 = 0xA58是在该ldr r0, [PC, #92]执行过程中与PC相关的事物相对的位置。
((0xA56 + 4) & -4) + 92 = 0xab4,反汇编程序计算的位置。0xA56 & -4 = 0xa54然后是+4 + 92,因为+4没有修改bit #1;您可以考虑在添加+4之前或之后清除它。但是,在添加PC相对偏移量之后,您无法清除该位;对于其他指令(如ldrb ),可以不对齐。(拇指模式ldr用文字编码偏移量,以更好地利用有限的位数,因此缩放的偏移量和最终的负载地址总是1:0清晰。)。
(感谢陈雷蒙德发现了这一点,我一开始也错过了!)
还请注意,调试器在断点停止时向您显示一个PC值,但这是被停止的指令的地址。(因为我认为ARM异常就是这样工作的,它保存了要返回的实际指令,而不是一些偏移量。)在指令的执行过程中,PC相关内容遵循不同的规则.调试器也不会“编写”这个值来显示在执行过程中PC会是什么。
这个规则不是“相对于这个/开始下一个指令的结尾”。的答案和注释指出,在这种情况下,规则碰巧得到了正确的答案,但在其他拇指情况下,会得到错误的答案,比如在LDR Rd,-Label vs LDR Rd,[PC+Offset]中,PC相对加载指令恰好从一个4字节对齐地址开始,因此PC的位#1已经被清除。
您的LDR位于address 0xA56,其中设置了位#1,因此舍入有效果。您的ldr指令使用的是2字节编码,而不是Thumb2 32位指令,因为您可能需要更大的偏移量。这两种情况都意味着四舍五入+4恰好是下一条指令的地址,而不是后面的2条指令或这条指令的中间地址。
发布于 2021-12-27 11:31:08
来自ARM文件:
Operation
address = (PC[31:2] << 2) + (immed_8 * 4)
Rd = Memory[address, 4]pc是0xA56+4,因为前面有两个指令,这是拇指,所以是4个字节。
(0xA5A>>2)<<2 + (0x17*4)
or
(0x00000A5A&0xFFFFFFFC) + (0x17<<2)
0xA58+92=0xA64这是一个LDR,所以理想情况下它是一个基于字的地址。由于拇指指令可以位于非字对齐地址上,所以首先添加两个指令(当然,thumb2会使这一点复杂化,而拇指则添加四个)。然后,零下两位(LDR)偏移量是字,所以需要把它转换成字节,乘以4。这使编码更有意义,如果你考虑它的每一个部分。在arm模式下,PC已经是字对齐了,所以不需要步骤(在arm模式下,您有更多的位用于即时,所以它是基于字节的,而不是基于字的),使得arm和拇指之间的偏移编码可能会混淆。
不同的文档将以不同的方式显示数学,但它仍然是相同的数学。个人电脑是唯一令人困惑的部分,特别是拇指。对于ARM,您可以在前面添加8,2,对于拇指,它基本上是4,因为执行不能判断是否有一个thumb2即将到来,而且如果他们试图这样做的话,它会破坏很多事情。所以前面的两个加4,拇指。由于拇指是压缩的,它们不使用字节偏移量,而是使用单词偏移量,使范围增加了4倍。同样,此指令和/或其他指令只能向前看,而不能向后看,这样没有符号的偏移量。这就是为什么在用拇指装配东西时,你会得到对齐错误,而在手臂上的东西只是不对齐(根据架构和设置,你得到了你得到的东西)。拇指不能为这样的指令编码任何地址。
为了理解指令编码,特别是基于pc的寻址,最好回到早期的ARM (在armv5之前,但如果不是那样的话,只得到armv5的)以及armv7 6-m和armv7 7-m和全尺寸的armv7 7-ar。看看每个人的伪码。旧的通常有最好的伪码,但有时他们没有掩蔽低位的地址。没有一个文档是完美的,它们和其他任何东西一样都有bug。当然,与您所使用的核心相关联的体系结构是芯片供应商使用的IP的正式文档(甚至到了TRM的特定版本,因为这些文件可能因不同而不同)。但是,如果该文档不太清楚,您有时可以从其他人那里得到一个想法,在检查时,它具有兼容的指令和体系结构特性。
发布于 2021-12-27 03:56:15
由于程序计数器指向下一条指令,当它在地址0x00000A56处执行LDR时,程序计数器将保存下一条指令的地址,即0x00000A58。
0x0A58 + 0x5C (decimal 92) == 0x00000AB4
https://stackoverflow.com/questions/70491482
复制相似问题