連結器Linker:是一個程式,将一個或多個編譯器或彙編生成的目标檔案,及依賴庫,連結為一個可執行檔案。
GNU Linker采用AT&T連結腳本語言;
連結腳本檔案:包含ld程式連結的規則,其決定輸出可執行檔案的記憶體布局;
LD指令:arm64版本的連接配接器是aarch64-linux-gnu-ld
檢視指令參數:
aarch64-linux-gnu-ld --help
LD指令的參數有很多,常用的如下:
$(ARMGNU)-ld -T $(SRC_DIR)/linker.ld -Map xxx.map -o $(BUILD_DIR)/xxx.elf $(OBJ_FILES)
常用參數:
-T: 指定連接配接腳本;
-Map:輸出一個符号表檔案;
-o: 輸出最終可執行二進制檔案;
基本概念
輸入段(input section),輸出段(output section)
每個段包括name和大小;
段的屬性:
loadable:運作時會加載這些段内容到記憶體中;
allocatable:運作時不加載段的内容;
段的位址:
VMA(virtual memory address):虛拟位址,運作位址;
LMA(load memory address):加載位址
通常ROM的位址為加載位址,而RAM位址為VMA;
連結腳本指令
Entry(symbol): 設定程式的入口函數;
GNU-AS程式入口點有以下幾種:
a.使用-e參數;
b.使用ENTRY(symbol);
c.在.text的最開始的地方;
d.0位址;
INCLUDE filename:引入filename的連結腳本;
OUTPUT filename:輸出二進制檔案,類似指令行的"-o filename"
OUTPUT_FORMAT(bfd):輸出BFD格式;
OUTPUT_ARCH(bfdarch): 輸出處理器體系架構格式
OUTPUT_ARCH(aarch64)
ENTRY(_text)
指派符号
符号可以像C語言一樣指派;
"."表示目前位置;
symbol = expression;
symbol += expression;
symbol -= expression;
symbol *= expression;
symbol /= expression;
symbol <<= expression;
symbol >>= expression;
symbol &= expression;
symbol |= expression;
符号的引用
在C語言定義一個變量并初始化,編譯器會配置設定一個符号,且在記憶體中配置設定空間存儲;
在連結腳本中定義的變量,僅僅是在符号表裡定義這個符号,沒有配置設定存儲空間;
xxx.ld
start_of_ROM = .ROM;
end_of_ROM = .ROM + sizeof(.ROM)
可以在每個段中設定一些符号,以友善C語言通路每個段的起始位址和結束位址;
C語言中,對連結腳本裡定義的符号引用
extern char start_of_ROM[],end_of_ROM[];
char buf[4096];
memcpy(buf, start_of_ROM, end_of_ROM - start_of_ROM);//引用的是位址
SECTIONS指令
告訴連結器如何把輸入段(input sections)映射到輸出段(output sections),決定輸出程式的記憶體布局;
輸出section的描述符:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI0gTMx81dsQWZ4lmZf1GLlpXazVmcvwFciV2dsQXYtJ3bm9CX9s2RkBnVHFmb1clWvB3MaVnRtp1XlBXe0xCMy81dvRWYoNHLwEzX5xCMx8FesU2cfdGLwMzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SNyEmZlRDN4MGZzEjMzYzX4QDNzATM0EzLclDMyIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLyM3Lc9CX6MHc0RHaiojIsJye.png)
LMA加載位址
每個段都有VMA和LMA;
在輸出段描述符中使用“AT”指定LMA, 如果沒有指定,通常LMA=VMA;
構造一個基于ROM的映像檔案,常常會設定輸出端的虛拟位址和加載位址不一樣;
SECTIONS
{
.text 0x1000:{*(.text)_etext=.;}
.mdata 0x2000:
AT(ADDR(.text)+SIZEOF(.text)) //指定加載位址
{
_data=.;
*(.data);
_edata=.; //_data和_edata隻是一個符号,用來記錄mdata段的VMA位址
}
.bss 0x3000;
{
_bstart=.;
*(.bss)*(COMMON);
-bend=.;
}
}
mdata段的加載位址和連結位址不一樣,是以程式初始化代碼需要把data段内容複制到SDRAM中的虛拟位址中;
常見的内建函數(builtin functions)
ADDR(section):傳回前面已經定義過的段的VMA位址;
ALIGN(n): 傳回下一個與n位元組對齊的位址;
SIZEOF(section):傳回一個段的大小;
MAX/MIN(exp1,exp2):傳回較大/較小值;
TEXT_ROM = 0X90000;
SECTIONS
{
/*
* -->location, current addr
* 0x80000 is entrance address
*/
. = 0x80000,
/*
* first instruction
**/
_text_boot = .;
.text.boot : { *(.text.boot) }
_etext_boot = .;
/*
* code segment
*/
_text = .;
.text : AT(TEXT_ROM)
{ *(.text) }
_etext = .;
/*
* readonly data segment
*/
_rodata = .;
.rodata : AT(ADDR(.rodata))
{ *(.rodata) }
_erodata = .;
/*
* data segment
*/
_data = .;
.data : { *(.data) }
_edata = .;
/*
* bss segment
* 8bytes algine
*/
. = ALIGN(0x8);
_bss = .;
.bss : { *(.bss*) }
_ebss = .;
/*
* alloc a page memory, store page table
* aligne 4096
*/
. = ALIGN(4096);
init_pg_dir = .;
. +=4096;
}
master:
/*
* copy code segment from rom(LMA) TO ram(VMA)
*/
adr x0, TEXT_ROM
adr x1, _text
adr x2, _etext
1:
ldr x4, [x0], #8
str x4, [x1], #8
cmp