編輯:關於Android編程
今天學習了一下 u-boot-2012.04.01 的第一階段,發現與 u-boot1.1.6 的差異還是很大的,尤其是在代碼重定位方面。在 u-boot1.1.6 中鏈接地址為 0x33f80000 ,重定位時,uboot也被拷貝到這個固定的地址。然而,u-boot-2012 的鏈接地址為 0 ,在重定位時,uboot 在 sdram 中的地址也不是固定的,這樣就帶來了一個問題 uboot 中使用了大量位置無關碼如果不作處理,那麼就無法正確執行,如何處理的,原理很簡單,後面分析。
還是先來看下鏈接腳本:
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
__image_copy_start = .;
arch/arm/cpu/arm920t/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
. = ALIGN(4);
.data : {
*(.data)
}
. = ALIGN(4);
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__image_copy_end = .;
.rel.dyn : {
__rel_dyn_start = .;
*(.rel*)
__rel_dyn_end = .;
}
.dynsym : {
__dynsym_start = .;
*(.dynsym)
}
_end = .;
. = ALIGN(4096);
.mmutable : {
*(.mmutable)
}
.bss __rel_dyn_start (OVERLAY) : {
__bss_start = .;
*(.bss)
. = ALIGN(4);
__bss_end__ = .;
}
鏈接:arm-linux-ld -pie -Tu-boot.lds -Bstatic -Ttext 0x0 ...
之前從未接觸過 -pie 參數,它的作用就是填充 .rel 段 和 .dynsym 段,至於這兩個段放的什麼東西,後邊再說吧。先來按照以前的流程分析一下。
start_code: /* set the cpu to SVC32 mode */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr, r0 /* turn off the watchdog */ ldr r0, =pWTCON mov r1, #0x0 str r1, [r0] /* mask all IRQs by setting all bits in the INTMR - default */ mov r1, #0xffffffff ldr r0, =INTMSK str r1, [r0] # if defined(CONFIG_S3C2410) ldr r1, =0x3ff ldr r0, =INTSUBMSK str r1, [r0] /* FCLK:HCLK:PCLK = 1:2:4 default FCLK is 120 MHz ! */ ldr r0, =CLKDIVN mov r1, #3 str r1, [r0] /* Set stackpointer in internal RAM to call board_init_f */ * #define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */ * #define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM_1 * #define CONFIG_SYS_INIT_SP_ADDR (CONFIG_SYS_SDRAM_BASE + 0x1000 - GENERATED_GBL_DATA_SIZE) == 30000f80 */ call_board_init_f: ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ ldr r0,=0x00000000 bl board_init_f上邊都是老掉牙的東西,沒啥好分析的了。
void board_init_f(ulong bootflag)
{
bd_t *bd;
init_fnc_t **init_fnc_ptr;
gd_t *id;
ulong addr, addr_sp;
/* Pointer is writable since we allocated a register for it */
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07); // 30000f80
memset((void *)gd, 0, sizeof(gd_t));
/* .word __bss_end__ - _start == u-boot 的長度 */
gd->mon_len = _bss_end_ofs;
/*
init_fnc_t *init_sequence[] = {
board_early_init_f,
timer_init, /* initialize timer */
env_init, /* initialize environment */
init_baudrate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_banner, /* say that we are here */
print_cpuinfo, /* display cpu info (and speed) */
dram_init, /* gd->ram_size = PHYS_SDRAM_1_SIZE = 0x04000000 64 MB */
NULL,
}
*/
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/* 0x30000000 + 0x04000000 = 0x3400 0000 */
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;
/* reserve kernel log buffer
* #define LOGBUFF_OVERHEAD (4096)
* #define LOGBUFF_LEN (16384)
* #define LOGBUFF_RESERVE (LOGBUFF_LEN+LOGBUFF_OVERHEAD) = 0x5000
*/
addr -= (LOGBUFF_RESERVE); //addr = 0x3400 0000 - 0x5000 = 33ff b000
/* reserve TLB table */
addr -= (4096 * 4); // addr = 33ff b000 - 4096*4 = 33ff 7000
/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1); // addr = 33ff 0000
gd->tlb_addr = addr; // addr = 33ff 0000
addr &= ~(4096 - 1); // addr = 33ff 0000
/*
* reserve memory for U-Boot code, data & bss
* round down to next 4 kB limit
*/
addr -= gd->mon_len; // addr = 33ff 0000 - u-boot的長度 = 33F4101C
addr &= ~(4096 - 1); // addr = 33ff 0000 - u-boot的長度 = 33F41000
/* reserve memory for malloc() arena */
* #define CONFIG_SYS_MALLOC_LEN (4 * 1024 * 1024)
* #define TOTAL_MALLOC_LEN CONFIG_SYS_MALLOC_LEN
*/
addr_sp = addr - TOTAL_MALLOC_LEN; // 33B41000
/* gd 結構體 */
addr_sp -= sizeof (bd_t); // 33B41000 - sizeof (bd_t)
bd = (bd_t *) addr_sp;
gd->bd = bd;
addr_sp -= sizeof (gd_t); // 33B41000 - sizeof (bd_t) - sizeof (gd_t)
id = (gd_t *) addr_sp;
/* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;
/* leave 3 words for abort-stack */
addr_sp -= 12; // 33B41000 - sizeof (bd_t) - sizeof (gd_t) - 12
/* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;
gd->bd->bi_baudrate = gd->baudrate;
/* Ram ist board specific, so move it to board code ... */
dram_init_banksize();
display_dram_config(); /* and display it */
gd->relocaddr = addr; // addr = 33ff 0000 - u-boot的長度 = 33F41000
gd->start_addr_sp = addr_sp;
gd->reloc_off = addr - _TEXT_BASE; // 33F41000 - 0 = 33F41000
memcpy(id, (void *)gd, sizeof(gd_t));
relocate_code(addr_sp, id, addr);
}
上邊的重點工作是進行了一些初始化,最重要的是進行了內存的劃分,大致如下圖所示:

