ARM 汇编中的 LDR 和 EQU

LDR and EQU in ARM Assembly

这是我的汇编代码。

a EQU 0x20000000 
b EQU 0x20000004 
c EQU 0x20000008 
LDR R4, =a 
LDR R0, [R4] 
LDR R4, =b 
LDR R1, [R4] 
LDR R4, =c 

我有两个问题。 LDR R0, [R4] 之后是什么 R[4]0x200000000x20000000 处的内存内容?
其次,在最后一行之后,R4 里面有什么? c 0x20000008 处的内存内容?

我在网上搜索了关于 EQU 和 LDR 的信息,我自己的猜测是,对于所有问题,它都是内存的内容,但我很困惑,因为我的朋友告诉我不,我的十六进制数字上面提到的存储在R[4].


更新: 对不起,我想说: LDR R0, [R4] 之后是什么 R00x200000000x20000000 处的内存内容?

里面有什么R40x200000080x20000008 处的内存内容?

第 3 行定义地址 - 这些可以存储在内存中的本地池中,但由于它们非常简单,assembler 可能会使用一对 MOV 指令来 assemble 首次使用时为您提供。

第 4 行将 R4 设置为宏 a -> 0x20000000 的值(可能通过带移位操作的 MOV,但它可能从内存池加载,如果assembler 出于某种原因想要这样做。)

第5行从0x20000000处的内存中读取,并将从内存中读取的值放入R0

