Exceptional Win64 stack not displaying entries

When reading Win64 structured exceptions on a trace (from Programming Versus x64 Exception Handling Support, Part 7: Combining All of This or Building a Stack Procedure ), I converted the code to StackWalk64.cpp .

procedure DumpExceptionStack(); var LContext : CONTEXT; LUnwindHistoryTable : _UNWIND_HISTORY_TABLE; LRuntimeFunction : Pointer; LImageBase : ULONGLONG; HandlerData : Pointer; EstablisherFrame : ULONG64; NvContext : KNONVOLATILE_CONTEXT_POINTERS; LLineNumber : integer; LModuleName : UnicodeString; LPublicAddr : pointer; LPublicName : UnicodeString; LUnitName : UnicodeString; begin // // First, we'll get the caller context. // RtlCaptureContext(LContext); // // Initialize the (optional) unwind history table. // LUnwindHistoryTable := Default(_UNWIND_HISTORY_TABLE); // LUnwindHistoryTable.Unwind := True; // // This unwind loop intentionally skips the first call frame, as it shall // correspond to the call to StackTrace64, which we aren't interested in. // repeat // // Try to look up unwind metadata for the current function. // LRuntimeFunction := RtlLookupFunctionEntry(LContext.Rip, LImageBase, LUnwindHistoryTable); NvContext := Default(KNONVOLATILE_CONTEXT_POINTERS); if not Assigned(LRuntimeFunction) then begin // // If we don't have a RUNTIME_FUNCTION, then we've encountered // a leaf function. Adjust the stack approprately. // //LContext.Rip := (ULONG64)(*(PULONG64)Context.Rsp); LContext.Rip := ULONG64(Pointer(LContext.Rsp)^); LContext.Rsp := LContext.Rsp + 8; end else begin // // Otherwise, call upon RtlVirtualUnwind to execute the unwind for // us. // RtlVirtualUnwind(UNW_FLAG_NHANDLER, LImageBase, LContext.Rip, LRuntimeFunction, LContext, HandlerData, EstablisherFrame, NvContext); end; // // If we reach an RIP of zero, this means that we've walked off the end // of the call stack and are done. // if LContext.Rip = 0 then Break; // // Display the context. Note that we don't bother showing the XMM // context, although we have the nonvolatile portion of it. // if madMapFile.GetMapFileInfos(Pointer(LContext.Rip), LModuleName, LUnitName, LPublicName, LPublicAddr, LLineNumber) then begin Writeln(Format('%p %s.%s %d', [Pointer(LContext.Rip), LUnitName, LPublicName, LLineNumber{, LSEHType}])); end; until LContext.Rip = 0; end; 

Then I call it with the following:

 procedure Main(); begin try try try try DumpExceptionStack(); finally // end; except on E : Exception do raise end; except on E : Exception do raise end; except on E : Exception do raise end; end; 

When I run the application (just a console application), I get only one entry for Main , but I expected that there would be four (three nested exceptions and one, finally).

Maybe I misinterpreted and that DumpExceptionStack will only give the result that interests me when the exception is thrown? If so, what would be the necessary changes to get all exception stacks (if possible) - i.e. have four outputs for Main ?

+4
source share
1 answer

The x64 exclusion model is table-based, unlike the stack-based x86 model. This means that exception stacks do not exist. In any case, I have never seen a routine walk that tries to throw an exception and finally block. This is no different. It performs a function call stack.

The flow of exceptions within a single function is controlled by area tables. In your function, if your code DumpExceptionStack exception at the point where there are calls to DumpExceptionStack , then several area table entries correspond to the location of the exception. The exception is handled by the innermost matching area. The distance between the start and end addresses of a region can be used to determine which region is the innermost one. If this innermost scope does not handle the exception or raises it again, the next innermost scope is requested. And so on, until all matching areas for the function are exhausted.

+3
source

All Articles