抽出編譯AM程式中的“打包使用者程式am-test到ELF”步驟,看看連結指令碼abstract-machine/scripts/linker.ld
如何將庫函式和使用者程式連結起來的。
首先看下連結命令:
echo + LD "->" build/amtest-riscv32-nemu.elf
($CROSS_COMPILE)ld -z noexecstack -melf64lriscv
-T /abstract-machine/scripts/linker.ld
--defsym=_pmem_start=0x80000000
--defsym=_entry_offset=0x0
--gc-sections
-e _start
-melf32lriscv
-o $AM_TEST/build/amtest-riscv32-nemu.elf
--start-group
$AM_TEST/build/riscv32-nemu/src/main.o
$AM_TEST/build/riscv32-nemu/src/tests/video.o
$AM_TEST/build/riscv32-nemu/src/tests/mp.o
$AM_TEST/build/riscv32-nemu/src/tests/hello.o
$AM_TEST/build/riscv32-nemu/src/tests/devscan.o
$AM_TEST/build/riscv32-nemu/src/tests/audio/audio-data.o
$AM_TEST/build/riscv32-nemu/src/tests/audio.o
$AM_TEST/build/riscv32-nemu/src/tests/keyboard.o
$AM_TEST/build/riscv32-nemu/src/tests/intr.o
$AM_TEST/build/riscv32-nemu/src/tests/rtc.o
$AM_TEST/build/riscv32-nemu/src/tests/vm.o
/abstract-machine/am/build/am-riscv32-nemu.a
/abstract-machine/klib/build/klib-riscv32-nemu.a
--end-group
這裡蘊含幾個關鍵資訊:
linker.ld
作為連結的自定義指令碼- 設定符號(symbol)
_pmem_start
的值為0x80000000,_entry_offset
為0。 - 設定程式的入口地址為
_start
接下來我們轉移到連結指令碼abstract-machine/scripts/linker.ld
的具體實現:
ENTRY(_start)
PHDRS { text PT_LOAD; data PT_LOAD; }
SECTIONS {
/* _pmem_start and _entry_offset are defined in LDFLAGS */
. = _pmem_start + _entry_offset;
.text : {
*(entry) /* 引用與符號 entry 相關的所有內容*/
*(.text*) /* 將所有檔案的 .text 段的內容合併到當前段*/
} : text /* 指定 .text 段被載入到名為 text 的記憶體區域中*/
etext = .;
_etext = .;
.rodata : {
*(.rodata*)
}
.data : {
*(.data)
} : data
edata = .;
_data = .;
.bss : {
_bss_start = .;
*(.bss*)
*(.sbss*)
*(.scommon)
}
_stack_top = ALIGN(0x1000);
. = _stack_top + 0x8000;
_stack_pointer = .;
end = .;
_end = .;
_heap_start = ALIGN(0x1000);
}
linker.ld
的連結內容:
-
設定
_start
作為程式的入口點:ENTRY(_start)
-
載入
.text
和.data
段到記憶體中:PHDRS { text PT_LOAD; data PT_LOAD; }
。PT_LOAD
是Program Header的型別 -
設定當前地址
_pmem_start + _entry_offset
:.
表示當前地址,_pmem_start + _entry_offset
就是定義當前地址的位置。這裡的地址就是0x80000000
-
設定
.text
、.rodata
、.data
、.bss
段。其中拿出.text
段的處理.text : { *(entry) /* 引用與符號 entry 相關的所有內容*/ *(.text*) /* 將所有檔案的 .text 段的內容合併到當前段*/ } : text /* 指定 .text 段被載入到名為 text 的記憶體區域中*/
-
設定棧和堆的佈局
_stack_top = ALIGN(0x1000);
確保棧從 4KB 對齊的位置開始。. = _stack_top + 0x8000;
設定棧的初始位置,分配了 32KB 的棧空間。stack_pointer = .;
設定_stack_pointer
符號,指示棧指標的位置
-
設定結束符號:
end
和_end
-
設定堆的起始地址,並確保堆從 4KB 對齊的位置開始
_heap_start = ALIGN(0x1000)
而其中符號entry
是在abstract-machine/am/src/riscv/nemu/start.S
中定義的
.section entry, "ax"
.globl _start
.type _start, @function
_start:
mv s0, zero # 清零s0暫存器
la sp, _stack_pointer # 將標籤 _stack_pointer 的地址載入到棧指標 sp 中,符號在linker.ld中定義。
jal _trm_init # 這條指令會跳轉到 _trm_init 標籤,並將當前指令地址(即 jal 的下一條指令的地址)壓入棧中
# _trm_init就是trm初始化函式
此程式設定了程式的入口點_start
,並且將符號 _start
宣告為一個函式型別。
至此,linker.d
的行為就分析完了。