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
判定通过条件:
- 插件成功加载,无版本不匹配错误;
- 三个回调均被触发;
- 输出顺序为
start_unit→pre_genericize(每个函数一次)→finish_unit,与GCC插件事件生命周期一致。
常见问题
- 编译插件时报找不到头文件:确认已安装对应gcc版本的
-plugin-dev包,且编译命令中$(gcc -print-file-name=plugin)/include路径正确。 - 加载插件时报版本不匹配:插件必须与调用gcc的版本完全一致,使用
gcc --version确认。 - 未看到
pre_genericize输出:该回调仅对函数定义触发,确保测试文件包含至少一个函数定义。