linux之内核vmlinux.lds.S 的汇编分析

一、vmlinux.lds.S 的汇编分析

vmlinux.lds.S 的汇编分析,有利于对内核的理解

 arch/arm64/kernel/vmlinux.lds.S

OUTPUT_ARCH(aarch64)
ENTRY(_text)

jiffies = jiffies_64;        


#ifdef CONFIG_KVM
#define HYPERVISOR_EXTABLE                                      \
        . = ALIGN(SZ_8);                                        \
        __start___kvm_ex_table = .;                             \
        *(__kvm_ex_table)                                       \
        __stop___kvm_ex_table = .;

#define HYPERVISOR_PERCPU_SECTION                               \
        . = ALIGN(PAGE_SIZE);                                   \
        HYP_SECTION_NAME(.data..percpu) : {                     \
                *(HYP_SECTION_NAME(.data..percpu))              \
        }
#else /* CONFIG_KVM */
#define HYPERVISOR_EXTABLE
#define HYPERVISOR_PERCPU_SECTION
#endif

#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);                               \
        __hyp_idmap_text_start = .;                     \
        *(.hyp.idmap.text)                              \
        __hyp_idmap_text_end = .;                       \
        __hyp_text_start = .;                           \
        *(.hyp.text)                                    \
        HYPERVISOR_EXTABLE                              \
        __hyp_text_end = .;

#define IDMAP_TEXT                                      \
        . = ALIGN(SZ_4K);                               \
        __idmap_text_start = .;                         \
        *(.idmap.text)                                  \
        __idmap_text_end = .;

#ifdef CONFIG_HIBERNATION
#define HIBERNATE_TEXT                                  \
        . = ALIGN(SZ_4K);                               \
        __hibernate_exit_text_start = .;                \
        *(.hibernate_exit.text)                         \
        __hibernate_exit_text_end = .;
#else
#define HIBERNATE_TEXT
#endif

#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
#define TRAMP_TEXT                                      \
        . = ALIGN(PAGE_SIZE);                           \
        __entry_tramp_text_start = .;                   \
        *(.entry.tramp.text)                            \
        . = ALIGN(PAGE_SIZE);                           \
        __entry_tramp_text_end = .;
#else
#define TRAMP_TEXT
#endif

/*
 * 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
 

OUTPUT_ARCH(aarch64)说明最终编译的格式为aarch64。
ENTRY(_text)表示其入口地址是_text

jiffies是一个32位无符号整数,它表示自系统启动以来的时钟滴答数。每个时钟滴答对应于一个时钟周期,其长度取决于系统的时钟频率。通过增加或减少jiffies的值,可以表示时间的流逝或倒退。

jiffies_64是一个64位无符号整数,它提供了更大的计数范围。在一些需要更长计时周期的场景下,可以使用jiffies_64来获得更精确的时间跟踪。

在某些情况下,为了保持兼容性或简化代码,jiffiesjiffies_64可能会被设置为相同的值。这样,无论是使用32位还是64位的内核,都可以使用相同的变量名来引用系统时间。

配置KVM,会设置hypervisor 的对齐,这是关于虚拟化的功能。涉及到内核源码中的 kvm/ virt/ xen/ 等目录下的代码块。

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
        }
        .text : {                       /* Real text segment            */
                _stext = .;             /* Text and read-only data      */
                        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          */
        }

   
 

  section

SECTIONS { } 是链接脚本语法中的关键命令,用以描述输出文件的内存布局。

SECTIONS 命令告诉链接文件如何把输入文件的段映射到输出文件的各个段中,如何将输入段整合为输出段,如何把输出段放入程序地址空间和进程地址空间中。

 /DISCARD/ 段

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

DISCARDS
        /DISCARD/ : {
                *(.interp .dynamic)
                *(.dynsym .dynstr .hash .gnu.hash)
        }

 .head.text 段

 . = KIMAGE_VADDR;

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

'.' 号是连接脚本中一个特殊的符号,用以表示当前位置计数器。

最开始将 KIMAGE_VADDR 赋值给 '.' 意思是把代码段的地址设置给当前位置;

KIMAGE_VADDR 在 memory.h 中定义

 #define VA_BITS                 (CONFIG_ARM64_VA_BITS)
