arm架构Linux5.0内核连接脚本文件vmlinux.lds.S分析

文章目录

  • 准备工作
  • 一、 参数
  • 二、 SECTIONS段
    • 2.1 DISCARD节
    • 2.2 .head.text节
    • 2.3 .text节
    • 2.4 .rodata节
    • 2.5 EXCEPTION_TABLE
    • 2.6 NOTES
    • 2.7 idmap_pg_dir 地址
    • 2.8 tramp_pg_dir 地址
    • 2.9 swapper_pg_dir 地址
    • 2.10 .init相关的节
      • 2.10.1 .init.text 节
      • 2.10.2 .exit.text 节
      • 2.10.3 .altinstructions和.altinstr_replace
      • 2.10.4 .init.data
      • 2.10.5 .exit.data
      • 2.10.6 PERCPU_SECTION节
      • 2.10.7 .rela.dyn节
    • 2.11 .data
      • 2.11.1 _sdata
      • 2.11.2 .mmuoff.data.write和.mmuoff.data.read
    • 2.12 BSS_SECTION
    • 2.13 init_pg_dir 地址
    • 2.14 STABS_DEBUG
    • 2.15 HEAD_SYMBOLS
  • 三、 断言

vmlinux.lds.S 是 Linux 内核中用于链接的脚本文件,用于定义内核对象文件的内存布局,即它告诉链接器如何将不同的内核代码段和数据段组合成一个最终的内核映像 vmlinux。编译内核源码生成内核文件的过程分两步,一个是“编译”,另一个是“链接”的过程,vmlinux.lds.S要做的就是告诉编译器如何链接编译好的各个内核.o文件,从而生成vmlinux文件。

准备工作

我们需要准备好vmlinux.lds.S文件,文件的位置是arch/arm64/kernel/vmlinux.lds.S。我们还可以查看该文件预处理后的连接文件,在编译完linux内核会自动生成文件是arch/arm64/kernel/vmlinux.lds。我们还可以使用下面的命令读取 vmlinux 文件中的各段地址,协助我们分析:

readelf -S vmlinux > vmlinux_elf.txt

一、 参数

我们先直接查看预处理好的vmlinux.lds文件,可以看到开头就是下面这几行代码,这些是连接文件的参数,在vmlinux.lds.S中也可以找到一模一样的,只是放在了不同的行中而已:

OUTPUT_ARCH(aarch64)
ENTRY(_text)
jiffies = jiffies_64;
PECOFF_FILE_ALIGNMENT = 0x200;
  1. OUTPUT_ARCH(aarch64):这行指定输出文件的目标架构是 aarch64,即 64 位 ARM 架构。这是告诉链接器生成适用于 ARM 64 位处理器的内核映像。

  2. ENTRY(_text):这行指定了内核的入口点是 _text 符号。_text 通常是一个在内核代码中定义的标签,指向内核启动时第一条要执行的指令。这意味着当内核加载到内存并跳转到它的入口点时,它将开始执行 _text 标签指定的代码。

  3. jiffies = jiffies_64;: 在标准的 Linux 内核构建系统中,jiffies 作为变量通常是在 C 代码中定义和使用的。这行代码看起来像是一个赋值操作,但是在文件中没有使用到,个人猜测这是表示使用64位的jiffies。

  4. PECOFF_FILE_ALIGNMENT = 0x200;:这行设置了 PECOFF(Portable Executable for Console File Format)文件的对齐字节。PECOFF 是一种常用于 Windows 系统的文件格式,但在某些交叉编译场景下,Linux 内核可能需要以这种格式生成,以便于在某些环境中加载和执行。0x200 表示每个文件节(section)在磁盘上以 512 字节对齐,这是一个常见的对齐设置,用于优化文件的加载和内存访问性能。

二、 SECTIONS段

2.1 DISCARD节

	/DISCARD/ : {
		ARM_EXIT_DISCARD(EXIT_TEXT)
		ARM_EXIT_DISCARD(EXIT_DATA)
		EXIT_CALL
		*(.discard)
		*(.discard.*)
		*(.interp .dynamic)
		*(.dynsym .dynstr .hash .gnu.hash)
		*(.eh_frame)
	}

预处理后变成:

 /DISCARD/ : {
 
 
  *(.exitcall.exit)
  *(.discard)
  *(.discard.*)
  *(.interp .dynamic)
  *(.dynsym .dynstr .hash .gnu.hash)
  *(.eh_frame)
 }

结合英文注释和网上的一些言论,个人猜测/DISCARD/ 段是一个特殊的指令,用于指示链接器在生成最终的内核映像时应该忽略或丢弃某些段。这样子gcc可以通过配置来确定是否丢弃这些段,可以减小最终映像的大小或移除调试信息从而优化内核的加载时间和内存占用。如果确定丢弃,这意味着这些段不会出现在最终的程序或内核的内存映像中。

2.2 .head.text节

	. = KIMAGE_VADDR + TEXT_OFFSET;

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

预处理后变成:

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

