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

news/2024/5/9 11:08:06

文章目录

  • 准备工作
  • 一、 参数
  • 二、 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, #0x16b	stext
#elseb	stext				// branch to kernel start, magic.long	0				// reserved
#endifle64sym	_kernel_offset_le		// Image load offset from start of RAM, little-endianle64sym	_kernel_size_le			// Effective size of kernel image, little-endianle64sym	_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_TEXTSOFTIRQENTRY_TEXTENTRY_TEXTTEXT_TEXTSCHED_TEXTCPUIDLE_TEXTLOCK_TEXTKPROBES_TEXTHYPERVISOR_TEXTIDMAP_TEXTHIBERNATE_TEXTTRAMP_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_DATAINIT_SETUP(16)INIT_CALLSCON_INITCALLINIT_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.mrgr.cn/p/03626213

相关文章

dwc3控制器是怎么处理otg

概念 在OTG中&#xff0c;初始主机设备称为A设备&#xff0c;外设称为B设备。可用电缆的连接方式来决定初始角色。两用设备使用新型Mini-AB插座&#xff0c;从而使Mini-A插头、Mini-B插头和Mini-AB插座增添了第5个引脚&#xff08;ID&#xff09;&#xff0c;以用于识别不同的…

存储器数据恢复相关知识

讲述硬盘基本结构及其储存理论,介绍如何恢复常用存储器数据。目录目录理论知识 硬盘如何储存数据? 磁道和扇区简介 盘面号 磁道 柱面 扇区 硬盘如何读写数据? 数据删除原理 数据如何丢失的? 人为原因造成的数据丢失: 自然灾害造成的数据丢失: 软件原因造成…

ARM学习(26)链接库的依赖查看

笔者今天来聊一下查看链接库的依赖。 通常情况下&#xff0c;运行一个可执行文件的时候&#xff0c;可能会出现找不到依赖库的情况&#xff0c;比如图下这种情况&#xff0c;可以看到是缺少了license.dll或者libtest.so&#xff0c;所以无法运行。怎么知道它到底缺少什么dll呢&…

构建RAG应用-day05: 如何评估 LLM 应用 评估并优化生成部分 评估并优化检索部分

评估 LLM 应用 1.一般评估思路 首先,你会在一到三个样本的小样本中调整 Prompt ,尝试使其在这些样本上起效。 随后,当你对系统进行进一步测试时,可能会遇到一些棘手的例子,这些例子无法通过 Prompt 或者算法解决。 最终,你会将足够多的这些例子添加到你逐步扩大的开发集中…

android脱壳第二发:grpc-dumpdex加修复

上一篇我写的dex脱壳&#xff0c;写到银行类型的app的dex修复问题&#xff0c;因为dex中被抽取出来的函数的code_item_off 的偏移所在的内存&#xff0c;不在dex文件范围内&#xff0c;所以需要进行一定的修复&#xff0c;然后就停止了。本来不打算接着搞得&#xff0c;但是写了…

ELK 日志分析系统(二)

一、ELK Kibana 部署 1.1 安装Kibana软件包 #上传软件包 kibana-5.5.1-x86_64.rpm 到/opt目录 cd /opt rpm -ivh kibana-5.5.1-x86_64.rpm 1.2 设置 Kibana 的主配置文件 vim /etc/kibana/kibana.yml --2--取消注释&#xff0c;Kiabana 服务的默认监听端口为5601 server.po…

简单的jmeter脚本自动化

1、创建线程组&#xff0c;定义自定义变量&#xff0c;保存请求默认值 2、用csv编写测试用例 备注&#xff1a;如果单元格内本身就有引号&#xff0c;则格式会有点小问题&#xff0c;不能直接修改为csv 用txt打开后 有引号的需要在最外层多包一层引号&#xff0c;每个引号前…

SpringBoot+vue开发记录(二)

说明&#xff1a;本篇文章的主要内容为SpringBoot开发中后端的创建 项目创建: 1. 新建项目&#xff1a; 如下&#xff0c;这样简单创建就行了&#xff0c;JDK什么的就先17&#xff0c;当然1.8也是可以的&#xff0c;后面可以改。 这样就创建好了&#xff1a; 2. pom.xml…

Golang | Leetcode Golang题解之第44题通配符匹配

题目&#xff1a; 题解&#xff1a; func isMatch(s string, p string) bool {for len(s) > 0 && len(p) > 0 && p[len(p)-1] ! * {if charMatch(s[len(s)-1], p[len(p)-1]) {s s[:len(s)-1]p p[:len(p)-1]} else {return false}}if len(p) 0 {retur…

