kernel移植之linux stage2:启动内核
2011-10-07 17:58
731 查看
linux/init/main.c
在此函数中又调用
line34:printk(KERN_NOTICE "%s", linux_banner); 开机打印出内核版本的就是它,比如
Linux version 2.6.39.4 (root@localhost.localdomain) (gcc version 4.4.3 (ctng-1.6.1) ) #3 Thu Oct 6 20:18:51 CST 2011
然后调用
line35:setup_arch(&command_line),此函数位于arch/arm/kernel/setup.c
在上篇有行
.boot_params = S3C2410_SDRAM_PA + 0x100,
在linux/arch/arm/mach-s3c2410/include/mach/map.h中
#define S3C2410_SDRAM_PA (S3C2410_CS6)
#define S3C2410_CS6 (0x30000000)
所以.boot_params = 0x30000100, 即如果uboot未指定tag存放的地址,linux会向这个默认的地址去寻找tag
可参考http://blog.chinaunix.net/space.php?uid=20564848&do=blog&id=73978
在setup.c中有如下结构__initdata
http://blog.csdn.net/babyfans/article/details/5819837
嵌入式Linux应用开发完全手册 ch16.3 p321
总结一下stage2的流程,start_kernell函数调用了几乎所有的初始化函数,直到建立第1个进程
知道了内核启动流程,就可以判断内核启动打印信息各是哪里发出的了,如下
清晰点的图片见download.csdn.net/detail/songqqnew/3664142
有几个问题,
1.如果uboot向内核传递的内存tag为sdram的起始地址0x3000 0000,大小0x400 0000(64M)和命令行tag中有比如mem=60M的字符串,linux系统最多也只能使用前60M的sdram(见嵌入式linux应用开发完全手册p323),那最后4M干嘛呢?后4M还属于linux的管理范围吗?
2.uboot中已经设置了fclk,hclk,pclk,内核启动时初始化时钟期间,是再去设置一遍呢还是去读取uboot的设置呢?
3.内核启动期间,打印了Virtual kernel memory layout,这些虚拟区间该怎么划分?特别是DMA虚拟区间位于0xffc0 0000--0xffe0 0000,其对应的物理区间可以在sdram的任意部分吗,linux为这些区间建立内核页表的代码在哪里
-------------------------------------------------------------------------------------------------------------------------------------------
2013-12-9
mini6410_machine_init什么时候调用的?:
setup_arch()调用了setup_machine(machine_arch_type),此函数返回了machine_desc指针,正是通过宏MACHINE_START在板子文件定了的板子描述结构体struct machine_desc __mach_desc_MINI6410:
MACHINE_START(MINI6410, "MINI6410")
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.boot_params
= S3C64XX_PA_SDRAM + 0x100,
.init_irq
= s3c6410_init_irq,
.map_io
= mini6410_map_io,
.init_machine
= mini6410_machine_init,
.timer
= &s3c24xx_timer,
MACHINE_END
在setup_arch()里面将setup_machine()返回的machine_desc类型指针赋给了全局变量machine_desc
然后在rest_init()->kernel_init->do_basic_setup()->do_initcalls()里面调用customize_machine()调用init_machine()
do_initcalls是怎么调用customize_machine()的?
通过宏arch_initcall(customize_machine);将符号链接到initcall段里面
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
其余各个成员函数在setup_arch()中被赋值到内核结构体,在不同时期被调用:
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->tegra_stingray_init()
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. .init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
3. .map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
4. .timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。在start_kernel() --> time_init()中被调用。
5. .boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。
http://blog.csdn.net/zhaohc_nj/article/details/7938116
-----------------------------------------------------------------------------------------------------------------------------------------------
2013-12-15 20
编译进内核的驱动怎么被调用?
比如:
static int __init helloinit(void)
{
printk("Hello, module is installed !\n");
return 0;
}
module_init(helloinit);
大部分宏和函数实现在inlcude/linux/init.h
module_init(fn)-->>__initcall(fn)-->>device_initcall(fn)-->>__define_initcall("6",fn,6)
其中
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
所以
#define __define_initcall(6,helloinit,6)
static initcall_t __initcall_helloinit6 __used
__attribute__((__section__(".initcall" 6 ".init"))) =helloinit
即:
1) 声明一个名称为__initcall_helloinit6的函数指针;
2) 将这个函数指针指向helloinit;
3) 编译的时候需要把这个函数指针变量放置到名称为 .initcall6.init的section中。
问题1:__attribute__和__section__的作用:
ATTRIBUTE是属性的说明,多个说明之间以逗号分隔。GCC可以支持十几个属性,__section__便是其中之一。
属性section用于函数和变量,连接器可以把相同节的代码或数据安排在一起,Linux 内核很喜欢使用这种技术,比如__init修饰的所有代码都会被放在.init.text节里,初始化结束后就可以释放这部分内存。
问题2:很多宏,如下,都是调用了__define_initcall来将自己放入某个init节里面。就包括了和mini6410_machine_init相关的arch_initcall,而arch_initcall作用于init3,所以mini6410_machine_init位于init3。
既然这个__initcall_helloinit6指针定义在了.initcall6.init节,那么是什么时候被调用的呢?
rest_init()->kernel_init->do_basic_setup()->do_initcalls()
init/main.c
static void __init do_initcalls(void)
{
initcall_t *fn;
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
}
会依次调用下面各个init节中的函数
include/asm-generic/vmlinux.lds.h
所有编译进内核的驱动都被链接在built-in.o,使用arm-linux-readelf -S查看此重定位文件中包含的各个init段:
不过只发现initcall2.init,initcall4.init,initcall5.init,initcall6.init,initcall7.init
----------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------
关于程序的连接原理及实例
待续
-------------------------------------------------------------------------------------------------------------------------------------------
linux image的生成过程
待续
-------------------------------------------------------------------------------------------------------------------------------------------
asmlinkage void __init start_kernel(void) { char * command_line; extern struct kernel_param __start___param[], __stop___param[]; smp_setup_processor_id(); /* * Need to run as early as possible, to initialize the * lockdep hash: */ lockdep_init(); debug_objects_early_init(); /* * Set up the the initial canary ASAP: */ boot_init_stack_canary(); cgroup_init_early(); local_irq_disable(); early_boot_irqs_off(); early_init_irq_lock_class(); /* * Interrupts are still disabled. Do necessary setups, then * enable them */ lock_kernel(); tick_init(); boot_cpu_init(); page_address_init(); printk(KERN_NOTICE "%s", linux_banner); setup_arch(&command_line); mm_init_owner(&init_mm, &init_task); setup_command_line(command_line); setup_nr_cpu_ids(); setup_per_cpu_areas(); smp_prepare_boot_cpu(); /* arch-specific boot-cpu hooks */ build_all_zonelists(NULL); page_alloc_init(); printk(KERN_NOTICE "Kernel command line: %s\n", boot_command_line); parse_early_param(); parse_args("Booting kernel", static_command_line, __start___param, __stop___param - __start___param, &unknown_bootoption); /* * These use large bootmem allocations and must precede * kmem_cache_init() */ pidhash_init(); vfs_caches_init_early(); sort_main_extable(); trap_init(); mm_init(); /* * Set up the scheduler prior starting any interrupts (such as the * timer interrupt). Full topology setup happens at smp_init() * time - but meanwhile we still have a functioning scheduler. */ sched_init(); /* * Disable preemption - early bootup scheduling is extremely * fragile until we cpu_idle() for the first time. */ preempt_disable(); if (!irqs_disabled()) { printk(KERN_WARNING "start_kernel(): bug: interrupts were " "enabled *very* early, fixing it\n"); local_irq_disable(); } rcu_init(); radix_tree_init(); /* init some links before init_ISA_irqs() */ early_irq_init(); init_IRQ(); prio_tree_init(); init_timers(); hrtimers_init(); softirq_init(); timekeeping_init(); time_init(); profile_init(); if (!irqs_disabled()) printk(KERN_CRIT "start_kernel(): bug: interrupts were " "enabled early\n"); early_boot_irqs_on(); local_irq_enable(); /* Interrupts are enabled now so all GFP allocations are safe. */ gfp_allowed_mask = __GFP_BITS_MASK; kmem_cache_init_late(); /* * HACK ALERT! This is early. We're enabling the console before * we've done PCI setups etc, and console_init() must be aware of * this. But we do want output early, in case something goes wrong. */ console_init(); if (panic_later) panic(panic_later, panic_param); lockdep_info(); /* * Need to run this when irqs are enabled, because it wants * to self-test [hard/soft]-irqs on/off lock inversion bugs * too: */ locking_selftest(); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && page_to_pfn(virt_to_page((void *)initrd_start)) < min_low_pfn) { printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - " "disabling it.\n", page_to_pfn(virt_to_page((void *)initrd_start)), min_low_pfn); initrd_start = 0; } #endif page_cgroup_init(); enable_debug_pagealloc(); kmemtrace_init(); kmemleak_init(); debug_objects_mem_init(); idr_init_cache(); setup_per_cpu_pageset(); numa_policy_init(); if (late_time_init) late_time_init(); sched_clock_init(); calibrate_delay(); pidmap_init(); anon_vma_init(); #ifdef CONFIG_X86 if (efi_enabled) efi_enter_virtual_mode(); #endif thread_info_cache_init(); cred_init(); fork_init(totalram_pages); proc_caches_init(); buffer_init(); key_init(); security_init(); dbg_late_init(); vfs_caches_init(totalram_pages); signals_init(); /* rootfs populating might need page-writeback */ page_writeback_init(); #ifdef CONFIG_PROC_FS proc_root_init(); #endif cgroup_init(); cpuset_init(); taskstats_init_early(); delayacct_init(); check_bugs(); acpi_early_init(); /* before LAPIC and SMP init */ sfi_init_late(); ftrace_init(); /* Do the rest non-__init'ed, we're now alive */ rest_init(); }stage2的入口函数start_kernel()如上,
在此函数中又调用
line34:printk(KERN_NOTICE "%s", linux_banner); 开机打印出内核版本的就是它,比如
Linux version 2.6.39.4 (root@localhost.localdomain) (gcc version 4.4.3 (ctng-1.6.1) ) #3 Thu Oct 6 20:18:51 CST 2011
然后调用
line35:setup_arch(&command_line),此函数位于arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p) { struct tag *tags = (struct tag *)&init_tags; struct machine_desc *mdesc; char *from = default_command_line; unwind_init(); setup_processor(); mdesc = setup_machine(machine_arch_type); machine_name = mdesc->name; if (mdesc->soft_reboot) reboot_setup("s"); if (__atags_pointer)//bootloader传入了非0的tags的物理地址,优先使用bootloader指定地址[luther.gliethttp] tags = phys_to_virt(__atags_pointer); else if (mdesc->boot_params)//否则使用默认的地址,0x30000100 tags = phys_to_virt(mdesc->boot_params);//转化成虚拟地址才可以操作,因为此时mmu已经启用 /* * If we have the old style parameters, convert them to * a tag list. */ if (tags->hdr.tag != ATAG_CORE) convert_to_tag_list(tags); if (tags->hdr.tag != ATAG_CORE) tags = (struct tag *)&init_tags; if (mdesc->fixup) mdesc->fixup(mdesc, tags, &from, &meminfo); if (tags->hdr.tag == ATAG_CORE) { if (meminfo.nr_banks != 0) squash_mem_tags(tags); save_atags(tags); parse_tags(tags); } init_mm.start_code = (unsigned long) _text; init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) _end; /* parse_early_param needs a boot_command_line */ strlcpy(boot_command_line, from, COMMAND_LINE_SIZE); /* populate cmd_line too for later use, preserving boot_command_line */ strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = cmd_line; parse_early_param(); paging_init(mdesc); request_standard_resources(&meminfo, mdesc); #ifdef CONFIG_SMP smp_init_cpus(); #endif cpu_init(); tcm_init(); /* * Set up various architecture-specific pointers */ init_arch_irq = mdesc->init_irq; system_timer = mdesc->timer; init_machine = mdesc->init_machine; #ifdef CONFIG_VT #if defined(CONFIG_VGA_CONSOLE) conswitchp = &vga_con; #elif defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; #endif #endif early_trap_init(); }line 11:取得在stage1中构造的__mach_desc_MINI2440结构,以获取其成员.boot_params(uboot传递给内核的参数在sdram上的地址)
在上篇有行
.boot_params = S3C2410_SDRAM_PA + 0x100,
在linux/arch/arm/mach-s3c2410/include/mach/map.h中
#define S3C2410_SDRAM_PA (S3C2410_CS6)
#define S3C2410_CS6 (0x30000000)
所以.boot_params = 0x30000100, 即如果uboot未指定tag存放的地址,linux会向这个默认的地址去寻找tag
可参考http://blog.chinaunix.net/space.php?uid=20564848&do=blog&id=73978
在setup.c中有如下结构__initdata
static struct init_tags { struct tag_header hdr1; struct tag_core core; struct tag_header hdr2; struct tag_mem32 mem; struct tag_header hdr3; } init_tags __initdata = { { tag_size(tag_core), ATAG_CORE }, { 1, PAGE_SIZE, 0xff }, { tag_size(tag_mem32), ATAG_MEM }, { MEM_SIZE, PHYS_OFFSET }, { 0, ATAG_NONE } };而在linux/arch/arm/include/asm/setup.h是有关的tag定义,恩,是不是很眼熟,估计uboot的uboot/include/asm-arm/setup.h里的代码大部分就是从这里考的
/* * linux/include/asm/setup.h * * Copyright (C) 1997-1999 Russell King * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Structure passed to kernel to tell it about the * hardware it's running on. See Documentation/arm/Setup * for more info. */ #ifndef __ASMARM_SETUP_H #define __ASMARM_SETUP_H #include <linux/types.h> #define COMMAND_LINE_SIZE 1024 /* The list ends with an ATAG_NONE node. */ #define ATAG_NONE 0x00000000 struct tag_header { __u32 size; __u32 tag; }; /* The list must start with an ATAG_CORE node */ #define ATAG_CORE 0x54410001 struct tag_core { __u32 flags; /* bit 0 = read-only */ __u32 pagesize; __u32 rootdev; }; /* it is allowed to have multiple ATAG_MEM nodes */ #define ATAG_MEM 0x54410002 struct tag_mem32 { __u32 size; __u32 start; /* physical start address */ }; /* VGA text type displays */ #define ATAG_VIDEOTEXT 0x54410003 struct tag_videotext { __u8 x; __u8 y; __u16 video_page; __u8 video_mode; __u8 video_cols; __u16 video_ega_bx; __u8 video_lines; __u8 video_isvga; __u16 video_points; }; /* describes how the ramdisk will be used in kernel */ #define ATAG_RAMDISK 0x54410004 struct tag_ramdisk { __u32 flags; /* bit 0 = load, bit 1 = prompt */ __u32 size; /* decompressed ramdisk size in _kilo_ bytes */ __u32 start; /* starting block of floppy-based RAM disk image */ }; /* describes where the compressed ramdisk image lives (virtual address) */ /* * this one accidentally used virtual addresses - as such, * it's deprecated. */ #define ATAG_INITRD 0x54410005 /* describes where the compressed ramdisk image lives (physical address) */ #define ATAG_INITRD2 0x54420005 struct tag_initrd { __u32 start; /* physical start address */ __u32 size; /* size of compressed ramdisk image in bytes */ }; /* board serial number. "64 bits should be enough for everybody" */ #define ATAG_SERIAL 0x54410006 struct tag_serialnr { __u32 low; __u32 high; }; /* board revision */ #define ATAG_REVISION 0x54410007 struct tag_revision { __u32 rev; }; /* initial values for vesafb-type framebuffers. see struct screen_info * in include/linux/tty.h */ #define ATAG_VIDEOLFB 0x54410008 struct tag_videolfb { __u16 lfb_width; __u16 lfb_height; __u16 lfb_depth; __u16 lfb_linelength; __u32 lfb_base; __u32 lfb_size; __u8 red_size; __u8 red_pos; __u8 green_size; __u8 green_pos; __u8 blue_size; __u8 blue_pos; __u8 rsvd_size; __u8 rsvd_pos; }; /* command line: \0 terminated string */ #define ATAG_CMDLINE 0x54410009 struct tag_cmdline { char cmdline[1]; /* this is the minimum size */ }; /* acorn RiscPC specific information */ #define ATAG_ACORN 0x41000101 struct tag_acorn { __u32 memc_control_reg; __u32 vram_pages; __u8 sounddefault; __u8 adfsdrives; }; /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */ #define ATAG_MEMCLK 0x41000402 struct tag_memclk { __u32 fmemclk; }; struct tag { struct tag_header hdr; union { struct tag_core core; struct tag_mem32 mem; struct tag_videotext videotext; struct tag_ramdisk ramdisk; struct tag_initrd initrd; struct tag_serialnr serialnr; struct tag_revision revision; struct tag_videolfb videolfb; struct tag_cmdline cmdline; /* * Acorn specific */ struct tag_acorn acorn; /* * DC21285 specific */ struct tag_memclk memclk; } u; }; struct tagtable { __u32 tag; int (*parse)(const struct tag *); }; #define tag_member_present(tag,member) \ ((unsigned long)(&((struct tag *)0L)->member + 1) \ <= (tag)->hdr.size * 4) #define tag_next(t) ((struct tag *)((__u32 *)(t) + (t)->hdr.size)) #define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) #define for_each_tag(t,base) \ for (t = base; t->hdr.size; t = tag_next(t)) #ifdef __KERNEL__ #define __tag __used __attribute__((__section__(".taglist.init"))) #define __tagtable(tag, fn) \ static struct tagtable __tagtable_##fn __tag = { tag, fn } /* * Memory map description */ #ifdef CONFIG_ARCH_LH7A40X # define NR_BANKS 16 #else # define NR_BANKS 8 #endif struct membank { unsigned long start; unsigned long size; unsigned short node; unsigned short highmem; }; struct meminfo { int nr_banks; struct membank bank[NR_BANKS]; }; extern struct meminfo meminfo; #define for_each_nodebank(iter,mi,no) \ for (iter = 0; iter < (mi)->nr_banks; iter++) \ if ((mi)->bank[iter].node == no) #define bank_pfn_start(bank) __phys_to_pfn((bank)->start) #define bank_pfn_end(bank) __phys_to_pfn((bank)->start + (bank)->size) #define bank_pfn_size(bank) ((bank)->size >> PAGE_SHIFT) #define bank_phys_start(bank) (bank)->start #define bank_phys_end(bank) ((bank)->start + (bank)->size) #define bank_phys_size(bank) (bank)->size #endif /* __KERNEL__ */ #endiflinux怎么解析这些参数的,可参考
http://blog.csdn.net/babyfans/article/details/5819837
嵌入式Linux应用开发完全手册 ch16.3 p321
总结一下stage2的流程,start_kernell函数调用了几乎所有的初始化函数,直到建立第1个进程
知道了内核启动流程,就可以判断内核启动打印信息各是哪里发出的了,如下
清晰点的图片见download.csdn.net/detail/songqqnew/3664142
有几个问题,
1.如果uboot向内核传递的内存tag为sdram的起始地址0x3000 0000,大小0x400 0000(64M)和命令行tag中有比如mem=60M的字符串,linux系统最多也只能使用前60M的sdram(见嵌入式linux应用开发完全手册p323),那最后4M干嘛呢?后4M还属于linux的管理范围吗?
2.uboot中已经设置了fclk,hclk,pclk,内核启动时初始化时钟期间,是再去设置一遍呢还是去读取uboot的设置呢?
3.内核启动期间,打印了Virtual kernel memory layout,这些虚拟区间该怎么划分?特别是DMA虚拟区间位于0xffc0 0000--0xffe0 0000,其对应的物理区间可以在sdram的任意部分吗,linux为这些区间建立内核页表的代码在哪里
-------------------------------------------------------------------------------------------------------------------------------------------
2013-12-9
mini6410_machine_init什么时候调用的?:
setup_arch()调用了setup_machine(machine_arch_type),此函数返回了machine_desc指针,正是通过宏MACHINE_START在板子文件定了的板子描述结构体struct machine_desc __mach_desc_MINI6410:
MACHINE_START(MINI6410, "MINI6410")
/* Maintainer: Ben Dooks <ben-linux@fluff.org> */
.boot_params
= S3C64XX_PA_SDRAM + 0x100,
.init_irq
= s3c6410_init_irq,
.map_io
= mini6410_map_io,
.init_machine
= mini6410_machine_init,
.timer
= &s3c24xx_timer,
MACHINE_END
在setup_arch()里面将setup_machine()返回的machine_desc类型指针赋给了全局变量machine_desc
然后在rest_init()->kernel_init->do_basic_setup()->do_initcalls()里面调用customize_machine()调用init_machine()
do_initcalls是怎么调用customize_machine()的?
通过宏arch_initcall(customize_machine);将符号链接到initcall段里面
MACHINE_START主要是定义了"struct machine_desc"的类型,放在 section(".arch.info.init"),是初始化数据,Kernel 起来之后将被丢弃。
其余各个成员函数在setup_arch()中被赋值到内核结构体,在不同时期被调用:
start_kernel()--->setup_arch()--->do_initcalls()--->customize_machine()--->tegra_stingray_init()
1. .init_machine 在 arch/arm/kernel/setup.c 中被 customize_machine 调用,放在 arch_initcall() 段里面,会自动按顺序被调用。
2. .init_irq在start_kernel() --> init_IRQ() --> init_arch_irq()中被调用
3. .map_io 在 setup_arch() --> paging_init() --> devicemaps_init()中被调用
4. .timer是定义系统时钟,定义TIMER4为系统时钟,在arch/arm/plat-s3c/time.c中体现。在start_kernel() --> time_init()中被调用。
5. .boot_params是bootloader向内核传递的参数的位置,这要和bootloader中参数的定义要一致。
http://blog.csdn.net/zhaohc_nj/article/details/7938116
-----------------------------------------------------------------------------------------------------------------------------------------------
2013-12-15 20
编译进内核的驱动怎么被调用?
比如:
static int __init helloinit(void)
{
printk("Hello, module is installed !\n");
return 0;
}
module_init(helloinit);
大部分宏和函数实现在inlcude/linux/init.h
module_init(fn)-->>__initcall(fn)-->>device_initcall(fn)-->>__define_initcall("6",fn,6)
其中
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
所以
#define __define_initcall(6,helloinit,6)
static initcall_t __initcall_helloinit6 __used
__attribute__((__section__(".initcall" 6 ".init"))) =helloinit
即:
1) 声明一个名称为__initcall_helloinit6的函数指针;
2) 将这个函数指针指向helloinit;
3) 编译的时候需要把这个函数指针变量放置到名称为 .initcall6.init的section中。
问题1:__attribute__和__section__的作用:
ATTRIBUTE是属性的说明,多个说明之间以逗号分隔。GCC可以支持十几个属性,__section__便是其中之一。
属性section用于函数和变量,连接器可以把相同节的代码或数据安排在一起,Linux 内核很喜欢使用这种技术,比如__init修饰的所有代码都会被放在.init.text节里,初始化结束后就可以释放这部分内存。
问题2:很多宏,如下,都是调用了__define_initcall来将自己放入某个init节里面。就包括了和mini6410_machine_init相关的arch_initcall,而arch_initcall作用于init3,所以mini6410_machine_init位于init3。
#define pure_initcall(fn) __define_initcall("0",fn,0) #define core_initcall(fn) __define_initcall("1",fn,1) #define core_initcall_sync(fn) __define_initcall("1s",fn,1s) #define postcore_initcall(fn) __define_initcall("2",fn,2) #define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s) #define arch_initcall(fn)__define_initcall("3",fn,3) #define arch_initcall_sync(fn) __define_initcall("3s",fn,3s) #define subsys_initcall(fn) __define_initcall("4",fn,4) #define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s) #define fs_initcall(fn) __define_initcall("5",fn,5) #define fs_initcall_sync(fn) __define_initcall("5s",fn,5s) #define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs) #define device_initcall(fn)__define_initcall("6",fn,6) #define device_initcall_sync(fn) __define_initcall("6s",fn,6s) #define late_initcall(fn) __define_initcall("7",fn,7) #define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
既然这个__initcall_helloinit6指针定义在了.initcall6.init节,那么是什么时候被调用的呢?
rest_init()->kernel_init->do_basic_setup()->do_initcalls()
init/main.c
static void __init do_initcalls(void)
{
initcall_t *fn;
for (fn = __early_initcall_end; fn < __initcall_end; fn++)
do_one_initcall(*fn);
}
会依次调用下面各个init节中的函数
include/asm-generic/vmlinux.lds.h
#define INITCALLS \ *(.initcallearly.init) \ VMLINUX_SYMBOL(__early_initcall_end) = .; \ *(.initcall0.init) \ *(.initcall0s.init) \ *(.initcall1.init) \ *(.initcall1s.init) \ *(.initcall2.init) \ *(.initcall2s.init) \ *(.initcall3.init) \ *(.initcall3s.init) \ *(.initcall4.init) \ *(.initcall4s.init) \ *(.initcall5.init) \ *(.initcall5s.init) \ *(.initcallrootfs.init) \ *(.initcall6.init) \ *(.initcall6s.init) \ *(.initcall7.init) \ *(.initcall7s.init)
所有编译进内核的驱动都被链接在built-in.o,使用arm-linux-readelf -S查看此重定位文件中包含的各个init段:
不过只发现initcall2.init,initcall4.init,initcall5.init,initcall6.init,initcall7.init
root@song-virtual-machine:drivers# arm-linux-readelf -S built-in.o There are 107 section headers, starting at offset 0x22d0ca8: Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al *[60] .initcall4.init PROGBITS 00000000 277954 000028 00 WA 0 0 4 [61] .rel.initcall4.in REL 00000000 2357598 000050 08 105 60 4 [62] .init.setup PROGBITS 00000000 27797c 000048 00 WA 0 0 4 [63] .rel.init.setup REL 00000000 23575e8 000060 08 105 62 4 [64] .data..read_mostl PROGBITS 00000000 2779c4 0002f8 00 WA 0 0 4 [65] .rel.data..read_m REL 00000000 2357648 000010 08 105 64 4 *[66] .initcall6.init PROGBITS 00000000 277cbc 0002d4 00 WA 0 0 4 [67] .rel.initcall6.in REL 00000000 2357658 0005a8 08 105 66 4 [68] .init.data PROGBITS 00000000 277f90 003174 00 WA 0 0 4 [69] .rel.init.data REL 00000000 2357c00 000de8 08 105 68 4 [70] .exitcall.exit PROGBITS 00000000 27b104 0002e8 00 WA 0 0 4 [71] .rel.exitcall.exi REL 00000000 23589e8 0005d0 08 105 70 4 *[72] .initcall2.init PROGBITS 00000000 27b3ec 00000c 00 WA 0 0 4 [73] .rel.initcall2.in REL 00000000 2358fb8 000018 08 105 72 4 [74] .con_initcall.ini PROGBITS 00000000 27b3f8 000008 00 WA 0 0 4 [75] .rel.con_initcall REL 00000000 2358fd0 000010 08 105 74 4 *[76] .initcall5.init PROGBITS 00000000 27b400 000008 00 WA 0 0 4 [77] .rel.initcall5.in REL 00000000 2358fe0 000010 08 105 76 4 *[78] .initcall7.init PROGBITS 00000000 27b408 00000c 00 WA 0 0 4 [79] .rel.initcall7.in REL 00000000 2358ff0 000018 08 105 78 4而是用arm-linux-readelf -s built-in.o |grep 66,可以查看连接在initcall6.init段里的符号:
24044: 00000258 4 OBJECT LOCAL DEFAULT 66 __initcall_s3c_tvenc_init 24066: 00167e88 0 NOTYPE LOCAL DEFAULT 1 $a 24110: 0000025c 0 NOTYPE LOCAL DEFAULT 66 $d 24111: 0000025c 4 OBJECT LOCAL DEFAULT 66 __initcall_s3c_tvscaler_p 24166: 00006fe4 0 NOTYPE LOCAL DEFAULT 3 $a 24180: 00000260 0 NOTYPE LOCAL DEFAULT 66 $d 24181: 00000260 4 OBJECT LOCAL DEFAULT 66 __initcall_s3c_rotator_in 24266: 0000c520 0 NOTYPE LOCAL DEFAULT 56 $d 24299: 00000264 0 NOTYPE LOCAL DEFAULT 66 $d 24300: 00000264 4 OBJECT LOCAL DEFAULT 66 __initcall_s3c_jpeg_init6 24366: 0016ad74 0 NOTYPE LOCAL DEFAULT 1 $d 24373: 00000268 0 NOTYPE LOCAL DEFAULT 66 $d 24374: 00000268 4 OBJECT LOCAL DEFAULT 66 __initcall_s3c_g2d_init6
----------------------------------------------------------------------------------------------------------------------------------------------
NAME readelf - Displays information about ELF files. SYNOPSIS readelf [-a|--all] [-h|--file-header]//elf文件头,包含Entry point address [-l|--program-headers|--segments] [-S|--section-headers|--sections]//段信息 [-g|--section-groups] [-t|--section-details] [-e|--headers] [-s|--syms|--symbols]//符号表 [--dyn-syms] [-n|--notes] [-r|--relocs] [-u|--unwind] [-d|--dynamic] [-V|--version-info] [-A|--arch-specific] [-D|--use-dynamic] [-x <number or name>|--hex-dump=<number or name>] [-p <number or name>|--string-dump=<number or name>] [-R <number or name>|--relocated-dump=<number or name>] [-c|--archive-index] [-w[lLiaprmfFsoRt]| --debug-dump[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index]] [-I|--histogram] [-v|--version] [-W|--wide] [-H|--help] elffile...
NAME objdump - display information from object files. SYNOPSIS objdump [-a|--archive-headers] [-b bfdname|--target=bfdname] [-C|--demangle[=style] ] [-d|--disassemble] [-D|--disassemble-all] [-z|--disassemble-zeroes] [-EB|-EL|--endian={big | little }] [-f|--file-headers] [-F|--file-offsets] [--file-start-context] [-g|--debugging] [-e|--debugging-tags] [-h|--section-headers|--headers]//段头 [-i|--info] [-j section|--section=section] [-l|--line-numbers] [-S|--source]//汇编和c组合显示的源代码 [-m machine|--architecture=machine] [-M options|--disassembler-options=options] [-p|--private-headers] [-r|--reloc] [-R|--dynamic-reloc] [-s|--full-contents]//二进制代码 [-W[lLiaprmfFsoRt]| --dwarf[=rawline,=decodedline,=info,=abbrev,=pubnames,=aranges,=macro,=frames,=frames-interp,=str,=loc,=Ranges,=pubtypes,=trace_info,=trace_abbrev,=trace_aranges,=gdb_index]] [-G|--stabs] [-t|--syms] [-T|--dynamic-syms] [-w|--wide] [--start-address=address] [--stop-address=address] [--prefix-addresses] [--[no-]show-raw-insn] [--adjust-vma=offset] [--special-syms] [--prefix=prefix] [--prefix-strip=level] [--insn-width=width] [-V|--version] [-H|--help] objfile...
-------------------------------------------------------------------------------------------------------------------------------------------
关于程序的连接原理及实例
待续
-------------------------------------------------------------------------------------------------------------------------------------------
linux image的生成过程
待续
-------------------------------------------------------------------------------------------------------------------------------------------
相关文章推荐
- 嵌入式Linux之Kernel(裁减移植)启动调试、打印技术_printascii(补充)、内核调试.doc
- 嵌入式Linux系统内核Kernel(裁减移植)启动调试技术(回顾)
- 嵌入式Linux之Kernel(裁减移植)启动调试、打印技术 printascii(补充)、内核调试
- s3c2440移植linux-2.6.22 内核启动卡在Uncompressing Linux..... done, booting the kernel.
- 嵌入式Linux之Kernel(裁减移植)启动调试、打印技术 printascii(补充)、内核调试
- linux 内核移植(六)——C语言启动部分分析(一)
- linux内核升级或者patch 后修改kernel开机启动顺序
- ARM-Linux内核移植之(一)——内核启动流程分析 .
- 跟踪分析Linux内核的启动过程(start_kernel到init进程启动)
- arm-linux内核start_kernel之前启动分析(2)- 页表的准备
- linux内核初始化及启动之start_kernel
- arm-linux内核start_kernel之前启动分析(1)-接过bootloader的衣钵
- Linux内核移植 part1:Exynos4412 sd卡启动uboot
- linux2.4启动分析(2)---内核解压缩过程 compress booting kernel
- Linux Kernel Boot Parameters(Linux内核启动参数)
- ARM-Linux内核移植之(一)——内核启动流程分析
- Linux-2.6.32.2内核在mini2440上的移植----通过LCD显示内核启动信息及修改Linux LOGO
- 嵌入式linux开发uboot移植(四)——uboot启动内核的机制
- 嵌入式 Linux开发Kernel移植(二)——kernel内核配置和编译
- 【嵌入式Linux学习七步曲之第四篇 Linux内核移植】PPC Linux启动流程分析