ARM架构下添加命令和程序计数器

Add command under ARM architecture and program counter

我关注的是使用 add 命令的 ARM 程序集片段。下面的代码片段简单地指出:在程序计数器的地址中加上计算得出的偏移量,以找到存储在 L._str 处的字符串的位置,其中 L._str 是一个符号(地址)数据段中包含的字符串。

movw    r0, :lower16:(L_.str-(LPC1_0+4))
movt    r0, :upper16:(L_.str-(LPC1_0+4))
LPC1_0:
    add r0, pc

前两条指令(movwmovt)加载表示该字符串地址的 32 位数字。我在 Thumb 模式,对吗? 好的,这么说,我很难弄清楚整体内存布局。以下是内存代码段的正确表示吗?另外,LPC1_0L._stradd r0, pc的基地址是A simple string字符串的地址吗?每个盒子的尺寸是多少? 32 位或 64 位取决于架构。

--------------------------------------------
| movw    r0, :lower16:(L_.str-(LPC1_0+4)) |
--------------------------------------------
| movt    r0, :upper16:(L_.str-(LPC1_0+4)) |
-------------------------------------------- LPC1_0
| add r0, pc                               |
--------------------------------------------
                       .
                       .
                       .
-------------------------------------------- L._str
| "A simple string"                        |
--------------------------------------------

如果是这样,我可以使用差异 L_.str-LPC1_0 检索偏移量(将添加到 pc)。但是,这里 +4 也被考虑在内。

来自ADD, pc or sp relative

ADD Rd, Rp, #expr

If Rp is the pc, the value used is: (the address of the current instruction + 4) AND &FFFFFFFC.

因此,如果 pcRp,那么我还需要考虑 +4 更多字节作为偏移量。好的。那么,这些字节添加到哪里了呢?为什么这 4 个字节被考虑到 mov 指令而不是 add 命令之前?这是编译器引入的优化功能吗?

我有根据的猜测:

您想获取内存中 L_.str 所在的 "absolute" 地址。 movwmovt 似乎添加立即值,因此该值在操作码内。

编译器计算LPC1_0L_.str之间的偏移量,并减去另一个4(字节)。

add r0,pc 指令将 pc+4 添加到该值。

+4 由处理器添加。我认为这是因为 pc 在处理器 "logic" 中很早就递增了,而 add 只能在之后读取 pc 的值。记录它确实是 pc+4 比添加额外的逻辑以通过处理器添加 pc+4-4 更简单...

计算 L_.str 地址的整个解决方案的优势在于它独立于该代码的重定位。

正常的与位置无关的"get the address of something"指令就是adr, r0, L._str(相当于让assembler/linker自动计算add r0, pc, #offset的适当偏移量)。然而,由于 ARM 架构使用固定宽度编码——ARM 指令是 32 位宽,Thumb 指令是 16 位或 32 位——只有有限数量的指令位可用于编码偏移量的立即值,所以最大范围是有限的。 adr 的 Thumb 编码可以支持的最大可能偏移量是 +/-4095 字节。由于编译器不知道链接器将这些部分分开多远,因此它不能安全地发出 adr,因为最终偏移量太大而无法 assemble 的风险,因此您得到 3-指令生成 immediate/add PC 序列。优点是它可以到达任何 32 位地址,权衡是它在程序映像和指令缓存中占用更多 space - adr 单独是 2 或 4 个字节(取决于偏移量和目标寄存器),movw/movt/add 序列重 10 个字节,执行时间至少是两倍。

至于为什么PC偏移量被折叠到节偏移量中,嗯,为什么不会呢?两者都是常数,因此当链接器计算最终图像中 LPC1_0L_.str 之间的距离以将立即值编码到 movw/movt 指令时,它有 同时添加 PC 校正绝对没有任何好处。这就是为什么原始 ARM 的 3 级流水线的 2 指令 fetch/execute 偏移量首先暴露的原因,因为在构建软件时修复 assembler/linker 中的地址比实现更简单"correct" 它在硬件中的所有逻辑。