驱动学习(三)符号导出

  • Post author:
  • Post category:其他




驱动学习(三)符号导出



1.什么是符号?

主要是指全局变量和函数



2.为什么要导出符号?

​ linux内核采用的是模块化的形式管理内核代码。内核中每个模块之间是相互独立的,也就是说A模块的全局变量和函数,B模块是无法访问的。若B模块想要使用A模块中的已有符号,那么必须将A模块中的符号做符号导出,导出到模块符号表中,然后B模块可以使用A模块导出的符号。



3.如何导出符号?

​ linux内核给我们提供了两个函数宏,用他们做符号导出。



3.1 EXPORT_SYMBOL(符号)


3.2 EXPORT_SYMBOL_GPL(符号)


4.符号表的类型


4.1 本地符号表

用户自定义模块导出的符号表,这些符号表保存在本模块目录下Module.symvers



4.2 全局符号表

内核已经导出的符号表,在 /proc/kallsyms

​ sudo cat /proc/kallsyms |grep printk 0xc15cd833



5.注意

安装时,先安装符号导出模块,再安装使用符号的模块;

卸载时,先卸载使用符号的模块,再卸载导出符号的模块。



6.测试


6.1 测试思路
  1. 分别创建两个驱动模块文件夹,分别编写符号导出驱动模块(export_symbols.c)和符号使用驱动模块(use_symbols.c)。

  2. 在export_symbols.c中使用EXPORT_SYMBOL()宏函数和EXPORT_SYMPOL_GPL()宏函数测试导出符号。

  3. 编译export_symbols.c会生成本地符号表Module.symvers。

  4. 将生成的本地符号表Module.symvers拷贝到usr_symbols驱动模块文件夹下,编译use_symbols.c。

  5. 查看内核打印消息

  6. 验证对错



6.2 本地符号表符号导出实际操作

创建文件夹,每个文件夹下有一个.c源码和Makefile工程管理文件

在这里插入图片描述

分别编写export_symbols.c和use_symbols.c

  1 #include <linux/module.h>
  2 
  3 int test = 1;
  4 
  5 void function(void)
  6 {
  7         printk("*********export test = %d\n",test);
  8 }
  9 
 10 int driver_init(void)
 11 {
 12         printk("*********export driver init\n");
 13         return 0;
 14 }
 15 void driver_clear(void)
 16 {
 17         printk("*********export driver clear\n");
 18 }
 19 
 20 
 21 module_init(driver_init);
 22 module_exit(driver_clear);
 23 
 24 EXPORT_SYMBOL(test);
 25 EXPORT_SYMBOL_GPL(function);
 26 
 27 MODULE_LICENSE("GPL");
 28 MODULE_AUTHOR("cfy");
 29 MODULE_ALIAS("liangzai");
 30 MODULE_DESCRIPTION("2022-7-6");


​ export_symbols.c

  1 ifeq ($(KERNELRELEASE),)
  2 KERNELDIR ?=/lib/modules/$(shell uname -r)/build
  3 PWD := $(shell pwd)
  4 modules:
  5         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  6 else
  7     obj-m :=export_symbols.o
  8 endif
  9 
 10 clean:
 11         rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*

​ Makefile

    1 #include <linux/module.h>
  2 
  3 extern int test;
  4 extern void function(void);
  5 
  6 int driver_init(void)
  7 {
  8         printk("*********use test = %d\n",test);
  9         function();
 10         return 0;
 11 }
 12 void driver_clear(void)
 13 {
 14         printk("*********use driver clear\n");
 15 }
 16 
 17 
 18 module_init(driver_init);
 19 module_exit(driver_clear);
 20 
 21 MODULE_LICENSE("GPL");
 22 MODULE_AUTHOR("cfy");
 23 MODULE_ALIAS("liangzai");
 24 MODULE_DESCRIPTION("2022-7-6");

​ use_symbols.c

  1 ifeq ($(KERNELRELEASE),)
  2 KERNELDIR ?=/lib/modules/$(shell uname -r)/build
  3 PWD := $(shell pwd)
  4 modules:
  5         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  6 else
  7         obj-m :=use_symbols.o
  8 endif
  9 
 10 clean:
 11         rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*

​ Makefile

编译export_symbols.c

在这里插入图片描述

生成Module.symvers

在这里插入图片描述

将Module.symvers拷贝到use_symbols下

在这里插入图片描述

编译use_symbols.c,出现了未定义的问题。

在这里插入图片描述

观察了一下,发现每次编译之后,之前复制进来的本地符号表文件会“消失”,翻了一下解决方案,还真给找到了,

https://blog.csdn.net/lu_embedded/article/details/51440817

在这里插入图片描述

修改use_symbols的Makefile,添加了第10行

  1 ifeq ($(KERNELRELEASE),)
  2 KERNELDIR ?= /lib/modules/$(shell uname -r)/build
  3 PWD := $(shell pwd)
  4 modules:
  5         $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  6 else
  7     obj-m := use_symbols.o
  8 endif
  9 
 10 KBUILD_EXTRA_SYMBOLS = /home/cfy/hqyj/drivers/sysbols_export/export_symbols/Module.symvers
 11 
 12 clean:
 13         rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*

重新编译

在这里插入图片描述


安装导出模块

在这里插入图片描述

查看内核打印信息,因为测试了好几次,前面的信息为之前测试打印信息

在这里插入图片描述


安装导入模块


在这里插入图片描述

可以看到export_symbols被use_symbols使用,再查看内核打印信息

在这里插入图片描述

倒数第二条为变量导入结果,最后一条为函数导入结果,验证完毕,全局符号导出还有点问题,后续解决后再发布。接下来是

字符设备。



版权声明:本文为a834605978原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。