MOD_INIT是定义在每个模块c语言源文件中的一个块,这个块需要由别的东西来解释才会最终生效。
下面是一段示例代码:
GRUB_MOD_INIT(acpi)
{
cmd = grub_register_extcmd_lockdown ("acpi", grub_cmd_acpi, 0,
N_("[-1|-2] [--exclude=TABLE1,TABLE2|"
"--load-only=TABLE1,TABLE2] FILE1"
" [FILE2] [...]"),
N_("Load host ACPI tables and tables "
"specified by arguments."),
options);
}可以看到,在这里以GRUB_MODE_INIT的这种仿函数代码的方式定义了一个acpi的命令,这个模块初始化的函数应该要以某种方式添加到grub的command中。
它对应的宏展开应该位:(dl.h文件中)
#define GRUB_MOD_INIT(name) \
static void grub_mod_init (grub_dl_t mod __attribute__ ((unused))) __attribute__ ((used)); \
static void \
grub_mod_init (grub_dl_t mod __attribute__ ((unused)))
这个含义就是说,这个东西被展开成一个grub_mod_init的函数。
当然,到此还远远谈不上完成,因为模块的动态加载才是这里init函数的主要目的,就是希望将整个的模块代码整合成一个带完整dl中描述module头的东西。
所以有关grub_mod_init这个函数,应该在/core/kern/dl.c文件的grub_dl_resolve_symbols函数中,通过对段的解析来完成真正的dl模块头。
grub_dl_resolve_symbols函数本身输入了一个当前要加载的mod,以及一个Elf_Ehdr的头指针。
// 32 位版本
typedef struct {
unsigned char e_ident[EI_NIDENT]; // 魔数和其他标识信息
Elf32_Half e_type; // 文件类型
Elf32_Half e_machine; // 目标架构
Elf32_Word e_version; // 文件版本
Elf32_Addr e_entry; // 程序入口虚拟地址
Elf32_Off e_phoff; // 程序头表在文件中的偏移
Elf32_Off e_shoff; // 节头表在文件中的偏移
Elf32_Word e_flags; // 处理器特定标志
Elf32_Half e_ehsize; // ELF 头本身的大小
Elf32_Half e_phentsize; // 程序头表中每个条目的大小
Elf32_Half e_phnum; // 程序头表中的条目数量
Elf32_Half e_shentsize; // 节头表中每个条目的大小
Elf32_Half e_shnum; // 节头表中的条目数量
Elf32_Half e_shstrndx; // 字符串表在节头表中的索引
} Elf32_Ehdr;这里还有一个是mod的结构
struct grub_dl
{
char *name;
grub_uint64_t ref_count;
int persistent;
grub_dl_dep_t dep;
grub_dl_segment_t segment;
Efl_Sym *symtab;
grub_size_t symsize;
void (*init) (struct grub_dl *mod);
void (*fini) (void);
void *base;
grub_size sz;
struct grub_dl *next;
}这将会在grub_dl_resolve_symbols函数的后半段体现:

可以看到这里,随着dl的加载器里,具体的针对symbol的检索的进行,会在找到地方进行mod头的赋值。
sym->st_value是该符号(“grub_mod_init”)在内存中的地址,即初始化函数的实际入口地址(void (*) (grub_dl_t))是一个强制类型转换,将地址转换为函数指针类型这个函数指针指向一个接受
grub_dl_t类型参数、无返回值的函数最终将转换后的函数指针赋值给
mod->init
这样就完成了原本C文件中的init函数向dl中头模块函数的具体的初始化函数的指针赋值了。
这里额外再补充一个具体的二进制文件展示。
代码是首先编译为kernel_exec-acpi.o,然后链接并生成为kernel.exec之后,最终转为efi文件的。这里的比如acpi的模块,将会被整合到kernel的内部。
在efi的源文件中,这里强烈建议通过阅读ELF头来做一个对应,下面是一个具体的对应关系展示:
1、首先,通过objdump命令可以确认最终编译出来的kaoqi.efi文件中有哪些段:

2、通过hexdump命令直接跳转到对应的mods段查看:

可以看到,这里0x20020的位置出现了一个明显的elf头的魔术字,这说明这里符合elf.h中定义的Elf64_Ehdr的格式(即,此处会将一系列的mods变成elf格式头的一个链表)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf64_Half e_type; /* Object file type */
Elf64_Half e_machine; /* Architecture */
Elf64_Word e_version; /* Object file version */
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags; /* Processor-specific flags */
Elf64_Half e_ehsize; /* ELF header size in bytes */
Elf64_Half e_phentsize; /* Program header table entry size */
Elf64_Half e_phnum; /* Program header table entry count */
Elf64_Half e_shentsize; /* Section header table entry size */
Elf64_Half e_shnum; /* Section header table entry count */
Elf64_Half e_shstrndx; /* Section header string table index */
} Elf64_Ehdr;这里的Elf64_Ehdr正是grub_dl_resolve_symbols中所需要的关键ELF链表头:

此时,mod模块名的查找正是通过这里的Ehdr中指向偏移所代表的含义所拼接成的:

3、一个直接的证据证明这里的mod段就是Ehdr的头格式:
在Ehdr中有一个描述架构的字段,这里的值为00B7
可以在elf的头文件中找到,0xB7对应的十进制的值为183,刚好就是AARCH64的枚举值:

相应的,这个mod的shoffset可以找到节偏移对应的str的名称(节偏移为0x26F0)
也即从基地址+0x26F0的地方,会有这个section的地址:

可以看到一系列的modname:

这里最终也没能完成完整的ELF格式的二进制代码解释,可惜。
但是明确INIT作为模块的初始化函数,没有问题。
进一步补充的说,模块名应该由下面这两个东西确定位置

已经阵亡了,看不懂。