go的编译以及运行时环境

开篇 很多语言都有自己的运行时环境&#xff0c;go自然也不例外&#xff0c;那么今天我们就来讲讲go语言的运行时环境&#xff01; 不同语言的运行时环境对比 我们都知道Java的运行时环境是jvm &#xff0c;javascript的运行时环境是浏览器内核 Java -->jvm javascript…

基于Springboot的网课管理系统

基于SpringbootVue的网课管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 课程表 论坛交流 学校公告 后端 学生管理 教师管理 班级管理 课程分类管理…

TODO -蓝桥杯2018年A组-付账问题

0.题目 题目描述 几个人一起出去吃饭是常有的事。但在结帐的时候,常常会出现一些争执。 现在有 \(n\) 个人出去吃饭,他们总共消费了 \(S\) 元。其中第 \(i\) 个人带了 \(a_i\) 元。幸运的是,所有人带的钱的总数是足够付账的,但现在问题来了:每个人分别要出多少钱呢? 为了…

【TCP:可靠数据传输,快速重传,流量控制,TCP流量控制】

文章目录 可靠数据传输TCP&#xff1a;可靠数据传输TCP发送方事件快速重传流量控制TCP流量控制 可靠数据传输 TCP&#xff1a;可靠数据传输 TCP在IP不可靠服务的基础上建立了rdt 管道化的报文段 GBN or SR 累计确认&#xff08;像GBN&#xff09;单个重传定时器&#xff08;像…

小伙伴:我是专升本,能不写在简历里吗?

大家好,我是树哥。 最近我推出了简历辅导服务(详见:500 块就能获得 10 年的行业经验,太赚了!),有一位同学找我做了简历辅导。 在阅读他的简历的时候,我发现他的学历没有写入学时间和毕业时间,感觉不是很直观,于是让他补全一下。小伙伴回复说:我是专升本的,本科只有…

JetBrains PhpStorm v2024.1 安装教程 (PHP集成开发IDE)

前言 PhpStorm是由JetBrains推出的一款轻量级集成开发环境&#xff0c;专为PHP开发者而设计。该软件融合了智能的HTML/CSS/JavaScript/PHP编辑器、代码质量分析工具、版本控制系统集成&#xff08;包括SVN和GIT&#xff09;、调试和测试等功能。除此之外&#xff0c;PhpStorm还…

PyQt介绍——动画使用详解之QPropertyAnimation

一、继承关系 PyQt5的动画框架是QAbstractAnimation&#xff0c;它是一个抽象类&#xff0c;不能直接使用&#xff0c;需要使用它的子类。它的类结构如下&#xff1a; QAbstractAnimation&#xff1a;抽象动画&#xff0c;是所有动画的基类&#xff0c;不能直接使用。 QVariant…

Jetpack Compose 中如何实现全面屏

看问题本质,设置全面屏,是系统窗口的行为,与 View 和 Compose 有什么关系呢? 所以,原理和传统 View 视图是一样的,甚至 Api 都是一模一样的,不熟悉的可以看我之前的文章。传送门: Android 全面屏体验 那为什么还要写这篇文章呢?主要是在 Compose 中写法上的一些区别,…

论文解读:Label Hallucination for Few-Shot Classification

文章汇总 动机 本文的一个思想就是&#xff1a;尽管新类的标签并不能“恰如其分”地表示基数据集中的样本&#xff0c;但是很多基数据集的样本会包含与新类中相似的对象&#xff0c;例如&#xff0c;基数据集中的老虎和新类中的猫有相似的特征&#xff0c;那么就有60%的概率将…

【CSS】使用 scroll snap 实现页面的垂直大屏滚动

CSS 属性 scroll-snap-type 设置了在有滚动容器的情形下吸附至吸附点的严格程度。 scroll-snap-type 使用 scroll snap 也可以用于垂直滚动&#xff0c;全屏展示就是一个很好的例子: <main><section class"section section-1"></section><sect…

4.26文件上传学习

文件上传,绕过,验证,检测一、文件上传 概念:(不赘述转web安全文件上传)[[9.6-9.7基础和过滤方式]] 前置知识:(除解析漏洞)后门代码需要以特定格式后缀解析,不能以图片后缀解析; 知识点 1、文件上传-前端验证 直接修改前端js代码,文件上传格式; 2、黑白名单 3、use…