u-boot之ldr pc _start_armboot解析以及relocation(重定位)问题

本文详细解析了U-Boot启动过程中的重定位问题,解释了如何通过设置TEXT_BASE来控制代码段的起始地址,并深入探讨了VMA与LMA的区别及作用,帮助读者理解U-Boot如何从Flash迁移到RAM执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在cpu/arm920t/start.S中,将text relocate 到Ram后,其代码段的最后1行有条语句:
ldr pc _start_armboot
_start_armboot: .word start_armboot
    start_armboot是一个函数指针,这个symbol对应了符号表里的函数地址,这个函数是一个C语言的函数,他就是u-boot的stage2的入口点,这个stage2应该是在RAM里面执行的。问题就来了,既然我们只是手动将text relocate到了RAM里面,此时FLASH和RAM里面都有start_armboot的代码,为什么程序就要跳转到RAM里,而不是依然在FLASH里?
    我最初感觉除非是ld在连接的时候,修改了符号表里的内容,指定start_armboot符号的地址是RAM里的地址,这样只要我们执行时取其地址,取到的肯定是RAM中的地址。
    经查阅资料,有如下一段解释:
转自: http://www.mail-archive.com/u-boot-users@lists.sourceforge.net/msg04018.html
----- Original Message ----
> > > > From: Vishal Oliyil Kunnil <[EMAIL PROTECTED]>
> > > > To: Tiju <[EMAIL PROTECTED]>
> > > >
> > > > Sent: Monday, 31 March, 2008 2:58:12 PM
> > > > Subject: Re: [U-Boot-Users] s3c2440 -- serial_init
> > > >
> > > > TEXT_BASE is the address for which u-boot is linked for.If you take an
> > > > objdump of u-boot
> > > > elf, you will see that it links for address beginning with that
> > > > specified by TEXT_BASE.
> > > > Meaning, you link for the address thus specified.
> > > > Typically the binary will be run from the reset vector of the
> > > > processor, which is not necessarily
> > > > TEXT_BASE : say 0x0 flash address. U-boot starts executing from the
> > > > reset vector,
> > > > relocates to RAM and since it is linked for TEXT_BASE, the ldr pc,
> > > > _start_armboot
> > > > will branch to the start_armboot which is in RAM.
> > > > -------snip - start.S --------------
> > > > ldr pc, _start_armboot
> > > > _start_armboot: .word start_armboot
> > > > -------snip - start.S --------------
> > > > Regards,
> > > > Vishal    上面的意思是说,我们在u-boot的C代码的编译环境里制定了TEXT_BASE的值,然后所有生成的C函数的可执行代码都是以TEXT_BASE作为连接基地址,而不是我们的0x00000000(u-boot在FLASH开始执行的起始地址),也就是说这是我们u-boot的代码中在FLASH里,除了最开始的一部分汇编码是在FLASH里执行的,其他由C语言实现的部分都只能在RAM里执行,因为我们给他们定好的基地址就是RAM的地址。
    这其实是个重定位问题,见另一篇文章:
