Linux內核模塊間通訊方法非常的多,最便捷的方法莫過于函數或變量符號導出,然后直接調用。默認情況下,模塊與模塊之間、模塊與內核之間的全局變量是相互獨立的,只有通過EXPORT_SYMBOL
將模塊導出才能對其他模塊或內核可見。
符號導出函數
EXPORT_SYMBOL()
:括號中定義的函數或變量對全部內核代碼公開EXPORT_SYMBOL_GPL()
和EXPORT_SYMBOL
類似,但范圍只適合GPL許可的模塊進行調用
一、內核符號表
Linux kallsyms,即內核符號表,其中會列出所有的Linux內核中的導出符號,在用戶態下可以通過/proc/kallsyms
訪問,此時由于內核保護,看到的地址為0x0000000000000000
,在root模式下可以看到真實地址。啟用kallsyms需要編譯內核時設置CONFIG_KALLSYMS
為y。
/proc/kallsyms
會顯示內核中所有的符號,但是這些符號不是都能被其他模塊引用的(絕大多數都不能),能被引用的符號是被EXPORT_SYMBOL
或EXPORT_SYMBOL_GPL
導出的
內核模塊在編譯時符號的查找順序:
- 在本模塊中符號表中,尋找符號(函數或變量實現)
- 在內核全局符號表中尋找
- 在模塊目錄下的Module.symvers文件中尋找
內核符號表類型
內核符號表就是在內核內部函數或變量中可供外部引用的函數和變量的符號表(/proc/kallsyms),表格如下:
符號類型 | 名稱 | 說明 |
---|---|---|
A | Absolute | 符號的值是絕對值,并且在進一步鏈接過程中不會被改變 |
B | BSS | 符號在未初始化數據區或區(section)中,即在BSS段中 |
C | Common | 符號是公共的。公共符號是未初始化的數據。在鏈接時,多個公共符號可能具有同一名稱。如果該符號定義在其他地方,則公共符號被看作是未定義的引用 |
D | Data | 符號在已初始化數據區中 |
G | Global | 符號是在小對象已初始化數據區中的符號。某些目標文件的格式允許對小數據對象(例如一個全局整型變量)可進行更有效的訪問 |
I | Inderect | 符號是對另一個符號的間接引用 |
N | Debugging | 符號是一個調試符號 |
R | Read only | 符號在一個只讀數據區中 |
S | Small | 符號是小對象未初始化數據區中的符號 |
T | Text | 符號是代碼區中的符號 |
U | Undefined | 符號是外部的,并且其值為0(未定義) |
V | Weaksymbol | 弱符號 |
W | Weaksymbol | 弱符號 |
- | Stabs | 符號是a.out目標文件中的一個stab符號,用于保存調試信息 |
? | Unknown | 符號的類型未知,或者與具體文件格式有關 |
注 :符號屬性,小寫表示局部符號,大寫表示全局符號
二、Linux內核符號導出
1、EXPORT_SYMBOL導出符號
這里我們定義兩個源文件myexportfunc.c
和myusefunc.c
,分別放置在不同目錄;在myexportfunc.c
文件中導出publicFunc
函數和變量myOwnVar
以供myusefunc.c
文件中的函數調用。myusefunc.c
文件中要想成功調用publicFunc
函數和myOwnVar
變量,必須進行extern
聲明,否則編譯時會報錯。源碼如下:
myexportfunc.c
文件:
/* myexportfunc.c */
#include < linux/module.h >
#include < linux/kernel.h >
#include < linux/init.h >
char myOwnVar[30]="Linux kernel communication.";
static int __init myfunc_init(void)
{
printk("Hello,this is my own module!
");
return 0;
}
static void __exit myfunc_exit(void)
{
printk("Goodbye,this is my own clean module!
");
}
void publicFunc(void)
{
printk(KERN_INFO "This is public module and used for another modules.
");
}
module_init(myfunc_init);
module_exit(myfunc_exit);
EXPORT_SYMBOL(publicFunc);
EXPORT_SYMBOL(myOwnVar);
MODULE_DESCRIPTION("First Personel Module");
MODULE_AUTHOR("Lebron James");
MODULE_LICENSE("GPL");
myexportfunc.c
文件的Makefile
文件:
ifneq ($(KERNELRELEASE),)
$(info "2nd")
obj-m:=myexportfunc.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
all:
$(info "1st")
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order *.mod
endif
myusefunc.c
文件:
/* myusefunc.c */
#include < linux/init.h >
#include < linux/module.h >
MODULE_LICENSE("GPL");
extern void publicFunc(void);
extern char myOwnVar[30];
void showVar(void);
static int __init hello_init(void)
{
printk(KERN_INFO "Hello,this is myusefunc module.
");
publicFunc();
showVar();
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye this is myusefunc module.
");
}
void showVar(void)
{
printk(KERN_INFO "%s
", myOwnVar);
}
module_init(hello_init);
module_exit(hello_exit);
myusefunc.c
文件的Makefile
文件:
KBUILD_EXTRA_SYMBOLS += /tmp/tmp/Module.symvers
ifneq ($(KERNELRELEASE),)
$(info "2nd")
obj-m:=myusefunc.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
PWD :=$(shell pwd)
all:
$(info "1st")
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.symvers *.cmd *.mod.c *.order *.mod
endif
注:
KBUILD_EXTRA_SYMBOLS
用來告訴內核當前module
需要引用另外一個module
導出的符號。KBUILD_EXTRA_SYMBOLS
后需要寫絕對路徑,相對路徑會出錯,因為scripts/mod/modpost
執行時, 以其在內核目錄的路徑為起始點進行解析。
分別通過make
命令編譯myexportfunc.c
和myusefunc.c
文件:
[root@localhost tmp]# make
"1st"
make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp modules
make[1]: Entering directory '/usr/src/kernels/4.18.0-394.el8.x86_64'
"2nd"
CC [M] /tmp/tmp/myexportfunc.o
Building modules, stage 2.
"2nd"
MODPOST 1 modules
CC /tmp/tmp/myexportfunc.mod.o
LD [M] /tmp/tmp/myexportfunc.ko
make[1]: Leaving directory '/usr/src/kernels/4.18.0-394.el8.x86_64'
[root@localhost tmp]# ls -l
total 488
drwxr-xr-x 3 root root 139 May 25 04:40 24
-rw-r--r-- 1 root root 260 May 24 13:34 Makefile
-rw-r--r-- 1 root root 32 May 25 04:40 modules.order
-rw-r--r-- 1 root root 114 May 25 04:40 Module.symvers
-rw-r--r-- 1 root root 655 May 25 04:40 myexportfunc.c
-rw-r--r-- 1 root root 237256 May 25 04:40 myexportfunc.ko
-rw-r--r-- 1 root root 826 May 25 04:40 myexportfunc.mod.c
-rw-r--r-- 1 root root 117856 May 25 04:40 myexportfunc.mod.o
-rw-r--r-- 1 root root 121336 May 25 04:40 myexportfunc.o
[root@localhost tmp]# cd 24/
[root@localhost 24]# ls
Makefile myusefunc.c
[root@localhost 24]# make
"1st"
make -C /lib/modules/4.18.0-394.el8.x86_64/build M=/tmp/tmp/24 modules
make[1]: Entering directory '/usr/src/kernels/4.18.0-394.el8.x86_64'
"2nd"
CC [M] /tmp/tmp/24/myusefunc.o
Building modules, stage 2.
"2nd"
MODPOST 1 modules
CC /tmp/tmp/24/myusefunc.mod.o
LD [M] /tmp/tmp/24/myusefunc.ko
make[1]: Leaving directory '/usr/src/kernels/4.18.0-394.el8.x86_64'
[root@localhost tmp]# cd ..
[root@localhost tmp]# insmod ./myexportfunc.ko
[root@localhost tmp]# cd 24/
[root@localhost 24]# ls -l
total 488
-rw-r--r-- 1 root root 305 May 24 13:34 Makefile
-rw-r--r-- 1 root root 32 May 25 04:42 modules.order
-rw-r--r-- 1 root root 114 May 25 04:42 Module.symvers
-rw-r--r-- 1 root root 557 May 24 13:33 myusefunc.c
-rw-r--r-- 1 root root 235832 May 25 04:42 myusefunc.ko
-rw-r--r-- 1 root root 898 May 25 04:42 myusefunc.mod.c
-rw-r--r-- 1 root root 117984 May 25 04:42 myusefunc.mod.o
-rw-r--r-- 1 root root