Windows 上的 StackWalk64 - 获取符号名称
好吧,这是一天内关于 SO 的第二个问题。看起来 Windows 编程让我很高兴... : S
我目前正在尝试获取 Win32 可执行文件上的函数调用堆栈。
今天早上我也问了这个问题:
现在,我很确定StackWalk64 函数是实现这一点的关键。 我读过一些关于如何使用它的文章以及 MS 文档。
它实际上在我的测试程序上显示帧,所以它有点工作......
问题是我无法从堆栈信息中检索符号名称。
为此,我使用 SymGetSymFromAddr64
函数和 UnDecorateSymbolName
。但我只得到垃圾角色。
这是我的代码。希望它不会太混乱,因为我不习惯 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;
}
}
}
实际输出是:
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
顺便说一句,堆栈地址总是 0 似乎很奇怪...任何帮助表示赞赏:)
谢谢大家!
编辑
我正在寻找一个简单的C解决方案,没有第三方库......
Alright, second question on SO in one day. Looks like Windows programming makes me happy... : S
I'm currently trying to get the function call stack on a Win32 executable.
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.
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.
Here's my code. Hope its not to messy, as I'm not used to Windows programming:
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 : )
Thanks to everyone!
EDIT
I'm looking for a plain C solution, without third party libraries...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
您已将
symbol.MaxNameLength
设置为 255,但您在堆栈上使用IMAGEHLP_SYMBOL64 symbol;
分配了“symbol”。该类型定义为:请注意,默认情况下“名称”字段只有一个字符。如果您想存储更大的名称,您需要执行以下操作:
否则,
SymGetSymFromAddr64()
可能会覆盖内存。以下是该结构的帮助页面的内容(强调额外):You have set
symbol.MaxNameLength
to 255, but you allocated "symbol" on the stack withIMAGEHLP_SYMBOL64 symbol;
. That type is defined as:Notice that the Name field only has one character by default. If you want to store bigger names, you need to do something like:
Otherwise,
SymGetSymFromAddr64()
is likely to overwrite memory. Here is what the help page for the structure says (emphasis added):查看 codeplex 上的 Stackwalker 项目 - 它是开源的。效果很好。
Check out the Stackwalker project on codeplex - it's open source. Works nicely.
我使用了您的代码,一开始它也不起作用,直到我在文档中注意到您首先需要调用 SymInitialize,例如 SymInitialize(process, NULL, TRUE) 。您可以在 RtlCaptureContext 之前调用它。
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.
首先需要解决两个问题:
1) 正如 AShelly 所指出的,名称需要预先分配。您不需要 malloc 来执行此操作:
2) 在 32 位版本中使用 RtlCaptureContext() 获取上下文是不行的。如果您有 64 位机器,请将 IMAGE_FILE_MACHINE_I386 更改为适当的 64 位类型。如果您有 32 位版本,则使用内联汇编来正确设置 EBP、ESP 和 EIP。这是一种方法:
次要问题 - SymGetSymFromAddr64 可以,但建议使用 SymFromAddr。
祝所有 Windows 上的跟踪堆栈好运。
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:
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:
Minor point - SymGetSymFromAddr64 is ok, but it is recommended to use SymFromAddr instead.
Good luck to all those tracing stack on Windows.
请参阅本质上相同问题的答案:
https://stackoverflow.com/a/28276227/10592
请注意您需要确保您的用户拥有 .pdb 文件,并且他们的进程可以找到它 - 请参阅该答案以获取更多详细信息。
See this answer to what is essentially the same question:
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.
除了在结构中分配足够的空间并正确设置结构尺寸之外。符号解析器未初始化。
因为这里的答案不是很好,并且后续的链接导致了一个使简单的 stackwalk 变得过于复杂的代码项目。我决定发布我修改后的代码。 OP 非常接近于拥有一个可工作的功能。
下面是修改后的代码,它为 win32 应用程序生成正确的堆栈遍历:
In addition to allocating enough space in the structure and setting structure size correctly. The symbol resolver was not initialized.
Because the answers here were not great and the subsequent links lead to a codeproject that overcomplicates a simple stackwalk. I decided to post my modified code. OP was really close to having a working function.
Here is the modified code, which produces a correct stack walk for a win32 app: