GCC Plugin
Table of Contents
1. GCC Plugin
https://gcc.gnu.org/onlinedocs/gccint/Plugins.html#Plugins
https://jongy.github.io/2020/04/25/gcc-assert-introspect.html
https://codesynthesis.com/~boris/blog//2010/05/03/parsing-cxx-with-gcc-plugin-part-1/
1.1. overview
gcc plugin 在 gcc 中定义了许多 hook (event), 用户可以在 so 中实现对应的 callback 来影响 gcc 的行为, 例如修改 AST 或添加 tree pass 或 rtl pass 来修改 GIMPLE 或 rtl. 通过 plugin 可以添加 instrumentation 或进行静态代码分析.
gcc 支持的 event 在 plugin.def 中, 主要包括以下几个 event:
- PLUGIN_START_PARSE_FUNCTION
PLUGIN_FINISH_PARSE_FUNCTION
解析完函数的 GENERIC 后, 参数为 func_decl
PLUGIN_PASS_MANAGER_SETUP
添加一个 pass
PLUGIN_FINISH_TYPE
解析完一个 type 后, 例如 struct x {}. 调用这个 hook 的参数是 type 对应的 tree
PLUGIN_FINISH_DECL
解析完一个 decl 后, 例如 int x. 参数为对应的 decl, 例如 var_decl 或 param_decl
- PLUGIN_FINISH_UNIT
PLUGIN_PRE_GENERICIZE
解析完函数的 AST 后, 参数为 func_decl.
- PLUGIN_ATTRIBUTES
- PLUGIN_START_UNIT
- PLUGIN_PRAGMAS
PLUGIN_ALL_PASSES_START
第一个 pass 之前, 没有参数
PLUGIN_ALL_PASSES_END
最后一个 pass 之后, 没有参数
PLUGIN_OVERRIDE_GATE
每个 pass 执行前, 参数为 gate 原始的结果, 通过它可以控制 pass 是否执行
- PLUGIN_PASS_EXECUTION
- PLUGIN_INCLUDE_FILE
1.2. tree
https://gcc.gnu.org/onlinedocs/gccint/GENERIC.html#GENERIC
https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html#GIMPLE
https://www.cse.iitb.ac.in/grc/gcc-workshop-13/downloads/slides/Day1/gccw13-gimple-manipulation.pdf
gcc plugin 操作的数据主要是 HIR (除了使用 plugin 自定义 rtl pass), 例如:
- PLUGIN_PRE_GENERICIZE 操作的是 AST
- PLUGIN_FINISH_PARSE_FUNCTION 操作的是 GENERIC
- PLUGIN_PASS_MANAGER_SETUP 添加的 gimple_opt_pass 操作的是 GIMPLE
这三种 HIR 都会使用 tree 来表示, 只不过它们使用的 tree node 类型会有些差别:
AST
gcc frontend 的输出是 AST (abstract syntax tree), 对应具体的语言. 不同语言的 AST 会有些差别. 例如 c 语言中的 `for` 会对应 `FOR_STMT`, 定义在 c-common.def 中
GENERIC
gcc 会把语言相关的 AST 转换成通用的 GENERIC. c 语言中的 `FOR_STMT` 会被转换成通用的 `LOOP_EXPR`, 定义在 tree.def 中.
GIMPLE
为了便于优化, gcc 会把 GENERIC 转换成 GIMPLE, GIMPLE 采用三地址形式, 且控制流变成 GOTO 的形式, 例如前面提到的 `LOOP_EXPR` 会转换成 `GIMPLE_GOTO`, 定义在 gimple.def 中
例如一个 function 的声明部分对应的 tree 通过 debug_tree
函数打印出来的 GENERIC
为:
<function_decl 0x7fe180089400 main type <function_type 0x7fe180085888 type <integer_type 0x7fe17ff745e8 int public SI size <integer_cst 0x7fe17ff5bee8 constant 32> unit-size <integer_cst 0x7fe17ff5bf00 constant 4> align:32 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fe17ff745e8 precision:32 min <integer_cst 0x7fe17ff5bea0 -2147483648> max <integer_cst 0x7fe17ff5beb8 2147483647> pointer_to_this <pointer_type 0x7fe17ff7c9d8>> QI size <integer_cst 0x7fe17ff5bd98 constant 8> unit-size <integer_cst 0x7fe17ff5bdb0 constant 1> align:8 warn_if_not_align:0 symtab:0 alias-set -1 canonical-type 0x7fe180085888 arg-types <tree_list 0x7fe180092500 value <integer_type 0x7fe17ff745e8 int> chain <tree_list 0x7fe1800924d8 value <pointer_type 0x7fe1800857e0> chain <tree_list 0x7fe17ff707d0 value <void_type 0x7fe17ff74f18 void>>>>> public static QI test.c:2:5 align:8 warn_if_not_align:0 initial <block 0x7fe18008e420> result <result_decl 0x7fe17ff68bb8 D.1913 type <integer_type 0x7fe17ff745e8 int> ignored SI test.c:2:5 size <integer_cst 0x7fe17ff5bee8 32> unit-size <integer_cst 0x7fe17ff5bf00 4> align:32 warn_if_not_align:0 context <function_decl 0x7fe180089400 main>> arguments <parm_decl 0x7fe1800b0000 argc type <integer_type 0x7fe17ff745e8 int> SI test.c:2:14 size <integer_cst 0x7fe17ff5bee8 32> unit-size <integer_cst 0x7fe17ff5bf00 4> align:32 warn_if_not_align:0 context <function_decl 0x7fe180089400 main> arg-type <integer_type 0x7fe17ff745e8 int> chain <parm_decl 0x7fe1800b0080 argv type <pointer_type 0x7fe1800857e0> unsigned decl_0 DI test.c:2:26 size <integer_cst 0x7fe17ff5bca8 constant 64> unit-size <integer_cst 0x7fe17ff5bcc0 constant 8> align:64 warn_if_not_align:0 context <function_decl 0x7fe180089400 main> arg-type <pointer_type 0x7fe1800857e0>>> struct-function 0x7fe1800b1000>
它包含了函数声明的所有信息: 函数的名字, 参数和返回值的名字和类型
这里面所有的 <>
都表示不同 tree 类型, 例如:
- function_decl
- function_type
- integer_type
- integer_cst
- result_decl
- parm_decl
这个 function_decl 相当于如下的伪代码:
function_decl { .name = "main", .type = function_type { .type = integer_type { .size = integer_cst { .val = 32, } ... } ... }, .result = result_decl { .type = integer_type { } } .arguments = chain( param_decl { .name = "argc", ... }, param_decl { .name = "argv", ... } ); }
gcc 的 tree 类型相当于一个接口, 各个具体的 tree 类型相当于抽象类或具体的派生类, 但这种类的层次关系是通过 c 的 union 和 struct 来实现的.
union tree_node { struct tree_base base; struct tree_typed typed; struct tree_common common; struct tree_int_cst int_cst; struct tree_poly_int_cst poly_int_cst; /* ... */ }; struct tree_typed { struct tree_base base; tree type; }; struct tree_common { struct tree_typed typed; tree chain; }; struct tree_int_cst { struct tree_typed typed; HOST_WIDE_INT val[1]; };
即 tree_int_cst 的派生树为: base <- typed <- int_cst
:
- 通过
x.int_cst.val
访问一个 int_cst tree 的 val 成员, - 通过
x.int_cst.typed.base.code
或者x.base.code
访问 int_cst 的 tree code
因为 union 的原因, x.int_cst.typed.base
和 x.base
实际是相同的地址. gcc 用
union 和 struct 模拟了 c++ 的对象模型, 具体的可以参考 Inside The C++ Object
Model
gcc 提供了大量的宏和函数来方便访问不同类型的 tree 中的数据, 例如:
- TREE_CODE, 适用于所有的 tree 类型, 返回 x.base.code, 即 tree code
- TREE_TYPE, 适用于继承自 tree_typed 的类型, 返回 x.typed.type, 例如 function decl 对应的 type, 或者 expr 的 type
- TREE_CHAIN 用来获得 chain 中的下一个 node, 例如前面的 arguments
- TREE_OPERAND, 适用于继承自 tree_exp 类型, 获得 exp 的 operand
- DECL_XXX, 适用于 decl 类的 tree, 例如 function_decl, var_decl 等, 获得 decl 相关信息, 例如 DECL_NAME, DECL_RESULT, DECL_ARGUMENTS
- TYPE_XXX, 获得 type 具体的信息, 例如 TYPE_NAME, TYPE_SIZE
- IDENTIFIER_POINTER, 适用于继承自 tree_identifier 的类型, 得到其 identifier 字符串, 例如 IDENTIFIER_POINTER(DECL_NAME(function_decl)) 可以得到函数声明的名字
- DECL_SAVED_TREE 用来得到 function_decl 的函数体
- tree_stmt_iterator 访问 statement_list 中的 stmt
- debug_tree 可以 dump 整个 tree
- walk_tree 可以 walk 整个 tree, 以遍历 tree 的 list, vec, operand 等
- build_xxx 可以构造新的 tree, 例如 build_int_cst, build_fn_decl, build_string_literal, …
- BIND_EXPR_BODY, BIND_EXPR_VARS 用来操作 block 中的 stmt 和 var
以前面的 function_decl 为例:
assert(TREE_CODE(TREE_TYPE(DECL_RESULT(x))) == INTEGER_TYPE); assert(strcmp(IDENTIFIER_POINTER(DECL_NAME(DECL_ARGUMENTS(x))), "argc") == 0); assert(TREE_INT_CST_ELT(TYPE_SIZE(TREE_TYPE(x)), 0) == 32);
http://icps.u-strasbg.fr/~pop/gcc-ast.html 用图片展示了几个简单的 AST 结构, 有些可能会有变化, 可以先通过 debug_tree
打印出 tree 的结构, 然后看看看用哪个宏或函数可以操作关心的数据, 具体需要去查找 tree.h
以及对调试非常有用的
print-tree.h
和 tree-pretty-print.h
1.2.1. GIMPLE
操作 AST 和 GENERIC 都是用宏操作 union 的方式, 但 GIMPLE 不再用 tree (或者 union), 而不是用 c++ class 来描述类型的层次结构, 例如 gimple, gcall, gassign, 相应的提供了和 tree 的宏类似的函数访问其成员, 例如 gimple_code (对应 TREE_CODE), gimple_get_lhs (对应 TREE_OPERAND), …
但 GIMPLE 并没有完全代替 tree, 它主要是针对 stmt
提供了另一个封装, 其它部分(例如 name, type, decl 等) 还会用 tree 来表示, 例如 gimple_get_lhs 返回的仍然是一个
tree (一个 var_decl).
关于 GIMPLE 的用法需要看一下 gimple.h
和 gimple-pretty-print.h
1.3. example
1.3.1. hello gcc plugin
- 给全局变量生成 getter 和 setter
- 在函数入口插入 trace 调用
- 一个简单的 dce
- 比较 AST/GENERIC/GIMPLE 的差别
1.3.2. linux kernel plugin
1.3.3. gdb plugin
gdb 使用 plugin 在动态编译 c 代码时对 IR 做一些修改
https://sourceware.org/gdb/onlinedocs/gdb/Compiling-and-Injecting-Code.html
1.3.4. gcc python plugin
Backlinks
GCC (GCC > GCC Plugin): GCC Plugin