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++) {