c - StackWalk64 på Windows - Få symbolnavn

Indlæg af Hanne Mølgaard Plasc

Problem



Okay, andet spørgsmål om så på en dag. Ser ud som Windows programmering gør mig glad ...: S


Jeg forsøger nu at få funktionssamtalen stakken på en Win32 eksekverbar.


I morges har jeg også stillet et spørgsmål om dette:



  Win32 - Backtrace fra C-kode



Nu er jeg ganske sikker på, at funktionen StackWalk64 er nøglen til dette.
Jeg har læst nogle artikler om, hvordan man bruger det, samt MS-dokumentationen.


Det viser faktisk rammer på mit testprogram, så det er lidt arbejde ...


Problemet er, at jeg ikke kan hente symbolnavnet fra stakinformationerne.


Jeg bruger funktionen SymGetSymFromAddr64 til dette, med UnDecorateSymbolName. Men jeg får kun junk-tegn.


Her er min kode. Håber det ikke at være rodet, da jeg ikke er vant til Windows programmering:


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:
"
            "    Symbol name:    \%s
"
            "    PC address:     0x\%08LX
"
            "    Stack address:  0x\%08LX
"
            "    Frame address:  0x\%08LX
"
            "
",
            frame,
            symbol.Name,
            ( ULONG64 )stack.AddrPC.Offset,
            ( ULONG64 )stack.AddrStack.Offset,
            ( ULONG64 )stack.AddrFrame.Offset
        );

        if( !result )
        {
            break;
        }
    }
}


Den faktiske produktion er:


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


Synes mærkeligt, at stakadressen altid er 0 forresten ... Enhver hjælp værdsat:)


Tak til alle!


EDIT


Jeg leder efter en almindelig C-løsning uden biblioteker fra tredjeparter ...

Bedste reference


Du har indstillet symbol.MaxNameLength til 255, men du tildelte 'symbol' på stakken med IMAGEHLP\_SYMBOL64 symbol;. Denne type er defineret som:


typedef struct \_IMAGEHLP\_SYMBOL64 {
  DWORD   SizeOfStruct;
  DWORD64 Address;
  DWORD   Size;
  DWORD   Flags;
  DWORD   MaxNameLength;
  TCHAR   Name[1];
} IMAGEHLP\_SYMBOL64;


Bemærk, at feltet Navn kun har et tegn som standard. Hvis du vil gemme større navne, skal du gøre noget som:


 const int MaxNameLen = 255;
 IMAGEHLP\_SYMBOL64* pSymbol = 
       malloc(sizeof(IMAGEHLP\_SYMBOL64)+MaxNameLen*sizeof(TCHAR));
 pSymbol->MaxNameLength = MaxNameLen;


Ellers vil SymGetSymFromAddr64() sandsynligvis overskrive hukommelse. Her er hvad hjælpesiden for strukturen siger (vægt tilføjet): [14]



   MaxNameLength : Den maksimale længde på
  den streng, som navnet medlemmet kan
  indeholder, i tegn, ikke inklusive
  nulstillende karakter.
  Fordi symbolnavne kan variere i
  længde, denne datastruktur er
   tildelt af den, der ringer op . Dette medlem
  bruges, så biblioteket ved, hvor meget
  hukommelse er tilgængelig til brug af
  symbolnavn.


Andre referencer 1


Tjek Stackwalker-projektet på codeplex - det er open source. Fungerer pænt. [15]

Andre referencer 2


Jeg brugte din kode, og den virkede heller ikke først, før jeg bemærkede i dokumentationen, at du først skal ringe til SymInitialize, ligesom SymInitialize (process, NULL, TRUE). Du kan ringe dette før RtlCaptureContext.

Andre referencer 3


Der er to problemer at behandle først:


1) Navn skal præallokeres som AShelly påpegede. Du behøver ikke malloc at gøre det:


#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) Det er ikke ok at bruge RtlCaptureContext () for at få kontekst i 32-bit builds. Hvis du har en 64-bit maskine, skal du ændre IMAGE\_FILE\_MACHINE\_I386 til den tilsvarende 64-bit type. Hvis du har 32-bit build, brug derefter inline assembly til korrekt sæt EBP, ESP og EIP. Her er en måde at gøre det på:


\_\_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.


Mindre punkt - SymGetSymFromAddr64 er ok, men det anbefales at bruge SymFromAddr i stedet.


Held og lykke til alle de sporingsstabler på Windows.

Andre referencer 4


Se dette svar på hvad der i det væsentlige er det samme spørgsmål:


https://stackoverflow.com/a/28276227/10592


Bemærk, at du skal sørge for, at dine brugere har .pdb-filen, og at deres proces kan finde den - se svaret for flere detaljer.