#define _PAGE_OFFSET(va)        (-(UL(1) << (va)))
#define PAGE_OFFSET             (_PAGE_OFFSET(VA_BITS))
#define KIMAGE_VADDR            (MODULES_END)
#define BPF_JIT_REGION_START    (KASAN_SHADOW_END)
#define BPF_JIT_REGION_SIZE     (SZ_128M)
#define BPF_JIT_REGION_END      (BPF_JIT_REGION_START + BPF_JIT_REGION_SIZE)
#define MODULES_END             (MODULES_VADDR + MODULES_VSIZE)
#define MODULES_VADDR           (BPF_JIT_REGION_END)
#define MODULES_VSIZE           (SZ_128M)
#define VMEMMAP_START           (-VMEMMAP_SIZE - SZ_2M)
#define VMEMMAP_END             (VMEMMAP_START + VMEMMAP_SIZE)
#define PCI_IO_END              (VMEMMAP_START - SZ_2M)
#define PCI_IO_START            (PCI_IO_END - PCI_IO_SIZE)
#define FIXADDR_TOP             (PCI_IO_START - SZ_2M)

 ".head.text" 表示输出段名称,对应的输入段位 HEAD_TEXT

 include/asm-generic/vmlinux.lds.h
 
#define HEAD_TEXT  KEEP(*(.head.text))

text 段 

.text : {                       /* Real text segment            */
                _stext = .;             /* Text and read-only data      */
                        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          */
        }

        /*
         * Make sure that the .got.plt is either completely empty or it
         * contains only the lazy dispatch entries.
         */
        .got.plt : { *(.got.plt) }
        ASSERT(SIZEOF(.got.plt) == 0 || SIZEOF(.got.plt) == 0x18,
               "Unexpected GOT/PLT entries detected!")

        . = ALIGN(SEGMENT_ALIGN);
        _etext = .;                     /* End of text section */
 

定义文本段内容,区间为 [ _stext, _etext ) 

开始的时候,将当前位置存放在 _stext 和 __exception_text_start 中;

接着加入所有输入目标文件的  .exception.text 段到 .text 段中;

添加完 .exception.text 后,将当前位置存入  __exception_text_end 中;

接着是宏定义  IRQENTRY_TEXT,定义在 vmlinux.lds.h 中:
 

include/asm-generic/vmlinux.lds.h
 
#define IRQENTRY_TEXT                            \
        ALIGN_FUNCTION();                    \
        __irqentry_text_start = .;                \
        *(.irqentry.text)                    \
        __irqentry_text_end = .;

 其他以此类推

                        SOFTIRQENTRY_TEXT
                        ENTRY_TEXT
                        TEXT_TEXT
                        SCHED_TEXT
                        CPUIDLE_TEXT
                        LOCK_TEXT
                        KPROBES_TEXT
                        HYPERVISOR_TEXT
                        IDMAP_TEXT
                        HIBERNATE_TEXT
                        TRAMP_TEXT
这些都是类似

RO_DATA(PAGE_SIZE)
只读区间

 /* everything from this point to __init_begin will be marked RO NX */
        RO_DATA(PAGE_SIZE)

 这个宏定义在 vmlinux.lds.文件中

/*
 * Read only Data
 */
