uboot代码重定位:解决调用与地址无关问题的测试

文章详细解释了U-Boot启动过程中重定位的问题,包括如何处理函数调用和全局变量地址在重定位后的变化,以及通过位置无关码和ld链接选项pie确保正确执行。

 一.  uboot启动流程

_main 函数中会调用 relocate_code 函数。

relocate_code 函数分两个部分:

1.  拷贝 uboot 代码部分

2.  有关 " 重定位后有关函数调用或全局变量地址的问题"的解决方法。

本文继上一篇文章的学习,地址如下:

uboot启动流程-uboot代码重定位说明一_凌肖战的博客-优快云博客

这里学习 uboot 重定位的第二部分内容:重定位后有关函数调用或全局变量地址的问题。

二.  uboot代码重定位

1.    重定位后函数调用或全局变量使用问题

具体问题描述:

重定位 就是 uboot 将自身拷贝到 DRAM 的另一个地放去继续运行(DRAM 的高地址处)。

我们知道,一个可执行的 bin 文件,其链接地址和运行地址要相等,也就是链接到哪个地址, 在运行之前,就要拷贝到哪个地址去。现在我们重定位以后,运行地址就和链接地址不同了,这 样寻址的时候不会出问题吗?

当简单粗暴的将uboot从0X87800000 拷贝到其他地方以后,关于函数调用、全局变量引用就会出问题。Uboot对于这个的处理方法就是采用位置无关码,这个就需要借助于 .rel.dyn段。

2. 代码测试

为了分析这个问题,在ubuntu下通过 vscode 打开uboot代码工程。所使用的 uboot源码包目录如下:
阿尔法Linux开发板(A盘-基础资料-2022 / 阿尔法Linux开发板(A盘)-基础资料 / 例程源码 / 开发板教程对应的uboot和linux源码/uboot-imx-rel_imx_4.1.15_2.1.0_ga_alientek.tar.bz2
使用 "ctrl+p"快捷键 打开搜索栏,搜索 "mx6ull_alientek_nand.c" 文件, 在 "mx6ull_alientek_nand.c" 文件中输入如下内容,最后在  board_init 函数里面调用 rel_test 函数:
int board_early_init_f(void)
{
	setup_iomux_uart();

	return 0;
}

static int rel_a  = 0;

void rel_test()
{
    rel_a = 100;
    printf("rel_test\r\n");
}

int board_init(void)
{
    rel_test();
  ......
}


 

编译uboot 源码

board_init 函数会调用 rel_test,rel_test 会调用全局变量 rel_a,使用如下命令编译对 uboot 进行编译:
 ./imx6ull_alientek_nand.sh 

编译完成以后,使用 arm-linux-gnueabihf-objdump 将 u-boot 进行反汇编,得到 u-boot.dis 这个汇编文件,命令如下:

arm-linux-gnueabihf-objdump -D -m arm u-boot > u-boot.dis

在 u-boot.dis 文件中找到 rel_a、rel_rest 和 board_init,相关内容如下所示:
87804088 <rel_test>:
87804088:	e59f300c 	ldr	r3, [pc, #12]	; 8780409c <rel_test+0x14>
8780408c:	e3a02064 	mov	r2, #100	; 0x64
87804090:	e59f0008 	ldr	r0, [pc, #8]	; 878040a0 <rel_test+0x18>
87804094:	e5832000 	str	r2, [r3]
87804098:	ea00faff 	b	87842c9c <printf>
8780409c:	8786b180 	strhi	fp, [r6, r0, lsl #3]
878040a0:	8784b1c3 	strhi	fp, [r4, r3, asr #3]

878040a4 <board_init>:
878040a4:	e92d4010 	push	{r4, lr}
878040a8:	ebfffff6 	bl	87804088 <rel_test>
......

8786b180 <rel_a>:
8786b180:	00000000 	andeq	r0, r0, r0
第 12 行,是 borad_init 调用 rel_test 函数,用到了 bl 指令,而 bl 指令时位置无关指令,bl 指令是相对寻址的(pc+offset),因此 uboot 中函数调用是与绝对位置无关的。
再来看一下 rel_test 函数对于全局变量 rel_a 的调用,第 2 行设置 r3 的值为 pc+12 地址处的 值,因为ARM流水线的原因,pc寄存器的值为:当前地址+8,因此pc=0X87804088+8=0X87804090,
r3=0X8780418C+12=0X8780409C。
第 7 行就是 0X8780409C 这个地址,0X8780409C 处的值为 0X8786B180。根据第 17 行可知,0X8786B180 正是变量 rel_a 的地址,最终 r3 = 0X8786B180 。
第 3 行,r2=100。
第 5 行,将 r2 内的值写到 r3 地址处,也就是设置地址 0X8786B180 的值为 100,这不就
是rel_test函数的第一行代码:rel_a = 100。

总结一下 rel_a=100 的汇编执行过程:
①  在函数 rel_test 末尾处有一个地址为0X8780409C 的内存空间,此内存空间保存着变量 rel_a 的地址。
②  函数 rel_test 要想访问变量 rel_a,首先访问末尾的 0X8780409C来获取变量 rel_a 的地 址,而访问 0X8780409C 是通过偏移来访问的,很明显是个位置无关的操作。
③  通过 0X8780409C 获取到变量 rel_a 的地址,对变量 rel_a 进行操作。
④  可以看出,函数 rel_test 对变量 rel_a 的访问没有直接进行,而是使用了一个第三方偏
移地址 0X87804198,专业术语叫做 Label。这个第三方偏移地址就是实现重定位后运行不会出
错的重要原因!

由之前的文章可知,uboot 重 定 位 后 偏 移 为 0X18747000(0X8FF38000 - 0X87800000的结果), 那 么 重 定 位 后 函 数 rel_test 的 首 地 址 就 是:

0X87804088+0X18747000 = 0X9FF4B088

保 存 变 量 rel_a 地 址 的 Label 为:

0X9FF4B088+8+12 = 0X9FF4B09C ( 即 0X8780409C+0X18747000)
变 量 rel_a 的 地 址 就 为 :
0X8786B180+0X18747000 = 0X9FFB2180。
重定位后, rel_test 函数要想正常访问  rel_a变量 ,就得 设置 0X8780409C (重定位后的 Label)地址出的值为 0X8786B180 (重定位后的变量 rel_a 地址)。 这样就解决了重定位后链接地址和运行地址不一致的问题。

可以看出,uboot 对于重定位后链接地址和运行地址不一致的解决方法就是采用位置无关码,在使用 ld 进行链接的时候使用选项 “-pie” 生成位置无关的可执行文件。在文件 arch/arm/config.mk 。

下一篇了解 uboot 重定位后函数调用地址问题进行的处理。

<think>好的,用户现在想了解U-Boot中进行代码重定位的原因,也就是代码重定位的作用和原因。我需要根据用户提供的引用资料和我之前的知识来回答这个问题。首先,我需要回忆一下U-Boot的启动流程,以及代码重定位的具体过程。 根据用户提供的引用[2]和[3],U-Boot在启动时会执行重定位操作,将自身代码从初始位置(比如Flash)复制到RAM中运行。引用[3]中的代码显示,在start.S文件中,通过比较当前代码地址和链接地址,判断是否需要重定位。如果运行地址和链接地址不同,就需要执行CopyCode2Ram函数,将代码复制到RAM中。 那用户的问题核心是:为什么要进行代码重定位?可能的原因包括: 1. **运行速度优化**:Flash的访问速度通常比RAM慢,将代码复制到RAM中可以加快执行速度。 2. **地址空间限制**:有些处理器在启动时可能运行在受限的地址空间,重定位到RAM可以解除这种限制。 3. **动态内存管理**:重定位后,U-Boot可以使用动态内存分配,为后续操作(如加载内核)做准备。 4. **多阶段启动需求**:U-Boot分为SPL和主U-Boot,SPL负责初始化硬件并将主U-Boot加载到RAM中运行。 接下来需要结合用户提供的引用资料来验证这些原因是否正确。引用[2]提到,U-Boot的代码段默认从reset函数开始,执行到_main后调用relocate_code进行重定位。这说明重定位是启动流程中的一个必要步骤,确保代码在正确的地址运行。 引用[3]中的代码展示了如何通过比较运行地址和链接地址来决定是否需要重定位。如果两者不同,就需要将代码从Flash复制到RAM中的链接地址。这说明重定位的原因之一是确保代码在编译时指定的地址运行,避免地址错误导致的运行问题。 此外,用户之前的回答中提到init_sequence_f负责第一阶段初始化,包括内存布局计算和重定位准备,这为代码重定位提供了必要的环境,比如内存初始化和地址计算。 现在需要将这些点整理成一个结构化的回答,解释重定位的原因和作用,可能分为几个部分:运行环境适配、性能优化、动态内存管理、多阶段启动支持等。同时,需要引用用户提供的资料来支持每个论点,例如引用[2]和[3]中关于重定位流程的具体代码和步骤。 需要确保回答清晰,技术细节准确,并且使用用户提供的信息来增强可信度。同时,用户可能对重定位的具体机制感兴趣,比如如何计算目标地址、复制代码的过程等,但当前问题主要关注原因和作用,因此需要集中在这些方面,避免过多深入实现细节。 最后,生成相关问题,帮助用户进一步理解相关内容,比如重定位的具体步骤、如何调试重定位问题地址无关代码的关系等。这些问题应该基于用户之前的提问和当前内容,确保连贯性和相关性。</think>### U-Boot 代码重定位的作用原因详解 --- #### 一、代码重定位的核心目标 U-Boot 的代码重定位(Relocation)是启动过程中将自身代码从**初始运行地址**(如 Flash、ROM 等非易失性存储)**复制到目标地址**(通常是 RAM 的高端内存区域)的关键操作。其根本目的是解决 **「运行地址链接地址不一致」** 的问题,确保后续流程(如内核加载、动态内存管理)能够正确执行[^2][^3]。 --- #### 二、重定位的具体原因 ##### 1. **硬件启动地址限制** - **问题背景**: 许多 ARM 处理器在复位时**强制从固定地址(如 0x0 或 0xFFFF0000)加载代码**,而这一地址可能位于低速 Flash 或 BootROM 中,无法满足 U-Boot 运行需求。 - **解决方法**: 通过重定位代码迁移到 RAM 的高性能区域,例如 SDRAM 的 `_TEXT_BASE`(如 0x87800000)[^3]。 ##### 2. **地址无关代码的局限性** - **链接地址依赖**: U-Boot 编译时通过链接脚本(如 `u-boot.lds`)指定了代码的**逻辑地址**(`_TEXT_BASE`)。若实际运行地址(如 Flash 中的物理地址逻辑地址不同,**绝对地址跳转和全局变量访问将失效**。 - **重定位作用**: 将代码复制到链接地址一致的 RAM 区域,消除地址差异[^3]。 ##### 3. **动态内存管理的需求** - **内存布局重构**: 重定位后,U-Boot 可重新计算内存布局(如堆、栈、设备树的位置),并初始化动态内存分配器(如 `gd->malloc_base`),为后续操作(加载内核、传递设备树)提供灵活的内存管理能力[^2]。 ##### 4. **多阶段启动的衔接** - **SPL 主 U-Boot 的协作**: 在包含 SPL(Secondary Program Loader)的系统中,SPL 需将主 U-Boot 镜像从存储介质加载到 RAM 的指定地址重定位是这一过程的实现手段[^2]。 --- #### 三、重定位的技术实现(以 ARMv7 为例) ##### 1. **地址判断重定位触发** ```c // arch/arm/cpu/armv7/start.S adr r0, _start // 获取当前运行地址(物理地址) ldr r1, _TEXT_BASE // 获取链接地址(逻辑地址) cmp r0, r1 // 比较两者是否相同 bne relocate_code // 若不同则触发重定位[^3] ``` ##### 2. **代码拷贝地址修正** - **数据复制**: 通过 `CopyCode2Ram` 函数将代码段(`.text`)、数据段(`.data`)从源地址(如 Flash)复制到目标地址(RAM)。 - **符号地址修正**: 重定位后需调整全局变量和函数指针的地址偏移,确保后续代码正确访问符号(通过 `relocate_code` 中的地址修正逻辑实现)[^3]。 ##### 3. **重定位后的内存布局** ```plaintext High Address +------------------+ | Stack | +------------------+ | Heap | +------------------+ | Relocated U-Boot| +------------------+ <-- _TEXT_BASE (0x87800000) | ... | Low Address ``` --- #### 四、重定位失败的影响调试 ##### 1. **典型故障** - **症状**:U-Boot 启动后卡死或输出 `relocation failed` 错误。 - **原因**: - 目标地址未预留足够内存(如 `CONFIG_SYS_TEXT_BASE` 配置错误)。 - 代码复制过程中数据损坏(Flash 时序配置不当)。 - 地址修正逻辑错误(链接脚本硬件地址不匹配)[^3]。 ##### 2. **调试方法** - **查看符号表**: 通过 `objdump -t u-boot` 确认 `_start` 和 `_TEXT_BASE` 的地址。 - **打印调试信息**: 在重定位代码中插入 `printf("Current addr: 0x%lx\n", r0)`,观察实际运行地址。 - **内存对比**: 使用 `md` 命令比较 Flash 和 RAM 中的代码内容,验证复制是否完整。 --- ### 相关问题 1. **U-Boot 重定位时如何处理全局数据(`gd` 结构体)?** 重定位前 `gd` 使用静态分配,重定位后通过 `setup_reloc` 更新其地址偏移[^2]。 2. **如何确定 `CONFIG_SYS_TEXT_BASE` 的值是否合理?** 需参考硬件内存映射,避开内核加载区、设备树区域,并确保 RAM 容量足够[^3]。 3. **是否所有架构的 U-Boot 都需要代码重定位?** 依赖启动介质特性:若 CPU 可直接在 Flash/XIP 中运行完整 U-Boot,可禁用重定位(通过 `CONFIG_SKIP_RELOCATE_UBOOT`)[^3]。 4. **重定位后的 U-Boot 如何跳转到新地址继续执行?** 通过 `ldr pc, =_main` 等指令强制跳转到修正后的地址,此后所有代码基于新地址运行[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值