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文件定义了:
CONFIG_PAGE_OFFSET是在内核配置里面配置的,比如拿ok6410来说,我的配置:#define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET)
#define CONFIG_PAGE_OFFSET 0xC0000000
textofs-y := 0x00008000
TEXT_OFFSET := $(textofs-y)
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
从上面的链接脚本可以知道所有:_sinittext开头的,_einittext结尾的HEAD_TEXT, INIT_TEXT,ARM_EXIT_KEEP(EXIT_TEXT)段都是从起始地址c0008000开始存放的。这些宏定义在:include/asm-generic/vmlinux.lds.h中。_sinittext = .;
HEAD_TEXT
INIT_TEXT
ARM_EXIT_KEEP(EXIT_TEXT)
_einittext = .;
紧接着以此存放的是:*(.arch.info.init), *(.taglist.init),*(.alt.smp.init),*(.pv_table)段的代码,其中我们最熟悉的*(.arch.info.init)段的代码就是:__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)段中:#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 \
};
紧接着存放的是初始化数据段:MACHINE_START(MCUOS6410, "MCUOS6410")
/* Maintainer: Ben Dooks*/
.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
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
(5)紧接着以初始化的.da ta段的是.bss,未经初始化的内核数据段。.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 = .;
最后我给出这些段的大致位置图,请参考我们上面所讲的来看:BSS_SECTION(0, 0, 0)
_end = .;