u-boot中.lds連接腳本檔案的分析

對於.lds檔案,它定義了整個程式編譯之後的連接過程,決定了一個可執行程式的各個段的存儲位置。
先看一下GNU官方網站上對.lds檔案形式的完整描述︰
SECTIONS {
...
secname start BLOCK(align) (NOLOAD) : AT ( ldadr )
  { contents } >region :phdr =fill
...
}



secname和contents是必須的,其他的都是可選的。下面挑幾個常用的看看︰
1、secname︰段名
2、contents︰決定哪些內容放在本段,可以是整個目標檔案,也可以是目標檔案中的某段(程式碼段、數據段等)
3、start︰本段連接(執行)的位址,如果沒有使用AT(ldadr),本段存儲的位址也是start。GNU網站上說start可以用任意一種描述位址的符號來描述。
4、AT(ldadr)︰定義本段存儲(加載)的位址。


看一個簡單的例子︰(摘自《2410完全開發》)
/* nand.lds */
SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0x30000000 : AT(4096) { main.o }
}


以上,
head.o放在0x00000000位址開始處,init.o放在head.o後面,他們的執行位址也是0x00000000,即連接和存儲位址相同(沒有AT指定);
main.o放在4096(0x1000,是AT指定的,存儲位址)開始處,但是它的執行位址在0x30000000,執行之前需要從0x1000(加載處)複製到0x30000000(執行處),此過程也就用到了讀取Nand flash。
這就是存儲位址和連接(執行)位址的不同,稱為加載時域和執行時域,可以在.lds連接腳本檔案中分別指定。


編寫好的.lds檔案,在用arm-linux-ld連接命令時帶-Tfilename來調用執行,如
arm-linux-ld   Tnand.lds x.o y.o   o xy.o。


也用-Ttext參數直接指定連接位址,如
arm-linux-ld   Ttext 0x30000000 x.o y.o   o xy.o。


既然程式有了兩種位址,就涉及到一些跳轉指令的區別
ARM彙編中,常有兩種跳轉方法︰b跳轉指令、ldr指令向PC賦值。我自己經過歸納如下︰
(1)b step1 ︰b跳轉指令是相對跳轉,倚賴當前PC的值,偏移量是透過該指令本身的bit[23:0]算出來的,這使得使用b指令的程式不倚賴於要跳到的程式碼的位置,只看指令本身。
(2)ldr pc, =step1 ︰該指令是從內存中的某個位置(step1)讀出數據並賦給PC,同樣倚賴當前PC的值,但是偏移量是那個位置(step1)的連接位址(執行時的位址),所以可以用它實現從Flash到RAM的程式跳轉。
(3)此外,有必要回味一下adr偽指令,U-boot中那段relocate程式碼就是透過adr實現當前程式是在RAM中還是flash中。仍然用我當時的註釋︰


relocate: /* 把U-Boot重新定位到RAM */
    adr r0, _start /* r0是程式碼的當前位置 */
/* adr偽指令,彙編器自動透過當前PC的值算出 如果執行到_start時PC的值,放到r0中︰
當此段在flash中執行時r0 = _start = 0;
當此段在RAM中執行時_start = _TEXT_BASE(在board/smdk2410/config.mk中指定的值為0x33F80000,即u-boot在把程式碼拷貝到RAM中去執行的程式碼段的開始) */
    ldr r1, _TEXT_BASE /* 測試判斷是從Flash啟動,還是RAM */
/* 此句執行的結果r1始終是0x33FF80000,因為此值是又編譯器指定的(ads中設定,或-D設定編譯器參數) */
    cmp r0, r1 /* 比較r0和r1,調試的時候不要執行重定位 */



    下面,結合u-boot.lds看看一個正式的連接腳本檔案。
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
  ;指定輸出可執行檔案是elf格式,32位ARM指令,小端
OUTPUT_ARCH(arm)
  ;指定輸出可執行檔案的平台為ARM
ENTRY(_start)
  ;指定輸出可執行檔案的起始程式碼段為_start.
SECTIONS
{
        . = 0x00000000    ; 從0x0位置開始
        . = ALIGN(4)    ; 程式碼以4位元組對齊
        .text :    ; 指定程式碼段
        {
          cpu/arm920t/start.o (.text)  ; 程式碼的第一個程式碼部分
          *(.text)    ; 其它程式碼部分
        }
        . = ALIGN(4)
        .rodata : { *(.rodata) }  ; 指定只讀數據段
        . = ALIGN(4);
        .data : { *(.data) }   ; 指定讀/寫數據段
        . = ALIGN(4);
        .got : { *(.got) }   ; 指定got段, got段式是uboot自定義的一個段, 非標準段
        __u_boot_cmd_start = .   ; 把__u_boot_cmd_start賦值為當前位置, 即起始位置
        .u_boot_cmd : { *(.u_boot_cmd) }; 指定u_boot_cmd段, uboot把所有的uboot命令放在該段.
        __u_boot_cmd_end = .  ; 把__u_boot_cmd_end賦值為當前位置,即結束位置
        . = ALIGN(4);
        __bss_start = .   ; 把__bss_start賦值為當前位置,即bss段的開始位置
        .bss : { *(.bss) }  ; 指定bss段
        _end = .   ; 把_end賦值為當前位置,即bss段的結束位置
}


 

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()