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:
Win32 - Backtrace from C code
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...
You have set symbol.MaxNameLength
to 255, but you allocated "symbol" on the stack with IMAGEHLP_SYMBOL64 symbol;
. That type is defined as:
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 page for the structure says (emphasis added):
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.
Check out the Stackwalker project on codeplex - it's open source. Works nicely.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With