核心EXPORT_SYMBOL函式講解(二)
阿新 • • 發佈:2018-12-08
書接上文,在
https://blog.csdn.net/weixin_37867857/article/details/84526808
這個部落格裡面寫到EXPORT_SYMBOL函式使用就是把匯出的符號以符號值+符號的字串表示的形式表示的。只是講解了EXPORT_SYMBOL巨集的作用就是把匯出的符號以符號名稱+符號值的形式儲存在struct kernel_symbol為資料結構的檔案裡面,如果檔案載入過程中則是把資料載入在記憶體裡,具體的儲存地方則是ksymtab資料棧裡。如果模組編譯成核心,則是編譯進核心的ksymtab的資料棧裡。
但是還沒有講解模組載入過程中對於ksymtab資料棧的操作。本章就講解下該資料棧的作用以及使用。
所有的已經載入的模組在核心中都有一個struct module的表示,並且加入進去modules的全域性連結串列中。其中struct module關於匯出的符號的表示如下:
struct module{
....
/* Exported symbols */
const struct kernel_symbol *syms;
/***儲存著模組匯出的符號的陣列**/
const unsigned long *crcs;//校驗
unsigned int num_syms;
/***儲存著模組匯出的符號的數量****/
/* GPL-only exported symbols. */
unsigned int num_gpl_syms;
/***儲存模組匯出gpl符號的陣列長***/
const struct kernel_symbol *gpl_syms;
/***儲存著模組匯出GPL符號的陣列的個數***/
const unsigned long *gpl_crcs;//校驗
....
};
核心在模組載入過程中先獲取正在載入的模組的符號表
static int verify_export_symbols(struct module *mod)
{
unsigned int i;
struct module *owner;
const struct kernel_symbol * s;
/***先獲取正在載入模組的符號表並且存放在arr陣列中**/
struct {
const struct kernel_symbol *sym;
unsigned int num;
} arr[] = {
{ mod->syms, mod->num_syms },
{ mod->gpl_syms, mod->num_gpl_syms },
{ mod->gpl_future_syms, mod->num_gpl_future_syms },
#ifdef CONFIG_UNUSED_SYMBOLS
{ mod->unused_syms, mod->num_unused_syms },
{ mod->unused_gpl_syms, mod->num_unused_gpl_syms },
#endif
};
/***遍歷arr陣列中每一個struct kernel_symbol結構***/
for (i = 0; i < ARRAY_SIZE(arr); i++) {
for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) {
/***查詢符號名稱是否是已知模組使用,若果是已知模組使用則把模組內容加入到owner中**/
if (find_symbol(s->name, &owner, NULL, true, false)) {
printk(KERN_ERR
"%s: exports duplicate symbol %s"
" (owned by %s)\n",
mod->name, s->name, module_name(owner));
return -ENOEXEC;
}
}
}
return 0;
}
以下是根據匯出的符號名稱查詢的的過程:
1.根據核心映象載入過程中匯出的符號查詢,儲存在:
{ __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 },
全域性變數裡;
2.遍歷各個模組,查詢各個模組匯出的符號表。
以下程式碼是查詢符號表的過程:
const struct kernel_symbol *find_symbol(const char *name,
struct module **owner,
const unsigned long **crc,
bool gplok,
bool warn)
{
struct find_symbol_arg fsa;
fsa.name = name;
fsa.gplok = gplok;
fsa.warn = warn;
/**each_symbol是遍歷每一個由映象或者核心模組匯出的符號表;
find_symbol_in_section則是一個回撥函式,由each_symbol呼叫,執行真正
的查詢過程
查詢的順序如下:
1.查詢由於核心映象匯出的符號表;
2.查詢由於各個模組匯出的符號表;
**/
if (each_symbol(find_symbol_in_section, &fsa)) {
if (owner)
*owner = fsa.owner;
if (crc)
*crc = fsa.crc;
return fsa.sym;
}
DEBUGP("Failed to find symbol %s\n", name);
return NULL;
}
以下是find_symbol_in_section執行過程:
find_symbol_in_section則是一個回撥函式,由each_symbol呼叫,執行真正
的查詢過程:
static bool find_symbol_in_section(const struct symsearch *syms,
struct module *owner,
unsigned int symnum, void *data)
{
struct find_symbol_arg *fsa = data;
/**比較開始的佇列的符號名稱,如果比較失敗則返回false*/
if (strcmp(syms->start[symnum].name, fsa->name) != 0)
return false;
if (!fsa->gplok) {
if (syms->licence == GPL_ONLY)
return false;
if (syms->licence == WILL_BE_GPL_ONLY && fsa->warn) {
printk(KERN_WARNING "Symbol %s is being used "
"by a non-GPL module, which will not "
"be allowed in the future\n", fsa->name);
printk(KERN_WARNING "Please see the file "
"Documentation/feature-removal-schedule.txt "
"in the kernel source tree for more details.\n");
}
}
#ifdef CONFIG_UNUSED_SYMBOLS
if (syms->unused && fsa->warn) {
printk(KERN_WARNING "Symbol %s is marked as UNUSED, "
"however this module is using it.\n", fsa->name);
printk(KERN_WARNING
"This symbol will go away in the future.\n");
printk(KERN_WARNING
"Please evalute if this is the right api to use and if "
"it really is, submit a report the linux kernel "
"mailinglist together with submitting your code for "
"inclusion.\n");
}
#endif
/***如果比較成功則返回true,並且把模組的指標賦予fsa->owner**/
fsa->owner = owner;
fsa->crc = symversion(syms->crcs, symnum);
fsa->sym = &syms->start[symnum];
return true;
}
each_symbol執行則相對簡單不在贅述,大體上說一下執行過程:
struct symsearch {
const struct kernel_symbol *start, *stop;
const unsigned long *crcs;
enum {
NOT_GPL_ONLY,
GPL_ONLY,
WILL_BE_GPL_ONLY,
} licence;
bool unused;
};
這個結構體在each_symbol函式裡面經常用到,用於臨時儲存匯出符號的符號表;
1.先匯出核心每一個模組到struct symsearch型別的數組裡面。
2.遍歷儲存的symsearch陣列,從start->stop過程;
3.遍歷每一個模組,把每個模組匯出的符號表匯出到symsearch數組裡面;
4.執行過程2;