u-boot的重新定位如何在这里工作

时间:2013-10-21 15:06:00

标签: assembly arm u-boot

我试图理解部分代码说它在RAM中重新定位U-boot,下面是代码

   #ifndef  CONFIG_SKIP_RELOCATE_UBOOT
   relocate:
     adr r0,_start      /*r0 <--- Current posistion of code8*/
     ldr r1,_TEXT_BASE  /* test if we run from flash or Ram /*
     cmp r0,r1
     beq stack_setup
     ldr r2,_armboot_start   
     ldr r3,_bss_start
     sub r2,r3,r2
     add r2,r0,r2
   copy_loop:
     ldmia r0!,{r3-r10}
     stmia r1!,{r3-r10}
     cmp   r0,r2
     ble   cop_loop
     #endif /*CONFIG_SKIP_RELOCATE_UBOOT/*

现在可以让我知道它是如何发生的吗?我们如何测试我们的u-boot是从RAM还是Flash运行?

我在ARM平台上。

1 个答案:

答案 0 :(得分:2)

这是一个非常简单的例子,

.globl _start
_start:

    adr r0,_start
    ldr r1,_TEXT_BASE

...

_TEXT_BASE: .word _start

组装,链接然后拆解时:

00008000 <_start>:
    8000:   e24f0008    sub r0, pc, #8
    8004:   e59f101c    ldr r1, [pc, #28]   ; 8028 <_TEXT_BASE>
...
00008028 <_TEXT_BASE>:
    8028:   00008000    andeq   r8, r0, r0

还有你的答案。 adr指令基于您的pc在执行时包含0x8008的假设。无论你身在何处,ldr都会引入链接时间值。

例如,如果此代码实际位于地址0x20000000,那么当第一条指令(adr是伪指令,在反汇编中它是8的子)时,adr,现在执行,你得到一个0x20000008-8 = 0x20000000并将其与0x8000进行比较,它们不匹配。如果您运行的代码为0x8000,则0x8008-8 = 0x8000,两者匹配。

只需阅读代码并查找adr指令(或执行我所做的操作,只需尝试并检查编译器/工具的输出,和/或在硬件上运行它,如果没有显示答案)。

编辑:

使用具有各种前缀的gnu工具,但这段代码很简单,几乎不在乎。 arm-none-eabi-或arm-none-linux-gnueabi - 。

假设程序集文件名为foo.s

arm-none-eabi-as foo.s -o foo.o
arm-none-eabi-ld -T memmap foo.o -o foo.elf
arm-none-eabi-objdump -D foo.elf

我称之为memmap的是链接描述文件,在这种情况下你可以使用命令行-Ttext = 0x8000。我喜欢将.elf扩展名用于我的交叉编译二进制文件,并不是每个人都这样做。

00008000 <_start>:
    8000:   e24f0008    sub r0, pc, #8
    8004:   e59f101c    ldr r1, [pc, #28]   ; 8028 <_TEXT_BASE>
...
00008028 <_TEXT_BASE>:
    8028:   00008000    andeq   r8, r0, r0

_start是链接器需要/希望此标签知道代码入口点的gnu工具。所以它不是真正的uboot事情,虽然uboot也可以关心,但它绝对是一个gnu链接器。

0x8000并不是基于ARM的Linux程序的一个不常见的地址作为切入点,你会看到linux作为一个内核从这样的地址开始,但是你可以设置你的系统和二进制文件。 / p>

对于这里发生的事情,没有任何特定的ARM特定或魔法。在任何平台上你都必须提出正确的指示。

程序计数器是前面的两个指令,这是一个32位的arm指令,因此执行指令时程序计数器被假定为该指令的地址加上8.因为这个adr是_start的第一条指令这意味着该指令在地址0x8000处被链接/编译,因此从0x8008到0x8000,指令被编码为r0 = pc-8;另一个信息是链接器在标签_TEXT_BASE之后提供_start的地址。所以另一步是将该值加载到r1中。

这仅适用于您假设代码实际存在于闪存中的假设和事实,以便处理器在地址0x8000处的闪存中看到该指令。 0x8000和0x8000的比较是相等的,那么程序在ram中复制它自己,然后它会跳转到这个副本的开头,这次当它通过代码的副本时,一个寄存器包含一些地址除了0x8000以外,比较失败,如果您从闪存运行原始版本或从ram运行副本,这只是一个检测器。目的是制作副本并运行基于ram的副本。需要采取其他预防措施以确保代码可以在两个地址上运行(与位置无关的代码)。

如果您碰巧知道闪存地址是0x00008000,并且碰巧知道例如ram地址是0x20000000,那么您可以将pc与0x10000进行比较,或者可以将pc的值与0xFF000000进行比较

and r0,pc,#0xFF000000
beq stack_setup

但我认为这是更通用的代码,因此使用了额外的指令和精确的比较。

此类技巧相当常见,检测flash或ram副本等。使用的确切指令以及如何让链接器为您填写内容是特定于目标的。

在这种情况下,闪存可能处于非零地址,0x8000是ram副本。我不知道。