内核链接脚本解析
Jacksing

具体的代码可以见仓库,这里挑选几个说一下。

输出架构

OUTPUT_ARCH 这个命令用于指定输出文件的系统架构,这里采用的是 riscv。

入口点

ENTRY 则定义了程序的入口点,xv6 的默认入口点是 _entry。入口点代码在 entry.S 内。在链接脚本内,这个入口地址会被设置为 0x80000000。

位置计数器

进入 SECTIONS 后,当前程序的初始地址默认是 0x0。这里跟 Linux 的文件系统一样,采用 “.” 来表示当前地址。这个符号的名字叫位置计数器 (location counter),表示随后的段 / 变量对应的内存地址。这就意味着如果直接给 “.” 赋值,相当于切换了工作目录,后续段的相对位置都会发生改变。

这里可以看一个简单的例子

1
2
3
4
5
6
7
8
SECTIONS
{
. = 0x10000;
.text : { *(.text) }
. = 0x8000000;
.data : { *(.data) }
.bss : { *(.bss) }
}

进入 SECTIONS 后,位置计数器会被设置为 0x10000,所以链接器会将 .text 的地址设置为 0x10000。完成 .text 的设置后,位置计数器会被修改为 0x8000000,.data 的地址会被设置为 0x8000000。轮到 .bss 的时候,它会紧跟着 .data 段,此时位置计数器的值就是 0x8000000 加上 .data 段的大小。

xv6 初始地址设置

一进入 SECTIONS 脚本就会将当前的初始路径设置为 0x80000000。老师课上说这个地址是 qemu 认可的地址,第一条指令必须放置是它。书本2.6节的说法是 0x0 到 0x80000000 这段地址用于放置 IO 设备,所以第一条指令所在的地址就必须是 0x80000000。

段内容解析

1
2
3
4
5
6
7
8
9
.text : {
*(.text .text.*)
. = ALIGN(0x1000);
_trampoline = .;
*(trampsec)
. = ALIGN(0x1000);
ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page");
PROVIDE(etext = .);
}

内核的段设置都大同小异,所以看一个 .text 就可以了。

.text 段的第一个表达式 *(.text .text.*),其中的 * 是通配符。其表示所有输入文件的 .text.text.* 段都放置于输出文件的 .text 段中。

接着是命令 ALIGN(exp),这个命令会返回位置计数器对齐到下一个 exp 边界的地址,其并不会修改位置计数器的值。

PROVIDE 这个命令类似于 GCC 中的 attribute((weak))。现在只需要简单的理解为定义了一个符号。

剩余的 _trampolinetrampsec 在页表的章节会涉及到,到时候再去了解即可。

参考

链接脚本

GNU 链接脚本0 - 链接脚本基本介绍

  • 本文标题:内核链接脚本解析
  • 本文作者:Jacksing
  • 创建时间:2022-01-23 11:49:00
  • 本文链接:https://wzzzx.github.io/6-S081/kernel-link-file/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
 评论