高版本linux的EXPORT_SYMBOL共享变量通过extern 导入编译出现 undefined 错误

出现此问题主要原因是在模块里用 EXPORT_SYMBOL ()导出变量 编译.ko文件后无法在linux源码根目录的 Module.symvers文件里
添加我们导出的变量

当然如果你的源码根目录连Module.symvers文件都没有你试一下如下:

  1. 先make一下 编译你的linux源码命令
    make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
  2. 再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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值