u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

时间:2023-03-09 02:05:08
u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

一、重定位  

  1.以前版本的重定位

    u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

  2.新版本

    u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

    我们的程序不只涉及一个变量和函数,我们若想访问程序里面的地址,则必须使用SDRAM处的新地址,即我们的程序里面的变量和函数必须修改地址。我们要修改地址,则必须知道程序的地址,就需要在链接的时候加上PIE选项:

    u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

    加上PIE选项后,链接时候的地址就会生成,然后存储在段里面,如下段(u-boot.lds):

    u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

    然后我们根据这些地址的信息来修改代码,程序就可以复制到SDRAM的任何地方去。

二、代码流程

  start.S中执行到了 bl _main,跳转到_main,_main函数入口在crt0.S (arch\arm\lib) 中。

  1.crt0.S  

 ENTRY(_main)

 /*
* Set up initial C runtime environment and call board_init_f(0).
* 初始化C运行环境并且调用 board_init_f(0) 函数
*/ /*
* 初始化栈地址
*/
/* Generic-asm-offsets.h (include\generated)
* #define GENERATED_GBL_DATA_SIZE 192
* JZ2440.h(include\config)
* #define PHYS_SDRAM_1 0x30000000
* #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1
* #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE)
*
* CONFIG_SYS_INIT_SP_ADDR = 0x30000000 + 0x1000 - 192(0xc0) = 0x30000f40
*/
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) /* 设置CFIG_SYS_INIT_SP_ADDR定义的地址,include/configs/jz2440.h中定义 */ /* sp 的8字节对齐 */
bic sp, sp, # /* 8-byte alignment for ABI compliance */ mov r0, sp /* r0 = sp */
bl board_init_f_mem /*跳转到 board_init_f_mem 执行*/
mov sp, r0 mov r0, #
bl board_init_f /* 调用单板的初始化函数,跳转到 borad_init_f 处执行 */

  执行到 board_init_f 处,则跳转到Board_f.c (common) 中去执行。

  2.baord_init_f

 /*
* 单板的初始化函数
*/
void board_init_f(ulong boot_flags)
{
gd->flags = boot_flags;
gd->have_console = ; if (initcall_run_list(init_sequence_f))
hang(); #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
!defined(CONFIG_EFI_APP)
/* NOTREACHED - jump_to_copy() does not return */
hang();
#endif
}

  在其中最重要的函数则是 initcall_run_list(init_sequence_f) ,init_sequence_f 执行单板的各种初始化任务,如下:

 static init_fnc_t init_sequence_f[] = {
//gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
//CONFIG_SYS_MONITOR_BASE = _start = 0
//设置gd->mon_len为编译出来的u-boot.bin+bss段的大小
setup_mon_len,
initf_malloc,
initf_console_record,
//这个函数应该是留给移植人员使用的,里面什么都没做,而且被__weak修饰,
//所以我们可以在别的地方重新定义这个函数来取代它
arch_cpu_init, /* basic arch cpu dependent setup:CPU初始化*/
initf_dm,
arch_cpu_init_dm, //同上
mark_bootstage, /* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
/* 初始化CPU时钟和各种IO(待修改) */
board_early_init_f,
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
defined(CONFIG_SPARC)
/* 初始化定时器 */
timer_init, /* 初始化定时器 */
#endif
env_init, /* 初始化环境变量 */
init_baud_rate, /* 初始化波特率为: 115200 */
serial_init, /* 设置串口通讯 */
console_init_f, /* stage 1 init of console */
// 打印版本信息,你可以修改include/version.h中的CONFIG_IDENT_STRING选项,
// 加入自己的身份信息
display_options, /* say that we are here */
//打印bss段信息及text_base, 需要 #define DEBUG
display_text_info, /* show debugging info if required */
print_cpuinfo, /* 打印CPUID和时钟频率 */
INIT_FUNC_WATCHDOG_INIT
INIT_FUNC_WATCHDOG_RESET
announce_dram_init, //输出"DRAM: " 然后在下面进行SDRAM参数设置
/* TODO: unify all these dram functions? */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
dram_init, /* 在smdk2440.c中定义,配置SDRAM大小,可根据实际进行修改 */
#endif
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
/*
* Now that we have DRAM mapped and working, we can
* relocate the code and continue running from DRAM.
*
* Reserve memory at end of RAM for (top down in that order):
* - area that won't get touched by U-Boot and Linux (optional)
* - kernel log buffer
* - protected RAM
* - LCD framebuffer
* - monitor code
* - board info struct
*/
setup_dest_addr, //将gd->relocaddr、gd->ram_top指向SDRAM最顶端
reserve_round_4k, //gd->relocaddr 4K对齐
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
defined(CONFIG_ARM)
//gd->arch.tlb_size = PGTABLE_SIZE; 预留16kb的MMU页表
//gd->relocaddr -= gd->arch.tlb_size;
//gd->relocaddr &= ~(0x10000 - 1); 64kb对齐
//gd->arch.tlb_addr = gd->relocaddr;
reserve_mmu,
#endif
#if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
!defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
!defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
reserve_video,
#endif
#if !defined(CONFIG_BLACKFIN)
//gd->relocaddr -= gd->mon_len; 一开始设置的u-boot.bin + bss段长度
//gd->relocaddr &= ~(4096 - 1); 4k对齐,这是最终重定位地址
//gd->start_addr_sp = gd->relocaddr; 设置重定位后的栈指针
reserve_uboot,
#endif
#ifndef CONFIG_SPL_BUILD
//gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN;
//预留4MB MALLOC内存池
reserve_malloc,
//gd->start_addr_sp -= sizeof(bd_t); 预留空间给重定位后的gd_t->bd
//gd->bd = (bd_t *)gd->start_addr_sp; 指定重定位bd地址
//memset(gd->bd, '\0', sizeof(bd_t)); 清零
reserve_board,
#endif
//gd->bd->bi_arch_number = CONFIG_MACH_TYPE;
//对于S3C2440来说就是MACH_TYPE_S3C2440 (arch/arm/include/asm/mach-types.h)
setup_machine,
reserve_global_data,
reserve_fdt,
reserve_arch,
//gd->start_addr_sp -= 16; 栈指针16字节对齐
//gd->start_addr_sp &= ~0xf;
reserve_stacks,
//gd->bd->bi_dram[i].start = addr; 设置sdram地址和大小
//gd->bd->bi_dram[i].size = size;
setup_dram_config,
show_dram_config,//打印SDRAM大小,与上面的announce_dram_init相对应
display_new_sp,
INIT_FUNC_WATCHDOG_RESET
reloc_fdt,
//gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; 计算重定位偏移地址
//memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));
//将原来的gd复制到重定位后的gd地址上去
setup_reloc,
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
/* 重定位代码 */
jump_to_copy,
#endif
NULL,

  3.relocate_code

  jump_to_copy中调用重定位代码relocate_code:C语言调用汇编代码,relocate_code 定义在 relocate.S (arch\arm\lib) 。

  relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr);

  内存分布图如下:

  u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

  gd->start_addr_sp所在位置也看的出来了。gd->start_addr_sp 代码(内存分布代码):

  

 Jz2440.h (include\configs)
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #0 */
#define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1
#define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE
static int setup_mon_len(void)
{
/* TODO: use (ulong)&__bss_end - (ulong)&__text_start; ? */
gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
return ;
} Jz2440.h (include\configs)
#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */ int dram_init(void)
{
/* dram_init must store complete ramsize in gd->ram_size */
gd->ram_size = PHYS_SDRAM_1_SIZE;
return ;
} Jz2440.h (include\configs)
#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 static int setup_dest_addr(void)
{
gd->ram_size = board_reserve_ram_top(gd->ram_size); //64M ram_size #ifdef CONFIG_SYS_SDRAM_BASE
gd->ram_top = CONFIG_SYS_SDRAM_BASE; //gd->ram_top = 0x30000000
#endif
gd->ram_top += get_effective_memsize(); //gd->ramtop = 0x30000000 + 64M
gd->ram_top = board_get_usable_ram_top(gd->mon_len);//gd->ram_top值不变
gd->relocaddr = gd->ram_top;//gd->relocaddr = 0x30000000 + 64M
return ;
} static int reserve_round_4k(void) //gd->relocaddr 4K对齐
{
gd->relocaddr &= ~( - );
return ;
} static int reserve_mmu(void)
{
/* reserve TLB table */
gd->arch.tlb_size = PGTABLE_SIZE; //预留16kb的MMU页表
gd->relocaddr -= gd->arch.tlb_size; //gd->relocaddr = gd->relocaddr - 16K = 0x33ffc000 /* round down to next 64 kB limit */
gd->relocaddr &= ~(0x10000 - ); //64kb对齐 gd->relocaddr = 0x33ff0000 gd->arch.tlb_addr = gd->relocaddr; //gd->arch.tlb_addr = 0x33ff0000
debug("TLB table from %08lx to %08lx\n", gd->arch.tlb_addr,
gd->arch.tlb_addr + gd->arch.tlb_size);
return ;
} static int reserve_uboot(void)
{
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
gd->relocaddr -= gd->mon_len; // 一开始设置的u-boot.bin + bss段长度
//gd->relocaddr=gd->relocaddr-4kb=0x33fef000
gd->relocaddr &= ~( - ); // 4k对齐,这是最终重定位地址 0x33fef000 gd->start_addr_sp = gd->relocaddr; //设置重定位后的栈指针gd->start_addr_sp=0x33fef000 return ;
} Jz2440.h (include\configs)
#define CONFIG_SYS_MALLOC_LEN (4 * 1024 * 1024) //4M 0x400000 4194304 Common.h (include)
#define TOTAL_MALLOC_LEN CONFIG_SYS_MALLOC_LEN static int reserve_malloc(void)
{ //gd->start_addr_sp = gd->start_addr_sp - 4 * 1024 * 1024 =0x33bef000
gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; //预留4MB MALLOC内存池
return ;
} static int reserve_board(void)
{
if (!gd->bd) {
gd->start_addr_sp -= sizeof(bd_t); //预留空间给重定位后的gd_t->bd
gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t)); //指定重定位bd地址
memset(gd->bd, '\0', sizeof(bd_t)); //清零
}
return ;
} static int reserve_global_data(void)
{
gd->start_addr_sp -= sizeof(gd_t);
gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t));
return ;
} static int reserve_stacks(void)
{
/* make stack pointer 16-byte aligned */
gd->start_addr_sp -= ; //栈指针16字节对齐
gd->start_addr_sp &= ~0xf; return arch_reserve_stacks();
} Stack.c (arch\arm\lib)
int arch_reserve_stacks(void)
{
/* setup stack pointer for exceptions */
gd->irq_sp = gd->start_addr_sp; # if !defined(CONFIG_ARM64)
/* leave 3 words for abort-stack, plus 1 for alignment */
gd->start_addr_sp -= ;
# endif
return ;
}

  relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr);中的三个参数也已经清楚。gd->relocaddr=0x33fef000 

  crt0.S (arch\arm\lib) 

 #if ! defined(CONFIG_SPL_BUILD)
