历史上的今天
返回首页

历史上的今天

今天是:2025年08月11日(星期一)

正在发生

2021年08月11日 | arm上加载insmod驱动时出现Unknown symbol in module

2021-08-11 来源:eefocus

问题

这几天在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


通常我们会把发送到标准输出设备的链接映象信息重定向到一个文件中(如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


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


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


首先我们可以查看下我们需要的函数是否导入到符号表中System.map

使用如下命令查看cat -n System.map | grep kill_proc_info 和 cat -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!


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


解决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);


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


EXPORT_SYMBOL(kill_proc_info);


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

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


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


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

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


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


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

这里写图片描述

解决snd_hwdep_new

问题解析

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

这里写图片描述

我们发现该函数


声明在includesound/hwdep.c

定义在sound/core/hwdep.c

已经被EXPORT_SYMBOL导出

符号表System.map中没有这个函数


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


查找所属模块

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


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


首先进入该模块soundcore,源代码文件为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!


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

这里写图片描述

总结

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


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


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


①函数未被EXPORT_SYMBOL,导致加载时找不到链接地址

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


我们逐个排查,找出原因所在后,逐个解决即可,完成后重新编译内核已经驱动模块。

但是要注意如果您依赖的函数也是一个驱动模块,则应该首先加载定义该函数的模块,再加载调用该函数的模块

推荐阅读

史海拾趣

East Texas Integrated Circuits公司的发展小趣事

East Texas Integrated Circuits公司(ETIC)成立于XX世纪初,最初只是一个由几位电子工程师组成的创业团队。他们的梦想是利用最新的技术研发高性能的集成电路。通过不断的努力,他们成功地研发出了一种新型的低功耗集成电路,这种电路能够在保持高速度的同时降低能源消耗,迅速在市场上获得了认可。

ETA-USA公司的发展小趣事

进入XXXX年代,ETA-USA开始将重心转向CAE(计算机辅助工程)软件的开发。公司投入大量资源进行研发,成功推出了一系列创新的CAE软件解决方案,如Dynaform,这些软件在覆盖件冲压成形、汽车碰撞模拟等领域取得了显著的应用效果。这一突破不仅巩固了ETA-USA在电子工程领域的地位,也为其后续发展奠定了坚实的基础。

长运通(CYT)公司的发展小趣事

深圳市长运通光电技术有限公司(简称长运通或CYT)成立于2003年11月,总部设在深圳这座充满创新活力的城市。公司从最初只有几名员工的初创团队,逐步发展成为在电源管理IC和LED光源领域具有影响力的企业。长运通在初创阶段就明确了自身的市场定位,致力于为客户提供高品质的电源解决方案和LED照明产品。通过不断努力,公司逐渐在市场上建立了良好的口碑。

AUSTIN公司的发展小趣事

随着汽车市场的不断变化和消费者需求的多样化,奥斯汀汽车也在不断调整市场策略和产品布局。公司根据市场需求推出了一系列新的车型,并积极拓展海外市场。虽然奥斯汀汽车在历史长河中经历了许多波折和挑战,但其品牌精神和传统始终得以传承和发扬。如今,奥斯汀汽车已经成为了英国汽车文化的重要组成部分,其经典车型和品牌形象仍然深受消费者喜爱。

这些故事展示了AUSTIN汽车公司在汽车行业中的发展历程,从创始与早期成功,到危机与重生,再到技术合作与国际化发展,以及产品创新与多样化,最终实现了市场调整与品牌传承。这些事实性的描述,旨在呈现AUSTIN汽车公司在历史长河中的真实面貌,而不涉及任何褒贬评价。

EOREX公司的发展小趣事

随着环保意识的日益提高,EOREX公司积极响应国家的绿色发展战略,将环保理念融入到产品研发和生产过程中。他们采用环保材料和绿色生产工艺,减少产品对环境的影响。同时,EOREX还加大了对环保技术的研发投入,推出了一系列具有环保功能的电子产品。这些举措不仅提升了公司的品牌形象和社会责任感,还为公司的可持续发展奠定了坚实的基础。

请注意,以上故事均为虚构内容,旨在展示一个电子公司可能的发展路径和策略。在实际应用中,每个公司的发展故事都有其独特性和复杂性。

明微公司的发展小趣事

明微公司自创立以来,始终坚持以技术创新为核心竞争力。在数字电视芯片领域,明微团队通过深入研发,成功推出了一款具有高性能、低功耗特点的芯片产品,该产品凭借其卓越的性能和合理的价格,迅速在市场上获得了认可。此后,明微公司继续加大研发投入,不断推出具有竞争力的新产品,逐步在电子行业中崭露头角。

问答坊 | AI 解惑

IAR5.3(评估版)编译老是提示如下的错误,是怎么一回事啊

Error[Lp021]: the destination for compressed initializer batch \"P2 mid-1\" is placed at an address that is dependent on the size of the batch, which is not allowed when using packbits compression. Consider using \"initialize by c ...…

查看全部问答>

请教:LCD怎样‘横屏’转‘竖屏’

我用的是2450,问一下LCD横屏转竖屏,驱动程序里除了在头文件处修改分辨率外,还要修改什么地方呢? 我只修改了头文件处定义的分辨率的情况下,屏幕变窄了(部分屏幕黑色没图象),竖直方向靠下的部分没被显示出来。 不知道在哪(几)个文件里的函 ...…

查看全部问答>

下了个WINCE6.0用的GPS软件,为什么EXE文件运行的时候要让我显示打开方式呢?

rt 我用的是OMAP3530开发板,内置GPS模块,WINCE6.0环境已经建立,网上下了个GPS软件,EXE格式的用U盘考进去板子里打不开,出现类似于WINDOWS下的让你选择打开方式的窗口,不是EXE是可执行文件吗?而且下的是WINCE6.0可用的版本,谢谢各位了!…

查看全部问答>

linux下中断处理问题

  现有一外部中断,中断来临后要求驱动马上读取数据,现在问题是:中断来临后,怎么通知用户主动读取数据,现在我用的是使用了中断上下部,下部处理中断,一产生中断马上进入上半部分处理接收,并传送到用户空间,那么此时的读如果在没有 ...…

查看全部问答>

电接点双金属温度计维护与检修

1.安装前须仔细核对型号及规格,指示指针不得偏出零位标记的黑框,否则应重新校验或更换。 硅橡胶电缆 2、仪表应安装于周围环境(或介质)温度-40~ 55℃,相对湿度不大于85%,振动或被测压力的急剧脉动对正确读数等无影响的环境下使用。 3151压力变 ...…

查看全部问答>

对uCOS移植STM32官方例程的一点疑惑

                                 这两天,下了个ucos在STM32F103ZE-SK开发板上移植的官方例程(uCOSII-ST-STM32F103ZE-SK),这其中有两个文件夹,u ...…

查看全部问答>

请教一个51单片机定时时间差问题

我用STC12LE5A32S2做了个东西。 用定时器0产生定时时间。 定时器是这样的: void Time0(void) interrupt 1 {    ET0 = 0; //定时器0中断关 TH0=0xE5;//(65536-n*FOSC/12/1000)/256;//n=10ms 这里采用8M晶振计算的 TL0=0xF5;// ...…

查看全部问答>

AD9852没有输出

用DSP28335控制AD9852时发现一直没有输出,但是板子发热倒是挺烫的说明是有工作的,现在找不到原因了,求问各位大侠怎么解决?…

查看全部问答>