[Linux]学习笔记系列 -- [arm][lds]

发布于:2025-08-10 ⋅ 阅读:(18) ⋅ 点赞:(0)


在这里插入图片描述

https://github.com/wdfk-prog/linux-study

include/asm-generic/vmlinux.lds.h

arch/arm/Makefile

生成vmlinux.lds

  1. 预处理

    • vmlinux.lds.S 文件首先会经过 C 预处理器(cpp)的处理。预处理器会处理文件中的宏定义、条件编译指令(如 #ifdef#include 等),并生成一个纯文本的链接脚本文件 vmlinux.lds
  2. 编译命令

    • 在内核构建过程中,Makefile 会调用 GCC 或 Clang 编译器来处理这个文件。具体的命令类似于:
      $(CC) -E -P -o vmlinux.lds vmlinux.lds.S
      
      其中:
      • $(CC) 是编译器命令(如 gccclang)。
      • -E 选项表示只运行预处理器,不进行编译。
      • -P 选项表示去掉预处理器生成的行号信息。
      • -o vmlinux.lds 指定输出文件名为 vmlinux.lds
      • vmlinux.lds.S 是输入文件。
  3. 生成 vmlinux.lds

    • 经过预处理后,vmlinux.lds.S 文件中的所有宏定义和条件编译指令都会被展开和处理,生成最终的 vmlinux.lds 文件。这个文件是一个纯文本文件,包含了链接器需要的所有指令,用于定义内核镜像的内存布局。
  4. 链接阶段

    • 最终,生成的 vmlinux.lds 文件会被传递给链接器(如 ld),用于链接内核各个部分,生成最终的内核镜像文件 vmlinux。

通过这种方式,vmlinux.lds.S 文件中的内容经过预处理后生成 vmlinux.lds,再由链接器使用这个链接脚本来生成最终的内核镜像。

CONFIG_CPU_BIG_ENDIAN

ifeq ($(CONFIG_CPU_BIG_ENDIAN),y)
KBUILD_CPPFLAGS	+= -mbig-endian
CHECKFLAGS	+= -D__ARMEB__
KBUILD_LDFLAGS	+= -EB
else
KBUILD_CPPFLAGS	+= -mlittle-endian
CHECKFLAGS	+= -D__ARMEL__
KBUILD_LDFLAGS	+= -EL
endif

arch/arm/include/asm/vmlinux.lds.h

ARM_CPU_DISCARD ARM_CPU_KEEP 热插拔CPU时需要保持或丢弃的段

#ifdef CONFIG_HOTPLUG_CPU
#define ARM_CPU_DISCARD(x)
#define ARM_CPU_KEEP(x)		x
#else
#define ARM_CPU_DISCARD(x)	x
#define ARM_CPU_KEEP(x)
#endif

CONFIG_HOTPLUG_CPU 热插拔CPU

  • CONFIG_HOTPLUG_CPU 是一个内核配置选项,用于启用或禁用 CPU 热插拔功能。热插拔允许在运行时添加或移除 CPU,而不需要重启系统。
  • 在 ARM 架构的 Linux 内核中,.ARM.exidx 和 .ARM.extab 段用于异常处理和栈回溯(unwinding)。这些段包含了异常索引表(exception index table)和异常表(exception table),它们对于处理异常和执行栈回溯是必需的。
  • 当启用 CONFIG_HOTPLUG_CPU 配置选项时,内核需要支持 CPU 的热插拔功能,这意味着在运行时可以动态地添加或移除 CPU。为了支持这种动态变化,内核必须确保在 CPU 被移除或添加时,异常处理和栈回溯机制能够正确工作。这就需要保留与 CPU 相关的异常处理信息。
  • 具体来说,.ARM.exidx.cpuexit.text 和 .ARM.extab.cpuexit.text 段包含了与 CPU 退出(cpuexit)相关的异常处理信息和栈回溯信息。当 CPU 被热插拔时,这些信息是必需的,以确保系统能够正确处理与该 CPU 相关的异常和栈回溯。因此,当启用 CONFIG_HOTPLUG_CPU 时,这些段不会被丢弃。

ARM_EXIT_KEEP ARM_EXIT_DISCARD

#if (defined(CONFIG_SMP_ON_UP) && !defined(CONFIG_DEBUG_SPINLOCK)) || \
	defined(CONFIG_GENERIC_BUG) || defined(CONFIG_JUMP_LABEL)
#define ARM_EXIT_KEEP(x)	x
#define ARM_EXIT_DISCARD(x)
#else
#define ARM_EXIT_KEEP(x)
#define ARM_EXIT_DISCARD(x)	x
#endif

CONFIG_SMP_ON_UP 在单处理器系统上启用SMP

  • SMP 内核包含在非 SMP 处理器上失败的指令。启用此选项允许内核修改自身以使这些说明安全。 禁用它会允许大约 1K 的空间储蓄。

CONFIG_JUMP_LABEL 启用跳转标签优化,以减少条件分支的开销

  • 此选项启用透明分支优化,该优化创建某些几乎总是 true 或几乎总是 false 的分支条件甚至比在 kernel 中执行更便宜。
  • 某些对性能敏感的内核代码(如跟踪点、调度程序功能、网络代码和 KVM 都有这样的分支,并包含对此优化技术的支持。
  • 如果检测到编译器支持 “asm goto”,内核将仅使用 nop 编译此类分支指令。当 condition 标志切换为 true 时,nop 将转换为 jump 指令来执行条件指令块。
  • 此技术降低了分支预测的开销和压力的处理器,通常会使内核更快。更新的情况较慢,但这种情况总是非常罕见的。

ARM_DISCARD arm丢弃的段

  • ARM_DISCARD 是一个宏定义,用于在链接脚本中指定哪些段应该被丢弃或忽略。这个宏通常用于内核的链接脚本中,以确保某些不需要的段不会被包含在最终的内核映像中。
#define ARM_DISCARD							\
		*(.ARM.exidx.exit.text)					\
		*(.ARM.extab.exit.text)					\
		*(.ARM.exidx.text.exit)					\
		*(.ARM.extab.text.exit)					\
		ARM_CPU_DISCARD(*(.ARM.exidx.cpuexit.text))		\
		ARM_CPU_DISCARD(*(.ARM.extab.cpuexit.text))		\
		ARM_EXIT_DISCARD(EXIT_TEXT)				\
		ARM_EXIT_DISCARD(EXIT_DATA)				\
		EXIT_CALL						\
		ARM_MMU_DISCARD(*(.text.fixup))				\
		ARM_MMU_DISCARD(*(__ex_table))				\
		COMMON_DISCARDS

解压部分代码的lds

arch/arm/boot/compressed/vmlinux.lds.S

CONFIG_CPU_ENDIAN_BE8 大端模式

  • 大端模式修改顺序
#ifdef CONFIG_CPU_ENDIAN_BE8
#define ZIMAGE_MAGIC(x) ( (((x) >> 24) & 0x000000ff) | \
			  (((x) >>  8) & 0x0000ff00) | \
			  (((x) <<  8) & 0x00ff0000) | \
			  (((x) << 24) & 0xff000000) )
#else
#define ZIMAGE_MAGIC(x) (x)
#endif

SANITIZER_DISCARDS 无害丢弃

#if defined(CONFIG_GCOV_KERNEL) || defined(CONFIG_KASAN_GENERIC) || defined(CONFIG_KCSAN)
# ifdef CONFIG_CONSTRUCTORS
#  define SANITIZER_DISCARDS						\
	DISCARD_EH_FRAME
# else
#  define SANITIZER_DISCARDS						\
	*(.init_array) *(.init_array.*)					\
	DISCARD_EH_FRAME
# endif
#else
# define SANITIZER_DISCARDS
#endif

PATCHABLE_DISCARDS 可修补的丢弃

/* 实际配置决定了 init/exit 部分是否
 * 作为文本/数据处理,或者它们可以被丢弃(
 * 经常发生在运行时)
 */
#ifndef CONFIG_HAVE_DYNAMIC_FTRACE_NO_PATCHABLE
#define KEEP_PATCHABLE		KEEP(*(__patchable_function_entries))
#define PATCHABLE_DISCARDS
#else
#define KEEP_PATCHABLE
#define PATCHABLE_DISCARDS	*(__patchable_function_entries)
#endif
CONFIG_HAVE_DYNAMIC_FTRACE_NO_PATCHABLE 控制动态函数跟踪(ftrace)功能的行为
  • 它决定了是否使用不可修补的函数条目。

COMMON_DISCARDS 公共丢弃

#define COMMON_DISCARDS							\
	SANITIZER_DISCARDS						\
	PATCHABLE_DISCARDS						\
	*(.discard)							\
	*(.discard.*)							\
	*(.export_symbol)						\
	*(.no_trim_symbol)						\
	*(.modinfo)							\
	/* ld.bfd warns about .gnu.version* even when not emitted */	\
	*(.gnu.version*)						\

内核的lds

arch/arm/kernel/vmlinux.lds.S

CONFIG_XIP_KERNEL 选择不同的lds.s文件

#ifdef CONFIG_XIP_KERNEL
#include "vmlinux-xip.lds.S"
#else
#endif

jiffies 取低32位使用

  • 这个定义在链接脚本中不会直接生成可见的输出段或内容,因为它只是定义了一个符号,并没有分配内存或生成代码段。因此,在最终生成的 vmlinux.lds 文件中,你不会看到显式的 jiffies 定义。
  • 链接脚本的主要作用是定义内核镜像的内存布局和各个段的排列方式,而符号定义(如 jiffies)通常用于在链接过程中为特定地址或变量提供别名或偏移量。这些符号在链接过程中会被解析和使用,但不会直接出现在最终的链接脚本输出中。
#ifndef __ARMEB__
jiffies = jiffies_64;
#else
jiffies = jiffies_64 + 4;
#endif

网站公告

今日签到

点亮在社区的每一天
去签到