内部搬迁没有修好

use*_*332 13 assembly arm gnu-assembler

我最近开始使用arm core进行汇编编程.我的第一个小演示,只有.text部分,没有任何问题.

作为一个逻辑扩展,我想将汇编代码构造成通常的部分:.text,.data,.bss.

所以我写了以下简单的程序:

 .globl _start

 .section .text

 _start:
     b   main
     b   .
     b   .
     b   .
     b   .
     b   .
     b   .
     b   .  


 main:
    ldr r0, x
    nop

 .section .data

 x:  .word  0xf0f0f0f0

 .end
Run Code Online (Sandbox Code Playgroud)

  /opt/arm/bin/arm-as -ggdb -mcpu=arm7tdmi demo.s -o demo.o
Run Code Online (Sandbox Code Playgroud)

退出并出错

 prog.s: Assembler messages:
 prog.s:17: Error: internal_relocation (type: OFFSET_IMM) not fixed up
 make: *** [prog.o] Error 1
Run Code Online (Sandbox Code Playgroud)

我不知道为什么汇编程序抱怨重定位,因为我认为这是链接器的任务.我可以想象我必须告诉汇编程序我的.data部分不是在汇编阶段的最终内存位置,但我找不到任何相关的东西.

虽然我找到了一种方法来通过替换来正确组装代码

 .section .data
Run Code Online (Sandbox Code Playgroud)

通过

 .org .
Run Code Online (Sandbox Code Playgroud)

这不是一个令人满意的解决方案.特别是考虑到气体文件突出了这一部分的意义.

也许你的专家可以帮助我获得一些智慧

tan*_*grs 18

看来你能做到的唯一方法就是抓住变量的地址并从该地址加载一个值.

ldr r1,=x    ; get address of x
ldr r0,[r1]  ; load from that address
Run Code Online (Sandbox Code Playgroud)

在某种程度上,这也是有道理的.毕竟,如果x的地址(链接后)对于PC相对访问来说太远了怎么办?由于编译器(不进行链接)不知道数据部分离文本部分有多远,因此它将拒绝编译该代码,以防它无法访问.

通过使用这种访​​问变量的间接方式,可以保证变量可以访问(或者至少编译器可以确定变量是否可访问).

代码改编自http://www.zap.org.au/elec2041-cdrom/examples/intro/pseudo.s


Xlo*_*icX 5

我不希望这是例外的答案,但它确实提供了更多的洞察力,并且还为仅使用一个 ldr 指令提供了一种不方便的解决方案。

使用这种两阶段 ldr 方法时,汇编程序实际上在您的代码之后添加了另外 4 个字节的数据!甚至在 .text 部分,这 4 个字节是 .data 变量的实际地址。第一个 ldr 指令实际上指向这个地址,然后你使用下一个 ldr 来使用真实地址。正如 tangrs 所讨论的,这个双指针可能是一种确保您的变量/常量可访问的方法,尤其是在 .data 部分更远的情况下(我上次运行时距离为 64k)。

查看一些正确方法的示例代码:

.text
.global _start
_start:
    ldr r0, =x
    ldr r0, [r0]
    mov r7, #1
    swi #0
    nop
.data
    x: .word 0xf0f0f0f0
Run Code Online (Sandbox Code Playgroud)

汇编器实际上产生了这个:

00010074 <_start>:
   10074:   e59f000c    ldr r0, [pc, #12]   ; 10088 <_start+0x14>
   10078:   e5900000    ldr r0, [r0]
   1007c:   e3a07001    mov r7, #1
   10080:   ef000000    svc 0x00000000
   10084:   e1a00000    nop         ; (mov r0, r0)
   10088:   0002008c    andeq   r0, r2, ip, lsl #1

Disassembly of section .data:

0002008c <x>:
   2008c:   f0f0f0f0            ; <UNDEFINED> instruction: 0xf0f0f0f0
Run Code Online (Sandbox Code Playgroud)

第一个 ldr 指向程序计数器之后的 12 个字节(考虑当前指令 + 8 个以上)。这指向地址 0x10088(如 objdump 所述),该地址指向 andeq 指令(在此上下文中不是真正的指令)。它实际上是一个地址,0x0002008c,它指向我们在变量 x 的 .data 部分中的正确地址。现在我们在 r0 中有变量的地址,我们可以在该地址上使用 ldr 来获取实际值。值得注意的是,尽管这两个 ldr 指令的源文件中的第二个操作数看起来非常不同,但机器编码是相同的 ldr 编码;它们都是 LDR 立即数(尽管第一个 ldr 变体也被认为是 LDR 文字,但它只是 LDR 立即数,其中“Rn”硬编码为“1111”,无论如何这只是 pc 寄存器)。

考虑到所有这些,虽然不方便,但我们可以想办法只使用一次 LDR Immediate(Literal) 形式。我们所要做的就是确保获得与我们的真实数据对应的正确即时值(偏移量)。做起来比说的容易:

.text
.global _start
_start:
    ldr r0, [pc, #8]
    mov r7, #1
    swi #0
    nop
x:  .word 0xf0f0f0f0
Run Code Online (Sandbox Code Playgroud)

除了只需要使用一条 LDR 指令来实现相同的结果之外,这个版本的源还有另一个细微的区别:没有 .data 部分。这可以通过数据段来完成,但它会将我们的数据放在一个更高的地址中,使我们的偏移量变得更大,以至于我们可能不得不使用额外的指令来获得正确的偏移量。另一个旁注是由于它位于 .text (rx) 部分,默认情况下不能在其上使用 str 。这是一个非常小的障碍,只需对 ld 使用 -N 选项,您的 .text 部分现在是 rwx。我敢肯定,最后一个建议会激怒 stackoverflow 之神,来找我吧;)