在linux核心模組中,我們可以使用很多核心export出的函式,來實現我們的功能,作為核心附屬功能的擴充套件。但是這些export出來的函式在hook的時候是遠遠不夠用的,所以我們往往還要獲取一些沒有export出來的函式。使用這些沒有export出來的函式就要我們清楚的知道核心模組是如何link的,也就是如何尋找符號的。
程式碼段的符號(匯出和沒有匯出的都有),這樣會導致一個初始化的全域性變數的符號是透過kallsyms_lookup_name找不到的。
核心模組載入後,解析模組中符號的函式在
kernel/module.c::find_symbol->each_symbol。
-
第一步會從一個叫做ksymtab的段中找符號。
-
如果沒有找到就會去遍歷所有的核心模組,看有沒有匯出的符號。
如果都沒有找到,就會報找不到函式的錯誤。口說無憑,看程式碼:
1//尋找符號的入口函式
2const struct kernel_symbol *find_symbol(const char *name,
3 struct module **owner,
4 const unsigned long **crc,
5 bool gplok,
6 bool warn)
7{
8 struct find_symbol_arg fsa;
9
10 fsa.name = name;
11 fsa.gplok = gplok;
12 fsa.warn = warn;
13
14 if (each_symbol_section(find_symbol_in_section, &fsa;)) {
15 if (owner)
16 *owner = fsa.owner;
17 if (crc)
18 *crc = fsa.crc;
19 return fsa.sym;
20 }
21
22 pr_debug("Failed to find symbol %s\n", name);
23 return NULL;
24}
25EXPORT_SYMBOL_GPL(find_symbol);
在這個函式中首先會從ksymtab段中去尋找,這個段中是在編譯期間,就會把kernel export出來的函式填充在vmlinux的這個段中的。
1bool each_symbol_section(bool (*fn)(const struct symsearch *arr,
2 struct module *owner,
3 void *data),
4 void *data)
5{
6 struct module *mod;
7 static const struct symsearch arr[] = {
8 { __start___ksymtab, __stop___ksymtab, __start___kcrctab,
9 NOT_GPL_ONLY, false },
10 { __start___ksymtab_gpl, __stop___ksymtab_gpl,
11 __start___kcrctab_gpl,
12 GPL_ONLY, false },
13 { __start___ksymtab_gpl_future, __stop___ksymtab_gpl_future,
14 __start___kcrctab_gpl_future,
15 WILL_BE_GPL_ONLY, false },
16#ifdef CONFIG_UNUSED_SYMBOLS
17 { __start___ksymtab_unused, __stop___ksymtab_unused,
18 __start___kcrctab_unused,
19 NOT_GPL_ONLY, true },
20 { __start___ksymtab_unused_gpl, __stop___ksymtab_unused_gpl,
21 __start___kcrctab_unused_gpl,
22 GPL_ONLY, true },
23#endif
24 };
25
26//在上面的ksymtab段中找地址,是個陣列,找到了就傳回
27 if (each_symbol_in_section(arr, ARRAY_SIZE(arr), NULL, fn, data))
28 return true;
29//沒找到就會遍歷所有的核心模組,找export出來的符號
30 list_for_each_entry_rcu(mod, &modules;, list) {
31 struct symsearch arr[] = {
32 { mod->syms, mod->syms + mod->num_syms, mod->crcs,
33 NOT_GPL_ONLY, false },
34 { mod->gpl_syms, mod->gpl_syms + mod->num_gpl_syms,
35 mod->gpl_crcs,
36 GPL_ONLY, false },
37 { mod->gpl_future_syms,
38 mod->gpl_future_syms + mod->num_gpl_future_syms,
39 mod->gpl_future_crcs,
40 WILL_BE_GPL_ONLY, false },
41#ifdef CONFIG_UNUSED_SYMBOLS
42 { mod->unused_syms,
43 mod->unused_syms + mod->num_unused_syms,
44 mod->unused_crcs,
45 NOT_GPL_ONLY, true },
46 { mod->unused_gpl_syms,
47 mod->unused_gpl_syms + mod->num_unused_gpl_syms,
48 mod->unused_gpl_crcs,
49 GPL_ONLY, true },
50#endif
51 };
52 if (each_symbol_in_section(arr, ARRAY_SIZE(arr), mod, fn, data))
53 return true;
54 }
55 return false;
56}
57EXPORT_SYMBOL_GPL(each_symbol_section);
註意:不管在核心模組還是ksymtab段中,都必須是export的符號才能找到。對於找不到的符號要使用system.map或者vmlinux找到,然後將對應的符號替換為地址。