mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 06:00:35 +03:00
653 lines
16 KiB
C
653 lines
16 KiB
C
//
|
|
// Copyright (c) 1999-2003 by Digital Mars, www.digitalmars.com
|
|
// All Rights Reserved
|
|
// Written by Walter Bright
|
|
|
|
// Exception handling support
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
/* ======================== Win32 =============================== */
|
|
|
|
#if _WIN32
|
|
|
|
#include <excpt.h>
|
|
#include <windows.h>
|
|
|
|
//#include "\sc\src\include\ehsup.h"
|
|
|
|
/*** From Digital Mars C runtime library ***/
|
|
EXCEPTION_DISPOSITION __cdecl _local_except_handler (EXCEPTION_RECORD *ExceptionRecord,
|
|
void* EstablisherFrame,
|
|
void *ContextRecord,
|
|
void *DispatcherContext
|
|
);
|
|
void __cdecl _global_unwind(void *frame,EXCEPTION_RECORD *eRecord);
|
|
#define EXCEPTION_UNWIND 6 // Flag to indicate if the system is unwinding
|
|
|
|
extern DWORD _except_list;
|
|
/*** ***/
|
|
|
|
#include "mars.h"
|
|
|
|
extern ClassInfo _Class_Exception;
|
|
|
|
typedef int (__pascal *fp_t)(); // function pointer in ambient memory model
|
|
|
|
// The layout of DEstablisherFrame is the same for C++
|
|
|
|
struct DEstablisherFrame
|
|
{
|
|
void *prev; // pointer to previous exception list
|
|
void *handler; // pointer to routine for exception handler
|
|
DWORD table_index; // current index into handler_info[]
|
|
DWORD ebp; // this is EBP of routine
|
|
};
|
|
|
|
struct DHandlerInfo
|
|
{
|
|
int prev_index; // previous table index
|
|
unsigned cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch)
|
|
void *finally_code; // pointer to finally code to execute
|
|
// (!=0 if try-finally)
|
|
};
|
|
|
|
// Address of DHandlerTable is passed in EAX to _d_framehandler()
|
|
|
|
struct DHandlerTable
|
|
{
|
|
void *fptr; // pointer to start of function
|
|
unsigned espoffset; // offset of ESP from EBP
|
|
unsigned retoffset; // offset from start of function to return code
|
|
struct DHandlerInfo handler_info[1];
|
|
};
|
|
|
|
struct DCatchBlock
|
|
{
|
|
ClassInfo *type; // catch type
|
|
unsigned bpoffset; // EBP offset of catch var
|
|
void *code; // catch handler code
|
|
};
|
|
|
|
// Create one of these for each try-catch
|
|
struct DCatchInfo
|
|
{
|
|
unsigned ncatches; // number of catch blocks
|
|
struct DCatchBlock catch_block[1]; // data for each catch block
|
|
};
|
|
|
|
// Macro to make our own exception code
|
|
#define MAKE_EXCEPTION_CODE(severity, facility, exception) \
|
|
(((severity) << 30) | (1 << 29) | (0 << 28) | ((facility) << 16) | (exception))
|
|
|
|
#define STATUS_DIGITAL_MARS_D_EXCEPTION MAKE_EXCEPTION_CODE(3,'D',1)
|
|
|
|
Object *_d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record);
|
|
void __cdecl _d_local_unwind(struct DHandlerTable *handler_table, struct DEstablisherFrame *frame, int stop_index);
|
|
|
|
|
|
/***********************************
|
|
* The frame handler, this is called for each frame that has been registered
|
|
* in the OS except_list.
|
|
* Input:
|
|
* EAX the handler table for the frame
|
|
*/
|
|
|
|
EXCEPTION_DISPOSITION _d_framehandler(
|
|
EXCEPTION_RECORD *exception_record,
|
|
struct DEstablisherFrame *frame,
|
|
CONTEXT context,
|
|
void *dispatcher_context)
|
|
{
|
|
struct DHandlerTable *handler_table;
|
|
|
|
__asm { mov handler_table,EAX }
|
|
|
|
if (exception_record->ExceptionFlags & EXCEPTION_UNWIND)
|
|
{
|
|
// Call all the finally blocks in this frame
|
|
_d_local_unwind(handler_table, frame, -1);
|
|
}
|
|
else
|
|
{
|
|
// Jump to catch block if matching one is found
|
|
|
|
int ndx,prev_ndx,i;
|
|
struct DHandlerInfo *phi;
|
|
struct DCatchInfo *pci;
|
|
struct DCatchBlock *pcb;
|
|
unsigned ncatches; // number of catches in the current handler
|
|
Object *pti;
|
|
ClassInfo *ci;
|
|
|
|
ci = NULL; // only compute it if we need it
|
|
|
|
// walk through handler table, checking each handler
|
|
// with an index smaller than the current table_index
|
|
for (ndx = frame->table_index; ndx != -1; ndx = prev_ndx)
|
|
{
|
|
phi = &handler_table->handler_info[ndx];
|
|
prev_ndx = phi->prev_index;
|
|
if (phi->cioffset)
|
|
{
|
|
// this is a catch handler (no finally)
|
|
pci = (struct DCatchInfo *)((char *)handler_table + phi->cioffset);
|
|
ncatches = pci->ncatches;
|
|
for (i = 0; i < ncatches; i++)
|
|
{
|
|
pcb = &pci->catch_block[i];
|
|
|
|
if (!ci)
|
|
{
|
|
// This code must match the translation code
|
|
if (exception_record->ExceptionCode == STATUS_DIGITAL_MARS_D_EXCEPTION)
|
|
{
|
|
//printf("ei[0] = %p\n", exception_record->ExceptionInformation[0]);
|
|
ci = **(ClassInfo ***)(exception_record->ExceptionInformation[0]);
|
|
}
|
|
else
|
|
ci = &_Class_Exception;
|
|
}
|
|
|
|
if (_d_isbaseof(ci, pcb->type))
|
|
{ // Matched the catch type, so we've found the handler.
|
|
int regebp;
|
|
|
|
pti = _d_translate_se_to_d_exception(exception_record);
|
|
|
|
// Initialize catch variable
|
|
regebp = (int)&frame->ebp; // EBP for this frame
|
|
*(void **)(regebp + (pcb->bpoffset)) = pti;
|
|
|
|
// Have system call all finally blocks in intervening frames
|
|
_global_unwind(frame, exception_record);
|
|
|
|
// Call all the finally blocks skipped in this frame
|
|
_d_local_unwind(handler_table, frame, ndx);
|
|
|
|
frame->table_index = prev_ndx; // we are out of this handler
|
|
|
|
// Jump to catch block. Does not return.
|
|
{
|
|
unsigned catch_esp;
|
|
fp_t catch_addr;
|
|
|
|
catch_addr = (fp_t)(pcb->code);
|
|
catch_esp = regebp - handler_table->espoffset - sizeof(fp_t);
|
|
_asm
|
|
{
|
|
mov EAX,catch_esp
|
|
mov ECX,catch_addr
|
|
mov [EAX],ECX
|
|
mov EBP,regebp
|
|
mov ESP,EAX // reset stack
|
|
ret // jump to catch block
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ExceptionContinueSearch;
|
|
}
|
|
|
|
/***********************************
|
|
* Exception filter for use in __try..__except block
|
|
* surrounding call to Dmain()
|
|
*/
|
|
|
|
int _d_exception_filter(struct _EXCEPTION_POINTERS *eptrs,
|
|
int retval,
|
|
Object **exception_object)
|
|
{
|
|
*exception_object = _d_translate_se_to_d_exception(eptrs->ExceptionRecord);
|
|
return retval;
|
|
}
|
|
|
|
/***********************************
|
|
* Throw a D object.
|
|
*/
|
|
|
|
void __stdcall _d_throw(Object *h)
|
|
{
|
|
//printf("_d_throw(h = %p, &h = %p)\n", h, &h);
|
|
//printf("\tvptr = %p\n", *(void **)h);
|
|
RaiseException(STATUS_DIGITAL_MARS_D_EXCEPTION,
|
|
EXCEPTION_NONCONTINUABLE,
|
|
1, (DWORD *)&h);
|
|
}
|
|
|
|
/***********************************
|
|
* Create an exception object
|
|
*/
|
|
|
|
Object *_d_create_exception_object(ClassInfo *ci, char *msg)
|
|
{
|
|
Exception *exc;
|
|
|
|
exc = (Exception *)_d_newclass(ci);
|
|
// BUG: what if _d_newclass() throws an out of memory exception?
|
|
|
|
if (msg)
|
|
{
|
|
exc->msglen = strlen(msg);
|
|
exc->msg = msg;
|
|
}
|
|
return (Object *)exc;
|
|
}
|
|
|
|
/***********************************
|
|
* Converts a Windows Structured Exception code to a D Exception Object.
|
|
*/
|
|
|
|
Object *_d_translate_se_to_d_exception(EXCEPTION_RECORD *exception_record)
|
|
{
|
|
Object *pti;
|
|
|
|
switch (exception_record->ExceptionCode) {
|
|
case STATUS_DIGITAL_MARS_D_EXCEPTION:
|
|
// Generated D exception
|
|
pti = (Object *)(exception_record->ExceptionInformation[0]);
|
|
break;
|
|
|
|
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
|
pti = _d_create_exception_object(&_Class_Exception, "Integer Divide by Zero");
|
|
break;
|
|
|
|
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
|
pti = _d_create_exception_object(&_Class_Exception, "Float Divide by Zero");
|
|
break;
|
|
|
|
case STATUS_ACCESS_VIOLATION:
|
|
pti = _d_create_exception_object(&_Class_Exception, "Access Violation");
|
|
break;
|
|
|
|
case STATUS_STACK_OVERFLOW:
|
|
pti = _d_create_exception_object(&_Class_Exception, "Stack Overflow");
|
|
break;
|
|
|
|
// convert all other exception codes into a Win32Exception
|
|
default:
|
|
pti = _d_create_exception_object(&_Class_Exception, "Win32 Exception");
|
|
break;
|
|
}
|
|
|
|
return pti;
|
|
}
|
|
|
|
/**************************************
|
|
* Call finally blocks in the current stack frame until stop_index.
|
|
* This is roughly equivalent to _local_unwind() for C in \src\win32\ehsup.c
|
|
*/
|
|
|
|
void __cdecl _d_local_unwind(struct DHandlerTable *handler_table,
|
|
struct DEstablisherFrame *frame, int stop_index)
|
|
{
|
|
struct DHandlerInfo *phi;
|
|
struct DCatchInfo *pci;
|
|
int i;
|
|
|
|
// Set up a special exception handler to catch double-fault exceptions.
|
|
__asm
|
|
{
|
|
push dword ptr -1
|
|
push dword ptr 0
|
|
push offset _local_except_handler // defined in src\win32\ehsup.c
|
|
push dword ptr fs:_except_list
|
|
mov FS:_except_list,ESP
|
|
}
|
|
|
|
for (i = frame->table_index; i != -1 && i != stop_index; i = phi->prev_index)
|
|
{
|
|
phi = &handler_table->handler_info[i];
|
|
if (phi->finally_code)
|
|
{
|
|
// Note that it is unnecessary to adjust the ESP, as the finally block
|
|
// accesses all items on the stack as relative to EBP.
|
|
|
|
DWORD *catch_ebp = &frame->ebp;
|
|
void *blockaddr = phi->finally_code;
|
|
|
|
_asm
|
|
{
|
|
push EBX
|
|
mov EBX,blockaddr
|
|
push EBP
|
|
mov EBP,catch_ebp
|
|
call EBX
|
|
pop EBP
|
|
pop EBX
|
|
}
|
|
}
|
|
}
|
|
|
|
_asm
|
|
{
|
|
pop FS:_except_list
|
|
add ESP,12
|
|
}
|
|
}
|
|
|
|
/***********************************
|
|
* external version of the unwinder
|
|
*/
|
|
|
|
__declspec(naked) void __cdecl _d_local_unwind2()
|
|
{
|
|
__asm
|
|
{
|
|
jmp _d_local_unwind
|
|
}
|
|
}
|
|
|
|
/***********************************
|
|
* The frame handler, this is called for each frame that has been registered
|
|
* in the OS except_list.
|
|
* Input:
|
|
* EAX the handler table for the frame
|
|
*/
|
|
|
|
EXCEPTION_DISPOSITION _d_monitor_handler(
|
|
EXCEPTION_RECORD *exception_record,
|
|
struct DEstablisherFrame *frame,
|
|
CONTEXT context,
|
|
void *dispatcher_context)
|
|
{
|
|
if (exception_record->ExceptionFlags & EXCEPTION_UNWIND)
|
|
{
|
|
_d_monitorexit((Object *)frame->table_index);
|
|
}
|
|
else
|
|
{
|
|
}
|
|
return ExceptionContinueSearch;
|
|
}
|
|
|
|
/***********************************
|
|
*/
|
|
|
|
void _d_monitor_prolog(void *x, void *y, Object *h)
|
|
{
|
|
__asm
|
|
{
|
|
push EAX
|
|
}
|
|
//printf("_d_monitor_prolog(x=%p, y=%p, h=%p)\n", x, y, h);
|
|
_d_monitorenter(h);
|
|
__asm
|
|
{
|
|
pop EAX
|
|
}
|
|
}
|
|
|
|
/***********************************
|
|
*/
|
|
|
|
void _d_monitor_epilog(void *x, void *y, Object *h)
|
|
{
|
|
//printf("_d_monitor_epilog(x=%p, y=%p, h=%p)\n", x, y, h);
|
|
__asm
|
|
{
|
|
push EAX
|
|
push EDX
|
|
}
|
|
_d_monitorexit(h);
|
|
__asm
|
|
{
|
|
pop EDX
|
|
pop EAX
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
/* ======================== linux =============================== */
|
|
|
|
#if linux
|
|
|
|
#include "mars.h"
|
|
|
|
extern ClassInfo _Class_Exception;
|
|
|
|
typedef int (*fp_t)(); // function pointer in ambient memory model
|
|
|
|
struct DHandlerInfo
|
|
{
|
|
unsigned offset; // offset from function address to start of guarded section
|
|
int prev_index; // previous table index
|
|
unsigned cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch)
|
|
void *finally_code; // pointer to finally code to execute
|
|
// (!=0 if try-finally)
|
|
};
|
|
|
|
// Address of DHandlerTable, searched for by eh_finddata()
|
|
|
|
struct DHandlerTable
|
|
{
|
|
void *fptr; // pointer to start of function
|
|
unsigned espoffset; // offset of ESP from EBP
|
|
unsigned retoffset; // offset from start of function to return code
|
|
unsigned nhandlers; // dimension of handler_info[]
|
|
struct DHandlerInfo handler_info[1];
|
|
};
|
|
|
|
struct DCatchBlock
|
|
{
|
|
ClassInfo *type; // catch type
|
|
unsigned bpoffset; // EBP offset of catch var
|
|
void *code; // catch handler code
|
|
};
|
|
|
|
// Create one of these for each try-catch
|
|
struct DCatchInfo
|
|
{
|
|
unsigned ncatches; // number of catch blocks
|
|
struct DCatchBlock catch_block[1]; // data for each catch block
|
|
};
|
|
|
|
// One of these is generated for each function with try-catch or try-finally
|
|
|
|
struct FuncTable
|
|
{
|
|
void *fptr; // pointer to start of function
|
|
struct DHandlerTable *handlertable; // eh data for this function
|
|
unsigned size; // size of function in bytes
|
|
};
|
|
|
|
extern struct FuncTable *table_start;
|
|
extern struct FuncTable *table_end;
|
|
|
|
void terminate()
|
|
{
|
|
// _asm
|
|
// {
|
|
// hlt
|
|
// }
|
|
}
|
|
|
|
/*******************************************
|
|
* Given address that is inside a function,
|
|
* figure out which function it is in.
|
|
* Return DHandlerTable if there is one, NULL if not.
|
|
*/
|
|
|
|
struct DHandlerTable *__eh_finddata(void *address)
|
|
{
|
|
struct FuncTable *ft;
|
|
|
|
for (ft = (struct FuncTable *)table_start;
|
|
ft < (struct FuncTable *)table_end;
|
|
ft++)
|
|
{
|
|
if (ft->fptr <= address &&
|
|
address < (void *)((char *)ft->fptr + ft->size))
|
|
{
|
|
return ft->handlertable;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/******************************
|
|
* Given EBP, find return address to caller, and caller's EBP.
|
|
* Input:
|
|
* regbp Value of EBP for current function
|
|
* *pretaddr Return address
|
|
* Output:
|
|
* *pretaddr return address to caller
|
|
* Returns:
|
|
* caller's EBP
|
|
*/
|
|
|
|
unsigned __eh_find_caller(unsigned regbp, unsigned *pretaddr)
|
|
{
|
|
unsigned bp = *(unsigned *)regbp;
|
|
|
|
if (bp) // if not end of call chain
|
|
{
|
|
// Perform sanity checks on new EBP.
|
|
// If it is screwed up, terminate() hopefully before we do more damage.
|
|
if (bp <= regbp)
|
|
// stack should grow to smaller values
|
|
terminate();
|
|
|
|
*pretaddr = *(unsigned *)(regbp + sizeof(int));
|
|
}
|
|
return bp;
|
|
}
|
|
|
|
/***********************************
|
|
* Throw a D object.
|
|
*/
|
|
|
|
void __stdcall _d_throw(Object *h)
|
|
{
|
|
unsigned regebp;
|
|
|
|
//printf("_d_throw(h = %p, &h = %p)\n", h, &h);
|
|
//printf("\tvptr = %p\n", *(void **)h);
|
|
|
|
regebp = _EBP;
|
|
|
|
while (1) // for each function on the stack
|
|
{
|
|
struct DHandlerTable *handler_table;
|
|
struct FuncTable *pfunc;
|
|
struct DHandlerInfo *phi;
|
|
unsigned retaddr;
|
|
unsigned funcoffset;
|
|
unsigned spoff;
|
|
unsigned retoffset;
|
|
int index;
|
|
int dim;
|
|
int ndx;
|
|
int prev_ndx;
|
|
|
|
regebp = __eh_find_caller(regebp,&retaddr);
|
|
if (!regebp)
|
|
// if end of call chain
|
|
break;
|
|
|
|
handler_table = __eh_finddata((void *)retaddr); // find static data associated with function
|
|
if (!handler_table) // if no static data
|
|
{
|
|
continue;
|
|
}
|
|
funcoffset = (unsigned)handler_table->fptr;
|
|
spoff = handler_table->espoffset;
|
|
retoffset = handler_table->retoffset;
|
|
|
|
#ifdef DEBUG
|
|
printf("retaddr = x%x\n",(unsigned)retaddr);
|
|
printf("regebp=x%04x, funcoffset=x%04x, spoff=x%x, retoffset=x%x\n",
|
|
regebp,funcoffset,spoff,retoffset);
|
|
#endif
|
|
|
|
// Find start index for retaddr in static data
|
|
dim = handler_table->nhandlers;
|
|
index = -1;
|
|
for (int i = 0; i < dim; i++)
|
|
{
|
|
phi = &handler_table->handler_info[i];
|
|
|
|
if ((unsigned)retaddr >= funcoffset + phi->offset)
|
|
index = i;
|
|
}
|
|
|
|
// walk through handler table, checking each handler
|
|
// with an index smaller than the current table_index
|
|
for (ndx = index; ndx != -1; ndx = prev_ndx)
|
|
{
|
|
phi = &handler_table->handler_info[ndx];
|
|
prev_ndx = phi->prev_index;
|
|
if (phi->cioffset)
|
|
{
|
|
// this is a catch handler (no finally)
|
|
struct DCatchInfo *pci;
|
|
int ncatches;
|
|
int i;
|
|
|
|
pci = (struct DCatchInfo *)((char *)handler_table + phi->cioffset);
|
|
ncatches = pci->ncatches;
|
|
for (i = 0; i < ncatches; i++)
|
|
{
|
|
struct DCatchBlock *pcb;
|
|
ClassInfo *ci = **(ClassInfo ***)h;
|
|
|
|
pcb = &pci->catch_block[i];
|
|
|
|
if (_d_isbaseof(ci, pcb->type))
|
|
{ // Matched the catch type, so we've found the handler.
|
|
|
|
// Initialize catch variable
|
|
*(void **)(regebp + (pcb->bpoffset)) = h;
|
|
|
|
// Jump to catch block. Does not return.
|
|
{
|
|
unsigned catch_esp;
|
|
fp_t catch_addr;
|
|
|
|
catch_addr = (fp_t)(pcb->code);
|
|
catch_esp = regebp - handler_table->espoffset - sizeof(fp_t);
|
|
_asm
|
|
{
|
|
mov EAX,catch_esp
|
|
mov ECX,catch_addr
|
|
mov [EAX],ECX
|
|
mov EBP,regebp
|
|
mov ESP,EAX // reset stack
|
|
ret // jump to catch block
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (phi->finally_code)
|
|
{ // Call finally block
|
|
// Note that it is unnecessary to adjust the ESP, as the finally block
|
|
// accesses all items on the stack as relative to EBP.
|
|
|
|
void *blockaddr = phi->finally_code;
|
|
|
|
_asm
|
|
{
|
|
push EBX
|
|
mov EBX,blockaddr
|
|
push EBP
|
|
mov EBP,regebp
|
|
call EBX
|
|
pop EBP
|
|
pop EBX
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#endif
|