http://blog.chinaunix.net/u2/72383/showart.php?id=1072523
关于u-boot的relocation(重定位)问题
阅读一下ld相关的资料就可以知道,通过培植lds连接脚本或者在连接时提供相应的参数,可以控制连接生成的代码的起始地址,这里的起始地址有2个:
    1. VMA
    2. LMA
    其中,VMA可以认为是程序运行时,认为自己的起始地址,就是他的所有的symbol的地址的基址,即如果访问一个symbol,那么这个symbol的地址应该是VMA+symbol在代码内的偏移地址。
    而LMA可以认为是将程序装载的地址,一般情况下,2者是相同的,但是在u-boot这种需要relocatin的情况下,2这就是不同的,貌似在u-boot中,LMA压根没什么意义。
    为什么这么说,因为在连接的时候,LDFLAGS中有-Ttext $(TEXT_BASE),其含义是将text section定位基址设置成TEXT_BASE,这样生成的结果里的诸如函数地址,等symbol对应的地址都是以TEXT_BASE作为基地址的。这个据说可以通过objdump查看生成的u-boot的各条汇编代码的地址看出来。但是为什么我们还可以将u-boot烧至到flash去执行呢?这是因为u-boot的stage1的汇编代码里虽然用到了一些label,但是他们在真正使用的时候其实是采用的偏移地址跳转,因此无论烧到哪里都能执行。但是还有个问题,那么stage1跳转到stage2时有条指令:
    ldr pc _start_armboot
    _start_armboot: .word start_armboot
    这个跳转为什么可以呢,首先,start_armboot是一个函数指针,他的地址是定义在由相应的c文件生成的目标文件中的,在连接的时候,他的对应的地址受到我们指定的TEXT_BASE的影响,它的地址其实是在RAM中,因此,如果我们执行这条语句,CPU会到RAM里去寻找start_armboot的代码,因此,可以这样说,如果我们不把代码拷贝到RAM里的话,当执行到这里的时候是会出错的,CPU不会去执行位于flash中的start_armboot函数的代码,start_armboot并不指向哪里。

    如果细心的话,还会看到在 /common/hush.c 文件里,有段代码:

    static struct reserved_combo reserved_list[] = {
    { "if",    RES_IF,    FLAG_THEN | FLAG_START },
    { "then",  RES_THEN,  FLAG_ELIF | FLAG_ELSE | FLAG_FI },
    { "elif",  RES_ELIF,  FLAG_THEN },
    { "else",  RES_ELSE,  FLAG_FI   },
    { "fi",    RES_FI,    FLAG_END  },
    { "for",   RES_FOR,   FLAG_IN   | FLAG_START },
    { "while", RES_WHILE, FLAG_DO   | FLAG_START },
    { "until", RES_UNTIL, FLAG_DO   | FLAG_START },
    { "in",    RES_IN,    FLAG_DO   },
    { "do",    RES_DO,    FLAG_DONE },
    { "done",  RES_DONE,  FLAG_END  }
};

static void u_boot_hush_reloc(void)
{
    unsigned long addr;
    struct reserved_combo *r;

    for (r=reserved_list; r<reserved_list+NRES; r++) {
        addr = (ulong) (r->literal) + gd->reloc_off;
        r->literal = (char *)addr;
    }
}

    可见,reserved_list中的数据应该是属于data段的,我们已经把代码relocation到RAM里面了,况且连接的时候指定了VMA,为什么这里还要修改r->literal呢?首先,我们的确已经把DATA段relocation到了RAM里面,查看board/smdk2410/u-boot.lds可以知道,data段位于text段和bss段之间,而拷贝的时候是将text段到bss段之间(包括text段)的内容全都拷贝到了RAM,可有一点,在连接的时候,我们用-Ttext $(TEXT_BASE)只是指定了连接得到的代码,指令段的起始地址设置为RAM里地址TEXT_BASE,而data段我们并没有管她,造成的结果就是数据段的地址还是flash里的地址。所以现在我们要修改我们这些自身已经位于RAM中的指针变量,让它们的值指向RAM里而不再是flash里。
    gd->reloc_off, gd是一个乘放全局变量的一个好地方。gd->reloc_off应该是在relocation时记录的relocation偏移。

    可见,代码,symbol的地址我们不用操心,因为ld自动给我们改了,而数据段没有改,我们就的自己改。可是为什么不连接期间也手动指定数据段的起始地址?因为我们肯定知道代码段的地址是TEXT_BASE,但是我们压根就不知道代码段的长度,以及一些其他的段的长度,因此就不能确定data段在代码段后面的位置,因此也就不能在连接的时候给data段指定位置。