**.head.text 😗*表示节的名字,后面的大括号表示节中包括的内容。
.表示当前位置,通过宏查找,在arch/arm64/include/asm/memory.h文件中,我们计算出KIMAGE_VADDR 大小为0xFFFF000010000000。还知道了一些小知识,在高端虚拟内存的最低地址开始是0xFFFF000000000000,从0xFFFF000000000000到0xFFFF000008000000是预留出来存放BPF JIT的,大小是128M。从0xFFFF000008000000到0xFFFF000010000000是预留出来存放各种ko模块的,大小也是128M。TEXT_OFFSET是512K,TEXT_OFFSET是arm64端口早期的历史产物,当时引导协议基本上是“将此映像复制到内存+ 512k的基础”,为我们提供512 KB的保证BSS空间来放置交换器页表。在linuxv3.10版本已经携带了TEXT_OFFSET的实际值,以允许引导加载程序动态地发现它,而不是将其硬编码为512 KB。在linux内核v5.7版本已经默认设置位0了。在linux内核v5.7版本已经把TEXT_OFFSET移除了。所以我们计算得到当前位置是0xFFFF000010080000。这跟我们读取vmlinux各段地址的vmlinux_elf.txt文件记录是一致的。

在链接脚本中,KEEP 是一个宏,它用于告诉链接器保留匹配的节(sections)内容,在这个例子中,KEEP 宏确保了所有标记为 .head.text 的节都会被保留在最终的内核映像中。linux内核是这样子标记.head.text段的,在arch/x86/kernel/head64.c文件中:

#define __head  __section(.head.text)

__head 在arch/arm64/kernel/head.S文件就用上了,表示下面这一段代码都是标记为 .head.text 的节,会放到linux内核映像的0xFFFF000010080000这个位置。

	__HEAD
_head:
	/*
	 * DO NOT MODIFY. Image header expected by Linux boot-loaders.
	 */
#ifdef CONFIG_EFI
	/*
	 * This add instruction has no meaningful effect except that
	 * its opcode forms the magic "MZ" signature required by UEFI.
	 */
	add	x13, x18, #0x16
	b	stext
#else
	b	stext				// branch to kernel start, magic
	.long	0				// reserved
#endif
	le64sym	_kernel_offset_le		// Image load offset from start of RAM, little-endian
	le64sym	_kernel_size_le			// Effective size of kernel image, little-endian
	le64sym	_kernel_flags_le		// Informative flags, little-endian
	.quad	0				// reserved
	.quad	0				// reserved
	.quad	0				// reserved
	.ascii	ARM64_IMAGE_MAGIC		// Magic number
#ifdef CONFIG_EFI
	.long	pe_header - _head		// Offset to the PE header.

关于表示当前位置的"."和各个节的内容,后面就不再详细说明了。

2.3 .text节

	.text : {			/* Real text segment		*/
		_stext = .;		/* Text and read-only data	*/
			__exception_text_start = .;
			*(.exception.text)
			__exception_text_end = .;
			IRQENTRY_TEXT
			SOFTIRQENTRY_TEXT
			ENTRY_TEXT
			TEXT_TEXT
			SCHED_TEXT
			CPUIDLE_TEXT
			LOCK_TEXT
			KPROBES_TEXT
			HYPERVISOR_TEXT
			IDMAP_TEXT
			HIBERNATE_TEXT
			TRAMP_TEXT
			*(.fixup)
			*(.gnu.warning)
		. = ALIGN(16);
		*(.got)			/* Global offset table		*/
	}
	. = ALIGN(SEGMENT_ALIGN);
	_etext = .;			/* End of text section */

预处理后变成:

 .text : {
  _stext = .;
   __exception_text_start = .;
   *(.exception.text)
   __exception_text_end = .;
   . = ALIGN(8); __irqentry_text_start = .; *(.irqentry.text) __irqentry_text_end = .;
   . = ALIGN(8); __softirqentry_text_start = .; *(.softirqentry.text) __softirqentry_text_end = .;
   . = ALIGN(8); __entry_text_start = .; *(.entry.text) __entry_text_end = .;
   . = ALIGN(8); *(.text.hot .text .text.fixup .text.unlikely) *(.text..refcount) *(.ref.text)
   . = ALIGN(8); __sched_text_start = .; *(.sched.text) __sched_text_end = .;
   . = ALIGN(8); __cpuidle_text_start = .; *(.cpuidle.text) __cpuidle_text_end = .;
   . = ALIGN(8); __lock_text_start = .; *(.spinlock.text) __lock_text_end = .;
   . = ALIGN(8); __kprobes_text_start = .; *(.kprobes.text) __kprobes_text_end = .;
   . = ALIGN(0x00001000); __hyp_idmap_text_start = .; *(.hyp.idmap.text) __hyp_idmap_text_end = .; __hyp_text_start = .; *(.hyp.text) __hyp_text_end = .;
   . = ALIGN(0x00001000); __idmap_text_start = .; *(.idmap.text) __idmap_text_end = .;
   . = ALIGN(0x00001000); __hibernate_exit_text_start = .; *(.hibernate_exit.text) __hibernate_exit_text_end = .;
   . = ALIGN((1 << 12)); __entry_tramp_text_start = .; *(.entry.tramp.text) . = ALIGN((1 << 12)); __entry_tramp_text_end = .;
   *(.fixup)
   *(.gnu.warning)
  . = ALIGN(16);
  *(.got)
 }
  . = ALIGN(0x00010000);
 _etext = .;

