目录
Unknown symbol osal_timer_init
问题背景
麒麟系统自带的内核为5.4.18,而我们交叉编译的驱动环境为5.4.58。编译出的ko文件在麒麟系统上加载报错。
test: version magic '5.4.58 SMP preempt mod_unload aarch64' should be '5.4.18 SMP preempt mod_unload modversions aarch64'
即使强制加载,也会报如下错误
pid:8732,cpu4,modprobe]: module_layout: kernel tainted.
问题分析
vermagic 查看
需要先将模块拷贝到目录,然后depmod -a后,modinfo才能显示。
新的
modinfo /usr/lib/modules/5.4.58/kernel/drivers/soc_test.ko | grep vermagic
vermagic: 5.4.58+ SMP preempt mod_unload modversions aarch64
原先的是这个:
modinfo /usr/lib/modules/5.4.18/kernel/drivers/soc_test.ko | grep vermagic
vermagic: 5.4.18 SMP preempt mod_unload modversions aarch64
修改源码编译的makefile,将vermagic修改为一样,但加载会报错误:disagrees about version of symbol module_layout
查看符号的CRC
老的
modprobe --dump-modversions v2.1/test.ko |grep layout
0xf87bebdf module_layout
modprobe --dump-modversions soc_test.ko |grep layout
0xf87bebdf module_layout
我们编译出的KO。
modprobe --dump-modversions test.ko |grep layout
0x68bd8d0d module_layout

直接用ue修改
修改为和原始ko一致的值,如下:

