插桩

插桩

可以利用 clang/gcc 的 -finstrument-functions 编译参数,使得每个函数都会在出口和入口以此执行 __cyg_profile_func_enter 函数和 __cyg_profile_func_exit 函数。

使用 dladdr 将函数的地址转换成符号

hook.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <dlfcn.h>
#include <stdio.h>
#include <cxxabi.h>

inline const char* get_funcname(const char* src) {
int status = 99;
const char* f = abi::__cxa_demangle(src, nullptr, nullptr, &status);
return f == nullptr ? src : f;
}

extern "C" void __cyg_profile_func_enter(void* func, void* caller) {
Dl_info info1, info2;
if (dladdr(func, &info1) & dladdr(caller, &info2)) {
fprintf(stdout, "enter func: %s father: %s\n", get_funcname(info1.dli_sname), get_funcname(info2.dli_sname));
}
}

extern "C" void __cyg_profile_func_exit(void* func, void* caller) {
Dl_info info1, info2;
if (dladdr(func, &info1) & dladdr(caller, &info2)) {
fprintf(stdout, "exit func: %s father: %s\n", get_funcname(info1.dli_sname), get_funcname(info2.dli_sname));
}
}

main.cpp

1
2
3
4
5
6
7
8
9
10
void func_02() {}

void func_01() {
func_02();
}

int main() {
func_01();
return 0;
}

编译 hook.cpp 如:g++ hook.cpp -c

然后链接到要被插桩的项目或代码中:g++ main.cpp hook.o -g -finstrument-functions -ldl -rdynamic

输出:

1
2
3
4
5
6
enter func: main father: __libc_start_main
enter func: func_01() father: main
enter func: func_02() father: func_01()
exit func: func_02() father: func_01()
exit func: func_01() father: main
exit func: main father: __libc_start_main