/src/rt/deh2.d
D | 503 lines | 367 code | 66 blank | 70 comment | 37 complexity | df6cdcb8520bd3f0f0f6ddeb05e2cee4 MD5 | raw file
1/** 2 * Written in the D programming language. 3 * Implementation of exception handling support routines for Posix and Win64. 4 * 5 * Copyright: Copyright Digital Mars 2000 - 2012. 6 * License: Distributed under the 7 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 8 * (See accompanying file LICENSE) 9 * Authors: Walter Bright, Sean Kelly 10 * Source: $(DRUNTIMESRC src/rt/_deh2.d) 11 */ 12 13module rt.deh2; 14 15version (Posix) 16{ 17 version = deh2; 18} 19else version (Win64) 20{ 21 version = deh2; 22} 23 24// Use deh.d for Win32 25 26version (deh2) 27{ 28 29//debug=1; 30debug import core.stdc.stdio : printf; 31 32extern (C) 33{ 34 version (OSX) 35 { 36 // Set by rt.memory_osx.onAddImage() 37 __gshared ubyte[] _deh_eh_array; 38 } 39 else 40 { 41 extern __gshared 42 { 43 /* Symbols created by the compiler and inserted into the object file 44 * that 'bracket' the __deh_eh segment 45 */ 46 void* _deh_beg; 47 void* _deh_end; 48 } 49 } 50 51 Throwable.TraceInfo _d_traceContext(void* ptr = null); 52 53 int _d_isbaseof(ClassInfo oc, ClassInfo c); 54 55 void _d_createTrace(Object*); 56} 57 58alias int function() fp_t; // function pointer in ambient memory model 59 60// DHandlerInfo table is generated by except_gentables() in eh.c 61 62struct DHandlerInfo 63{ 64 uint offset; // offset from function address to start of guarded section 65 uint endoffset; // offset of end of guarded section 66 int prev_index; // previous table index 67 uint cioffset; // offset to DCatchInfo data from start of table (!=0 if try-catch) 68 size_t finally_offset; // offset to finally code to execute 69 // (!=0 if try-finally) 70} 71 72// Address of DHandlerTable, searched for by eh_finddata() 73 74struct DHandlerTable 75{ 76 uint espoffset; // offset of ESP from EBP 77 uint retoffset; // offset from start of function to return code 78 size_t nhandlers; // dimension of handler_info[] (use size_t to set alignment of handler_info[]) 79 DHandlerInfo handler_info[1]; 80} 81 82struct DCatchBlock 83{ 84 ClassInfo type; // catch type 85 size_t bpoffset; // EBP offset of catch var 86 size_t codeoffset; // catch handler offset 87} 88 89// Create one of these for each try-catch 90struct DCatchInfo 91{ 92 size_t ncatches; // number of catch blocks 93 DCatchBlock catch_block[1]; // data for each catch block 94} 95 96// One of these is generated for each function with try-catch or try-finally 97 98struct FuncTable 99{ 100 void *fptr; // pointer to start of function 101 DHandlerTable *handlertable; // eh data for this function 102 uint fsize; // size of function in bytes 103} 104 105private 106{ 107 struct InFlight 108 { 109 InFlight* next; 110 void* addr; 111 Throwable t; 112 } 113 114 InFlight* __inflight = null; 115} 116 117void terminate() 118{ 119 asm 120 { 121 hlt ; 122 } 123} 124 125/******************************************* 126 * Given address that is inside a function, 127 * figure out which function it is in. 128 * Return DHandlerTable if there is one, NULL if not. 129 */ 130 131FuncTable *__eh_finddata(void *address) 132{ 133 debug printf("FuncTable.sizeof = %p\n", FuncTable.sizeof); 134 debug printf("__eh_finddata(address = %p)\n", address); 135 136 version (OSX) 137 { 138 auto pstart = cast(FuncTable *)_deh_eh_array.ptr; 139 auto pend = cast(FuncTable *)(_deh_eh_array.ptr + _deh_eh_array.length); 140 } 141 else 142 { 143 auto pstart = cast(FuncTable *)&_deh_beg; 144 auto pend = cast(FuncTable *)&_deh_end; 145 } 146 debug printf("_deh_beg = %p, _deh_end = %p\n", pstart, pend); 147 148 for (auto ft = pstart; 1; ft++) 149 { 150 Lagain: 151 if (ft >= pend) 152 break; 153 154 version (Win64) 155 { 156 /* The MS Linker has an inexplicable and erratic tendency to insert 157 * 8 zero bytes between sections generated from different .obj 158 * files. This kludge tries to skip over them. 159 */ 160 if (ft.fptr == null) 161 { 162 ft = cast(FuncTable *)(cast(void**)ft + 1); 163 goto Lagain; 164 } 165 } 166 167 debug printf(" ft = %p, fptr = %p, handlertable = %p, fsize = x%03x\n", 168 ft, ft.fptr, ft.handlertable, ft.fsize); 169 170 void *fptr = ft.fptr; 171 version (Win64) 172 { 173 /* If linked with /DEBUG, the linker rewrites it so the function pointer points 174 * to a JMP to the actual code. The address will be in the actual code, so we 175 * need to follow the JMP. 176 */ 177 if ((cast(ubyte*)fptr)[0] == 0xE9) 178 { // JMP target = RIP of next instruction + signed 32 bit displacement 179 fptr = fptr + 5 + *cast(int*)(fptr + 1); 180 } 181 } 182 183 if (fptr <= address && 184 address < cast(void *)(cast(char *)fptr + ft.fsize)) 185 { 186 debug printf("\tfound handler table\n"); 187 return ft; 188 } 189 } 190 debug printf("\tnot found\n"); 191 return null; 192} 193 194 195/****************************** 196 * Given EBP, find return address to caller, and caller's EBP. 197 * Input: 198 * regbp Value of EBP for current function 199 * *pretaddr Return address 200 * Output: 201 * *pretaddr return address to caller 202 * Returns: 203 * caller's EBP 204 */ 205 206size_t __eh_find_caller(size_t regbp, size_t *pretaddr) 207{ 208 size_t bp = *cast(size_t *)regbp; 209 210 if (bp) // if not end of call chain 211 { 212 // Perform sanity checks on new EBP. 213 // If it is screwed up, terminate() hopefully before we do more damage. 214 if (bp <= regbp) 215 // stack should grow to smaller values 216 terminate(); 217 218 *pretaddr = *cast(size_t *)(regbp + size_t.sizeof); 219 } 220 return bp; 221} 222 223 224/*********************************** 225 * Throw a D object. 226 */ 227 228extern (C) void _d_throwc(Object *h) 229{ 230 size_t regebp; 231 232 debug 233 { 234 printf("_d_throw(h = %p, &h = %p)\n", h, &h); 235 printf("\tvptr = %p\n", *cast(void **)h); 236 } 237 238 version (D_InlineAsm_X86) 239 asm 240 { 241 mov regebp,EBP ; 242 } 243 else version (D_InlineAsm_X86_64) 244 asm 245 { 246 mov regebp,RBP ; 247 } 248 else 249 static assert(0); 250 251 _d_createTrace(h); 252 253//static uint abc; 254//if (++abc == 2) *(char *)0=0; 255 256//int count = 0; 257 while (1) // for each function on the stack 258 { 259 size_t retaddr; 260 261 regebp = __eh_find_caller(regebp,&retaddr); 262 if (!regebp) 263 { // if end of call chain 264 debug printf("end of call chain\n"); 265 break; 266 } 267 268 debug printf("found caller, EBP = %p, retaddr = %p\n", regebp, retaddr); 269//if (++count == 12) *(char*)0=0; 270 auto func_table = __eh_finddata(cast(void *)retaddr); // find static data associated with function 271 auto handler_table = func_table ? func_table.handlertable : null; 272 if (!handler_table) // if no static data 273 { 274 debug printf("no handler table\n"); 275 continue; 276 } 277 auto funcoffset = cast(size_t)func_table.fptr; 278 version (Win64) 279 { 280 /* If linked with /DEBUG, the linker rewrites it so the function pointer points 281 * to a JMP to the actual code. The address will be in the actual code, so we 282 * need to follow the JMP. 283 */ 284 if ((cast(ubyte*)funcoffset)[0] == 0xE9) 285 { // JMP target = RIP of next instruction + signed 32 bit displacement 286 funcoffset = funcoffset + 5 + *cast(int*)(funcoffset + 1); 287 } 288 } 289 auto spoff = handler_table.espoffset; 290 auto retoffset = handler_table.retoffset; 291 292 debug 293 { 294 printf("retaddr = %p\n", retaddr); 295 printf("regebp=%p, funcoffset=%p, spoff=x%x, retoffset=x%x\n", 296 regebp,funcoffset,spoff,retoffset); 297 } 298 299 // Find start index for retaddr in static data 300 auto dim = handler_table.nhandlers; 301 302 debug 303 { 304 printf("handler_info[%d]:\n", dim); 305 for (int i = 0; i < dim; i++) 306 { 307 auto phi = &handler_table.handler_info.ptr[i]; 308 printf("\t[%d]: offset = x%04x, endoffset = x%04x, prev_index = %d, cioffset = x%04x, finally_offset = %x\n", 309 i, phi.offset, phi.endoffset, phi.prev_index, phi.cioffset, phi.finally_offset); 310 } 311 } 312 313 auto index = -1; 314 for (int i = 0; i < dim; i++) 315 { 316 auto phi = &handler_table.handler_info.ptr[i]; 317 318 debug printf("i = %d, phi.offset = %04x\n", i, funcoffset + phi.offset); 319 if (retaddr > funcoffset + phi.offset && 320 retaddr <= funcoffset + phi.endoffset) 321 index = i; 322 } 323 debug printf("index = %d\n", index); 324 325 if (dim) 326 { 327 auto phi = &handler_table.handler_info.ptr[index+1]; 328 debug printf("next finally_offset %p\n", phi.finally_offset); 329 auto prev = cast(InFlight*) &__inflight; 330 auto curr = prev.next; 331 332 if (curr !is null && curr.addr == cast(void*)(funcoffset + phi.finally_offset)) 333 { 334 auto e = cast(Error)(cast(Throwable) h); 335 if (e !is null && (cast(Error) curr.t) is null) 336 { 337 debug printf("new error %p bypassing inflight %p\n", h, curr.t); 338 339 e.bypassedException = curr.t; 340 prev.next = curr.next; 341 //h = cast(Object*) t; 342 } 343 else 344 { 345 debug printf("replacing thrown %p with inflight %p\n", h, __inflight.t); 346 347 auto t = curr.t; 348 auto n = curr.t; 349 350 while (n.next) 351 n = n.next; 352 n.next = cast(Throwable) h; 353 prev.next = curr.next; 354 h = cast(Object*) t; 355 } 356 } 357 } 358 359 // walk through handler table, checking each handler 360 // with an index smaller than the current table_index 361 int prev_ndx; 362 for (auto ndx = index; ndx != -1; ndx = prev_ndx) 363 { 364 auto phi = &handler_table.handler_info.ptr[ndx]; 365 prev_ndx = phi.prev_index; 366 if (phi.cioffset) 367 { 368 // this is a catch handler (no finally) 369 370 auto pci = cast(DCatchInfo *)(cast(char *)handler_table + phi.cioffset); 371 auto ncatches = pci.ncatches; 372 for (int i = 0; i < ncatches; i++) 373 { 374 auto ci = **cast(ClassInfo **)h; 375 376 auto pcb = &pci.catch_block.ptr[i]; 377 378 if (_d_isbaseof(ci, pcb.type)) 379 { 380 // Matched the catch type, so we've found the handler. 381 382 // Initialize catch variable 383 *cast(void **)(regebp + (pcb.bpoffset)) = h; 384 385 // Jump to catch block. Does not return. 386 { 387 size_t catch_esp; 388 fp_t catch_addr; 389 390 catch_addr = cast(fp_t)(funcoffset + pcb.codeoffset); 391 catch_esp = regebp - handler_table.espoffset - fp_t.sizeof; 392 version (D_InlineAsm_X86) 393 asm 394 { 395 mov EAX,catch_esp ; 396 mov ECX,catch_addr ; 397 mov [EAX],ECX ; 398 mov EBP,regebp ; 399 mov ESP,EAX ; // reset stack 400 ret ; // jump to catch block 401 } 402 else version (D_InlineAsm_X86_64) 403 asm 404 { 405 mov RAX,catch_esp ; 406 mov RCX,catch_esp ; 407 mov RCX,catch_addr ; 408 mov [RAX],RCX ; 409 mov RBP,regebp ; 410 mov RSP,RAX ; // reset stack 411 ret ; // jump to catch block 412 } 413 else 414 static assert(0); 415 } 416 } 417 } 418 } 419 else if (phi.finally_offset) 420 { 421 // Call finally block 422 // Note that it is unnecessary to adjust the ESP, as the finally block 423 // accesses all items on the stack as relative to EBP. 424 debug printf("calling finally_offset %p\n", phi.finally_offset); 425 426 auto blockaddr = cast(void*)(funcoffset + phi.finally_offset); 427 InFlight inflight; 428 429 inflight.addr = blockaddr; 430 inflight.next = __inflight; 431 inflight.t = cast(Throwable) h; 432 __inflight = &inflight; 433 434 version (OSX) 435 { 436 version (D_InlineAsm_X86) 437 asm 438 { 439 sub ESP,4 ; 440 push EBX ; 441 mov EBX,blockaddr ; 442 push EBP ; 443 mov EBP,regebp ; 444 call EBX ; 445 pop EBP ; 446 pop EBX ; 447 add ESP,4 ; 448 } 449 else version (D_InlineAsm_X86_64) 450 asm 451 { 452 sub RSP,8 ; 453 push RBX ; 454 mov RBX,blockaddr ; 455 push RBP ; 456 mov RBP,regebp ; 457 call RBX ; 458 pop RBP ; 459 pop RBX ; 460 add RSP,8 ; 461 } 462 else 463 static assert(0); 464 } 465 else 466 { 467 version (D_InlineAsm_X86) 468 asm 469 { 470 push EBX ; 471 mov EBX,blockaddr ; 472 push EBP ; 473 mov EBP,regebp ; 474 call EBX ; 475 pop EBP ; 476 pop EBX ; 477 } 478 else version (D_InlineAsm_X86_64) 479 asm 480 { 481 sub RSP,8 ; 482 push RBX ; 483 mov RBX,blockaddr ; 484 push RBP ; 485 mov RBP,regebp ; 486 call RBX ; 487 pop RBP ; 488 pop RBX ; 489 add RSP,8 ; 490 } 491 else 492 static assert(0); 493 } 494 495 if (__inflight is &inflight) 496 __inflight = __inflight.next; 497 } 498 } 499 } 500 terminate(); 501} 502 503}