2013年12月11日 星期三

How linux initramfs start and simulate

1. 
###after linux built
~/objs/linux-5.10.107/arch/arm64/kernel/vmlinux.lds
__initcall_start = 
.; 
KEEP(*(.initcallearly.init)) __initcall0_start = .;
KEEP(*(.initcall0.init)) KEEP(*(.initcall0s.init)) __initcall1_start = .;
KEEP(*(.initcall1.init)) KEEP(*(.initcall1s.init)) __initcall2_start = .;
KEEP(*(.initcall2.init)) KEEP(*(.initcall2s.init)) __initcall3_start = .;
KEEP(*(.initcall3.init)) KEEP(*(.initcall3s.init)) __initcall4_start = .;
KEEP(*(.initcall4.init)) KEEP(*(.initcall4s.init)) __initcall5_start = .;
KEEP(*(.initcall5.init)) KEEP(*(.initcall5s.init)) __initcallrootfs_start = .;
KEEP(*(.initcallrootfs.init)) KEEP(*(.initcallrootfss.init)) __initcall6_start = .;
KEEP(*(.initcall6.init)) KEEP(*(.initcall6s.init)) __initcall7_start = .;
KEEP(*(.initcall7.init)) KEEP(*(.initcall7s.init)) __initcall_end = .;

2.
~/linux-5.10.107/include/linux/compiler.h
#define __ADDRESSABLE(sym) \
    static void * __section(".discard.addressable") __used \
        __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)&sym;

~/linux-5.10.107/include/linux/init.h
#define ___define_initcall(fn, id, __sec)            \
    __ADDRESSABLE(fn)                    \
    asm(".section    \"" #__sec ".init\", \"a\"    \n"    \
    "__initcall_" #fn #id ":            \n"    \
        ".long    " #fn " - .            \n"    \
        ".previous                    \n");

3.
~/linux-5.10.107/init/initramfs.c
rootfs_initcall(populate_rootfs);

4.
~/linux-5.10.107/init/main.c
static initcall_entry_t *initcall_levels[] __initdata = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};

int __init_or_module do_one_initcall(initcall_t fn)
{
    int count = preempt_count();
    char msgbuf[64];
    int ret;

    if (initcall_blacklisted(fn))
        return -EPERM;

    do_trace_initcall_start(fn);
    ret = fn();
    do_trace_initcall_finish(fn, ret);

    msgbuf[0] = 0;

    if (preempt_count() != count) {
        sprintf(msgbuf, "preemption imbalance ");
        preempt_count_set(count);
    }
    if (irqs_disabled()) {
        strlcat(msgbuf, "disabled interrupts ", sizeof(msgbuf));
        local_irq_enable();
    }
    WARN(msgbuf[0], "initcall %pS returned with %s\n", fn, msgbuf);

    add_latent_entropy();
    return ret;
}

static void __init do_initcall_level(int level, char *command_line)
{
    initcall_entry_t *fn;

    parse_args(initcall_level_names[level],
           command_line, __start___param,
           __stop___param - __start___param,
           level, level,
           NULL, ignore_unknown_bootoption);

    trace_initcall_level(initcall_level_names[level]);
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++)
        do_one_initcall(initcall_from_entry(fn));
}

5. gedit ./vmlinux_sim.lds
SECTIONS
{
    /* 1. Start address after ELF headers */
    . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;

    /* System Metadata: Essential for the Dynamic Linker/Loader */
    .interp    : { *(.interp) }
    .hash      : { *(.hash) }
    .gnu.hash  : { *(.gnu.hash) }
    .dynsym    : { *(.dynsym) }
    .dynstr    : { *(.dynstr) }
    .rela.dyn  : { *(.rela.dyn) }
    .rela.plt  : { *(.rela.plt) }

    /* Core Code: Executable instructions */
    .text      : { *(.text .text.*) }
    .plt       : { *(.plt) }
    .plt.got   : { *(.plt.got) }

    /* Constant Data: Strings and read-only variables */
    .rodata    : { *(.rodata .rodata.*) }

    /* Initcall Table: Since these are "a" (read-only), they stay here */
    .initcalls : {
        __initcall0_start = .;
        KEEP(*(.initcall0.init))
        __initcall1_start = .;
        KEEP(*(.initcall1.init))
        __initcall2_start = .;
        KEEP(*(.initcall2.init))
        __initcall3_start = .;
        KEEP(*(.initcall3.init))
        __initcall4_start = .;
        KEEP(*(.initcall4.init))
        __initcall5_start = .;
        KEEP(*(.initcall5.init))
        __initcallrootfs_start = .;
        KEEP(*(.initcallrootfs.init))
        __initcall6_start = .;
        KEEP(*(.initcall6.init))
        __initcall7_start = .;
        KEEP(*(.initcall7.init))
        __initcall_end = .;
    }

    /* 
    SEGMENT SEPARATOR: Alignment to Page Boundary (4KB)
    This prevents the "RWX Permissions" warning by ensuring RX and RW. 
    */
    . = ALIGN(0x1000);

    .data    : { *(.data .data.*) }
    .dynamic : { *(.dynamic) }
    .got     : { *(.got .got.plt) }
    .bss     : { *(.bss .bss.* COMMON) }
}

