【问题标题】:internal relocation not fixed up内部搬迁未解决
【发布时间】:2012-04-23 01:40:46
【问题描述】:

我最近开始为 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 .

这不是一个令人满意的解决方案。尤其是考虑到气体文档突出了本节的意义。

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

【问题讨论】:

    标签: assembly arm gnu-assembler


    【解决方案1】:

    似乎唯一的方法是获取变量的地址并从该地址加载一个值。

    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 程序并希望获得更多的见解......
    【解决方案2】:

    我不打算将其作为例外答案,但它确实提供了更多洞察力,并且还为仅使用一条 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,它指向我们在变量 x 的 .data 部分中的正确地址。现在我们在 r0 中有了变量的地址,我们可以在该地址上使用 ldr 来获取实际值。值得注意的是,尽管这两条 ldr 指令的源文件中的第二个操作数看起来非常不同,但机器编码是针对相同的 ldr 编码的;它们都是 LDR Immediate(虽然第一个 ldr 变体也被认为是 LDR Literal,但它只是 LDR Immediate,将“Rn”硬编码为“1111”,无论如何这只是 pc 寄存器)。

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

    .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之神,来找我;)

    【讨论】:

      【解决方案3】:

      这不适用于问题中的代码,但一般来说,此错误通常意味着您忘记定义使用ldr 指令加载的常量。

      在应该可以正常编译的代码中,当项目在具有不同的汇编文件扩展名的不同工具链上编译时,通常会发生这种情况,因此.include 指令可能包含错误的文件(例如file.asm.s file.asm),导致缺少定义。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-05-07
        • 2013-03-18
        • 2011-10-28
        • 1970-01-01
        • 1970-01-01
        • 2017-07-09
        相关资源
        最近更新 更多