_stext记录了当前的内存地址,这里表示 .text是紧跟在 .head.text节后面。最后的_etext 是记录当前节的结束地址
**. = ALIGN(0x00010000);**表示当前地址进行64K的对齐。
我们可以把这个代码段分为4个部分看,第一部分是寻找合适的地址,一般是使用ALIGN宏进行内存对齐,也有一些是直接跟在上一个段后面;第二部分是记录段起始地址到某个标记中;第三部分是加载各个代码段;第四部分是记录段结束地址到标记中。我们看到 .text节中有好多段,第一个是arm64异常处理代码段,比如内存异常do_mem_abort、栈异常do_sp_pc_abort、用户态指令访问异常do_el0_ia_bp_hardening和未定义异常do_undefinstr等。其他自己看。查看方法是使用grep 搜索,具体如下:

rlk@rlk:runninglinuxkernel_5.0$ grep -nr exception.text
...
arch/arm64/include/asm/exception.h:25:#define __exception	__attribute__((section(".exception.text")))
...
rlk@rlk:runninglinuxkernel_5.0$ grep -nrw __exception
arch/arm64/mm/fault.c:741:asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
arch/arm64/mm/fault.c:759:asmlinkage void __exception do_el0_irq_bp_hardening(void)
arch/arm64/mm/fault.c:765:asmlinkage void __exception do_el0_ia_bp_hardening(unsigned long addr,
arch/arm64/mm/fault.c:782:asmlinkage void __exception do_sp_pc_abort(unsigned long addr,
arch/arm64/mm/fault.c:827:asmlinkage int __exception do_debug_exception(unsigned long addr,
arch/arm64/kernel/traps.c:413:asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
arch/arm64/kernel/traps.c:684:asmlinkage void __exception do_cp15instr(unsigned int esr, struct pt_regs *regs)
arch/arm64/kernel/traps.c:724:asmlinkage void __exception do_sysinstr(unsigned int esr, struct pt_regs *regs)

2.4 .rodata节

RO_DATA(PAGE_SIZE)		/* everything from this point to     */

预处理后变成:

 . = ALIGN(((1 << 12))); 
 .rodata : AT(ADDR(.rodata) - 0) { __start_rodata = .; *(.rodata) *(.rodata.*) __start_ro_after_init = .; *(.data..ro_after_init) . = ALIGN(8); __start___jump_table = .; KEEP(*(__jump_table)) __stop___jump_table = .; __end_ro_after_init = .; KEEP(*(__vermagic)) . = ALIGN(8); __start___tracepoints_ptrs = .; KEEP(*(__tracepoints_ptrs)) __stop___tracepoints_ptrs = .; *(__tracepoints_strings) } 
 .rodata1 : AT(ADDR(.rodata1) - 0) { *(.rodata1) } 
 .pci_fixup : AT(ADDR(.pci_fixup) - 0) { __start_pci_fixups_early = .; KEEP(*(.pci_fixup_early)) __end_pci_fixups_early = .; __start_pci_fixups_header = .; KEEP(*(.pci_fixup_header)) __end_pci_fixups_header = .; __start_pci_fixups_final = .; KEEP(*(.pci_fixup_final)) __end_pci_fixups_final = .; __start_pci_fixups_enable = .; KEEP(*(.pci_fixup_enable)) __end_pci_fixups_enable = .; __start_pci_fixups_resume = .; KEEP(*(.pci_fixup_resume)) __end_pci_fixups_resume = .; __start_pci_fixups_resume_early = .; KEEP(*(.pci_fixup_resume_early)) __end_pci_fixups_resume_early = .; __start_pci_fixups_suspend = .; KEEP(*(.pci_fixup_suspend)) __end_pci_fixups_suspend = .; __start_pci_fixups_suspend_late = .; KEEP(*(.pci_fixup_suspend_late)) __end_pci_fixups_suspend_late = .; } 
 .builtin_fw : AT(ADDR(.builtin_fw) - 0) { __start_builtin_fw = .; KEEP(*(.builtin_fw)) __end_builtin_fw = .; } 
 __ksymtab : AT(ADDR(__ksymtab) - 0) { __start___ksymtab = .; KEEP(*(SORT(___ksymtab+*))) __stop___ksymtab = .; } 
 __ksymtab_gpl : AT(ADDR(__ksymtab_gpl) - 0) { __start___ksymtab_gpl = .; KEEP(*(SORT(___ksymtab_gpl+*))) __stop___ksymtab_gpl = .; } 
 __ksymtab_unused : AT(ADDR(__ksymtab_unused) - 0) { __start___ksymtab_unused = .; KEEP(*(SORT(___ksymtab_unused+*))) __stop___ksymtab_unused = .; }
 __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - 0) { __start___ksymtab_unused_gpl = .; KEEP(*(SORT(___ksymtab_unused_gpl+*))) __stop___ksymtab_unused_gpl = .; } 
 __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - 0) { __start___ksymtab_gpl_future = .; KEEP(*(SORT(___ksymtab_gpl_future+*))) __stop___ksymtab_gpl_future = .; } 
 __kcrctab : AT(ADDR(__kcrctab) - 0) { __start___kcrctab = .; KEEP(*(SORT(___kcrctab+*))) __stop___kcrctab = .; } 
 __kcrctab_gpl : AT(ADDR(__kcrctab_gpl) - 0) { __start___kcrctab_gpl = .; KEEP(*(SORT(___kcrctab_gpl+*))) __stop___kcrctab_gpl = .; }
 __kcrctab_unused : AT(ADDR(__kcrctab_unused) - 0) { __start___kcrctab_unused = .; KEEP(*(SORT(___kcrctab_unused+*))) __stop___kcrctab_unused = .; } 
 __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - 0) { __start___kcrctab_unused_gpl = .; KEEP(*(SORT(___kcrctab_unused_gpl+*))) __stop___kcrctab_unused_gpl = .; }
 __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - 0) { __start___kcrctab_gpl_future = .; KEEP(*(SORT(___kcrctab_gpl_future+*))) __stop___kcrctab_gpl_future = .; } 
 __ksymtab_strings : AT(ADDR(__ksymtab_strings) - 0) { *(__ksymtab_strings) } 
 __init_rodata : AT(ADDR(__init_rodata) - 0) { *(.ref.rodata) } 
 __param : AT(ADDR(__param) - 0) { __start___param = .; KEEP(*(__param)) __stop___param = .; } 
 __modver : AT(ADDR(__modver) - 0) { __start___modver = .; KEEP(*(__modver)) __stop___modver = .; . = ALIGN(((1 << 12))); __end_rodata = .; } 
 . = ALIGN(((1 << 12)));

