arm上加载insmod驱动时出现Unknown symbol in module

问题



这几天在arm上做蓝牙耳机驱动的时候,编译好了驱动但是在板子上insmod时候。

怎么会出现这种情况,不对呀,仔细查我们会发现,其实编译驱动的时候,就出现了一些警告,只是当时没有在意而已,而恰恰是这些警告导致的这些问题。

硬件设备
板子用的是realarm
内核linux-2.6.35
交叉编译器arm-linux-gcc 4.4.3

问题解析



究其原因,其原因就是我们的驱动找不到内核的几个函数,我们可以看到我们找不到的函数有两个,一个是kill_proc_info 一个是snd_hwdep_new

问题来了,我们内核编的好好的怎么会找不到这两个函数呢

System.map与内核符号表



亲自编译过linux内核的可能编译完内核都会发现在生成vmlinuz的同时,生成一个System.map文件。

nm /boot/vmlinux > System.map
  • 1
  • 1

通常我们会把发送到标准输出设备的链接映象信息重定向到一个文件中(如System.map)。编译内核时,System.map文件用于存放内核符号表信息。符号表是所有内核符号及其对应地址的一个列表,随着每次内核的编译,就会产生一个新的对应System.map文件,当内核运行出错时,通过System.map中的符号表解析,就可以查到一个地值应的变量名,或反之。

利用System.map,在内核或相关程序出错时,就可以获得我们比较容易识别的信息。

System.map位于使用它的软件(例如内核日志记录后台程序klogd)能够找到的地方。在系统启动时,如果没有以一个参数的形式为klogd给出System.map的位置,则klogd会在三个地方寻找System.map:

/boot/System.map
/System.map
/usr/src/linux/System.map
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

尽管内核本身实际上不使用System.map,但其它程序,像klogd、lsof、ps以及其它像dosemu等许多软件

都需要有一个正确的System.map文件。利用该文件,这些程序就可以根据已知的内存地址查找出对应的内核变量名称,便于对内核的调试工作。

首先我们可以查看下我们需要的函数是否导入到符号表中System.map
使用如下命令查看cat -n System.map | grep kill_proc_infocat -n System.map | grep snd_hwdep_new

我们发现函数snd_hwdep_new这个函数不在内核中,但是kill_proc_info已经在符号标志中,也就是已经编译在内核里面了,但是为什么内核还是找不到它呢,没事我们逐个解决。

EXPORT_SYMBOL



EXPORT_SYMBOL只出现在2.6内核中,在2.4内核默认的非static 函数和变量都会自动导入到kernel 空间的, 都不用EXPORT_SYMBOL() 做标记的。

2.6以后的内核就必须用EXPORT_SYMBOL() 来导出来(因为2.6默认不到处所有的符号)。
EXPORT_SYMBOL的作用是什么?
EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。
这里要和System.map做一下对比:
System.map 中的是链接时的函数地址。链接完成以后,在2.6内核运行过程中,是不知道哪个符号在哪个地址的。

EXPORT_SYMBOL 的符号, 是把这些符号和对应的地址保存起来,在内核运行的过程中,可以找到这些符号对应的地址。而模块在加载过程中,其本质就是能动态连接到内核,如果在模块中引用了内核或其它模块的符号,就要EXPORT_SYMBOL这些符号,这样才能找到对应的地址连接。

使用需要三个步骤
第一、在模块函数定义之后使用EXPORT_SYMBOL(函数名)
第二、在掉用该函数的模块中使用extern对之声明
第三、首先加载定义该函数的模块,再加载调用该函数的模块

另外,在编译调用某导出函数的模块时,往往会有

WARNING: "****" [**********] undefined!
  • 1
  • 1

这个正好就是我们编译驱动时出现的那个警告

解决kill_proc_info


问题解析



kill_proc_info这个函数已经编译进了内核(在符号表中有这个函数),但是模块仍然找不到地址,这个说明一个问题,就是我们的函数kill_proc_info并没有被导出,也就是加载模块时候,模块驱动找不到地址。

我们查找一下内核源码中,所有出现kill_proc_info 的地方

