抽出编译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)} : dataedata = .;_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
的行为就分析完了。