2013年07月17日
GCC のプラグインを作ってみる (2) C++ で g++ のプラグインを書いてみる
GCC はバージョン 4.8 から、実装言語が C++ になりました。というわけで、プラグインも今後は C++ で書きたいところです。また、せっかくなので、gcc ではなく g++ のプラグインを C++ で書いてみました。
参考サイト:Parsing C++ with GCC plugins, Part 2
とりあえず、g++ -4.7 をインストールします。
$ sudo apt-get install g++-4.7
以下のような g++ のプラグインを書いてみました。PLUGIN_OVERRIDE_GATE だと、何回もコールバックが呼び出されてしまうので、PLUGIN_FINISH_UNIT でコンパイル単位(cpp ファイル)が終わった時だけ、global namespace 内の宣言(の名前だけ)を表示するようにしてみました。
#include "gcc-plugin.h" /* must be the first */ #include "tm.h" /* main_input_filename */ #include "cp/cp-tree.h" /* C++ */ #include <iostream> int plugin_is_GPL_compatible; /* gcc_data - event-specific data provided by GCC user_data - plugin-specific data provided by the plug-in. */ extern "C" void finish_unit_callback(void *gcc_data, void *user_data) { cp_binding_level *level = NAMESPACE_LEVEL(global_namespace); for (tree decl = level->names; decl != NULL; decl = TREE_CHAIN(decl)) { if (!DECL_IS_BUILTIN(decl)) { tree id = DECL_NAME(decl); if (id != NULL) { const char *name = IDENTIFIER_POINTER(id); std::cerr << name << std::endl; } } } } extern "C" int plugin_init(struct plugin_name_args *info, struct plugin_gcc_version *version) { std::cerr << "main_input_filename: " << main_input_filename << std::endl; register_callback(info->base_name, PLUGIN_FINISH_UNIT, finish_unit_callback, NULL/* user_data */); return 0; }
※ 参考サイトでは、プラグイン関係のヘッダを include する時、全体を extern "C" で囲んでいますが、どうも tree.h などを C リンケージで include すると、大量のエラーが出てしまいます。プラグインの API を定義する gcc-plugin.h で、__cplusplus の場合は extern "C" しているので、API だけ C リンケージで公開しておけば、とりあえず問題無さそうです。
ビルドと実行は、g++-4.7 を gcc-4.7 の代わりに使うだけです。(プラグインは、gcc でも読み込むことが可能ですが、C には namespace という概念が無いのでエラーになります。)
$ g++-4.7 -I`g++-4.7 -print-file-name=plugin`/include -fPIC -shared -O2 test_gcc_plugin.cpp -o test_gcc_plugin.so $ cat test.cpp int g() { return 123; } int f() { return g(); } int main() { return 0; } $ g++-4.7 -fplugin=./test_gcc_plugin.so test.cpp main_input_filename: test.cpp main f g