这里是只读数据段集合。**. = ALIGN(((1 << 12))); 表示地址进行4K对齐。对齐后.rodata节的地址是ffff0000118f0000,这是使用readelf读取出来的信息。RO_DATA展开后是18个rodate相关的节,每一个节使用AT指定了加载地址, AT(ADDR(.rodata) - 0) **表示加载到当前.rodata节的地址,也就是跟当前地址是一样的。这里没有指定运行地址,默认是当前地址。所以这里运行地址和加载地址是相同的,不需要额外的重定向。前面的节中没有使用AT指定加载地址,说明是和运行地址一样的。每一个节里面的内容就不再具体展开分析了。

2.5 EXCEPTION_TABLE

预处理后变成:

. = ALIGN(8); __ex_table : AT(ADDR(__ex_table) - 0) { __start___ex_table = .; KEEP(*(__ex_table)) __stop___ex_table = .; }

这个是存放arm64系统调用表代码段的,也就是系统调用进来的处理代码。从elf来看内存地址为0xffff000011bf7000。

2.6 NOTES

预处理后变成:

.notes : AT(ADDR(.notes) - 0) { __start_notes = .; KEEP(*(.note.*)) __stop_notes = .; }

从elf来看内存地址为0xffff000011bf9120。不知道是干嘛用的。

2.7 idmap_pg_dir 地址

	. = ALIGN(PAGE_SIZE);
	idmap_pg_dir = .;
	. += IDMAP_DIR_SIZE;

idmap_pg_dir是Linux 内核中是一个用于建立恒等映射的页全局目录基地址,它的主要作用是在内核启动的早期阶段为物理地址和虚拟地址提供直接映射。这意味着通过这个页表,虚拟地址和物理地址是相等的,从而允许内核在启用 MMU(内存管理单元)之前和之后无差别地访问物理内存。
**. = ALIGN(PAGE_SIZE);**表示当前地址进行4K对齐。
**. += IDMAP_DIR_SIZE;**表示占用内存大小为IDMAP_DIR_SIZE,计算得出结果为3个页。

2.8 tramp_pg_dir 地址

	tramp_pg_dir = .;
	. += PAGE_SIZE;

tramp_pg_dir 在 Linux 内核中是用于解决某些特定安全问题(如漏洞)的页表目录。它提供了一种方法来创建一个安全的过渡或“跳板”执行环境,以便在处理某些潜在的安全风险时,内核能够安全地过渡到一个隔离的、受控的执行上下文。

2.9 swapper_pg_dir 地址

	swapper_pg_dir = .;
	. += PAGE_SIZE;
	swapper_pg_end = .;

swapper_pg_dir 在 Linux 内核中是一个非常重要的数据结构,它指的是内核使用的一组页表的起始位置,这些页表构成了内核的全局页目录。这里预留了页全局目录页表的内存。

2.10 .init相关的节

	. = ALIGN(SEGMENT_ALIGN);
	__init_begin = .;
	__inittext_begin = .;
...
	. = ALIGN(PAGE_SIZE);
	__inittext_end = .;
	__initdata_begin = .;
...
	. = ALIGN(SEGMENT_ALIGN);
	__initdata_end = .;
	__init_end = .;

从elf读取到.init.text的地址是0xffff000011c00000。可以分为代码段和数据段。

2.10.1 .init.text 节

这是init的第一个代码段:

INIT_TEXT_SECTION(8)

预处理后变成:

