内部搬迁没有修好

时间:2012-04-10 18:22:36

标签: assembly arm gas

我最近开始使用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

但是

  /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 .

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

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

3 个答案:

答案 0 :(得分:18)

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

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

答案 1 :(得分:4)

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

使用此两阶段的ldr方法时,汇编器实际上在代码之后又添加了4个字节的数据!甚至在.text部分中,这4个字节也是.data变量的实际地址。然后,第一个ldr指令实际上指向该地址,然后使用下一个ldr来使用实际地址。正如缠结者所讨论的那样,此双指针可能是确保变量/常量可访问的一种方法,尤其是.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 Instant(尽管第一个ldr变体也被认为是LDR Literal,但是它只是LDR Instant,硬编码为“ 1111”的“ Rn”,无论如何也只是pc寄存器)。

考虑到所有这些,尽管很不方便,但是我们可以找到一种方法,只使用一次LDR Instant(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 :(得分:1)

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

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