-
2022-03-08 21:57:24
编译内核模块
$ make M=/home/liyongjun/project/c/linux-4.4.1/net/mac80211/ modules
安装报错
$ sudo insmod net/mac80211/mac80211.ko insmod: ERROR: could not insert module net/mac80211/mac80211.ko: Invalid module format
dmesg
$ dmesg ... [ 3187.509374] mac80211: no symbol version for module_layout
解决办法
添加 -C /lib/modules/`uname -r`/build 选项
$ make -C /lib/modules/`uname -r`/build M=/home/liyongjun/project/c/linux-4.4.1/net/mac80211/ modules
更多相关内容 -
insmod过程.pdf
2021-10-22 23:11:57insmod过程pdf--莫搬砖员 -
insmod过程
2021-10-22 22:32:26insmod过程 模块加载与卸载 众所周知,Linux下模块加载可以用insmod命令来完成,卸载模块通过rmmod命令来完成,比如下面,非常简单: 那么第一个问题来了,模块是什么? 模块文件 那么接下来,我们就可以通过file...insmod过程
模块加载与卸载
众所周知,Linux下模块加载可以用
insmod
命令来完成,卸载模块通过rmmod
命令来完成,比如下面,非常简单:那么第一个问题来了,模块是什么?
模块文件
那么接下来,我们就可以通过
file
命令来查看一下这个.ko
文件到底是何方神圣,可以看到,此ko
文件是一个ELF 32-bit LSB relocatable
文件,ELF
格式的,32-bit
的,LSB
小端,relocatable
可重定位文件,此文件未stripped
。可以看到,
file
命令非常好用,直接就知道了ko
模块文件是ELF relocatable
文件。话说回来,编译过程中产生的
.o
文件也是ELF relocatable
文件,两个看起来并没有太大的差异。所以模块文件为什么以
.ko
结尾,就是因为模块文件实际是一个和.o
文件差不多的中间文件,但又加入了内核kernel
特有的一些section
,所以特此命名为.ko
。(那么生成.ko
的过程,可以参考一下我的另一篇==《模块生成过程》==)话又说回来,
ELF
文件是什么文件?ELF文件
推荐大家去看
ELF.pdf
这个文档,我这里就选取部分可能用到的放这里。首先,我们看一下这个
ELF
的全称是什么:Executable and Linkable Format
,可执行,可链接格式。那么在模块文件中,这个文件的格式就是可链接格式;对于普通的可执行文件,就是可执行格式,比如下面这个
busybox
:在
ELF
文档中提到,Object File Format
有两个视角,一个是链接过程的视角,一个是执行过程的视角。目标文件在链接时,就是左边的链接视角;可执行程序执行时,是右边的执行视角。链接时需要
Section header table
,执行时则需要Program header table
。我们可以通过
arm-linux-gnueabi-readelf -S
命令来看一下链接过程的视角,即看一下这些section
(在==《模块生成过程》==中有提到,.ko
只是增加了一些section
,比如gnu.linkonce.this_module
,.modinfo
等):
但执行过程的视角,由于都在内存中,目前就暂时不管了。
那么接下来就是看一下代码的过程。
busybox的insmod过程
我们这里还是用的
busybox
做的根文件系统,insmod
命令也是由busybox
提供的,那么就直接在busybox
里面找代码:调用bb_init_module()
。bb_init_module()
也比较简单,目前内核的版本是linux-4.0
,不走下面的bb_init_module_24()
,且定义了系统调用finit_module()
,成果后直接返回。如果失败了,还有后面的init_module()
系统调用,这里还需要先将模块文件映射到内存上,结束后再取消映射整个模块文件。
接下来就是要看内核系统调用的时候了。
内核系统调用
当前用到系统调用是
finit_module()
,系统调用里传递的fd
是在busybox
打开的,这里通过copy_module_from_fd()
将打开的模块文件读到申请的内核内存中,并交由load_module()
处理。copy_module_from_fd()
这里只需要知道,load_info
结构体的hdr
成员是指向内核申请的内存,即整个模块文件的内存起始地址,len
则是这个模块文件的长度。这里我们用
qemu+gdb
调试一下,并在load_module()
打上断点(关于qemu和gdb网上的教程比较多去参考):# 网桥配置 -- 仅供参考 sudo brctl addbr br0 sudo ifconfig enp0s6 down sudo brctl addif br0 enp0s6 sudo brctl stp br0 off sudo ifconfig br0 10.37.129.17 netmask 255.255.255.0 promisc up sudo ifconfig enp0s6 10.37.129.10 netmask 255.255.255.0 promisc up sudo tunctl -t tap0 sudo ifconfig tap0 10.37.129.18 netmask 255.255.255.0 promisc up sudo brctl addif br0 tap0 # 起 qemu -- 仅供参考 sudo qemu-system-arm -M vexpress-a9 -m 512M -kernel arch/arm/boot/zImage -nographic -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -net nic -net tap,ifname=tap0,script=no -append "init=/linuxrc root=/dev/nfs rw nfsroot=10.37.129.17:/home/mj/work/rootfs/,proto=tcp,nolock ip=10.37.129.20:10.37.129.17:10.37.129.1:255.255.255.0::eth0:off console=ttyAMA0,38400n" -S -s # 起 gdb arm-linux-gnueabi-gdb vmlinux # 连接设备 target remote localhost:1234 # 打断点 b load_module
此时执行模块加载,
gdb
那边就触发了断点:可以看到大小一致:
且。内存上的与编译出来的
.ko
一模一样:Part1 模型信息校验与模块重新布局
module_sig_check()
需要开启CONFIG_MODULE_SIG
才会起作用,这里忽略。elf_header_check()
主要是检查一下ELF
文件头信息,别忘了模块文件.ko
是ELF relocatable
文件,所以需要检查一下是否是正确的ELF
文件格式。layout_and_allocate()
获取模块的布局,重新申请一次内存,并对相关section
对内容进行修改。add_unformed_module()
将模块加入到内核的模块链表中。Part1.1 ELF头校验
elf_header_check()
是根据规范来校验整个ELF文件头的信息,比如ELF Header
的Magic
,必须是'0x7f' , 'E', 'L', 'F'
;ELF 的文件类型必须是ET_REL
(模块文件是可重定位文件,不必多说了吧);不同架构芯片的编译器编译时会把e_machine
设置为对应的类型,这里是EM_ARM
。内存上的数据如下所示:
可以参观一下一个标准的
ELF Header
的内容:这里的
e_type
必须是ET_REL
可重定位类型,就不用多说了。Part1.2 模块内存的申请与layout
看章节小标题就大概可以知道,
layout_and_allocate()
函数所做的工作有:1,记录模块信息与重写模块数据;2,检查模块license;3,取消percpu section
的内存申请标记,后续特殊处理;4,统计和预留需要layout数据空间;5,统计和预留模块符号的空间;6,申请内存并拷贝模块数据到layout
;7,将记录的模块信息指向layout
的内存。Part1.2.1 记录模块信息与重写模块数据
setup_load_info()
记录节头表位置,再根据节头表找到节名字字符串表在内存中位置。然后遍历所有的节,保证相关节的偏移+长度不超过整个ELF文件的大小,把节的地址设在为当前内核内存下的地址。并且要找到模块特有的节.gnu.linkonce.this_module
,表明是模块的信息。并记录当前模块的.data..percpu
节,方便后续对每CPU变特殊申请处理。
rewrite_section_headers()
函数将内存里的每一个section
的sh_addr
节地址记录为当前内核内存上的地址,并找到__versions
节和.modinfo
节,取消这两个节的内存申请标记:显而易见,经过
rewrite_section_headers()
之后,内存上的数据肯定发生了变化,用通用的方法,导出内存上的数据:dump memory hello.ko-rewirte-section.dump 0xa0ab0000 0xa0ab0000+31476
比较一波,除去第0个
section
,后面每一个section
的sh_addr
已经被修改为当前内存的地址:回头看
find_sec()
也比较简单,因为前面在setup_load_info()
中已经获取到每个section
到描述头以及section
到名字字符串表的位置,那么遍历一下所有section
,比对名字即可:查找当前模块的
.data..percpu
节也比较简单:Part1.2.2 检查模块license
check_modinfo()
主要是检查.modinfo
节里面的一些包括license
的信息,并记录此模块是否存在污染内核的可能:Part1.2.3 统计和预留需要layout数据空间
layout_sections()
函数开头的注释可以得知,这里其实是为后续执行类似ld
的操作做准备,计算代码、只读数据、小数据的大小以及总的大小。对于部分存在架构额外内存的或者地址对齐限制的,在get_offset()
函数中体现。这里主要是分两个部分,一开始先统计模块的core_layout
部分,然后再统计init_layout
部分的数据。这里的masks
也注释得很清楚,0
是AX
可执行的非小数据,1
是A
只需申请内存的不可写的非小数据,3
是小数据申请内存的。
Part1.2.4 统计和预留模块符号的空间
layout_symtab()
这里一开始在init layout
里面预留空间,用于存放模块的符号表。紧接着遍历模块的符号,找出哪些属于内核的符号,然后在core layout
中预留出内核符号的符号表和字符串表空间。最后在init layout
后面预留模块字符串表的空间。判断是否是内核的符号,可以根据符号的
st_shndx
是否是为定义的SHN_UNDEF
,未定义的符号肯定不是内核符号;符号的名字是否存在等:Part1.2.5 申请内存并拷贝模块数据到layout
move_module()
主要的工作就是把模块信息移到新申请的可执行的内存中去。layout_sections()
和layout_symtab()
统计了要申请的内存大小,设定了布局,所以在move_module()
中申请完内存后,就需要将相关的数据拷贝到对应的布局中,数据来源自然是前面经过修正后的模块信息,修改完后,还需要再次修正每个section
在layout
内存中的位置。
申请内存的过程调用的是
vmalloc_exec()
申请虚拟连续的可执行内存,并更新模块地址的边界信息:这里本来想
dump
出当前layout
的内存数据的,但由于内核是-O2
编译的,局部变量已经被优化了,所以暂时没有办法dump
出内存数据。在Part2.7部分的
apply_relocate()
断点时,发现mod
参数未被优化,那么就可以直接看一下在apply_relocate()
时的struct module
内存数据,但会经历Part2.6部分的simplify_symbols()
修改符号地址的。这里稍微看一下就行了,后续如果再研究这个内存的布局的话,可以再细看一下:
Part1.3 将模块加入到内核的模块记录链表中
add_unformed_module()
主要就是将模块加入到内核的模块链表中,保证模块唯一。Part2 模块的符号处理与重定位
percpu_modalloc()
对模块里面的percpu
进行特殊的内存申请,这个percpu
变量比较特殊,在每个CPU上都存在一份,所以用特殊的申请内存函数去处理。module_unload_init()
为后续执行rmmod
卸载命令时初始化一些引用变量以及链表等。find_module_sections()
找模块里面的一些section
,比如参数的__param
节,内核符号表__ksymtab
节,GPL
范围协议的符号__ksymtab_gpl
节,ftrace
增加的_ftrace_events
节等待。check_module_license_and_versions()
主要是检查模块的license
是否存在污染内核的可能。setup_modinfo()
是对sys
属性进行一些预先setup
处理。simplify_symbols()
要修复加载模块的符号,使符号指向内核正确的运行地址。apply_relocations()
则是对.rel
节和.rela
节进行重定位的一个过程。post_relocation()
对重定位后的percpu
变量重新赋值,并将即将加载完成的模块的符号加入到内核模块的符号链表中,如果成功加载此模块且内核配置了CONFIG_KALLSYMS
,那么在/proc/kallsyms
下可以看到此模块的符号(我另有《kallsyms 内核符号表》可以稍微瞄一眼)。之后flush_module_icache()
执行刷新模块的init_layout
和core_layout
的cache。Part2.1 申请percpu内存
percpu_modalloc()
这里重新为percpu section
的数据申请内存,是补上Part1部分的去向了percpu
的SHF_ALLOC
:Part2.2 为后续模块卸载做准备
module_unload_init()
这里主要是初始化了一个原子变量与两个链表,链表分别用于存放哪些模块依赖于我以及我依赖于哪些模块的信息,主要是卸载掉时候会用。
Part2.3 找模块的参数、内核符号等节
find_module_sections()
这里是找到模块的一些节的地址,比如模块参数__param
,内核符号__ksymtab
,内核GPL符号__ksymtab_gpl
等:找节地址的函数如下,也比较简单,先获取节的序号,之后再根据序号获取地址:
Part2.4 再次检查GPL协议?
这里暂时没有看懂为什么。
Part2.5 模块sys参数setup
setup_modinfo()
遍历modinfo_attrs
数组里面的属性,如果有setup
回调,尝试从模块的.modinfo
中获取相关的setup
参数:好像也没有看到相关的参数出现,所以就此作罢。
Part2.6 模块符号处理
simplify_symbols()
是对模块的符号进行处理,模块节的获取在前面已经相当的多了,这里就不赘述了。对于SHN_COMMON
符号,可能是模块内部自己定义的一些普通符号,可以不做处理;SHN_ABS
绝对地址,不需要做处理;而SHN_UNDEF
为定义符号,则需要在当前内核以及已经加载的模块中找到,否则模块加载失败(链接的过程需要解决为定义的符号,否则链接失败;当然弱符号另说);如果有一些类似于软连接的符号或者perpcu
的符号,要再进行处理。先看一下ELF的符号定义,可以看到代表符号的信息都是固定大小的:
resolve_symbol_wait()
这里是一个等待超时的过程,超时时间最大是30s,这里还用了一个逗号表达式,差点没认出来。最主要的函数还是resolve_symbol()
查找符号的过程。resolve_symbol()
主要是依赖与find_symbol()
查找符号,找到符号对检查相关信息并记录引用。find_symbol()
通过遍历记录内核符号的地址以及已经加载模块的地址,尝试找到此符号:each_symbol_section()
则是遍历内核与已加载模块的过程:each_symbol_in_section()
则是遍历具体地址调用回调的过程:查找与比较函数是
find_symbol_in_section()
,用的是二分查找,因为编译时的符号已经是排序过的了:Part2.7 节数据的重定位
apply_relocations()
主要是对SHT_REL
和SHT_RELA
类型的节进行重定位操作,通过sh_info
获取到附加的重定位节数据,再根据要重定位的节的类型去调用具体的重定位函数:sh_info
类型,上面用到下面的SHT_REL
和SHT_RELA
。关于这两个的详情可以看elf
的文档,我这里的理解是,SHT_REL
是表明需要重定位,SHT_RELA
是需要重定位且附带一个额外的地址偏移。先看一下这里
Elf32_Rel
类型和Elf32_Rela
类型,apply_relocate()
是对应架构下的重定位函数,主要是根据架构提供的aaelf32.pdf
文档来对具体符号进行重定位,类型也比较多,暂时就不细看了:
比如打印第一个需要重定位对符号,
r_info
对值是0x162b
,低8bit
的值是0x2b
,即43
,根据类型定义可以知道是#define R_ARM_MOVW_ABS_NC 43
:再去查看
aaelf32.pdf
文档,可以看到这个就是arm
规定的重定位类型怎么算:Part2.8 结构相关的模块初始化
前面
find_module_sections()
里面有找__ex_table
节,然后赋给mod->extable
,在post_relocation()
里面就对里面的数据进行排序,由于当前没有这个mod->extable
成员,就略过了。这里主要是拷贝了percpu
数据以及拷贝符号到core_layout
中。add_kallsyms()
这里对符号进行一个字符的字符赋值,更直接地表明符号的类型;然后拷贝符号数据到core_layout
中。elf_type()
判断符号的类型,赋值给一个字符值:最后是架构相关的初始化,主要是一个回溯信息的增加以及SMP初始化节等:
Part2.9 刷新layout的icache
flush_module_icache()
刷新core_layout
和init_layout
的icache
。Part3 模块init执行
strndup_user()
复制一下模块的参数;dynamic_debug_setup()
需要开启内核CONFIG_DYNAMIC_DEBUG
才会启用,这里先略过;ftrace_module_init()
也是需要开启相关的ftrace
配置,略过。complete_formation()
对模块的内存属性进行修改,比如.text
的读+执行,.data
的读写属性。parse_args()
是解析模块参数,mod_sysfs_setup()
则是对sys
进行创建的过程。free_copy()
释放最初内核申请的用于保存模块原文件信息的内存。trace_module_load()
也是trace
相关的,略过。do_init_module()
就是最后模块执行init
函数的过程了。Part3.1 模块符号重名确认、内存属性设置、模块上线通知
complete_formation()
主要是最后的模块符号重名确认、内存属性设置、以及通知链通知一下模块上线了。verify_export_symbols()
确认一下模块的符号是否会和内核的符号或者已经加载的模块的符号重名。查找符号的函数看Part2.6的find_symbol()
。Part3.2 模块init的执行
do_init_module()
主要是模块init的执行,然后通知链通知一下模块在线了,前面是即将上线,这里是已经上线了。
do_one_initcall()
这里就是调用模块的初始化函数了,到这里模块加载基本已经完成了。Part4 错误处理
至于最后的这里,都是上面出错时的异常处理,就不多看了。
-
insmod命令加载驱动详解
2022-04-24 23:08:431、insmod命令介绍 (1)insmod命令是busybox中支持的命令之一,insmod命令的源码也是在busybox中; (2)insmod命令是常用的加载驱动的命令,用法:insmod xxx.ko [xxx];xxx.ko是表示要加载的驱动文件名字,[xxx]是...1、insmod命令介绍
(1)insmod命令是busybox中支持的命令之一,insmod命令的源码也是在busybox中;
(2)insmod命令是常用的加载驱动的命令,用法:insmod xxx.ko [xxx];xxx.ko是表示要加载的驱动文件名字,[xxx]是代表给驱动的传参;2、busybox中insmod命令源码分析
2.1、insmod命令函数调用关系
//insmod_main()函数在modutils/insmod.c insmod_main() parse_cmdline_module_options() //解析insmod命令加载驱动时给驱动的传参 bb_init_module() try_to_mmap_module() //将驱动文件映射到内存中 init_module() //真正的开始加载驱动文件
2.2、bb_init_module函数源码
//filename:ko文件的路径; options:解析出来的加载驱动的传参 int FAST_FUNC bb_init_module(const char *filename, const char *options) { size_t image_size; char *image; int rc; bool mmaped; if (!options) options = ""; image_size = INT_MAX - 4095; //文件的最大长度 mmaped = 0; image = try_to_mmap_module(filename, &image_size); //将驱动文件映射到内存中 if (image) { mmaped = 1; } else { errno = ENOMEM; /* may be changed by e.g. open errors below */ image = xmalloc_open_zipped_read_close(filename, &image_size); if (!image) return -errno; } errno = 0; init_module(image, image_size, options); //调用系统函数sys_init_module()加载驱动 rc = errno; if (mmaped) munmap(image, image_size); else free(image); return rc; }
(1)try_to_mmap_module:将驱动文件映射到内存中;
(2)调用系统函数sys_init_module()加载驱动;3、init_module函数分析
# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts) //上面的宏逻辑上相当于 # define init_module(mod, len, opts) sys_init_module(mod, len, opts)
(1)调用init_module()函数就是调用syscall()函数,__NR_init_module是个宏定义,并且还是和sys_init_module()函数绑定的;在调用syscall()函数时第一个参数填__NR_init_module,实际就是在调用内核的sys_init_module()函数;
(2)sys_init_module()函数介绍参考博客:《内核中驱动加载函数(sys_init_module)详解》;4、总结
insmod命令只是进行一些加载驱动的预处理(比如将ko文件映射到内存中、解析加载驱动的传参),最后还是调用的系统函数init_module()去完成具体的驱动加载工作;
-
Android10 insmod源码分析
2021-01-06 15:34:13static int insmod(const char *filename, const char *args) { int ret; int fd; fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); printf(“insmod filename = %s\Android10 insmod源码分析
本文代码来源于rk3399-android10。insmod函数
此处的insmod函数是驱动加载wifi_load_driver之后会被调用的函数:关于wifi_load_driver的源码分析可见之前的博客:android移植wifi驱动流程porting
文件位置:
rk3399-android-10/frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp
static int insmod(const char *filename, const char *args) { int ret; int fd; fd = TEMP_FAILURE_RETRY(open(filename, O_RDONLY | O_CLOEXEC | O_NOFOLLOW)); printf("insmod filename = %s\n",filename); if (fd < 0) { PLOG(ERROR) << "Failed to open " << filename; return -1; } ret = syscall(__NR_finit_module, fd, args, 0); close(fd); if (ret < 0) { PLOG(ERROR) << "finit_module return: " << ret; } return ret; }
其中,他调用到的函数有:
关于syscall:
rk3399-android-10/kernel/arch/arm/kernel/entry-common.S
.macro syscall, nr, func .ifgt __sys_nr - \nr .error "Duplicated/unorded system call entry" .endif .rept \nr - __sys_nr .long sys_ni_syscall .endr .long \func .equ __sys_nr, \nr + 1
这部分看了很久也查了很多资料,但是依然无法理解macro宏定义和被调用函数之间的具体联系。希望有清楚的能指导下。。。
关于__NR_finit_module:
rk3399-android-10/kernel/include/uapi/asm-generic/unistd.h
#define __NR_finit_module 273//系统调用号 __SYSCALL(__NR_finit_module, sys_finit_module)
目前只知道,insmod能通过上述的定义,通过这句代码
syscall(__NR_finit_module, fd, args, 0);
,利用系统调用号(中断号)来调用sys_finit_module,finit_module的系统调用号是__NR_finit_module。sys_finit_module的定义:
rk3399-android-10/kernel/include/linux/syscalls.h
asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
注:asmlinkage表示函数读取的参数来于栈,而非寄存器。
sys_finit_module
在
rk3399-android-10/kernel/kernel/module.c
中,有以下定义:SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) { struct load_info info = { }; loff_t size; void *hdr; int err; err = may_init_module(); if (err) return err; pr_debug("finit_module: fd=%d, uargs=%p, flags=%i\n", fd, uargs, flags); if (flags & ~(MODULE_INIT_IGNORE_MODVERSIONS |MODULE_INIT_IGNORE_VERMAGIC)) return -EINVAL; err = kernel_read_file_from_fd(fd, &hdr, &size, INT_MAX, READING_MODULE); //定义于rk3399-android-10/kernel/fs/exec.c里,函数作用是在保证安全的前提下,打开fd指向的文件并读取内容 if (err) return err; info.hdr = hdr; info.len = size; return load_module(&info, uargs, flags); }
首先,由上述综合来看,可知该函数定义式为:
SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags)
。这一行的意思与asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags);
的那行定义基本相同。详细的中间的宏定义的转换过程感兴趣的可见文章的”宏定义的等价“章节。接着来看上方sys_finit_module调用的下一级函数。
文件位置:rk3399-android-10/kernel/kernel/module.c
static int may_init_module(void) { if (!capable(CAP_SYS_MODULE) || modules_disabled)//capable检查是否有权对指定的资源进行操作,无权限就返回0 //CAP_SYS_MODULE代表着加载或移除系统模块的操作,定义于rk3399-android-10/kernel/include/uapi/linux/capability.h return -EPERM; return 0; } //load_module就是最终实际执行加载模块的函数,本文略过 static int load_module(struct load_info *info, const char __user *uargs, int flags) { struct module *mod; long err = 0; char *after_dashes err = elf_header_check(info); ..... return do_init_module(mod); ... }
宏定义的等价
以下是关于宏定义最终等价的过程:
再来看syscalls.h文件。rk3399-android-10/kernel/include/linux/syscalls.h
#ifndef SYSCALL_DEFINE0 #define SYSCALL_DEFINE0(sname) \ SYSCALL_METADATA(_##sname, 0); \ asmlinkage long sys_##sname(void); \ ALLOW_ERROR_INJECTION(sys_##sname, ERRNO); \ asmlinkage long sys_##sname(void) #endif /* SYSCALL_DEFINE0 */ #define SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) #define SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) #define SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) #define SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) #define SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
由上方,可知,SYSCALL_DEFINE3 ==》 SYSCALL_DEFINEx(3, _finit_module, VA_ARGS)
其中__VA_ARGS__对应的是SYSCALL_DEFINE3 中的后面的所有参数集,
即int, fd, const char __user *, uargs, int, flags
还是在
rk3399-android-10/kernel/include/linux/syscalls.h
中,有以下定义:#define SYSCALL_DEFINEx(x, sname, ...) \ SYSCALL_METADATA(sname, x, __VA_ARGS__) \ __SYSCALL_DEFINEx(x, sname, __VA_ARGS__)
由上,知道SYSCALL_DEFINEx ==》 __SYSCALL_DEFINEx(3, _finit_module, VA_ARGS)
继续分析,还是在
rk3399-android-10/kernel/include/linux/syscalls.h
#ifndef __SYSCALL_DEFINEx #define __SYSCALL_DEFINEx(x, name, ...) \ __diag_push(); \ __diag_ignore(GCC, 8, "-Wattribute-alias", \ "Type aliasing is used to sanitize syscall arguments");\ asmlinkage long sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ __attribute__((alias(__stringify(__se_sys##name)))); \ c \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ __MAP(x,__SC_TEST,__VA_ARGS__); \ __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ return ret; \ } \ __diag_pop(); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) #endif /* __SYSCALL_DEFINEx */
由上,知
__SYSCALL_DEFINEx(3, _finit_module, __VA_ARGS__)
等价于:asmlinkage long sys_finit_module(__MAP(3,__SC_DECL, __VA_ARGS__)) \ __attribute__((alias(__stringify(__se_sys_finit_module)))); \ ALLOW_ERROR_INJECTION(sys_finit_module, ERRNO); \ static inline long __do_sys_finit_module(__MAP(3,__SC_DECL,__VA_ARGS__));\ asmlinkage long __se_sys_finit_module(__MAP(3,__SC_LONG,__VA_ARGS__));\ asmlinkage long __se_sys_finit_module(__MAP(3,__SC_LONG,__VA_ARGS__)) \ {\ long ret = __do_sys_finit_module(__MAP(3,__SC_CAST,__VA_ARGS__));\ __MAP(3,__SC_TEST,__VA_ARGS__); \ __PROTECT(3, ret,__MAP(3,__SC_ARGS,__VA_ARGS__)); \ return ret; \ }\ static inline long __do_sys_finit_module(__MAP(3,__SC_DECL, __VA_ARGS__))
经过上述各类宏转换:
asmlinkage long sys_finit_module(int fd, const char __user * uargs, int flags) \ __attribute__((alias(__stringify(__se_sys_finit_module)))); \ ALLOW_ERROR_INJECTION(sys_finit_module, ERRNO); \ static inline long __do_sys_finit_module(int fd, const char __user * uargs, int flags);\ asmlinkage long __se_sys_finit_module(long fd, long uargs, long flags);\ asmlinkage long __se_sys_finit_module(long fd, long uargs, long flags) \ {\ long ret = __do_sys_finit_module( (int) fd, (const char __user *) uargs, (int) flags);\ BUILD_BUG_ON_ZERO(sizeof(int) > sizeof(long)); \ BUILD_BUG_ON_ZERO(sizeof(char*) > sizeof(long)); \ __PROTECT(2, ret, fd, uargs , flags); \ return ret; \ }\ static inline long __do_sys_finit_module(int fd, const char __user * uargs, int flags)
实际上
SYSCALL_DEFINE3(finit_module,int, fd, 。。。)
,最终等价于asmlinkage long sys_finit_module(int fd, 。。。)
关于__NR_init_module和sys_init_module
在学习本部分代码的时候,借鉴了网上的一些博客,但不知道为什么大家写的insmod调用的函数都是__NR_init_module,也就是sys_init_module,应该是版本差异吧。
为了对比,也将它的代码贴上来。
定义位置:
rk3399-android-10/kernel/include/linux/syscalls.h
asmlinkage long sys_init_module(void __user *umod, unsigned long len, const char __user *uargs);
文件位置:
rk3399-android-10/kernel/kernel/module.c
SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs) { int err; struct load_info info = { }; err = may_init_module(); if (err) return err; printk("init_module: umod=%p, len=%lu, uargs=%p\n",umod,len,uargs); pr_debug("init_module: umod=%p, len=%lu, uargs=%p\n", umod, len, uargs); err = copy_module_from_user(umod, len, &info); if (err) return err; return load_module(&info, uargs, 0); }
经对比会发现,二者区别在于:
init_module 通过传统的 copy_from_user 来拷贝 ko 代码,finit_module 则通过文件描述符fd来调用 vfs 底层的读函数来读取 ko 代码到内核中。总结:在学习过程中,发现其实还有一个insmod函数,位于
external/kmod/tools/modprobe.c
,估计是工具modprobe所用到的insmod函数。不在此贴代码了。modprobe和insmod的区别:
- modprobe可以解决load module时的依赖关系
- modprobe默认会去/lib/modules/当前kernel/下面查找module,而insmod只在给它的参数中去找module
-
Busybox版insmod的一个坑
2022-03-14 21:22:40我们都知道可以通过insmod给内核模块传递参数,例子可以参考这篇博文: Linux内核module_param的实现原理_tugouxp的专栏-CSDN博客 Busybox使能insmod传递参数,需要打开选项: CONFIG_BUSYBOX_CONFIG_FEATURE_... -
Linux insmod指令原理
2021-05-25 15:29:51输入insmod xxx.ko之后(基于linux3.5版本) linux 系统会调用linux/kernel/module.c里面的sys_init_modul函数 sys_init_modul函数先做一些校验,之后进行关键的两步 mod = load_module(umod, len, uargs);//模块... -
modprobe和insmod的区别
2021-11-02 15:55:56modprobe和insmod 首先modprobe和insmod都可以实现加载模块,但是更加推荐使用modprobe而不是insmod。因为modprobe可以解决模块间依赖的问题,比如A模块使用了符号导出,导出的符号需要在B模块中使用,这就要求在... -
insmod时候的详细过程
2021-05-13 16:45:27LINUX系统下执行 insmod时候的详细过程 LINUX系统下执行 insmod时候的详细过程 之前在面试linux驱动的实习岗位的时候问到的问题,没想到几乎每天都用的一个指令,我居然都都没有留心,现在做个记录。 参考文章:... -
Linux中insmod命令起什么作用呢?
2021-05-14 21:21:26insmod命令功能:用于将指定模块加载到内核中insmod命令的语法格式:insmod [参数]-------参数说明------f:不检查目前kernel版本与模块编译时的kernel版本是否一致,强制将模块载入-k:将模块设置为自动卸除-m:输出模块... -
linux insmod error ko文件
2021-05-18 15:39:25与之相关的命令是rmmod 内核模块名 #卸载内核模块insmod 内核模块名 #加载内核模块lsmod 查看 内核模块名,如果对应的计数为0,则表示此内核模块没有被应用程序使用。被其他进程引用的内核模块不能删除。内核模块的... -
linux找不到insmod命令
2022-01-30 16:53:58linux 驱动 insmod -
Ubuntu18.04之modprobe/insmod/rmmod: command not found
2022-01-24 20:27:43# apt install kmod -
linux insmod 提示vmalloc失败
2019-07-19 12:07:03[ 105.642101] insmod.static: page allocation failure: order:0, mode:0xd0 [ 105.642115] CPU: 2 PID: 2963 Comm: insmod.static Not tainted 4.1.15 #3 [ 105.642124] Hardware name: Freescale i.MX6 Quad/... -
linux系统把驱动编译成.ko模块 insmod动态加载
2021-11-19 10:31:15第二种就是将驱动编译成模块(Linux 下模块扩展名为.ko),在Linux 内核启动以后使用“insmod”命令加载驱动模块。在调试驱动的时候一般都选择将其编译为模块,这样我们修改驱动以后只需要编译一下驱动代码即可,不... -
linux insmod命令载入模块内核
2021-05-09 07:39:47【insmod命令作用】静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译和下载内核,效率较低。若采用静态加载的驱动较多,会导致内核... -
Linux_insmod之后一直处于等等状态
2021-11-02 11:18:33先make clean 一下 重启 reboot 如果程序有系统地址变量的话,再重新查看一下并修改成对应的地址 编译make 插入sudo insmod 模块.ko dmesg就会出现奇迹 啊哈哈哈哈哈哈哈哈哈 -
insmod nbd.ko 报错‘Invalid module format‘ 处理方法
2021-12-29 14:12:45若nbd驱动为官网下载的对应版本的内核源码编译出来的,安装时任然报错'Invalid module format',用modinfo nbd.ko查看驱动对应的内核版本号,看看驱动是否少了详细版本段。 若是版本号不够详细导致的,是因为代码... -
Modultils工具源码分析之insmod篇.pdf
2015-06-17 22:24:22Modultils工具源码分析之insmod篇 -
linux内核模块相关命令:lsmod,depmod,modprobe,modinfo,insmod,rmmod 使用说明
2021-03-12 20:51:18inux内核模块相关命令:lsmod,depmod,modprobe,modinfo,insmod,rmmod 使用说明 加载内核驱动的通常流程: 1.先将.ko文件拷贝到/lib/module/`uname -r`(内核版本号)/kernel/driver/...目录下, 根据具体用途的... -
linux insmod *.ko Exec format error
2022-01-24 15:43:45insmod: failed to load vendor/lib/mali.ko: Exec format error 最近调试一个KO的驱动,之前insmod是正常的,突然更新boot代码后,insmod失败。 后面一直没查出原因,编译的时候查找mali.ko,没有看到源码,删除... -
Modultils工具源码分析 depmod.insmod rmmod lsmod
2011-11-17 20:22:33Modultils工具源码分析(depmod.insmod rmmod lsmod Modultils工具源码分析(depmod.insmod rmmod lsmod -
【brcm】博通insmod module失败
2021-05-25 19:46:45自定义module,toolchain同SDK,编译自定义module并导入 2.insmod报错,insmod: can’t insert ‘xxxxxxxko’: invalid module format 2.1 dmesg:<3>xxxxxxxx: sections missing 2.FIX 1 PLTS设置问题,修改/target... -
insmod时vermagic报错及签名报错处理
2021-11-03 11:21:46insmod 驱动时,vermagic报错处理方法: uname -a查看环境上的vermagic 若驱动代码一致,可强制修改include/generated/utsrelease.h中UTS_RELEASE宏,与环境保持一致,再编译出KO。 **签名报错:**signature and/or ... -
Insmod模块加载过程分析
2018-02-05 23:39:00a)在进行JZ2440的一个小demo开发的时候,使用自己编译的内核(3.4.2)及lcd模块进行加载时,insmod会提示加载失败因为内核版本不匹配(提示当前内核版本为空),并且显示模块的内核版本为空。 b)尝试过修改编译... -
insmod提示“Invalid module format”
2021-05-14 16:15:421.现象:编译usb驱动程序,提示 2.原因:在不同机器上编译了驱动导致无效格式 3.解决:在目标机器搭建好编译环境,执行编译内核操作即可 make make install ...insmod XXX.ko modprobe XXX ... -
insmod 后直接提示“已杀死”,内核构造数据包
2021-05-12 17:08:32insmod 后直接提示“已杀死”。lsmod 后发现这个模块正在被使用(used 一栏显示 1),但是后面没有显示是被哪个模块使用...(by一栏是空的),所以也没办法卸载,rmmod -f 也不好用...dmesg发现打印的出错信息是指针问题... -
lsmod、insmod、rmmod
2020-09-09 13:34:23insmod 将模块装载到内核 常用语法: insmod <模块文件名> 完整说明: insmod [-fkmpsvxX][-o <模块名称>][模块文件][符号名称 = 符号值] 参数 描述 -f 不检查目前kernel版本与模块编译时的... -
mdev目录更改后驱动insmod无法被自动创建/dev节点
2021-05-18 12:34:59mdev目录更改导致驱动insmod后无法被自动创建/dev节点分析和解决:1. 在仔细检查自己写的驱动和使用课程老师写的驱动后结果依旧是无法在/dev下创建xyz节点,但是在/sys/class目录下驱动注册的class和class_device都... -
Linux设备驱动程序加载/卸载方法 insmod和modprobe命令
2021-05-10 01:49:59insmod/rmmod insmod需要指定模块的绝对路径,和modprobe不同,insmod并且不会把载入模块的依赖模块也载入进来。 用法:insmod drv.ko rmmod只需要模块的名字即可实现模块的卸载,同样也不会把待卸载模块的依赖模块...