You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中实现类似Linux内核MODULE_INIT的注册宏

问题原因

gold 链接器默认会启用段垃圾回收,没有被显式引用的自定义段会被当做无用内容丢弃,你当前的链接脚本没有标记段需要保留,也没有引用段内的内容,所以会出现段消失的问题。

可行解决方案

方案1:使用自定义段+修改链接脚本(可自主控制初始化时机)

第一步:修改链接脚本规则

首先通过命令导出当前架构的默认链接脚本,避免完全覆盖默认链接规则导致程序异常:

arm-oe-linux-gnueabi-ld --verbose > default.lds

打开导出的default.lds,在.text段定义的后面新增你自定义的mysec段,给段内容加上KEEP标记,强制链接器保留该段内容:

mysec : {
    __start_mysec = .;
    KEEP(*(mysec))
    __end_mysec = .;
}

第二步:实现MODULE_INIT宏

要给段内符号加used属性,防止编译阶段被优化,同时用extern "C"避免C++名字修饰导致符号引用失败:

#include <cstdint>

// 初始化函数类型定义
typedef void (*module_init_func)(void);

#define MODULE_INIT(func) \
extern "C" const module_init_func __init_##func \
__attribute__((used, section("mysec"))) = func

第三步:调用所有初始化函数

在你需要执行初始化的位置(比如main函数开头)遍历段内的函数指针挨个调用:

extern "C" module_init_func __start_mysec[];
extern "C" module_init_func __end_mysec[];

void run_all_module_init() {
    for (module_init_func* fn = __start_mysec; fn < __end_mysec; ++fn) {
        if (*fn != nullptr) {
            (*fn)();
        }
    }
}

第四步:编译链接参数

链接时指定修改后的链接脚本即可:

arm-oe-linux-gnueabi-g++ your_code.cpp -T default.lds -o output

方案2:使用系统默认.init_array段(无需修改链接脚本)

如果不需要自主控制初始化时机,希望和标准C++全局构造函数一样在main执行前自动运行,可以直接用系统默认的.init_array段,gold链接器默认会保留该段,无需修改链接脚本:

typedef void (*module_init_func)(void);

#define MODULE_INIT(func) \
extern "C" const module_init_func __init_##func \
__attribute__((used, section(".init_array"))) = func

所有用该宏声明的函数会自动在main函数执行前被系统调用,不需要自己实现遍历逻辑。

注意事项
  • 如果仍然出现段丢失的情况,可以在链接参数中加入-Wl,--print-gc-sections,确认是否是链接器回收了你的自定义段
  • 不要用仅包含自定义段的精简链接脚本,会覆盖所有默认的段规则,导致程序无法正常链接运行

内容的提问来源于stack exchange,提问作者Silver

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 04:51:00