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