1 /** 2 * Stacktracing 3 * 4 * Inclusion of this module activates traced exceptions using the tango own tracers if possible. 5 * 6 * Copyright: Copyright (C) 2009 h3r3tic 7 * License: Tango License, Apache 2.0 8 * Author: Tomasz Stachowiak (h3r3tic) 9 */ 10 module tango.core.tools.WinStackTrace; 11 12 version(Windows) { 13 14 import tango.core.Thread; 15 import tango.core.tools.FrameInfo; 16 17 version(D_Version2) 18 { 19 private const(char)[] intToUtf8 (char[] tmp, uint val) 20 in { 21 assert (tmp.length > 9, "atoi buffer should be more than 9 chars wide"); 22 } 23 body 24 { 25 char* p = tmp.ptr + tmp.length; 26 27 do { 28 *--p = cast(char)((val % 10) + '0'); 29 } while (val /= 10); 30 31 return tmp [cast(size_t)(p - tmp.ptr) .. $]; 32 } 33 34 // function to compare two strings 35 private int stringCompare (in char[] s1, in char[] s2) 36 { 37 auto len = s1.length; 38 39 if (s2.length < len) 40 len = s2.length; 41 42 int result = memcmp(s1.ptr, s2.ptr, len); 43 44 if (result == 0) 45 result = (s1.length<s2.length)?-1:((s1.length==s2.length)?0:1); 46 47 return result; 48 } 49 } 50 51 private { 52 import tango.core.tools.Demangler; 53 import tango.core.Runtime; 54 static import tango.stdc.stdlib; 55 static import tango.stdc.string; 56 version (StacktraceSpam) import tango.stdc.stdio : printf; 57 } 58 59 version = StacktraceTryMatchCallAddresses; 60 version = StacktraceTryToBeSmart; 61 //version = UseCustomFiberForDemangling; 62 version = DemangleFunctionNames; 63 64 struct TraceContext{ 65 LPCONTEXT context; 66 HANDLE hProcess; 67 HANDLE hThread; 68 } 69 70 size_t winAddrBacktrace(TraceContext* winCtx,TraceContext* contextOut,size_t*traceBuf,size_t traceBufLength,int *flags){ 71 CONTEXT context; 72 CONTEXT* ctxPtr = &context; 73 74 HANDLE hProcess = void; 75 HANDLE hThread = void; 76 77 if (winCtx !is null) { 78 ctxPtr=winCtx.context; 79 hProcess=winCtx.hProcess; 80 hThread=winCtx.hThread; 81 } else { 82 uint eipReg, espReg, ebpReg; 83 asm { 84 call GIMMEH_EIP; 85 GIMMEH_EIP: 86 pop EAX; 87 mov eipReg, EAX; 88 mov espReg, ESP; 89 mov ebpReg, EBP; 90 } 91 92 hProcess = GetCurrentProcess(); 93 hThread = GetCurrentThread(); 94 95 context.ContextFlags = CONTEXT_i386 | CONTEXT_CONTROL; 96 GetThreadContext(hThread, &context); 97 context.Eip = eipReg; 98 context.Esp = espReg; 99 context.Ebp = ebpReg; 100 } 101 if (contextOut !is null){ 102 contextOut.context=ctxPtr; 103 contextOut.hProcess=hProcess; 104 contextOut.hThread=hThread; 105 } 106 107 version (StacktraceSpam) printf("Eip: %x, Esp: %x, Ebp: %x\n", ctxPtr.Eip, ctxPtr.Esp, ctxPtr.Ebp); 108 109 version (StacktraceUseWinApiStackWalking) { 110 // IsBadReadPtr will always return true here 111 } else { 112 if (IsBadReadPtr(cast(void*)ctxPtr.Ebp, 4)) { 113 ctxPtr.Ebp = ctxPtr.Esp; 114 } 115 } 116 117 size_t traceLen = 0; 118 walkStack(ctxPtr, hProcess, hThread, delegate void(size_t[]tr){ 119 if (tr.length > traceBufLength) { 120 traceLen = traceBufLength; 121 } else { 122 traceLen = tr.length; 123 } 124 125 traceBuf[0..traceLen] = tr[0..traceLen]; 126 }); 127 128 version(StacktraceTryMatchCallAddresses){ 129 *flags=3; 130 } else { 131 *flags=1; 132 } 133 return traceLen; 134 } 135 136 137 bool winSymbolizeFrameInfo(ref FrameInfo fInfo, const(TraceContext) *context,char[] buf){ 138 HANDLE hProcess; 139 if (context!is null){ 140 hProcess=cast(HANDLE)context.hProcess; 141 } else { 142 hProcess=GetCurrentProcess(); 143 } 144 return addrToSymbolDetails(fInfo.address, hProcess, (const(char)[] func, const(char)[] file, int line, ptrdiff_t addrOffset) { 145 if (func.length > buf.length) { 146 buf[] = func[0..buf.length]; 147 fInfo.func = buf; 148 } else { 149 buf[0..func.length] = func; 150 fInfo.func = buf[0..func.length]; 151 } 152 fInfo.file = file; 153 fInfo.line = line; 154 fInfo.offsetSymb = addrOffset; 155 }); 156 } 157 158 //#line 2 "parts/Main.di" 159 160 161 private extern(C) { 162 void _Dmain(); 163 void D4core6thread5Fiber3runMFZv(); 164 } 165 private { 166 size_t fiberRunFuncLength = 0; 167 } 168 169 struct Context 170 { 171 void* bstack, 172 tstack; 173 Context* within; 174 Context* next, 175 prev; 176 } 177 178 extern(C) Context* D4core6thread6Thread10topContextMFZPS4core6thread6Thread7Context(core.thread.Thread); 179 alias D4core6thread6Thread10topContextMFZPS4core6thread6Thread7Context Thread_topContext; 180 181 182 void walkStack(LPCONTEXT ContextRecord, HANDLE hProcess, HANDLE hThread, void delegate(size_t[]) traceReceiver) { 183 const int maxStackSpace = 32; 184 const int maxHeapSpace = 256; 185 static assert (maxHeapSpace > maxStackSpace); 186 187 size_t[maxStackSpace] stackTraceArr = void; 188 size_t[] heapTraceArr; 189 size_t[] stacktrace = stackTraceArr; 190 uint i = void; 191 192 void addAddress(size_t addr) { 193 if (i < maxStackSpace) { 194 stacktrace[i++] = addr; 195 } else { 196 if (maxStackSpace == i) { 197 if (heapTraceArr is null) { 198 heapTraceArr.alloc(maxHeapSpace, false); 199 heapTraceArr[0..maxStackSpace] = stackTraceArr; 200 stacktrace = heapTraceArr; 201 } 202 stacktrace[i++] = addr; 203 } else if (i < maxHeapSpace) { 204 stacktrace[i++] = addr; 205 } 206 } 207 } 208 209 210 version (StacktraceUseWinApiStackWalking) { 211 STACKFRAME64 frame; 212 memset(&frame, 0, frame.sizeof); 213 214 frame.AddrStack.Offset = ContextRecord.Esp; 215 frame.AddrPC.Offset = ContextRecord.Eip; 216 frame.AddrFrame.Offset = ContextRecord.Ebp; 217 frame.AddrStack.Mode = frame.AddrPC.Mode = frame.AddrFrame.Mode = ADDRESS_MODE.AddrModeFlat; 218 219 //for (int sanity = 0; sanity < 256; ++sanity) { 220 for (i = 0; i < maxHeapSpace; ) { 221 auto swres = StackWalk64( 222 IMAGE_FILE_MACHINE_I386, 223 hProcess, 224 hThread, 225 &frame, 226 ContextRecord, 227 null, 228 SymFunctionTableAccess64, 229 SymGetModuleBase64, 230 null 231 ); 232 233 if (!swres) { 234 break; 235 } 236 237 version (StacktraceSpam) printf("pc:%x ret:%x frm:%x stk:%x parm:%x %x %x %x\n", 238 frame.AddrPC.Offset, frame.AddrReturn.Offset, frame.AddrFrame.Offset, frame.AddrStack.Offset, 239 frame.Params[0], frame.Params[1], frame.Params[2], frame.Params[3]); 240 241 addAddress(frame.AddrPC.Offset); 242 } 243 } else { 244 struct Layout { 245 Layout* ebp; 246 size_t ret; 247 } 248 Layout* p = cast(Layout*)ContextRecord.Esp; 249 250 251 bool foundMain = false; 252 enum Phase { 253 TryEsp, 254 TryEbp, 255 GiveUp 256 } 257 258 Phase phase = ContextRecord.Esp == ContextRecord.Ebp ? Phase.TryEbp : Phase.TryEsp; 259 stacktrace[0] = ContextRecord.Eip; 260 261 version (StacktraceTryToBeSmart) { 262 Thread tobj = Thread.getThis(); 263 } 264 265 while (!foundMain && phase < Phase.GiveUp) { 266 version (StacktraceSpam) printf("starting a new tracing phase\n"); 267 268 version (StacktraceTryToBeSmart) { 269 auto curStack = Thread_topContext(tobj); 270 } 271 272 for (i = 1; p && !IsBadReadPtr(p, Layout.sizeof) && i < maxHeapSpace && !IsBadReadPtr(cast(void*)p.ret, 4);) { 273 auto sym = p.ret; 274 275 enum { 276 NearPtrCallOpcode = 0xe8, 277 RegisterBasedCallOpcode = 0xff 278 } 279 280 uint callAddr = p.ret; 281 if (size_t.sizeof == 4 && !IsBadReadPtr(cast(void*)(p.ret - 5), 8) && NearPtrCallOpcode == *cast(ubyte*)(p.ret - 5)) { 282 callAddr += *cast(uint*)(p.ret - 4); 283 version (StacktraceSpam) printf("ret:%x frm:%x call:%x\n", sym, p, callAddr); 284 version (StacktraceTryMatchCallAddresses) { 285 addAddress(p.ret - 5); // a near call is 5 bytes 286 } 287 } else { 288 version (StacktraceTryMatchCallAddresses) { 289 if (!IsBadReadPtr(cast(void*)p.ret - 2, 4) && RegisterBasedCallOpcode == *cast(ubyte*)(p.ret - 2)) { 290 version (StacktraceSpam) printf("ret:%x frm:%x register-based call:[%x]\n", sym, p, *cast(ubyte*)(p.ret - 1)); 291 addAddress(p.ret - 2); // an offset-less register-based call is 2 bytes for the call + register setup 292 } else if (!IsBadReadPtr(cast(void*)p.ret - 3, 4) && RegisterBasedCallOpcode == *cast(ubyte*)(p.ret - 3)) { 293 version (StacktraceSpam) printf("ret:%x frm:%x register-based call:[%x,%x]\n", sym, p, *cast(ubyte*)(p.ret - 2), *cast(ubyte*)(p.ret - 1)); 294 addAddress(p.ret - 3); // a register-based call is 3 bytes for the call + register setup 295 } else { 296 version (StacktraceSpam) printf("ret:%x frm:%x\n", sym, p); 297 addAddress(p.ret); 298 } 299 } 300 } 301 302 version (StacktraceTryToBeSmart) { 303 bool inFiber = false; 304 if ( 305 callAddr == cast(uint)&_Dmain 306 || true == (inFiber = ( 307 callAddr >= cast(uint)&D4core6thread5Fiber3runMFZv 308 && callAddr < cast(uint)&D4core6thread5Fiber3runMFZv + fiberRunFuncLength 309 )) 310 ) 311 { 312 foundMain = true; 313 if (inFiber) { 314 version (StacktraceSpam) printf("Got or Thread.Fiber.run\n"); 315 316 version (StacktraceTryMatchCallAddresses) { 317 // handled above 318 } else { 319 addAddress(p.ret); 320 } 321 322 curStack = curStack.within; 323 if (curStack) { 324 void* newp = curStack.tstack; 325 326 if (!IsBadReadPtr(newp + 28, 8)) { 327 addAddress(*cast(size_t*)(newp + 32)); 328 p = *cast(Layout**)(newp + 28); 329 continue; 330 } 331 } 332 } else { 333 version (StacktraceSpam) printf("Got _Dmain\n"); 334 } 335 } 336 } 337 338 version (StacktraceTryMatchCallAddresses) { 339 // handled above 340 } else { 341 addAddress(p.ret); 342 } 343 344 p = p.ebp; 345 } 346 347 ++phase; 348 p = cast(Layout*)ContextRecord.Ebp; 349 version (StacktraceSpam) printf("end of phase\n"); 350 } 351 352 version (StacktraceSpam) printf("calling traceReceiver\n"); 353 } 354 355 traceReceiver(stacktrace[0..i]); 356 heapTraceArr.free(); 357 } 358 359 360 bool addrToSymbolDetails(size_t addr, HANDLE hProcess, void delegate(const(char)[] func, const(char)[] file, int line, ptrdiff_t addrOffset) dg) { 361 ubyte[256] buffer; 362 363 SYMBOL_INFO* symbol_info = cast(SYMBOL_INFO*)buffer.ptr; 364 symbol_info.SizeOfStruct = SYMBOL_INFO.sizeof; 365 symbol_info.MaxNameLen = buffer.length - SYMBOL_INFO.sizeof + 1; 366 367 ptrdiff_t addrOffset = 0; 368 auto ln = getAddrDbgInfo(addr, &addrOffset); 369 370 bool success = true; 371 372 char* symname = null; 373 if (!SymFromAddr(hProcess, addr, null, symbol_info)) { 374 //printf("%.*s\n", SysError.lastMsg); 375 symname = ln.func; 376 success = ln != AddrDebugInfo.init; 377 } else { 378 symname = symbol_info.Name.ptr; 379 } 380 381 dg(fromStringz(symname).dup, fromStringz(ln.file).dup, ln.line, addrOffset); 382 return success; 383 } 384 385 386 //#line 2 "parts/Memory.di" 387 private { 388 import tango.stdc.stdlib : cMalloc = malloc, cRealloc = realloc, cFree = free; 389 } 390 391 public { 392 import tango.stdc.string : memset; 393 } 394 395 396 /** 397 Allocate the array using malloc 398 399 Params: 400 array = the array which will be resized 401 numItems = number of items to be allocated in the array 402 init = whether to init the allocated items to their default values or not 403 404 Examples: 405 --- 406 int[] foo; 407 foo.alloc(20); 408 --- 409 410 Remarks: 411 The array must be null and empty for this function to succeed. The rationale behind this is that the coder should state his decision clearly. This will help and has 412 already helped to spot many intricate bugs. 413 */ 414 void alloc(T, intT)(ref T array, intT numItems, bool init = true) 415 in { 416 assert (array is null); 417 assert (numItems >= 0); 418 } 419 out { 420 assert (numItems == array.length); 421 } 422 body { 423 alias typeof(T.init[0]) ItemT; 424 array = (cast(ItemT*)cMalloc(ItemT.sizeof * numItems))[0 .. numItems]; 425 426 static if (is(typeof(ItemT.init))) { 427 if (init) { 428 array[] = ItemT.init; 429 } 430 } 431 } 432 433 434 /** 435 Clone the given array. The result is allocated using alloc() and copied piecewise from the param. Then it's returned 436 */ 437 T clone(T)(T array) { 438 T res; 439 res.alloc(array.length, false); 440 res[] = array[]; 441 return res; 442 } 443 444 445 /** 446 Realloc the contents of an array 447 448 array = the array which will be resized 449 numItems = the new size for the array 450 init = whether to init the newly allocated items to their default values or not 451 452 Examples: 453 --- 454 int[] foo; 455 foo.alloc(20); 456 foo.realloc(10); // <-- 457 --- 458 */ 459 void realloc(T, intT)(ref T array, intT numItems, bool init = true) 460 in { 461 assert (numItems >= 0); 462 } 463 out { 464 assert (numItems == array.length); 465 } 466 body { 467 alias typeof(T.init[0]) ItemT; 468 intT oldLen = array.length; 469 array = (cast(ItemT*)cRealloc(array.ptr, ItemT.sizeof * numItems))[0 .. numItems]; 470 471 static if (is(typeof(ItemT.init))) { 472 if (init && numItems > oldLen) { 473 array[oldLen .. numItems] = ItemT.init; 474 } 475 } 476 } 477 478 479 /** 480 Deallocate an array allocated with alloc() 481 */ 482 void free(T)(ref T array) 483 out { 484 assert (0 == array.length); 485 } 486 body { 487 cFree(array.ptr); 488 array = null; 489 } 490 491 492 /** 493 Append an item to an array. Optionally keep track of an external 'real length', while doing squared reallocation of the array 494 495 Params: 496 array = the array to append the item to 497 elem = the new item to be appended 498 realLength = the optional external 'real length' 499 500 Remarks: 501 if realLength isn't null, the array is not resized by one, but allocated in a std::vector manner. The array's length becomes it's capacity, while 'realLength' 502 is the number of items in the array. 503 504 Examples: 505 --- 506 uint barLen = 0; 507 int[] bar; 508 append(bar, 10, &barLen); 509 append(bar, 20, &barLen); 510 append(bar, 30, &barLen); 511 append(bar, 40, &barLen); 512 assert (bar.length == 16); 513 assert (barLen == 4); 514 --- 515 */ 516 void append(T, I)(ref T array, I elem, uint* realLength = null) { 517 uint len = realLength is null ? array.length : *realLength; 518 uint capacity = array.length; 519 alias typeof(T.init[0]) ItemT; 520 521 if (len >= capacity) { 522 if (realLength is null) { // just add one element to the array 523 int numItems = len+1; 524 array = (cast(ItemT*)cRealloc(array.ptr, ItemT.sizeof * numItems))[0 .. numItems]; 525 } else { // be smarter and allocate in power-of-two increments 526 const uint initialCapacity = 4; 527 int numItems = capacity == 0 ? initialCapacity : capacity * 2; 528 array = (cast(ItemT*)cRealloc(array.ptr, ItemT.sizeof * numItems))[0 .. numItems]; 529 ++*realLength; 530 } 531 } else if (realLength !is null) ++*realLength; 532 533 array[len] = elem; 534 } 535 //#line 2 "parts/WinApi.di" 536 import tango.text.Util; 537 import tango.core.Thread; 538 import tango.core.Array; 539 import tango.sys.Common : SysError; 540 import tango.sys.SharedLib : SharedLib; 541 import tango.stdc.stdio; 542 import tango.stdc.string; 543 import tango.stdc.stringz; 544 545 546 547 548 549 enum { 550 MAX_PATH = 260, 551 } 552 553 enum : WORD { 554 IMAGE_FILE_MACHINE_UNKNOWN = 0, 555 IMAGE_FILE_MACHINE_I386 = 332, 556 IMAGE_FILE_MACHINE_R3000 = 354, 557 IMAGE_FILE_MACHINE_R4000 = 358, 558 IMAGE_FILE_MACHINE_R10000 = 360, 559 IMAGE_FILE_MACHINE_ALPHA = 388, 560 IMAGE_FILE_MACHINE_POWERPC = 496 561 } 562 563 version(X86) { 564 const SIZE_OF_80387_REGISTERS=80; 565 const CONTEXT_i386=0x10000; 566 const CONTEXT_i486=0x10000; 567 const CONTEXT_CONTROL=(CONTEXT_i386|0x00000001L); 568 const CONTEXT_INTEGER=(CONTEXT_i386|0x00000002L); 569 const CONTEXT_SEGMENTS=(CONTEXT_i386|0x00000004L); 570 const CONTEXT_FLOATING_POINT=(CONTEXT_i386|0x00000008L); 571 const CONTEXT_DEBUG_REGISTERS=(CONTEXT_i386|0x00000010L); 572 const CONTEXT_EXTENDED_REGISTERS=(CONTEXT_i386|0x00000020L); 573 const CONTEXT_FULL=(CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_SEGMENTS); 574 const MAXIMUM_SUPPORTED_EXTENSION=512; 575 576 struct FLOATING_SAVE_AREA { 577 DWORD ControlWord; 578 DWORD StatusWord; 579 DWORD TagWord; 580 DWORD ErrorOffset; 581 DWORD ErrorSelector; 582 DWORD DataOffset; 583 DWORD DataSelector; 584 BYTE[80] RegisterArea; 585 DWORD Cr0NpxState; 586 } 587 588 struct CONTEXT { 589 DWORD ContextFlags; 590 DWORD Dr0; 591 DWORD Dr1; 592 DWORD Dr2; 593 DWORD Dr3; 594 DWORD Dr6; 595 DWORD Dr7; 596 FLOATING_SAVE_AREA FloatSave; 597 DWORD SegGs; 598 DWORD SegFs; 599 DWORD SegEs; 600 DWORD SegDs; 601 DWORD Edi; 602 DWORD Esi; 603 DWORD Ebx; 604 DWORD Edx; 605 DWORD Ecx; 606 DWORD Eax; 607 DWORD Ebp; 608 DWORD Eip; 609 DWORD SegCs; 610 DWORD EFlags; 611 DWORD Esp; 612 DWORD SegSs; 613 BYTE[MAXIMUM_SUPPORTED_EXTENSION] ExtendedRegisters; 614 } 615 616 } else { 617 pragma(msg, "Unsupported CPU"); 618 static assert(0); 619 // Versions for PowerPC, Alpha, SHX, and MIPS removed. 620 } 621 622 623 alias CONTEXT* PCONTEXT, LPCONTEXT; 624 625 alias void* HANDLE; 626 627 alias char CHAR; 628 alias void* PVOID, LPVOID; 629 630 alias wchar WCHAR; 631 alias WCHAR* PWCHAR, LPWCH, PWCH, LPWSTR, PWSTR; 632 alias CHAR* PCHAR, LPCH, PCH, LPSTR, PSTR; 633 634 // const versions 635 alias const(WCHAR)* LPCWCH, PCWCH, LPCWSTR, PCWSTR; 636 alias const(CHAR)* LPCCH, PCSTR, LPCSTR; 637 638 version(Unicode) { 639 alias WCHAR TCHAR, _TCHAR; 640 } else { 641 alias CHAR TCHAR, _TCHAR; 642 } 643 644 alias TCHAR* PTCH, PTBYTE, LPTCH, PTSTR, LPTSTR, LP, PTCHAR, LPCTSTR; 645 646 alias ubyte BYTE; 647 alias ubyte* PBYTE, LPBYTE; 648 alias ushort USHORT, WORD, ATOM; 649 alias ushort* PUSHORT, PWORD, LPWORD; 650 alias uint ULONG, DWORD, UINT, COLORREF; 651 alias uint* PULONG, PDWORD, LPDWORD, PUINT, LPUINT; 652 alias int BOOL, INT, LONG; 653 alias HANDLE HMODULE; 654 655 enum : BOOL { 656 FALSE = 0, 657 TRUE = 1, 658 } 659 660 struct EXCEPTION_POINTERS { 661 void* ExceptionRecord; 662 CONTEXT* ContextRecord; 663 } 664 665 version (Win64) { 666 alias long INT_PTR, LONG_PTR; 667 alias ulong UINT_PTR, ULONG_PTR, HANDLE_PTR; 668 } else { 669 alias int INT_PTR, LONG_PTR; 670 alias uint UINT_PTR, ULONG_PTR, HANDLE_PTR; 671 } 672 673 alias ulong ULONG64, DWORD64, UINT64; 674 alias ulong* PULONG64, PDWORD64, PUINT64; 675 676 677 extern(Windows) { 678 HANDLE GetCurrentProcess(); 679 HANDLE GetCurrentThread(); 680 BOOL GetThreadContext(HANDLE, LPCONTEXT); 681 } 682 683 684 void loadWinAPIFunctions() { 685 auto dbghelp = SharedLib.load(`dbghelp.dll`); 686 687 auto SymEnumerateModules64 = cast(fp_SymEnumerateModules64)dbghelp.getSymbol("SymEnumerateModules64"); 688 SymFromAddr = cast(fp_SymFromAddr)dbghelp.getSymbol("SymFromAddr"); 689 assert (SymFromAddr !is null); 690 SymFromName = cast(fp_SymFromName)dbghelp.getSymbol("SymFromName"); 691 assert (SymFromName !is null); 692 SymLoadModule64 = cast(fp_SymLoadModule64)dbghelp.getSymbol("SymLoadModule64"); 693 assert (SymLoadModule64 !is null); 694 SymInitialize = cast(fp_SymInitialize)dbghelp.getSymbol("SymInitialize"); 695 assert (SymInitialize !is null); 696 SymCleanup = cast(fp_SymCleanup)dbghelp.getSymbol("SymCleanup"); 697 assert (SymCleanup !is null); 698 SymSetOptions = cast(fp_SymSetOptions)dbghelp.getSymbol("SymSetOptions"); 699 assert (SymSetOptions !is null); 700 SymGetLineFromAddr64 = cast(fp_SymGetLineFromAddr64)dbghelp.getSymbol("SymGetLineFromAddr64"); 701 assert (SymGetLineFromAddr64 !is null); 702 SymEnumSymbols = cast(fp_SymEnumSymbols)dbghelp.getSymbol("SymEnumSymbols"); 703 assert (SymEnumSymbols !is null); 704 SymGetModuleBase64 = cast(fp_SymGetModuleBase64)dbghelp.getSymbol("SymGetModuleBase64"); 705 assert (SymGetModuleBase64 !is null); 706 StackWalk64 = cast(fp_StackWalk64)dbghelp.getSymbol("StackWalk64"); 707 assert (StackWalk64 !is null); 708 SymFunctionTableAccess64 = cast(fp_SymFunctionTableAccess64)dbghelp.getSymbol("SymFunctionTableAccess64"); 709 assert (SymFunctionTableAccess64 !is null); 710 711 712 auto psapi = SharedLib.load(`psapi.dll`); 713 GetModuleFileNameExA = cast(fp_GetModuleFileNameExA)psapi.getSymbol("GetModuleFileNameExA"); 714 assert (GetModuleFileNameExA !is null); 715 } 716 717 718 719 extern (Windows) { 720 __gshared fp_SymFromAddr SymFromAddr; 721 __gshared fp_SymFromName SymFromName; 722 __gshared fp_SymLoadModule64 SymLoadModule64; 723 __gshared fp_SymInitialize SymInitialize; 724 __gshared fp_SymCleanup SymCleanup; 725 __gshared fp_SymSetOptions SymSetOptions; 726 __gshared fp_SymGetLineFromAddr64 SymGetLineFromAddr64; 727 __gshared fp_SymEnumSymbols SymEnumSymbols; 728 __gshared fp_SymGetModuleBase64 SymGetModuleBase64; 729 __gshared fp_GetModuleFileNameExA GetModuleFileNameExA; 730 __gshared fp_StackWalk64 StackWalk64; 731 __gshared fp_SymFunctionTableAccess64 SymFunctionTableAccess64; 732 733 734 alias DWORD function( 735 DWORD SymOptions 736 ) fp_SymSetOptions; 737 738 enum { 739 SYMOPT_ALLOW_ABSOLUTE_SYMBOLS = 0x00000800, 740 SYMOPT_DEFERRED_LOADS = 0x00000004, 741 SYMOPT_UNDNAME = 0x00000002 742 } 743 744 alias BOOL function( 745 HANDLE hProcess, 746 LPCTSTR UserSearchPath, 747 BOOL fInvadeProcess 748 ) fp_SymInitialize; 749 750 alias BOOL function( 751 HANDLE hProcess 752 ) fp_SymCleanup; 753 754 alias DWORD64 function( 755 HANDLE hProcess, 756 HANDLE hFile, 757 LPCSTR ImageName, 758 LPCSTR ModuleName, 759 DWORD64 BaseOfDll, 760 DWORD SizeOfDll 761 ) fp_SymLoadModule64; 762 763 struct SYMBOL_INFO { 764 ULONG SizeOfStruct; 765 ULONG TypeIndex; 766 ULONG64[2] Reserved; 767 ULONG Index; 768 ULONG Size; 769 ULONG64 ModBase; 770 ULONG Flags; 771 ULONG64 Value; 772 ULONG64 Address; 773 ULONG Register; 774 ULONG Scope; 775 ULONG Tag; 776 ULONG NameLen; 777 ULONG MaxNameLen; 778 TCHAR[1] Name; 779 } 780 alias SYMBOL_INFO* PSYMBOL_INFO; 781 782 alias BOOL function( 783 HANDLE hProcess, 784 DWORD64 Address, 785 PDWORD64 Displacement, 786 PSYMBOL_INFO Symbol 787 ) fp_SymFromAddr; 788 789 alias BOOL function( 790 HANDLE hProcess, 791 PCSTR Name, 792 PSYMBOL_INFO Symbol 793 ) fp_SymFromName; 794 795 alias BOOL function( 796 HANDLE hProcess, 797 PSYM_ENUMMODULES_CALLBACK64 EnumModulesCallback, 798 PVOID UserContext 799 ) fp_SymEnumerateModules64; 800 801 alias BOOL function( 802 LPTSTR ModuleName, 803 DWORD64 BaseOfDll, 804 PVOID UserContext 805 ) PSYM_ENUMMODULES_CALLBACK64; 806 807 const DWORD TH32CS_SNAPPROCESS = 0x00000002; 808 const DWORD TH32CS_SNAPTHREAD = 0x00000004; 809 810 811 enum { 812 MAX_MODULE_NAME32 = 255, 813 TH32CS_SNAPMODULE = 0x00000008, 814 SYMOPT_LOAD_LINES = 0x10, 815 } 816 817 struct IMAGEHLP_LINE64 { 818 DWORD SizeOfStruct; 819 PVOID Key; 820 DWORD LineNumber; 821 PTSTR FileName; 822 DWORD64 Address; 823 } 824 alias IMAGEHLP_LINE64* PIMAGEHLP_LINE64; 825 826 alias BOOL function( 827 HANDLE hProcess, 828 DWORD64 dwAddr, 829 PDWORD pdwDisplacement, 830 PIMAGEHLP_LINE64 Line 831 ) fp_SymGetLineFromAddr64; 832 833 834 alias BOOL function( 835 PSYMBOL_INFO pSymInfo, 836 ULONG SymbolSize, 837 PVOID UserContext 838 ) PSYM_ENUMERATESYMBOLS_CALLBACK; 839 840 alias BOOL function( 841 HANDLE hProcess, 842 ULONG64 BaseOfDll, 843 LPCTSTR Mask, 844 PSYM_ENUMERATESYMBOLS_CALLBACK EnumSymbolsCallback, 845 PVOID UserContext 846 ) fp_SymEnumSymbols; 847 848 849 alias DWORD64 function( 850 HANDLE hProcess, 851 DWORD64 dwAddr 852 ) fp_SymGetModuleBase64; 853 alias fp_SymGetModuleBase64 PGET_MODULE_BASE_ROUTINE64; 854 855 856 alias DWORD function( 857 HANDLE hProcess, 858 HMODULE hModule, 859 LPSTR lpFilename, 860 DWORD nSize 861 ) fp_GetModuleFileNameExA; 862 863 864 enum ADDRESS_MODE { 865 AddrMode1616, 866 AddrMode1632, 867 AddrModeReal, 868 AddrModeFlat 869 } 870 871 struct KDHELP64 { 872 DWORD64 Thread; 873 DWORD ThCallbackStack; 874 DWORD ThCallbackBStore; 875 DWORD NextCallback; 876 DWORD FramePointer; 877 DWORD64 KiCallUserMode; 878 DWORD64 KeUserCallbackDispatcher; 879 DWORD64 SystemRangeStart; 880 DWORD64 KiUserExceptionDispatcher; 881 DWORD64 StackBase; 882 DWORD64 StackLimit; 883 DWORD64[5] Reserved; 884 } 885 alias KDHELP64* PKDHELP64; 886 887 struct ADDRESS64 { 888 DWORD64 Offset; 889 WORD Segment; 890 ADDRESS_MODE Mode; 891 } 892 alias ADDRESS64* LPADDRESS64; 893 894 895 struct STACKFRAME64 { 896 ADDRESS64 AddrPC; 897 ADDRESS64 AddrReturn; 898 ADDRESS64 AddrFrame; 899 ADDRESS64 AddrStack; 900 ADDRESS64 AddrBStore; 901 PVOID FuncTableEntry; 902 DWORD64[4] Params; 903 BOOL Far; 904 BOOL Virtual; 905 DWORD64[3] Reserved; 906 KDHELP64 KdHelp; 907 } 908 alias STACKFRAME64* LPSTACKFRAME64; 909 910 911 912 alias BOOL function( 913 HANDLE hProcess, 914 DWORD64 lpBaseAddress, 915 PVOID lpBuffer, 916 DWORD nSize, 917 LPDWORD lpNumberOfBytesRead 918 ) PREAD_PROCESS_MEMORY_ROUTINE64; 919 920 alias PVOID function( 921 HANDLE hProcess, 922 DWORD64 AddrBase 923 ) PFUNCTION_TABLE_ACCESS_ROUTINE64; 924 alias PFUNCTION_TABLE_ACCESS_ROUTINE64 fp_SymFunctionTableAccess64; 925 926 alias DWORD64 function( 927 HANDLE hProcess, 928 HANDLE hThread, 929 LPADDRESS64 lpaddr 930 ) PTRANSLATE_ADDRESS_ROUTINE64; 931 932 933 alias BOOL function ( 934 DWORD MachineType, 935 HANDLE hProcess, 936 HANDLE hThread, 937 LPSTACKFRAME64 StackFrame, 938 PVOID ContextRecord, 939 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, 940 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, 941 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, 942 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress 943 ) fp_StackWalk64; 944 945 946 BOOL IsBadReadPtr(void*, uint); 947 } 948 949 //#line 2 "parts/DbgInfo.di" 950 import tango.text.Util; 951 import tango.stdc.stdio; 952 import tango.stdc.stringz; 953 import tango.stdc.string : strcpy; 954 import tango.sys.win32.CodePage; 955 import tango.core.Exception; 956 957 958 959 struct AddrDebugInfo { 960 align(1) { 961 size_t addr; 962 char* file; 963 char* func; 964 ushort line; 965 } 966 } 967 968 class ModuleDebugInfo { 969 AddrDebugInfo[] debugInfo; 970 uint debugInfoLen; 971 size_t[char*] fileMaxAddr; 972 char*[] strBuffer; 973 uint strBufferLen; 974 975 void addDebugInfo(size_t addr, char* file, char* func, ushort line) { 976 debugInfo.append(AddrDebugInfo(addr, file, func, line), &debugInfoLen); 977 978 if (auto a = file in fileMaxAddr) { 979 if (addr > *a) *a = addr; 980 } else { 981 fileMaxAddr[file] = addr; 982 } 983 } 984 985 char* bufferString(const(char)[] str) { 986 char[] res; 987 res.alloc(str.length+1, false); 988 res[0..$-1] = str[]; 989 res[str.length] = 0; 990 strBuffer.append(res.ptr, &strBufferLen); 991 return res.ptr; 992 } 993 994 void freeArrays() { 995 debugInfo.free(); 996 debugInfoLen = 0; 997 998 fileMaxAddr = null; 999 foreach (ref s; strBuffer[0..strBufferLen]) { 1000 cFree(s); 1001 } 1002 strBuffer.free(); 1003 strBufferLen = 0; 1004 } 1005 1006 ModuleDebugInfo prev; 1007 ModuleDebugInfo next; 1008 } 1009 1010 class GlobalDebugInfo { 1011 ModuleDebugInfo head; 1012 ModuleDebugInfo tail; 1013 1014 1015 int opApply(scope int delegate(ref ModuleDebugInfo) dg) { 1016 synchronized(this) 1017 { 1018 for (auto it = head; it !is null; it = it.next) { 1019 if (auto res = dg(it)) { 1020 return res; 1021 } 1022 } 1023 } 1024 return 0; 1025 } 1026 1027 1028 void addDebugInfo(ModuleDebugInfo info) { 1029 synchronized(this) 1030 if (head is null) { 1031 head = tail = info; 1032 info.next = info.prev = null; 1033 } else { 1034 tail.next = info; 1035 info.prev = tail; 1036 info.next = null; 1037 tail = info; 1038 } 1039 } 1040 1041 1042 void removeDebugInfo(ModuleDebugInfo info) { 1043 assert (info !is null); 1044 assert (info.next !is null || info.prev !is null || head is info); 1045 synchronized(this) 1046 { 1047 if (info is head) { 1048 head = head.next; 1049 } 1050 if (info is tail) { 1051 tail = tail.prev; 1052 } 1053 if (info.prev) { 1054 info.prev.next = info.next; 1055 } 1056 if (info.next) { 1057 info.next.prev = info.prev; 1058 } 1059 info.freeArrays(); 1060 info.prev = info.next = null; 1061 1062 info.destroy; 1063 } 1064 } 1065 } 1066 1067 private __gshared GlobalDebugInfo globalDebugInfo; 1068 shared static this() { 1069 globalDebugInfo = new GlobalDebugInfo; 1070 } 1071 1072 extern(C) void _initLGPLHostExecutableDebugInfo(const(char)[] progName) { 1073 scope info = new DebugInfo(progName); 1074 // we'll let it die now :) 1075 } 1076 1077 1078 AddrDebugInfo getAddrDbgInfo(size_t a, ptrdiff_t* diff = null) { 1079 AddrDebugInfo bestInfo; 1080 int minDiff = 0x7fffffff; 1081 int bestOff = 0; 1082 const int addBias = 0; 1083 1084 foreach (modInfo; globalDebugInfo) { 1085 bool local = false; 1086 1087 foreach (l; modInfo.debugInfo[0 .. modInfo.debugInfoLen]) { 1088 int diff = a - l.addr - addBias; 1089 1090 // increasing it will make the lookup give results 'higher' in the code (at lower addresses) 1091 // using the value of 1 is recommended when not using version StacktraceTryMatchCallAddresses, 1092 // but it may result in AVs reporting an earlier line in the source code 1093 const int minSymbolOffset = 0; 1094 1095 if (diff < minSymbolOffset) { 1096 continue; 1097 } 1098 1099 int absdiff = diff > 0 ? diff : -diff; 1100 if (absdiff < minDiff) { 1101 minDiff = absdiff; 1102 bestOff = diff; 1103 bestInfo = l; 1104 local = true; 1105 } 1106 } 1107 1108 if (local) { 1109 if (minDiff > 0x100) { 1110 bestInfo = bestInfo.init; 1111 minDiff = 0x7fffffff; 1112 } 1113 else { 1114 if (auto ma = bestInfo.file in modInfo.fileMaxAddr) { 1115 if (a > *ma+addBias) { 1116 bestInfo = bestInfo.init; 1117 minDiff = 0x7fffffff; 1118 } 1119 } else { 1120 version (StacktraceSpam) printf("there ain't '%s' in fileMaxAddr\n", bestInfo.file); 1121 bestInfo = bestInfo.init; 1122 minDiff = 0x7fffffff; 1123 } 1124 } 1125 } 1126 } 1127 1128 if (diff !is null) { 1129 *diff = bestOff; 1130 } 1131 return bestInfo; 1132 } 1133 1134 1135 1136 class DebugInfo { 1137 ModuleDebugInfo info; 1138 1139 1140 this(const(char)[] filename) { 1141 info = new ModuleDebugInfo; 1142 ParseCVFile(filename); 1143 assert (globalDebugInfo !is null); 1144 globalDebugInfo.addDebugInfo(info); 1145 } 1146 1147 private { 1148 int ParseCVFile(const(char)[] filename) { 1149 FILE* debugfile; 1150 1151 if (filename == "") return (-1); 1152 1153 //try { 1154 debugfile = fopen((filename ~ "\0").ptr, "rb"); 1155 /+} catch(Exception e){ 1156 return -1; 1157 }+/ 1158 1159 if (!ParseFileHeaders (debugfile)) return -1; 1160 1161 g_secthdrs.length = g_nthdr.FileHeader.NumberOfSections; 1162 1163 if (!ParseSectionHeaders (debugfile)) return -1; 1164 1165 g_debugdirs.length = g_nthdr.OptionalHeader.DataDirectory[IMAGE_FILE_DEBUG_DIRECTORY].Size / 1166 IMAGE_DEBUG_DIRECTORY.sizeof; 1167 1168 if (!ParseDebugDir (debugfile)) return -1; 1169 if (g_dwStartOfCodeView == 0) return -1; 1170 if (!ParseCodeViewHeaders (debugfile)) return -1; 1171 if (!ParseAllModules (debugfile)) return -1; 1172 1173 g_dwStartOfCodeView = 0; 1174 g_exe_mode = true; 1175 g_secthdrs = null; 1176 g_debugdirs = null; 1177 g_cvEntries = null; 1178 g_cvModules = null; 1179 g_filename = null; 1180 g_filenameStringz = null; 1181 1182 fclose(debugfile); 1183 return 0; 1184 } 1185 1186 bool ParseFileHeaders(FILE* debugfile) { 1187 CVHeaderType hdrtype; 1188 1189 hdrtype = GetHeaderType (debugfile); 1190 1191 if (hdrtype == CVHeaderType.DOS) { 1192 if (!ReadDOSFileHeader (debugfile, &g_doshdr))return false; 1193 hdrtype = GetHeaderType (debugfile); 1194 } 1195 if (hdrtype == CVHeaderType.NT) { 1196 if (!ReadPEFileHeader (debugfile, &g_nthdr)) return false; 1197 } 1198 1199 return true; 1200 } 1201 1202 CVHeaderType GetHeaderType(FILE* debugfile) { 1203 ushort hdrtype; 1204 CVHeaderType ret = CVHeaderType.NONE; 1205 1206 int oldpos = ftell(debugfile); 1207 1208 if (!ReadChunk (debugfile, &hdrtype, ushort.sizeof, -1)){ 1209 fseek(debugfile, oldpos, SEEK_SET); 1210 return CVHeaderType.NONE; 1211 } 1212 1213 if (hdrtype == 0x5A4D) // "MZ" 1214 ret = CVHeaderType.DOS; 1215 else if (hdrtype == 0x4550) // "PE" 1216 ret = CVHeaderType.NT; 1217 else if (hdrtype == 0x4944) // "DI" 1218 ret = CVHeaderType.DBG; 1219 1220 fseek(debugfile, oldpos, SEEK_SET); 1221 1222 return ret; 1223 } 1224 1225 /* 1226 * Extract the DOS file headers from an executable 1227 */ 1228 bool ReadDOSFileHeader(FILE* debugfile, IMAGE_DOS_HEADER *doshdr) { 1229 uint bytes_read; 1230 1231 bytes_read = fread(doshdr, 1, IMAGE_DOS_HEADER.sizeof, debugfile); 1232 if (bytes_read < IMAGE_DOS_HEADER.sizeof){ 1233 return false; 1234 } 1235 1236 // Skip over stub data, if present 1237 if (doshdr.e_lfanew) { 1238 fseek(debugfile, doshdr.e_lfanew, SEEK_SET); 1239 } 1240 1241 return true; 1242 } 1243 1244 /* 1245 * Extract the DOS and NT file headers from an executable 1246 */ 1247 bool ReadPEFileHeader(FILE* debugfile, IMAGE_NT_HEADERS *nthdr) { 1248 uint bytes_read; 1249 1250 bytes_read = fread(nthdr, 1, IMAGE_NT_HEADERS.sizeof, debugfile); 1251 if (bytes_read < IMAGE_NT_HEADERS.sizeof) { 1252 return false; 1253 } 1254 1255 return true; 1256 } 1257 1258 bool ParseSectionHeaders(FILE* debugfile) { 1259 if (!ReadSectionHeaders (debugfile, g_secthdrs)) return false; 1260 return true; 1261 } 1262 1263 bool ReadSectionHeaders(FILE* debugfile, ref IMAGE_SECTION_HEADER[] secthdrs) { 1264 for(int i=0;i<secthdrs.length;i++){ 1265 uint bytes_read; 1266 bytes_read = fread((§hdrs[i]), 1, IMAGE_SECTION_HEADER.sizeof, debugfile); 1267 if (bytes_read < 1){ 1268 return false; 1269 } 1270 } 1271 return true; 1272 } 1273 1274 bool ParseDebugDir(FILE* debugfile) { 1275 int i; 1276 int filepos; 1277 1278 if (g_debugdirs.length == 0) return false; 1279 1280 filepos = GetOffsetFromRVA (g_nthdr.OptionalHeader.DataDirectory[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress); 1281 1282 fseek(debugfile, filepos, SEEK_SET); 1283 1284 if (!ReadDebugDir (debugfile, g_debugdirs)) return false; 1285 1286 for (i = 0; i < g_debugdirs.length; i++) { 1287 enum { 1288 IMAGE_DEBUG_TYPE_CODEVIEW = 2, 1289 } 1290 1291 if (g_debugdirs[i].Type == IMAGE_DEBUG_TYPE_CODEVIEW) { 1292 g_dwStartOfCodeView = g_debugdirs[i].PointerToRawData; 1293 } 1294 } 1295 1296 g_debugdirs = null; 1297 1298 return true; 1299 } 1300 1301 // Calculate the file offset, based on the RVA. 1302 uint GetOffsetFromRVA(uint rva) { 1303 int i; 1304 uint sectbegin; 1305 1306 for (i = g_secthdrs.length - 1; i >= 0; i--) { 1307 sectbegin = g_secthdrs[i].VirtualAddress; 1308 if (rva >= sectbegin) break; 1309 } 1310 uint offset = g_secthdrs[i].VirtualAddress - g_secthdrs[i].PointerToRawData; 1311 uint filepos = rva - offset; 1312 return filepos; 1313 } 1314 1315 // Load in the debug directory table. This directory describes the various 1316 // blocks of debug data that reside at the end of the file (after the COFF 1317 // sections), including FPO data, COFF-style debug info, and the CodeView 1318 // we are *really* after. 1319 bool ReadDebugDir(FILE* debugfile, ref IMAGE_DEBUG_DIRECTORY[] debugdirs) { 1320 uint bytes_read; 1321 for(int i=0;i<debugdirs.length;i++) { 1322 bytes_read = fread((&debugdirs[i]), 1, IMAGE_DEBUG_DIRECTORY.sizeof, debugfile); 1323 if (bytes_read < IMAGE_DEBUG_DIRECTORY.sizeof) { 1324 return false; 1325 } 1326 } 1327 return true; 1328 } 1329 1330 bool ParseCodeViewHeaders(FILE* debugfile) { 1331 fseek(debugfile, g_dwStartOfCodeView, SEEK_SET); 1332 if (!ReadCodeViewHeader (debugfile, g_cvSig, g_cvHeader)) return false; 1333 g_cvEntries.length = g_cvHeader.cDir; 1334 if (!ReadCodeViewDirectory (debugfile, g_cvEntries)) return false; 1335 return true; 1336 } 1337 1338 1339 bool ReadCodeViewHeader(FILE* debugfile, out OMFSignature sig, out OMFDirHeader dirhdr) { 1340 uint bytes_read; 1341 1342 bytes_read = fread((&sig), 1, OMFSignature.sizeof, debugfile); 1343 if (bytes_read < OMFSignature.sizeof){ 1344 return false; 1345 } 1346 1347 fseek(debugfile, sig.filepos + g_dwStartOfCodeView, SEEK_SET); 1348 bytes_read = fread((&dirhdr), 1, OMFDirHeader.sizeof, debugfile); 1349 if (bytes_read < OMFDirHeader.sizeof){ 1350 return false; 1351 } 1352 return true; 1353 } 1354 1355 bool ReadCodeViewDirectory(FILE* debugfile, ref OMFDirEntry[] entries) { 1356 uint bytes_read; 1357 1358 for(int i=0;i<entries.length;i++){ 1359 bytes_read = fread((&entries[i]), 1, OMFDirEntry.sizeof, debugfile); 1360 if (bytes_read < OMFDirEntry.sizeof){ 1361 return false; 1362 } 1363 } 1364 return true; 1365 } 1366 1367 bool ParseAllModules (FILE* debugfile) { 1368 if (g_cvHeader.cDir == 0){ 1369 return true; 1370 } 1371 1372 if (g_cvEntries.length == 0){ 1373 return false; 1374 } 1375 1376 fseek(debugfile, g_dwStartOfCodeView + g_cvEntries[0].lfo, SEEK_SET); 1377 1378 if (!ReadModuleData (debugfile, g_cvEntries, g_cvModules)){ 1379 return false; 1380 } 1381 1382 1383 for (int i = 0; i < g_cvModules.length; i++){ 1384 ParseRelatedSections (i, debugfile); 1385 } 1386 1387 return true; 1388 } 1389 1390 1391 bool ReadModuleData(FILE* debugfile, OMFDirEntry[] entries, out OMFModuleFull[] modules) { 1392 uint bytes_read; 1393 int pad; 1394 1395 int module_bytes = (ushort.sizeof * 3) + (char.sizeof * 2); 1396 1397 if (entries == null) return false; 1398 1399 modules.length = 0; 1400 1401 for (int i = 0; i < entries.length; i++){ 1402 if (entries[i].SubSection == sstModule) 1403 modules.length = modules.length + 1; 1404 } 1405 1406 for (int i = 0; i < modules.length; i++){ 1407 1408 bytes_read = fread((&modules[i]), 1, module_bytes, debugfile); 1409 if (bytes_read < module_bytes){ 1410 return false; 1411 } 1412 1413 int segnum = modules[i].cSeg; 1414 OMFSegDesc[] segarray; 1415 segarray.length=segnum; 1416 for(int j=0;j<segnum;j++){ 1417 bytes_read = fread((&segarray[j]), 1, OMFSegDesc.sizeof, debugfile); 1418 if (bytes_read < OMFSegDesc.sizeof){ 1419 return false; 1420 } 1421 } 1422 modules[i].SegInfo = segarray.ptr; 1423 1424 char namelen; 1425 bytes_read = fread((&namelen), 1, char.sizeof, debugfile); 1426 if (bytes_read < 1){ 1427 return false; 1428 } 1429 1430 pad = ((namelen + 1) % 4); 1431 if (pad) namelen += (4 - pad); 1432 1433 modules[i].Name = (new char[namelen+1]).ptr; 1434 modules[i].Name[namelen]=0; 1435 bytes_read = fread((modules[i].Name), 1, namelen, debugfile); 1436 if (bytes_read < namelen){ 1437 return false; 1438 } 1439 } 1440 return true; 1441 } 1442 1443 bool ParseRelatedSections(int index, FILE* debugfile) { 1444 int i; 1445 1446 if (g_cvEntries == null) 1447 return false; 1448 1449 for (i = 0; i < g_cvHeader.cDir; i++){ 1450 if (g_cvEntries[i].iMod != (index + 1) || 1451 g_cvEntries[i].SubSection == sstModule) 1452 continue; 1453 1454 switch (g_cvEntries[i].SubSection){ 1455 case sstSrcModule: 1456 ParseSrcModuleInfo (i, debugfile); 1457 break; 1458 default: 1459 break; 1460 } 1461 } 1462 1463 return true; 1464 } 1465 1466 bool ParseSrcModuleInfo (int index, FILE* debugfile) { 1467 int i; 1468 1469 byte *rawdata; 1470 byte *curpos; 1471 short filecount; 1472 short segcount; 1473 1474 int moduledatalen; 1475 int filedatalen; 1476 int linedatalen; 1477 1478 if (g_cvEntries == null || debugfile == null || 1479 g_cvEntries[index].SubSection != sstSrcModule) 1480 return false; 1481 1482 int fileoffset = g_dwStartOfCodeView + g_cvEntries[index].lfo; 1483 1484 rawdata = (new byte[g_cvEntries[index].cb]).ptr; 1485 if (!rawdata) return false; 1486 1487 if (!ReadChunk (debugfile, rawdata, g_cvEntries[index].cb, fileoffset)) return false; 1488 uint[] baseSrcFile; 1489 ExtractSrcModuleInfo (rawdata, &filecount, &segcount,baseSrcFile); 1490 1491 for(i=0;i<baseSrcFile.length;i++){ 1492 uint[] baseSrcLn; 1493 ExtractSrcModuleFileInfo (rawdata+baseSrcFile[i],baseSrcLn); 1494 for(int j=0;j<baseSrcLn.length;j++){ 1495 ExtractSrcModuleLineInfo (rawdata+baseSrcLn[j], j); 1496 } 1497 } 1498 1499 return true; 1500 } 1501 1502 void ExtractSrcModuleInfo (byte* rawdata, short *filecount, short *segcount,out uint[] fileinfopos) { 1503 int i; 1504 int datalen; 1505 1506 short cFile; 1507 short cSeg; 1508 uint *baseSrcFile; 1509 uint *segarray; 1510 ushort *segindexarray; 1511 1512 cFile = *cast(short*)rawdata; 1513 cSeg = *cast(short*)(rawdata + 2); 1514 baseSrcFile = cast(uint*)(rawdata + 4); 1515 segarray = &baseSrcFile[cFile]; 1516 segindexarray = cast(ushort*)(&segarray[cSeg * 2]); 1517 1518 *filecount = cFile; 1519 *segcount = cSeg; 1520 1521 fileinfopos.length=cFile; 1522 for (i = 0; i < cFile; i++) { 1523 fileinfopos[i]=baseSrcFile[i]; 1524 } 1525 } 1526 1527 void ExtractSrcModuleFileInfo(byte* rawdata,out uint[] offset) { 1528 int i; 1529 int datalen; 1530 1531 short cSeg; 1532 uint *baseSrcLn; 1533 uint *segarray; 1534 byte cFName; 1535 1536 cSeg = *cast(short*)(rawdata); 1537 // Skip the 'pad' field 1538 baseSrcLn = cast(uint*)(rawdata + 4); 1539 segarray = &baseSrcLn[cSeg]; 1540 cFName = *(cast(byte*)&segarray[cSeg*2]); 1541 1542 g_filename = (cast(char*)&segarray[cSeg*2] + 1)[0..cFName].dup; 1543 g_filenameStringz = info.bufferString(g_filename); 1544 1545 offset.length=cSeg; 1546 for (i = 0; i < cSeg; i++){ 1547 offset[i]=baseSrcLn[i]; 1548 } 1549 } 1550 1551 void ExtractSrcModuleLineInfo(byte* rawdata, int tablecount) { 1552 int i; 1553 1554 ushort Seg; 1555 ushort cPair; 1556 uint *offset; 1557 ushort *linenumber; 1558 1559 Seg = *cast(ushort*)rawdata; 1560 cPair = *cast(ushort*)(rawdata + 2); 1561 offset = cast(uint*)(rawdata + 4); 1562 linenumber = cast(ushort*)&offset[cPair]; 1563 1564 uint base=0; 1565 if (Seg != 0){ 1566 base = g_nthdr.OptionalHeader.ImageBase+g_secthdrs[Seg-1].VirtualAddress; 1567 } 1568 1569 for (i = 0; i < cPair; i++) { 1570 uint address = offset[i]+base; 1571 info.addDebugInfo(address, g_filenameStringz, null, linenumber[i]); 1572 } 1573 } 1574 1575 1576 bool ReadChunk(FILE* debugfile, void *dest, int length, int fileoffset) { 1577 uint bytes_read; 1578 1579 if (fileoffset >= 0) { 1580 fseek(debugfile, fileoffset, SEEK_SET); 1581 } 1582 1583 bytes_read = fread(dest, 1, length, debugfile); 1584 if (bytes_read < length) { 1585 return false; 1586 } 1587 1588 return true; 1589 } 1590 1591 1592 enum CVHeaderType : int { 1593 NONE, 1594 DOS, 1595 NT, 1596 DBG 1597 } 1598 1599 int g_dwStartOfCodeView = 0; 1600 1601 bool g_exe_mode = true; 1602 IMAGE_DOS_HEADER g_doshdr; 1603 IMAGE_SEPARATE_DEBUG_HEADER g_dbghdr; 1604 IMAGE_NT_HEADERS g_nthdr; 1605 1606 IMAGE_SECTION_HEADER[] g_secthdrs; 1607 1608 IMAGE_DEBUG_DIRECTORY[] g_debugdirs; 1609 OMFSignature g_cvSig; 1610 OMFDirHeader g_cvHeader; 1611 OMFDirEntry[] g_cvEntries; 1612 OMFModuleFull[] g_cvModules; 1613 const(char)[] g_filename; 1614 char* g_filenameStringz; 1615 } 1616 } 1617 1618 1619 1620 1621 enum { 1622 IMAGE_FILE_DEBUG_DIRECTORY = 6 1623 } 1624 1625 enum { 1626 sstModule = 0x120, 1627 sstSrcModule = 0x127, 1628 sstGlobalPub = 0x12a, 1629 } 1630 1631 struct OMFSignature { 1632 char[4] Signature; 1633 int filepos; 1634 } 1635 1636 struct OMFDirHeader { 1637 ushort cbDirHeader; 1638 ushort cbDirEntry; 1639 uint cDir; 1640 int lfoNextDir; 1641 uint flags; 1642 } 1643 1644 struct OMFDirEntry { 1645 ushort SubSection; 1646 ushort iMod; 1647 int lfo; 1648 uint cb; 1649 } 1650 1651 struct OMFSegDesc { 1652 ushort Seg; 1653 ushort pad; 1654 uint Off; 1655 uint cbSeg; 1656 } 1657 1658 struct OMFModule { 1659 ushort ovlNumber; 1660 ushort iLib; 1661 ushort cSeg; 1662 char[2] Style; 1663 } 1664 1665 struct OMFModuleFull { 1666 ushort ovlNumber; 1667 ushort iLib; 1668 ushort cSeg; 1669 char[2] Style; 1670 OMFSegDesc *SegInfo; 1671 char *Name; 1672 } 1673 1674 struct OMFSymHash { 1675 ushort symhash; 1676 ushort addrhash; 1677 uint cbSymbol; 1678 uint cbHSym; 1679 uint cbHAddr; 1680 } 1681 1682 struct DATASYM16 { 1683 ushort reclen; // Record length 1684 ushort rectyp; // S_LDATA or S_GDATA 1685 int off; // offset of symbol 1686 ushort seg; // segment of symbol 1687 ushort typind; // Type index 1688 byte[1] name; // Length-prefixed name 1689 } 1690 alias DATASYM16 PUBSYM16; 1691 1692 1693 struct IMAGE_DOS_HEADER { // DOS .EXE header 1694 ushort e_magic; // Magic number 1695 ushort e_cblp; // Bytes on last page of file 1696 ushort e_cp; // Pages in file 1697 ushort e_crlc; // Relocations 1698 ushort e_cparhdr; // Size of header in paragraphs 1699 ushort e_minalloc; // Minimum extra paragraphs needed 1700 ushort e_maxalloc; // Maximum extra paragraphs needed 1701 ushort e_ss; // Initial (relative) SS value 1702 ushort e_sp; // Initial SP value 1703 ushort e_csum; // Checksum 1704 ushort e_ip; // Initial IP value 1705 ushort e_cs; // Initial (relative) CS value 1706 ushort e_lfarlc; // File address of relocation table 1707 ushort e_ovno; // Overlay number 1708 ushort[4] e_res; // Reserved words 1709 ushort e_oemid; // OEM identifier (for e_oeminfo) 1710 ushort e_oeminfo; // OEM information; e_oemid specific 1711 ushort[10] e_res2; // Reserved words 1712 int e_lfanew; // File address of new exe header 1713 } 1714 1715 struct IMAGE_FILE_HEADER { 1716 ushort Machine; 1717 ushort NumberOfSections; 1718 uint TimeDateStamp; 1719 uint PointerToSymbolTable; 1720 uint NumberOfSymbols; 1721 ushort SizeOfOptionalHeader; 1722 ushort Characteristics; 1723 } 1724 1725 struct IMAGE_SEPARATE_DEBUG_HEADER { 1726 ushort Signature; 1727 ushort Flags; 1728 ushort Machine; 1729 ushort Characteristics; 1730 uint TimeDateStamp; 1731 uint CheckSum; 1732 uint ImageBase; 1733 uint SizeOfImage; 1734 uint NumberOfSections; 1735 uint ExportedNamesSize; 1736 uint DebugDirectorySize; 1737 uint SectionAlignment; 1738 uint[2] Reserved; 1739 } 1740 1741 struct IMAGE_DATA_DIRECTORY { 1742 uint VirtualAddress; 1743 uint Size; 1744 } 1745 1746 struct IMAGE_OPTIONAL_HEADER { 1747 // 1748 // Standard fields. 1749 // 1750 1751 ushort Magic; 1752 byte MajorLinkerVersion; 1753 byte MinorLinkerVersion; 1754 uint SizeOfCode; 1755 uint SizeOfInitializedData; 1756 uint SizeOfUninitializedData; 1757 uint AddressOfEntryPoint; 1758 uint BaseOfCode; 1759 uint BaseOfData; 1760 1761 // 1762 // NT additional fields. 1763 // 1764 1765 uint ImageBase; 1766 uint SectionAlignment; 1767 uint FileAlignment; 1768 ushort MajorOperatingSystemVersion; 1769 ushort MinorOperatingSystemVersion; 1770 ushort MajorImageVersion; 1771 ushort MinorImageVersion; 1772 ushort MajorSubsystemVersion; 1773 ushort MinorSubsystemVersion; 1774 uint Win32VersionValue; 1775 uint SizeOfImage; 1776 uint SizeOfHeaders; 1777 uint CheckSum; 1778 ushort Subsystem; 1779 ushort DllCharacteristics; 1780 uint SizeOfStackReserve; 1781 uint SizeOfStackCommit; 1782 uint SizeOfHeapReserve; 1783 uint SizeOfHeapCommit; 1784 uint LoaderFlags; 1785 uint NumberOfRvaAndSizes; 1786 1787 enum { 1788 IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16, 1789 } 1790 1791 IMAGE_DATA_DIRECTORY[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] DataDirectory; 1792 } 1793 1794 struct IMAGE_NT_HEADERS { 1795 uint Signature; 1796 IMAGE_FILE_HEADER FileHeader; 1797 IMAGE_OPTIONAL_HEADER OptionalHeader; 1798 } 1799 1800 enum { 1801 IMAGE_SIZEOF_SHORT_NAME = 8, 1802 } 1803 1804 struct IMAGE_SECTION_HEADER { 1805 byte[IMAGE_SIZEOF_SHORT_NAME] Name;//8 1806 union misc{ 1807 uint PhysicalAddress; 1808 uint VirtualSize;//12 1809 } 1810 misc Misc; 1811 uint VirtualAddress;//16 1812 uint SizeOfRawData;//20 1813 uint PointerToRawData;//24 1814 uint PointerToRelocations;//28 1815 uint PointerToLinenumbers;//32 1816 ushort NumberOfRelocations;//34 1817 ushort NumberOfLinenumbers;//36 1818 uint Characteristics;//40 1819 } 1820 1821 struct IMAGE_DEBUG_DIRECTORY { 1822 uint Characteristics; 1823 uint TimeDateStamp; 1824 ushort MajorVersion; 1825 ushort MinorVersion; 1826 uint Type; 1827 uint SizeOfData; 1828 uint AddressOfRawData; 1829 uint PointerToRawData; 1830 } 1831 1832 struct OMFSourceLine { 1833 ushort Seg; 1834 ushort cLnOff; 1835 uint[1] offset; 1836 ushort[1] lineNbr; 1837 } 1838 1839 struct OMFSourceFile { 1840 ushort cSeg; 1841 ushort reserved; 1842 uint[1] baseSrcLn; 1843 ushort cFName; 1844 char Name; 1845 } 1846 1847 struct OMFSourceModule { 1848 ushort cFile; 1849 ushort cSeg; 1850 uint[1] baseSrcFile; 1851 } 1852 //#line 2 "parts/CInterface.di" 1853 extern (C) { 1854 ModuleDebugInfo ModuleDebugInfo_new() { 1855 return new ModuleDebugInfo; 1856 } 1857 1858 void ModuleDebugInfo_addDebugInfo(ModuleDebugInfo minfo, size_t addr, char* file, char* func, ushort line) { 1859 minfo.addDebugInfo(addr, file, func, line); 1860 } 1861 1862 char* ModuleDebugInfo_bufferString(ModuleDebugInfo minfo, char[] str) { 1863 char[] res; 1864 res.alloc(str.length+1, false); 1865 res[0..$-1] = str[]; 1866 res[str.length] = 0; 1867 minfo.strBuffer.append(res.ptr, &minfo.strBufferLen); 1868 return res.ptr; 1869 } 1870 1871 void GlobalDebugInfo_addDebugInfo(ModuleDebugInfo minfo) { 1872 globalDebugInfo.addDebugInfo(minfo); 1873 } 1874 1875 void GlobalDebugInfo_removeDebugInfo(ModuleDebugInfo minfo) { 1876 globalDebugInfo.removeDebugInfo(minfo); 1877 } 1878 } 1879 //#line 2 "parts/Init.di" 1880 shared static this() { 1881 loadWinAPIFunctions(); 1882 1883 for (fiberRunFuncLength = 0; fiberRunFuncLength < 0x100; ++fiberRunFuncLength) { 1884 ubyte* ptr = cast(ubyte*)&D4core6thread5Fiber3runMFZv + fiberRunFuncLength; 1885 enum { 1886 RetOpcode = 0xc3 1887 } 1888 if (IsBadReadPtr(ptr, 1) || RetOpcode == *ptr) { 1889 break; 1890 } 1891 } 1892 1893 version (StacktraceSpam) printf ("found Thread.Fiber.run at %p with length %x", 1894 &D4core6thread5Fiber3runMFZv, fiberRunFuncLength); 1895 1896 char[512] modNameBuf = 0; 1897 int modNameLen = GetModuleFileNameExA(GetCurrentProcess(), null, modNameBuf.ptr, modNameBuf.length-1); 1898 char[] modName = modNameBuf[0..modNameLen]; 1899 SymSetOptions(SYMOPT_DEFERRED_LOADS/+ | SYMOPT_UNDNAME+/); 1900 SymInitialize(GetCurrentProcess(), null, false); 1901 DWORD64 base; 1902 if (0 == (base = SymLoadModule64(GetCurrentProcess(), HANDLE.init, modName.ptr, null, 0, 0))) { 1903 if (SysError.lastCode != 0) { 1904 throw new Exception("Could not SymLoadModule64: " ~ SysError.lastMsg.idup); 1905 } 1906 } 1907 1908 size_t slash_idx; 1909 for(slash_idx = modName.length - 1; slash_idx >= 0; slash_idx--) 1910 { 1911 if(modName[slash_idx] == '\\') 1912 break; 1913 } 1914 auto sym_name = modName[slash_idx + 1..$-4] ~ "!__initLGPLHostExecutableDebugInfo\0"; 1915 1916 SYMBOL_INFO sym; 1917 sym.SizeOfStruct = SYMBOL_INFO.sizeof; 1918 1919 extern(C) void function(const(char)[]) initTrace; 1920 if (SymFromName(GetCurrentProcess(), sym_name.ptr, &sym)) { 1921 initTrace = cast(typeof(initTrace))sym.Address; 1922 assert (initTrace !is null); 1923 initTrace(modName); 1924 } else { 1925 throw new Exception ("Can't initialize the TangoTrace LGPL stuff"); 1926 } 1927 } 1928 1929 }