第 6 行加载 R40x20000004(可能变成 MOVADD

第 7 行从内存中读取 0x20000004 并将值放入 R1 最后一行用 0x2000008 加载 R4(可能变成 MOVADD

简而言之,LDR R4, = 指令仅将EQU 语句中存储的地址加载到寄存器中。接下来的 R0/R1 加载是从它们指向的地址加载的内容。

现在,如果真正了解汇编的人正在编写此代码,它可能只会变成 2 条指令:0x2MOV 转换为 R4LDM 处理加载和增量的指令。

首先无需搜索整个互联网,直接找到源代码,arm 文档涵盖了指令集。根据说明,这是 arm 或 thumb 代码,但不是 aarch64,因此假设 arm...

接下来的事情是,EQU 是指令而不是指令,这应该是相当明显的,处理器将如何使用变量和汇编语言中的此类指令?他们没有。

接下来是汇编语言由 assembler 定义,您使用的工具不是目标(指令集)。这看起来可能是 ARM 的 assemblers 之一。许多人使用 gnus 工具,两者都可以。我有 gnu,所以汇编语言更改为:

.EQU a, 0x20000000
.EQU b, 0x20000004
.EQU c, 0x20000008
LDR R4, =a
LDR R0, [R4]
LDR R4, =b
LDR R1, [R4]
LDR R4, =c

对于 gnu assembler,我 assemble 然后 disassemble 得到这个:

arm-none-eabi-as so.s -o so.o
arm-none-eabi-objdump -D so.o

so.o:     file format elf32-littlearm


Disassembly of section .text:

00000000 <.text>:
   0:   e3a04202    mov r4, #536870912  ; 0x20000000
   4:   e5940000    ldr r0, [r4]
   8:   e3a04242    mov r4, #536870916  ; 0x20000004
   c:   e5941000    ldr r1, [r4]
  10:   e3a04282    mov r4, #536870920  ; 0x20000008

=a 类型语法是伪代码,因此不是真正的指令内容,因此因 assembler 而异。正如本页其他地方所指出的那样,对该功能的支持取决于工具,一些工具处理它的方式与其他工具不同。 Gnu assembler,似乎功能最丰富....让我离题...寄存器是 32 位的,指令是 32 位的,不可能有 32 位立即数的加载立即数指令一条 32 位(固定长度)指令,没有其他位。不同的(固定长度)指令集以不同的方式解决这个问题,arm 有其有趣的解决方案,并且在 arm、thumb 和 thumb2 扩展之间有所不同,对于 arm 你最多可以有一组 8 个可以旋转的非零位偶数位来生成立即数....gnu assembler 将尽最大努力选择 mov 或 mvn,否则它会创建一个 pc 相对负载并将值放在附近的池中。

在你的例子中,这些常量都工作正常,所以 mov 被替换为 ldr = 伪指令。

   0:   e3a04202    mov r4, #536870912  ; 0x20000000
   4:   e5940000    ldr r0, [r4]
   8:   e3a04242    mov r4, #536870916  ; 0x20000004
   c:   e5941000    ldr r1, [r4]
  10:   e3a04282    mov r4, #536870920  ; 0x20000008

您应该已经阅读了 arm 文档(如果没有处理器供应商提供的文档,您根本无法启动 learning/reading 程序集)。 ldr r0,[r4] 表示获取 r4 中的位并将它们用作地址(先前的指令将 0x20000000 放入 r4 因此对于该指令 0x20000000 成为地址),从该地址读取(加载)并放置结果(来自的位返回)在 r0.

用于演示目的

.EQU a, 0x20000004
.EQU b, 0x20000200
.EQU c, 0x20000008
LDR R4, =a
LDR R0, [R4]
LDR R4, =b
LDR R1, [R4]
LDR R4, =c

给予

00000000 <.text>:
   0:   e3a04242    mov r4, #536870916  ; 0x20000004
   4:   e5940000    ldr r0, [r4]
   8:   e59f4004    ldr r4, [pc, #4]    ; 14 <a-0x1ffffff0>
   c:   e5941000    ldr r1, [r4]
  10:   e3a04282    mov r4, #536870920  ; 0x20000008
  14:   20000200

0x20000004 可以旋转,例如可以旋转 0x00000042,围绕偶数位旋转,这看起来像他们所做的。但是 0x20000200 不能根据 mov 或 mvn 指令的规则创建,因此使用了 pc 相对负载,其值在 0x20000200 附近。由于这不是完整的代码,处理器会像一条指令一样猛击该数据,直到它最终崩溃或幸运地陷入循环。对于真正的代码,该池将放置在某种 and/or 的无条件分支之后,您告诉它该池基于 assembler 特定的汇编语言指令。

从技术上讲,arm assembler 的汇编语言不必支持 ldr r0,[r4] 语法 assembler 作者可以自由地做任何他们想做的事情 ldr r0,(r4) , ldr [r4],r0 loadw r0,0(r4), bob pickle,[onion],只要生成正确的机器码,就是生成arm指令的汇编语言,也就是arm汇编语言。到目前为止,我使用的所有 assemblers 都按顺序支持 ldr r0,[r4] 语法,但我不认为所有支持 =address 的东西并且根据经验并非所有都以相同的方式支持它这里有问题和其他人的帖子。

您可以自己加载,但您还需要了解汇编语言的特定差异:

.EQU a, 0x20000000
.EQU b, 0x20000000
.EQU c, 0xFFFFFF20
LDR R4, avalue
LDR R0, [R4]
LDR R4, =b
LDR R1, [R4]
LDR R4, =c
avalue: .word a

Disassembly of section .text:

00000000 <avalue-0x14>:
   0:   e59f400c    ldr r4, [pc, #12]   ; 14 <avalue>
   4:   e5940000    ldr r0, [r4]
   8:   e3a04202    mov r4, #536870912  ; 0x20000000
   c:   e5941000    ldr r1, [r4]
  10:   e3e040df    mvn r4, #223    ; 0xdf

00000014 <avalue>:
  14:   20000000

这迫使 pc 相对负载,因为这正是我所要求的。在 arm 目标中,一些汇编语言需要冒号来标记标签,而其他语言则不需要,您可以看到 EQU 差异(EQU 类似于 C 中的简单#define #define a 0x2000000,但与 C 不同,您不会将它用于更复杂的宏可能有一个 assembler 语言的特定部分用于宏)。大多数理智的 assemblers 使用冒号来标记注释,gnu assembler for arm 则不使用(请注意 gnu assembler 汇编语言并不都必须有通用的 rules/features 在不同的目标中,假设每个目标都是由不同的人编写和维护的,因此每种语言在评论和其他非指令内容等简单事物方面都是不同的。如果它们重叠,那就这样吧。

这是未链接的,因此此工具的反汇编从 0 开始,一旦链接此反汇编输出将具有不同的地址,但与位置无关(pc 相对负载是 ..... pc 相对)机器代码将保持原样(对于这些示例)。


EQU 是一个类似于 C 中定义的指令,它允许您在这种情况下取​​值 0x20000000 而不是键入变量或名称或字符串 a,预处理器将搜索并替换明显的将字母 a 与 0x20000000 一起使用(当然,它不会替换 add r0,r1,r2 中的 a)。

LDR 是指令族 LoaD Register。提供位于寄存器中的地址、可能的偏移量和目标寄存器。 LDR R0,[R4] 表示 r4 包含地址(在您的情况下为 0x20000000),r0 是保存加载结果的目标寄存器。

ARM 制造内核而不是芯片,没有理由根据您提供的内容假设 0x20000000 是“内存”,它可能是外围设备,处理器不关心,它只是放在总线上的地址,芯片供应商知道该地址的含义,并且 returns 是总线上的一个值。处理器不知道它是内存还是其他东西。

假设这不是一些奇怪的 assembler 破坏事物,第一个 ldr 从地址 0x20000000 加载并将结果放入 r0。