Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

556 lines
17 KiB

/***
*trnsctrl.cxx - Routines for doing control transfers
*
* Copyright (c) 1993-1994, Microsoft Corporation. All rights reserved.
*
*Purpose:
* Routines for doing control transfers; written using inline
* assembly in naked functions. Contains the public routine
* _CxxFrameHandler, the entry point for the frame handler
*
*Revision History:
* 05-24-93 BES Module created
* 01-13-95 JWM NLG notifications now called from _CallSettingFrame().
* 04-10-95 JWM _CallSettingFrame() moved to lowhelpr.asm
*
****/
#include <windows.h>
#include <mtdll.h>
#include <ehdata.h>
#include <trnsctrl.h>
#include <eh.h>
#include <ehhooks.h>
#pragma hdrstop
#include <setjmp.h>
/////////////////////////////////////////////////////////////////////////////
//
// _JumpToContinuation - sets up EBP and jumps to specified code address.
//
// Does not return.
//
// NT leaves a marker registration node at the head of the list, under the
// assumption that RtlUnwind will remove it. As it happens, we need to keep
// it in case of a rethrow (see below). We only remove the current head
// (assuming it is NT's), because there may be other nodes that we still
// need.
//
void __stdcall _JumpToContinuation(
void *target, // The funclet to call
EHRegistrationNode *pRN // Registration node, represents location of frame
) {
register long targetEBP;
#if !CC_EXPLICITFRAME
targetEBP = (long)pRN + FRAME_OFFSET;
#else
targetEBP = pRN->frame;
#endif
__asm {
//
// Unlink NT's marker node:
//
mov ebx, FS:[0]
mov eax, [ebx]
mov FS:[0], eax
//
// Transfer control to the continuation point
//
mov eax, target // Load target address
mov ebx, pRN // Restore target esp
mov esp, [ebx-4]
mov ebp, targetEBP // Load target frame pointer
jmp eax // Call the funclet
}
}
/////////////////////////////////////////////////////////////////////////////
//
// _CallMemberFunction0 - call a parameterless member function using __thiscall
// calling convention, with 0 parameters.
//
__declspec(naked) void __stdcall _CallMemberFunction0(
void *pthis, // Value for 'this' pointer
void *pmfn // Pointer to the member function
) {
__asm {
pop eax // Save return address
pop ecx // Get 'this'
xchg [esp],eax // Get function address, stash return address
jmp eax // jump to the function (function will return
// to caller of this func)
}
}
/////////////////////////////////////////////////////////////////////////////
//
// _CallMemberFunction1 - call a member function using __thiscall
// calling convention, with 1 parameter.
//
__declspec(naked) void __stdcall _CallMemberFunction1(
void *pthis, // Value for 'this' pointer
void *pmfn, // Pointer to the member function
void *pthat // Value of 1st parameter (type assumes copy ctor)
) {
__asm {
pop eax // Save return address
pop ecx // Get 'this'
xchg [esp],eax // Get function address, stash return address
jmp eax // jump to the function (function will return
// to caller of this func)
}
}
/////////////////////////////////////////////////////////////////////////////
//
// _CallMemberFunction2 - call a member function using __thiscall
// calling convention, with 2 parameter.
//
__declspec(naked) void __stdcall _CallMemberFunction2(
void *pthis, // Value for 'this' pointer
void *pmfn, // Pointer to the member function
void *pthat, // Value of 1st parameter (type assumes copy ctor)
int val2 // Value of 2nd parameter (type assumes copy ctor w/vb)
) {
__asm {
pop eax // Save return address
pop ecx // Get 'this'
xchg [esp],eax // Get function address, stash return address
jmp eax // jump to the function (function will return
// to caller of this func)
}
}
/////////////////////////////////////////////////////////////////////////////
//
// _UnwindNestedFrames - Call RtlUnwind, passing the address after the call
// as the continuation address.
//
// Win32 assumes that after a frame has called RtlUnwind, it will never return
// to the dispatcher.
//
// Let me explain:
// When the dispatcher calls a frame handler while searching
// for the appropriate handler, it pushes an extra guard registration node
// onto the list. When the handler returns to the dispatcher, the dispatcher
// assumes that its node is at the head of the list, restores esp from the
// address of the head node, and then unlinks that node from the chain.
// However, if RtlUnwind removes ALL nodes below the specified node, including
// the dispatcher's node, so without intervention the result is that the
// current subject node gets poped from the list, and the stack pointer gets
// reset to somewhere within the frame of that node, which is totally bogus
// (this last side effect is not a problem, because esp is then immediately
// restored from the ebp chain, which is still valid).
//
// So:
// To get arround this, WE ASSUME that the registration node at the head of
// the list is the dispatcher's marker node (which it is in NT 1.0), and
// we keep a handle to it when we call RtlUnwind, and then link it back in
// after RtlUnwind has done its stuff. That way, the dispatcher restores
// its stack exactly as it expected to, and leave our registration node alone.
//
// What happens if there is an exception during the unwind?
// We can't put a registration node here, because it will be removed
// immediately.
//
#pragma optimize("g", off) // WORKAROUND for DOLPH:3322
void __stdcall _UnwindNestedFrames(
EHRegistrationNode *pRN, // Unwind up to (but not including) this frame
EHExceptionRecord *pExcept // The exception that initiated this unwind
) {
void* pReturnPoint;
EHRegistrationNode *pDispatcherRN; // Magic!
__asm {
//
// Save the dispatcher's marker node
//
mov eax, dword ptr FS:[0]
mov pDispatcherRN, eax
}
__asm mov pReturnPoint, offset ReturnPoint
RtlUnwind(pRN, pReturnPoint, (PEXCEPTION_RECORD)pExcept, NULL);
ReturnPoint:
PER_FLAGS(pExcept) &= ~EXCEPTION_UNWINDING; // Clear the 'Unwinding' flag
// in case exception is rethrown
__asm {
//
// Re-link the dispatcher's marker node
//
mov eax, dword ptr FS:[0] // Get the current head
mov ebx, pDispatcherRN // Get the saved head
mov [ebx], eax // Link saved head to current head
mov dword ptr FS:[0], ebx // Make saved head current head
}
return;
}
#pragma optimize("", on)
/////////////////////////////////////////////////////////////////////////////
//
// __CxxFrameHandler - Real entry point to the runtime; this thunk fixes up
// the parameters, and then calls the workhorse.
//
extern "C" EXCEPTION_DISPOSITION __cdecl __InternalCxxFrameHandler(
EHExceptionRecord *pExcept, // Information for this exception
EHRegistrationNode *pRN, // Dynamic information for this frame
void *pContext, // Context info (we don't care what's in it)
DispatcherContext *pDC, // More dynamic info for this frame (ignored on Intel)
FuncInfo *pFuncInfo, // Static information for this frame
int CatchDepth, // How deeply nested are we?
EHRegistrationNode *pMarkerRN, // Marker node for when checking inside
// catch block
BOOL recursive); // True if this is a translation exception
extern "C" _CRTIMP __declspec(naked) EXCEPTION_DISPOSITION __cdecl __CxxFrameHandler(
/*
EAX=FuncInfo *pFuncInfo, // Static information for this frame
*/
EHExceptionRecord *pExcept, // Information for this exception
EHRegistrationNode *pRN, // Dynamic information for this frame
void *pContext, // Context info (we don't care what's in it)
DispatcherContext *pDC // More dynamic info for this frame (ignored on Intel)
) {
FuncInfo *pFuncInfo;
EXCEPTION_DISPOSITION result;
__asm {
//
// Standard function prolog
//
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
push ebx
push esi
push edi
cld // A bit of paranoia -- Our code-gen assumes this
//
// Save the extra parameter
//
mov pFuncInfo, eax
}
result = __InternalCxxFrameHandler( pExcept, pRN, pContext, pDC, pFuncInfo, 0, NULL, FALSE );
__asm {
pop edi
pop esi
pop ebx
mov eax, result
mov esp, ebp
pop ebp
ret 0
}
}
/////////////////////////////////////////////////////////////////////////////
//
// __CxxLongjmpUnwind - Entry point for local unwind required by longjmp
// when setjmp used in same function as C++ EH.
//
extern "C" void __FrameUnwindToState( // in frame.cpp
EHRegistrationNode *pRN, // Dynamic information for this frame
DispatcherContext *pDC, // More dynamic info for this frame (ignored on Intel)
FuncInfo *pFuncInfo, // Static information for this frame
__ehstate_t targetState); // State to unwind to
extern "C" void __stdcall __CxxLongjmpUnwind(
_JUMP_BUFFER *jbuf
) {
__FrameUnwindToState((EHRegistrationNode *)jbuf->Registration,
(DispatcherContext*)NULL,
(FuncInfo *)jbuf->UnwindData[0],
(__ehstate_t)jbuf->TryLevel);
}
/////////////////////////////////////////////////////////////////////////////
//
// _CallCatchBlock2 - The nitty-gritty details to get the catch called
// correctly.
//
// We need to guard the call to the catch block with a special registration
// node, so that if there is an exception which should be handled by a try
// block within the catch, we handle it without unwinding the SEH node
// in CallCatchBlock.
//
struct CatchGuardRN {
EHRegistrationNode *pNext; // Frame link
void *pFrameHandler; // Frame Handler
FuncInfo *pFuncInfo; // Static info for subject function
EHRegistrationNode *pRN; // Dynamic info for subject function
int CatchDepth; // How deeply nested are we?
};
static EXCEPTION_DISPOSITION __cdecl CatchGuardHandler( EHExceptionRecord*, CatchGuardRN *, void *, void * );
void *_CallCatchBlock2(
EHRegistrationNode *pRN, // Dynamic info of function with catch
FuncInfo *pFuncInfo, // Static info of function with catch
void *handlerAddress, // Code address of handler
int CatchDepth, // How deeply nested in catch blocks are we?
unsigned long NLGCode
) {
//
// First, create and link in our special guard node:
//
CatchGuardRN CGRN = { NULL, (void*)CatchGuardHandler, pFuncInfo, pRN, CatchDepth + 1 };
__asm {
mov eax, FS:[0] // Fetch frame list head
mov CGRN.pNext, eax // Link this node in
lea eax, CGRN // Put this node at the head
mov FS:[0], eax
}
//
// Call the catch
//
void *continuationAddress = _CallSettingFrame( handlerAddress, pRN, NLGCode );
//
// Unlink our registration node
//
__asm {
mov eax, CGRN.pNext // Get parent node
mov FS:[0], eax // Put it at the head
}
return continuationAddress;
}
/////////////////////////////////////////////////////////////////////////////
//
// CatchGuardHandler - frame handler for the catch guard node.
//
// This function will attempt to find a handler for the exception within
// the current catch block (ie any nested try blocks). If none is found,
// or the handler rethrows, returns ExceptionContinueSearch; otherwise does
// not return.
//
// Does nothing on an unwind.
//
static EXCEPTION_DISPOSITION __cdecl CatchGuardHandler(
EHExceptionRecord *pExcept, // Information for this exception
CatchGuardRN *pRN, // The special marker frame
void *pContext, // Context info (we don't care what's in it)
void * // (ignored)
) {
__asm cld; // Our code-gen assumes this
return __InternalCxxFrameHandler( pExcept, pRN->pRN, pContext, NULL, pRN->pFuncInfo, pRN->CatchDepth, (EHRegistrationNode*)pRN, FALSE );
}
/////////////////////////////////////////////////////////////////////////////
//
// CallSEHTranslator - calls the SEH translator, and handles the translation
// exception.
//
// Assumes that a valid translator exists.
//
// Method:
// Sets up a special guard node, whose handler handles the translation
// exception, and remembers NT's marker node (See _UnwindNestedFrames above).
// If the exception is not fully handled, the handler returns control to here,
// so that this function can return to resume the normal search for a handler
// for the original exception.
//
// Returns: TRUE if translator had a translation (handled or not)
// FALSE if there was no translation
// Does not return if translation was fully handled
//
struct TranslatorGuardRN /*: CatchGuardRN */ {
EHRegistrationNode *pNext; // Frame link
void *pFrameHandler; // Frame Handler
FuncInfo *pFuncInfo; // Static info for subject function
EHRegistrationNode *pRN; // Dynamic info for subject function
int CatchDepth; // How deeply nested are we?
EHRegistrationNode *pMarkerRN; // Marker for parent context
void *pContinue; // Continuation address within CallSEHTranslator
void *ESP; // ESP within CallSEHTranslator
void *EBP; // EBP within CallSEHTranslator
BOOL DidUnwind; // True if this frame was unwound
};
static EXCEPTION_DISPOSITION __cdecl TranslatorGuardHandler( EHExceptionRecord*, TranslatorGuardRN *, void *, void * );
#pragma optimize("g", off) // WORKAROUND for DOLPH:3322
BOOL _CallSETranslator(
EHExceptionRecord *pExcept, // The exception to be translated
EHRegistrationNode *pRN, // Dynamic info of function with catch
void *pContext, // Context info (we don't care what's in it)
DispatcherContext *pDC, // More dynamic info of function with catch (ignored)
FuncInfo *pFuncInfo, // Static info of function with catch
int CatchDepth, // How deeply nested in catch blocks are we?
EHRegistrationNode *pMarkerRN // Marker for parent context
) {
//
// Create and link in our special guard node:
//
TranslatorGuardRN TGRN = { NULL, // Frame link
(void*)TranslatorGuardHandler,
pFuncInfo,
pRN,
CatchDepth,
pMarkerRN,
NULL, // Continue
NULL, // ESP
NULL, // EBP
FALSE }; // DidUnwind
__asm {
//
// Fill in the blanks:
//
mov TGRN.pContinue, offset ExceptionContinuation
mov TGRN.ESP, esp
mov TGRN.EBP, ebp
//
// Link this node in:
//
mov eax, FS:[0] // Fetch frame list head
mov TGRN.pNext, eax // Link this node in
lea eax, TGRN // Put this node at the head
mov FS:[0], eax
}
//
// Call the translator; assume it will give a translation.
//
BOOL DidTranslate = TRUE;
_EXCEPTION_POINTERS pointers = {
(PEXCEPTION_RECORD)pExcept,
(PCONTEXT)pContext };
__pSETranslator(PER_CODE(pExcept), &pointers);
//
// If translator returned normally, that means it didn't translate the
// exception.
//
DidTranslate = FALSE;
//
// Here's where we pick up if the translator threw something.
// Note that ESP and EBP were restored by our frame handler.
//
ExceptionContinuation:
if (TGRN.DidUnwind) {
//
// If the translated exception was partially handled (ie caught but
// rethrown), then the frame list has the NT guard for the translation
// exception context instead of the one for the original exception
// context. Correct that sequencing problem. Note that our guard
// node was unlinked by RtlUnwind.
//
__asm {
mov ebx, FS:[0] // Get the node below the (bad) NT marker
mov eax, [ebx] // (it was the target of the unwind)
mov ebx, TGRN.pNext // Get the node we saved (the 'good' marker)
mov [ebx], eax // Link the good node to the unwind target
mov FS:[0], ebx // Put the good node at the head of the list
}
}
else {
//
// Translator returned normally or translation wasn't handled.
// unlink our registration node and exit
//
__asm {
mov eax, TGRN.pNext // Get parent node
mov FS:[0], eax // Put it at the head
}
}
return DidTranslate;
}
#pragma optimize("g", on)
/////////////////////////////////////////////////////////////////////////////
//
// TranslatorGuardHandler - frame handler for the translator guard node.
//
// On search:
// This frame handler will check if there is a catch at the current level
// for the translated exception. If there is no handler or the handler
// did a re-throw, control is transfered back into CallSEHTranslator, based
// on the values saved in the registration node.
//
// Does not return.
//
// On unwind:
// Sets the DidUnwind flag in the registration node, and returns.
//
static EXCEPTION_DISPOSITION __cdecl TranslatorGuardHandler(
EHExceptionRecord *pExcept, // Information for this exception
TranslatorGuardRN *pRN, // The translator guard frame
void *pContext, // Context info (we don't care what's in it)
void * // (ignored)
) {
__asm cld; // Our code-gen assumes this
if (IS_UNWINDING(PER_FLAGS(pExcept)))
{
pRN->DidUnwind = TRUE;
return ExceptionContinueSearch;
}
else {
//
// Check for a handler:
//
__InternalCxxFrameHandler( pExcept, pRN->pRN, pContext, NULL, pRN->pFuncInfo, pRN->CatchDepth, pRN->pMarkerRN, TRUE );
if (!pRN->DidUnwind) {
//
// If no match was found, unwind the context of the translator
//
_UnwindNestedFrames( (EHRegistrationNode*)pRN, pExcept );
}
//
// Transfer control back to establisher:
//
__asm {
mov ebx, pRN // Get address of registration node
mov esp, [ebx]TranslatorGuardRN.ESP
mov ebp, [ebx]TranslatorGuardRN.EBP
jmp [ebx]TranslatorGuardRN.pContinue
}
// Unreached.
return ExceptionContinueSearch;
}
}