一、重定位
1.以前版本的重定位
2.新版本
我們的程序不只涉及一個變量和函數,我們若想訪問程序里面的地址,則必須使用SDRAM處的新地址,即我們的程序里面的變量和函數必須修改地址。我們要修改地址,則必須知道程序的地址,就需要在鏈接的時候加上PIE選項:
加上PIE選項后,鏈接時候的地址就會生成,然后存儲在段里面,如下段(u-boot.lds):
然后我們根據這些地址的信息來修改代碼,程序就可以復制到SDRAM的任何地方去。
二、代碼流程
start.S中執行到了 bl _main,跳轉到_main,_main函數入口在crt0.S (arch\arm\lib) 中。
1.crt0.S
1 ENTRY(_main) 2 3 /* 4 * Set up initial C runtime environment and call board_init_f(0). 5 * 初始化C運行環境並且調用 board_init_f(0) 函數 6 */ 7 8 /* 9 * 初始化棧地址 10 */ 11 /* Generic-asm-offsets.h (include\generated) 12 * #define GENERATED_GBL_DATA_SIZE 192 13 * JZ2440.h(include\config) 14 * #define PHYS_SDRAM_1 0x30000000 15 * #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 16 * #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE) 17 * 18 * CONFIG_SYS_INIT_SP_ADDR = 0x30000000 + 0x1000 - 192(0xc0) = 0x30000f40 19 */ 20 ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) /* 設置CFIG_SYS_INIT_SP_ADDR定義的地址,include/configs/jz2440.h中定義 */ 21 22 /* sp 的8字節對齊 */ 23 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ 24 25 mov r0, sp /* r0 = sp */ 26 bl board_init_f_mem /*跳轉到 board_init_f_mem 執行*/ 27 mov sp, r0 28 29 mov r0, #0 30 bl board_init_f /* 調用單板的初始化函數,跳轉到 borad_init_f 處執行 */
執行到 board_init_f 處,則跳轉到Board_f.c (common) 中去執行。
2.baord_init_f
1 /* 2 * 單板的初始化函數 3 */ 4 void board_init_f(ulong boot_flags) 5 { 6 gd->flags = boot_flags; 7 gd->have_console = 0; 8 9 if (initcall_run_list(init_sequence_f)) 10 hang(); 11 12 #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \ 13 !defined(CONFIG_EFI_APP) 14 /* NOTREACHED - jump_to_copy() does not return */ 15 hang(); 16 #endif 17 }
在其中最重要的函數則是 initcall_run_list(init_sequence_f) ,init_sequence_f 執行單板的各種初始化任務,如下:
1 static init_fnc_t init_sequence_f[] = { 2 //gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE; 3 //CONFIG_SYS_MONITOR_BASE = _start = 0 4 //設置gd->mon_len為編譯出來的u-boot.bin+bss段的大小 5 setup_mon_len, 6 initf_malloc, 7 initf_console_record, 8 //這個函數應該是留給移植人員使用的,里面什么都沒做,而且被__weak修飾, 9 //所以我們可以在別的地方重新定義這個函數來取代它 10 arch_cpu_init, /* basic arch cpu dependent setup:CPU初始化*/ 11 initf_dm, 12 arch_cpu_init_dm, //同上 13 mark_bootstage, /* need timer, go after init dm */ 14 #if defined(CONFIG_BOARD_EARLY_INIT_F) 15 /* 初始化CPU時鍾和各種IO(待修改) */ 16 board_early_init_f, 17 #if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \ 18 defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \ 19 defined(CONFIG_SPARC) 20 /* 初始化定時器 */ 21 timer_init, /* 初始化定時器 */ 22 #endif 23 env_init, /* 初始化環境變量 */ 24 init_baud_rate, /* 初始化波特率為: 115200 */ 25 serial_init, /* 設置串口通訊 */ 26 console_init_f, /* stage 1 init of console */ 27 // 打印版本信息,你可以修改include/version.h中的CONFIG_IDENT_STRING選項, 28 // 加入自己的身份信息 29 display_options, /* say that we are here */ 30 //打印bss段信息及text_base, 需要 #define DEBUG 31 display_text_info, /* show debugging info if required */ 32 print_cpuinfo, /* 打印CPUID和時鍾頻率 */ 33 INIT_FUNC_WATCHDOG_INIT 34 INIT_FUNC_WATCHDOG_RESET 35 announce_dram_init, //輸出"DRAM: " 然后在下面進行SDRAM參數設置 36 /* TODO: unify all these dram functions? */ 37 #if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \ 38 defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) 39 dram_init, /* 在smdk2440.c中定義,配置SDRAM大小,可根據實際進行修改 */ 40 #endif 41 INIT_FUNC_WATCHDOG_RESET 42 INIT_FUNC_WATCHDOG_RESET 43 /* 44 * Now that we have DRAM mapped and working, we can 45 * relocate the code and continue running from DRAM. 46 * 47 * Reserve memory at end of RAM for (top down in that order): 48 * - area that won't get touched by U-Boot and Linux (optional) 49 * - kernel log buffer 50 * - protected RAM 51 * - LCD framebuffer 52 * - monitor code 53 * - board info struct 54 */ 55 setup_dest_addr, //將gd->relocaddr、gd->ram_top指向SDRAM最頂端 56 reserve_round_4k, //gd->relocaddr 4K對齊 57 #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \ 58 defined(CONFIG_ARM) 59 //gd->arch.tlb_size = PGTABLE_SIZE; 預留16kb的MMU頁表 60 //gd->relocaddr -= gd->arch.tlb_size; 61 //gd->relocaddr &= ~(0x10000 - 1); 64kb對齊 62 //gd->arch.tlb_addr = gd->relocaddr; 63 reserve_mmu, 64 #endif 65 #if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \ 66 !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \ 67 !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K) 68 reserve_video, 69 #endif 70 #if !defined(CONFIG_BLACKFIN) 71 //gd->relocaddr -= gd->mon_len; 一開始設置的u-boot.bin + bss段長度 72 //gd->relocaddr &= ~(4096 - 1); 4k對齊,這是最終重定位地址 73 //gd->start_addr_sp = gd->relocaddr; 設置重定位后的棧指針 74 reserve_uboot, 75 #endif 76 #ifndef CONFIG_SPL_BUILD 77 //gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; 78 //預留4MB MALLOC內存池 79 reserve_malloc, 80 //gd->start_addr_sp -= sizeof(bd_t); 預留空間給重定位后的gd_t->bd 81 //gd->bd = (bd_t *)gd->start_addr_sp; 指定重定位bd地址 82 //memset(gd->bd, '\0', sizeof(bd_t)); 清零 83 reserve_board, 84 #endif 85 //gd->bd->bi_arch_number = CONFIG_MACH_TYPE; 86 //對於S3C2440來說就是MACH_TYPE_S3C2440 (arch/arm/include/asm/mach-types.h) 87 setup_machine, 88 reserve_global_data, 89 reserve_fdt, 90 reserve_arch, 91 //gd->start_addr_sp -= 16; 棧指針16字節對齊 92 //gd->start_addr_sp &= ~0xf; 93 reserve_stacks, 94 //gd->bd->bi_dram[i].start = addr; 設置sdram地址和大小 95 //gd->bd->bi_dram[i].size = size; 96 setup_dram_config, 97 show_dram_config,//打印SDRAM大小,與上面的announce_dram_init相對應 98 display_new_sp, 99 INIT_FUNC_WATCHDOG_RESET 100 reloc_fdt, 101 //gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE; 計算重定位偏移地址 102 //memcpy(gd->new_gd, (char *)gd, sizeof(gd_t)); 103 //將原來的gd復制到重定位后的gd地址上去 104 setup_reloc, 105 #if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) 106 /* 重定位代碼 */ 107 jump_to_copy, 108 #endif 109 NULL, 110 }
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);
內存分布圖如下:
gd->start_addr_sp所在位置也看的出來了。gd->start_addr_sp 代碼(內存分布代碼):
1 Jz2440.h (include\configs) 2 #define PHYS_FLASH_1 0x00000000 /* Flash Bank #0 */
3 #define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1
4 #define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_FLASH_BASE
5 static int setup_mon_len(void) 6 { 7 /* TODO: use (ulong)&__bss_end - (ulong)&__text_start; ? */
8 gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE; 9 return 0; 10 } 11
12 Jz2440.h (include\configs) 13 #define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
14
15 int dram_init(void) 16 { 17 /* dram_init must store complete ramsize in gd->ram_size */
18 gd->ram_size = PHYS_SDRAM_1_SIZE; 19 return 0; 20 } 21
22 Jz2440.h (include\configs) 23 #define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
24 #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1
25
26 static int setup_dest_addr(void) 27 { 28 gd->ram_size = board_reserve_ram_top(gd->ram_size); //64M ram_size
29
30 #ifdef CONFIG_SYS_SDRAM_BASE 31 gd->ram_top = CONFIG_SYS_SDRAM_BASE; //gd->ram_top = 0x30000000
32 #endif
33 gd->ram_top += get_effective_memsize(); //gd->ramtop = 0x30000000 + 64M
34 gd->ram_top = board_get_usable_ram_top(gd->mon_len);//gd->ram_top值不變
35 gd->relocaddr = gd->ram_top;//gd->relocaddr = 0x30000000 + 64M
36 return 0; 37 } 38
39 static int reserve_round_4k(void) //gd->relocaddr 4K對齊
40 { 41 gd->relocaddr &= ~(4096 - 1); 42 return 0; 43 } 44
45 static int reserve_mmu(void) 46 { 47 /* reserve TLB table */
48 gd->arch.tlb_size = PGTABLE_SIZE; //預留16kb的MMU頁表
49 gd->relocaddr -= gd->arch.tlb_size; //gd->relocaddr = gd->relocaddr - 16K = 0x33ffc000
50
51 /* round down to next 64 kB limit */
52 gd->relocaddr &= ~(0x10000 - 1); //64kb對齊 gd->relocaddr = 0x33ff0000
53
54 gd->arch.tlb_addr = gd->relocaddr; //gd->arch.tlb_addr = 0x33ff0000
55 debug("TLB table from %08lx to %08lx\n", gd->arch.tlb_addr, 56 gd->arch.tlb_addr + gd->arch.tlb_size); 57 return 0; 58 } 59
60
61 static int reserve_uboot(void) 62 { 63 /*
64 * reserve memory for U-Boot code, data & bss 65 * round down to next 4 kB limit 66 */
67 gd->relocaddr -= gd->mon_len; // 一開始設置的u-boot.bin + bss段長度 68 //gd->relocaddr=gd->relocaddr-4kb=0x33fef000
69 gd->relocaddr &= ~(4096 - 1); // 4k對齊,這是最終重定位地址 0x33fef000
70
71 gd->start_addr_sp = gd->relocaddr; //設置重定位后的棧指針gd->start_addr_sp=0x33fef000
72
73 return 0; 74 } 75
76 Jz2440.h (include\configs) 77 #define CONFIG_SYS_MALLOC_LEN (4 * 1024 * 1024) //4M 0x400000 4194304
78
79 Common.h (include) 80 #define TOTAL_MALLOC_LEN CONFIG_SYS_MALLOC_LEN
81
82 static int reserve_malloc(void) 83 { //gd->start_addr_sp = gd->start_addr_sp - 4 * 1024 * 1024 =0x33bef000
84 gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN; //預留4MB MALLOC內存池
85 return 0; 86 } 87
88 static int reserve_board(void) 89 { 90 if (!gd->bd) { 91 gd->start_addr_sp -= sizeof(bd_t); //預留空間給重定位后的gd_t->bd
92 gd->bd = (bd_t *)map_sysmem(gd->start_addr_sp, sizeof(bd_t)); //指定重定位bd地址
93 memset(gd->bd, '\0', sizeof(bd_t)); //清零
94 } 95 return 0; 96 } 97
98 static int reserve_global_data(void) 99 { 100 gd->start_addr_sp -= sizeof(gd_t); 101 gd->new_gd = (gd_t *)map_sysmem(gd->start_addr_sp, sizeof(gd_t)); 102 return 0; 103 } 104
105 static int reserve_stacks(void) 106 { 107 /* make stack pointer 16-byte aligned */
108 gd->start_addr_sp -= 16; //棧指針16字節對齊
109 gd->start_addr_sp &= ~0xf; 110
111 return arch_reserve_stacks(); 112 } 113
114 Stack.c (arch\arm\lib) 115 int arch_reserve_stacks(void) 116 { 117 /* setup stack pointer for exceptions */
118 gd->irq_sp = gd->start_addr_sp; 119
120 # if !defined(CONFIG_ARM64) 121 /* leave 3 words for abort-stack, plus 1 for alignment */
122 gd->start_addr_sp -= 16; 123 # endif 124 return 0; 125 }
relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr);中的三個參數也已經清楚。gd->relocaddr=0x33fef000
crt0.S (arch\arm\lib)
1 #if ! defined(CONFIG_SPL_BUILD)
2 /*
3 * 這一段代碼是將board_init_f中設置好的start_addr_sp地址值賦給棧指針,使其指向重定位后的棧頂 4 * 8字節對齊后,將r9設為新的GD地址( gd地址=bd地址-sizeof(gd_t)) 5 */
6 ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
7 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
8 ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
9 sub r9, r9, #GD_SIZE /* new GD is below bd */
10
11 adr lr, here /*設置返回地址為下面的here,重定位到sdram后返回here運行*/
12 ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off 取重定位地址偏移值 */
13 add lr, lr, r0 /*返回地址加偏移地址等於重定位后在sdram中的here地址*/
14 ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr 傳入參數為重定位地址 */
15 b relocate_code /*跳到arch/arm/lib/relocate.S中執行*/
16 here: /*返回后跳到sdram中運行 */
17 bl relocate_vectors 18 /* Set up final (full) environment */
19 bl c_runtime_cpu_setup /* we still call old routine here */
20
21 ldr r0, =__bss_start /* this is auto-relocated! */
22 ldr r1, =__bss_end /* this is auto-relocated! */
23 mov r2, #0x00000000 /* prepare zero to clear BSS */
24
25 clbss_l:cmp r0, r1 /* while not at end of BSS */
26
27 strlo r2, [r0] /* clear 32-bit BSS word */
28 addlo r0, r0, #4 /* move to next */
29 blo clbss_l 30 #endif
relocate.S
1 ENTRY(relocate_code) 2 ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start 3 * 這是u-boot.bin起始鏈接地址, 4 * 定義在u-boot.lds中 (編譯后在頂層目錄生成) 5 * 原文件是arch/arm/cpu/u-boot.lds 6 */ 7 subs r4, r0, r1 /* r4 <- relocation offset 8 * r0是crt0.S中傳入的重定位地址 9 * 這里是算出偏移值 10 */ 11 beq relocate_done /* skip relocation 12 * 如果r4為0,則認為重定位已完成 13 */ 14 ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end 15 * 同第一條指令,在u-boot.lds中定義 16 */ 17 18 /* r1是源地址__image_copy_start,r0是目的地址relocaddr, 19 * size = __image_copy_start - __image_copy_end 20 */ 21 copy_loop: 22 ldmia r1!, {r10-r11} /* 從 r1 中拷貝數據到 r10、r11 寄存器中 */ 23 stmia r0!, {r10-r11} /* 把r10、r11 寄存器的數據存到r0寄存器中 */ 24 cmp r1, r2 /* 比較 r1 和 r2 ,若 r0 < r2 則繼續拷貝*/ 25 blo copy_loop 26 27 /* 28 * fix .rel.dyn relocations 定義了"-PIE"選項就會執行下面這段代碼 29 * 目的是為了讓位置相關的資源(代碼、參數、變量)的地址在重定位后仍然能被尋址到,所以讓他們加上偏移地址, 30 * 即等於他們重定位后的真正地址 31 * 這些 "存放(資源的地址)的地址" 存放在.rel.dyn這個段中,每個參數后面都會跟着一個起標志作用的參數, 32 * 如果這個標志參數為23,即0x17,則表示這個 (資源的地址) 是位置相關的,需要加上重定位偏移值 33 * 這一段代碼首先讓.rel.dyn這個段中的存放的地址值加上偏移值,使其在sdram中取出(資源的地址) 34 * 然后再讓這些(資源的地址)加上偏移值,存回rel.dyn中存放這些地址的地址中, 35 */ 36 37 ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */ 38 ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */ 39 fixloop: 40 /* r0為"存放(資源的地址)的地址",這個地址里存放的是需要用到的(資源的地址),r1為標志值 */ 41 ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */ 42 and r1, r1, #0xff /* r1 = r1 & 0xff r1取低八位*/ 43 cmp r1, #23 /* relative fixup? r1 是否等於23(0x17)*/ 44 bne fixnext /* 若相等跳轉到 fixnext執行 */ 45 46 /* relative fix: increase location by offset */ 47 /* r4存放的是重定位偏移值,r0這個地址存放的是位置相關的(資源的地址), 48 * r4+r0即為重定位后的"存放(資源的地址)的地址", 49 */ 50 add r0, r0, r4 /* r0 = r0 + r4 */ 51 ldr r1, [r0] /* r1 = r0的地址?在sdram中取出還未修改的(資源的地址)*/ 52 add r1, r1, r4 /* r1 = r1 + r4 加上偏移值*/ 53 str r1, [r0] /* 存回去 */ 54 fixnext: /* 跳到下一個繼續檢測是否需要重定位 */ 55 cmp r2, r3 56 blo fixloop /* 跳轉到 fixloop 繼續執行 */ 57 58 relocate_done: 59 #ifdef __ARM_ARCH_4__ 60 /* ARM920T用的匯編指令集是ARMv4,所以使用這條返回指令,返回重定位后的here標志 */ 61 mov pc, lr 62 #else 63 bx lr 64 #endif 65 66 ENDPROC(relocate_code)
參考:http://blog.csdn.net/funkunho/article/details/52474373