Chinaunix首页 | 论坛 | 博客
  • 博客访问: 17747
  • 博文数量: 3
  • 博客积分: 166
  • 博客等级: 入伍新兵
  • 技术积分: 60
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-27 14:01
文章分类
文章存档

2012年(3)

我的朋友

分类: C/C++

2012-03-27 15:55:37

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

 

回调函数的原型是

点击(此处)折叠或打开

  1. typedef void (*plugin_callback_func)(void *gcc_data, void *user_data);

 

注册回调函数的方法是 

点击(此处)折叠或打开

  1. extern void register_callback (const char *plugin_name,
  2.                                int event,
  3.                                plugin_callback_func callback,
  4.                                void *user_data);

 

那么GCC提供的可以注册回调函数的Event都有哪些呢?

首先,我们来看gcc-plugin.h,它一般在GCC_PATH/lib/gcc/ i686-pc-linux-gnu/4.6.3/plugin/include里,当然具体路径跟据你安装的版本有所不同。

点击(此处)折叠或打开

  1. /* Event names. */
  2. enum plugin_event
  3. {
  4. # define DEFEVENT(NAME) NAME,
  5. # include "plugin.def"
  6. # undef DEFEVENT
  7.   PLUGIN_EVENT_FIRST_DYNAMIC
  8. };


好吧,我们继续看plugin.def


点击(此处)折叠或打开

  1. /* To hook into pass manager. */
  2. DEFEVENT (PLUGIN_PASS_MANAGER_SETUP)

  3. /* After finishing parsing a type. */
  4. DEFEVENT (PLUGIN_FINISH_TYPE)

  5. /* Useful for summary processing. */
  6. DEFEVENT (PLUGIN_FINISH_UNIT)

  7. /* Allows to see low level AST in C and C frontends. */
  8. DEFEVENT (PLUGIN_PRE_GENERICIZE)

  9. /* Called before GCC exits. */
  10. DEFEVENT (PLUGIN_FINISH)

  11. /* Information about the plugin. */
  12. DEFEVENT (PLUGIN_INFO)

  13. /* Called at start of GCC Garbage Collection. */
  14. DEFEVENT (PLUGIN_GGC_START)

  15. /* Extend the GGC marking. */
  16. DEFEVENT (PLUGIN_GGC_MARKING)

  17. ....

 

我们还需要知道什么时候调用register_callback来注册回调函数。

 

重要的入口函数


点击(此处)折叠或打开

  1. int plugin_init (struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)

每个插件都应该导出一个叫做plugin_init的函数,其在插件刚被加载之后调用。该函数负责注册插件需要的所有回调,并做其它所需要的初始化。

其参数1顾名思义,它是用来传递我们插件的名称的,从其定义可知还包含插件的参数(下边再说)

点击(此处)折叠或打开

  1. struct plugin_name_args
  2. {
  3. char *base_name; /* Short name of the plugin (filename without .so suffix). */
  4. const char *full_name; /* Path to the plugin as specified with -fplugin=. */
  5. int argc; /* Number of arguments specified with -fplugin-arg-.... */
  6. struct plugin_argument *argv; /* Array of ARGC key-value pairs. */
  7. const char *version; /* Version string provided by plugin. */
  8. const char *help; /* Help string provided by plugin. */
  9. }


参数2,是加载pluginGCC的版本信息

 

点击(此处)折叠或打开

  1. struct plugin_gcc_version
  2. {
  3. const char *basever;
  4. const char *datestamp;
  5. const char *devphase;
  6. const char *revision;
  7. const char *configuration_arguments;
  8. };

 

另外,在plugin-version.h中,GCC还以gcc_version 提供了编译插件的GCC版本。

有了这两个版本信息,我们就可以检查加载插件的GCC是否和编译插件的GCC版本兼容。 我们可以使用GCC提供的plugin_default_version_check,也可以自己编写版本检查函数。

 

返回值

如果plugin初始化失败,plugin_init必须返回一个非零的值,否则返回0.

 

简单的hello world from GCC

 

了解了上述内容,我们就可以写个hello world来看看

点击(此处)折叠或打开

  1. #include "gcc-plugin.h"
  2. #include "plugin-version.h"
  3. #include <stdio.h>

  4. int plugin_is_GPL_compatible;

  5. void test_callback(void *gcc_data, void *user_data)
  6. {
  7.     printf("hello world from GCC!\n");
  8. }

  9. static bool
  10. version_check (struct plugin_gcc_version *gcc_version,
  11.               struct plugin_gcc_version *plugin_version)
  12. {
  13.   if (!gcc_version || !plugin_version)
  14.     return false;

  15.   if (strcmp (gcc_version->basever, plugin_version->basever))
  16.     return false;

  17.   return true;
  18. }

  19. int plugin_init (struct plugin_name_args *plugin_info,
  20.              struct plugin_gcc_version *version)
  21. {
  22.     if (!version_check (version, &gcc_version))
  23.     {
  24.          printf("gcc version is not compatible with the plugin\n");
  25.          return 1;
  26.     }


  27.     register_callback(plugin_info->base_name,
  28.                       PLUGIN_OVERRIDE_GATE,
  29.                       &test_callback,
  30.                       0);

  31.     return 0;
  32. }

需要注意的是,头文件`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

 

然后编译你的插件

点击(此处)折叠或打开

  1. g -I gcc-4.6.3/lib/gcc/i686-pc-linux-gnu/4.6.3/plugin/include -shared -fPIC -o helloworld.so plugin.cxx

 

测试一下

点击(此处)折叠或打开

  1. g -fplugin=./helloworld.so test.cxx

好多hello world

 

插件参数

 

这里还有一个小问题,我们知道用参数"-fplugin=./helloworld.so"来加载插件,但从回调函数的
原型中我们可知,应该是有办法给plugin传递参数的。

-fplugin-arg--[=]

我们把下面的测试代码加入到plugin_init中,来打印一下插件参数


 

点击(此处)折叠或打开

  1. int plugin_argc = plugin_info->argc;
  2. plugin_argument * plugin_args = plugin_info->argv;
  3. for(int i = 0; i < plugin_argc; ++i)
  4. {
  5.     if(plugin_args != NULL)
  6.     {
  7.         printf("key is %s, value is %s\n", plugin_args->key, plugin_args->value == N ULL ? "NULL" : plugin_args->value);
  8.     }
  9.     ++plugin_args; 
  10. }

使用命令

点击(此处)折叠或打开

  1. g++ -fplugin=./helloworld.so -fplugin-arg-lll-first=1111 -fplugin-arg-lll-second=2222 test.cxx

传递两个参数,结果是

 

点击(此处)折叠或打开

  1. key is first, value is 1111
  2. key is second, value is 2222

 

参考

~boris/blog/2010/05/03/parsing-cxx-with-gcc-plugin-part-1/

 


 

 



阅读(3538) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~