. = ALIGN(8); .init.text : AT(ADDR(.init.text) - 0) { _sinittext = .; *(.init.text .init.text.*) *(.text.startup) *(.meminit.text*) _einittext = .; }

.init.text的内存位置是0xffff000011c00000,这部分是存放内核启动相关代码的。

2.10.2 .exit.text 节

这是init的第二个代码段:

	.exit.text : {
		ARM_EXIT_KEEP(EXIT_TEXT)
	}

预处理后变成:

 .exit.text : {
  *(.exit.text) *(.text.exit) *(.memexit.text)
 }

.exit.text的内存位置是ffff000011cc37d0,这部分是存放内核退出相关代码的。

2.10.3 .altinstructions和.altinstr_replace

这是init的第三个代码段:

	. = ALIGN(4);
	.altinstructions : {
		__alt_instructions = .;
		*(.altinstructions)
		__alt_instructions_end = .;
	}
	.altinstr_replacement : {
		*(.altinstr_replacement)
	}

代码段放在arch/arm64/include/asm/alternative.h文件中,代码的使用具体看arch/arm64/kernel/alternative.c文件,作用是实现内核代码的动态指令替换功能。这个功能允许内核在运行时根据 CPU 的特性来优化或修改执行的指令,而不需要重启系统或更换内核。

2.10.4 .init.data

这是init的第一个数据段:

	.init.data : {
		INIT_DATA
		INIT_SETUP(16)
		INIT_CALLS
		CON_INITCALL
		INIT_RAM_FS
		*(.init.rodata.* .init.bss)	/* from the EFI stub */
	}

预处理后变成:

 .init.data : {
  KEEP(*(SORT(___kentry+*))) *(.init.data init.data.*) *(.meminit.data*) 
  . = ALIGN(8); __start_mcount_loc = .; KEEP(*(__mcount_loc)) KEEP(*(__patchable_function_entries)) __stop_mcount_loc = .; *(.init.rodata .init.rodata.*) 
  . = ALIGN(8); __start_ftrace_events = .; KEEP(*(_ftrace_events)) __stop_ftrace_events = .; __start_ftrace_eval_maps = .; KEEP(*(_ftrace_eval_map)) __stop_ftrace_eval_maps = .; 
  . = ALIGN(8); __start_syscalls_metadata = .; KEEP(*(__syscalls_metadata)) __stop_syscalls_metadata = .; 
  . = ALIGN(8); __start_kprobe_blacklist = .; KEEP(*(_kprobe_blacklist)) __stop_kprobe_blacklist = .; *(.meminit.rodata) 
  . = ALIGN(8); __clk_of_table = .; KEEP(*(__clk_of_table)) KEEP(*(__clk_of_table_end)) 
  . = ALIGN(8); __reservedmem_of_table = .; KEEP(*(__reservedmem_of_table)) KEEP(*(__reservedmem_of_table_end)) 
  . = ALIGN(8); __timer_of_table = .; KEEP(*(__timer_of_table)) KEEP(*(__timer_of_table_end)) 
  . = ALIGN(8); __cpu_method_of_table = .; KEEP(*(__cpu_method_of_table)) KEEP(*(__cpu_method_of_table_end)) 
  . = ALIGN(8); __cpuidle_method_of_table = .; KEEP(*(__cpuidle_method_of_table)) KEEP(*(__cpuidle_method_of_table_end)) 
  . = ALIGN(32); __dtb_start = .; KEEP(*(.dtb.init.rodata)) __dtb_end = .; 
  . = ALIGN(8); __irqchip_of_table = .; KEEP(*(__irqchip_of_table)) KEEP(*(__irqchip_of_table_end)) 
  . = ALIGN(8); __earlycon_table = .; KEEP(*(__earlycon_table)) __earlycon_table_end = .;
  . = ALIGN(16); __setup_start = .; KEEP(*(.init.setup)) __setup_end = .;
  __initcall_start = .; KEEP(*(.initcallearly.init)) __initcall0_start = .; KEEP(*(.initcall0.init)) KEEP(*(.initcall0s.init)) __initcall1_start = .; KEEP(*(.initcall1.init)) KEEP(*(.initcall1s.init)) __initcall2_start = .; KEEP(*(.initcall2.init)) KEEP(*(.initcall2s.init)) __initcall3_start = .; KEEP(*(.initcall3.init)) KEEP(*(.initcall3s.init)) __initcall4_start = .; KEEP(*(.initcall4.init)) KEEP(*(.initcall4s.init)) __initcall5_start = .; KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init)) __initcallrootfs_start = .; KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init)) __initcall6_start = .; KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init)) __initcall7_start = .; KEEP(*(.initcall7.init)) KEEP(*(.initcall7s.init)) __initcall_end = .;
  __con_initcall_start = .; KEEP(*(.con_initcall.init)) __con_initcall_end = .;
 
  *(.init.rodata.* .init.bss)
 }

.init.data包括了init.data、mcount相关、ftrace的event相关、kprobe相关、设备树、irqchip相关、各个级别的initcall、启动使用的只读数据段和bss段。

2.10.5 .exit.data

