1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
#include "stdafx.h"
#include "Seh.h"
#if defined(WINDOWS) && defined(X86)
#include "SafeSeh.h"
#include "Code/Binary.h"
#include "Code/FnState.h"
#include "Utils/Memory.h"
namespace code {
namespace eh {
/**
* Stack frame on x86:
*/
struct OnStack {
// SEH chain.
OnStack *prev;
const void *sehHandler;
// Pointer to the running code. This is so that we are able to extract the location of
// the Binary object during unwinding.
void *self;
// The topmost active block and active variables.
Nat activePartVars;
// Current EBP points to this.
void *lastEbp;
// Get ebp.
inline void *ebp() {
return &lastEbp;
}
// Create from base pointer:
static OnStack *fromEBP(void *ebp) {
byte *p = (byte *)ebp;
return (OnStack *)(p - OFFSET_OF(OnStack, lastEbp));
}
// Get SEH address.
inline void *seh() {
return &prev;
}
// Create from SEH pointer:
static OnStack *fromSEH(void *seh) {
byte *p = (byte *)seh;
return (OnStack *)(p - OFFSET_OF(OnStack, prev));
}
};
SehFrame extractFrame(_EXCEPTION_RECORD *er, void *frame, _CONTEXT *ctx, void *dispatch) {
OnStack *onStack = OnStack::fromSEH(frame);
SehFrame result;
result.stackPtr = onStack->ebp();
result.frameOffset = 0;
result.binary = codeBinary(onStack->self);
decodeFnState(onStack->activePartVars, result.part, result.activation);
return result;
}
// Called when we catch an exception. Called from a shim in assembler located in SafeSeh.asm
extern "C"
void *x86SEHCleanup(OnStack *onStack, storm::Nat cleanUntil, void *exception) {
// Perform a partial cleanup of the frame:
SehFrame frame;
frame.stackPtr = onStack->ebp();
frame.frameOffset = 0;
frame.binary = codeBinary(onStack->self);
decodeFnState(onStack->activePartVars, frame.part, frame.activation);
Nat part = cleanupPartialFrame(frame, cleanUntil);
// Update the state.
onStack->activePartVars = encodeFnState(part, frame.activation);
return exception;
}
void resumeFrame(SehFrame &frame, Binary::Resume &resume, storm::RootObject *object,
_CONTEXT *ctx, _EXCEPTION_RECORD *er, void *dispatch) {
OnStack *onStack = OnStack::fromEBP(frame.stackPtr);
// Note: we could just let RtlUnwind return from the exception handler for us.
// Note: In contrast to on 64-bit Windows, the RtlUnwind function does not traverse the
// caller's frame.
// First, we need to unwind the stack:
er->ExceptionFlags |= EXCEPTION_UNWINDING;
x86Unwind(er, onStack->seh());
er->ExceptionFlags &= EXCEPTION_UNWINDING;
// Clear the noncontinuable flag. Otherwise, we can't continue from the exception.
er->ExceptionFlags &= ~DWORD(EXCEPTION_NONCONTINUABLE);
// Build a stack "frame" that executes 'x86EhEntry' and returns to the resume point with Eax
// set as intended. This approach places all data in registers, so that data on the stack is
// not clobbered if we need it. It is also nice, as we don't have to think too carefully about
// calling conventions and stack manipulations.
ctx->Ebp = (UINT_PTR)frame.stackPtr;
ctx->Esp = ctx->Ebp + resume.stackOffset;
// Run.
ctx->Eip = (UINT_PTR)&x86HandleException;
// Return to.
ctx->Edx = (UINT_PTR)resume.ip;
// Exception.
ctx->Eax = (UINT_PTR)object;
// Current part.
ctx->Ebx = (UINT_PTR)resume.cleanUntil;
// Current frame.
ctx->Ecx = (UINT_PTR)onStack;
// Note: We also need to restore the EH chain (fs:[0]). The ASM shim does this for us.
}
void cleanupPartialFrame(SehFrame &, _EXCEPTION_RECORD *) {
// Not used on x86.
}
}
}
#else
// Symbol to avoid warning when building on 64-bit systems.
// Defined in SafeSeh.h
void x86SafeSEH() {}
#endif
|