注:过自网资料的收录编辑,记录实施过程疑问及查找资料
1 )、EXPORT_SYMBOL(),这个宏也是将函数导出让所有模块都可以使用,而EXPORT_SYMBOL_GPL()这个宏主要是给有GPL认证的模块使用。
2)、EXPORT_SYMBOL标签内定义的函数对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用。
3)、EXPORT_SYMBOL(符号名); EXPORT_SYMBOL_GPL(符号名)
一)主要作之一: 内核"导出"的符号表,这个表在insmod 时候会用到.
1./proc/kallsyms
cat /proc/kallsyms会打印出内核当前的符号表,例如:
...
d8834a24 t snd_free_sgbuf_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处理过的符号。
2.System.map内核符号文件
通过more /boot/System.map 可以查看内核符号列表。
可以显示编译好内核后所有在内核中的符号,模块中的要另行查看。
3.通过nm vmlinux也可以查看内核符号列表
可以显示编译好内核后所有在内核中的符号,模块中的要另行查看。
4.通过nm module_name可以查看模块的符号列表
但是得到是相对地址,只有加载后才会分配绝对地址。比如:e1000模块,如果e1000中的符号经过EXPORT_SYMBOL处理,
等加载后,我们可以通过more /boot/System.map和nm vmlinux命令查看到,但是没有EXPORT_SYMBOL的,不能查看。
代码如:
int __gpio_cansleep(unsigned gpio)
{
struct gpio_chip *chip; /* only call this on GPIOs that are valid! */
chip = gpio_to_chip(gpio);
return chip->can_sleep;
}
EXPORT_SYMBOL_GPL(__gpio_cansleep);
/**
* __gpio_to_irq() - return the IRQ corresponding to a GPIO
* @gpio: gpio whose IRQ will be returned (already requested)
* Context: any
*
* This is used directly or indirectly to implement gpio_to_irq().
* It returns the number of the IRQ signaled by this (input) GPIO,
* or a negative errno.
*/
int __gpio_to_irq(unsigned gpio)
{
struct gpio_chip *chip;
chip = gpio_to_chip(gpio);
return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO;
}
EXPORT_SYMBOL_GPL(__gpio_to_irq);
二) EXPORT_SYMBOL_GPL导出函数
如果要用EXPORT_SYMBOL_GPL导出函数,使用此函数的模块需要MODULE_LICENSE("GPL") 或MODULE_LICENSE("Dual BSD/GPL")之后才能在模块中引用来声明
三)EXPORT_SYMBOL 导出函数
include/module.h:
struct kernel_symbol
{
unsigned long value;
const char *name;
};
/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec) \
__CRC_SYMBOL(sym, sec) \
static const char __kstrtab_##sym[] \
__attribute__((section("__ksymtab_strings"))) \
= MODULE_SYMBOL_PREFIX #sym; \
static const struct kernel_symbol __ksymtab_##sym \
__attribute_used__ \
__attribute__((section("__ksymtab" sec), unused)) \
= { (unsigned long)&sym, __kstrtab_##sym }
#define EXPORT_SYMBOL(sym) \
__EXPORT_SYMBOL(sym, "")
#define EXPORT_SYMBOL_GPL(sym) \
__EXPORT_SYMBOL(sym, "_gpl")
#endif
Analysis:
1. kernel_symbol: 内核函数符号结构体
value: 记录使用EXPORT_SYMBOL(fun),函数fun的地址
name: 记录函数名称("fun"),在静态内存中
2. EXPORT_SYMBOL(sym) :导出函数符号,保存函数地址和名称
宏等价于:(去掉gcc的一些附加属性,MODULE_SYMBOL_PREFIX该宏一般是"")
static const char __kstrtab_sym[] = "sym";
static const struct kernel_symbol __ksymtab_sym =
{(unsigned long)&sym, __kstrtab_sym }
3. gcc 附加属性
1>. __atrribute__ 指定变量或者函数属性。
__attribute((section("section-name")) var : 编译器将变量var放在section-name所指定的data或者bss段里面。
很容易看出:EXPORT_SYMBOL(sym)将sym函数的名称__kstrtab_sym记录在,段名为"__kstrtab_strings"数据段中。 将sym所对应的kernel_symbol记录在名为__ksymtab段中。
EXPORT_SYMBOL_GPL(sym) 和EXPORT_SYMBOL不同之处在于sym对应的kenel_symbol记录在__ksymtab_gpl段中。 |