In his article about understanding the Linux Kernel Initcall Mechanism, Trevor created a userspace program that simulates the mechanism for calling the init_module() of linux drivers.
#include <stdio.h>
typedef int (*initcall_t)(void);
extern initcall_t __initcall_start, __initcall_end;
#define __initcall(fn) \
static initcall_t __initcall_##fn __init_call = fn
#define __init_call __attribute__ ((unused,__section__ ("function_ptrs")))
#define module_init(x) __initcall(x);
#define __init __attribute__ ((__section__ ("code_segment")))
static int __init
my_init1 (void)
{
printf ("my_init () #1\n");
return 0;
}
static int __init
my_init2 (void)
{
printf ("my_init () #2\n");
return 0;
}
module_init (my_init1);
module_init (my_init2);
void
do_initcalls (void)
{
initcall_t *call_p;
call_p = &__initcall_start;
do {
fprintf (stderr, "call_p: %p\n", call_p);
(*call_p)();
++call_p;
} while (call_p < &__initcall_end);
}
int
main (void)
{
fprintf (stderr, "in main()\n");
do_initcalls ();
return 0;
}
As you can see, the __initcall_start and __initcall_end are not defined so the linker will complain and will not produce an executable. The solution was to customize the default linker script(generated by ld --verbose) by adding the following lines before the text section:
__initcall_start = .;
function_ptrs : { *(function_ptrs) }
__initcall_end = .;
code_segment : { *(code_segment) }
Here is a snippet from the output of objdump -t :
0000000000000618 g function_ptrs 0000000000000000 __initcall_end<br>
0000000000000608 g .plt.got 0000000000000000 __initcall_start<br>
0000000000000608 l O function_ptrs 0000000000000008 __initcall_my_init1<br>
0000000000000610 O function_ptrs 0000000000000008 __initcall_my_init2<br>
0000000000000618 l F code_segment 0000000000000017 my_init1<br>
I understand the mechanism, I just don't see how the linker understood that __initcall_start should point to function_ptrs section or how the __initcall_end will point to the code_segment section either.
The way I see it, __initcall_start is assigned the value of the current output location, then a section function_ptrs is defined, which will point to the function_ptrs section from the input files, but I cannot see the link between the __initcall_start and the funtction_ptrs section.
My question is: How the linker is able to understand that __initcall_start should point to the funtion_ptrs ??