6. gedit ./api_initramfs.cpp ###full source simulation
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>

typedef int (*initcall_t)(void); //refer to ~/linux-5.10.107/include/linux/init.h

/* keep symbol addressable (avoid GC) */
#define ADDRESSABLE(sym) \
    static void * __attribute__((section(".discard.addressable"), used)) __addr_##sym = (void *)&(sym)

/*
NOTE: .long in gas is 4 bytes (PREL32). This requires linker script to place
.initcall*.init sections safely.
.long fn - . 32bits linktime address simulate fail
.quad fn - . 64bits linktime address simulate fail
.quad fn     64bits runtime relocation address simulate success
*/
#define __DEFINE_INITCALL(fn, id) \
ADDRESSABLE(fn); \
asm( \
    ".section .initcall" #id ".init,\"a\"\n" \
    ".quad " #fn "\n" \
    ".previous\n" \
)

/* convenience macros like kernel */
#define pure_initcall(fn)      __DEFINE_INITCALL(fn, 0)
#define core_initcall(fn)      __DEFINE_INITCALL(fn, 1)
#define postcore_initcall(fn)  __DEFINE_INITCALL(fn, 2)
#define arch_initcall(fn)      __DEFINE_INITCALL(fn, 3)
#define subsys_initcall(fn)    __DEFINE_INITCALL(fn, 4)
#define fs_initcall(fn)        __DEFINE_INITCALL(fn, 5)
#define rootfs_initcall(fn)    __DEFINE_INITCALL(fn, rootfs)
#define device_initcall(fn)    __DEFINE_INITCALL(fn, 6)
#define late_initcall(fn)      __DEFINE_INITCALL(fn, 7)

//".quad " #fn need extern "C" for g++
#ifdef __cplusplus
extern "C" {
#endif
int l0_fn(void)    {
    printf("l0_fn\n");
    return 0;
}
int l1_fn(void)    {
    printf("l1_fn\n");
    return 0;
}
int l2_fn(void)    {
    printf("l2_fn\n");
    return 0;
}
int l3_fn(void)    {
    printf("l3_fn\n");
    return 0;
}
int l4_fn(void) {
    printf("l4_fn\n");
    return 0;
}
int l5_fn(void)    {
    printf("l5_fn\n");
    return 0;
}
int rootfs_fn(void){
    printf("rootfs_fn\n");
    return 0;
}
int l6_fn(void)    {
    printf("l6_fn\n");
    return 0;
}
int l7_fn(void)    {
    printf("l7_fn\n");
    return 0;
}
#ifdef __cplusplus
}
#endif

/* --- register initcalls into their sections (emit PREL entries) */
pure_initcall(l0_fn);
core_initcall(l1_fn);
postcore_initcall(l2_fn);
arch_initcall(l3_fn);
subsys_initcall(l4_fn);
fs_initcall(l5_fn);
rootfs_initcall(rootfs_fn);
device_initcall(l6_fn);
late_initcall(l7_fn);

/* ---------------------------
 * 3) extern labels (define in asm above) and levels array
 * --------------------------- */
extern initcall_t __initcall0_start[];
extern initcall_t __initcall1_start[];
extern initcall_t __initcall2_start[];
extern initcall_t __initcall3_start[];
extern initcall_t __initcall4_start[];
extern initcall_t __initcall5_start[];
extern initcall_t __initcall6_start[];
extern initcall_t __initcall7_start[];
extern initcall_t __initcall_end[];

static initcall_t *initcall_levels[] = {
    __initcall0_start,
    __initcall1_start,
    __initcall2_start,
    __initcall3_start,
    __initcall4_start,
    __initcall5_start,
    __initcall6_start,
    __initcall7_start,
    __initcall_end,
};

/* ---------------------------
 * 4) simulate do_initcalls loop
 * --------------------------- */
int do_one_initcall(initcall_t fn)
{
    int ret = 0;
    if (fn)
        ret = fn();
    return ret;
}

static void do_initcall_level(int level)
{
    initcall_t *fn;
    printf("do_initcall_level: %d\n", level);
    for (fn = initcall_levels[level]; fn < initcall_levels[level+1]; fn++) {
        do_one_initcall(*fn);
    }
}