<think>好的,我现在需要解决用户提出的关于AArch64架构中R_AARCH64_ADR_PREL_PG_HI21重定位错误以及.bss段相关的截断问题。用户提到了引用中的几个情况,我需要结合这些信息来找到解决方法。 首先,用户提供的引用[1]提到,旧的uboot(2019.04之前)只能处理21位的重定位,导致代码段超过1MB就会报错。解决方法是通过4K对齐,利用高位计算重定位,实现32位寻址。这涉及到修改start.S和relocate_64.S文件。这说明可能需要调整代码段的对齐方式或者重定位的计算方法。 引用[2]指出问题明确是由ADRP指令引起的,而ADRP指令在AArch64中用于生成地址的高位部分,结合ADD等指令形成完整地址。如果地址偏移超过ADRP能表示的范围(±4GB),就会导致重定位错误。这可能意味着代码或数据的位置超出了ADRP指令的寻址范围,需要调整内存布局或使用不同的指令。 引用[3]提到AArch64虽然有64位的相对PC寻址指令,但缺少对应的重定位方式,导致链接器无法正确处理某些情况。这可能涉及到工具链的支持问题,需要检查编译器或链接器的版本,或者使用特定的编译选项。 接下来,我需要将这些信息整合起来,形成解决方案。可能的步骤包括: 1. **检查工具链版本**:确保使用的编译器、链接器版本较新,支持AArch64的更大地址范围。 2. **调整内存布局**:确保.bss段或其他相关段的位置在ADRP指令的寻址范围内,可能需要修改链接脚本,将关键段放置在4GB范围内。 3. **代码段对齐**:按照引用[1]的建议,将代码段4K对齐,从而允许使用高位地址计算,扩展寻址范围。 4. **修改重定位代码**:在start.S和relocate_64.S中调整重定位的逻辑,使用高位计算方式,避免截断。 5. **替换ADRP指令**:如果可能,使用LDR指令结合文字池(literal pool)来加载大地址,避免依赖ADRP的寻址限制。 6. **链接脚本优化**:在链接脚本中明确分配.bss段的位置,避免其超出ADRP的寻址范围,或者调整段的对齐方式。 需要验证这些步骤的有效性。例如,在修改链接脚本时,可能需要将.bss段放置在靠近代码段的位置,或者确保其地址在±4GB范围内。同时,代码中的ADRP指令如果引用了超出范围的符号,可能需要替换为其他寻址方式,或者调整符号的位置。 另外,可能需要查阅更多关于AArch64重定位类型的文档,了解R_AARCH64_ADR_PREL_PG_HI21的具体限制和处理方法。比如,该重定位类型是否允许更大的偏移,或者是否有其他重定位类型可以替代。 最后,可能需要实际编译和链接代码,观察错误是否解决,并根据调试信息进一步调整。例如,使用编译选项-ffunction-sections和-fdata-sections来优化段的分布,或者使用链接器选项--fix-cortex-a53-843419来应对特定的处理器问题,虽然这可能不直接相关,但有时能提供线索。</think>### 解决R_AARCH64_ADR_PREL_PG_HI21重定位截断问题的分步方法 #### 问题本质 此错误由AArch64架构的`ADRP`指令寻址范围限制引发,当`.bss`段或其他关键段与代码段距离超过`ADRP`指令的4GB寻址范围时,会导致重定位失败[^2][^3]。 --- #### 解决方案 1. **调整链接脚本(关键步骤)** - **目标**:确保`.bss`段与代码段距离在4GB范围内。 - **修改方法**:在链接脚本中强制指定`.bss`段紧邻代码段: ```ld .bss : { __bss_start = .; *(.bss*) __bss_end = .; } >RAM AT>RAM ``` 添加`>RAM AT>RAM`约束内存区域连续性[^1]。 2. **代码段4K对齐优化** - **原理**:4K对齐后,低位地址可忽略,利用高位地址扩展寻址范围。 - **修改文件**:`arch/arm/cpu/armv8/start.S` ```asm .align 12 /* 4K对齐 */ _start: ... ``` 3. **重定位逻辑修正** - **修改文件**:`arch/arm/lib/relocate_64.S` ```asm ldr x0, =__rel_dyn_start ldr x1, =__rel_dyn_end sub x2, x1, x0 lsr x2, x2, #3 /* 右移3位,实现高位寻址计算 */ ``` 4. **替代ADRP指令(可选)** - **使用文字池加载地址**: ```asm ldr x0, =symbol_name // 替代ADRP+ADD组合 ``` --- #### 验证方法 1. 使用`aarch64-linux-gnu-objdump -d u-boot`检查`.bss`段与代码段偏移 2. 观察编译日志是否出现`relocation truncated`警告 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值