.globl relocate_code
relocate_code:
mov r4, r0 /* addr_sp 棧 */
mov r5, r1 /* id gd結構體 */
mov r6, r2 /* sdram 中 addr u-boot 起始地址 */
/* Set up the stack */
stack_setup:
mov sp, r4 /* 設置棧 */
adr r0, _start /* 判斷當前代碼位置 */
cmp r0, r6
beq clear_bss /* skip relocation */
/* 位於norflash中 */
mov r1, r6 /* sdram 中 addr u-boot 起始地址 */
ldr r3, _bss_start_ofs /* .word __bss_start - _start 代碼段長度 */
add r2, r0, r3 /* 當前代碼段結束地址 */
copy_loop:
/* 將 uboot 代碼段 拷貝到 sdram */
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
#ifndef CONFIG_SPL_BUILD
/*
* 修正位置有關碼的數據
* 舉個簡單的例子:ldr pc,=_start 假設鏈接地址為 0 ,_start 的地址為 0
* ldr pc, =_start 將被翻譯成 ldr pc, [pc,#x]
* pc + #x 存放的是個定值 _start 的地址 0 這個值是根據鏈接地址來確定的
* 代碼位於鏈接地址處運行,pc 能夠跳轉到正確的地方 0 處
* 如果代碼被重定位到了0x3000 0000 ,此時 ldr pc, =_start
* 依舊會去 pc + #x 處取值,注意此時的 pc + #x 已經是個 > 0x3000 0000 的值了,相比之前的 pc + #x 差了 0x3000 0000
* 但是裡面存的值卻是相同的 都是 0 ,因此它跳轉不到 我們想跳的 0x3000 0000 處去,
* 為了能夠正確跳轉,我們要修改 pc + #x 處的值,給它加上一個 0x3000 0000
* 而,這些要修改的值的地址 pc + #x 都存放在 __rel_dyn_start 開始的地方
*/
ldr r0, _TEXT_BASE /* 0 */
sub r9, r6, r0 /* sdram 中 addr u-boot 地址 與 norflash 中 u-boot 地址的偏差 */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */
add r10, r10, r0 /* r10 <- sym table in FLASH */
/*
* __rel_dyn_start 起始的地址放了些啥?
* 比如 ldr r0, =main
* 首先,ldr r0, [pc,#xxx]
* __rel_dyn_start 存放的就是 pc + #xxx ,要去取數的地方
*/
ldr r2, _rel_dyn_start_ofs /* __rel_dyn_start - _start ,__rel_dyn_start 相對起始地址的偏移量 */
add r2, r2, r0 /* __rel_dyn_start 地址 */
ldr r3, _rel_dyn_end_ofs /* _rel_dyn_end_ofs - _start , _rel_dyn_end_ofs 相對起始地址的偏移量 */
add r3, r3, r0 /* _rel_dyn_end_ofs 的地址 */
fixloop:
ldr r0, [r2] /* 從 __rel_dyn_start 處取一個值 */
add r0, r0, r9 /* 加上 sdram 中 addr u-boot 地址 與 norflash 中 u-boot 地址的偏差 */
ldr r1, [r2, #4] /* 判斷 下一個值 是不是 0x17 ,我看了一下反匯編,都是0x17的情況 */
and r7, r1, #0xff
cmp r7, #23 /* relative fixup? */
beq fixrel
cmp r7, #2 /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* 不會執行 */
mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #4] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* 取出數據 */
ldr r1, [r0]
add r1, r1, r9 /* 修正數據 */
fixnext:
str r1, [r0]
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
cmp r2, r3
blo fixloop
#endif
clear_bss:
#ifndef CONFIG_SPL_BUILD
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
mov r4, r6 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
bne clbss_l
bl coloured_LED_init
bl red_led_on
#endif
/*
* We are done. Do not return, instead branch to second part of board
* initialization, now running from RAM.
*/
#ifdef CONFIG_NAND_SPL
ldr r0, _nand_boot_ofs
mov pc, r0
_nand_boot_ofs:
.word nand_boot
#else
ldr r0, _board_init_r_ofs
adr r1, _start
add lr, r0, r1
add lr, lr, r9
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
mov pc, lr
_board_init_r_ofs:
.word board_init_r - _start
#endif
以上將全部的位置有關碼的數據進行了偏差修正。新版的 u-boot 都采用這種方法,導致代碼復雜了不說,還讓uboot 大了不少,不知道是利大於弊,還是弊大於利呀?
韋東山老師說,在引用動態鏈接庫函數的時候,運行時才能確定函數地址,這個動態地址也是需要修正的,對應於上邊代碼的另一個分支 fixabs ,但是我在反匯編文件中沒有發現這種情況,後邊遇到在分析吧~
仿ViewPager相冊(使用HorizontalScrollView)
近期看了一堂某在線IT學習的視頻公開課,這裡就不說名字了,省的有打廣告的嫌疑。講到了利用HorizontalScrollView仿ViewPager設計的一個簡單相冊。其
Android實現三級聯動下拉框 下拉列表spinner的實例
主要實現辦法:動態加載各級下拉值的適配器 在監聽本級下拉框,當本級下拉框的選中值改變時,隨之修改下級的適配器的綁定值
標題欄的公共抽取
一、情形描述在常使用的頁面布局中,為保持用戶一貫的使用風格,會保持頁面的整體風格相似。除開底部導航外,標題欄是使用頻率較高的另一種頁面布局。如圖所示:在程序猿&ldquo
Android ContentProvider查看/讀取手機聯系人實例
看到某些App裡面有讀取聯系人的功能,然後自己嘗試了一下。發現這個挺簡單的。然後自己就做了一個demo給大家,希望借這個demo可以讓大家學習一下怎麼實現讀取手機聯系人。