我们会发现没有EXPORT_SYMBOL 的记录,也就是说**该函数的确没有导出, 即外界不可访问。
那么我们就按照那三个步骤我们以此查找

在模块函数定义之后使用EXPORT_SYMBOL(函数名)



首先我们找到定义函数的地方,前我们已经用grep -r kill_proc_info * 查找内核源码中所有出现kill_proc_info的地方。
我们可以看到其声明在include/linux/sched.h 定义在kernel/signal.c
其声明的函数原型为

extern int kill_proc_info(int, struct siginfo *, pid_t);
  • 1
  • 1

那么我们就在kernel/signal.c 文件中函数定义之后添加如下代码

EXPORT_SYMBOL(kill_proc_info);
  • 1
  • 1

在掉用该函数的模块中使用extern对之声明



然后我们在自己的模块中,调用该函数之前,声明此函数,声明如下

extern int kill_proc_info(int, struct siginfo *, pid_t);
  • 1
  • 1

重新编译内核和我们的驱动模块



最后我们重新编译内核和我们的驱动模块,我们可以发现我们的

WARNING: "****" [kill_proc_info] undefined!
  • 1
  • 1

这个警告消失了,同时我们再次查找kill_proc_info 的信息,我们可以看到,除了EXPORT_SYMBOL的信息,System.map中也多了几项关于kill_proc_info的信息

解决snd_hwdep_new


问题解析



对于snd_hwdep_new 我们采用同kill_proc_info 同样的方法逐个排除其原因

我们发现该函数

声明在include\sound/hwdep.c
定义在sound/core/hwdep.c
已经被EXPORT_SYMBOL导出
符号表System.map中没有这个函数

这个说明我们的函数所属的模块在内核编译的过程中,没有被编译进内核中。 这样我们的驱动模块使用它的函数同样找不到地址。

查找所属模块



那么我们来确认一下我们推断的正确性。

我们需要查找到该函数所属的模块,然后到该模块sound/core下,查看Kconfig和Makefile的信息。

首先进入该模块sound\core,源代码文件为hwdep.c,那么目标代码很有可能就叫做hwdep.o,我们看目录下有没有这个目标文件

很明显没有,但是我们并不能保证它就没有参与编译,因为它编译的目标文件也有可能不是hwdep.o , 这个依赖关系我们可以到Makefile中查找。

很明显编译后,的确会生成hwdep.o
而且我们也发现,这个配置信息是CONFIG_HW_DEP
那么我们就去Kconfig中查找这个变量的配置

我们可以发现这个模块属于ALSA SOUND,那么我们就好解决了,可以有多种方案实现
,其实解决方案的本质是一样的,

一个是直接修改配置文件Kconfig,使其编译进去内核
一个是使用make menuconfig或者其他配置工具,选择编译所属模块

我选择了第一种,因为我们现在已经找到了其Kconfig配置所在的位置,直接修改即可,选择第二种,方案的话往往不好取舍。

配置编译



我们在模块配置的地方加入default y
意为在编译内核的时候,直接将模块编译进内核,
这就相当于在make menuconfig的时候在该模块的地方选择y(或者*)

重新编译内核和我们的驱动模块



最后我们重新编译内核和我们的驱动模块,我们可以发现我们的

WARNING: "****" [snd_hwdep_new] undefined!
  • 1
  • 1

这个警告消失了,同时我们再次查找snd_hwdep_new 的信息,我们可以看到,除了EXPORT_SYMBOL的信息,System.map中出现关于snd_hwdep_new的信息

总结



出现Unknown symbol in module,其本质就是模块在加载过程中,找不到函数的地址

那么我们就查找System.map和函数声明和定义的地方时候用EXPORT_SYMBOL

然后确认其具体出现原因,一般有两个

①函数未被EXPORT_SYMBOL,导致加载时找不到链接地址
②函数所在模块未被编译到内核中,导致加载时找不到链接地址

我们逐个排查,找出原因所在后,逐个解决即可,完成后重新编译内核已经驱动模块。
但是要注意如果您依赖的函数也是一个驱动模块,则应该首先加载定义该函数的模块,再加载调用该函数的模块

转载:http://blog.csdn.net/gatieme/article/details/48979427

时间: 2024-09-26 22:06:49

