hsy75的个人空间 https://blog.eetop.cn/vivilife [收藏] [复制] [分享] [RSS]

空间首页 动态 记录 日志 相册 主题 分享 留言板 个人资料

日志

[b][z]linux模块动态加载必须使用的内嵌命令

已有 3134 次阅读| 2013-2-28 10:32 |个人分类:linux

linux系统开发时候,经常遇到insmod动态加载模块找不到相关的一些函数的问题,很多时候,

原因是makefile的LIB引用没考虑好,还有一个就是驱动代码的EXPORT没有写对


1 谈EXPORT_SYMBOL使用

EXPORT_SYMBOL只出现在2.6内核中,在2.4内核默认的非static 函数和变量都会自动导入到kernel 空间的, 都不用EXPORT_SYMBOL() 做标记的。
 2.6就必须用EXPORT_SYMBOL() 来导出来(因为2.6默认不到处所有的符号)。

1.1、EXPORT_SYMBOL的作用是什么?
 EXPORT_SYMBOL标签内定义的函数或者符号全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,

即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用
 这里要和System.map做一下对比:
 System.map 中的是连接时的函数地址。连接完成以后,在2.6内核运行过程中,是不知道哪个符号在哪个地址的。
 EXPORT_SYMBOL 的符号, 是把这些符号和对应的地址保存起来,在内核运行的过程中,可以找到这些符号对应的地址。而模块在加载过程中,其本质就是能动态连接到内核,如果在模块中引用了内核或其它模块的符号,就要EXPORT_SYMBOL这些符号,这样才能找到对应的地址连接。
 
1.2、使用方法
    第一、在模块函数定义之后使用EXPORT_SYMBOL(函数名)
    第二、在调用该函数的模块中使用extern对之声明
    第三、首先加载定义该函数的模块,再加载调用该函数的模块
 
另外,在编译调用某导出函数的模块时,往往会有WARNING: "****" [**********] undefined!
使用dmesg命令后会看到相同的信息。开始我以为只要有这个错误就不能加载模块,后来上网查了一下,发现这主要是因为在编译连接的时候还没有和内核打交道,

当然找不到symbol了,但是由于你生成的是一个内核模块,所以LD不提示error,而是给出一个warning,寄希望于在insmod的时候,内核能够把这个symbol连接上。
 
参考例子:
 
-------------------------------------------------------------
 
http://blog.chinaunix.net/u/12592/showart_461504.html
 
一个模块mod1中定义一个函数func1;在另外一个模块mod2中定义一个函数func2,func2调用func1。
 在模块mod1中,EXPORT_SYMBOL(func1);
 在模块mod2中,extern int func1();
 就可以在mod2中调用func1了。
 
参考:
 http://topic.csdn.net/u/20070910/09/ee2cff13-9179-41e3-9292-4fd73261f709.html
 http://www.dev-archive.com/msdn-archive/524/kernel-driver-5244619.shtm
 
mod1.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,Init!/n");
         return 0;
 }
 
static void __exit hello_exit(void)
 {
         printk("Module 1,Exit!/n");
 }
 
module_init(hello_init);
module_exit(hello_exit);
 

#############################################################
 mod2.c
 #include<linux/init.h>
 #include<linux/kernel.h>
 #include<linux/module.h>
 
static int func2(void)
 {
         extern int func1(void);
         func1();
         printk("In Func: %s.../n",__func__);
         return 0;
 }
 
static int __init hello_init(void)
 {
         printk("Module 2,Init!/n");
         func2();
         return 0;
 }
 
static void __exit hello_exit(void)
 {
         printk("Module 2,Exit!/n");
 }
 
module_init(hello_init);
 module_exit(hello_exit);
 
################################################################
 Makefile
 ifneq ($(KERNELRELEASE),)
 obj-m   := XXXX.o
 else
 KDIR    := /lib/modules/$(shell uname -r)/build
 PWD             := $(shell pwd)
 
default:
         $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
 
clean:
         rm -rf Module.symvers *.ko *.o *.mod.c .*.cmd .tmp_versions
 
endif
 
################################################################
 
#insmod ./mod1.ko
 #insmod ./mod2.ko
 #rmmod mod2
 #rmmod mod1
 
Jan 11 11:59:17 wangyao-desktop kernel: [ 9886.801010] Module 2,Exit!
 Jan 11 11:59:21 wangyao-desktop kernel: [ 9891.450214] Module 1,Exit!
 Jan 11 12:05:29 wangyao-desktop kernel: [10258.385014] Module 1,Init!
 Jan 11 12:05:38 wangyao-desktop kernel: [10267.465923] Module 2,Init!
 Jan 11 12:05:38 wangyao-desktop kernel: [10267.465928] In Func: func1...
 Jan 11 12:05:38 wangyao-desktop kernel: [10267.465930] In Func: func2...
 Jan 11 12:05:50 wangyao-desktop kernel: [10280.091755] Module 2,Exit!
 Jan 11 12:05:57 wangyao-desktop kernel: [10287.332596] Module 1,Exit!
 
