关于EXPORT_SYMBOL_GPL()问题【转】



关于EXPORT_SYMBOL_GPL()问题,起源于我的毕业设计中要在linux下写一个模块,用于监视目标进程的系统调用,对其中的写操作进行重定向.这其中就涉及到在内核态下截获了write-syscall以后将他需要写的buf写到我事先创建的文件中.
    因此我需要在内核态下的写文件的函数,查看了内核文件/fs/read_write.c,里面是read,write类底层对sys-call的上层封装.我原先希望可以直接使用sys_write,但是由于linux出于安全因素,拒绝将sys-call导出到全局符号表,因此编译的内核会有 sys_write undefined 的警告 ,当 insmod 模块的时候会产生未知symbol的错误.
    我顺着这条道走下去,考虑到sys-call是面向user用户空间,让application使用的,我在网上搜索到改变内存空间的方法,主要就是set_fs(),get_ds()这两个函数来完成内存空间切换.一般定义的宏如下:
    #define BEGIN_KMEM {mm_segment_t old = get_fs();set_fs(get_ds())}
    #define END_KMEM   {set_fs(old);}
前面一个宏主要是先记录下内存空间的首地址,再设置内存空间的首地址为用户空间首地址,进行内存空间切换
后一个宏主要是将内存空间切换回去.
    这两个宏的使用主要是因为sys-call在调用前会进行各个参数的内存空间检查,如果发现内存空间不正确,将会产生段错误.
   然而网上的方法主要是针对2.4内核下的,在2.6.30下无法成功.在百般无奈下我选择了重新编译内核,使用EXPORT_SYMBOL_GPL()这个宏将使用到的sys-calls导出.
导出方法就是到函数定义的文件中: 如需要将sys_write导出,首先进入/fs/read_write.c中,在系统调用定义后面使用EXPORT_SYMBOL_GPL(sys_write)即可.
如果注意一点就会发现,每个文件都有许多的EXPORT_SYMBOL(),这个宏也是将函数导出让所有模块都可以使用.而EXPORT_SYMBOL_GPL()这个宏主要是给有GPL认证的模块使用.
   重新编译内核,在内核中确实可以使用sys_write 等函数进行操作,不过后来想了一下,毕竟如果要有比较好的移植性,修改内核这种方法还是不太好.于是咨询了一下同学,他给出一个函数filp_open(),返回值是一个struct file,在file里面可以有files_operation结构指针,在此结构中可以使用write()函数进行文件写操作.我使用这个宏进行文件的读写还比较顺利.
   今天就写到这吧.
   由于需要使用到mkdir()进行目录的创建.下一次会将使用mkdir()遇到的相关的问题进行解析.








转L:
1.EXPORT_SYMBOL
EXPORT_SYMBOL( my_pub_func);
在预编译阶段会解析为:
extern void *__crc_my_pub_func __attribute__((weak)); 
static const unsigned long __kcrctab_my_pub_func __attribute__((__used__)) __attribute__((section("__kcrctab" ""), unused)) = (unsigned long) &__crc_my_pub_func; 
static const char __kstrtab_my_pub_func[] __attribute__((section("__ksymtab_strings"))) = "" "my_pub_func"; 
static const struct kernel_symbol __ksymtab_my_pub_func __attribute__((__used__)) __attribute__((section("__ksymtab" ""), unused)) = { (unsigned long)&my_pub_func, __kstrtab_my_pub_func };
很显然__ksymtab_my_pub_func存储了my_pub_func的地址和符号信息,该符号对应的地址
只有insmod后才会确定;
__ksymtab_my_pub_func会链接到__ksymtab section,__ksymtab section中的所有内容就构成了
内核"导出"的符号表,这个表在insmod 时候会用到.

2./proc/kallsyms
cat /proc/kallsyms会打印出内核当前的符号表,例如:
...
d8834a24 t snd_free_sgbuf_pages [snd_page_alloc]
c0180d7a U create_proc_entry [snd_page_alloc]
d88341d8 T snd_dma_free_pages [snd_page_alloc]
c013d858 U __get_free_pages [snd_page_alloc]
d8834ab5 t snd_malloc_sgbuf_pages [snd_page_alloc]
c014f906 U kmem_cache_alloc [snd_page_alloc]
c0106dcd U dma_alloc_coherent [snd_page_alloc]
...
其中第一列是该符号在内核地址空间中的地址;第二列是符号属性,小写表示局部符号,大写表示全局符号,具体含义参考man nm; 第三列表示符号字符串. 这里只显示EXPORT_SYMBOL,EXPROT_SYMBOL_GPL处理过的符号。

3.System.map内核符号文件
通过more /boot/System.map 可以查看内核符号列表。可以显示编译好内核后所有在内核中的符号,模块中的要另行查看。

4.通过nm vmlinux也可以查看内核符号列表
可以显示编译好内核后所有在内核中的符号,模块中的要另行查看。

5.通过nm module_name可以查看模块的符号列表
但是得到是相对地址,只有加载后才会分配绝对地址。比如:e1000模块,如果e1000中的符号经过EXPORT_SYMBOL处理,等加载后,我们可以通过more /boot/System.map和nm vmlinux命令查看到,但是没有EXPORT_SYMBOL的,我目前不知道怎么查看。