arm上加载insmod驱动时出现Unknown symbol in module的相关文章

加载jdbc驱动时异常

问题描述 环境是:struts1.3hibernate3mssqljdbc的驱动包项目加载时有时会报如下异常:ava.sql.SQLException:AnSQLExceptionwasprovokedbythefollowingfailure:java.lang.SecurityException:class"com.microsoft.sqlserver.jdbc.SQLServerStatement"'ssignerinformationdoesnotmatchsignerinf

ASP.net中动态加载控件时一些问题的总结

asp.net|动态|加载|控件|问题 经常见到有人说在ASP.net中不要使用动态控件,我想主要的原因在于使用动态控件会带来一些问题,在做项目的过程中,我将由动态加载控件引发的总是作了一个小小的总结.1 .在使用LoadControl加载控件后,用户控件中的某些控件不再响应事件. 这个问题主要是由于将控件加载放在if (!Page.IsPostBack)之内引起的,放在外面即可.在思归的blog上对此问题进行了详细的说明.2.用户控件中某些控件的响应出现问题,如某个按钮第一次选择时不触发CLI

windows编程 在一个窗口上加载一个位图图片

问题描述 windows编程 在一个窗口上加载一个位图图片 希望前辈们能给个demo程序.或者最基本的窗口上加载一个位图图片就好,加载位图时用的LoadImage 初学windows编程,不太懂上下文设备,希望大神能给个例程,让我学习. 解决方案 c++windows编程加载图片 解决方案二: #include #include "resource.h" LRESULT CALLBACK WndProc1(HWND hwnd,UINT message,WPARAM wParam,LPA

ios webview加载本地文件时崩溃

问题描述 ios webview加载本地文件时崩溃 加载本地一个docx格式的文件,程序崩溃,exc_bad_access 完全没有异常信息 解决方案 http://www.lxway.com/884821684.htm 解决方案二: 至少要上一下给我们代码看看嘛 解决方案三: iOS webView 加载文件 及 文件操作iOS中webview加载本地html文件iOS webview 加载html 乱码

Android开发中如何解决加载大图片时内存溢出的问题

Android开发中如何解决加载大图片时内存溢出的问题    在Android开发过程中,我们经常会遇到加载的图片过大导致内存溢出的问题,其实类似这样的问题已经屡见不鲜了,下面将一些好的解决方案分享给大家.   尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存. 因此,改用先通过Bitmap

环境-php 加载mysqlL模块时不成功

问题描述 php 加载mysqlL模块时不成功 ubutun上配置php环境 我已经安装好php5-mysql,并且已经在php.ini中加入了 但是phpinfo()中就不显示MySQL模块,mysql__connect 也不能用_ ___这到底什么情况啊,求大神 解决方案 并且已经加入了extension=mysql.so 解决方案二: 有装mysql-client mysql-server?

JSON.NET 加载json字符串时,如何自定义映射到某个属性?

问题描述 JSON.NET加载json字符串时,如何自定义映射到某个属性?例如,原来写JSON时属性叫"Name",现在叫"NewName",如何把旧的名称,映射到新的属性上?有没有类似IXmlSerializable的接口,可以自定义读写过程? 解决方案 解决方案二:staticclassProgram{classMy{[JsonProperty("Name")]//用JsonProperty特性publicstringNewName{get;

求助,如何自动加载打印机驱动

问题描述 各位大神,请问如何用AddPrinterDriver加载打印机驱动,网上下了些代码,稍做修改(如下),但执行到AddPrinterDriver时,就报"系统资源不足,无法完成请求的服务",琢磨了好久,也看不出所以然,故请大神指教以下在WIN10环境下运行,用VC6.0编译//Setup.cpp:Definestheentrypointfortheapplication.//#include"stdafx.h"//#include"Winspool

[JAVA100例]053、加载JDBC驱动

import java.sql.*; /** * <p>Title: JDBC连接数据库</p> * <p>Description: 本实例演示如何使用JDBC连接Oracle数据库,并演示添加数据和查询数据.</p> * <p>Copyright: Copyright (c) 2003</p> * <p>Filename: JDBCConn.java</p> * @version 1.0 */ public