【linux 系统移植③】makefile、relocate_code、uboot.lds

本文详细介绍了U-Boot启动过程中如何实现代码的重定位,包括relocate_code函数的具体实现,以及链接脚本如何配置来支持重定位。通过分析源码和链接脚本,解释了为什么需要进行重定位及其实现细节。

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

makefile、relocate_code、uboot.lds

.makefile

.relocate_code(uboot 自举)

https://blog.csdn.net/skyflying2012/article/details/37660265?spm=1001.2014.3001.5501

link 链接地址、load 加载地址、运行地址。

将 uboot 重新定位到 SDRAM上部一个位置,原因可能是为 kernel 腾出低端空间。

// _main
...
#if ! defined(CONFIG_SPL_BUILD)
    // gd 更新
	ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
	sub	r9, r9, #GD_SIZE		/* new GD is below bd */
	// 为 relocate 做准备
	adr	lr, here
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	add	lr, lr, r0
	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
	b	relocate_code
here:
...

// relocate.S
// relocate_code —— 将uboot code拷贝到relocaddr

#include <linux/linkage.h>

ENTRY(relocate_code)
	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start */
	subs	r4, r0, r1		/* r4 <- relocation offset */
	beq	relocate_done		/* skip relocation */
	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end */

copy_loop:
	ldmia	r1!, {r10-r11}		/* copy from source address [r1]    */
	stmia	r0!, {r10-r11}		/* copy to   target address [r0]    */
	cmp	r1, r2			/* until source end address [r2]    */
	blo	copy_loop

	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end	/* r3 <- SRC &__rel_dyn_end */
fixloop:
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
	and	r1, r1, #0xff
	cmp	r1, #23			/* relative fixup? */
	bne	fixnext

	/* relative fix: increase location by offset */
	add	r0, r0, r4
	ldr	r1, [r0]
	add	r1, r1, r4
	str	r1, [r0]
fixnext:
	cmp	r2, r3
	blo	fixloop

relocate_done:

#ifdef __XSCALE__
	/*
	 * On xscale, icache must be invalidated and write buffers drained,
	 * even with cache disabled - 4.2.7 of xscale core developer's manual
	 */
	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
#endif

	/* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
	mov        pc, lr
#else
	bx        lr
#endif

ENDPROC(relocate_code)

查看包含有调试信息的镜像文件:

arm-linux-readelf -r u-boot | less

利用了 PIC 位置无关代码,通过为编译器指定编译选项 -fpic-fpie 产生。

// arch/arm/config.mk
# needed for relocation
LDFLAGS_u-boot += -pie

使用 -pie 选项的 compiler,将全局变量地址、函数入口地址存储在rel.dyn段中,relocate_code 遍历 rel.dyn 段,根据 rel.dyn 中存储的值,对以(这些值+offset)为地址上的值进行了 relocate,完成对所有需要 relocate 的变量的修改。在 relocate_code 中 rel.dyn 不仅没有拷贝,也没有修改,修改只是针对 rel.dyn+offset 为地址上的值。

uboot 只指定了-pie给 ld,指定 -pie 后编译生成的 uboot 就会有一个 rel.dyn 段,uboot 就是靠 rel.dyn 实现了完美的 relocation。

relocate 主要问题是修改函数标号的地址,先从 rel 段找到需要修改的标号,从 image 中找到该标号位置,将该位置增加 offset 后写入到新的标号处。

在这里插入图片描述

. /arch/arm/cpu/u-boot.lds

uboot 的入口地址由链接脚本(链接器)决定,在配置文件中由 CONFIG_SYS_TEXT_BASE 指定,会在编译时加载 ld 链接器的选项-T text中,脚本规定了 start.o 作为 uboot 的起点。

目的:为了将 uboot 中多个目标文件的 .text、.data 和 .bss 等段链接在一起,提供该脚本文件告知链接器从什么地址开始放置这些段,怎么安排这些段的顺序。

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") // 指定输出可执行文件是elf格式,32位ARM指令,小端格式
OUTPUT_ARCH(arm)	// 指定输出的可执行文件的平台为ARM
ENTRY(_start) // 链接完毕后的执行入口
SECTIONS
{
	. = 0x00000000; // 指定 image 文件的全局入口点,通常在 flash 0x0 处
	. = ALIGN(4); // 代码以4字节对齐
	.text :
	{
		*(.__image_copy_start) // __image_copy_start 全局变量
		CPUDIR/start.o (.text*) // 先放置 start.o
		*(.text*) // 其它文件的代码段
	}
	. = ALIGN(4);
	.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } // 只读数据段(排序)
	. = ALIGN(4);
	.data : {	// 全局变量段
		*(.data*)
	}
	. = ALIGN(4);
	. = .;
	. = ALIGN(4);
	.u_boot_list : {
		KEEP(*(SORT(.u_boot_list*)));
	}
	. = ALIGN(4);
	.image_copy_end :
	{
		*(.__image_copy_end)
	}
	.rel_dyn_start :	// 动态符号链接段
	{
		*(.__rel_dyn_start)
	}
	.rel.dyn : {
		*(.rel*)
	}
	.rel_dyn_end :
	{
		*(.__rel_dyn_end)
	}
	_end = .;
	. = ALIGN(4096);
	.mmutable : {	// 不再使用
		*(.mmutable)
	}
	.bss_start __rel_dyn_start (OVERLAY) : { // 可变化的局部数据
		KEEP(*(.__bss_start));
		__bss_base = .;
	}
	.bss __bss_base (OVERLAY) : {
		*(.bss*)
		 . = ALIGN(4);
		 __bss_limit = .;
	}
	.bss_end __bss_limit (OVERLAY) : {
		KEEP(*(.__bss_end));
	}
	// 下面不使用
	/DISCARD/ : { *(.dynsym) }
	/DISCARD/ : { *(.dynstr*) }
	/DISCARD/ : { *(.dynamic*) }
	/DISCARD/ : { *(.plt*) }
	/DISCARD/ : { *(.interp*) }
	/DISCARD/ : { *(.gnu*) }
	/DISCARD/ : { *(.ARM.exidx*) }
	/DISCARD/ : { *(.gnu.linkonce.armexidx.*) }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jia ming

感谢欣赏!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值