精华内容
下载资源
问答
  • insmod 报错 Invalid module format
    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:57
    insmod过程pdf--莫搬砖员
  • insmod过程

    2021-10-22 22:32:26
    insmod过程 模块加载与卸载 众所周知,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文件头信息,别忘了模块文件.koELF relocatable文件,所以需要检查一下是否是正确的ELF文件格式。layout_and_allocate()获取模块的布局,重新申请一次内存,并对相关section对内容进行修改。add_unformed_module()将模块加入到内核的模块链表中。

    在这里插入图片描述

    Part1.1 ELF头校验

    elf_header_check()是根据规范来校验整个ELF文件头的信息,比如ELF HeaderMagic,必须是 '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()函数将内存里的每一个sectionsh_addr节地址记录为当前内核内存上的地址,并找到__versions节和.modinfo节,取消这两个节的内存申请标记:

    在这里插入图片描述

    显而易见,经过rewrite_section_headers()之后,内存上的数据肯定发生了变化,用通用的方法,导出内存上的数据:dump memory hello.ko-rewirte-section.dump 0xa0ab0000 0xa0ab0000+31476

    在这里插入图片描述

    比较一波,除去第0个section,后面每一个sectionsh_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也注释得很清楚,0AX可执行的非小数据,1A只需申请内存的不可写的非小数据,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()中申请完内存后,就需要将相关的数据拷贝到对应的布局中,数据来源自然是前面经过修正后的模块信息,修改完后,还需要再次修正每个sectionlayout内存中的位置。

    在这里插入图片描述
    在这里插入图片描述

    申请内存的过程调用的是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_layoutcore_layout的cache。

    在这里插入图片描述

    Part2.1 申请percpu内存

    percpu_modalloc()这里重新为percpu section的数据申请内存,是补上Part1部分的去向了percpuSHF_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_RELSHT_RELA类型的节进行重定位操作,通过sh_info获取到附加的重定位节数据,再根据要重定位的节的类型去调用具体的重定位函数:

    在这里插入图片描述

    sh_info类型,上面用到下面的SHT_RELSHT_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_layoutinit_layouticache

    在这里插入图片描述

    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 错误处理

    至于最后的这里,都是上面出错时的异常处理,就不多看了。

    在这里插入图片描述

    展开全文
  • 1、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:13
    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\


    本文代码来源于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的区别:

    1. modprobe可以解决load module时的依赖关系
    2. 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:56
    modprobe和insmod 首先modprobe和insmod都可以实现加载模块,但是更加推荐使用modprobe而不是insmod。因为modprobe可以解决模块间依赖的问题,比如A模块使用了符号导出,导出的符号需要在B模块中使用,这就要求在...
  • insmod时候的详细过程

    2021-05-13 16:45:27
    LINUX系统下执行 insmod时候的详细过程 LINUX系统下执行 insmod时候的详细过程 之前在面试linux驱动的实习岗位的时候问到的问题,没想到几乎每天都用的一个指令,我居然都都没有留心,现在做个记录。 参考文章:...
  • insmod命令功能:用于将指定模块加载到内核中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:58
    linux 驱动 insmod
  • # apt install kmod
  • [ 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),在Linux 内核启动以后使用“insmod”命令加载驱动模块。在调试驱动的时候一般都选择将其编译为模块,这样我们修改驱动以后只需要编译一下驱动代码即可,不...
  • insmod命令作用】静态加载就是把驱动程序直接编译进内核,系统启动后可以直接调用。静态加载的缺点是调试起来比较麻烦,每次修改一个地方都要重新编译和下载内核,效率较低。若采用静态加载的驱动较多,会导致内核...
  • 先make clean 一下 重启 reboot 如果程序有系统地址变量的话,再重新查看一下并修改成对应的地址 编译make 插入sudo insmod 模块.ko dmesg就会出现奇迹 啊哈哈哈哈哈哈哈哈哈
  • 若nbd驱动为官网下载的对应版本的内核源码编译出来的,安装时任然报错'Invalid module format',用modinfo nbd.ko查看驱动对应的内核版本号,看看驱动是否少了详细版本段。 若是版本号不够详细导致的,是因为代码...
  • Modultils工具源码分析之insmod
  • inux内核模块相关命令: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:45
    insmod: failed to load vendor/lib/mali.ko: Exec format error 最近调试一个KO的驱动,之前insmod是正常的,突然更新boot代码后,insmod失败。 后面一直没查出原因,编译的时候查找mali.ko,没有看到源码,删除...
  • Modultils工具源码分析(depmod.insmod rmmod lsmod Modultils工具源码分析(depmod.insmod rmmod lsmod
  • 自定义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报错处理方法: uname -a查看环境上的vermagic 若驱动代码一致,可强制修改include/generated/utsrelease.h中UTS_RELEASE宏,与环境保持一致,再编译出KO。 **签名报错:**signature and/or ...
  •  a)在进行JZ2440的一个小demo开发的时候,使用自己编译的内核(3.4.2)及lcd模块进行加载时,insmod会提示加载失败因为内核版本不匹配(提示当前内核版本为空),并且显示模块的内核版本为空。  b)尝试过修改编译...
  • 1.现象:编译usb驱动程序,提示 2.原因:在不同机器上编译了驱动导致无效格式 3.解决:在目标机器搭建好编译环境,执行编译内核操作即可 make make install ...insmod XXX.ko modprobe XXX ...
  • insmod 后直接提示“已杀死”。lsmod 后发现这个模块正在被使用(used 一栏显示 1),但是后面没有显示是被哪个模块使用...(by一栏是空的),所以也没办法卸载,rmmod -f 也不好用...dmesg发现打印的出错信息是指针问题...
  • lsmod、insmod、rmmod

    2020-09-09 13:34:23
    insmod 将模块装载到内核 常用语法: insmod <模块文件名> 完整说明: insmod [-fkmpsvxX][-o <模块名称>][模块文件][符号名称 = 符号值] 参数 描述 -f 不检查目前kernel版本与模块编译时的...
  • mdev目录更改导致驱动insmod后无法被自动创建/dev节点分析和解决:1. 在仔细检查自己写的驱动和使用课程老师写的驱动后结果依旧是无法在/dev下创建xyz节点,但是在/sys/class目录下驱动注册的class和class_device都...
  • insmod/rmmod insmod需要指定模块的绝对路径,和modprobe不同,insmod并且不会把载入模块的依赖模块也载入进来。 用法:insmod drv.ko rmmod只需要模块的名字即可实现模块的卸载,同样也不会把待卸载模块的依赖模块...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,734
精华内容 16,693
关键字:

insmod