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