GCC - 如何自动检测每个基本模块

gcc - how to auto instrument every basic block

本文关键字:模块 自动检测 GCC      更新时间:2023-10-16

GCC具有用于功能进入/退出的自动检测选项。

-

finstrument-functions生成用于进入和退出函数的检测调用。就在函数输入之后和函数之前 退出时,将使用 当前函数的地址及其调用站点。(在某些平台上, __builtin_return_address 在当前功能之外不起作用,因此调用站点信息可能无法用于分析 否则。 无效__cyg_profile_func_enter(无效 *this_fn, 无效 *call_site); 无效__cyg_profile_func_exit(无效 *this_fn, 无效 *call_site);

我希望为每个"基本块"提供这样的东西,以便我可以动态记录每个分支的执行。

我该怎么做?

有一个名为American Fuzzy Lop的模糊器,它解决了非常相似的问题,即在基本块之间检测跳跃以收集边缘覆盖:如果基本块是顶点,则在执行过程中遇到了什么跳跃(边)。可能值得看看它的来源。它有三种方法:

  • afl-gcc是 gcc 的包装器,它用根据基本块标签和跳转指令重写汇编代码的包装器替换as
  • Clang编译器插件
  • 用于检测已编译代码的 QEMU 补丁

另一个可能也是最简单的选择可能是使用 DynamoRIO 动态仪表系统。与 QEMU 不同,它是专门为实现自定义检测而设计的(无论是手动重写机器代码,还是简单地插入调用,如果我的文档正确,在某些情况下甚至可能自动内联)。如果你认为动态检测是非常困难的,看看它们的例子——它们只有大约 100-200 行(但你仍然需要至少在这里阅读他们的文档,以及使用的函数,因为它可能包含重要的点:例如 DR 构造动态基本块,这与编译器的经典基本块不同)。通过动态检测,您甚至可以检测已使用的系统库。如果它不是您想要的,您可以使用类似的东西

static module_data_t *traced_module;
// in dr_client_main
traced_module = dr_get_main_module();
// in basic block event handler
void *app_pc = dr_fragment_app_pc(tag);
if (!dr_module_contains_addr(traced_module, app_pc)) {
return DR_EMIT_DEFAULT;
}