// // Copyright (c) 1999-2003 by Digital Mars, www.digitalmars.com // All Rights Reserved // Written by Walter Bright // Exception handling support for linux //debug=1; import std.c.linux.linuxextern; extern (C) int _d_isbaseof(ClassInfo oc, ClassInfo c); alias int (*fp_t)(); // function pointer in ambient memory model struct DHandlerInfo { uint offset; // offset from function address to start of guarded section uint endoffset; // offset of end of guarded section int prev_index; // previous table index uint 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 uint espoffset; // offset of ESP from EBP uint retoffset; // offset from start of function to return code uint nhandlers; // dimension of handler_info[] DHandlerInfo handler_info[1]; } struct DCatchBlock { ClassInfo type; // catch type uint bpoffset; // EBP offset of catch var void *code; // catch handler code } // Create one of these for each try-catch struct DCatchInfo { uint ncatches; // number of catch blocks 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 DHandlerTable *handlertable; // eh data for this function uint fsize; // size of function in bytes } 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. */ DHandlerTable *__eh_finddata(void *address) { FuncTable *ft; // debug printf("__eh_finddata(address = x%x)\n", address); // debug printf("_deh_beg = x%x, _deh_end = x%x\n", &_deh_beg, &_deh_end); for (ft = cast(FuncTable *)&_deh_beg; ft < cast(FuncTable *)&_deh_end; ft++) { // debug printf("\tfptr = x%x, fsize = x%03x, handlertable = x%x\n", // ft.fptr, ft.fsize, ft.handlertable); if (ft.fptr <= address && address < cast(void *)(cast(char *)ft.fptr + ft.fsize)) { // debug printf("\tfound handler table\n"); return ft.handlertable; } } // debug printf("\tnot found\n"); 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 */ uint __eh_find_caller(uint regbp, uint *pretaddr) { uint bp = *cast(uint *)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 = *cast(uint *)(regbp + int.sizeof); } return bp; } /*********************************** * Throw a D object. */ extern (Windows) void _d_throw(Object *h) { uint regebp; debug { printf("_d_throw(h = %p, &h = %p)\n", h, &h); printf("\tvptr = %p\n", *cast(void **)h); } asm { mov regebp,EBP ; } //static uint abc; //if (++abc == 2) *(char *)0=0; //int count = 0; while (1) // for each function on the stack { DHandlerTable *handler_table; FuncTable *pfunc; DHandlerInfo *phi; uint retaddr; uint funcoffset; uint spoff; uint retoffset; int index; int dim; int ndx; int prev_ndx; regebp = __eh_find_caller(regebp,&retaddr); if (!regebp) { // if end of call chain debug printf("end of call chain\n"); break; } debug printf("found caller, EBP = x%x, retaddr = x%x\n", regebp, retaddr); //if (++count == 12) *(char*)0=0; handler_table = __eh_finddata(cast(void *)retaddr); // find static data associated with function if (!handler_table) // if no static data { debug printf("no handler table\n"); continue; } funcoffset = cast(uint)handler_table.fptr; spoff = handler_table.espoffset; retoffset = handler_table.retoffset; debug { printf("retaddr = x%x\n",cast(uint)retaddr); printf("regebp=x%04x, funcoffset=x%04x, spoff=x%x, retoffset=x%x\n", regebp,funcoffset,spoff,retoffset); } // Find start index for retaddr in static data dim = handler_table.nhandlers; debug { printf("handler_info[]:\n"); for (int i = 0; i < dim; i++) { phi = &handler_table.handler_info[i]; printf("\t[%d]: offset = x%04x, endoffset = x%04x, prev_index = %d, cioffset = x%04x, finally_code = %x\n", i, phi.offset, phi.endoffset, phi.prev_index, phi.cioffset, phi.finally_code); } } index = -1; for (int i = 0; i < dim; i++) { phi = &handler_table.handler_info[i]; debug printf("i = %d, phi.offset = %04x\n", i, funcoffset + phi.offset); if (cast(uint)retaddr > funcoffset + phi.offset && cast(uint)retaddr <= funcoffset + phi.endoffset) index = i; } debug printf("index = %d\n", index); // 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) DCatchInfo *pci; int ncatches; int i; pci = cast(DCatchInfo *)(cast(char *)handler_table + phi.cioffset); ncatches = pci.ncatches; for (i = 0; i < ncatches; i++) { DCatchBlock *pcb; ClassInfo ci = **cast(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 *cast(void **)(regebp + (pcb.bpoffset)) = h; // Jump to catch block. Does not return. { uint catch_esp; fp_t catch_addr; catch_addr = cast(fp_t)(pcb.code); catch_esp = regebp - handler_table.espoffset - fp_t.sizeof; 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 ; } } } } }