【linux kernel】基于ARM64分析linux内核的链接脚本vmlinux.lds.S
创始人
2024-01-21 18:50:02
0

文章目录

    • 一、导读
    • 二、链接器是什么
    • 三、链接脚本
    • 四、linux内核的链接脚本
      • 4-1 头文件包含描述
      • 4-2 参数设置和宏定义描述
      • 4-3 SECTIONS内容分析
    • 五、linux内核的“头”
    • 六、总结

一、导读

在linux内核中,arch目录下放置的是关于linux内核所支持的具体架构相关的代码描述文件。其中在具体架构目录下的kernel目录中都会有一个链接脚本文件。

二、链接器是什么

现代软件工程中,一个大的程序通常都由多个源文件组成,其中包含以高级计算机语言编写的源文件以及汇编语言编写的汇编文件。在编译构建过程中会分别对这些源文件进行汇编或者编译并生成目标文件,这些目录文件包含代码段、数据段、符号表等内容。而链接则是把这些目标文件的代码段、数据段以及符号表等内容收集起来并按照某种格式(例如ELF)组合成一个可执行二进制文件的过程。而这个过程是使用链接器来完成的。

链接器在链接过程中会使用到一个链接脚本文件,该文件用于描述链接的过程,当没有通过“-T”参数指定链接脚本时,链接器会使用内置的链接脚本。

三、链接脚本

链接脚本控制着如何把输入文件中的段合并到输出文件的段中,以及这些段的地址空间布局等。本质上则是把在编译构建过程中大量的二进制文件(.o文件)合并成一个可执行的二进制文件。

四、linux内核的链接脚本

本文以ARM64架构为例,首先贴上链接脚本的完整内容,后面会详细描述。linux内核针对ARM64架构的链接脚本放置于/arch/arm64/kernel/vmlinux.lds.S文件中:

/** ld script to make ARM Linux kernel* taken from the i386 version by Russell King* Written by Martin Mares */#include 
#include 
#include 
#include 
#include #include "image.h"/* .exit.text needed in case of alternative patching */
#define ARM_EXIT_KEEP(x)	x
#define ARM_EXIT_DISCARD(x)OUTPUT_ARCH(aarch64)
ENTRY(_text)jiffies = jiffies_64;#define HYPERVISOR_TEXT					\/*						\* Align to 4 KB so that			\* a) the HYP vector table is at its minimum	\*    alignment of 2048 bytes			\* b) the HYP init code will not cross a page	\*    boundary if its size does not exceed	\*    4 KB (see related ASSERT() below)		\*/						\. = ALIGN(SZ_4K);				\VMLINUX_SYMBOL(__hyp_idmap_text_start) = .;	\*(.hyp.idmap.text)				\VMLINUX_SYMBOL(__hyp_idmap_text_end) = .;	\VMLINUX_SYMBOL(__hyp_text_start) = .;		\*(.hyp.text)					\VMLINUX_SYMBOL(__hyp_text_end) = .;/** The size of the PE/COFF section that covers the kernel image, which* runs from stext to _edata, must be a round multiple of the PE/COFF* FileAlignment, which we set to its minimum value of 0x200. 'stext'* itself is 4 KB aligned, so padding out _edata to a 0x200 aligned* boundary should be sufficient.*/
PECOFF_FILE_ALIGNMENT = 0x200;#ifdef CONFIG_EFI
#define PECOFF_EDATA_PADDING	\.pecoff_edata_padding : { BYTE(0); . = ALIGN(PECOFF_FILE_ALIGNMENT); }
#else
#define PECOFF_EDATA_PADDING
#endif#if defined(CONFIG_DEBUG_ALIGN_RODATA)
#define ALIGN_DEBUG_RO			. = ALIGN(1<phys conversions will fail.*/
ASSERT(_text == (PAGE_OFFSET + TEXT_OFFSET), "HEAD is misaligned")

4-1 头文件包含描述

vmlinux.lds.S文件的开始处,会使用#include包含头文件,这一点与C语言类似:

#include 
#include 
#include 
#include 
#include #include "image.h"

在以上列出的头文件中,大多会使用宏定义方式编写特定段的描述内容,用于在vmlinux.lds.S文件中引用。

4-2 参数设置和宏定义描述

OUTPUT_ARCH(aarch64)语句用于

ENYRY(_text)语言用于设置程序的入口为_text,程序执行的第一条指令称为入口点(entry point)。除了这种方式,还有其他的方式:

(1)在GCC工具链的LD命令通过“-e”参数指定入口点。

(2)在链接脚本中通过ENTRY命令设置入口点。

(3)通过特定符号(例如start符号)设置入口点。

(4)使用代码段的起始地址。

(5)使用地址0。

在上述五种方式中,链接器会依次尝试来设置入口点,直到成功为止。

接下来设置jiffies参数值:jiffies = jiffies_64;jiffies_64定义在/kernel/time/timer.c文件中:

接着定义HYPERVISOR_TEXT代码段:

#define HYPERVISOR_TEXT					\. = ALIGN(SZ_4K);				\VMLINUX_SYMBOL(__hyp_idmap_text_start) = .;	\*(.hyp.idmap.text)				\VMLINUX_SYMBOL(__hyp_idmap_text_end) = .;	\VMLINUX_SYMBOL(__hyp_text_start) = .;		\*(.hyp.text)					\VMLINUX_SYMBOL(__hyp_text_end) = .;

4-3 SECTIONS内容分析

SECTIONS{}是链接脚本语法中的关键命令,用于描述输出文件的内存布局。SECTIONS命令告诉链接文件如何把输入文件的段映射到输出文件的各个段中,如何将输入端整合为输出段,如何把输出段放入程序地址空间和进程地址空间中。