Unknown symbol osal_timer_init
经过上述修改后,依然报错。看起来像是符号没有。其实符号村子,但是CRC校验不过。下面示例一个,实际有好多个,这样我们可以参考module_layout的例子一一修改。
[ 3744.392582] [2026:01:28 10:58:07][pid:35130,cpu5,insmod]test: disagrees about version of symbol osal_timer_init
[ 3744.392591] [pid:35130,cpu5,insmod]test: Unknown symbol osal_timer_init (err -22)
CONFIG_MODVERSIONS
但是符号太多了,我们就通过内核配置,不增加CRC的方式生成模块。将此配置项目去除后,的确没有CRC了,但加载依然报上面的错误。
kos/kernel/linux-5.4/arch/arm64/configs/pc/zhongxing/linux/pc_zhongxing_defconfig
自动修改的脚本
#!/bin/bash
# 检查参数数量
if [ $# -ne 3 ]; then
echo "用法: $0 <老ko文件> <新ko文件> <符号列表文件>"
echo "示例: $0 soc_test.ko_old soc_test.ko symbols.txt"
exit 1
fi
OLD_KO="$1"
NEW_KO="$2"
SYMBOL_FILE="$3"
# 检查文件是否存在
if [ ! -f "$OLD_KO" ]; then
echo "错误: 老ko文件 '$OLD_KO' 不存在"
exit 1
fi
if [ ! -f "$NEW_KO" ]; then
echo "错误: 新ko文件 '$NEW_KO' 不存在"
exit 1
fi
if [ ! -f "$SYMBOL_FILE" ]; then
echo "错误: 符号列表文件 '$SYMBOL_FILE' 不存在"
exit 1
fi
# 创建临时文件
OLD_VERSIONS=$(mktemp)
NEW_VERSIONS=$(mktemp)
echo "正在提取版本信息..."
# 提取老ko文件的版本信息
modprobe --dump-modversions "$OLD_KO" > "$OLD_VERSIONS"
if [ $? -ne 0 ]; then
echo "错误: 无法提取老ko文件的版本信息"
rm -f "$OLD_VERSIONS" "$NEW_VERSIONS"
exit 1
fi
# 提取新ko文件的版本信息
modprobe --dump-modversions "$NEW_KO" > "$NEW_VERSIONS"
if [ $? -ne 0 ]; then
echo "错误: 无法提取新ko文件的版本信息"
rm -f "$OLD_VERSIONS" "$NEW_VERSIONS"
exit 1
fi
echo "正在处理符号替换..."
# 创建新ko文件的备份
BACKUP_FILE="${NEW_KO}.backup"
cp "$NEW_KO" "$BACKUP_FILE"
echo "已创建备份文件: $BACKUP_FILE"
# 处理每个符号
while IFS= read -r SYMBOL || [ -n "$SYMBOL" ]; do
# 跳过空行
SYMBOL=$(echo "$SYMBOL" | tr -d '\r' | xargs)
if [ -z "$SYMBOL" ]; then
continue
fi
echo "处理符号: $SYMBOL"
# 从老ko文件获取符号的16进制值
OLD_HEX=$(grep -E "^\s*0x[0-9a-fA-F]{8}\s+$SYMBOL\s*$" "$OLD_VERSIONS" | awk '{print $1}')
# 从新ko文件获取符号的16进制值
NEW_HEX=$(grep -E "^\s*0x[0-9a-fA-F]{8}\s+$SYMBOL\s*$" "$NEW_VERSIONS" | awk '{print $1}')
if [ -z "$OLD_HEX" ]; then
echo "警告: 在老ko文件中未找到符号 '$SYMBOL'"
continue
fi
if [ -z "$NEW_HEX" ]; then
echo "警告: 在新ko文件中未找到符号 '$SYMBOL'"
continue
fi
echo " 老版本: $OLD_HEX -> 新版本: $NEW_HEX"
if [ "$OLD_HEX" = "$NEW_HEX" ]; then
echo " 版本相同,跳过"
continue
fi
# 将16进制值转换为二进制格式(小端序)
# 注意:这里假设是32位的CRC值(8个十六进制字符)
# 将16进制字符串转换为二进制字节(小端序)
# 例如:0x12345678 -> \x78\x56\x34\x12
OLD_BYTES=$(echo "$OLD_HEX" | sed 's/^0x//' |
awk '{for(i=7;i>0;i-=2) printf "\\x" substr($0,i,2)}')
NEW_BYTES=$(echo "$NEW_HEX" | sed 's/^0x//' |
awk '{for(i=7;i>0;i-=2) printf "\\x" substr($0,i,2)}')
# 使用sed进行二进制替换
# 注意:这里使用perl来处理二进制文件更可靠
if command -v perl >/dev/null 2>&1; then
# 使用perl进行二进制替换
perl -pi -e "s/$NEW_BYTES/$OLD_BYTES/g" "$NEW_KO"
if [ $? -eq 0 ]; then
echo " 替换成功"
else
echo " 替换失败"
fi
else
echo " 错误: 需要perl来执行二进制替换"
echo " 请安装perl或手动替换"
fi
done < "$SYMBOL_FILE"
# 清理临时文件
rm -f "$OLD_VERSIONS" "$NEW_VERSIONS"
echo ""
echo "处理完成!"
echo "原始文件已备份为: $BACKUP_FILE"
echo "修改后的文件: $NEW_KO"
运行示例
./crc.sh soc_test.ko_old soc_test.ko symbol.txt
其中symbol.txt中存放的是加载报错的符号,每隔符号占用一行。

有些模块,即使上述修改完全后,加载不报错,但是insmod 插入模块时,module_init不会被调用。那就只能用下面的终极方法了。
内核完全替换
另外一种方法是内核全部重新编译,替换,但是这种影响较大。
进入内核目录,执行配置命令
make ARCH=arm64 O=out CROSS_COMPILE=/mnt//tools/host//toolchains/aarch64-v100-linux/bin/aarch64-linux-gnu- pc/linux/tmp_defconfig
然后编译
make ARCH=arm64 O=out CROSS_COMPILE=/mnt/tools/host/toolchains/aarch64-v100-linux/bin/aarch64-linux-gnu- bindeb-pkg CFLAGS_MODULE+="-DSDK_VERSION=V100R001C00SPC036B010" -j8
编译后会生成libc和内核header以及内核镜像、KO的deb包。将这些包拷贝到目标机器上进行安装。
安装报这个错误: bin/sh: 1: scripts/basic/fixdep: Exec format error,影响不大,主要交叉编译这个工具是X86 编译出来的,而目标是arm。
总结
实际工作中经常遇到厂商提供的内核版本源码与商用系统有些差异的情况,通过上述两种方法可以解决模块加载的问题。便于通过驱动跟踪处理问题。

1万+

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



