linux module (2) - EXPORT_SYMBOL

  • 了解linux EXPORT_SYMBOL

1.Introduction

  In programming language, a symbol is either a variable or a function. Or more generally, we can say, a symbol is a name representing an space in the memory, which stores data (variable, for reading and writing) or instructions (function, for executing).

  By default, any global variables or functions defined in a module are exported to the kernel symbol table when the module is loaded. However, there are ways which you may control which symbols are exported.

  • If you only require that none of the module’s symbols are exported you can use the EXPORT_NO_SYMBOLS macro.

  • If however, you require that only some of your global symbols are exported you will need to use the EXPORT_SYMBOL macro to export it.

  When you look at some kernel codes, you may find EXPORT_SYMBOL() very often. Have you wondered any time what the heck is that?

  In the Linux Kernel 2.4, all the non-static symbols are exported to the kernel space automatically. But later, in Linux Kernel 2.6 instead of exporting all non-static symbols, they wanted to export the only symbols which is marked by EXPORT_SYMBOL() macro.

1.1.EXPORT_SYMBOL() macro

  Generally the modules will never live alone. We need to divide the code into multiple modules for better organization and readability as well as we need to use the APIs or functionality which is available in other modules or the functions which are made available to us by the Linux Kernel.

  EXPORT_SYMBOL() is a macro the Linux kernel headers define. It tells the kbuild mechanism that the symbol referred to should be part of the global list of kernel symbols. That, in turn allows kernel modules to access them.

  EXPORT_SYMBOL is to expose the function/Variable to the others. Assume you developed a function in one of the kernel module and you want to use same function in some other module, in this case you have to expose this function. Linux kernel provided EXPORT_SYMBOL to do this.

  • EXPORT_SYMBOL exports the symbol to any loadable module.
  • EXPORT_SYMBOL_GPL exports the symbol only to GPL-licensed modules.

2.Definition

  EXPORT_SYMBOL宏定义在include/linux/export.h的头文件中,其定义如下:

