Windows 上的 StackWalk64 - 获取符号名称
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5705650/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
StackWalk64 on Windows - Get symbol name
提问by Macmade
Alright, second question on SO in one day. Looks like Windows programming makes me happy... : S
好的,一天之内关于 SO 的第二个问题。看起来 Windows 编程让我很开心...:S
I'm currently trying to get the function call stack on a Win32 executable.
我目前正在尝试获取 Win32 可执行文件上的函数调用堆栈。
This morning, I've also asked a question about this:
今天早上,我也问了一个关于这个的问题:
Now, I'm pretty sure that the StackWalk64
function is the key for this.
I've read some articles on how to use it, as well as the MS documentation.
现在,我很确定StackWalk64
函数是实现此目的的关键。我已经阅读了一些关于如何使用它的文章,以及 MS 文档。
It actually displays frames on my test program, so it kinda work...
它实际上在我的测试程序上显示帧,所以它有点工作......
The problem is that I'm not able to retrieve the symbol name from the stack informations.
问题是我无法从堆栈信息中检索符号名称。
I'm using the SymGetSymFromAddr64
function for this, with UnDecorateSymbolName
. But I only get junk characters.
我正在SymGetSymFromAddr64
为此使用该功能,带有UnDecorateSymbolName
. 但我只得到垃圾字符。
Here's my code. Hope its not to messy, as I'm not used to Windows programming:
这是我的代码。希望不要乱七八糟,因为我不习惯 Windows 编程:
void printStack( void )
{
BOOL result;
HANDLE process;
HANDLE thread;
CONTEXT context;
STACKFRAME64 stack;
ULONG frame;
IMAGEHLP_SYMBOL64 symbol;
DWORD64 displacement;
char name[ 256 ];
RtlCaptureContext( &context );
memset( &stack, 0, sizeof( STACKFRAME64 ) );
process = GetCurrentProcess();
thread = GetCurrentThread();
displacement = 0;
stack.AddrPC.Offset = context.Eip;
stack.AddrPC.Mode = AddrModeFlat;
stack.AddrStack.Offset = context.Esp;
stack.AddrStack.Mode = AddrModeFlat;
stack.AddrFrame.Offset = context.Ebp;
stack.AddrFrame.Mode = AddrModeFlat;
for( frame = 0; ; frame++ )
{
result = StackWalk64
(
IMAGE_FILE_MACHINE_I386,
process,
thread,
&stack,
&context,
NULL,
SymFunctionTableAccess64,
SymGetModuleBase64,
NULL
);
symbol.SizeOfStruct = sizeof( IMAGEHLP_SYMBOL64 );
symbol.MaxNameLength = 255;
SymGetSymFromAddr64( process, ( ULONG64 )stack.AddrPC.Offset, &displacement, &symbol );
UnDecorateSymbolName( symbol.Name, ( PSTR )name, 256, UNDNAME_COMPLETE );
printf
(
"Frame %lu:\n"
" Symbol name: %s\n"
" PC address: 0x%08LX\n"
" Stack address: 0x%08LX\n"
" Frame address: 0x%08LX\n"
"\n",
frame,
symbol.Name,
( ULONG64 )stack.AddrPC.Offset,
( ULONG64 )stack.AddrStack.Offset,
( ULONG64 )stack.AddrFrame.Offset
);
if( !result )
{
break;
}
}
}
The actual output is:
实际输出为:
Frame 0:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠
PC address: 0x00BA2763
Stack address: 0x00000000
Frame address: 0x0031F7E8
Frame 1:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠?
PC address: 0x00BB4FFF
Stack address: 0x00000000
Frame address: 0x0031F940
Frame 2:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠?
PC address: 0x00BB4E2F
Stack address: 0x00000000
Frame address: 0x0031F990
Frame 3:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠?
PC address: 0x75BE3677
Stack address: 0x00000000
Frame address: 0x0031F998
Frame 4:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠?
PC address: 0x770F9D72
Stack address: 0x00000000
Frame address: 0x0031F9A4
Frame 5:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠?
PC address: 0x770F9D45
Stack address: 0x00000000
Frame address: 0x0031F9E4
Frame 6:
Symbol name: ╠╠╠╠╠╠╠╠╠╠╠╠?
PC address: 0x770F9D45
Stack address: 0x00000000
Frame address: 0x0031F9E4
Seems weird that the stack address is always 0 by the way... Any help appreciated : )
顺便说一下,堆栈地址始终为 0 似乎很奇怪......感谢任何帮助:)
Thanks to everyone!
谢谢大家!
EDIT
编辑
I'm looking for a plain C solution, without third party libraries...
我正在寻找一个简单的 C 解决方案,没有第三方库......
采纳答案by AShelly
You have set symbol.MaxNameLength
to 255, but you allocated "symbol" on the stack with IMAGEHLP_SYMBOL64 symbol;
. That type is defined as:
您已设置symbol.MaxNameLength
为 255,但您使用IMAGEHLP_SYMBOL64 symbol;
. 该类型定义为:
typedef struct _IMAGEHLP_SYMBOL64 {
DWORD SizeOfStruct;
DWORD64 Address;
DWORD Size;
DWORD Flags;
DWORD MaxNameLength;
TCHAR Name[1];
} IMAGEHLP_SYMBOL64;
Notice that the Name field only has one character by default. If you want to store bigger names, you need to do something like:
请注意,名称字段默认只有一个字符。如果要存储更大的名称,则需要执行以下操作:
const int MaxNameLen = 255;
IMAGEHLP_SYMBOL64* pSymbol =
malloc(sizeof(IMAGEHLP_SYMBOL64)+MaxNameLen*sizeof(TCHAR));
pSymbol->MaxNameLength = MaxNameLen;
Otherwise, SymGetSymFromAddr64()
is likely to overwrite memory. Here is what the help pagefor the structure says (emphasis added):
否则,SymGetSymFromAddr64()
很可能会覆盖内存。以下是该结构的帮助页面内容(已添加强调):
MaxNameLength: The maximum length of the string that the Name member can contain, in characters, not including the null-terminating character. Because symbol names can vary in length, this data structure is allocated by the caller. This member is used so the library knows how much memory is available for use by the symbol name.
MaxNameLength:Name 成员可以包含的字符串的最大长度,以字符为单位,不包括空终止字符。由于符号名称的长度可能不同,因此该数据结构 由调用者分配。使用这个成员是为了让库知道有多少内存可供符号名称使用。
回答by Cheeso
Check out the Stackwalker project on codeplex- it's open source. Works nicely.
查看codeplex 上的Stackwalker 项目- 它是开源的。很好用。
回答by Muqker
I used your code and it also didn't work at first, until I noticed in the documentation that you first need to call SymInitialize, like SymInitialize(process, NULL, TRUE) . You can call this before RtlCaptureContext.
我使用了你的代码,它起初也不起作用,直到我在文档中注意到你首先需要调用 SymInitialize,比如 SymInitialize(process, NULL, TRUE) 。您可以在 RtlCaptureContext 之前调用它。
回答by Sergey D
There are two problems to address first:
首先有两个问题需要解决:
1) Name needs to be preallocated as pointed out by AShelly. You don't need malloc to do it:
1) 如 AShelly 所指出的,名称需要预先分配。你不需要 malloc 来做到这一点:
#define MY_MAX_SYM_LEN 255
printStack()
{
struct sym_pack_tag {
IMAGEHLP_SYMBOL64 sym;
char name[MY_MAX_SYM_LEN];
} sym_pack;
IMAGEHLP_SYMBOL64 *symbol = &sym_pack.sym;
...
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64 );
symbol->MaxNameLength = MY_MAX_SYM_LEN;
if (!SymGetSymFromAddr64( process, stack.AddrPC.Offset, &displacement, symbol )) ...
2) Its not ok to use RtlCaptureContext() to get context in 32-bit builds. If you have 64-bit machine then change IMAGE_FILE_MACHINE_I386 to the appropriate 64-bit type. If you have 32-bit build then use inline assembly to correctly set EBP, ESP and EIP. Here is one way to do it:
2) 在 32 位构建中使用 RtlCaptureContext() 获取上下文是不行的。如果您有 64 位机器,则将 IMAGE_FILE_MACHINE_I386 更改为适当的 64 位类型。如果您有 32 位版本,则使用内联汇编正确设置 EBP、ESP 和 EIP。这是一种方法:
__declspec(naked) void WINAPI CaptureContext_X86ControlOnly(CONTEXT *context) {
__asm {
push ebp
mov ebp, esp
mov ecx, context //ecx = [ebp + 8]
pop ebp //restore old frame
pop eax //pop return address
pop ecx //pop context as WINAPI needs. Note: ecx will stay the same
mov [ecx]CONTEXT.ContextFlags, CONTEXT_CONTROL
mov [ecx]CONTEXT.Ebp, ebp
mov [ecx]CONTEXT.Eip, eax
mov [ecx]CONTEXT.Esp, esp
jmp eax
}
} //I'm writing from my memory - so step through the code above to double check.
Minor point - SymGetSymFromAddr64 is ok, but it is recommended to use SymFromAddr instead.
次要问题 - SymGetSymFromAddr64 没问题,但建议改用 SymFromAddr。
Good luck to all those tracing stack on Windows.
祝所有在 Windows 上跟踪堆栈的人好运。
回答by Tim Cooper
See this answer to what is essentially the same question:
请参阅此对本质上相同问题的回答:
https://stackoverflow.com/a/28276227/10592
https://stackoverflow.com/a/28276227/10592
Note that you need to make sure your users have the .pdb file, and that their process can find it - see that answer for more details.
请注意,您需要确保您的用户拥有 .pdb 文件,并且他们的进程可以找到它 - 有关更多详细信息,请参阅该答案。