了解 ARM 中的 ADR 指令,并为其添加偏移量
我正在查看代码的汇编程序输出,需要以下说明的帮助。
0x00000fe8: e28fc000 .... ADR r12,{pc}+8 ; 0xff0
0x00000fec: e28cca08 .... ADD r12,r12,#8, 20 ; #0x8000
根据我的理解,第一条指令导致 r12 加载 {pc value} + 8
即
“{当前执行指令的地址 (0xfe8) 加上前面 2 个指令 (8)} + 8”,
因此在第一个指令执行加载 0xff8 (0xfe8+8+8)
后,r12
也是关于第二个指令指令-
如何计算添加并存储到r12的值? (评论说它是0x8000,尽管我无法理解它是如何得到的)
I was looking at the assembler output of my code and need help with below instructions.
0x00000fe8: e28fc000 .... ADR r12,{pc}+8 ; 0xff0
0x00000fec: e28cca08 .... ADD r12,r12,#8, 20 ; #0x8000
From my understanding the 1st instruction causes r12 to be loaded with {pc value} + 8
that is
"{Address of current instruction in execution (0xfe8) plus 2 instructions ahead (8)} + 8"
so is r12 after 1st instruction execution loaded with 0xff8 (0xfe8+8+8)
Also regarding the 2nd instruction -
How to calculate the value being added and stored to r12? (the comment says its 0x8000, though i am not able to understand how it got this)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
第一条指令(实际上是伪指令)将 PC 相对地址加载到 R12 中。由于指令位于地址 0xFE8,因此表达式 {pc}+8 的计算结果为 0xFF0。所以第一条指令的结果是将值0xFF0加载到R12中。评论实际上表明了这一点。
(请注意,ADR 不是真正的 ARM 指令,汇编器将其替换为 ADD 等指令。另请注意,该表达式的值是在汇编时计算的。在程序执行期间 em>,由于处理器的流水线,PC 比当前指令提前多少取决于架构(例如 ARM7 等)和操作模式(Thumb/ARM)。我冒着在这里提供有关 ADR 和 ADR 的“太多信息”的风险。与 PC 相关的表达式/寻址,但如果您不了解幕后发生的事情,很容易被咬。)
第二条指令(实际上是从右到左读取)有效地表示“取常量 0x8,将其向右旋转20 位(与左移 12 位相同,32-20 = 12),将其添加到 R12(当前保存 0xFF0),并将其存储在 R12 中。” 0x8 <<; 12 = 0x8000,因此第二条指令导致 R12 保存 0x8000 + 0xFF0 = 0x8FF0。
注意:在你的解释中,当你说“前面有2条指令”时,不要陷入这种习惯,将其视为8个字节,而不是2条指令。指令说添加 8 个字节,但没有说明任何有关指令的内容。指令不一定是 4 字节长(例如,在 Thumb 中,它们是 2 字节;在 Thumb2 中,它们是 2 字节或 4 字节,具体取决于指令)。
The first instruction (really a pseudo-instruction) loads a PC-relative address into R12. Since the instruction is at address 0xFE8, the expression {pc}+8 evaluates to 0xFF0. So the result of the first instruction is to load the value 0xFF0 into R12. The comment actually indicates this.
(Note that ADR isn't a real ARM instruction, the assembler replaces it with an instruction such as ADD. Also note that this expression's value is calculated at assembly time. During program execution, the PC points ahead of the current instruction, due to the processor's pipeline. How much ahead depends on the architecture (e.g. ARM7, etc.) and the operating mode (Thumb/ARM). ) I'm risking giving "too much information" here about ADR & PC-relative expressions/addressing, but it's easy to get bitten if you don't understand what's going on behind the scenes.)
The second instruction (actually reading from right to left) effectively says "take the constant 0x8, rotate it right by 20 bits (which is the same as a left shift by 12 bits, 32-20 = 12), ADD it to R12 (which currently holds 0xFF0), and store it in R12." 0x8 << 12 = 0x8000, so the 2nd instruction results in R12 holding 0x8000 + 0xFF0 = 0x8FF0.
Note: In your explanation, when you said "2 instructions ahead", don't fall into that habit, think of it as 8 bytes, not 2 instructions. The instruction says add 8 bytes, it doesn't say anything about instructions. Instructions aren't necessarily 4 bytes long (for example, in Thumb, they are 2 bytes; in Thumb2, they are 2 bytes or 4 bytes, depending on the instruction).
我恭敬地不同意 Dan 的观点,前面有两条指令,这就是管道的工作原理。指令的大小对于thumb 来说是2 字节,对于arm 来说是4 字节,因此前面的两条指令确实会产生4 或8 字节。它不是提前任意 X 个字节,而是提前两个指令提取。
大多数人只会使用标签,而不必知道它是如何工作的。对于异常处理程序,如果您使用拇指模式,您将不得不处理它,并且并非所有版本的 ARM 都清楚这一点,某些版本只是说返回寄存器保存地址+8,当它们意味着地址+两条指令时(这意味着4 或 8,具体取决于地址的 lsbit 指示的模式),随着时间的推移,ARM ARM 有所改进,但较旧的有很多错误。大多数人不需要知道或担心这两个指令。
你的问题的主要答案在于ARM ARM(ARM架构参考手册)中的指令编码。为了拥有固定长度的指令(这意味着所有 ARM 模式指令都是 32 位),立即值必须受到相当的限制。因此,对于像 add 这样的许多指令,您只能有 8 个“有效位”和一些用于移位的位。所以数字 0x1001 不起作用,在二进制中该值为 0b0001000000000001。第一个和最后一个非零位(有效位)需要 13 位存储空间。但示例中的 0x8000 只有 1 个有效位,因此可以轻松地在指令中以多种方式存储和移位。对于具有可变长度指令的指令集(例如 x86),您可以拥有完整的立即数,您可以加载或添加值 0x12345678,因为 0x12345678 并未在主操作码本身中进行编码,它遵循内存中的操作码并且可以具有不同的大小以满足指令集的需要。固定长度和可变长度各有利弊,超出了本次讨论的范围。重点是,虽然 ARM ARM 不仅包括位字段定义,而且每个指令都有伪代码解释如何使用不同的位字段,包括诸如 pc 在当前执行指令之前进行两次提取之类的内容。
PC 相对寻址不是您通常要处理的有限立即数,您将一直处理这些立即数,最好知道哪些指令具有多少立即数长度。拇指模式比手臂模式更难记住哪些操作允许多大的立即数。
I respectfully disagree with Dan, it IS two instructions ahead, that is how the pipeline works. The size of the instruction is either 2 bytes for thumb or 4 bytes for arm, so two instructions ahead does result in either 4 or 8 bytes. It is not an arbitrary X bytes ahead, it is two instruction fetches ahead.
Most folks will just use labels and never have to know how this works. For exception handlers IF you use thumb mode you will have to deal with it and not all versions of the ARM ARM are clear on this, some versions simply say that the return register holds address+8 when they mean address+two instructions (which means 4 or 8 depending on the mode which is indicated by the lsbit of the address), over time the ARM ARM improves but older ones have lots of bugs. Most folks wont ever need to know or worry about this two instruction ahead thing.
The main answer to your questions lies in the ARM ARM (the ARM Architectural Reference Manual), in the instruction encoding. In order to have fixed length instructions, meaning all ARM mode instructions are 32 bits, immediate values have to be quite limited. So for many instructions like the add you can only have say 8 "significant bits" and a few bits for shifting. So the number 0x1001 wouldnt work, in binary this value is 0b0001000000000001. The first and last non-zero bits (significant bits) require 13 bits of storage. but the 0x8000 in your example has only 1 significant bit so that can easily be stored and shifted in a number of ways in the instruction. For instruction sets that have variable length instructions, x86 for example, you can have complete immediates, you can load or add the value 0x12345678 because that 0x12345678 is not encoded in the main opcode itself it follows the opcode in memory and can be of varying sizes to meet the needs of the instruction set. There are pros and cons to fixed and variable length that is beyond this discussion. The point being though the ARM ARM not only includes bit field definitions but each instruction has pseudo code explaining how the different bit fields are used, including things like the pc being two fetches ahead of the currently executing instruction.
The pc relative addressing is not something you normally deal with the limited immediates you will deal with all the time, it is good to know which instructions have what immediate lengths. It gets more difficult with thumb mode than arm mode to remember which operations allow what sized immediates.