- 🐚作者简介:花神庙码农(专注于Linux、WLAN、TCP/IP、Python等技术方向)
- 🐳博客主页:花神庙码农 ,地址:https://blog.csdn.net/qxhgd
- 🌐系列专栏:Linux技术
- 📰如觉得博主文章写的不错或对你有所帮助的话,还望大家三连支持一下呀!!! 👉关注✨、点赞👍、收藏📂、评论。
- 如需转载请参考转载须知!!
问题描述
- 在做某个产品的时候,加载cfg80211.ko的时候,报下面错误:
root@qxhgd # insmod cfg80211.ko
[ 104.387530] cfg80211: Unknown symbol rfkill_unregister (err -2)
- rfkill是Linux内核提供的一个框架,用于控制无线通信硬件(如 WiFi、蓝牙、NFC 等)的开关和状态。rfkill就是RF(射频) 设备的开关,有类似一键关闭所有射频外设的功能。rfkill的出现方便管理各种RF芯片的开关, 目前已经很多厂商的设备使用的是rfkill的驱动来管理一些无线设备的电源了,都是和RF相关的芯片,比如WiFi,蓝牙, NFC, FM,,GPS等等。
- 但是对比其他使用相同版本backports的产品,一切正常,并没有报这个错误。
- 接下来准备从两个产品的差异入手来突破这个问题。
问题分析
代码层面的分析
- cfg80211模块的core.c文件中,wiphy_unregister函数调用了rfkill_unregister函数:
void wiphy_unregister(struct wiphy *wiphy)
{
...
if (rdev->wiphy.rfkill)
rfkill_unregister(rdev->wiphy.rfkill);
...
}
- 开始怀疑,两个产品开的宏可能不同。但调用rfkill_unregister的部分及wiphy_unregister函数外并没有任何宏控。
查看符号表
- 查看符号表,发现有问题的产品,调用了rfkill_unregister函数。而以前正常的产品,并没有调用此函数。
readelf -a core.o | grep rfkill_unregister
readelf -a cfg80211.ko | grep rfkill_unregister
- 为了确认,笔者又通过增加编译错误进一步确认,两个产品都实实在在的会调用rfkill_unregister函数。这个就比较诡异了。此时,甚至怀疑rfkill_unregister可能是个宏了。
查看预处理的结果
找到core.o的编译命令
-.o.cmd是Linux内核编译的副产品(.o、.ko都有对应的.cmd文件), 关于.o.cmd的说明,可参考ols2003-pages-185-200.pdf,
- 从core.o.cmd找到core.c的编译命令(这里截取一段):
cmd_xxx_core.o := aarch64-sanechips-linux-gnu-gcc -c -o core.o core.c
- .o.cmd中也有包含头文件信息的,有些类似问题也可以从这里突破。
查看core.c的预处理数据
- gcc -E查看预处理的结果,以下两种命令都可以(从.o.cmd中提取修改,这里是缩写的):
aarch64-sanechips-linux-gnu-gcc -E core.c > core.txt
aarch64-sanechips-linux-gnu-gcc -E core.c -o core.txt
- 通过查看core.txt可知,正常产品的core.c所调用的rfkill_unregister函数,是一个内联的空函数,而本次所做的产品却不是内联函数:
static inline __attribute__((__gnu_inline__)) __attribute__((__unused__)) __attribute__((__no_instrument_function__)) void rfkill_unregister(struct rfkill *rfkill)
{
}
-在 【Linux】【开发】dump_stack导栈信息不准确问题浅析
一文中,提到了内联函数会导致导栈信息不准确的问题,和本文ko文件中没有rfkill_unregister符号的问题同出一辙。
查看内核头文件
- 查看include/linux/rfkill.h文件,终于发现端倪了。两个产品的内核版本不一样。
- 5.4.55内核(有问题的产品):
#if defined(CONFIG_RFKILL) || defined(CONFIG_RFKILL_MODULE)
#else
static inline void rfkill_unregister(struct rfkill *rfkill)
{
}
#endif
- 5.4.196内核(正常的产品):
#if defined(CONFIG_RFKILL_FULL) || defined(CONFIG_RFKILL_FULL_MODULE)
#else
static inline void rfkill_unregister(struct rfkill *rfkill)
{
}
- 这两个产品开的都是CONFIG_RFKILL这个宏。由于宏控的不同,导致有问题产品,使用的是上面分支(使用net/rfkill模块,但是该模块未编译),而正常产品,走的下面分支(空函数)。
问题解决
- 找到问题后,解决比较简单了。可以将5.4.55内核中的CONFIG_RFKILL宏控替换为CONFIG_RFKILL_FULL这个宏(参考5.4.196内核)或者直接关闭CONFIG_RFKILL这个宏。
问题小结
- 通过这个问题,可以看到gcc -E、readelf、.o.cmd等使用方法及场景。
如本文对你有些许帮助,欢迎大佬支持我一下(点赞+收藏+关注、关注公众号等),您的支持是我持续创作的竭动力
支持我的方式
1374

被折叠的 条评论
为什么被折叠?



