【Linux】【开发】从一个编译问题的处理来看各种二进制工具(gcc -E、readelf等)的使用

  • 🐚作者简介:花神庙码农(专注于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等使用方法及场景。

如本文对你有些许帮助,欢迎大佬支持我一下(点赞+收藏+关注、关注公众号等),您的支持是我持续创作的竭动力
支持我的方式

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花神庙码农

你的鼓励是我码字的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值