尝试回答两个问题:
1. Module如何解决kernel的函数调用问题
2. Module如何解决使用到其它module的函数调用问题
*.ko是个ELF文件,本质上与跑在user space的APP一样. 从Module编译结果看起:
执行指令: readelf -a usb_wwan.ko,摘录部分与本主题有关系:
00002104 0001801c R_ARM_CALL 00000000 printk
0000210c 00018b1c R_ARM_CALL 00001014 usb_serial_get_by_inde
与printk不一样, usb_serial_get_by_inde来自于usbserial.ko,但是二者在usb_wwan.ko并无值得关注的差别,也就是说此刻,usb_wwan.ko并没区分它所调用的函数来自于何方
那加载usb_wwan.ko的时候,kernel做了些什么事情呢?
load_module干的活太多,我们还是只关注relocation相关细节, simplify_symbols()完成了symbols的处理,看具体细节
load_module() --> simplify_symbols()
case SHN_UNDEF:
ksym = resolve_symbol_wait(mod, info, name);
SHN_UNDEF处理了kernel及其他模块的符号,go on...
simplify_symbols() -->resolve_symbol_wait()-->resolve_symbol() -->find_symbol()-->each_symbol_section()
bool each_symbol_section(bool (*fn)(const struct symsearch *arr,
struct module *owner,
void *data),
void *data)
{
struct module *mod;
static const struct symsearch arr[] = {
{ __start___ksymtab, __stop___ksymtab, __start___kcrctab,
NOT_GPL_ONLY, false },
{ __start___ksymtab_gpl, __stop___ksymtab_gpl,
__start___kcrctab_gpl,
GPL_ONLY, false },
{ __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future,
__start___kcrctab_gpl_future,
WILL_BE_GPL_ONLY, false },
#ifdef CONFIG_UNUSED_SYMBOLS
{ __start___ksymtab_unused, __stop___ksymtab_unused,
__start___kcrctab_unused,
NOT_GPL_ONLY, true },
{ __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl,
__start___kcrctab_unused_gpl,
GPL_ONLY, true },
#endif
};//Kernel EXPORT_SYMBOL
//所以,下面是在kernel的符号表里搜索,找到了就高高兴兴的返回
if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data))
return true;
//modules是所有已加载的module的表头,所以开始苦逼的一个模块一个模块的寻找了
list_for_each_entry_rcu(mod, &modules, list) {
struct symsearch arr[] = {
{ mod->syms, mod->syms + mod->num_syms, mod->crcs,
NOT_GPL_ONLY, false },
{ mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms,
mod->gpl_crcs,
GPL_ONLY, false },
{ mod->gpl_future_syms,
mod->gpl_future_syms + mod->num_gpl_future_syms,
mod->gpl_future_crcs,
WILL_BE_GPL_ONLY, false },
#ifdef CONFIG_UNUSED_SYMBOLS
{ mod->unused_syms,
mod->unused_syms + mod->num_unused_syms,
mod->unused_crcs,
NOT_GPL_ONLY, true },
{ mod->unused_gpl_syms,
mod->unused_gpl_syms + mod->num_unused_gpl_syms,
mod->unused_gpl_crcs,
GPL_ONLY, true },
#endif
};
//得之,我幸
if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data))
return true;
}
return false;//不得,我命,报错吧
}
符号处理完了,下部就是动真格的地址替换了
load_module() -->apply_relocations()-->apply_relocate()
apply_relocate()是个架构相关的函数.
至于post_relocation(), 就是宣告下,大概像什么事情都办完了,可以去民政局领证的意思吧,呵呵
结论:
module对于来自kernel或者来自其他module的函数处理方法一致. module.c帮忙处理了这部分差异. 而load_module真的是劳苦功高,脏活累活都干完了,只要这个函数执行完,新加载的module就跟built in的东西,一样一样的了