在开始之前,先描述两个linux内核中重要的知识点:

(1)在链接脚本中,有一个特殊的符号:“.”,用于表示当前位置计数器。在vmlinux.lds.S文件中很多地方都会使用到。

(2)在链接脚本中有一个常用的编程技巧:为每个段(或者多个段)设置一些符号,用于标识内存位置的开始和结束,这样便可以在C语言代码中访问每个段(或者多个段)的起始地址结束地址

SECTIONS{}中最先开始的是:

	/DISCARD/ : {ARM_EXIT_DISCARD(EXIT_TEXT)ARM_EXIT_DISCARD(EXIT_DATA)EXIT_CALL*(.discard)*(.discard.*)}

/DISCARD/ 是一个特殊的输出段,被该段引用的任何输入段将不会出现在输出文件中。

接着是_text段:

	. = PAGE_OFFSET + TEXT_OFFSET;.head.text : {_text = .;HEAD_TEXT}

上述. = PAGE_OFFSET + TEXT_OFFSET;意思是把代码段的链接地址设置为PAGE_OFFSET + TEXT_OFFSET的计算值。PAGE_OFFSET表示内核空间和用户空间对虚拟地址空间的划分,TEXT_OFFSET表示代码段的偏移地址。

.head.text表示输出段,对应的输入段为HEAD_TEXT,本质为*(.head.text)。意思是将所有目标文件中的.head.text段放入.head.text输出段中。其中_text = .;用于标识_text段的开始。

接下来是.text输出段,本质上是代码段:

	.text : {			/* Real text segment		*/_stext = .;		/* Text and read-only data	*/__exception_text_start = .;*(.exception.text)__exception_text_end = .;IRQENTRY_TEXTTEXT_TEXTSCHED_TEXTLOCK_TEXTHYPERVISOR_TEXT*(.fixup)*(.gnu.warning). = ALIGN(16);*(.got)			/* Global offset table		*/}

上述代码会汇集目标文件中的多个输入段到.text中。例如:.exception.text.irqentry.text.sched.text.spinlock.text.hyp.idmap.text等。

接下来则是RO_DATA(PAGE_SIZE)宏代表的只读数据段,该宏定义非常长(此处不展开)。紧随其后的是异常表段:

#define EXCEPTION_TABLE(align)						\. = ALIGN(align);						\__ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) {		\VMLINUX_SYMBOL(__start___ex_table) = .;			\*(__ex_table)						\VMLINUX_SYMBOL(__stop___ex_table) = .;			\}

接下来放置.notes段:

#define NOTES								\.notes : AT(ADDR(.notes) - LOAD_OFFSET) {			\VMLINUX_SYMBOL(__start_notes) = .;			\*(.note.*)						\VMLINUX_SYMBOL(__stop_notes) = .;			\}

上述内容就是textrodata段的定义了,最后以_etext = .位置计数器结束。

接下来是与初始化相关的段,由[__init_begin , __init_end]符号标识:

	__init_begin = .;INIT_TEXT_SECTION(8).exit.text : {ARM_EXIT_KEEP(EXIT_TEXT)}ALIGN_DEBUG_RO_MIN(16).init.data : {INIT_DATAINIT_SETUP(16)INIT_CALLSCON_INITCALLSECURITY_INITCALLINIT_RAM_FS}.exit.data : {ARM_EXIT_KEEP(EXIT_DATA)}PERCPU_SECTION(64). = ALIGN(PAGE_SIZE);__init_end = .;

接着是.altinstructions.altinstr_replacement两个输出段。

后面是[_data , _edata]符号代表的数据相关段:

	_data = .;_sdata = .;RW_DATA_SECTION(64, PAGE_SIZE, THREAD_SIZE)PECOFF_EDATA_PADDING_edata = .;

然后是BSS相关段:BSS_SECTION(0, 0, 0)

最后以_end = .;标识linux内核的结束。然后还放置了与stab相关的调试段:

#define STABS_DEBUG							\.stab 0 : { *(.stab) }					\.stabstr 0 : { *(.stabstr) }				\.stab.excl 0 : { *(.stab.excl) }			\.stab.exclstr 0 : { *(.stab.exclstr) }			\.stab.index 0 : { *(.stab.index) }			\.stab.indexstr 0 : { *(.stab.indexstr) }		\.comment 0 : { *(.comment) }

在内存布局的最后会放置HEAD_SYMBOLS代表的三个符号标志:

#define HEAD_SYMBOLS						\_kernel_size_le		= DATA_LE64(_end - _text);	\_kernel_offset_le	= DATA_LE64(TEXT_OFFSET);	\_kernel_flags_le	= DATA_LE64(__HEAD_FLAGS);
  • _kernel_offset_le是镜像从RAM开始加载的偏移量(小端序)。

  • _kernel_flags_le是信息标志(小端序)。

  • _kernel_offset_le表示linux内核镜像的有效大小(小端序)。

在内核镜像生成过程中,上述三个符号标志代表的值会作为镜像头的一部分输出。

五、linux内核的“头”

上述内容对linux内核的vmlinux.lds.S进行了描述,已经知道在内存布局的开始处放置的是.head.text输出段,这正是linux内核的“头”,对应的输入段为*(.head.text)。在linux内核源码中,在arch/arm64/kernel/head.S文件中则描述了.head.text段:

六、总结

本文主要描述了linux内核针对ARM64的链接脚本文件vmlinux.lds.S,寻找linux内核镜像的入口点。不同架构下的vmlinux.lds.S文件内容大多不同,需要具体查看。

总而言之,linux内核镜像中的组成内容由链接脚本控制,从链接脚本和head.S启动汇编代码中可以寻找到linux内核镜像的入口点。

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...