这是init的第二个数据段:

 .exit.data : {
  *(.exit.data .exit.data.*) *(.fini_array .fini_array.*) *(.dtors .dtors.*) *(.memexit.data*) *(.memexit.rodata*)
 }

这里主要是存放了initcall的退出函数。

2.10.6 PERCPU_SECTION节

这是init的第三个数据段:

	PERCPU_SECTION(L1_CACHE_BYTES)

预处理后变成:

 . = ALIGN((1 << 12)); 
 .data..percpu : AT(ADDR(.data..percpu) - 0) { 
 __per_cpu_load = .; __per_cpu_start = .; *(.data..percpu..first) 
 . = ALIGN((1 << 12)); *(.data..percpu..page_aligned) 
 . = ALIGN((1 << (6))); *(.data..percpu..read_mostly) 
 . = ALIGN((1 << (6))); *(.data..percpu) *(.data..percpu..shared_aligned) 
 __per_cpu_end = .; }

这里预留了是存放每cpu数据的内存。放在0xffff000011ddf000上,占用了大约82K。

2.10.7 .rela.dyn节

这是init的第四个数据段:

	.rela.dyn : ALIGN(8) {
		*(.rela .rela*)
	}
	__rela_offset	= ABSOLUTE(ADDR(.rela.dyn) - KIMAGE_VADDR);
	__rela_size	= SIZEOF(.rela.dyn);

不知道干嘛用的。

2.11 .data

	_data = .;
...
	_edata = .;

这里是内核的数据段。放在ffff000012090000上。数据段有分为几个部分,包括:_sdata、.mmuoff.data.write和.mmuoff.data.read。

2.11.1 _sdata

这里是.data的第一个数据段:

	_sdata = .;
	RW_DATA_SECTION(L1_CACHE_BYTES, PAGE_SIZE, THREAD_ALIGN)

预处理后变成:

 _data = .;
 _sdata = .;
 . = ALIGN((1 << 12)); .data : AT(ADDR(.data) - 0) { 
 . = ALIGN((((1)) << (15 + 0))); __start_init_task = .; init_thread_union = .; init_stack = .; KEEP(*(.data..init_task)) KEEP(*(.data..init_thread_info)) . = __start_init_task + (((1)) << (15 + 0)); __end_init_task = .; 
 . = ALIGN((1 << 12)); __nosave_begin = .; *(.data..nosave) 
 . = ALIGN((1 << 12)); __nosave_end = .; 
 . = ALIGN((1 << 12)); *(.data..page_aligned) 
 . = ALIGN((1 << (6))); *(.data..cacheline_aligned) 
 . = ALIGN((1 << (6))); *(.data..read_mostly) 
 . = ALIGN((1 << (6))); *(.xiptext) *(.data) *(.ref.data) *(.data..shared_aligned) *(.data.unlikely) __start_once = .; *(.data.once) __end_once = .; 
 . = ALIGN(32); *(__tracepoints) 
 . = ALIGN(8); __start___verbose = .; KEEP(*(__verbose)) __stop___verbose = .; __start___trace_bprintk_fmt = .; KEEP(*(__trace_printk_fmt)) __stop___trace_bprintk_fmt = .; 
 . = ALIGN(32); __start__bpf_raw_tp = .; KEEP(*(__bpf_raw_tp_map)) __stop__bpf_raw_tp = .; __start___tracepoint_str = .; KEEP(*(__tracepoint_str)) __stop___tracepoint_str = .; CONSTRUCTORS } 

这里是内核的静态数据段。放在ffff000012090000上。

2.11.2 .mmuoff.data.write和.mmuoff.data.read

这里是.data的第二个数据段:

	.mmuoff.data.write : ALIGN(SZ_2K) {
		__mmuoff_data_start = .;
		*(.mmuoff.data.write)
	}
	. = ALIGN(SZ_2K);
	.mmuoff.data.read : {
		*(.mmuoff.data.read)
		__mmuoff_data_end = .;
	}

如果遇到了一种情况,就是在关闭MMU的情况下写入数据,但在打开MMU的情况下读取数据,则需要使缓存线路无效,从而丢弃缓存中的数据的cache回写颗粒。将需要这种类型维护的部分保持在其自己的缓存回写颗粒(CWG)区域中,这样缓存维护操作就不会干扰相邻的数据。这里就是预留了内存空间,从elf输出来看, .mmuoff.data.write预留12个bit,.mmuoff.data.read预留8个bit。

2.12 BSS_SECTION

BSS_SECTION(0, 0, 0)

预处理后变成:

 . = ALIGN(0); 
 __bss_start = .; 
 . = ALIGN(0); 
 .sbss : AT(ADDR(.sbss) - 0) { 
 	*(.dynsbss) *(.sbss) *(.scommon) 
 } 
 . = ALIGN(0); 
 .bss : AT(ADDR(.bss) - 0) { 
	 *(.bss..page_aligned) *(.dynbss) *(.bss) *(COMMON) 
 } 
 . = ALIGN(0); __bss_stop = .;

BSS段主要用于存储程序中未初始化的全局变量和静态变量。

2.13 init_pg_dir 地址

