vmlinux.lds.s檔案分析
vmlinux.lds.S是如何組織核心的每個函式存放在核心映象檔案的位置,我們知道你在編譯核心生成核心檔案的時候,其實這個過程分兩步,一個是“編譯”,另一個是“連結”的過程,vmlinux.lds.S要做的就是告訴編譯器如何連結編譯好的各個核心.o檔案。
小知識:連結器中的entry 連結器 按以下優先順序設入口點,找到即停止 1 -e 命令列選項 2 指令碼中的entry(symbol)命令 3如定義了start的值,取其值為入口點 4.text的第一個位元組的地址 5地址0 所以你可以從vmlinux.lds.S下面的程式碼中看到:
OUTPUT_ARCH(arm) ENTRY(stext) 所以你可以從vmlinux.lds.S下面的程式碼中看到:
OUTPUT_ARCH(arm) ENTRY(stext) 表明我們指定stext作為,程式的入口點。
SECTIONS { #ifdef CONFIG_XIP_KERNEL . = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR); #else . = PAGE_OFFSET + TEXT_OFFSET; #endif
.init : { /* Init code and data */ _stext = .; _sinittext = .; HEAD_TEXT INIT_TEXT ARM_EXIT_KEEP(EXIT_TEXT) _einittext = .; ARM_CPU_DISCARD(PROC_INFO) __arch_info_begin = .; *(.arch.info.init) __arch_info_end = .; __tagtable_begin = .; *(.taglist.init) __tagtable_end = .; #ifdef CONFIG_SMP_ON_UP __smpalt_begin = .; *(.alt.smp.init) __smpalt_end = .; #endif
__pv_table_begin = .; *(.pv_table) __pv_table_end = .;
INIT_SETUP(16)
INIT_CALLS CON_INITCALL SECURITY_INITCALL INIT_RAM_FS
#ifndef CONFIG_XIP_KERNEL __init_begin = _stext; INIT_DATA ARM_EXIT_KEEP(EXIT_DATA) #endif }
PERCPU_SECTION(32)
#ifndef CONFIG_XIP_KERNEL . = ALIGN(PAGE_SIZE); __init_end = .; #endif
/* * unwind exit sections must be discarded before the rest of the * unwind sections get included. */ /DISCARD/ : { *(.ARM.exidx.exit.text) *(.ARM.extab.exit.text) ARM_CPU_DISCARD(*(.ARM.exidx.cpuexit.text)) ARM_CPU_DISCARD(*(.ARM.extab.cpuexit.text)) #ifndef CONFIG_HOTPLUG *(.ARM.exidx.devexit.text) *(.ARM.extab.devexit.text) #endif #ifndef CONFIG_MMU *(.fixup) *(__ex_table) #endif }
.text : { /* Real text segment */ _text = .; /* Text and read-only data */ __exception_text_start = .; *(.exception.text) __exception_text_end = .; IRQENTRY_TEXT TEXT_TEXT SCHED_TEXT LOCK_TEXT KPROBES_TEXT #ifdef CONFIG_MMU *(.fixup) #endif *(.gnu.warning) *(.rodata) *(.rodata.*) *(.glue_7) *(.glue_7t) . = ALIGN(4); *(.got) /* Global offset table */ ARM_CPU_KEEP(PROC_INFO) }
RO_DATA(PAGE_SIZE)
#ifdef CONFIG_ARM_UNWIND /* * Stack unwinding tables */ . = ALIGN(8); .ARM.unwind_idx : { __start_unwind_idx = .; *(.ARM.exidx*) __stop_unwind_idx = .; } .ARM.unwind_tab : { __start_unwind_tab = .; *(.ARM.extab*) __stop_unwind_tab = .; } #endif
_etext = .; /* End of text and rodata section */
#ifdef CONFIG_XIP_KERNEL __data_loc = ALIGN(4); /* location in binary */ . = PAGE_OFFSET + TEXT_OFFSET; #else . = ALIGN(THREAD_SIZE); __data_loc = .; #endif
.data : AT(__data_loc) { _data = .; /* address in memory */ _sdata = .;
/* * first, the init task union, aligned * to an 8192 byte boundary. */ INIT_TASK_DATA(THREAD_SIZE)
#ifdef CONFIG_XIP_KERNEL . = ALIGN(PAGE_SIZE); __init_begin = .; INIT_DATA ARM_EXIT_KEEP(EXIT_DATA) . = ALIGN(PAGE_SIZE); __init_end = .; #endif
NOSAVE_DATA CACHELINE_ALIGNED_DATA(32) READ_MOSTLY_DATA(32)
/* * The exception fixup table (might need resorting at runtime) */ . = ALIGN(32); __start___ex_table = .; #ifdef CONFIG_MMU *(__ex_table) #endif __stop___ex_table = .;
/* * and the usual data section */ DATA_DATA CONSTRUCTORS
_edata = .; } _edata_loc = __data_loc + SIZEOF(.data);
#ifdef CONFIG_HAVE_TCM /* * We align everything to a page boundary so we can * free it after init has commenced and TCM contents have * been copied to its destination. */ .tcm_start : { . = ALIGN(PAGE_SIZE); __tcm_start = .; __itcm_start = .; }
/* * Link these to the ITCM RAM * Put VMA to the TCM address and LMA to the common RAM * and we'll upload the contents from RAM to TCM and free * the used RAM after that. */ .text_itcm ITCM_OFFSET : AT(__itcm_start) { __sitcm_text = .; *(.tcm.text) *(.tcm.rodata) . = ALIGN(4); __eitcm_text = .; }
/* * Reset the dot pointer, this is needed to create the * relative __dtcm_start below (to be used as extern in code). */ . = ADDR(.tcm_start) + SIZEOF(.tcm_start) + SIZEOF(.text_itcm);
.dtcm_start : { __dtcm_start = .; }
/* TODO: add remainder of ITCM as well, that can be used for data! */ .data_dtcm DTCM_OFFSET : AT(__dtcm_start) { . = ALIGN(4); __sdtcm_data = .; *(.tcm.data) . = ALIGN(4); __edtcm_data = .; }
/* Reset the dot pointer or the linker gets confused */ . = ADDR(.dtcm_start) + SIZEOF(.data_dtcm);
/* End marker for freeing TCM copy in linked object */ .tcm_end : AT(ADDR(.dtcm_start) + SIZEOF(.data_dtcm)){ . = ALIGN(PAGE_SIZE); __tcm_end = .; } #endif
NOTES
BSS_SECTION(0, 0, 0) _end = .;
STABS_DEBUG .comment 0 : { *(.comment) }
/* Default discards */ DISCARDS
#ifndef CONFIG_SMP_ON_UP /DISCARD/ : { *(.alt.smp.init) } #endif }
先看第一個知識點:
(1). = PAGE_OFFSET + TEXT_OFFSET;
arch/arm/include/asm中的memory.h檔案定義了:
#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
CONFIG_PAGE_OFFSET是在核心配置裡面配置的,比如拿ok6410來說,我的配置: #define CONFIG_PAGE_OFFSET 0xC0000000
PAGE_OFFSET代表是的核心image的的起始虛擬地址。 在arch/arm/Makefile中有定義: textofs-y := 0x00008000 TEXT_OFFSET := $(textofs-y)
TEXT_OFFSET代表的是核心的image存放在記憶體中地址,注意這個地址為相對於記憶體的起始地址的偏移量,是個相對的偏移量不是實際的存放記憶體實體地址。而且這個偏移量取得有講究,必須為:0xXXXX8000,xxx為任意值。 所以:. = PAGE_OFFSET + TEXT_OFFSET就變成為:.=c0008000,這個地址就是核心存放在記憶體的虛擬的起始地址。從指令碼來看,也就是.stext的地址就是:c0008000,那麼我們如何驗證我們的這個說法呢,你可以開啟Linux目錄下的System.map檔案檢視: 00000020 A cpu_v6_suspend_size c0004000 A swapper_pg_dir c0008000 T __init_begin c0008000 T _stext T _sinittext c0008000 T _stext c0008000 T stext c000804c t __create_page_tables c0008148 t __enable_mmu_loc c0008154 t __enable_mmu c0008180 t __turn_mmu_on c0008198 t __enable_mmu_end c0008198 t __vet_atags c00081e0 t __mmap_switched c0008228 t __mmap_switched_data c000824c T lookup_processor_type c0008260 t set_reset_devices c0008284 t debug_kernel
從map檔案可以知道_stext是核心的入口地址,這個地址就是c0008000,這也驗證了我們在第一章講搭建環境的時候,我為什麼要用dnw c0008000,就是因為我們已經指定好了,核心的存放地址必須為c0008000,針對ok6410來說。 (二).init核心的初始化程式碼和資料段 _sinittext = .; HEAD_TEXT INIT_TEXT ARM_EXIT_KEEP(EXIT_TEXT) _einittext = .;
從上面的連結指令碼可以知道所有:_sinittext開頭的,_einittext結尾的HEAD_TEXT, INIT_TEXT,ARM_EXIT_KEEP(EXIT_TEXT)段都是從起始地址c0008000開始存放的。這些巨集定義在:include/asm-generic/vmlinux.lds.h中。 __arch_info_begin = .; *(.arch.info.init) __arch_info_end = .; __tagtable_begin = .; *(.taglist.init) __tagtable_end = .; #ifdef CONFIG_SMP_ON_UP __smpalt_begin = .; *(.alt.smp.init) __smpalt_end = .; #endif
__pv_table_begin = .; *(.pv_table) __pv_table_end = .;
緊接著以此存放的是:*(.arch.info.init), *(.taglist.init),*(.alt.smp.init),*(.pv_table)段的程式碼,其中我們最熟悉的*(.arch.info.init)段的程式碼就是: #define MACHINE_START(_type,_name) \ static const struct machine_desc __mach_desc_##_type \ __used \ __attribute__((__section__(".arch.info.init"))) = { \ .nr = MACH_TYPE_##_type, \ .name = _name,
#define MACHINE_END \ };
也就是說所有平臺的下面這段程式碼都放到了*(.arch.info.init)段中: MACHINE_START(MCUOS6410, "MCUOS6410") /* Maintainer: Ben Dooks <[email protected]> */ .boot_params = S3C64XX_PA_SDRAM + 0x100,
.init_irq = s3c6410_init_irq, .map_io = mcuos6410_map_io, .init_machine = mcuos6410_machine_init, .timer = &s3c24xx_timer, MACHINE_END
緊接著存放的是初始化資料段: #ifndef CONFIG_XIP_KERNEL __init_begin = _stext; INIT_DATA ARM_EXIT_KEEP(EXIT_DATA) #endif }
PERCPU_SECTION(32)
#ifndef CONFIG_XIP_KERNEL . = ALIGN(PAGE_SIZE); __init_end = .; #endif
這個段存放的是所有*(.init.data),*(.init.rodata)段中的程式碼。
.init段中的程式碼段和資料段,在Linux初始化完成之後,這個段的記憶體都會被請空,被釋放。因為他們只需要在初始化的時候使用一次,沒有必要再駐留在記憶體中,浪費空間。為了驗證我們以上的分析,我們可以檢視system.map這個檔案,下面我們看到的從_sinittext開頭的,_einittext結尾的所有函式,都會在初始化完成之後被釋放: c0008000 T _sinittext c0008000 T _stext c0008000 T stext c000804c t __create_page_tables c0008148 t __enable_mmu_loc c0008154 t __enable_mmu c0008180 t __turn_mmu_on c0008198 t __enable_mmu_end c0008198 t __vet_atags c00081e0 t __mmap_switched c0008228 t __mmap_switched_data c000824c T lookup_processor_type c0008260 t set_reset_devices c0008284 t debug_kernel c00082a8 t quiet_kernel c00082cc t init_setup c0008308 t rdinit_setup c0008344 W smp_setup_processor_id c0008354 W thread_info_cache_init c0008364 t loglevel c0008398 T parse_early_options c00083d4 t unknown_bootoption c00085f8 T parse_early_param c0008648 t do_early_param c0008724 t kernel_init c0008850 T start_kernel c0008b9c t readonly c0008bd0 t readwrite c0008c04 t rootwait_setup c0008c34 t root_data_setup c0008c54 t fs_names_setup c0008c74 t load_ramdisk c0008ca4 t root_dev_setup c0008ccc t root_delay_setup c0008cf8 T change_floppy c0008e20 T mount_block_root c0009108 T mount_root c0009174 T prepare_namespace c0009364 t ramdisk_start_setup c0009390 t prompt_ramdisk c00093c0 t error c00093fc t compr_fill c0009464 t compr_flush c00094d4 T rd_load_image c0009b5c T rd_load_disk c0009c10 t no_initrd c0009c34 T initrd_load c0009f74 t do_linuxrc c0009fc8 t error c0009fec t read_into c000a0a8 t do_start c000a0d4 t write_buffer c000a12c t flush_buffer c000a1d8 t retain_initrd_param c000a208 t clean_path c000a268 t do_utime c000a2ac t do_symlink c000a378 t unpack_to_rootfs c000a6d4 t maybe_link c000a840 t do_name c000aad8 t do_header c000adf0 t free_initrd
(3)真正的駐留在記憶體中的核心內程式碼段 從指令碼上來看,所有.text開始,.etext結束區間的段都是核心程式碼段: .text : { /* Real text segment */ _text = .; /* Text and read-only data */ __exception_text_start = .; *(.exception.text) __exception_text_end = .; IRQENTRY_TEXT TEXT_TEXT SCHED_TEXT LOCK_TEXT KPROBES_TEXT #ifdef CONFIG_MMU *(.fixup) #endif *(.gnu.warning) *(.rodata) *(.rodata.*) *(.glue_7) *(.glue_7t) . = ALIGN(4); *(.got) /* Global offset table */ ARM_CPU_KEEP(PROC_INFO) }
RO_DATA(PAGE_SIZE)
#ifdef CONFIG_ARM_UNWIND /* * Stack unwinding tables */ . = ALIGN(8); .ARM.unwind_idx : { __start_unwind_idx = .; *(.ARM.exidx*) __stop_unwind_idx = .; } .ARM.unwind_tab : { __start_unwind_tab = .; *(.ARM.extab*) __stop_unwind_tab = .; } #endif
_etext = .; /* End of text and rodata section */ 複製程式碼
這些段中的程式碼都是事實在在的核心函式。 (4)已經初始化的data資料段: .data : AT(__data_loc) { _data = .; /* address in memory */ _sdata = .;
/* * first, the init task union, aligned * to an 8192 byte boundary. */ INIT_TASK_DATA(THREAD_SIZE)
#ifdef CONFIG_XIP_KERNEL . = ALIGN(PAGE_SIZE); __init_begin = .; INIT_DATA ARM_EXIT_KEEP(EXIT_DATA) . = ALIGN(PAGE_SIZE); __init_end = .; #endif
NOSAVE_DATA CACHELINE_ALIGNED_DATA(32) READ_MOSTLY_DATA(32)
/* * The exception fixup table (might need resorting at runtime) */ . = ALIGN(32); __start___ex_table = .; #ifdef CONFIG_MMU *(__ex_table) #endif __stop___ex_table = .;
/* * and the usual data section */ DATA_DATA CONSTRUCTORS
_edata = .;
(5)緊接著以初始化的.data段的是.bss,未經初始化的核心資料段。 BSS_SECTION(0, 0, 0) _end = .;
最後我給出這些段的大致位置圖,請參考我們上面所講的來看: --------------------- 作者:dumb_man 來源:CSDN 原文:https://blog.csdn.net/dahailantian1/article/details/78584841 版權宣告:本文為博主原創文章,轉載請附上博文連結!