/** * Written in the D programming language. * Implementation of exception handling support routines for Posix and Win64. * * Copyright: Copyright Digital Mars 2000 - 2013. * License: Distributed under the * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). * (See accompanying file LICENSE) * Authors: Walter Bright, Sean Kelly * Source: $(DRUNTIMESRC src/rt/deh_win64_posix.d) */ module rt.deh_win64_posix; version (Win64) version = Win64_Posix; version (Posix) version = Win64_Posix; version (Win64_Posix): version (OSX) version = Darwin; else version (iOS) version = Darwin; else version (TVOS) version = Darwin; else version (WatchOS) version = Darwin; //debug=PRINTF; debug(PRINTF) import core.stdc.stdio : printf; extern (C) { Throwable.TraceInfo _d_traceContext(void* ptr = null); int _d_isbaseof(ClassInfo oc, ClassInfo c); void _d_createTrace(Object o, void* context); } alias int function() fp_t; // function pointer in ambient memory model // DHandlerInfo table is generated by except_gentables() in eh.c 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) size_t finally_offset; // offset to finally code to execute // (!=0 if try-finally) } // Address of DHandlerTable, searched for by eh_finddata() struct DHandlerTable { uint espoffset; // offset of ESP from EBP uint retoffset; // offset from start of function to return code size_t nhandlers; // dimension of handler_info[] (use size_t to set alignment of handler_info[]) DHandlerInfo[1] handler_info; } struct DCatchBlock { ClassInfo type; // catch type size_t bpoffset; // EBP offset of catch var size_t codeoffset; // catch handler offset } // Create one of these for each try-catch struct DCatchInfo { size_t ncatches; // number of catch blocks DCatchBlock[1] catch_block; // 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 } private { struct InFlight { InFlight* next; void* addr; Throwable t; } InFlight* __inflight = null; /// __inflight is per-stack, not per-thread, and as such needs to be /// swapped out on fiber context switches. extern(C) void* _d_eh_swapContext(void* newContext) nothrow { auto old = __inflight; __inflight = cast(InFlight*)newContext; return old; } } 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. */ immutable(FuncTable)* __eh_finddata(void *address) { import rt.sections; foreach (ref sg; SectionGroup) { auto pstart = sg.ehTables.ptr; auto pend = pstart + sg.ehTables.length; if (auto ft = __eh_finddata(address, pstart, pend)) return ft; } return null; } immutable(FuncTable)* __eh_finddata(void *address, immutable(FuncTable)* pstart, immutable(FuncTable)* pend) { debug(PRINTF) printf("FuncTable.sizeof = %p\n", FuncTable.sizeof); debug(PRINTF) printf("__eh_finddata(address = %p)\n", address); debug(PRINTF) printf("_deh_beg = %p, _deh_end = %p\n", pstart, pend); for (auto ft = pstart; 1; ft++) { Lagain: if (ft >= pend) break; version (Win64) { /* The MS Linker has an inexplicable and erratic tendency to insert * 8 zero bytes between sections generated from different .obj * files. This kludge tries to skip over them. */ if (ft.fptr == null) { ft = cast(immutable(FuncTable)*)(cast(void**)ft + 1); goto Lagain; } } debug(PRINTF) printf(" ft = %p, fptr = %p, handlertable = %p, fsize = x%03x\n", ft, ft.fptr, ft.handlertable, ft.fsize); immutable(void)* fptr = ft.fptr; version (Win64) { /* If linked with /DEBUG, the linker rewrites it so the function pointer points * to a JMP to the actual code. The address will be in the actual code, so we * need to follow the JMP. */ if ((cast(ubyte*)fptr)[0] == 0xE9) { // JMP target = RIP of next instruction + signed 32 bit displacement fptr = fptr + 5 + *cast(int*)(fptr + 1); } } if (fptr <= address && address < cast(void *)(cast(char *)fptr + ft.fsize)) { debug(PRINTF) printf("\tfound handler table\n"); return ft; } } debug(PRINTF) 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 */ size_t __eh_find_caller(size_t regbp, size_t *pretaddr) { size_t bp = *cast(size_t *)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(size_t *)(regbp + size_t.sizeof); } return bp; } /*********************************** * Throw a D object. */ extern (C) void _d_throwc(Object h) { size_t regebp; debug(PRINTF) { printf("_d_throw(h = %p, &h = %p)\n", h, &h); printf("\tvptr = %p\n", *cast(void **)h); } version (D_InlineAsm_X86) asm { mov regebp,EBP ; } else version (D_InlineAsm_X86_64) asm { mov regebp,RBP ; } else static assert(0); _d_createTrace(h, null); //static uint abc; //if (++abc == 2) *(char *)0=0; //int count = 0; while (1) // for each function on the stack { size_t retaddr; regebp = __eh_find_caller(regebp,&retaddr); if (!regebp) { // if end of call chain debug(PRINTF) printf("end of call chain\n"); break; } debug(PRINTF) printf("found caller, EBP = %p, retaddr = %p\n", regebp, retaddr); //if (++count == 12) *(char*)0=0; auto func_table = __eh_finddata(cast(void *)retaddr); // find static data associated with function auto handler_table = func_table ? func_table.handlertable : null; if (!handler_table) // if no static data { debug(PRINTF) printf("no handler table\n"); continue; } auto funcoffset = cast(size_t)func_table.fptr; version (Win64) { /* If linked with /DEBUG, the linker rewrites it so the function pointer points * to a JMP to the actual code. The address will be in the actual code, so we * need to follow the JMP. */ if ((cast(ubyte*)funcoffset)[0] == 0xE9) { // JMP target = RIP of next instruction + signed 32 bit displacement funcoffset = funcoffset + 5 + *cast(int*)(funcoffset + 1); } } auto spoff = handler_table.espoffset; auto retoffset = handler_table.retoffset; debug(PRINTF) { printf("retaddr = %p\n", retaddr); printf("regebp=%p, funcoffset=%p, spoff=x%x, retoffset=x%x\n", regebp,funcoffset,spoff,retoffset); } // Find start index for retaddr in static data auto dim = handler_table.nhandlers; debug(PRINTF) { printf("handler_info[%d]:\n", dim); for (uint i = 0; i < dim; i++) { auto phi = &handler_table.handler_info.ptr[i]; printf("\t[%d]: offset = x%04x, endoffset = x%04x, prev_index = %d, cioffset = x%04x, finally_offset = %x\n", i, phi.offset, phi.endoffset, phi.prev_index, phi.cioffset, phi.finally_offset); } } auto index = -1; for (uint i = 0; i < dim; i++) { auto phi = &handler_table.handler_info.ptr[i]; debug(PRINTF) printf("i = %d, phi.offset = %04x\n", i, funcoffset + phi.offset); if (retaddr > funcoffset + phi.offset && retaddr <= funcoffset + phi.endoffset) index = i; } debug(PRINTF) printf("index = %d\n", index); if (dim) { auto phi = &handler_table.handler_info.ptr[index+1]; debug(PRINTF) printf("next finally_offset %p\n", phi.finally_offset); auto prev = cast(InFlight*) &__inflight; auto curr = prev.next; if (curr !is null && curr.addr == cast(void*)(funcoffset + phi.finally_offset)) { auto e = cast(Error)(cast(Throwable) h); if (e !is null && (cast(Error) curr.t) is null) { debug(PRINTF) printf("new error %p bypassing inflight %p\n", h, curr.t); e.bypassedException = curr.t; prev.next = curr.next; //h = cast(Object*) t; } else { debug(PRINTF) printf("replacing thrown %p with inflight %p\n", h, __inflight.t); auto t = curr.t; auto n = curr.t; while (n.next) n = n.next; n.next = cast(Throwable) h; prev.next = curr.next; h = t; } } } // walk through handler table, checking each handler // with an index smaller than the current table_index int prev_ndx; for (auto ndx = index; ndx != -1; ndx = prev_ndx) { auto phi = &handler_table.handler_info.ptr[ndx]; prev_ndx = phi.prev_index; if (phi.cioffset) { // this is a catch handler (no finally) auto pci = cast(DCatchInfo *)(cast(char *)handler_table + phi.cioffset); auto ncatches = pci.ncatches; for (uint i = 0; i < ncatches; i++) { auto ci = **cast(ClassInfo **)h; auto pcb = &pci.catch_block.ptr[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)) = cast(void*)h; // Jump to catch block. Does not return. { size_t catch_esp; fp_t catch_addr; catch_addr = cast(fp_t)(funcoffset + pcb.codeoffset); catch_esp = regebp - handler_table.espoffset - fp_t.sizeof; version (D_InlineAsm_X86) 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 version (D_InlineAsm_X86_64) asm { mov RAX,catch_esp ; mov RCX,catch_esp ; mov RCX,catch_addr ; mov [RAX],RCX ; mov RBP,regebp ; mov RSP,RAX ; // reset stack ret ; // jump to catch block } else static assert(0); } } } } else if (phi.finally_offset) { // 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. debug(PRINTF) printf("calling finally_offset %p\n", phi.finally_offset); auto blockaddr = cast(void*)(funcoffset + phi.finally_offset); InFlight inflight; inflight.addr = blockaddr; inflight.next = __inflight; inflight.t = cast(Throwable) h; __inflight = &inflight; version (Darwin) { version (D_InlineAsm_X86) asm { sub ESP,4 ; push EBX ; mov EBX,blockaddr ; push EBP ; mov EBP,regebp ; call EBX ; pop EBP ; pop EBX ; add ESP,4 ; } else version (D_InlineAsm_X86_64) asm { sub RSP,8 ; push RBX ; mov RBX,blockaddr ; push RBP ; mov RBP,regebp ; call RBX ; pop RBP ; pop RBX ; add RSP,8 ; } else static assert(0); } else { version (D_InlineAsm_X86) asm { push EBX ; mov EBX,blockaddr ; push EBP ; mov EBP,regebp ; call EBX ; pop EBP ; pop EBX ; } else version (D_InlineAsm_X86_64) asm { sub RSP,8 ; push RBX ; mov RBX,blockaddr ; push RBP ; mov RBP,regebp ; call RBX ; pop RBP ; pop RBX ; add RSP,8 ; } else static assert(0); } if (__inflight is &inflight) __inflight = __inflight.next; } } } terminate(); }