int main(int argc, char** argv)
{
    for (int level = 0; level <= 7; level++)
        do_initcall_level(level);

    return 0;
}

7. gedit ./Makefile
TARGET = api_initramfs

SRCDIR    = ./source
OBJDIR    = ./obj
BINDIR    = ./bin

#CROSS_COMPILE =
CROSS_COMPILE = /opt/toolchain/x86_64-asustor_x64_g3_2024.02.26-linux-gnu/bin/x86_64-asustor_x64_g3_2024.02.26-linux-gnu-
CXX = $(CROSS_COMPILE)g++

CXXFLAGS    = -fmessage-length=0 -std=c++14 -pthread -fPIC -O0 -g
LDFLAGS        = -Wl,-T,./vmlinux_sim.lds -lpthread 

CXXFILES    = $(notdir $(wildcard $(SRCDIR)/*.cpp))
CXXOBJS        = $(patsubst %.cpp, $(OBJDIR)/%.o, $(CXXFILES))

$(shell mkdir -p $(OBJDIR))
$(shell mkdir -p $(BINDIR))

$(OBJDIR)/%.o : $(SRCDIR)/%.cpp
    $(CXX) -c $(CXXFLAGS) $< -o $@

$(TARGET): $(COBJS) $(CXXOBJS)
    $(CXX) $^ $(LDFLAGS) -o $(BINDIR)/$@

all: $(TARGET)

clean:
    rm -rf $(OBJDIR) $(BINDIR)

8.
nm -n ./api_initramfs
                 w __gmon_start__
                 U __libc_start_main@GLIBC_2.34
                 U printf@GLIBC_2.2.5
                 U puts@GLIBC_2.2.5
00000000004002d0 r __abi_tag
0000000000400490 T _start
00000000004004c0 T _dl_relocate_static_pie
00000000004004c1 t deregister_tm_clones
00000000004004e0 t register_tm_clones
0000000000400517 t __do_global_dtors_aux
0000000000400537 t frame_dummy
000000000040053d T l0_fn
0000000000400557 T l1_fn
0000000000400571 T l2_fn
000000000040058b T l3_fn
00000000004005a5 T l4_fn
00000000004005bf T l5_fn
00000000004005d9 T rootfs_fn
00000000004005f3 T l6_fn
000000000040060d T l7_fn
0000000000400627 T _Z15do_one_initcallPFivE
000000000040064f t _ZL17do_initcall_leveli
00000000004006ca T main
0000000000400700 T _init
0000000000400718 T _fini
0000000000400760 R _IO_stdin_used
0000000000400990 r __FRAME_END__
0000000000400a10 r __GNU_EH_FRAME_HDR
0000000000400a8c R __initcall0_start
0000000000400a94 R __initcall1_start
0000000000400a9c R __initcall2_start
0000000000400aa4 R __initcall3_start
0000000000400aac R __initcall4_start
0000000000400ab4 R __initcall5_start
0000000000400abc R __initcallrootfs_start
0000000000400ac4 R __initcall6_start
0000000000400acc R __initcall7_start
0000000000400ad4 R __initcall_end
0000000000401000 D __data_start
0000000000401000 W data_start
0000000000401008 D __dso_handle
0000000000401020 d _ZL15initcall_levels
0000000000401068 d __do_global_dtors_aux_fini_array_entry
0000000000401068 D __TMC_END__
0000000000401070 d __frame_dummy_init_array_entry
0000000000401078 d _ZL12__addr_l0_fn
0000000000401080 d _ZL12__addr_l1_fn
0000000000401088 d _ZL12__addr_l2_fn
0000000000401090 d _ZL12__addr_l3_fn
0000000000401098 d _ZL12__addr_l4_fn
00000000004010a0 d _ZL12__addr_l5_fn
00000000004010a8 d _ZL16__addr_rootfs_fn
00000000004010b0 d _ZL12__addr_l6_fn
00000000004010b8 d _ZL12__addr_l7_fn
00000000004010c0 d _DYNAMIC
00000000004012e0 d _GLOBAL_OFFSET_TABLE_
0000000000401308 b completed.0

ps:
How is rootfs_fn added to the end of level 5? That's not the right question.
The execution of rootfs_fn is not because it is "deliberately placed at the end of level 5".
Is it because rootfs_fn is between initcall_levels[5] and initcall_levels[6], and therefore executed.

refer to:
https://static.sched.com/hosted_files/osseu2020/f5/2020_ELCE_initcalls_myjosserand.pdf
download:
https://www.mediafire.com/file/vb919erkp3s95m1/api_initramfs.tar.xz/file

沒有留言:

張貼留言