內核模塊加載時的版本檢查(轉載)

 

2.4內核下,執行“cat /proc/ksyms”,將會看到內核符號,而且在名字後還會跟隨着一串校驗字符串,此校驗字符串與內核版本有關。在內核源碼頭文件linux/modules目錄下存在許多*.ver文件,這些文件起着爲內核符號添加校驗後綴的作用,如ksyms.ver文件裏有一行#define printk _set_ver(printk),linux/modversions.h 文件會包含所有的.ver文件。所以當模塊包含linux/modversions.h文件後,編譯時,模塊裏使用的內核符號實質上成爲帶有校驗後綴的內核符號。在加載模塊時,如果模塊使用的內核符號的校驗字符串與當前運行內核所導出的相應的內核符號的校驗字符串不一致,即當前內核空間並不存在模塊所使用的內核符號,就會出現“Invalid module format ”的錯誤。

Linux內核所採用的在內核符號添加校驗字符串來驗證模塊的版本與內核的版本是否匹配的方法繁且會浪費內核空間,而且隨着SMP、PREEMPT等機制在2.6內核的引入和完善,模塊運行時對內核的依賴不再僅僅取決於內核版本,還取決於內核的配置,此時內核符號的校驗碼是否一致不能成爲判斷模塊可否被加載的充分條件。

在Linux 2.6內核的linux/vermagic.h頭文件中定義了“版本魔術字符串”――VERMAGIC_STRING(如代碼清單23.7),VERMAGIC_STRING不僅包含內核版本號,還包含內核編譯所使用的gcc版本、SMP與PREEMPT等配置信息。在編譯模塊時,我們可以看到屏幕上會顯示“MODPOST”(模塊後續處理),在內核源碼目錄下scripts/mod/modpost.c文件中可以看到模塊後續處理部分的代碼。就是在這個階段,VERMAGIC_STRING會被添加到模塊的modinfo段中,模塊編譯生成後,通過“modinfo mymodule.ko”命令可以查看此模塊的vermagic等信息。2.6 內核下的模塊裝載器裏保存有內核的版本信息,在裝載模塊時,裝載器會比較所保存的內核vermagic與此模塊的modinfo段裏保存的vermagic信息是否一致,兩者一致時,模塊才能被裝載。

代碼清單23.7 VERMAGIC_STRING的定義

1  #ifdef CONFIG_SMP   //配置了SMP

2  #define MODULE_VERMAGIC_SMP "SMP "

3  #else

4  #define MODULE_VERMAGIC_SMP ""

5  #endif

7  #ifdef CONFIG_PREEMPT     //配置了PREEMPT     

8  #define MODULE_VERMAGIC_PREEMPT "preempt "

9  #else

10 #define MODULE_VERMAGIC_PREEMPT ""

11 #endif 

12

13 #ifdef CONFIG_MODULE_UNLOAD  //支持module卸載

14 #define MODULE_VERMAGIC_MODULE_UNLOAD "mod_unload "

15 #else

16 #define MODULE_VERMAGIC_MODULE_UNLOAD ""

17 #endif

18

19 #ifndef MODULE_ARCH_VERMAGIC  //體系結構VERMAGIC

20 #define MODULE_ARCH_VERMAGIC ""

21 #endif

22

23 /* 拼接內核版本、上述VERMAGIC以及gcc版本 */       

24 #define VERMAGIC_STRING                /          

25  UTS_RELEASE " "                       /               

26  MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT  /      

27  MODULE_VERMAGIC_MODULE_UNLOAD MODULE_ARCH_VERMAGIC /                       

28  "gcc-" __stringify(__GNUC__) "." __stringify(__GNUC_MINOR__)     

在通過make menuconfig對內核進行新的配置後,再基於2.6.11.5內核編譯生成的新第4章中的hello.ko模塊,且這個模塊的modinfo結果如下:

[root@localhost driver_study]# modinfo hello.ko

filename:       hello.ko

license:        Dual BSD/GPL

author:         Song Baohua

description:    A simple Hello World Module

alias:          a simplest module

vermagic:       2.6.15.5 SMP preempt PENTIUM4 gcc-3.2

depends:       

從中可以看出,其vermagic爲“2.6.15.5 SMP preempt PENTIUM4 gcc-3.2”,運行“insmod hello.ko”命令,得到如下錯誤:

insmod: error inserting 'hello.ko': -1 Invalid module format

