You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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并非直接调用系统库的原生函数:

  1. 编译器的桩函数替换:x64版本的MSVC默认会将用户代码中的malloc/free替换为跳转桩,直接跳转到msvcrt.dll的原生实现。用户模块中的malloc符号对应的只是一个极小的跳转代码,没有独立的返回指令——Pin的RTN_InsertCall在IPOINT_AFTER插桩时,无法捕捉到这个桩的返回流程。
  2. 系统库函数的完整性:msvcrt.dll中的malloc/free是完整的函数实现,有明确的返回指令,因此Pin能正常识别并插桩返回路径。
  3. 跨平台差异: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 09:20:41