123 #define EXPORT_SYMBOL(sym)                  \
124     __EXPORT_SYMBOL(sym, "")

 77 /* For every exported symbol, place a struct in the __ksymtab section */
 78 #define ___EXPORT_SYMBOL(sym, sec)                  \
 79     extern typeof(sym) sym;                     \
 80     __CRC_SYMBOL(sym, sec)                      \
 81     static const char __kstrtab_##sym[]             \
 82     __attribute__((section("__ksymtab_strings"), used, aligned(1))) \
 83     = #sym;                             \
 67     static const struct kernel_symbol __ksymtab_##sym       \
 68     __attribute__((section("___ksymtab" sec "+" #sym), used))   \
 69     = { (unsigned long)&sym, __kstrtab_##sym }

  使用EXPORT_SYMBOL(my_function)为例进行描述,即向外部导出一个名为my_function的函数。每个EXPORT_SYMBOL宏实际定义了两个变量:

  • static const char __kstrtab_my_function = “my_function”;
  • static const struct kernel_symbol __ksymtab_my_function =
    { (unsigned long)&my_function, __kstrtab_my_function};

  第一个变量是char指针,表示导出的符号名称;第二个变量是struct kernel_symbol结构,表示一个内核符号的实例,struct kernel_symbol定义:

 71 struct kernel_symbol {                                                                                   
 72     unsigned long value;   //符号在内存中的地址
 73     const char *name;      //符号名
 74 };

  用EXPORT_SYMBOL(my_function)导出符号“my_function”,实际上是要通过struct kernel_symbol中的一个对象告诉外部世界关于这个符号的两点信息:符号名称和地址。导出的符号放在特定的section中。

  • __kstrtab_my_function放在名为“__ksymtab_strings”的section中;
  • __ksymtab_my_function放在名为“__ksymtab”的section中。

  section的使用需要一个中间环节,即链接脚本与链接器部分。链接脚本告诉链接器把所有目标文件中的名为“__ksymtab”的section放在最终内核映像文件的名为“__ksymtab”的section中。

   kernel/module.c:
   376 /* Provided by the linker */
   377 extern const struct kernel_symbol __start___ksymtab[];
   378 extern const struct kernel_symbol __stop___ksymtab[];
   379 extern const struct kernel_symbol __start___ksymtab_gpl[];
   380 extern const struct kernel_symbol __stop___ksymtab_gpl[];
   381 extern const struct kernel_symbol __start___ksymtab_gpl_future[];
   382 extern const struct kernel_symbol __stop___ksymtab_gpl_future[];
   383 extern const s32 __start___kcrctab[];
   384 extern const s32 __start___kcrctab_gpl[];
   385 extern const s32 __start___kcrctab_gpl_future[];

3.How to use EXPORT_SYMBOL?

  • Declare and define the symbol (functions or variables) which you want to make it visible to other kernel modules. Then below the definition, use EXPORT_SYMBOL(symbol name). Now it is visible to all loadable modules.
  • Now take the kernel driver who is gonna use the above exported symbol. Declare the symbol using extern. Then use the symbol directly.
  • Finally, load the module first, who has the definition of the export symbol. Then load the caller module using insmod“.

Limitation

  • That symbol should not be static or inline.
  • Order of loading the driver is matter. ie. We should load the module which has the definition of the symbol, then only we can load the module who is using that symbol.

4.Driver Source Code – EXPORT_SYMBOL in Linux

  In this tutorial we have two drivers.

  • Driver 1 has one function called func1. This function has been shared among with all the loadable modules using EXPORT_SYMBOL.

  • Driver 2 will be using that function which are shared by Driver 1. When we read this Driver 2, then it will call the shared function and we can read that variable also.

Let’s see the source code below.

driver1.c

#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h>
 
static int func1(void)
{
       printk("In Func: %s...\n",__func__);
       return 0;
}
 
EXPORT_SYMBOL(func1);
 
static int __init hello_init(void)
{
       printk("Module 1,say hello world!\n");
       return 0;
}
 
static void __exit hello_exit(void)
{
       printk("Module 1,Exit!\n");
}
 
module_init(hello_init);
module_exit(hello_exit);

driver2.c

#include<linux/init.h>
#include<linux/kernel.h>
#include<linux/module.h>

extern int func1(void);

static int func2(void)
{
       func1();
       printk("In Func: %s...\n",__func__);
       return 0;
}
 
static int __init hello_init(void)
{
       printk("Module 2,is used Module 1 function!\n");
       func2();
       return 0;
}
 
static void __exit hello_exit(void)
{
       printk("Module 2,Exit!\n");
}

module_init(hello_init);
module_exit(hello_exit);

Makefile:

obj-m += driver1.o
obj-m += driver2.o
 
KDIR = /lib/modules/$(shell uname -r)/build
 
all:
	make -C $(KDIR) M=$(shell pwd) modules
 
clean:
	make -C $(KDIR) M=$(shell pwd) clean

Compiling and Testing Driver

  • Build the driver by using Makefile (sudo make)
  • After compiling, you can able to see the file named as “Module.symvers“. If you open that file, then our shared function will be mentioned there.
  • Load the driver 1 using sudo insmod driver1.ko(Driver 1 should be loaded first. If you try to load the Driver 2 first, then you will get an error like “insmod: ERROR: could not insert module driver2.ko: Unknown symbol in module“).
  • Load the driver 1 using sudo insmod driver2.ko
  • Now check the dmesg
  • Then do cat /proc/kallsyms | grep func1 check whether our shared function and variable become the part of kernel’s symbol table or not.
  • Now we can see the print from shared function also.
  • Unload the module 2 using sudo rmmod driver2(Driver 2 should be unloaded first. If you unload the Driver 1 first, then you will get error like “rmmod: ERROR: Module driver1 is in use by: driver2“).
  • Unload the module 1 using sudo rmmod driver1

Kernel node:

  • cat /sys/module/xxx
  • cat /proc/modules/
  • cat /proc/sys/kernel/tainted
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值