hello: version magic '2.6.15.5 SMP preempt PENTIUM4 gcc-3.2' should be '2.6.15.5 686 gcc-3.2'

原因在於加載該hello.ko時候所使用的內核雖然還是2.6.15.5,但是和編譯hello.ko時的內核的關鍵部分配置不一樣,導致vermagic不一致發生衝突,從而加載失敗。

1、我的系統是 2.4 升級到 2.6 的,編譯後加載出現insmod:error inserting hello.o:-1 invalid module format...

原因參考下文:

你的模塊代碼一定要爲每個它要連接的內核版本重新編譯 -- 至少, 在缺乏 modversions 時, 這裏不涉及因爲它們更多的是給內核發佈製作者, 而不是開發者. 模塊是緊密結合到一個特殊內核版本的數據結構和函數原型上的; 模塊見到的接口可能一個內核版本與另一個有很大差別. 當然, 在開發中的內核更加是這樣.

內核不只是認爲一個給定模塊是針對一個正確的內核版本建立的. 建立過程的其中一步是對一個當前內核樹中的文件(稱爲 vermagic.o)連接你的模塊; 這個東東含有相當多的有關要爲其建立模塊的內核的信息, 包括目標內核版本, 編譯器版本, 以及許多重要配置變量的設置. 當嘗試加載一個模塊, 這些信息被檢查與運行內核的兼容性. 如果不匹配, 模塊不會加載; 代之的是你見到如下內容:

# insmod hello.ko Error inserting './hello.ko': -1 Invalid module format
看一下系統日誌文件(/var/log/message 或者任何你的系統被配置來用的)將發現導致模塊無法加載特定的問題.

如果你需要編譯一個模塊給一個特定的內核版本, 你將需要使用這個特定版本的建立系統和源碼樹.

內核接口在各個發行之間常常變化. 如果你編寫一個模塊想用來在多個內核版本上工作(特別地是如果它必須跨大的發行版本), 你可能只能使用宏定義和 #ifdef 來使你的代碼正確建立. 本書的這個版本只關心內核的一個主要版本, 因此不會在我們的例子代碼中經常見到版本檢查. 但是這種需要確實有時會有. 在這樣情況下, 你要利用在 linux/version.h 中發現的定義. 這個頭文件, 自動包含在 linux/module.h, 定義了下面的宏定義:

 

UTS_RELEASE
這個宏定義擴展成字符串, 描述了這個內核樹的版本. 例如, "2.6.10".

LINUX_VERSION_CODE
這個宏定義擴展成內核版本的二進制形式, 版本號發行號的每個部分用一個字節表示. 例如, 2.6.10 的編碼是 132618 ( 就是, 0x02060a ). [4]有了這個信息, 你可以(幾乎是)容易地決定你在處理的內核版本.

KERNEL_VERSION(major,minor,release)
這個宏定義用來建立一個整型版本編碼, 從組成一個版本號的單個數字. 例如, KERNEL_VERSION(2.6.10) 擴展成 132618. 這個宏定義非常有用, 當你需要比較當前版本和一個已知的檢查點.

大部分的基於內核版本的依賴性可以使用預處理器條件解決, 通過利用 KERNEL_VERSION 和 LINUX_VERSION_VODE. 版本依賴不應當, 但是, 用繁多的 #ifdef 條件來搞亂驅動的代碼; 處理不兼容的最好的方式是把它們限制到特定的頭文件. 作爲一個通用的原則, 明顯版本(或者平臺)依賴的代碼應當隱藏在一個低級的宏定義或者函數後面. 高層的代碼就可以只調用這些函數, 而不必關心低層的細節. 這樣書寫的代碼易讀並且更健壯.

2、./hello:kernel –module version mismatch
hello.o was compiled for kernel version 2.4.20
while this kernel is version 2.4.20-8

原因:模塊和內核版本不匹配,即編譯內核的編譯器與現在編譯模塊的編譯器版本不一致。
解決方法:1> 將/usr/include/linux/version.h 文件中的#define… “2.4.20”修改成#define…    “2.4.20-8”(),再重新編譯!

原來主要原因是隻有你真正編譯過 /usr/src/linux/目錄下的內核一次後,纔會自動生成對應的version.h文件。以前在RedHat中拿到的Kernel-Devel包都是編譯過的,所以沒有這樣的問題。

馬上進入/usr/src/linux目錄:
make menuconfig
make

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章