What motivated me to explore this item? I was looking through the source code of the Linux kernel, trying to get my head around the exact steps and workings of how the kernel boots. Partway through the code I came across some code I just couldn't figure out, it just didn't make sense to me how the code was working.
In init/main.c:do_basic_setup() is a call to do_initcalls() which is defined as:
static void __init do_initcalls(void) { initcall_t *call; int count = preempt_count(); for (call = __initcall_start; call < __initcall_end; call++) { char *msg = NULL; char msgbuf[40]; int result; if (initcall_debug) { printk("Calling initcall 0x%p", *call); print_fn_descriptor_symbol(": %s()", (unsigned long) *call); printk("\n"); } result = (*call)(); if (result && result != -ENODEV && initcall_debug) { sprintf(msgbuf, "error code %d", result); msg = msgbuf; } if (preempt_count() != count) { msg = "preemption imbalance"; preempt_count() = count; } if (irqs_disabled()) { msg = "disabled interrupts"; local_irq_enable(); } if (msg) { printk(KERN_WARNING "initcall at 0x%p", *call); print_fn_descriptor_symbol(": %s()", (unsigned long) *call); printk(": returned with %s\n", msg); } } /* Make sure there is no pending stuff from the initcall sequence */ flush_scheduled_work(); }
Searching hi and low for __initcall_start reveals that it doesn't appear in any *.c source files anywhere, it only appears in the linker scripts (*.lds) for the various architectures.
[trevor@trevor linux-2.6.18]$ grep -r __initcall_start * arch/alpha/kernel/vmlinux.lds.S: __initcall_start = .; arch/arm/kernel/vmlinux.lds.S: __initcall_start = .; arch/arm26/kernel/vmlinux-arm26-xip.lds.in: __initcall_start = .; arch/arm26/kernel/vmlinux-arm26.lds.in: __initcall_start = .; arch/cris/arch-v10/vmlinux.lds.S: __initcall_start = .; arch/cris/arch-v32/vmlinux.lds.S: __initcall_start = .; arch/frv/kernel/vmlinux.lds.S: __initcall_start = .; arch/h8300/kernel/vmlinux.lds.S: ___initcall_start = .; arch/i386/kernel/vmlinux.lds.S: __initcall_start = .; arch/ia64/kernel/vmlinux.lds.S: __initcall_start = .; arch/m32r/kernel/vmlinux.lds.S: __initcall_start = .; arch/m68k/kernel/vmlinux-std.lds: __initcall_start = .; arch/m68k/kernel/vmlinux-sun3.lds: __initcall_start = .; arch/m68knommu/kernel/vmlinux.lds.S: __initcall_start = .; arch/mips/kernel/vmlinux.lds.S: __initcall_start = .; arch/parisc/kernel/vmlinux.lds.S: __initcall_start = .; arch/powerpc/kernel/vmlinux.lds.S: __initcall_start = .; arch/ppc/kernel/vmlinux.lds.S: __initcall_start = .; arch/s390/kernel/vmlinux.lds.S: __initcall_start = .; arch/sh/kernel/vmlinux.lds.S: __initcall_start = .; arch/sh64/kernel/vmlinux.lds.S: __initcall_start = .; arch/sparc/kernel/vmlinux.lds.S: __initcall_start = .; arch/sparc64/kernel/vmlinux.lds.S: __initcall_start = .; arch/v850/kernel/vmlinux.lds.S: ___initcall_start = . ; \ arch/x86_64/kernel/vmlinux.lds.S: __initcall_start = .; arch/xtensa/kernel/vmlinux.lds.S: __initcall_start = .; include/asm-um/common.lds.S: __initcall_start = .; init/main.c:extern initcall_t __initcall_start[], __initcall_end[]; init/main.c: for (call = __initcall_start; call < __initcall_end; call++) {