天天看點

gcc連結腳本

對于.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完全開發》)

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:

    adr r0, _start  

    ldr r1, _TEXT_BASE  

    cmp r0, r1

    下面,結合u-boot.lds看看一個正式的連接配接腳本檔案。這個檔案的基本功能還能看明白,雖然上面分析了好多,但其中那些GNU風格的符号還是着實讓我感到迷惑。。。

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段的結束位置

}

繼續閱讀