[Linux Kernel] 内核EXPORT_SYMBOL宏定义分析

0. 背景

EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。

在模块函数定义之后使用“EXPORT_SYMBOL(函数名)”来声明。
在调用该函数的另外一个模块中使用extern对之声明。
//经观察内核里面使用EXPORT_SYMBOL声明之后,很多不需要extern也能直接使用函数;

先加载定义该函数的模块,然后再加载调用该函数的模块,请注意这个先后顺序。

1. 举例

函数声明位置:
LA.UM.5.6\kernel\msm-3.18\kernel\irq\devres.c
在这里插入图片描述
函数使用位置:
LA.UM.5.6\kernel\msm-3.18\drivers\input\hall_sensor.c

在这里插入图片描述

grep一下不存在extern的地方;

在这里插入图片描述

2. EXPORT_SYMBOL代码追踪

EXPORT_SYMBOL(devm_request_threaded_irq);
->__EXPORT_SYMBOL(sym, “”)
->

     /* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec)				\
	extern typeof(sym) sym;					\
	__CRC_SYMBOL(sym, sec)					\
	static const char __kstrtab_##sym[]			\
	__attribute__((section("__ksymtab_strings"), aligned(1))) \
	= VMLINUX_SYMBOL_STR(sym);				\
	extern const struct kernel_symbol __ksymtab_##sym;	\
	__visible const struct kernel_symbol __ksymtab_##sym	\
	__used							\
	__attribute__((section("___ksymtab" sec "+" #sym), unused))	\
	= { (unsigned long)&sym, __kstrtab_##sym }

3. 代码结构简化与理解

这个宏代码很庞大,让人看的懵逼。
我们尝试优化代码,首先这个extern typeof(sym) sym;
extern int sym; typeof(sym)用于获取这个函数的返回值。
在这里插入图片描述在这里插入图片描述

__attribute__是gcc中的属性(__used也是gcc属性),用于向指定的函数或者变量添加相关的属性,为了不影响对变量定义的理解,先将__attribute__属性掩盖,

简化如下:
在这里插入图片描述

/* Some toolchains use a `_' prefix for all user symbols. */
#ifdef CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX
#define __VMLINUX_SYMBOL(x) _##x
#define __VMLINUX_SYMBOL_STR(x) "_" #x
#else
#define __VMLINUX_SYMBOL(x) x
#define __VMLINUX_SYMBOL_STR(x) #x
#endif

在这里插入图片描述
这个也不晓得加没加_,这里按照不开这个CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX宏来分析:

一个#的意思是把后面的变成字符串;
VMLINUX_SYMBOL_STR(sym); --> “sym”

两个##号的意思是把前后链接起来;

所以宏继续简化如下:

在这里插入图片描述
在这里插入图片描述

4. 那我们再看这个宏干了啥

  1. 首先把传进来的这个函数指针声明为全局;

  2. 定义了一个const类型的字符串数组来存放这个函数指针的名字

  3. 定义一个内核符号结构__ksymtab_sym用于存放导出符号的内存地址和名称,
    (unsigned long)&sym存地址,__kstrtab_sym指针传给name

5. 补充

// (unsigned long)&sym,这行代码,首先指针本身的类型都是unsigned long int,所以需要强制类型转换,此外这里&sym的意思,是取指向函数sym的指针,如果直接用sym的话,
表明函数的首地址.
在这里插入图片描述

gcc编译运行两者结果是一致的:

在这里插入图片描述

__attribute__相关的理解可以参阅:

http://blog.sina.com.cn/s/blog_661314940100qujt.html

还有以下这篇文章对我得帮助很大,再次特别特别感谢博主:

https://blog.csdn.net/fzubbsc/article/details/33726329

原创文章 42 获赞 15 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Codeliang666/article/details/105655534