慢慢欣赏arm64内核启动0 链接脚本.head.text部分的解析

以5.10内核为例

链接脚本分析

KIMAGE_VADDR

KIMAGE_VADDR表示虚拟内核的虚拟起始地址,其值计算比较复杂,是linux kernel一堆宏定义的运算,得出的结果是 0xFFFF 8000 1000 0000

这值在后续如下章节有详细的推导

慢慢欣赏arm64内核启动18 primary_entry之__create_page_tables代码第五部分-CSDN博客

我们还可以查看构建vmlinux.lds.S生成的文件vmlinux.lds,里面的定义如下

 . = (((((((-(((1)) << ((((48))) - 1))))) + (0x08000000))) + (0x08000000)));
 .head.text : {
  _text = .;
  KEEP(*(.head.text))
 }
 .text : {
  _stext = .;

我们尝试计算KIMAGE_VADDR的数值

(((((((-(((1)) << ((((48))) - 1))))) + (0x08000000))) + (0x08000000)))

1 << 47 == 0x8000 0000 0000

-(1 << 47) = 0xFFFF 7FFF FFFF FFFF + 1 == 0xFFFF 8000 0000 0000

-(1 << 47) + + (0x0800 0000))) + (0x0800 0000))) = 0xFFFF 8000 1000 0000

