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
前两条指令(movw
和 movt
)加载表示该字符串地址的 32 位数字。我在 Thumb 模式,对吗?
好的,这么说,我很难弄清楚整体内存布局。以下是内存代码段的正确表示吗?另外,LPC1_0
和L._str
是add 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 Rd, Rp, #expr
If Rp is the pc, the value used is: (the address of the current instruction + 4) AND &FFFFFFFC.
因此,如果 pc
是 Rp
,那么我还需要考虑 +4
更多字节作为偏移量。好的。那么,这些字节添加到哪里了呢?为什么这 4 个字节被考虑到 mov
指令而不是 add
命令之前?这是编译器引入的优化功能吗?
我有根据的猜测:
您想获取内存中 L_.str
所在的 "absolute" 地址。
movw
和 movt
似乎添加立即值,因此该值在操作码内。
编译器计算LPC1_0
和L_.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_0
和 L_.str
之间的距离以将立即值编码到 movw
/movt
指令时,它有不 同时添加 PC 校正绝对没有任何好处。这就是为什么原始 ARM 的 3 级流水线的 2 指令 fetch/execute 偏移量首先暴露的原因,因为在构建软件时修复 assembler/linker 中的地址比实现更简单"correct" 它在硬件中的所有逻辑。
我关注的是使用 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
前两条指令(movw
和 movt
)加载表示该字符串地址的 32 位数字。我在 Thumb 模式,对吗?
好的,这么说,我很难弄清楚整体内存布局。以下是内存代码段的正确表示吗?另外,LPC1_0
和L._str
是add 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 Rd, Rp, #expr
If Rp is the pc, the value used is: (the address of the current instruction + 4) AND &FFFFFFFC.
因此,如果 pc
是 Rp
,那么我还需要考虑 +4
更多字节作为偏移量。好的。那么,这些字节添加到哪里了呢?为什么这 4 个字节被考虑到 mov
指令而不是 add
命令之前?这是编译器引入的优化功能吗?
我有根据的猜测:
您想获取内存中 L_.str
所在的 "absolute" 地址。
movw
和 movt
似乎添加立即值,因此该值在操作码内。
编译器计算LPC1_0
和L_.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_0
和 L_.str
之间的距离以将立即值编码到 movw
/movt
指令时,它有不 同时添加 PC 校正绝对没有任何好处。这就是为什么原始 ARM 的 3 级流水线的 2 指令 fetch/execute 偏移量首先暴露的原因,因为在构建软件时修复 assembler/linker 中的地址比实现更简单"correct" 它在硬件中的所有逻辑。