ENTRY(start) /* 设置入口点 */
SECTIONS
{
. = 0x100000;
PROVIDE( _start = . );
. = ALIGN(1 << 12);
.text.init :
{
*(.text.init)
. = ALIGN(4096);
}
. = ALIGN(1 << 12);
.data.init :
{
*(.data.init)
. = ALIGN(8192);
}
. += 0xC0000000;
. = ALIGN(1 << 12);
.text : AT(ADDR(.text) - 0xC0000000)
{
*(.text)
. = ALIGN(4096);
}
. = ALIGN(1 << 12);
.data : AT(ADDR(.data) - 0xC0000000)
{
__start_stack = .;
kern_stack = .;
. = __start_stack + 0x4000;
__end_stack = .;
*(.data)
*(.rodata)
. = ALIGN(8192);
}
. = ALIGN(1 << 12);
.bss : AT(ADDR(.bss) - 0xC0000000)
{
*(.bss)
. = ALIGN(4096);
}
.stab : AT(ADDR(.stab) - 0xC0000000)
{
*(.stab)
. = ALIGN(8192);
}
.data.init_task : AT(ADDR(.data.init_task) - 0xC0000000)
{
*(.data.init_task)
. = ALIGN(8192);
}
PROVIDE( __initcall_start = .);
.initcall.init : AT(ADDR(.initcall.init) - 0xC0000000)
{
*(.initcall.init)
PROVIDE( __initcall_end = .);
. = ALIGN(8192);
}
. += 0x2000;
.stabstr : AT(ADDR(.stabstr) - 0xC0000000)
{
*(.stabstr)
. = ALIGN(4096);
}
PROVIDE( _end = . );
/DISCARD/ : {
*(.comment) *(.eh_frame) }
}
以上是一个链接器脚本
ENTRY(start)表示设置程序的入口点为start函数。
SECTIONS开始定义链接器输出文件的段布局。
. = 0x100000;表示设置当前的段地址起始点为0x100000。
PROVIDE( _start = . );提供一个名为_start的全局符号,其地址值为当前地址。
. = ALIGN(1 << 12);是4KB对齐。
.text.init段,这段代码是程序初始化时执行的代码。*(.text.init)表示把所有的.text.init输入段都放在这里。
ALIGN(4096)确保这个段的结束是按4KB对齐的。
.data.init段,这是程序初始化时使用的数据段。它也是按8KB对齐的。
.+=0xC0000000;,增加当前地址0xC0000000。
.text,.data和.bss段,这些是常见的代码段、数据段和未初始化数据段。每个段开始之前,它们都被对齐到4KB的边界。
.stab和.stabstr段,这些段通常与调试信息有关。在这里,它们都被对齐,并且都使用了AT(ADDR(.stab)-0xC0000000)这种形式来描述它们在虚拟地址空间中的位置。
.data.init_task段,这个段包含了初始化任务的数据。
.initcall.init段,这个段可能与内核的初始化调用有关。定义了两个特殊的符号__initcall_start和__initcall_end,它们定义了初始化调用段的开始和结束位置。
PROVIDE(_end=.);,提供一个名为_end的全局符号,表示文件的结束地址。
/DISCARD/段,这个段的内容在链接后会被丢弃。
- 以上链接器脚本通过如下四行开辟了空间
__start_stack = .;
kern_stack = .;
. = __start_stack + 0x4000;
__end_stack = .;
通过将当前地址给__start_stack,和将当前地址增加0x4000,共开辟了4KB的空间