#define RO_DATA(align)                                                  \
        . = ALIGN((align));                                             \
        .rodata           : AT(ADDR(.rodata) - LOAD_OFFSET) {           \
                __start_rodata = .;                                     \
                *(.rodata) *(.rodata.*)                                 \
                SCHED_DATA                                              \
                RO_AFTER_INIT_DATA      /* Read only after init */      \
                . = ALIGN(8);                                           \
                __start___tracepoints_ptrs = .;                         \
                KEEP(*(__tracepoints_ptrs)) /* Tracepoints: pointer array */ \
                __stop___tracepoints_ptrs = .;                          \
                *(__tracepoints_strings)/* Tracepoints: strings */      \
        }                                                               \
                                                                        \
        .rodata1          : AT(ADDR(.rodata1) - LOAD_OFFSET) {          \
                *(.rodata1)                                             \
        }                                                               \
                                                                        \
        /* PCI quirks */                                                \
        .pci_fixup        : AT(ADDR(.pci_fixup) - LOAD_OFFSET) {        \
                __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 = .;                      \
        }                                                               \
                                                                        \
        /* Built-in firmware blobs */                                   \
        .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) ALIGN(8) {    \
                __start_builtin_fw = .;                                 \
                KEEP(*(.builtin_fw))                                    \
                __end_builtin_fw = .;                                   \
        }                                                               \
                                                                        \
        TRACEDATA                                                       \
                                                                        \
        /* Kernel symbol table: Normal symbols */                       \
        __ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {         \
                __start___ksymtab = .;                                  \
                KEEP(*(SORT(___ksymtab+*)))                             \
                __stop___ksymtab = .;                                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __ksymtab_gpl     : AT(ADDR(__ksymtab_gpl) - LOAD_OFFSET) {     \
                __start___ksymtab_gpl = .;                              \
                KEEP(*(SORT(___ksymtab_gpl+*)))                         \
                __stop___ksymtab_gpl = .;                               \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal unused symbols */                \
        __ksymtab_unused  : AT(ADDR(__ksymtab_unused) - LOAD_OFFSET) {  \
                __start___ksymtab_unused = .;                           \
                KEEP(*(SORT(___ksymtab_unused+*)))                      \
                __stop___ksymtab_unused = .;                            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __ksymtab_unused_gpl : AT(ADDR(__ksymtab_unused_gpl) - LOAD_OFFSET) { \
                __start___ksymtab_unused_gpl = .;                       \
                KEEP(*(SORT(___ksymtab_unused_gpl+*)))                  \
                __stop___ksymtab_unused_gpl = .;                        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __ksymtab_gpl_future : AT(ADDR(__ksymtab_gpl_future) - LOAD_OFFSET) { \
                __start___ksymtab_gpl_future = .;                       \
                KEEP(*(SORT(___ksymtab_gpl_future+*)))                  \
                __stop___ksymtab_gpl_future = .;                        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: Normal symbols */                       \
        __kcrctab         : AT(ADDR(__kcrctab) - LOAD_OFFSET) {         \
                __start___kcrctab = .;                                  \
                KEEP(*(SORT(___kcrctab+*)))                             \
                __stop___kcrctab = .;                                   \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only symbols */                     \
        __kcrctab_gpl     : AT(ADDR(__kcrctab_gpl) - LOAD_OFFSET) {     \
                __start___kcrctab_gpl = .;                              \
                KEEP(*(SORT(___kcrctab_gpl+*)))                         \
                __stop___kcrctab_gpl = .;                               \
        }      

/* Kernel symbol table: Normal unused symbols */                \
        __kcrctab_unused  : AT(ADDR(__kcrctab_unused) - LOAD_OFFSET) {  \
                __start___kcrctab_unused = .;                           \
                KEEP(*(SORT(___kcrctab_unused+*)))                      \
                __stop___kcrctab_unused = .;                            \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-only unused symbols */              \
        __kcrctab_unused_gpl : AT(ADDR(__kcrctab_unused_gpl) - LOAD_OFFSET) { \
                __start___kcrctab_unused_gpl = .;                       \
                KEEP(*(SORT(___kcrctab_unused_gpl+*)))                  \
                __stop___kcrctab_unused_gpl = .;                        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: GPL-future-only symbols */              \
        __kcrctab_gpl_future : AT(ADDR(__kcrctab_gpl_future) - LOAD_OFFSET) { \
                __start___kcrctab_gpl_future = .;                       \
                KEEP(*(SORT(___kcrctab_gpl_future+*)))                  \
                __stop___kcrctab_gpl_future = .;                        \
        }                                                               \
                                                                        \
        /* Kernel symbol table: strings */                              \
        __ksymtab_strings : AT(ADDR(__ksymtab_strings) - LOAD_OFFSET) { \
                *(__ksymtab_strings)                                    \
        }                                                               \
                                                                        \
        /* __*init sections */                                          \
        __init_rodata : AT(ADDR(__init_rodata) - LOAD_OFFSET) {         \
                *(.ref.rodata)                                          \
                MEM_KEEP(init.rodata)                                   \
                MEM_KEEP(exit.rodata)                                   \
        }                                                               \
                                                                        \
        /* Built-in module parameters. */                               \
        __param : AT(ADDR(__param) - LOAD_OFFSET) {                     \
                __start___param = .;                                    \
                KEEP(*(__param))                                        \
                __stop___param = .;                                     \
        }                                                               \
                                                                        \
        /* Built-in module versions. */                                 \
        __modver : AT(ADDR(__modver) - LOAD_OFFSET) {                   \
                __start___modver = .;                                   \
                KEEP(*(__modver))                                       \
                __stop___modver = .;                                    \
        }                                                               \
                                                                        \
        RO_EXCEPTION_TABLE                                              \
        NOTES                                                           \
        BTF                                                             \
                                                                        \
        . = ALIGN((align));                                             \
        __end_rodata = .;
                                         

 页表地址段


        idmap_pg_dir = .;
        . += IDMAP_DIR_SIZE;
        idmap_pg_end = .;

#ifdef CONFIG_UNMAP_KERNEL_AT_EL0
        tramp_pg_dir = .;
        . += PAGE_SIZE;
#endif

        reserved_pg_dir = .;
        . += PAGE_SIZE;

        swapper_pg_dir = .;
        . += PAGE_SIZE;
 

在 .init.text 段之前,会预留一部分的空间给一些页表初始化使用。

例如,idmap_pg_dir、tramp_pg_dir、reserved_pg_dir、swapper_pg_dir 等。

idmap_pg_dir 是 identity mapping 用到的页表。
 

init段

 . = ALIGN(SEGMENT_ALIGN);
        __init_begin = .;      //开始
        __inittext_begin = .;             

        INIT_TEXT_SECTION(8)

        __exittext_begin = .;
        .exit.text : {
                EXIT_TEXT
        }
        __exittext_end = .;

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

        . = ALIGN(SEGMENT_ALIGN);
        __inittext_end = .;
        __initdata_begin = .;

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

        PERCPU_SECTION(L1_CACHE_BYTES)
        HYPERVISOR_PERCPU_SECTION

        .rela.dyn : ALIGN(8) {
                *(.rela .rela*)
        }

        __rela_offset   = ABSOLUTE(ADDR(.rela.dyn) - KIMAGE_VADDR);
        __rela_size     = SIZEOF(.rela.dyn);

#ifdef CONFIG_RELR
        .relr.dyn : ALIGN(8) {
                *(.relr.dyn)
        }

        __relr_offset   = ABSOLUTE(ADDR(.relr.dyn) - KIMAGE_VADDR);
        __relr_size     = SIZEOF(.relr.dyn);
#endif

        . = ALIGN(SEGMENT_ALIGN);
        __initdata_end = .;
        __init_end = .;    //结束
 

初始化段范围是 [ __init_begin, __init_end ),包括了这里的 initext 段,以及后面 initdata 段,在 kernel_init() 中内核初始化完成,会将这部分内存释放掉,详细看 free_initmem() 函数。 

inittext 段

 __inittext_begin = .;

        INIT_TEXT_SECTION(8)

        __exittext_begin = .;
        .exit.text : {
                EXIT_TEXT
        }
        __exittext_end = .;

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

        . = ALIGN(SEGMENT_ALIGN);
        __inittext_end = .;
 

 本节剖析的是 inittext 段,而不仅仅是 .init.text,inittext 区间为:

[ __inittext_begin, __inittext_end )

其中包括:

.init.text 段;
.exit.text 段;
.altinstuctions 段;
.altinstr_replacement 段;

  INIT_TEXT_SECTION(8)
.init.text 段主要通过宏 INIT_TEXT_SECTION() 进行定义,下面来看下这个宏:

 include/asm-generic/vmlinux.lds.h
 
#define INIT_TEXT_SECTION(inittext_align)                \
    . = ALIGN(inittext_align);                    \
    .init.text : AT(ADDR(.init.text) - LOAD_OFFSET) {        \
        _sinittext = .;                        \
        INIT_TEXT                        \
        _einittext = .;                        \
    }
 
 
#define INIT_TEXT                            \
    *(.init.text .init.text.*)                    \
    *(.text.startup)                        \
    MEM_DISCARD(init.text*)

 将所有目标文件中的 .init.text 段 或 .init.text.* 段放入 .init.text 段中;
将所有目标文件中的 .text.startup 段放入 .init.text 段中;
确认是否将所有目标文件中的 .mem##init.text.* 的段放入 .init.text 段中;
 

exit.text

  __exittext_begin = .;
        .exit.text : {
                EXIT_TEXT
        }
        __exittext_end = .;

 主要是通过宏 EXIT_TEXT 来定义 .exit.text 的输入section描述:

 include/asm-generic/vmlinux.lds.h
 
#define EXIT_TEXT                            \
    *(.exit.text)                            \
    *(.text.exit)                            \
    MEM_DISCARD(exit.text)

initdata段

__initdata_begin = .;

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

        PERCPU_SECTION(L1_CACHE_BYTES)
        HYPERVISOR_PERCPU_SECTION

        .rela.dyn : ALIGN(8) {
                *(.rela .rela*)
        }

        __rela_offset   = ABSOLUTE(ADDR(.rela.dyn) - KIMAGE_VADDR);
        __rela_size     = SIZEOF(.rela.dyn);

#ifdef CONFIG_RELR
        .relr.dyn : ALIGN(8) {
                *(.relr.dyn)
        }

        __relr_offset   = ABSOLUTE(ADDR(.relr.dyn) - KIMAGE_VADDR);
        __relr_size     = SIZEOF(.relr.dyn);
#endif

        . = ALIGN(SEGMENT_ALIGN);
        __initdata_end = .;
 

本节剖析的是 initdata 段,而不仅仅是 .init.data,initdata 区间为:

[ __initdata_begin,__initdata_end)

这其中包括:

.init.data 段;
.exit.data 段;
.data..percpu 段;
.rela.dyn 段;
.init.data 又包括:

INIT_DATA
INIT_SETUP
INIT_CALLS
CON_INITCALL
INIT_RAM_FS
.init.rodata.*
.init.bss
下面笔者将会逐个剖析这些 .init.data 段的宏定义。当然,initdata 中还有个重要的 .data.percpu 段,通过 PERCPU_SECTION 进行定义,本节也会剖析该字段。
 

INIT_DATA

include/asm-generic/vmlinux.lds.h
 
#define INIT_DATA                            \
    KEEP(*(SORT(___kentry+*)))                    \
    *(.init.data init.data.*)                    \
    MEM_DISCARD(init.data*)                        \
    KERNEL_CTORS()                            \
    MCOUNT_REC()                            \
    *(.init.rodata .init.rodata.*)                    \
    FTRACE_EVENTS()                            \
    TRACE_SYSCALLS()                        \
    KPROBE_BLACKLIST()                        \
    ERROR_INJECT_WHITELIST()                    \
    MEM_DISCARD(init.rodata)                    \
    CLK_OF_TABLES()                            \
    RESERVEDMEM_OF_TABLES()                        \
    TIMER_OF_TABLES()                        \
    CPU_METHOD_OF_TABLES()                        \
    CPUIDLE_METHOD_OF_TABLES()                    \
    KERNEL_DTB()                            \
    IRQCHIP_OF_MATCH_TABLE()                    \
    ACPI_PROBE_TABLE(irqchip)                    \
    ACPI_PROBE_TABLE(timer)                        \
    THERMAL_TABLE(governor)                        \
    EARLYCON_TABLE()                        \
    LSM_TABLE()                            \
    EARLY_LSM_TABLE()

INIT_SETUP(16)

 include/asm-generic/vmlinux.lds.h
 
#define INIT_SETUP(initsetup_align)                    \
        . = ALIGN(initsetup_align);                \
        __setup_start = .;                    \
        KEEP(*(.init.setup))                    \
        __setup_end = .;

INIT_CALLS

 include/asm-generic/vmlinux.lds.h
 
#define INIT_CALLS                            \
        __initcall_start = .;                    \
        KEEP(*(.initcallearly.init))                \
        INIT_CALLS_LEVEL(0)                    \
        INIT_CALLS_LEVEL(1)                    \
        INIT_CALLS_LEVEL(2)                    \
        INIT_CALLS_LEVEL(3)                    \
        INIT_CALLS_LEVEL(4)                    \
        INIT_CALLS_LEVEL(5)                    \
        INIT_CALLS_LEVEL(rootfs)                \
        INIT_CALLS_LEVEL(6)                    \
        INIT_CALLS_LEVEL(7)                    \
        __initcall_end = .;

INIT_CALLS 总的来说,做了四件事情:

#设定 __initcall_start,这里是initcall 函数指针的起始位置;
#将所有目标文件的 .initcallearly.init 段加入 .init.data 段;
#调用 INIT_CALLS_LEVEL(),将各个initcall 级别的段加入到 .init.data 段中;
#设定 __initcall_end,这里是initcall 函数指针的结束位置;
 

下面来看下 INIT_CALLS_LEVEL() 做了什么: 

 #define INIT_CALLS_LEVEL(level)                        \
        __initcall##level##_start = .;                \
        KEEP(*(.initcall##level##.init))            \
        KEEP(*(.initcall##level##s.init))            \

首先,定义每个 level 的起始位置 __initcall##level##_start,连续调用每个 level 下所有 initcall 函数,而这个 level 下第一个 initcall 函数指针,也就是这个 level 函数指针的起始位置定义在 initcall_levels 这个函数指针数组中:

 init/main.c
 
static initcall_entry_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};

回到 INIT_CALLS_LEVEL(),接下来会将所有目标文件中 .initcall##level##.init 和 .initcall##level##s.init 段加入到 .init.data 段中;

这样,在 System.map 中我们清晰看到 initcall 的各个段
...
ffffffc012032ee0 d __initcall_223_42_trace_init_flags_sys_enterearly
ffffffc012032ee0 D __initcall_start
ffffffc012032ee0 D __setup_end
ffffffc012032ee8 d __initcall_224_66_trace_init_flags_sys_exitearly
ffffffc012032ef0 d __initcall_163_146_cpu_suspend_initearly
ffffffc012032ef8 d __initcall_151_267_asids_initearly
ffffffc012032f00 d __initcall_167_688_spawn_ksoftirqdearly
ffffffc012032f08 d __initcall_343_6656_migration_initearly
...
ffffffc012032f90 d __initcall_312_768_initialize_ptr_randomearly
ffffffc012032f98 D __initcall0_start
ffffffc012032f98 d __initcall_241_771_bpf_jit_charge_init0
ffffffc012032fa0 d __initcall_141_53_init_mmap_min_addr0
ffffffc012032fa8 d __initcall_209_6528_pci_realloc_setup_params0
ffffffc012032fb0 d __initcall_339_1143_net_ns_init0
ffffffc012032fb8 D __initcall1_start
ffffffc012032fb8 d __initcall_160_1437_fpsimd_init1
ffffffc012032fc0 d __initcall_181_669_tagged_addr_init1
...
ffffffc012033178 d __initcall_347_1788_init_default_flow_dissectors1
ffffffc012033180 d __initcall_360_2821_netlink_proto_init1
ffffffc012033188 D __initcall2_start
ffffffc012033188 d __initcall_165_139_debug_monitors_init2
ffffffc012033190 d __initcall_141_333_irq_sysfs_init2
...
ffffffc0120332b8 d __initcall_304_814_kobject_uevent_init2
ffffffc0120332c0 d __initcall_184_1686_msm_rpm_driver_init2s
ffffffc0120332c8 D __initcall3_start
ffffffc0120332c8 d __initcall_173_390_debug_traps_init3
ffffffc0120332d0 d __initcall_161_275_reserve_memblock_reserved_regions3
...
ffffffc012033370 d __initcall_132_5273_gsi_init3
ffffffc012033378 d __initcall_149_547_of_platform_default_populate_init3s
ffffffc012033380 D __initcall4_start
...
ffffffc012033878 D __initcall5_start
...
ffffffc0120339d8 d __initcall_317_1188_xsk_init5
ffffffc0120339e0 d __initcall_211_194_pci_apply_final_quirks5s
ffffffc0120339e8 d __initcall_168_680_populate_rootfsrootfs
ffffffc0120339e8 D __initcallrootfs_start
ffffffc0120339f0 D __initcall6_start
...
ffffffc012034b30 D __initcall7_start
...
ffffffc012034c88 d __initcall_150_554_of_platform_sync_state_init7s
ffffffc012034c90 d __initcall_123_29_alsa_sound_last_init7s
ffffffc012034c98 D __con_initcall_start
ffffffc012034c98 d __initcall_151_246_hvc_console_initcon
ffffffc012034c98 D __initcall_end
ffffffc012034ca0 D __con_initcall_end

CON_INITCALL

 include/asm-generic/vmlinux.lds.h
 
#define CON_INITCALL                            \
        __con_initcall_start = .;                \
        KEEP(*(.con_initcall.init))                \
        __con_initcall_end = .;

 console initcall 不同其他的incall 函数,这里会单独确定其起始地址 __con_initcall_start 和结尾地址 __con_initcall_end.

 INIT_RAM_FS

include/asm-generic/vmlinux.lds.h
 
#ifdef CONFIG_BLK_DEV_INITRD
#define INIT_RAM_FS                            \
    . = ALIGN(4);                            \
    __initramfs_start = .;                        \
    KEEP(*(.init.ramfs))                        \
    . = ALIGN(8);                            \
    KEEP(*(.init.ramfs.info))
#else
#define INIT_RAM_FS
#endif

 PERCPU_SECTION

include/asm-generic/vmlinux.lds.h
 
#define PERCPU_SECTION(cacheline)                    \
    . = ALIGN(PAGE_SIZE);                        \
    .data..percpu    : AT(ADDR(.data..percpu) - LOAD_OFFSET) {    \
        __per_cpu_load = .;                    \
        PERCPU_INPUT(cacheline)                    \
    }

首先是进行当前位置的地址对齐,需要 PAGE_SIZE 对齐;

接着,记录 percpu 段的起始位置 __per_cpu_load;

接下来是 percpu 的输入描述:

 include/asm-generic/vmlinux.lds.h
 
#define PERCPU_INPUT(cacheline)                        \
    __per_cpu_start = .;                        \
    *(.data..percpu..first)                        \
    . = ALIGN(PAGE_SIZE);                        \
    *(.data..percpu..page_aligned)                    \
    . = ALIGN(cacheline);                        \
    *(.data..percpu..read_mostly)                    \
    . = ALIGN(cacheline);                        \
    *(.data..percpu)                        \
    *(.data..percpu..shared_aligned)                \
    PERCPU_DECRYPTED_SECTION                    \
    __per_cpu_end = .;

.data.percpu 段包括:

所有目标文件中的 .data..percpu..first 段;
所有目标文件中的 .data..percpu..page_aligned 段;
所有目标文件中的 .data..percpu..read_mostly 段;
所有目标文件中的 .data..percpu 段;
所有目标文件中的 .data..percpu..shared_aligned 段;
 

.data 段

_data = .;
        _sdata = .;
        RW_DATA(L1_CACHE_BYTES, PAGE_SIZE, THREAD_ALIGN)

        /*
         * Data written with the MMU off but read with the MMU on requires
         * cache lines to be invalidated, discarding up to a Cache Writeback
         * Granule (CWG) of data from the cache. Keep the section that
         * requires this type of maintenance to be in its own Cache Writeback
         * Granule (CWG) area so the cache maintenance operations don't
         * interfere with adjacent 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 = .;
        }

        PECOFF_EDATA_PADDING
        __pecoff_data_rawsize = ABSOLUTE(. - __initdata_begin);
        _edata = .;
 

[ _data, _edata ] 代表数据相关段。 

下面来看下 RW_DATA_SECTION:

include/asm-generic/vmlinux.lds.h
 
/*
 * Writeable data.
 * All sections are combined in a single .data section.
 * The sections following CONSTRUCTORS are arranged so their
 * typical alignment matches.
 * A cacheline is typical/always less than a PAGE_SIZE so
 * the sections that has this restriction (or similar)
 * is located before the ones requiring PAGE_SIZE alignment.
 * NOSAVE_DATA starts and ends with a PAGE_SIZE alignment which
 * matches the requirement of PAGE_ALIGNED_DATA.
 *
 * use 0 as page_align if page_aligned data is not used */
#define RW_DATA_SECTION(cacheline, pagealigned, inittask)        \
    . = ALIGN(PAGE_SIZE);                        \
    .data : AT(ADDR(.data) - LOAD_OFFSET) {                \
        INIT_TASK_DATA(inittask)                \
        NOSAVE_DATA                        \
        PAGE_ALIGNED_DATA(pagealigned)                \
        CACHELINE_ALIGNED_DATA(cacheline)            \
        READ_MOSTLY_DATA(cacheline)                \
        DATA_DATA                        \
        CONSTRUCTORS                        \
    }                                \
    BUG_TABLE                            \

 .bss 段

     BSS_SECTION(0, 0, 0)

 #define BSS_SECTION(sbss_align, bss_align, stop_align)            \
    . = ALIGN(sbss_align);                        \
    __bss_start = .;                        \
    SBSS(sbss_align)                        \
    BSS(bss_align)                            \
    . = ALIGN(stop_align);                        \
    __bss_stop = .;

 镜像结尾段

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

        . = ALIGN(SEGMENT_ALIGN);
        __pecoff_data_size = ABSOLUTE(. - __initdata_begin);
        _end = .;
 

定义了 init_pg_dir,这是临时页表初始化页表,对于三级页表,会占用 3 个pages 空间。

在页表映射完成之后,这部分的内存会被释放掉,变成普通内存供 buddy 使用。

stab 段

  STABS_DEBUG

 #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) }        \
      

 DWARF_DEBUG  

全是调试信息

#define DWARF_DEBUG                                                     \
                /* DWARF 1 */                                           \
                .debug          0 : { *(.debug) }                       \
                .line           0 : { *(.line) }                        \
                /* GNU DWARF 1 extensions */                            \
                .debug_srcinfo  0 : { *(.debug_srcinfo) }               \
                .debug_sfnames  0 : { *(.debug_sfnames) }               \
                /* DWARF 1.1 and DWARF 2 */                             \
                .debug_aranges  0 : { *(.debug_aranges) }               \
                .debug_pubnames 0 : { *(.debug_pubnames) }              \
                /* DWARF 2 */                                           \
                .debug_info     0 : { *(.debug_info                     \
                                .gnu.linkonce.wi.*) }                   \
                .debug_abbrev   0 : { *(.debug_abbrev) }                \
                .debug_line     0 : { *(.debug_line) }                  \
                .debug_frame    0 : { *(.debug_frame) }                 \
                .debug_str      0 : { *(.debug_str) }                   \
                .debug_loc      0 : { *(.debug_loc) }                   \
                .debug_macinfo  0 : { *(.debug_macinfo) }               \
                .debug_pubtypes 0 : { *(.debug_pubtypes) }              \
                /* DWARF 3 */                                           \
                .debug_ranges   0 : { *(.debug_ranges) }                \
                /* SGI/MIPS DWARF 2 extensions */                       \
                .debug_weaknames 0 : { *(.debug_weaknames) }            \
                .debug_funcnames 0 : { *(.debug_funcnames) }            \
                .debug_typenames 0 : { *(.debug_typenames) }            \
                .debug_varnames  0 : { *(.debug_varnames) }             \
                /* GNU DWARF 2 extensions */                            \
                .debug_gnu_pubnames 0 : { *(.debug_gnu_pubnames) }      \
                .debug_gnu_pubtypes 0 : { *(.debug_gnu_pubtypes) }      \
                /* DWARF 4 */                                           \
                .debug_types    0 : { *(.debug_types) }                 \
                /* DWARF 5 */                                           \
                .debug_addr     0 : { *(.debug_addr) }                  \
                .debug_line_str 0 : { *(.debug_line_str) }              \
                .debug_loclists 0 : { *(.debug_loclists) }              \
                .debug_macro    0 : { *(.debug_macro) }                 \
                .debug_names    0 : { *(.debug_names) }                 \
                .debug_rnglists 0 : { *(.debug_rnglists) }              \
                .debug_str_offsets      0 : { *(.debug_str_offsets) }
 

 ELF_DETAILS

 
#define ELF_DETAILS                                                     \
                .comment 0 : { *(.comment) }                            \
                .symtab 0 : { *(.symtab) }                              \
                .strtab 0 : { *(.strtab) }                              \
                .shstrtab 0 : { *(.shstrtab) }

 HEAD_SYMBOLS

arch/arm64/kernel/image.h
 
/*
 * These will output as part of the Image header, which should be little-endian
 * regardless of the endianness of the kernel. While constant values could be
 * endian swapped in head.S, all are done here for consistency.
 */
#define HEAD_SYMBOLS                        \
    DEFINE_IMAGE_LE64(_kernel_size_le, _end - _text);    \
    DEFINE_IMAGE_LE64(_kernel_offset_le, TEXT_OFFSET);    \
    DEFINE_IMAGE_LE64(_kernel_flags_le, __HEAD_FLAGS);
 
 
#define DEFINE_IMAGE_LE64(sym, data)                \
    sym##_lo32 = DATA_LE32((data) & 0xffffffff);        \
    sym##_hi32 = DATA_LE32((data) >> 32)

定义头符号,分别是:

_kernel_size_le_lo32
_kernel_size_le_hi32          //用以存放 kernel img 实际大小的高32bits和低32bits
_kernel_offset_le_lo32
_kernel_offset_le_hi32       //用以存放 kernel img 相对 RAM 偏移的高32bits和低32bits
_kernel_flags_le_lo32
_kernel_flags_le_hi32         //用以存放 kernel img 的几个信息flag
在内核镜像生成过程中,这几个符号标志代表的值会作为镜像头的一部分输出。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值