另个试验:
1.验证EXPORT_SYMBOL
模块hello.c代码如下:
      1 #include <linux/module.h>
      2 #include <linux/kernel.h>
      3
      4 static int __init a_init_module(void)
      5 {
      6     unsigned long *__kcrctab_per_cpu__hrtimer_bases = 0xc02678b4;
      7     unsigned char *__kstrtab_per_cpu__hrtimer_bases = 0xc026926b;
      8     struct kernel_symbol *__ksymtab_per_cpu__hrtimer_bases = 0xc0265018;
      9
     10     printk("__kcrctab_per_cpu__hrtimer_bases = %08x\n", *__kcrctab_per_cpu__hrtimer_bases);
     11     printk("__kstrtab_per_cpu__hrtimer_bases = %s\n", __kstrtab_per_cpu__hrtimer_bases);
     12     printk("__ksymtab_per_cpu__hrtimer_bases value = %08x, name = %s\n", __ksymtab_per_cpu__hrtimer_bases->value,\
     13     __ksymtab_per_cpu__hrtimer_bases->name);
     14
     15     return 0;
     16 }
     17
     18 static void __exit a_cleanup_module(void)
     19 {
     20     printk("Bye, Bye\n");
     21 }
      22 module_init(a_init_module);
     23 module_exit(a_cleanup_module);
     
     Makefile配置文件如下:
      1 #
      2 # Makefile for hello.c file
      3 #
      4 KDIR:=/lib/modules/$(shell uname -r)/build
      5
      6 obj-m:=hello.o
      7
      8 default:
      9     $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
     10 clean:
     11     $(RM) .*.cmd *.mod.c *.o *.ko -r .tmp*
make; insmod hello.ko之后通过dmesg的运行结果:
__kcrctab_per_cpu__hrtimer_bases = 1ac19564
__kstrtab_per_cpu__hrtimer_bases = per_cpu__hrtimer_bases
__ksymtab_per_cpu__hrtimer_bases value = c0279ea0, name = per_cpu__hrtimer_bases
通过nm vmlinux | grep per_cpu__hrtimer_bases我们可以看到如下的对应关系:
1ac19564 A __crc_per_cpu__hrtimer_bases
c02678b4 r __kcrctab_per_cpu__hrtimer_bases
c026926b r __kstrtab_per_cpu__hrtimer_bases
c0265018 r __ksymtab_per_cpu__hrtimer_bases
c0279ea0 d per_cpu__hrtimer_bases
对比如上两列数据。
本实验只是为了验证一下EXPROT_SYMBOL.

2.1.EXPORT_SYMBOL和EXPORT_SYMBOL_GPL的区别
模块1:hello.c
      1 #include <linux/module.h>
      2 #include <linux/kernel.h>
      3
      4 void function1(void)
      5 {
      6     printk("hello wold\n");
      7 }
      8 EXPORT_SYMBOL(function1);
      9
     10 void function2(void)
     11 {
     12     printk("hello wold again\n");
     13 }
     14 EXPORT_SYMBOL_GPL(function2);
     15
     16
     17 static int __init a_init_module(void)
     18 {
     19     return 0;
     20 }
     21
     22 static void __exit a_cleanup_module(void)
     23 {
     24     printk("<1>Bye, Bye\n");
     25
     26 }
     27
     28 module_init(a_init_module);
     29 module_exit(a_cleanup_module);

模块2:hello2.c
    1 #include <linux/module.h>
      2 #include <linux/kernel.h>
      3
      4 //MODULE_LICENSE("GPL");
      5
      6 static int __init a_init_module(void)
      7 {
      8     function1();
      9 //    function2();
     10     return 0;
     11 }
     12
     13 static void __exit a_cleanup_module(void)
     14 {
     15     printk("<1>Bye, Bye\n");
     16
     17 }
     18
     19 module_init(a_init_module);
     20 module_exit(a_cleanup_module);

    
    首先编译后两个模块:hello.ko, hello2.ko
    A.在hello2.c中注释掉MODULE_LICENSE("GPL")和function2()两行,先insmod hello.ko然后insmod hello2.ko,一切正常。
    B.先insmod hello2.ko然后insmod hello.ko,会有出错信息,通过dmesg查看显示如下:hello2: Unknown symbol function1
    C.在hello2.c中打开function2(),先insmod hello.ko然后insmod hello2.ko,模块hello2.ko无法加载,显示如下信息:insmod: error inserting 'hello2.ko': -1 File exists,通过dmesg查看显示如下:hello2: Unknown symbol function2。在hello2.c中打开MODULE_LICENSE("GPL")或者用MODULE_LICENSE("Dual BSD/GPL"),先insmod hello.ko然后insmod hello2.ko,一切正常。

所以说EXPORT_SYMBOL_GPL的符号必须要用MODULE_LICENSE("GPL")或者用MODULE_LICENSE("Dual BSD/GPL")之后才能在模块中引用。而且MODULE_LICENSE("char")中的char不可以是任意字符,否则错误和没有MODULE_LICENSE效果一样。
    D.没有看MODULE_LICENSE内核代码,下一步去研读代码。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值