GCC 4.5.0之后支持了用户插件。它可以加载你自己写的Shared library (*.so),并且把你提供的回调函数hook到你想要的地方 (function register_callback and enum plugin_event)。
参照GCC的文档,写一个GCC plugin还真不太复杂 – 当然是指"hello world"这种插件。
既然GCC Plugin是以事件回调的方式提供的,想当然,步骤应该有
1. 编写回调函数
2. 注册回调函数到Event
回调函数的原型是
- typedef void (*plugin_callback_func)(void *gcc_data, void *user_data);
注册回调函数的方法是
- extern void register_callback (const char *plugin_name,
- int event,
- plugin_callback_func callback,
- void *user_data);
那么GCC提供的可以注册回调函数的Event都有哪些呢?
首先,我们来看gcc-plugin.h,它一般在GCC_PATH/lib/gcc/ i686-pc-linux-gnu/4.6.3/plugin/include里,当然具体路径跟据你安装的版本有所不同。
- /* Event names. */
- enum plugin_event
- {
- # define DEFEVENT(NAME) NAME,
- # include "plugin.def"
- # undef DEFEVENT
- PLUGIN_EVENT_FIRST_DYNAMIC
- };
好吧,我们继续看plugin.def
- /* To hook into pass manager. */
- DEFEVENT (PLUGIN_PASS_MANAGER_SETUP)
- /* After finishing parsing a type. */
- DEFEVENT (PLUGIN_FINISH_TYPE)
- /* Useful for summary processing. */
- DEFEVENT (PLUGIN_FINISH_UNIT)
- /* Allows to see low level AST in C and C frontends. */
- DEFEVENT (PLUGIN_PRE_GENERICIZE)
- /* Called before GCC exits. */
- DEFEVENT (PLUGIN_FINISH)
- /* Information about the plugin. */
- DEFEVENT (PLUGIN_INFO)
- /* Called at start of GCC Garbage Collection. */
- DEFEVENT (PLUGIN_GGC_START)
- /* Extend the GGC marking. */
- DEFEVENT (PLUGIN_GGC_MARKING)
- ....
我们还需要知道什么时候调用register_callback来注册回调函数。
重要的入口函数
- int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
每个插件都应该导出一个叫做plugin_init的函数,其在插件刚被加载之后调用。该函数负责注册插件需要的所有回调,并做其它所需要的初始化。
其参数1,顾名思义,它是用来传递我们插件的名称的,从其定义可知还包含插件的参数(下边再说)。
- struct plugin_name_args
- {
- char *base_name; /* Short name of the plugin (filename without .so suffix). */
- const char *full_name; /* Path to the plugin as specified with -fplugin=. */
- int argc; /* Number of arguments specified with -fplugin-arg-.... */
- struct plugin_argument *argv; /* Array of ARGC key-value pairs. */
- const char *version; /* Version string provided by plugin. */
- const char *help; /* Help string provided by plugin. */
- }
参数2,是加载plugin的GCC的版本信息
- struct plugin_gcc_version
- {
- const char *basever;
- const char *datestamp;
- const char *devphase;
- const char *revision;
- const char *configuration_arguments;
- };
另外,在plugin-version.h中,GCC还以gcc_version 提供了编译插件的GCC版本。
有了这两个版本信息,我们就可以检查加载插件的GCC是否和编译插件的GCC版本兼容。 我们可以使用GCC提供的plugin_default_version_check,也可以自己编写版本检查函数。
返回值
如果plugin初始化失败,plugin_init必须返回一个非零的值,否则返回0.
简单的hello world from GCC
了解了上述内容,我们就可以写个hello world来看看
- #include "gcc-plugin.h"
- #include "plugin-version.h"
- #include <stdio.h>
- int plugin_is_GPL_compatible;
- void test_callback(void *gcc_data, void *user_data)
- {
- printf("hello world from GCC!\n");
- }
- static bool
- version_check (struct plugin_gcc_version *gcc_version,
- struct plugin_gcc_version *plugin_version)
- {
- if (!gcc_version || !plugin_version)
- return false;
- if (strcmp (gcc_version->basever, plugin_version->basever))
- return false;
- return true;
- }
- int plugin_init (struct plugin_name_args *plugin_info,
- struct plugin_gcc_version *version)
- {
- if (!version_check (version, &gcc_version))
- {
- printf("gcc version is not compatible with the plugin\n");
- return 1;
- }
- register_callback(plugin_info->base_name,
- PLUGIN_OVERRIDE_GATE,
- &test_callback,
- 0);
- return 0;
- }
需要注意的是,头文件`gcc-plugin.h'必须为第一个包含的gcc头文件.
而且,int plugin_is_GPL_compatible是必须的。它是用来宣称Plugin具有GPL兼容的版权。
如果没有它,当你加载插件时,GCC会产生一个致命错误
fatal error: plugin is not licensed under a GPL-compatible license
: undefined symbol: plugin_is_GPL_compatible
compilation terminated
然后编译你的插件
- g -I gcc-4.6.3/lib/gcc/i686-pc-linux-gnu/4.6.3/plugin/include -shared -fPIC -o helloworld.so plugin.cxx
测试一下
- g -fplugin=./helloworld.so test.cxx
好多hello world 啊
插件参数
这里还有一个小问题,我们知道用参数"-fplugin=./helloworld.so"来加载插件,但从回调函数的
原型中我们可知,应该是有办法给plugin传递参数的。
-fplugin-arg--[=]
我们把下面的测试代码加入到plugin_init中,来打印一下插件参数
- int plugin_argc = plugin_info->argc;
- plugin_argument * plugin_args = plugin_info->argv;
- for(int i = 0; i < plugin_argc; ++i)
- {
- if(plugin_args != NULL)
- {
- printf("key is %s, value is %s\n", plugin_args->key, plugin_args->value == N ULL ? "NULL" : plugin_args->value);
- }
- ++plugin_args;
- }
使用命令
- g++ -fplugin=./helloworld.so -fplugin-arg-lll-first=1111 -fplugin-arg-lll-second=2222 test.cxx
传递两个参数,结果是
- key is first, value is 1111
- key is second, value is 2222
参考
~boris/blog/2010/05/03/parsing-cxx-with-gcc-plugin-part-1/
阅读(3726) | 评论(0) | 转发(0) |