内核模块加载异常分析

目录

问题背景

问题分析

vermagic 查看

查看符号的CRC

直接用ue修改

Unknown symbol osal_timer_init 

CONFIG_MODVERSIONS

自动修改的脚本

内核完全替换

总结


问题背景

   麒麟系统自带的内核为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。

总结

     实际工作中经常遇到厂商提供的内核版本源码与商用系统有些差异的情况,通过上述两种方法可以解决模块加载的问题。便于通过驱动跟踪处理问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

proware

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值