Intel Pin在Windows下malloc/free无法触发后置回调问题咨询
Windows下Intel Pin插桩malloc/free的After回调未触发问题
场景重现
基于Intel Pin 3.25开发了一款追踪malloc/free调用前后执行情况的Pintool,代码如下:
#include "pin.H" #include <iostream> VOID before_malloc(char* img_name, int size) { printf("--> malloc(%d) (%s)\n", size, img_name); } VOID after_malloc(char* img_name, ADDRINT ret) { printf("--> malloc returned %lx (%s)\n", ret, img_name); } VOID before_free(char* img_name, ADDRINT addr) { printf("--> free(%lx) (%s)\n", addr, img_name); } VOID after_free(char* img_name, int ret) { printf("--> free returned %d (%s)\n", ret, img_name); } VOID Image(IMG img, VOID* v) { RTN rtn; const char* img_name = IMG_Name(img).c_str(); // malloc rtn = RTN_FindByName(img, "malloc"); if (RTN_Valid(rtn)) { RTN_Open(rtn); RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)before_malloc, IARG_PTR, img_name, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)after_malloc, IARG_PTR, img_name, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); RTN_Close(rtn); } // free rtn = RTN_FindByName(img, "free"); if (RTN_Valid(rtn)) { RTN_Open(rtn); RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)before_free, IARG_PTR, img_name, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END); RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)after_free, IARG_PTR, img_name, IARG_FUNCRET_EXITPOINT_VALUE, IARG_END); RTN_Close(rtn); } } int main(int argc, char* argv[]) { if (PIN_Init(argc, argv)) { printf("PIN_Init failed\n"); return -1; } PIN_InitSymbols(); IMG_AddInstrumentFunction(Image, 0); PIN_StartProgram(); return 0; }
编写测试程序验证工具:
int main() { char* string = (char*)malloc(32 * sizeof(char)); strcpy(string, "Lughnatic"); printf("Your name is: %s\n", string); free(string); return 0; }
运行Pintool后得到输出(关键部分):
--> malloc(32) (C:\path\to\heap_demo.exe) Your name is: Lughnatic --> free(a3e7f630) (C:\path\to\heap_demo.exe) --> malloc(9327) (C:\WINDOWS\System32\msvcrt.dll) --> malloc returned b8781260 (C:\WINDOWS\System32\msvcrt.dll) ... // msvcrt.dll的malloc/free完整触发before/after回调
核心问题
从输出可见,用户程序(heap_demo.exe)调用的malloc/free仅触发了before_*回调,对应的after_malloc和after_free完全未执行;但系统库msvcrt.dll中的malloc/free却能正常触发前后回调。根据Pin文档,IPOINT_AFTER会插桩函数的所有返回路径,为何Windows下用户程序的malloc/free会出现这种差异?
原因分析
这是Windows MSVC编译环境的特性导致的,用户代码中的malloc/free并非直接调用系统库的原生函数:
- 编译器的桩函数替换:x64版本的MSVC默认会将用户代码中的
malloc/free替换为跳转桩,直接跳转到msvcrt.dll的原生实现。用户模块中的malloc符号对应的只是一个极小的跳转代码,没有独立的返回指令——Pin的RTN_InsertCall在IPOINT_AFTER插桩时,无法捕捉到这个桩的返回流程。 - 系统库函数的完整性:msvcrt.dll中的
malloc/free是完整的函数实现,有明确的返回指令,因此Pin能正常识别并插桩返回路径。 - 跨平台差异:Linux下的
malloc通常直接链接到libc的函数实现,没有这种本地跳转桩的逻辑,因此RTN级别的插桩可以正常工作。
解决方案
针对这个问题,可以通过以下方式修正:
- 调整编译选项:编译测试程序时添加
/Oi-禁用内联优化,或定义_NO_DEBUG_HEAP等宏,让malloc/free直接链接到系统库的原生函数,而非本地桩。 - 指令级插桩:改用Pin的
INS_AddInstrumentFunction,直接追踪所有调用malloc/free的call指令,而不是通过RTN_FindByName寻找模块内的函数符号。 - 追踪底层API:直接插桩Windows堆管理的底层API(如
HeapAlloc/HeapFree),因为用户态的malloc/free最终都会调用这些系统级接口,能覆盖所有堆操作场景。
内容的提问来源于stack exchange,提问作者Lught
相关产品推荐
相关产品推荐