可见,在mod2中的func2函数成功的调用了mod1中的func1函数。
 
注意:
 在编译mod2的时候,出现一个WARNING:
 root@wangyao-desktop:~/modules/export_symbol/mod2# make
 make -C /lib/modules/2.6.22-14-generic/build SUBDIRS=/root/modules/export_symbol/mod2 modules
 make[1]: Entering directory `/usr/src/linux-headers-2.6.22-14-generic'
   Building modules, stage 2.
   MODPOST 1 modules
 WARNING: "func1" [/root/modules/export_symbol/mod2/mod2.ko] undefined!
 make[1]: Leaving directory `/usr/src/linux-headers-2.6.22-14-generic'
 

这主要是因为在编译连接的时候还没有和内核打交道,当然找不到symbol了,但是由于你生成的是一个内核模块,所以LD不提示error,而是给出一个warning,寄希望于在insmod的时候,内核能够把这个symbol连接上。
 
 
 
-----------------------------------------------------------
 
http://www.lslnet.com/linux/f/docs1/i46/big5316526.htm
 
 請教關於EXPORT_SYMBOL
 
在一個文件裡要用到別的文件中的函數 用extern不就可以了麼
 
為什麼還需要EXPORT_SYMBOL?
 
   
Re: 請教關於EXPORT_SYMBOL
 
EXPORT_SYMBOL是給模塊用的。
 

   
Re: 請教關於EXPORT_SYMBOL
 
我看好像只要函數不是聲明為static的
 在System.map中也有並且和EXPORT_SYMBOL的函數一樣。
 
模塊也可以使用的吧
 
   
Re: 請教關於EXPORT_SYMBOL
 
System.map 中的是鏈接時的函數地址。 連接完成以後,在內核運行過程中,是不知道哪個符號在那個地址的。而這個文件是給調試用的。其中的內容,kernel並不知道 。
 

EXPORT_SYMBOL 的符號, 是把這些符號和對應的地址,保存起來,在內核運行的過程中,可以找到這些符號對應的地址的。
 
而module在加載過程中,其本質就是動態連接到內核,如果在模塊中引用了內核或其它模塊的符號,就要 EXPORT_SYMBOL 這些符號,這樣才能找到對應的地址連接呀。 要不沒法連接的。
 
   
Re: 請教關於EXPORT_SYMBOL
 
那個是2.4的, 2.4中只要全局的, 符號就算導出了; 2.6則必須顯式調用EXPORT_SYMBOL或其變體。
 
   
Re: 請教關於EXPORT_SYMBOL
 
內核模塊加載的鏈接過程,不是一個普通的鏈接過程,是內核自己做的一個特殊的過程,
 因此,單純extern是不可以的,內核強行要求用EXPORT_SYMBOL

 
 
-------------------------------------------------------------
 
http://hi.baidu.com/leal/blog/item/d3e1cafcb97c2dfdfd037fc2.html
 
System.map[1]是Linux内核符号文件,维护有内核函数名称和非堆栈变量名称与各自地址的对应关系。
 
若内核函数或变量要被内核模块调用,则必须使用EXPORT_SYMBOL宏进行处理,作用之一是将该符号连接到二进制文件的各个 __ksymtab_xx_xx section(参看include/linux/module.h,使用GCC编译器的__attribute__关键字实现[2])。内核加载模块 时,会先确认该模块调用的各内核函数是否已export(参看__find_symbol() kernel/module.c)。
 
比如FC5缺省会给vanilla内核打补丁,使其不再export sys_open符号,这一点可搜索该内核对应的System.map文件进行确认,看是否存在__ksymtab_sys_open符号。
 
[1] The system.map File
 http://www.dirac.org/linux/system.map/
 
[2] Using GNU C __attribute__
 http://www.unixwiz.net/techtips/gnu-c-attributes.html
 
 
 
------------------------------------------------------------
 
http://www.linuxsir.org/bbs/thread347677.html
 
 
 
在编写module是,如果函数的声明没有加static,那么我理解就应该是全局的阿,没什么要用
 EXPORT_SYMBOL()
 
 
 
好象是因为系统需要生成类似于C00021_PRINTK 一类的链接用的,因为系统的函数本身其实是有其前缀的
 
 
 
