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