/*
* 这一段代码是将board_init_f中设置好的start_addr_sp地址值赋给栈指针,使其指向重定位后的栈顶
* 8字节对齐后,将r9设为新的GD地址( gd地址=bd地址-sizeof(gd_t))
*/
ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
bic sp, sp, # /* 8-byte alignment for ABI compliance */
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
sub r9, r9, #GD_SIZE /* new GD is below bd */ adr lr, here /*设置返回地址为下面的here,重定位到sdram后返回here运行*/
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off 取重定位地址偏移值 */
add lr, lr, r0 /*返回地址加偏移地址等于重定位后在sdram中的here地址*/
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr 传入参数为重定位地址 */
b relocate_code /*跳到arch/arm/lib/relocate.S中执行*/
here: /*返回后跳到sdram中运行 */
bl relocate_vectors
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */ ldr r0, =__bss_start /* this is auto-relocated! */
ldr r1, =__bss_end /* this is auto-relocated! */
mov r2, #0x00000000 /* prepare zero to clear BSS */ clbss_l:cmp r0, r1 /* while not at end of BSS */ strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, # /* move to next */
blo clbss_l
#endif

   relocate.S

 ENTRY(relocate_code)
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start
* 这是u-boot.bin起始链接地址,
* 定义在u-boot.lds中 (编译后在顶层目录生成)
* 原文件是arch/arm/cpu/u-boot.lds
*/
subs r4, r0, r1 /* r4 <- relocation offset
* r0是crt0.S中传入的重定位地址
* 这里是算出偏移值
*/
beq relocate_done /* skip relocation
* 如果r4为0,则认为重定位已完成
*/
ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end
* 同第一条指令,在u-boot.lds中定义
*/ /* r1是源地址__image_copy_start,r0是目的地址relocaddr,
* size = __image_copy_start - __image_copy_end
*/
copy_loop:
ldmia r1!, {r10-r11} /* 从 r1 中拷贝数据到 r10、r11 寄存器中 */
stmia r0!, {r10-r11} /* 把r10、r11 寄存器的数据存到r0寄存器中 */
cmp r1, r2 /* 比较 r1 和 r2 ,若 r0 < r2 则继续拷贝*/
blo copy_loop /*
* fix .rel.dyn relocations 定义了"-PIE"选项就会执行下面这段代码
* 目的是为了让位置相关的资源(代码、参数、变量)的地址在重定位后仍然能被寻址到,所以让他们加上偏移地址,
* 即等于他们重定位后的真正地址
* 这些 "存放(资源的地址)的地址" 存放在.rel.dyn这个段中,每个参数后面都会跟着一个起标志作用的参数,
* 如果这个标志参数为23,即0x17,则表示这个 (资源的地址) 是位置相关的,需要加上重定位偏移值
* 这一段代码首先让.rel.dyn这个段中的存放的地址值加上偏移值,使其在sdram中取出(资源的地址)
* 然后再让这些(资源的地址)加上偏移值,存回rel.dyn中存放这些地址的地址中,
*/ ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
fixloop:
/* r0为"存放(资源的地址)的地址",这个地址里存放的是需要用到的(资源的地址),r1为标志值 */
ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
and r1, r1, #0xff /* r1 = r1 & 0xff r1取低八位*/
cmp r1, # /* relative fixup? r1 是否等于23(0x17)*/
bne fixnext /* 若相等跳转到 fixnext执行 */ /* relative fix: increase location by offset */
/* r4存放的是重定位偏移值,r0这个地址存放的是位置相关的(资源的地址),
* r4+r0即为重定位后的"存放(资源的地址)的地址",
*/
add r0, r0, r4 /* r0 = r0 + r4 */
ldr r1, [r0] /* r1 = r0的地址?在sdram中取出还未修改的(资源的地址)*/
add r1, r1, r4 /* r1 = r1 + r4 加上偏移值*/
str r1, [r0] /* 存回去 */
fixnext: /* 跳到下一个继续检测是否需要重定位 */
cmp r2, r3
blo fixloop /* 跳转到 fixloop 继续执行 */ relocate_done:
#ifdef __ARM_ARCH_4__
/* ARM920T用的汇编指令集是ARMv4,所以使用这条返回指令,返回重定位后的here标志 */
mov pc, lr
#else
bx lr
#endif ENDPROC(relocate_code)

u-boot移植(四)---修改前工作:代码流程分析3---代码重定位

参考:http://blog.****.net/funkunho/article/details/52474373