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

AMD机器SymFromAddr调用失败:访问无效地址问题求助

从EXCEPTION_POINTER获取未处理异常调用栈时AMD机器SymFromAddr报错的解决方法

问题描述

现有代码用于从EXCEPTION_POINTER的CONTEXT结构中提取未处理异常的调用栈,在Intel x86/x64架构Windows机器上运行正常,但在AMD x64机器上调用SymFromAddr时抛出错误:Attempt to access Invalid address。

原代码

struct StackFrame
{
    DWORD64 address;
    std::string name;
    std::string module;
    std::string filename;
    int line_number;
};

std::vector<StackFrame> GetStackTrace(CONTEXT context)
{
    DWORD  machine = IMAGE_FILE_MACHINE_I386;
    HANDLE process = GetCurrentProcess();
    HANDLE thread  = GetCurrentThread();

    STACKFRAME64 frame   = {};
    IMAGEHLP_LINE64* line = nullptr;
    DWORD disp;

    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS);
    if (SymInitialize(process, NULL, TRUE) == FALSE)
    {
        return std::vector<StackFrame>(); 
    }

#ifdef _M_IX86
    machine                 = IMAGE_FILE_MACHINE_I386;
    frame.AddrPC.Offset     = context.Eip;
    frame.AddrPC.Mode       = AddrModeFlat;
    frame.AddrFrame.Offset  = context.Ebp;
    frame.AddrFrame.Mode    = AddrModeFlat;
    frame.AddrStack.Offset  = context.Esp;
    frame.AddrStack.Mode    = AddrModeFlat;
#elif _M_X64
    machine                 = IMAGE_FILE_MACHINE_AMD64;
    frame.AddrPC.Offset     = context.Rip;
    frame.AddrPC.Mode       = AddrModeFlat;
    frame.AddrFrame.Offset  = context.Rsp;
    frame.AddrFrame.Mode    = AddrModeFlat;
    frame.AddrStack.Offset  = context.Rsp;
    frame.AddrStack.Mode    = AddrModeFlat;
#elif _M_IA64
    machine                 = IMAGE_FILE_MACHINE_IA64;
    frame.AddrPC.Offset     = context.StIIP;
    frame.AddrPC.Mode       = AddrModeFlat;
    frame.AddrFrame.Offset  = context.IntSp;
    frame.AddrFrame.Mode    = AddrModeFlat;
    frame.AddrBStore.Offset = context.RsBSP;
    frame.AddrBStore.Mode   = AddrModeFlat;
    frame.AddrStack.Offset  = context.IntSp;
    frame.AddrStack.Mode    = AddrModeFlat;
#else
#error "This platform is not supported."
#endif

    std::vector<StackFrame> frames;
    while (StackWalk64(machine, process, thread, &frame, &context , NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
    {
        StackFrame f;
        f.address = frame.AddrPC.Offset;

        DWORD64 moduleBase = SymGetModuleBase64(process, frame.AddrPC.Offset);

        char moduelBuff[MAX_PATH];            
        if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduelBuff, MAX_PATH))
        {
            f.module = moduelBuff;
        }
        else
        {
            f.module = "Unknown Module";
        }

        DWORD64 offset = 0;
        char symbolBuff[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];   
        PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbolBuff;

        symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        symbol->MaxNameLen   = MAX_SYM_NAME;

        if (SymFromAddr(process, frame.AddrPC.Offset, &offset, symbol))
        {
            f.name = symbol->Name;
            
            SymSetOptions(SYMOPT_LOAD_LINES);

            line = (IMAGEHLP_LINE64*)malloc(sizeof(IMAGEHLP_LINE64));
            line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);

            if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &disp, line))
            {
                f.filename = line->FileName;
                f.line_number = line->LineNumber;
            }
            else
            {
                std::cout << "Error occured in SymGetLineFromAddr64 : Reason :: " << std::system_category().message(GetLastError()) << "\n";
            }

            free(line);
        }
        else
        {
            DWORD error = GetLastError();
            std::cout << "SymFromAddr Failed. Err Reason : " << std::system_category().message(error) << "\n";

            f.name = "Unknown Function";
        }

        frames.push_back(f);
    }

    SymCleanup(process);

    return frames;
}

问题原因及修复方案

核心原因

AMD x64架构下,StackWalk64可能返回一些无效的用户态地址(比如超出进程地址空间、属于内核态区域的地址),直接传入SymFromAddr会触发地址访问错误。此外,符号加载选项的时机设置不当也会加剧问题。