模块是动态加载的,需要一个运行时存在的符号表,找到符号。而我们一般所说的符号表是供静态连接时定位符号地址用的。EXPORT_SYMBOL宏的作用就是把静态符号表中的符号和地址放到运行时的符号表中(在一个section中)供运行时寻找符号用。
 看一下EXPORT_SYMBOL的定义就知道了。
 
 
 
-------------------------------------------------------------
 
http://www.unixresources.net/linux/clf/linuxK/archive/00/00/71/60/716080.html
 
driver/char/console.c 里面提供了 这个函数:

/* console_sem is held (except via vc_init()) */

void reset_terminal(int currcons, int do_clear)

{

 top  = 0;

 bottom  = video_num_lines;

。。。。。。

 

在console.c 的最后面也有:

/*
* Visible symbols for modules
*/

EXPORT_SYMBOL(fg_console);
EXPORT_SYMBOL(console_blank_hook);
EXPORT_SYMBOL(hide_cursor);
EXPORT_SYMBOL(reset_terminal); //这个


drivers/char/Makefile 里面也有:


# All of the (potential) objects that export symbols.

# This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.

 

export-objs     := busmouse.o console.o keyboard.o sysrq.o

   misc.o pty.o random.o selection.o serial.o

   sonypi.o tty_io.o tty_ioctl.o generic_serial.o

   au1000_gpio.o hp_psaux.o nvram.o scx200.o

 

我要在kernel/power/ui.c 里面调用:


void pm_restore_console(void)

{

 

 if (TEST_ACTION_STATE(SUSPEND_NO_OUTPUT))

  return;

 

// reset_terminal(suspend_console, 1);

 reset_terminal(TTY_MAJOR,1);


可是编译内核的时候, 最后, 就说 undefined reset_terminal () , 奇怪了。
/usr/bin/mips-linux-ld -G 0 -static -T arch/mips/ld.script. arch/mips/kernel/head.o
 arch/mips/kernel/init_task.o init/main.o init/version.o init/do_mounts.o --start-group
 arch/mips/kernel/kernel.o arch/mips/mm/mm.o kernel/kernel.o mm/mm.o fs/fs.o ipc/ipc.o
 arch/mips/math-emu/fpu_emulator.o arch/mips/emma2_se/emma2.o drivers/char/char.o
 drivers/block/block.o drivers/misc/misc.o drivers/net/net.o drivers/ide/idedriver.o
 drivers/pci/driver.o drivers/mtd/mtdlink.o drivers/net/wireless/wireless_net.o
drivers/usb/usbdrv.o drivers/media/media.o drivers/md/mddev.o net/network.o
arch/mips/lib/lib.a /home/work/data3/standby/dv_kernel_suspend2/linux/lib/lib.a
--end-group -o vmlinux
kernel/kernel.o: In function `pm_restore_console':
kernel/kernel.o(.text+0x16030): undefined reference to `reset_terminal'
kernel/kernel.o(.text+0x16030): relocation truncated to fit: R_MIPS_26 reset_terminal
 make[1]: *** [kallsyms] Error 1
make[1]: Leaving directory `/home/work/data3/standby/dv_kernel_suspend2/linux'
make: *** [vmlinux] Error 2

大家帮诊断一下。 -DEXPORT_SYMTAB 我也加了。 还是不行。
EXPORT_SYMBOL()
 被export的符号,是用来给加载模块时链接时用的, 编译内核自身时, 和export 应该是没有关系的. 看是否包含了对应的头文件, console.c是被编译为模块, 还是编译到内核? 如果是别编译到内核, 只要在ui.c
 里包含了定义reset_terminal的头文件,应该是可以编译出来的.
 
没错,一阵见血。
 找到原因了,console.o 的编译被我们自己的宏给包住了(我们自己改了一些东西), 结果 console.o 没有被编译出来,当然就找不到reset_terminal() 了。
 所以光有:
 export-objs := busmouse.o console.o keyboard.o sysrq.o
 misc.o pty.o random.o selection.o serial.o
 sonypi.o tty_io.o tty_ioctl.o generic_serial.o
 au1000_gpio.o hp_psaux.o nvram.o scx200.o
 也是不行的,
 还有了一个 obj-y =+ console.o
 
我补充一下:
 以前做2.4的时候确实如此,
 我记得是 对于2.4 来说, 默认的非static 函数和变量都会自动导入到kernel 空间的, 都不用EXPORT_SYMBOL() 做标记的。
 
2.6就必须用EXPORT_SYMBOL() 来导出来(因为2.6默认不到处所有的符号)。



2 还可以手工修改内核源代码来导出另外的函数,用于重新编译并加载新内核后的测试。
 

Linux symbol export method:
 
[1] If we want export the symbol in a module, just use the EXPORT_SYMBOL(xxxx ) in the C or H file.
     And compile the module by adding the compile flag -DEXPORT_SYMTAB .
     Then we can use the xxxx in the other module.
 

[2] If we want export some symbol in Kernel that is not in a module such as xxxx in the /arch/ppc/fec.c .
     Firstly, define the xxxx in the fec.c ;
     Secondly, make a new file which contain the "extern" define the xxxx (for example, extern int xxxx );
     Lastly, in the ppc_ksyms.c we includes the new file, and add the EXPORT_SYMBOL ( xxxx ).
     Then we can use the xxxx .
 


使用时注意事项:
 在使用EXPORT_SYMBOL 的.c文件中 需要 #include <linux/module.h> 文件。
 // 先写函数
 func_a ()
 {
 
}
 //再使用EXPORT_SYMBOL
 EXPORT_SYMBOL(func_a);


linux2.6的“/proc/kallsyms”文件对应着内核符号表,记录了符号以及符号所在的内存地址。模块可以使用如下宏导出符号到内核符号表:
 EXPORT_SYMBOL(符号名);  
EXPORT_SYMBOL_GPL(符号名) 
导出的符号可以被其他模块使用,不过使用之前一定要extern声明一下。EXPORT_SYMBOL_GPL()只适用于包含GPL许可权的模块。
       举一个代码演示:
 一个文件是hello.c文件,定义2个函数,用于导出 
#ifndef _KERNEL_
      #define _KERNEL_
 #endif

#ifndef MODULE
      #define MODULE
 #endif
 #include <linux/init.h> 
#include <linux/module.h>  
MODULE_LICENSE("Dual BSD/GPL");  
int add_integar(int a,int b)  
{  
    return a + b;  
}  
int sub_integar(int a,int b)  
{  
    return a - b;  
}  
EXPORT_SYMBOL(add_integar);  
EXPORT_SYMBOL(sub_integar);  


另一个文件是test.c 用于调用hello模块导出的函数 
#ifndef _KERNEL_
      #define _KERNEL_
 #endif

#include <linux/init.h> 
#include <linux/module.h>  
MODULE_LICENSE("Dual BSD/GPL");  
extern int add_integar(int ,int); //声明要调用的函数  
extern int sub_integar(int ,int); //声明要调用的函数  
int result(void)  
{  
    int a,b;  
    a = add_integar(1,1);  
    b = sub_integar(1,1);     
     printk("%d/n",a);  
    printk("%d/n",b);  
     return 0;  

     两个文件编译都要有对应的makefile。模板如下:
 ifeq ($(KERNELRELEASE),)

    # Assume the source tree is where the running kernel was built
     # You should set KERNELDIR in the environment if it's elsewhere
     KERNELDIR ?=  /lib/modules/$(shell uname -r)/build

    # The current directory is passed to sub-makes as argument
     PWD := $(shell pwd)

modules:
  $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
  $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
  rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions

.PHONY: modules modules_install clean

else
     # called from kernel build system: just declare what our modules are
     obj-m := hello.o
 endif
       把hello.o改成对应的文件名.o就可以了。分别make后,先加载hello模块,再加载test模块。然后cat /proc/kallsyms | grep integar,显示:
 f8eae000 u add_integar  [hello2]
 f8eae004 u sub_integar  [hello2]
 f8eae02c r __ksymtab_sub_integar        [hello]
 f8eae03c r __kstrtab_sub_integar        [hello]
 f8eae034 r __ksymtab_add_integar        [hello]
 f8eae048 r __kstrtab_add_integar        [hello]
 f8eae000 T add_integar  [hello]
 f8eae004 T sub_integar  [hello]

 

      可以看出符号表的名字和对应的内存地址。EXPORT_SYMBOL标签内定义的函数对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用。
 
原文地址:http://blog.csdn.net/lisan04/archive/2009/04/16/4076013.aspx

 

 

 

 

 

 

 

 

 


点赞

评论 (0 个评论)

facelist

您需要登录后才可以评论 登录 | 注册

  • 关注TA
  • 加好友
  • 联系TA
  • 0

    周排名
  • 0

    月排名
  • 0

    总排名
  • 0

    关注
  • 2

    粉丝
  • 1

    好友
  • 2

    获赞
  • 14

    评论
  • 3241

    访问数
关闭

站长推荐 上一条 /1 下一条

小黑屋| 关于我们| 联系我们| 在线咨询| 隐私声明| EETOP 创芯网
( 京ICP备:10050787号 京公网安备:11010502037710 )

GMT+8, 2024-5-22 14:18 , Processed in 0.017423 second(s), 7 queries , Gzip On, Redis On.

eetop公众号 创芯大讲堂 创芯人才网
返回顶部