cipxe

How iPXE executes functions without calling its names


In main.c, it calls initialize() and startup(). Inside each of these functions in init.c, it loops through a table that contains the registered functions and calls them:

void startup ( void ) {
    struct startup_fn *startup_fn;

    if ( started )
        return;

    /* Call registered startup functions */
    for_each_table_entry ( startup_fn, STARTUP_FNS ) {
        if ( startup_fn->startup )
            startup_fn->startup();
    }

    started = 1;
}

I don't know where the registered functions are, according to the comment.

STARTUP_FNS:

#define STARTUP_FNS __table ( struct startup_fn, "startup_fns" )

__table:

#define __table( type, name ) ( type, name )

__table is the end of what I can look into. The comment says that it "Declare a linker table". But how can it get the functions?

There are more in table.h, such as __table_entry, table_start... where does this table come from? Where are its entries? what does it mean by:

#define table_start( table ) __table_entries ( table, 00 )

What does 00 mean here?

Please help. I really want to understand. Thanks.


Solution

  • (I'm the person who wrote the code in question.)

    The linker script instructs the linker to arrange sections ".tbl.*" in alphabetical order. The __table_entry etc macros are used to place structures into these sections. The easiest way to understand this is probably to look at the linker map, which you can produce using e.g. "make bin/rtl8139.rom.map":

    .tbl.init_fns.00
                0x000000000001784c        0x0 bin/blib.a(init.o)
    .tbl.init_fns.01
                0x000000000001784c        0x4 bin/blib.a(malloc.o)
                0x000000000001784c                heap_init_fn
    .tbl.init_fns.04
                0x0000000000017850        0x4 bin/blib.a(pxe_call.o)
                0x0000000000017850                pxe_init_fn
    .tbl.init_fns.04
                0x0000000000017854        0x4 bin/blib.a(settings.o)
                0x0000000000017854                builtin_init_fn
    .tbl.init_fns.04
                0x0000000000017858        0x4 bin/blib.a(smbios_settings.o)
                0x0000000000017858                smbios_init_fn
    .tbl.init_fns.04
                0x000000000001785c        0x4 bin/blib.a(process.o)
                0x000000000001785c                process_init_fn
    .tbl.init_fns.05
                0x0000000000017860        0x4 bin/blib.a(embedded.o)
                0x0000000000017860                embedded_init_fn
    .tbl.init_fns.99
                0x0000000000017864        0x0 bin/blib.a(init.o)
    

    Here you can see the various structures (heap_init_fn, pxe_init_fn, smbios_init_fn) etc. have been placed consecutively in the final image, sorted by initialisation order (01=INIT_EARLY, used for heap_init_fn in malloc.c; 04=INIT_NORMAL, used for smbios_init_fn in smbios_settings.c etc).

    The __table_start and __table_end macros in init.c produce zero-length arrays placed in .tbl.init_fns.00 and .tbl.init_fns.99; these can then be used by the code in init.c to identify the start and end of the table that has been constructed by the linker.

    Hope that helps!

    Michael