出现此问题主要原因是在模块里用 EXPORT_SYMBOL ()导出变量 编译.ko文件后无法在linux源码根目录的 Module.symvers文件里
添加我们导出的变量
当然如果你的源码根目录连Module.symvers文件都没有你试一下如下:
- 先make一下 编译你的linux源码命令
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- - 再make一下linux源码里所有的模块命令:
make modules ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
如果有Module.symvers文件还是没办法解决 那再看看下面解决办法
操作之前 先备份linux源码根目录的 Module.symvers文件
1.手动方式
在linux源码根目录的 Module.symvers文件最后面写入我们的共享变量 如下:
假如我们要导出的变量 EXPORT_SYMBOL (my_fun)
那么我们在Module.symvers文件最后加入:
0x00000000 my_fun vmlinux EXPORT_SYMBOL
注意的是 你看到它们之间的间隔并非空格 而是 tab建 否则会出错
2.写一个.sh文件 在make完后运行.sh文件自动将EXPORT_SYMBOL的共享变量加入linux源码根目录的 Module.symvers文件里
<1>当前环境
交叉编译系统 ubuntu 16.04
linux源码为 linux-5.2
gcc编译器为 arm-linux-gnueabihf-
<2>新建一个名为 modules.sh 文件
写入如下:
#!/bin/bash
if [ "$1" = "ret" ]
then #这是make ret命令分支
sys_path="${2}/Module.symvers" #系统make modules生成的Module.symvers路径(里面的 $1 为获取.sh运行传递进来的第二参数(传递了linux源码根目录) 第一个为.sh的文件名)
if [ -f "$sys_path" ] #查看Module.symvers是否存在
then
echo ""
echo "---------------clean modules from system Module.symvers--------------"
echo ""
addr=0x00000000 #在Module.symvers里面所有 EXPORT_SYMBOL 的共享变量都是以这个值开头的
endStr="vmlinux EXPORT_SYMBOL" #在Module.symvers里面所有 EXPORT_SYMBOL 的共享变量都是以这个值结尾
addSys=""
sysMark=0
sysCount=$(wc -l < $sys_path) #获取系统Module.symvers文件总行数
IFS=$'\n' read -d '' -r -a array < $sys_path #读取文件 并且以 IFS=$'\n' 换行作为分割 并且将分割的每个值保存在数组里
for ((i=$sysCount;i>0;i--)); #主要作用是寻找我们的标志位所在倒数第几行
do
sysName=$(echo ${array[i]} | awk -F ' ' '{print $2}') #通过空格 -F ' ' 分割字符串 并且获取第二个值 $2 并且将echo出来的值赋值给sysName
if [ "$sysName" = "__________" ] #查看Module.symvers是否有我们的标志行
then
sysMark=1
break
fi
sysMarkCount=$((sysMarkCount+1))
if [ ${sysMarkCount} = 300 ] #超过300个有可能第一次用没有标志行 所以会一直搜索整个文件 提醒用户可能会等很久
then
echo "----this program is first time usering need some time to set init----"
fi
done
sysMarkCount=$((sysMarkCount-2)) #系统Module.symvers文件读到分割线会比实际多2行 只靠实践而来的
max=$((sysCount-sysMarkCount))
if [ ${sysMark} != 0 ] #证明有标记行 删除所有添加的自定义共享变量
then
for ((i=$sysCount;i>max;i--));
do
sed -i "${i}d" $sys_path #删除文件第 ${i} 行
done
else #如果没有标志位 就写一个进去
addSys="\n${addSys}${addr}\t__________\t${endStr}" #初始化分割线 这里 \t 为 tab建
echo -e $addSys >> $sys_path #将内容追加写进文件里
fi
echo "-----------------------------successfull-----------------------------"
echo ""
else
#意思是说 系统源码 Module.symvers 文件不存在 你可以先尝试运行: make modules ARCH=arm CROSS_COMPILE=armeb-linux-gnueabihf- 然后再尝试运行本程序
echo ""
echo "system code under find --> Module.symvers <-- file you many run:"
echo "make modules ARCH=arm CROSS_COMPILE=armeb-linux-gnueabihf- "
echo "then run this program"
echo ""
fi
else #这是make 或者make mod命令分支
sys_path="${1}/Module.symvers" #系统make modules生成的Module.symvers路径(里面的 $1 为获取.sh运行传递进来的第二参数(传递了linux源码根目录) 第一个为.sh的文件名)
file_path="Module.symvers" #当前编译模块生成的Module.symvers文件路径
addr=0x00000000 #在Module.symvers里面所有 EXPORT_SYMBOL 的共享变量都是以这个值开头胡
endStr="vmlinux EXPORT_SYMBOL" #在Module.symvers里面所有 EXPORT_SYMBOL 的共享变量都是以这个值结尾
addSys=""
addUsr=""
if [ -f "$file_path" ] #查看当前要编译的模块是否有生成Module.symvers文件 是否存在
then
if [ -s "$file_path" ] #查看当前要编译的模块是否有生成Module.symvers文件 是否为空
then
if [ -f "$sys_path" ] #查看Module.symvers是否有生成 没有就得去makefile运行make modules生成
then
echo ""
echo "-----------------add modules to system Module.symvers----------------"
echo ""
count=$(wc -l < $file_path) #获取文件总行数
arr=()
for i in $(seq 1 $count) #循环函数
do
line=$(cat $file_path | sed -n "$i"p) #读取第i行
name=$(echo $line | awk -F ' ' '{print $2}') #通过空格 -F ' ' 分割字符串 并且获取第二个值 $2 并且将echo出来的值赋值给strHead
arr+=($name)
done
sysCount=$(wc -l < $sys_path) #获取文件总行数
IFS=$'\n' read -d '' -r -a array < $sys_path #读取文件 并且以 IFS=$'\n' 换行作为分割 并且将分割的每个值保存在数组里
sysArr=()
sysMark=0
sysMarkCount=0
for ((i=$sysCount;i>0;i--));
do
sysName=$(echo ${array[i]} | awk -F ' ' '{print $2}') #通过空格 -F ' ' 分割字符串 并且获取第二个值 $2 并且将echo出来的值赋值给strHead
if [ "$sysName" = "__________" ] #查看标志行
then
sysMark=1
break
fi
sysArr+=($sysName)
sysMarkCount=$((sysMarkCount+1))
if [ ${sysMarkCount} = 300 ] #超过300个有可能第一次用没有标志行 所以会一直搜索整个文件 提醒用户可能会等很久
then
echo "----this program is first time usering need some time to set init----"
fi
done
sysMarkCount=$((sysMarkCount-2)) #系统Module.symvers文件读到分割线会比实际多2行 只靠实践而来的
if [ ${sysMark} = 0 ] #证明没有标记行 直接将我们的共享变量写进去
then #写入标记行
addSys="\n${addSys}${addr}\t__________\t${endStr}\n" #初始化分割线 这里 \t 为 tab建
for i in $(seq 0 $((count-1))) #循环函数
do
if [ $i != $((count-1)) ] #主要作用是最后放进去的共享变量不换行
then
addSys="${addSys}${addr}\t${arr[i]}\t${endStr}\n" #这里 \t 为 tab建
else
addSys="${addSys}${addr}\t${arr[i]}\t${endStr}" #这里 \t 为 tab建
fi
done
else
for i in $(seq 0 $((count-1))) #循环函数
do
mark=0
for j in $(seq 0 $((sysMarkCount-1))) #循环函数
do
if [ "${arr[i]}" = "${sysArr[j]}" ] #查看是否有重复共享变量
then
unset arr[$i] #删除数组项
mark=1
break
fi
done
if [ ${mark} = 0 ] #在arr[i]没有跟系统 Module.symvers文件 有重复的共享变量 将其保存进addSys变量用于后面写入系统Module.symvers文件
then
if [ $i != $((count-1)) ]
then
addSys="${addSys}${addr}\t${arr[i]}\t${endStr}\n" #这里 \t 为 tab建
else
addSys="${addSys}${addr}\t${arr[i]}\t${endStr}" #这里 \t 为 tab建
fi
fi
done
fi
if [ "${addSys}" != "" ] #查看是否为空 为空代表不用添加进文件里
then
echo -e $addSys >> $sys_path #将内容追加写进文件里
#else #到这里代表这次编译没有要添加到 系统Module.symvers 的共享变量
# echo "nothing need to add"
fi
echo "-----------------------------successfull-----------------------------"
echo ""
else
#意思是说 系统源码 Module.symvers 文件不存在 你可以先尝试运行: make modules ARCH=arm CROSS_COMPILE=armeb-linux-gnueabihf- 然后再尝试运行本程序
echo ""
echo "system code under find --> Module.symvers <-- file you many run:"
echo "make modules ARCH=arm CROSS_COMPILE=armeb-linux-gnueabihf- "
echo "then run this program"
echo ""
fi
else
#echo "当前模块 Module.symvers 文件为空"
echo "this Module.symvers is empty"
fi
else
#意思是先make完之后再运行 make mod 才能将当前的共享变量添加进系统Module.symvers文件
echo "please run make then run make mod"
fi
fi
将此文件放到linux源码根目录下
<2>新建一个名为 Makefile 文件
写入如下:
obj-m += pfdevice.o #本次编译模块c文件名与编译完成后的.ko名
KERN_DIR = /home/qemu/linux-5.2 #本次编译模块linux源码的根目录
CR = arm-linux-gnueabihf- #本次编译模块所用gcc
AR = arm #本次编译架构
#---------------------------------------------------------------------#
ccflags-y += -I$(CURDIR) #添加头文件路径 $(CURDIR)为makefile所在路径(系统默认变量)
LL = ARCH=$(AR) CROSS_COMPILE=$(CR)
IN := -I$(CURDIR)
all:
make $(LL) -C $(KERN_DIR) M=`pwd` modules
@$(KERN_DIR)/modules.sh $(KERN_DIR)
clean:
make $(LL) -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order
mod: #将共享变量添加进系统的Module.symvers文件里
@$(KERN_DIR)/modules.sh $(KERN_DIR)
ret: #清空一下历史用户添加进系统的Module.symvers里的模块共享变量 ret为运行modules.sh输入的参数
@$(KERN_DIR)/modules.sh ret $(KERN_DIR)
#---------------------------------------------------------------------#
上面Makefile要注意修改第一条分割线上的内容为你实际的:
1.修改你项目实际的c文件名
2.修改linux源码根目录地址
3.修改gcc为你所用的编译器
4.修改编译架构
将此 Makefile文件放到要编译成.ko带有EXPORT_SYMBOL (my_fun) 共享变量的 项目文件夹中(当然其他无共享变量的项目也可以用 里面自带过滤掉没共享变量不运行modules.sh里的写入linux源码Module.symvers文件的代码)
然后运行命令:
make
这个命令会编译用户模块源码 然后根据编译.ko文件夹下的Module.symvers文件 将共享变量写进 linux源码的Module.symvers文件
最后说明一下modules.sh干了什么:
1.运行modules.sh后会根据输入进来的linux源码路径找到Module.symvers文件
2.第一次运行modules.sh会搜索Module.symvers文件是否有我们的标志行 第一次运行会很慢 因为要搜索整个文件几千行 搜索完如果没有标志行就添加
标志行 搜索标志行是由最后一行开始搜索的 有了标志行后以后编译就会很快了 加入进Module.symvers文件的标志行如下:
0x00000000 __________ vmlinux EXPORT_SYMBOL
3.运行modules.sh 添加EXPORT_SYMBOL (my_fun) 共享变量到linux源码Module.symvers文件 命令在Makefile里
可以用命令:
make
这个命令会编译用户模块源码 然后根据编译.ko文件夹下的Module.symvers文件 将共享变量写进 linux源码的Module.symvers文件
或者
make mod
这个命里只会将 共享变量写进 linux源码的Module.symvers文件里 不会对源码进行编译
4.modules.sh每个项目的共享变量都会加入进源码Module.symvers文件 同名的变量会被过滤掉 这样如果很多项目 都加入共享变量会导致程序搜索过多变慢
可以清除一下之前写入的自定变量 注意 会全部清除 也就是清除所有在 linux源码的Module.symvers文件里我们写入的标志位
0x00000000 __________ vmlinux EXPORT_SYMBOL 之后的所有自定义的共享变量
命令如下:
make ret