4. 插件注册与回调机制

任务说明

本案例将编写一个GCC插件,注册PLUGIN_START_UNIT、PLUGIN_FINISH_UNIT和PLUGIN_PRE_GENERICIZE三个回调,在编译C源文件时输出回调触发顺序,验证GCC插件注册与回调机制。

环境准备

需要GCC插件开发头文件(通常由gcc-12-plugin-dev或gcc-13-plugin-dev包提供)。确认gcc版本:

gcc --version

插件完整代码

创建文件plugin_order.c:

#include <gcc-plugin.h>
#include <plugin-version.h>
#include <tree.h>
#include <stringpool.h>

int plugin_is_GPL_compatible;

static void start_unit_callback(void *gcc_data, void *user_data) {
    fprintf(stderr, "[PLUGIN] start_unit callback fired\n");
}

static void finish_unit_callback(void *gcc_data, void *user_data) {
    fprintf(stderr, "[PLUGIN] finish_unit callback fired\n");
}

static void pre_genericize_callback(void *gcc_data, void *user_data) {
    tree fndecl = (tree)gcc_data;
    if (fndecl && TREE_CODE(fndecl) == FUNCTION_DECL) {
        const char *name = IDENTIFIER_POINTER(DECL_NAME(fndecl));
        fprintf(stderr, "[PLUGIN] pre_genericize callback fired for function: %s\n", name);
    }
}

int plugin_init(struct plugin_name_args *plugin_info,
                struct plugin_gcc_version *version) {
    if (!plugin_default_version_check(version, &gcc_version)) {
        fprintf(stderr, "[PLUGIN] version mismatch\n");
        return 1;
    }

    register_callback(plugin_info->base_name,
                      PLUGIN_START_UNIT,
                      start_unit_callback,
                      NULL);
    register_callback(plugin_info->base_name,
                      PLUGIN_FINISH_UNIT,
                      finish_unit_callback,
                      NULL);
    register_callback(plugin_info->base_name,
                      PLUGIN_PRE_GENERICIZE,
                      pre_genericize_callback,
                      NULL);

    fprintf(stderr, "[PLUGIN] plugin %s initialized\n", plugin_info->base_name);
    return 0;
}

编译与运行

编译插件为共享库:

gcc -fPIC -shared -o plugin_order.so plugin_order.c $(gcc -print-file-name=plugin)/include

创建测试文件test.c:

int add(int a, int b) {
    return a + b;
}

int main(void) {
    return add(2, 3);
}

使用插件编译测试文件:

gcc -fplugin=./plugin_order.so -c test.c -o test.o 2>&1 | tee plugin_output.txt

验证方法与判定标准

查看plugin_output.txt,应观察到以下输出顺序:

[PLUGIN] plugin plugin_order initialized
[PLUGIN] start_unit callback fired
[PLUGIN] pre_genericize callback fired for function: add
[PLUGIN] pre_genericize callback fired for function: main
[PLUGIN] finish_unit callback fired

判定通过条件:

  1. 插件成功加载,无版本不匹配错误;
  2. 三个回调均被触发;
  3. 输出顺序为start_unit → pre_genericize(每个函数一次)→ finish_unit,与GCC插件事件生命周期一致。

常见问题

  • 编译插件时报找不到头文件:确认已安装对应gcc版本的-plugin-dev包,且编译命令中$(gcc -print-file-name=plugin)/include路径正确。
  • 加载插件时报版本不匹配:插件必须与调用gcc的版本完全一致,使用gcc --version确认。
  • 未看到pre_genericize输出:该回调仅对函数定义触发,确保测试文件包含至少一个函数定义。