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_ helloinit 6 __used
__attribute__((__section__(".initcall" 6 ".init"))) = helloinit
即:
1) 声明一个名称为 __initcall_ helloinit 6 的函数指针;
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_ helloinit 6指针定义在了.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__ */
#endif
linux怎么解析这些参数的,可参考
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_ helloinit 6 __used
__attribute__((__section__(".initcall" 6 ".init"))) = helloinit
即:
1) 声明一个名称为 __initcall_ helloinit 6 的函数指针;
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_ helloinit 6指针定义在了.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的生成过程
待续
-------------------------------------------------------------------------------------------------------------------------------------------