. = ALIGN(PAGE_SIZE);
init_pg_dir = .;
. += INIT_DIR_SIZE;
init_pg_end = .;

这是内核启动过程中建立粗粒度映射页表。init_pg_dir 是页全局目录页表基地址。INIT_DIR_SIZE是5个页大小。

2.14 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) }

2.15 HEAD_SYMBOLS

预处理后变成:

 _kernel_size_le_lo32 = (((_end - _text) & 0xffffffff) & 0xffffffff); 
 _kernel_size_le_hi32 = (((_end - _text) >> 32) & 0xffffffff); 
 _kernel_offset_le_lo32 = (((0x00080000) & 0xffffffff) & 0xffffffff); 
 _kernel_offset_le_hi32 = (((0x00080000) >> 32) & 0xffffffff); 
 _kernel_flags_le_lo32 = (((((0 << 0) | (((12 - 10) / 2) << (0 + 1)) | (1 << ((0 + 1) + 2)))) & 0xffffffff) & 0xffffffff); 
 _kernel_flags_le_hi32 = (((((0 << 0) | (((12 - 10) / 2) << (0 + 1)) | (1 << ((0 + 1) + 2)))) >> 32) & 0xffffffff);

这里仅仅是计算了一些变量的大小。

三、 断言

ASSERT(__hyp_idmap_text_end - (__hyp_idmap_text_start & ~(0x00001000 - 1)) <= 0x00001000,
 "HYP init code too big or misaligned")
ASSERT(__idmap_text_end - (__idmap_text_start & ~(0x00001000 - 1)) <= 0x00001000,
 "ID map text too big or misaligned")
ASSERT(__hibernate_exit_text_end - (__hibernate_exit_text_start & ~(0x00001000 - 1))
 <= 0x00001000, "Hibernate exit text too big or misaligned")
ASSERT((__entry_tramp_text_end - __entry_tramp_text_start) == (1 << 12),
 "Entry trampoline text too big")
ASSERT(_text == (((((((((0xffffffffffffffff)) - (((1)) << (48)) + 1) + (0)) + (0x08000000))) + (0x08000000))) + 0x00080000), "HEAD is misaligned")

这里的作用是避免一些代码或者数据越界了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/569641.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redux 状态持久化之 redux-persist 使用示例

同vuex一样&#xff0c;redux中的状态会在刷新浏览器后状态又恢复到初始状态&#xff0c;有些数据想在浏览器刷新后仍然是在最新的状态&#xff0c;不会丢失&#xff0c;就需要借助一些插件实现。本文通过 redux-persist 插件来实现Redux状态的持久化。 下面使用 redux-persis…

error while loading shared libraries: libaio.so.1: wrong ELF class: ELFCLASS32

这个错误的意思是编译对象需要32位的libaio库 centos版本执行以下命令检查系统有哪些libaio的版本 yum list libaio 如图&#xff0c;有两个版本&#xff0c;将两个版本都安装一下 yum install libaio.x86_64 再编译&#xff0c;成功

Whatsapp在中国下架了?这招教你解决!

今天有一个紧急的消息要告诉大家&#xff0c;根据最新的电信办要求&#xff0c;苹果手机的中国应用商店已经下架了WhatsApp&#xff01;这意味着&#xff0c;如果你的苹果设备是在中国大陆地区注册的&#xff0c;那么你将无法直接在App Store搜索到WhatsApp。 但是&#xff0c;…

ESD 防静电监控系统解决方案,提升工作环境安全性

ESD 防静电监控系统解决方案是一种专门针对静电防护的监控系统&#xff0c;通过实时监测静电情况&#xff0c;及时发现并处理可能存在的静电危险&#xff0c;保障设备和人员的安全。该解决方案包括静电检测设备、报警系统、防护设备等组成&#xff0c;有效地预防静电引起的火灾…

计算机中浮点数的表示

浮点数是计算机科学中用于表示实数的一种方法&#xff0c;它可以表示非常大或非常小的值。这种表示方式类似于科学记数法&#xff0c;由一个符号位、一个指数部分和一个尾数&#xff08;或称有效数字&#xff09;部分组成。 浮点数的组成 在最常用的IEEE 754标准中&#xff0…

Advanced RAG 03:运用 RAGAs 与 LlamaIndex 评估 RAG 应用

编者按&#xff1a;目前&#xff0c;检索增强生成&#xff08;Retrieval Augmented Generation&#xff0c;RAG&#xff09;技术已经广泛使用于各种大模型应用场景。然而&#xff0c;如何准确评估 RAG 系统的性能和效果&#xff0c;一直是业界和学界共同关注的重点问题。若无法…

Kafka 3.x.x 入门到精通(01)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;01&#xff09;——对标尚硅谷Kafka教程 1. Kafka入门1.1 概述1.1.1 初识Kafka1.1.2 消息队列1.1.3 生产者-消费者模式1.1.4 消息中间件对比1.1.5 ZooKeeper 1.2 快速上手1.2.1 环境安装1.2.1.1 安装Java8&#xff08;略&#xff09;1.2.1.2…

【南京工程学院×朗汀留学】部分录取案例合集

