解密module_init

本文深入解析了Linux内核模块初始化宏module_init的工作原理,包括其在MODULE宏定义与未定义情况下的展开方式,以及如何通过这些机制实现模块在内核启动过程中的正确初始化。同时解释了如何统一模块初始化函数的调用方式,确保模块能够在insmod时被正确加载。

在Linux底下写过driver模块的对这个宏一定不会陌生。module_init宏在MODULE宏有没有定义的情况下展开的内容是不同的,如果这个宏没有定义,基本上表明阁下的模块是要编译进内核的(obj-y)。
1.在MODULE没有定义这种情况下,module_init定义如下:
#define module_init(x) __initcall(x);
因为
#define __initcall(fn)                            device_initcall(fn)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
所以,module_init(x)最终展开为:
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(".initcall" level ".init"))) = fn
更直白点,假设阁下driver所对应的模块的初始化函数为int gpio_init(void),那么module_init(gpio_init)实际上等于:
static initcall_t  __initcall_gpio_init_6 __used __attribute__((__section__(".initcall6.init"))) = gpio_init;
就是声明一类型为initcall_t(typedef int (*initcall_t)(void))函数指针类型的变量__initcall_gpio_init_6并将gpio_init赋值与它。
这里的函数指针变量声明比较特殊的地方在于,将这个变量放在了一名为".initcall6.init"节中。接下来结合vmlinux.lds中的
.initcall.init : AT(ADDR(.initcall.init) - (0xc0000000 -0x00000000)) {
   __initcall_start = .;
   *(.initcallearly.init) __early_initcall_end = .; *(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
   __initcall_end = .;
   }
以及do_initcalls:
static void __init do_initcalls(void)
{
initcall_t *call;
for (call = __initcall_start; call < __initcall_end; call++)
do_one_initcall(*call);
/* Make sure there is no pending stuff from the initcall sequence */
flush_scheduled_work();
}
那么就不难理解阁下模块中的module_init中的初始化函数何时被调用了:在系统启动过程中start_kernel()->rest_init()->kernel_init()->do_basic_setup()->do_initcalls()。

 

 
 
  




2.在MODULE被定义的情况下(大部分可动态加载的driver模块都属于此, obj-m),module_init定义如下:
#define module_init(initfn) \
static inline initcall_t __inittest(void) \
{ return initfn; } \
int init_module(void) __attribute__((alias(#initfn)));
这段宏定义关键点是后面一句,通过alias将initfn变名为init_module。前面那个__inittest的定义其实是种技巧,用来对initfn进行某种静态的类型检查,如果阁下将模块初始化函数定义成,比如,void gpio_init(void)或者是int gpio_init(int),那么在编译时都会有类似下面的warning:
GPIO/fsl-gpio.c: In function '__inittest':
GPIO/fsl-gpio.c:46: warning: return from incompatible pointer type
通过module_init将模块初始化函数统一别名为init_module,这样以后insmod时候,在系统内部会调用sys_init_module()去找到init_module函数的入口地址。
如果objdump -t gpio.ko,就会发现init_module和gpio_init位于相同的地址偏移处。简言之,这种情况下模块的初始化函数在insmod时候被调用。

目前我的最新加密脚本如下:#!/usr/bin/env python # -*- coding: utf-8 -*- # 版权信息:华为技术有限公司,版本所有(C) 2025-2099 """ 脚本编译 - 打包整个目录 1. 修改 root_dir 和 package 目录路径 2. 在 terminal 执行:python setup.py build_ext --inplace 结果说明: Windows环境:生成多个 .pyd 文件 Linux环境:生成多个 .so 文件 """ import os from Cython.Build import cythonize from setuptools import setup, Extension # 配置:指定要编译的根目录和相对包名 root_dir = r"D:\agent_frame_work\iscp-algorithm-foundation-ai\src\isc_agent_kit" package_name = "isc_agent_kit" # 对应的 Python 包路径 def get_extensions(root_dir, package_name): extensions = [] for root, _, files in os.walk(root_dir): for file in files: if file.endswith(".py"): # 跳过 __init__.py 可选 if file == "__init__.py": continue file_path = os.path.join(root, file) rel_path = os.path.relpath(file_path, root_dir) # 正确处理:先 splitext 拿到无扩展名的路径,再替换 / module_name = os.path.splitext(rel_path)[0].replace(os.sep, ".") full_module_name = f"{package_name}.{module_name}" extensions.append( Extension( name=full_module_name, sources=[file_path], ) ) return extensions extensions = get_extensions(root_dir, package_name) setup( ext_modules=cythonize( extensions, compiler_directives={ 'language_level': 3, 'embedsignature': True, 'binding': True, }, ), options={'build_ext': {'inplace': True}}, ) 如何加密yaml文件呢??
最新发布
11-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值