先來分析一個簡單的.lds鏈接腳本
例1,假如現在有head.c init.c nand.c main.c這4個文件:
1.1 首先創建鏈接腳本nand.lds:
1 SECTIONS { 2 firtst 0x00000000 : { head.o init.o nand.o} 3 second 0x30000000 : AT(4096) { main.o } 4 }
SECTIONS { ... } 用來描述輸出文件的內存布局。
這個腳本里規定了兩個段,firtst和cecond
0x00000000 0x30000000
表示鏈接地址或運行地址,指程序在SRAM、SDRAM實際運行的地址,也就是使PC等於這個地址。
這里指head.o init.o nand.o的加載地址為0,運行地址在0x00000000,main.o運行地址在0x30000000
AT(4096)
表示加載地址或存儲地址,指程序編譯后存放的地址,一般存在ROM、FLASH中,也就是運行這個指令時,會先將4096地址~(4096+2048)地址處的內容復制到0x30000000處運行(因為已經初始化了SDRAM以及Nand Flash)。
這里指main.o的加載地址為Nand Flash里的地址4096,運行地址在SDRAM里的地址 0x30000000。
1.2 制作Makefile
objs := head.o init.o nand.o main.o nand.bin : $(objs) arm-linux-ld -Tnand.lds -o nand_elf $^ arm-linux-objcopy -O binary -S nand_elf $@ arm-linux-objdump -D -m arm nand_elf > nand.dis %.o:%.c arm-linux-gcc -Wall -c -O2 -o $@ $< %.o:%.S arm-linux-gcc -Wall -c -O2 -o $@ $< clean: rm -f nand.dis nand.bin nand_elf *.o
其中 objs 是代表的一個變量,表示obj文件,也可以是objects, OBJECTS, objs, OBJS, obj, 或是 OBJ,后面就可以使用$(objs)來使用這個變量了。
$@ 目標文件
$^ 所有的依賴文件
$< 第一個依賴文件
例如: arm-linux-ld -Tnand.lds -o nand_elf $^ <<—— 等價於 ——>> arm-linux-ld -o nand_elf head.o init.o nand.o main.o
%.o:%.c 表示所有的.o文件,依賴於對應的.c文件
%.o:%.S 表示所有的.o文件,依賴於對應的.S文件
當有多個.o文件時,這時候.lds鏈接腳本 又該如何安排它們在可執行文件中的順序?
這里就需要將多個目標文件的.text、.data和.bss等段鏈接在一起而鏈接腳本文件是告訴鏈接器從什么地址開始放置這些段
- .text:代碼段,存放程序執行代碼的一塊內存
- .data:讀/寫數據段,存放已初始的全局變量或靜態變量的一塊內存
- .rodata:只讀數據段,存放只讀數據段,比如全局const變量和#define定義的變量
- .bss:存放未初始化的全局變量或靜態變量,這里的變量存放只是用來預留位置,並不占用空間
常用命令:
ENTRY(SYMBOL);將SYMBOL的值設置成入口地址。一般設置為_start。
OUTPUT(FILENAME); 定義輸出文件的名字。可以用它來指定默認的輸出文件名稱。當然我們一般都用手動-o進行指定,如果我們沒有進行手動指定的話,輸出文件名稱就以這個FILENAME為輸出文件名。
STARTUP(filename);指定filename為第一個輸入文件。
OUTPUT_FORMAT(default, big, little);定義3種輸出文件的格式。若有命令行選項-EB(大端),則使用第二個輸出格式,有命令行指定-EL(小端),則使用第三個格式。否則使用默認的default輸出格式。
OUT_ARCH(arch);設置輸出文件的體系架構。
SECTIONS :最重要的,最基本的,也是最主要的命令,它告訴鏈接器如何把輸入文件的各個section輸出到目標文件中的各個section中去。
例2:分析 board/100ask24x0/u-boot.lds鏈接腳本
OUTPUT_ARCH(arm) //設置輸出文件的體系架構。 ENTRY(_start) //將_start這個全局符號設置成入口地址。 SECTIONS //輸出文件內容布局 { . = 0x00000000; //指定地址0x00000000 . = ALIGN(4); //代碼以4字節對齊 .text : //指定.text section段(位於0x00000000) { cpu/arm920t/start.o (.text) //添加第一個目標文件: cpu/arm920t/start.o里面的.text代碼段 board/100ask24x0/boot_init.o (.text) //添加第二個目標文件: board/100ask24x0/boot_init.o里面的.text代碼段 *(.text) // *(.text) 表示添加剩下的全部文件的.text代碼段 } . = ALIGN(4); .rodata : { *(.rodata) } //指定.rodata section段(位於0x00000000+.text section),將所有的.rodata只讀數據段合並成一個.rodata只讀數據段 . = ALIGN(4); .data : { *(.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段,所有的u-boot命令相關的定義都放在這個位置 __u_boot_cmd_end = .; // u_boot_cmd段結束位置 . = ALIGN(4); __bss_start = .; //把__bss_start賦值為當前位置,即bss段的開始位置 .bss : { *(.bss) } //指定bss段,這里NOLOAD的意思是這段不需裝載,僅在執行域中才會有這段 _end = .; //把_end賦值為當前位置,即bss段的結束位置 }