具体修复步骤

  1. 过滤无效地址
    在调用SymFromAddr前,通过VirtualQueryEx验证地址是否为当前进程的有效可读地址,跳过无效帧:

    MEMORY_BASIC_INFORMATION64 mbi;
    if (!VirtualQueryEx(process, (LPCVOID)frame.AddrPC.Offset, (PMEMORY_BASIC_INFORMATION)&mbi, sizeof(mbi))) {
        continue;
    }
    // 仅处理已提交且可读/可执行的用户态地址
    if ((mbi.State != MEM_COMMIT) || !(mbi.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE))) {
        continue;
    }
    
  2. 统一符号加载选项
    将所有符号选项在SymInitialize前一次性设置,避免循环内重复修改:

    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS);
    
  3. 修复内存分配与拼写问题

    • 修正moduelBuff的拼写错误为moduleBuff;
    • 将IMAGEHLP_LINE64的内存分配移到循环外,避免重复malloc/free:
      // 循环外初始化栈分配的结构,替代动态分配
      IMAGEHLP_LINE64 line = {0};
      line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
      
  4. 跳过空地址帧
    检查frame.AddrPC.Offset是否为0,直接跳过无效帧。

修改后的完整代码

#include <Windows.h>
#include <DbgHelp.h>
#include <vector>
#include <string>
#include <iostream>

#pragma comment(lib, "Dbghelp.lib")

struct StackFrame
{
    DWORD64 address;
    std::string name;
    std::string module;
    std::string filename;
    int line_number;
};

std::vector<StackFrame> GetStackTrace(CONTEXT context)
{
    DWORD  machine = IMAGE_FILE_MACHINE_I386;
    HANDLE process = GetCurrentProcess();
    HANDLE thread  = GetCurrentThread();

    STACKFRAME64 frame   = {};
    IMAGEHLP_LINE64 line = {0};
    line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    DWORD disp;

    // 一次性设置所有符号选项
    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS);
    if (SymInitialize(process, NULL, TRUE) == FALSE)
    {
        return std::vector<StackFrame>(); 
    }

#ifdef _M_IX86
    machine                 = IMAGE_FILE_MACHINE_I386;
    frame.AddrPC.Offset     = context.Eip;
    frame.AddrPC.Mode       = AddrModeFlat;
    frame.AddrFrame.Offset  = context.Ebp;
    frame.AddrFrame.Mode    = AddrModeFlat;
    frame.AddrStack.Offset  = context.Esp;
    frame.AddrStack.Mode    = AddrModeFlat;
#elif _M_X64
    machine                 = IMAGE_FILE_MACHINE_AMD64;
    frame.AddrPC.Offset     = context.Rip;
    frame.AddrPC.Mode       = AddrModeFlat;
    frame.AddrFrame.Offset  = context.Rsp;
    frame.AddrFrame.Mode    = AddrModeFlat;
    frame.AddrStack.Offset  = context.Rsp;
    frame.AddrStack.Mode    = AddrModeFlat;
#elif _M_IA64
    machine                 = IMAGE_FILE_MACHINE_IA64;
    frame.AddrPC.Offset     = context.StIIP;
    frame.AddrPC.Mode       = AddrModeFlat;
    frame.AddrFrame.Offset  = context.IntSp;
    frame.AddrFrame.Mode    = AddrModeFlat;
    frame.AddrBStore.Offset = context.RsBSP;
    frame.AddrBStore.Mode   = AddrModeFlat;
    frame.AddrStack.Offset  = context.IntSp;
    frame.AddrStack.Mode    = AddrModeFlat;
#else
#error "This platform is not supported."
#endif

    std::vector<StackFrame> frames;
    while (StackWalk64(machine, process, thread, &frame, &context , NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
    {
        // 跳过空地址帧
        if (frame.AddrPC.Offset == 0) {
            continue;
        }

        // 验证地址有效性
        MEMORY_BASIC_INFORMATION64 mbi;
        if (!VirtualQueryEx(process, (LPCVOID)frame.AddrPC.Offset, (PMEMORY_BASIC_INFORMATION)&mbi, sizeof(mbi))) {
            continue;
        }
        if ((mbi.State != MEM_COMMIT) || !(mbi.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE))) {
            continue;
        }

        StackFrame f;
        f.address = frame.AddrPC.Offset;

        DWORD64 moduleBase = SymGetModuleBase64(process, frame.AddrPC.Offset);

        char moduleBuff[MAX_PATH];            
        if (moduleBase && GetModuleFileNameA((HINSTANCE)moduleBase, moduleBuff, MAX_PATH))
        {
            f.module = moduleBuff;
        }
        else
        {
            f.module = "Unknown Module";
        }

        DWORD64 offset = 0;
        char symbolBuff[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];   
        PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbolBuff;

        symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        symbol->MaxNameLen   = MAX_SYM_NAME;

        if (SymFromAddr(process, frame.AddrPC.Offset, &offset, symbol))
        {
            f.name = symbol->Name;
            
            if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &disp, &line))
            {
                f.filename = line.FileName;
                f.line_number = line.LineNumber;
            }
            else
            {
                std::cout << "Error occured in SymGetLineFromAddr64 : Reason :: " << std::system_category().message(GetLastError()) << "\n";
            }
        }
        else
        {
            DWORD error = GetLastError();
            std::cout << "SymFromAddr Failed. Err Reason : " << std::system_category().message(error) << "\n";
            f.name = "Unknown Function";
        }

        frames.push_back(f);
    }

    SymCleanup(process);
    return frames;
}

内容的提问来源于stack exchange,提问作者Hari E

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 08:02:46