内部重定位未固定

19

我最近开始学习ARM核的汇编语言编程。我的第一个小例程只有.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
但是。
  /opt/arm/bin/arm-as -ggdb -mcpu=arm7tdmi demo.s -o demo.o

出现错误后退出

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

我完全不知道汇编器为什么会抱怨关于重定位的问题,因为我认为这是链接器的任务。我可以想象,在汇编阶段,我需要告诉汇编器我的.data段不在最终的内存位置上,但我找不到任何相关的东西。

虽然我找到了一种正确汇编代码的方法,即替换掉原本的代码

 .section .data

通过

 .org .

那不是一个令人满意的解决方案。特别是考虑到Gas文档强调了这一部分的意义。

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

3个回答

26
似乎唯一的方法是获取变量的地址并从该地址加载值。
ldr r1,=x    ; get address of x
ldr r0,[r1]  ; load from that address

某种程度上,这也是有道理的。毕竟,如果x的地址(链接后)距离PC相对访问太远怎么办?由于编译器(不进行链接)不知道数据段可能与文本段之间的距离有多远,它会拒绝编译那段代码,以防它无法访问。

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

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


感谢您的解释和示例。这个或类似的内容应该放在binutils文档的arm部分中。我会记住链接器执行大规模重定位,而编码器执行小规模重定位。如果我有额外的时间,我会研究一些反汇编的C程序,并希望获得更多的见解... - user1146332

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

汇编器实际上生成了这个:
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

第一个ldr指向程序计数器之后12个字节(被视为当前指令+另外8个字节)。这将指向地址0x10088(如objdump所示),该地址指向andeq指令(在此上下文中不是真正的指令)。实际上,它是一个地址,即0x0002008c,该地址指向.data部分中我们变量x的正确地址。现在,我们已经在r0中获得了变量的地址,我们可以使用ldr对该地址进行读取以获取实际值。值得注意的是,尽管这两个ldr指令的源文件中第二个操作数看起来非常不同,但机器编码是相同的ldr编码;它们都是LDR Immediate(尽管第一个ldr变体也被认为是LDR Literal,但它只是将“Rn”硬编码为“1111”,而这只是pc寄存器而已)。
考虑到所有这些,虽然很不方便,但我们可以想办法仅使用LDR Immediate(Literal)形式一次。我们所要做的就是确保获取与我们的实际数据相对应的正确立即值(偏移量)。比说容易做。
.text
.global _start
_start:
    ldr r0, [pc, #8]
    mov r7, #1
    swi #0
    nop
x:  .word 0xf0f0f0f0

除了只需使用一个LDR指令来实现相同的结果外,此版本源代码的另一个微妙差异是:没有.data部分。这可以通过数据部分完成,但它会将我们的数据放在更高的地址,使我们的偏移量变得更大,以至于我们可能不得不使用额外的指令来正确获取偏移量。另一个副作用是由于它位于.text(r-x)部分,您无法默认使用str进行操作。这是一个非常小的障碍,只需使用ld的-N选项,您的.text部分现在就是rwx。我相信最后一个建议会激怒stackoverflow的神灵,但请不要介意 ;)


2
这不适用于问题中的代码,但通常情况下,这种错误意味着您忘记了定义一个使用ldr指令加载的常量。

在一个本应编译正常的代码中,当该项目在不同的工具链上编译时,往往会发生这种情况,因为汇编文件的扩展名不同,如.include指令可能会包含错误的文件(例如file.asm.s而不是file.asm),导致定义丢失。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接