朗汀留学 X 南京工程学院 作为深耕留学的专业资深团队&#xff0c;朗汀留学成功帮助上千名学生出国留学。 在此我们将南京工程学院的部分留学案例作以总结&#xff0c;以供新生参考。再次恭喜所有 获得理想大学offer的学生们&#xff0c;你们的努力让梦想照进现实。 学校介绍…

2024年外贸独立站建设首选:WordPress引领市场,助力企业出海

随着全球经济的不断融合与发展&#xff0c;越来越多的企业开始关注海外市场&#xff0c;希望通过建设外贸独立站来扩大品牌影响力和销售额。在众多的内容管理系统&#xff08;CMS&#xff09;中&#xff0c;WordPress以其强大的功能、丰富的插件资源和用户友好的操作界面&#…

日志框架整合SpringBoot保姆级教程+日志文件拆分(附源码)

目录 介绍 日志概述 日志文件 调试日志 系统日志 日志框架 日志框架的作用 日志框架的价值 流行的日志框架 SLF4J日志门面 介绍 环境搭建简单测试 集成log4j logback Logback简介 Logback中的组件 Logback配置文件 日志输出格式 控制台输出日志 输出日志到…

演示在一台Windows主机上运行两个Mysql服务器(端口号3306 和 3307),安装步骤详解

目录 在一台Windows主机上运行两个Mysql服务器&#xff0c;安装步骤详解因为演示需要两个 MySQL 服务器终端&#xff0c;我只有一个 3306 端口号的 MySQL 服务器&#xff0c;所以需要再创建一个 3307 的。创建一个3307端口号的MySQL服务器1、复制 mysql 的安装目录2、修改my.in…

通过Bedrock Access Gateway解决方案快速访问Amazon Bedrock的多种大语言模型

Bedrock Access Gateway&#xff08;BAG&#xff09;解决方案提供了开箱即用、兼容 OpenAI 的代理功能&#xff0c;帮助用户轻松无缝地从 OpenAI 迁移到 Amazon Bedrock。 1. 概述 亚马逊云科技的 Amazon Bedrock 服务支持一系列领先的基础模型&#xff0c;为客户提供多种选择…

SpringCloud Alibaba--nacos简介和注册中心和登录

目录 一.理论基础 二.nacos 2.1 简介 2.2 安装 三.父项目 三.生产者 3.1 配置依赖 3.2 配置文件 3.3 启动类 3.4 控制类 四.消费者 4.1 配置依赖 4.2 配置文件 4.3 启动类 4.4 feign的接口 五.效果 六.负载均衡--权重算法 6.1重启nacos 6.2 设置权重 6.3 设…

【1431】java学习网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java 学习网站系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

【Django】django.core.exceptions.AppRegistryNotReady: Apps aren‘t loaded yet.

其中django后台manage.py入口程序报错&#xff0c;检索很多问题解决方案&#xff0c;这里记录下个人问题原因 1.django启动异常问题详情 django.core.exceptions.AppRegistryNotReady: Apps aren’t loaded yet. 2.问题原因 Python第三方包安装版本不一致或缺少依赖包&…

利用AI知识库,优化医保系统售后信息管理流程

在医疗行业中&#xff0c;传统知识库管理虽能整合医疗行业知识&#xff0c;但搜索和管理效率有限&#xff0c;导致医护人员难以高效利用。特别是面对医保系统等复杂系统时&#xff0c;他们常需依赖人工客服或繁琐的电子产品手册解决问题。而HelpLook AI知识库利用AI技术&#x…

拼多多面试题——力扣版测试用例纠错

最近我看到力扣上这个题目&#xff0c;用了三种方法&#xff0c;结果没有一种正确&#xff0c;我就纳闷儿了&#xff0c;为何总有一个测试用例过不了&#xff0c;结果我发现这个测试用例确实有问题啊。。。。。 题目&#xff1a; 表&#xff1a;Logs ----------------------…

Anon Network:基于 Ator Protocol 的 DePIN 匿名互联网

Anon Network正在以Ator Protocol为基础构建世界上最大的Web3隐私互联网生态&#xff0c;其旨在基于DePIN网络&#xff08;Ator protocol&#xff09;&#xff0c;通过激励体系构建一个自下而上、自我维持且可持续、不依赖于任何三方实体且完全匿名的完备互联体系。在该体系中&…

在html页面中使用Vue3和Element-Plus实现基金成本计算器

背景 周边朋友都说基金亏麻了&#xff0c;有些 “鸡” 到底部了&#xff0c;想要补个仓&#xff0c;但是又不知道要投入多少才能拉低到心里预期的成本&#xff0c;并且每只 “鸡” 都得自己输入计算&#xff0c;很麻烦&#xff0c;所以本着偷懒的原则&#xff0c;做了下面的这个…

机器学习中常见的数据分析,处理方式(以泰坦尼克号为例)

数据分析 读取数据查看数据各个参数信息查看有无空值如何填充空值一些特殊字段如何处理读取数据查看数据中的参数信息实操具体问题具体分析年龄问题 重新划分数据集如何删除含有空白值的行根据条件删除一些行查看特征和标签的相关性 读取数据 查看数据各个参数信息 查看有无空…
最新文章