從Linux內核的2.6某個版本開始,內核引入了導出符號的機制。只有在內核中使用EXPORT_SYMBOL或EXPORT_SYMBOL_GPL導出的符號才能在內核模塊中直接使用。然而,內核並沒有導出所有的符號。例如,在3.8.0的內核中,do_page_fault就沒有被導出。
而我的內核模塊中需要使用do_page_fault,那么有那些方法呢?這些方法分別有什么優劣呢?
下面以do_page_fault為例,一一進行分析:
- 修改內核,添加EXPORT_SYMBOL(do_page_fault)或EXPORT_SYMBOL_GPL(do_page_fault)。
這種方法適用於可以修改內核的情形。在可以修改內核的情況下,這是最簡單的方式。 - 使用kallsyms_lookup_name讀取
kallsyms_lookup_name本身也是一個內核符號,如果這個符號被導出了,那么就可以在內核模塊中調用kallsyms_lookup_name(“do_page_fault”)來獲得do_page_fault的符號地址。
這種方法的局限性在於kallsyms_lookup_name本身不一定被導出。 - 讀取/boot/System.map-<kernel-version>,再使用內核模塊參數傳入內核模塊
System.map-<kernel-version>是編譯內核時產生的,它里面記錄了編譯時內核符號的地址。如果能夠保證當前使用的內核與System.map-<kernel-version>是一一對應的,那么從System.map-<kernel-version>中讀出的符號地址就是正確的。其中,kernel-version可以通過’uname -r’獲得。
但是這種方法也有局限性,在模塊運行的時候,System.map-<kernel-version>文件不一定存在,即使存在也不能保證與當前內核是正確對應的。 - 讀取/proc/kallsyms,再使用內核模塊參數傳入內核模塊
/proc/kallsyms是一個特殊的文件,它並不是存儲在磁盤上的文件。這個文件只有被讀取的時候,才會由內核產生內容。因為這些內容是內核動態生成的,所以可以保證其中讀到的地址是正確的,不會有System.map-<kernel-version>的問題。
需要注意的是,從內核2.6.37開始,普通用戶是沒有辦法從/proc/kallsyms中讀到正確的值。在某些版本中,該文件為空。在較新的版本中,該文件中所有符號的地址均為0(除非/porc/sys/kernel/kptr_restrict 的值被設為0)。但是root用戶是可以從/proc/kallsyms中讀到正確的值的。好在加載模塊也需要root權限,可以在加載模塊時用腳本獲取符號的地址。命令:
#cat /proc/kallsyms | grep "\<do_page_fault\>" | awk '{print $1}'不過,根據我的實際使用經驗,/proc/kallsyms中符號的數量比Systemp.map-<kernel-version>要少一些。