SECTIONS
{
	/*
	 * XXX: The linker does not define how output sections are
	 * assigned to input sections when there are multiple statements
	 * matching the same input section name.  There is no documented
	 * order of matching.
	 */
	DISCARDS
	/DISCARD/ : {
		*(.interp .dynamic)
		*(.dynsym .dynstr .hash .gnu.hash)
	}

	. = KIMAGE_VADDR;

	.head.text : {
		_text = .;
		HEAD_TEXT
	}

镜像的第一个段是.head.test段,起始地址也就是上文计算的0xFFFF 8000 1000 0000,再接着看,_text的地址也是0xFFFF 8000 1000 0000

查看Sysmap也确实如此

$ cat System.map | grep ffff800010000000
ffff800010000000 t __efistub__text
ffff800010000000 t _head
ffff800010000000 T _text

HEAD_TEXT

HEAD_TEXT是指什么呢?我们搜索一下内核源码

$ grep -rn HEAD_TEXT ./
./include/asm-generic/vmlinux.lds.h:661:#define HEAD_TEXT  KEEP(*(.head.text))


$ grep -rn .head.text ./
./include/linux/init.h:95:#define __HEAD		.section	".head.text","ax"

我们再接着查.head.text

$ grep -rnw .head.text ./
./include/linux/init.h:95:#define __HEAD		.section	".head.text","ax"
$ grep -rnw __HEAD ./arch/arm64/
./arch/arm64/kernel/head.S:60:	__HEAD

也就是说,HEAD_TEXT对应的代码段就在arm64/kernel/head.S里面,这也应该是bootloader把控制权交给内核后的启动地址。为了验证我们的猜想,我们解析一下内核ELF文件

$ readelf -e vmlinux
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           AArch64
  Version:                           0x1
  Entry point address:               0xffff800010000000
  Start of program headers:          64 (bytes into file)
  Start of section headers:          368544920 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         5
  Size of section headers:           64 (bytes)
  Number of section headers:         38
  Section header string table index: 37

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .head.text        PROGBITS         ffff800010000000  00010000
       0000000000010000  0000000000000000  AX       0     0     65536
  [ 2] .text             PROGBITS         ffff800010010000  00020000
       0000000000db24b8  0000000000000008  AX       0     0     2048
  [ 3] .got.plt          PROGBITS         ffff800010dc24b8  00dd24b8
       0000000000000018  0000000000000008  WA       0     0     8
  [ 4] .rodata           PROGBITS         ffff800010dd0000  00de0000
       00000000004a09c0  0000000000000000  WA       0     0     4096
  [ 5] .pci_fixup        PROGBITS         ffff8000112709c0  012809c0
       00000000000026b0  0000000000000000   A       0     0     16
  [ 6] __ksymtab         PROGBITS         ffff800011273070  01283070
       0000000000011dcc  0000000000000000   A       0     0     4
  [ 7] __ksymtab_gpl     PROGBITS         ffff800011284e3c  01294e3c
       0000000000014490  0000000000000000   A       0     0     4
  [ 8] __ksymtab_strings PROGBITS         ffff8000112992cc  012a92cc
       000000000003ee20  0000000000000001 AMS       0     0     1
  [ 9] __param           PROGBITS         ffff8000112d80f0  012e80f0
       00000000000046a0  0000000000000000   A       0     0     8
  [10] __modver          PROGBITS         ffff8000112dc790  012ec790
       0000000000000100  0000000000000000   A       0     0     8
  [11] __ex_table        PROGBITS         ffff8000112dc890  012ec890
       0000000000002300  0000000000000000   A       0     0     8
  [12] .notes            NOTE             ffff8000112deb90  012eeb90
       000000000000003c  0000000000000000   A       0     0     4
  [13] .init.text        PROGBITS         ffff8000112f0000  012f0000
       0000000000057da0  0000000000000000  AX       0     0     4
  [14] .exit.text        PROGBITS         ffff800011347da0  01347da0
       0000000000005da0  0000000000000000  AX       0     0     4
  [15] .altinstructions  PROGBITS         ffff80001134db40  0134db40
       0000000000028044  0000000000000000   A       0     0     1
  [16] .init.data        PROGBITS         ffff800011380000  01380000
       000000000001ea16  0000000000000000  WA       0     0     256
  [17] .data..percpu     PROGBITS         ffff80001139f000  0139f000
       000000000000dcd8  0000000000000000  WA       0     0     64
  [18] .hyp.data..percpu PROGBITS         ffff8000113ad000  013ad000
       0000000000000e20  0000000000000000  WA       0     0     16
  [19] .rela.dyn         RELA             ffff8000113ade20  013ade20
       0000000000183ea0  0000000000000018   A       0     0     8
  [20] .data             PROGBITS         ffff800011540000  01540000
       000000000011d260  0000000000000000  WA       0     0     4096
  [21] __bug_table       PROGBITS         ffff80001165d260  0165d260
       0000000000019434  0000000000000000  WA       0     0     4
  [22] .mmuoff.data[...] PROGBITS         ffff800011676800  01676800
       0000000000000018  0000000000000000  WA       0     0     2048
  [23] .mmuoff.data.read PROGBITS         ffff800011677000  01677000
       0000000000000008  0000000000000000  WA       0     0     8
  [24] .pecoff_edat[...] PROGBITS         ffff800011677008  01677008
       00000000000001f8  0000000000000000  WA       0     0     1
  [25] .bss              NOBITS           ffff800011678000  01677200
       000000000007955c  0000000000000000  WA       0     0     4096
  [26] .debug_aranges    PROGBITS         0000000000000000  01677200
       000000000002daa0  0000000000000000           0     0     16
  [27] .debug_info       PROGBITS         0000000000000000  016a4ca0
       000000000c9b6c4c  0000000000000000           0     0     1
  [28] .debug_abbrev     PROGBITS         0000000000000000  0e05b8ec
       00000000005bf625  0000000000000000           0     0     1
  [29] .debug_line       PROGBITS         0000000000000000  0e61af11
       00000000019046b9  0000000000000000           0     0     1
  [30] .debug_frame      PROGBITS         0000000000000000  0ff1f5d0
       0000000000345f90  0000000000000000           0     0     8
  [31] .debug_str        PROGBITS         0000000000000000  10265560
       00000000003d0adf  0000000000000001  MS       0     0     1
  [32] .debug_loc        PROGBITS         0000000000000000  1063603f
       0000000004060187  0000000000000000           0     0     1
  [33] .debug_ranges     PROGBITS         0000000000000000  146961d0
       000000000134f6e0  0000000000000000           0     0     16
  [34] .comment          PROGBITS         0000000000000000  159e58b0
       000000000000005d  0000000000000001  MS       0     0     1
  [35] .symtab           SYMTAB           0000000000000000  159e5910
       00000000003536d0  0000000000000018          36   118265     8
  [36] .strtab           STRTAB           0000000000000000  15d38fe0
       000000000023fb1e  0000000000000000           0     0     1
  [37] .shstrtab         STRTAB           0000000000000000  15f78afe
       0000000000000198  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  D (mbind), p (processor specific)

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000010000 0xffff800010000000 0xffff800010000000
                 0x00000000012debcc 0x00000000012debcc  RWE    0x10000
  LOAD           0x00000000012f0000 0xffff8000112f0000 0xffff8000112f0000
                 0x0000000000085b84 0x0000000000085b84  R E    0x10000
  LOAD           0x0000000001380000 0xffff800011380000 0xffff800011380000
                 0x00000000002f7200 0x000000000037155c  RW     0x10000
  NOTE           0x00000000012eeb90 0xffff8000112deb90 0xffff8000112deb90
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10

 Section to Segment mapping:
  Segment Sections...
   00     .head.text .text .got.plt .rodata .pci_fixup __ksymtab __ksymtab_gpl __ksymtab_strings __param __modver __ex_table .notes 
   01     .init.text .exit.text .altinstructions 
   02     .init.data .data..percpu .hyp.data..percpu .rela.dyn .data __bug_table .mmuoff.data.write .mmuoff.data.read .pecoff_edata_padding .bss 
   03     .notes 
   04   

与我们猜想的一样,内核镜像ELF的入口地址果然就是0xFFFF 8000 1000 0000,这样bootloader先加载vmlinux到物理内存之后,然后根据ELF的头部信息解析ELF文件,找到入口地址和所属的.head.test代码段,跳入该地址所在的代码段开始执行,实际上是从head.S的__HEAD位置开始执行。

但是有一个问题,各种Linux硬件的内存大小不一样,很大的概率没有该物理地址,bootloader很可能把镜像加载到一个空闲的合适大小的物理地址上,所以内核一开始执行的时候,虚拟地址和物理地址不同,这就导致了.head.text这个段是地址无关的代码段,这也就是内核把这个段单独划分出来的原因。

参考

参考1

第二章 ARM64在Linux内核中的实现
https://blog.csdn.net/weixin_39247141/article/details/125514337

参考2

kernel启动流程-head.S的执行_1.概述

https://blog.csdn.net/jasonactions/article/details/111190923

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值