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会触发地址访问错误。此外,符号加载选项的时机设置不当也会加剧问题。
具体修复步骤
过滤无效地址
在调用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; }统一符号加载选项
将所有符号选项在SymInitialize前一次性设置,避免循环内重复修改:SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS);修复内存分配与拼写问题
- 修正
moduelBuff的拼写错误为moduleBuff; - 将
IMAGEHLP_LINE64的内存分配移到循环外,避免重复malloc/free:// 循环外初始化栈分配的结构,替代动态分配 IMAGEHLP_LINE64 line = {0}; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
- 修正
跳过空地址帧
检查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
相关产品推荐
相关产品推荐

