1 /** 2 * This module exposes functionality for inspecting and manipulating memory. 3 * 4 * Copyright: Copyright (C) 2005-2006 Digital Mars, www.digitalmars.com. 5 * All rights reserved. 6 * License: 7 * This software is provided 'as-is', without any express or implied 8 * warranty. In no event will the authors be held liable for any damages 9 * arising from the use of this software. 10 * 11 * Permission is granted to anyone to use this software for any purpose, 12 * including commercial applications, and to alter it and redistribute it 13 * freely, in both source and binary form, subject to the following 14 * restrictions: 15 * 16 * o The origin of this software must not be misrepresented; you must not 17 * claim that you wrote the original software. If you use this software 18 * in a product, an acknowledgment in the product documentation would be 19 * appreciated but is not required. 20 * o Altered source versions must be plainly marked as such, and must not 21 * be misrepresented as being the original software. 22 * o This notice may not be removed or altered from any source 23 * distribution. 24 * Authors: Walter Bright, Sean Kelly 25 */ 26 27 /* NOTE: This implementation is borrowed from the LDC tango runtime, and 28 patched to work with the GDC compiler. 29 30 Modified by Iain Buclaw, October 2010 31 */ 32 module rt.compiler.gdc.rt.memory; 33 34 import gcc.builtins; 35 36 version = GC_Use_Dynamic_Ranges; 37 38 version(darwin) 39 { 40 version = GC_Use_Data_Dyld; 41 version = GC_Use_Dynamic_Ranges; 42 import tango.stdc.config : c_ulong; 43 } 44 else version(Posix) 45 { 46 version = GC_Use_Data_Proc_Maps; 47 } 48 else version(solaris) 49 { 50 version = GC_Use_Data_Proc_Maps; 51 } 52 else version(freebsd) 53 { 54 version = GC_Use_Data_Proc_Maps; 55 } 56 57 58 version(GC_Use_Data_Proc_Maps) 59 { 60 version(Posix) {} else { 61 static assert(false, "Proc Maps only supported on Posix systems"); 62 } 63 import tango.stdc.string : memmove; 64 import tango.stdc.posix.fcntl : open, O_RDONLY; 65 import tango.stdc.posix.unistd : close, read; 66 67 version = GC_Use_Dynamic_Ranges; 68 } 69 70 private 71 { 72 version( linux ) 73 { 74 version = SimpleLibcStackEnd; 75 76 version( SimpleLibcStackEnd ) 77 { 78 extern (C) extern void* __libc_stack_end; 79 } 80 else 81 { 82 import tango.stdc.posix.dlfcn; 83 } 84 } 85 else version(freebsd) 86 { 87 import tango.sys.freebsd.consts.sysctl; 88 extern (C) int sysctl(int *, uint, void *, size_t *, void *, size_t); 89 } 90 } 91 92 93 /** 94 * 95 */ 96 97 version( solaris ) { 98 version(X86_64) { 99 extern (C) void* _userlimit; 100 } 101 } 102 103 extern (C) void* rt_stackBottom() 104 { 105 version( Win32 ) 106 { 107 void* bottom; 108 asm 109 { 110 mov EAX, FS:4; 111 mov bottom, EAX; 112 } 113 return bottom; 114 } 115 else version( linux ) 116 { 117 version( SimpleLibcStackEnd ) 118 { 119 return __libc_stack_end; 120 } 121 else 122 { 123 // See discussion: http://autopackage.org/forums/viewtopic.php?t=22 124 static void** libc_stack_end; 125 126 if( libc_stack_end == libc_stack_end.init ) 127 { 128 void* handle = dlopen( null, RTLD_NOW ); 129 libc_stack_end = cast(void**) dlsym( handle, "__libc_stack_end" ); 130 dlclose( handle ); 131 } 132 return *libc_stack_end; 133 } 134 } 135 else version( freebsd ) 136 { 137 int mib[2]; 138 size_t userStack; // vm_size_t 139 size_t len; 140 int retval; 141 142 mib[] = [ SysCtl.CTL_KERN, SysCtl.KERN_USRSTACK ]; 143 len = userStack.sizeof; 144 retval = sysctl(cast(int*)&mib, 2, &userStack, &len, null, 0); 145 if (retval < 0) 146 assert(false, "cannot get the stack end address"); 147 148 return cast(void*)userStack; 149 } 150 else version( darwin ) 151 { 152 // darwin has a fixed stack bottom 153 version( D_LP64 ) 154 return cast(void*) 0x7fff5fc00000; 155 else 156 return cast(void*) 0xc0000000; 157 } 158 else version( solaris ) 159 { 160 version(X86_64) { 161 return _userlimit; 162 } 163 else { 164 // <sys/vmparam.h> 165 return cast(void*) 0x8048000; 166 } 167 } 168 else 169 { 170 static assert( false, "Operating system not supported." ); 171 } 172 } 173 174 175 /** 176 * 177 */ 178 extern (C) void* rt_stackTop() 179 { 180 version( D_InlineAsm_X86 ) 181 { 182 asm 183 { 184 naked; 185 mov EAX, ESP; 186 ret; 187 } 188 } 189 else 190 { 191 // This works, even if frame pointer is omitted. 192 return __builtin_frame_address(0); 193 } 194 } 195 196 197 private 198 { 199 version( Win32 ) 200 { 201 extern (C) 202 { 203 extern int _data_start__; 204 extern int _bss_end__; 205 } 206 207 alias _data_start__ Data_Start; 208 alias _bss_end__ Data_End; 209 } 210 else version( linux ) 211 { 212 extern (C) 213 { 214 extern int _data; 215 extern int __data_start; 216 extern int _end; 217 extern int _data_start__; 218 extern int _data_end__; 219 extern int _bss_start__; 220 extern int _bss_end__; 221 extern int __fini_array_end; 222 } 223 224 alias __data_start Data_Start; 225 alias _end Data_End; 226 } 227 else version( freebsd ) 228 { 229 extern (C) 230 { 231 version(X86) { extern char etext; } 232 else { extern char __preinit_array_start; } 233 extern int _end; 234 } 235 236 version(X86) { alias etext Data_Start; } 237 else { alias __preinit_array_start Data_Start; } 238 alias _end Data_End; 239 } 240 else version( solaris ) 241 { 242 extern(C) 243 { 244 extern int _environ; 245 extern int _end; 246 } 247 248 alias _environ Data_Start; 249 alias _end Data_End; 250 } 251 252 version( GC_Use_Dynamic_Ranges ) 253 { 254 private import tango.stdc.stdlib; 255 256 struct DataSeg 257 { 258 void* beg; 259 void* end; 260 } 261 262 DataSeg* allSegs = null; 263 size_t numSegs = 0; 264 265 extern (C) void _d_gcc_gc_add_range( void* beg, void* end ) 266 { 267 void* ptr = realloc( allSegs, (numSegs + 1) * DataSeg.sizeof ); 268 269 if( ptr ) // if realloc fails, we have problems 270 { 271 allSegs = cast(DataSeg*) ptr; 272 allSegs[numSegs].beg = beg; 273 allSegs[numSegs].end = end; 274 numSegs++; 275 } 276 } 277 278 extern (C) void _d_gcc_gc_remove_range( void* beg ) 279 { 280 for( size_t pos = 0; pos < numSegs; ++pos ) 281 { 282 if( beg == allSegs[pos].beg ) 283 { 284 while( ++pos < numSegs ) 285 { 286 allSegs[pos-1] = allSegs[pos]; 287 } 288 numSegs--; 289 return; 290 } 291 } 292 } 293 } 294 295 alias void delegate( void*, void* ) scanFn; 296 297 void* dataStart, dataEnd; 298 } 299 300 301 /** 302 * 303 */ 304 extern (C) void rt_scanStaticData( scanFn scan ) 305 { 306 scan( dataStart, dataEnd ); 307 308 version( GC_Use_Dynamic_Ranges ) 309 { 310 for( size_t pos = 0; pos < numSegs; ++pos ) 311 { 312 scan( allSegs[pos].beg, allSegs[pos].end ); 313 } 314 } 315 } 316 317 void initStaticDataPtrs() 318 { 319 const int S = (void*).sizeof; 320 321 // Can't assume the input addresses are word-aligned 322 static void* adjust_up( void* p ) 323 { 324 return p + ((S - (cast(size_t)p & (S-1))) & (S-1)); // cast ok even if 64-bit 325 } 326 327 static void * adjust_down( void* p ) 328 { 329 return p - (cast(size_t) p & (S-1)); 330 } 331 332 version( Win32 ) 333 { 334 dataStart = adjust_up( &Data_Start ); 335 dataEnd = adjust_down( &Data_End ); 336 } 337 else version(linux) 338 { 339 dataStart = adjust_up( &Data_Start ); 340 dataEnd = adjust_down( &Data_End ); 341 } 342 else version( freebsd ) 343 { 344 dataStart = adjust_up( &Data_Start ); 345 dataEnd = adjust_down( &Data_End ); 346 } 347 else version(solaris) 348 { 349 dataStart = adjust_up( &Data_Start ); 350 dataEnd = adjust_down( &Data_End ); 351 } 352 else version(GC_Use_Data_Dyld) 353 { 354 _d_gcc_dyld_start(); 355 } 356 else 357 { 358 static assert( false, "Operating system not supported." ); 359 } 360 361 version( GC_Use_Data_Proc_Maps ) 362 { 363 parseDataProcMaps(); 364 } 365 } 366 367 version( GC_Use_Data_Proc_Maps ) 368 { 369 version(solaris) 370 { 371 import tango.stdc.stdint : uintptr_t; 372 373 typedef long offset_t; 374 enum : uint { PRMAPSZ = 64, MA_WRITE = 0x02 } 375 extern(C) 376 { 377 struct prmap { 378 uintptr_t pr_vaddr; /* virtual address of mapping */ 379 size_t pr_size; /* size of mapping in bytes */ 380 char[PRMAPSZ] pr_mapname; /* name in /proc/<pid>/object */ 381 private offset_t pr_offset; /* offset into mapped object, if any */ 382 int pr_mflags; /* protection and attribute flags (see below) */ 383 int pr_pagesize; /* pagesize (bytes) for this mapping */ 384 int pr_shmid; /* SysV shmid, -1 if not SysV shared memory */ 385 386 private int[1] pr_filler; 387 } 388 } 389 390 debug (ProcMaps) extern (C) int printf(char*, ...); 391 392 void parseDataProcMaps() 393 { 394 debug (ProcMaps) printf("initStaticDataPtrs()\n"); 395 // http://docs.sun.com/app/docs/doc/816-5174/proc-4 396 prmap pr; 397 398 int fd = open("/proc/self/map", O_RDONLY); 399 scope (exit) close(fd); 400 401 while (prmap.sizeof == read(fd, &pr, prmap.sizeof)) 402 if (pr.pr_mflags & MA_WRITE) 403 { 404 void* start = cast(void*) pr.pr_vaddr; 405 void* end = cast(void*)(pr.pr_vaddr + pr.pr_size); 406 debug (ProcMaps) printf(" vmem at %p - %p with size %d bytes\n", start, end, pr.pr_size); 407 408 // Exclude stack and dataStart..dataEnd 409 if ( ( !dataEnd || 410 !( dataStart >= start && dataEnd <= end ) ) && 411 !( &pr >= start && &pr < end ) ) 412 { 413 // we already have static data from this region. anything else 414 // is heap (%% check) 415 debug (ProcMaps) printf(" Adding map range %p - %p\n", start, end); 416 _d_gcc_gc_add_range(start, end); 417 } 418 } 419 } 420 } 421 else 422 { 423 const int S = (void*).sizeof; 424 425 // TODO: This could use cleanup! 426 void parseDataProcMaps() 427 { 428 // TODO: Exclude zero-mapped regions 429 430 int fd = open("/proc/self/maps", O_RDONLY); 431 ptrdiff_t count; // %% need to configure ret for read.. 432 char buf[2024]; 433 char* p; 434 char* e; 435 char* s; 436 void* start; 437 void* end; 438 439 p = buf.ptr; 440 if (fd != -1) 441 { 442 while ( (count = read(fd, p, buf.sizeof - (p - buf.ptr))) > 0 ) 443 { 444 e = p + count; 445 p = buf.ptr; 446 while (true) 447 { 448 s = p; 449 while (p < e && *p != '\n') 450 p++; 451 if (p < e) 452 { 453 // parse the entry in [s, p) 454 static if( S == 4 ) 455 { 456 enum Ofs 457 { 458 Write_Prot = 19, 459 Start_Addr = 0, 460 End_Addr = 9, 461 Addr_Len = 8, 462 } 463 } 464 else static if( S == 8 ) 465 { 466 //X86-64 only has 12 bytes address space(in PAE mode) - not 16 467 //We also need the 32 bit offsets for 32 bit apps 468 version(X86_64) { 469 enum Ofs 470 { 471 Write_Prot = 27, 472 Start_Addr = 0, 473 End_Addr = 13, 474 Addr_Len = 12, 475 Write_Prot_32 = 19, 476 Start_Addr_32 = 0, 477 End_Addr_32 = 9, 478 Addr_Len_32 = 8, 479 } 480 } 481 else 482 { 483 enum Ofs 484 { 485 Write_Prot = 35, 486 Start_Addr = 0, 487 End_Addr = 9, 488 Addr_Len = 17, 489 } 490 } 491 } 492 else 493 { 494 static assert( false ); 495 } 496 497 // %% this is wrong for 64-bit: 498 // long strtoul(const char*,char**,int); 499 // but seems to work on x86-64: 500 // probably because C's long is 64 bit there 501 502 if( s[Ofs.Write_Prot] == 'w' ) 503 { 504 s[Ofs.Start_Addr + Ofs.Addr_Len] = '\0'; 505 s[Ofs.End_Addr + Ofs.Addr_Len] = '\0'; 506 start = cast(void*) strtoul(s + Ofs.Start_Addr, null, 16); 507 end = cast(void*) strtoul(s + Ofs.End_Addr, null, 16); 508 509 // 1. Exclude anything overlapping [dataStart, dataEnd) 510 // 2. Exclude stack 511 if ( ( !dataEnd || 512 !( dataStart >= start && dataEnd <= end ) ) && 513 !( &buf[0] >= start && &buf[0] < end ) ) 514 { 515 // we already have static data from this region. anything else 516 // is heap (%% check) 517 debug (ProcMaps) printf("Adding map range %p 0%p\n", start, end); 518 _d_gcc_gc_add_range(start, end); 519 } 520 } 521 version(X86_64) 522 { 523 //We need to check here for 32 bit apps like ldc produces 524 //and add them to the gc scan range 525 if( s[Ofs.Write_Prot_32] == 'w' ) 526 { 527 s[Ofs.Start_Addr_32 + Ofs.Addr_Len_32] = '\0'; 528 s[Ofs.End_Addr_32 + Ofs.Addr_Len_32] = '\0'; 529 start = cast(void*) strtoul(s + Ofs.Start_Addr_32, null, 16); 530 end = cast(void*) strtoul(s + Ofs.End_Addr_32, null, 16); 531 if ( ( !dataEnd || 532 !( dataStart >= start && dataEnd <= end ) ) && 533 !( &buf[0] >= start && &buf[0] < end ) ) 534 { 535 _d_gcc_gc_add_range(start, end); 536 } 537 } 538 } 539 540 p++; 541 } 542 else 543 { 544 count = p - s; 545 memmove(buf.ptr, s, cast(size_t)count); 546 p = buf.ptr + count; 547 break; 548 } 549 } 550 } 551 close(fd); 552 } 553 } 554 } 555 } 556 557 /* 558 * GDC dyld memory module: 559 * http://www.dsource.org/projects/tango/browser/trunk/lib/compiler/gdc/memory_dyld.c 560 * Port to the D programming language: Jacob Carlborg 561 */ 562 version (GC_Use_Data_Dyld) 563 { 564 private 565 { 566 const char* SEG_DATA = "__DATA".ptr; 567 const char* SECT_DATA = "__data".ptr; 568 const char* SECT_BSS = "__bss".ptr; 569 const char* SECT_COMMON = "__common".ptr; 570 571 struct SegmentSection 572 { 573 const char* segment; 574 const char* section; 575 } 576 577 struct mach_header 578 { 579 uint magic; 580 int cputype; 581 int cpusubtype; 582 uint filetype; 583 uint ncmds; 584 uint sizeofcmds; 585 uint flags; 586 version (D_LP64) 587 uint reserved; 588 } 589 590 struct section 591 { 592 char[16] sectname; 593 char[16] segname; 594 c_ulong addr; 595 c_ulong size; 596 uint offset; 597 uint align_; 598 uint reloff; 599 uint nreloc; 600 uint flags; 601 uint reserved1; 602 uint reserved2; 603 version (D_LP64) 604 uint reserved3; 605 } 606 607 alias extern (C) void function (mach_header* mh, ptrdiff_t vmaddr_slide) DyldFuncPointer; 608 609 version (D_LP64) 610 extern (C) /*const*/ section* getsectbynamefromheader_64(/*const*/ mach_header* mhp, /*const*/ char* segname, /*const*/ char* sectname); 611 else 612 extern (C) /*const*/ section* getsectbynamefromheader(/*const*/ mach_header* mhp, /*const*/ char* segname, /*const*/ char* sectname); 613 extern (C) void _dyld_register_func_for_add_image(DyldFuncPointer func); 614 extern (C) void _dyld_register_func_for_remove_image(DyldFuncPointer func); 615 616 const SegmentSection[3] GC_dyld_sections = [SegmentSection(SEG_DATA, SECT_DATA), SegmentSection(SEG_DATA, SECT_BSS), SegmentSection(SEG_DATA, SECT_COMMON)]; 617 618 extern (C) void on_dyld_add_image (/*const*/ mach_header* hdr, ptrdiff_t slide) 619 { 620 void* start; 621 void* end; 622 /*const*/ section* sec; 623 624 foreach (s ; GC_dyld_sections) 625 { 626 version (D_LP64) 627 sec = getsectbynamefromheader_64(hdr, s.segment, s.section); 628 else 629 sec = getsectbynamefromheader(hdr, s.segment, s.section); 630 631 if (sec == null || sec.size == 0) 632 continue; 633 634 start = cast(void*) (sec.addr + slide); 635 end = cast(void*) (start + sec.size); 636 637 _d_gcc_gc_add_range(start, end); 638 } 639 } 640 641 extern (C) void on_dyld_remove_image (/*const*/ mach_header* hdr, ptrdiff_t slide) 642 { 643 void* start; 644 void* end; 645 /*const*/ section* sec; 646 647 foreach (s ; GC_dyld_sections) 648 { 649 version (D_LP64) 650 sec = getsectbynamefromheader_64(hdr, s.segment, s.section); 651 else 652 sec = getsectbynamefromheader(hdr, s.segment, s.section); 653 654 if (sec == null || sec.size == 0) 655 continue; 656 657 start = cast(void*) (sec.addr + slide); 658 end = cast(void*) (start + sec.size); 659 660 _d_gcc_gc_remove_range(start); 661 } 662 } 663 664 void _d_gcc_dyld_start () 665 { 666 static bool started; 667 668 if (!started) 669 { 670 started = true; 671 672 _dyld_register_func_for_add_image(&on_dyld_add_image); 673 _dyld_register_func_for_remove_image(&on_dyld_remove_image); 674 } 675 } 676 } 677 } 678