1 /**
2  * Written in the D programming language.
3  * This module provides OS X x86 specific support for sections.
4  *
5  * Copyright: Copyright Digital Mars 2008 - 2016.
6  * License: Distributed under the
7  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
8  *    (See accompanying file LICENSE)
9  * Authors: Walter Bright, Sean Kelly, Martin Nowak, Jacob Carlborg
10  * Source: $(DRUNTIMESRC rt/_sections_osx_x86.d)
11  */
12 module rt.sections_osx_x86;
13 
14 version (OSX)
15     version = Darwin;
16 else version (iOS)
17     version = Darwin;
18 else version (TVOS)
19     version = Darwin;
20 else version (WatchOS)
21     version = Darwin;
22 
23 version (Darwin):
24 version (X86):
25 
26 // debug = PRINTF;
27 import core.stdc.stdio;
28 import core.stdc.string, core.stdc.stdlib;
29 import core.sys.posix.pthread;
30 import core.sys.darwin.mach.dyld;
31 import core.sys.darwin.mach.getsect;
32 import rt.deh, rt.minfo;
33 import core.internal.container.array;
34 
35 struct SectionGroup
36 {
37     static int opApply(scope int delegate(ref SectionGroup) dg)
38     {
39         return dg(_sections);
40     }
41 
42     static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
43     {
44         return dg(_sections);
45     }
46 
47     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
48     {
49         return _moduleGroup.modules;
50     }
51 
52     @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
53     {
54         return _moduleGroup;
55     }
56 
57     @property inout(void[])[] gcRanges() inout nothrow @nogc
58     {
59         return _gcRanges[];
60     }
61 
62     @property immutable(FuncTable)[] ehTables() const nothrow @nogc
63     {
64         return _ehTables[];
65     }
66 
67 private:
68     immutable(FuncTable)[] _ehTables;
69     ModuleGroup _moduleGroup;
70     Array!(void[]) _gcRanges;
71     immutable(void)[][2] _tlsImage;
72 }
73 
74 /****
75  * Boolean flag set to true while the runtime is initialized.
76  */
77 __gshared bool _isRuntimeInitialized;
78 
79 /****
80  * Gets called on program startup just before GC is initialized.
81  */
82 void initSections() nothrow @nogc
83 {
84     pthread_key_create(&_tlsKey, null);
85     _dyld_register_func_for_add_image(&sections_osx_onAddImage);
86     _isRuntimeInitialized = true;
87 }
88 
89 /***
90  * Gets called on program shutdown just after GC is terminated.
91  */
92 void finiSections() nothrow @nogc
93 {
94     _sections._gcRanges.reset();
95     pthread_key_delete(_tlsKey);
96     _isRuntimeInitialized = false;
97 }
98 
99 void[]* initTLSRanges() nothrow @nogc
100 {
101     return &getTLSBlock();
102 }
103 
104 void finiTLSRanges(void[]* rng) nothrow @nogc
105 {
106     .free(rng.ptr);
107     .free(rng);
108 }
109 
110 void scanTLSRanges(void[]* rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
111 {
112     dg(rng.ptr, rng.ptr + rng.length);
113 }
114 
115 // NOTE: The Mach-O object file format does not allow for thread local
116 //       storage declarations. So instead we roll our own by putting tls
117 //       into the __tls_data and the __tlscoal_nt sections.
118 //
119 //       This function is called by the code emitted by the compiler.  It
120 //       is expected to translate an address into the TLS static data to
121 //       the corresponding address in the TLS dynamic per-thread data.
122 
123 extern(C) void* __tls_get_addr( void* p )
124 {
125     immutable off = tlsOffset(p);
126     auto tls = getTLSBlockAlloc();
127     assert(off < tls.length);
128     return tls.ptr + off;
129 }
130 
131 private:
132 
133 __gshared pthread_key_t _tlsKey;
134 
135 size_t tlsOffset(void* p)
136 in
137 {
138     assert(_sections._tlsImage[0].ptr !is null ||
139            _sections._tlsImage[1].ptr !is null);
140 }
141 do
142 {
143     // NOTE: p is an address in the TLS static data emitted by the
144     //       compiler.  If it isn't, something is disastrously wrong.
145     immutable off0 = cast(size_t)(p - _sections._tlsImage[0].ptr);
146     if (off0 < _sections._tlsImage[0].length)
147     {
148         return off0;
149     }
150     immutable off1 = cast(size_t)(p - _sections._tlsImage[1].ptr);
151     if (off1 < _sections._tlsImage[1].length)
152     {
153         size_t sz = (_sections._tlsImage[0].length + 15) & ~cast(size_t)15;
154         return sz + off1;
155     }
156     assert(0);
157 }
158 
159 ref void[] getTLSBlock() nothrow @nogc
160 {
161     auto pary = cast(void[]*)pthread_getspecific(_tlsKey);
162     if (pary is null)
163     {
164         pary = cast(void[]*).calloc(1, (void[]).sizeof);
165         if (pthread_setspecific(_tlsKey, pary) != 0)
166         {
167             import core.stdc.stdio;
168             perror("pthread_setspecific failed with");
169             assert(0);
170         }
171     }
172     return *pary;
173 }
174 
175 ref void[] getTLSBlockAlloc()
176 {
177     auto pary = &getTLSBlock();
178     if (!pary.length)
179     {
180         auto imgs = _sections._tlsImage;
181         immutable sz0 = (imgs[0].length + 15) & ~cast(size_t)15;
182         immutable sz2 = sz0 + imgs[1].length;
183         auto p = .malloc(sz2);
184         memcpy(p, imgs[0].ptr, imgs[0].length);
185         memcpy(p + sz0, imgs[1].ptr, imgs[1].length);
186         *pary = p[0 .. sz2];
187     }
188     return *pary;
189 }
190 
191 __gshared SectionGroup _sections;
192 
193 extern (C) void sections_osx_onAddImage(const scope mach_header* h, intptr_t slide)
194 {
195     foreach (e; dataSegs)
196     {
197         auto sect = getSection(h, slide, e.seg.ptr, e.sect.ptr);
198         if (sect != null)
199             _sections._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]);
200     }
201 
202     auto minfosect = getSection(h, slide, "__DATA", "__minfodata");
203     if (minfosect != null)
204     {
205         // no support for multiple images yet
206         // take the sections from the last static image which is the executable
207         if (_isRuntimeInitialized)
208         {
209             fprintf(stderr, "Loading shared libraries isn't yet supported on Darwin.\n");
210             return;
211         }
212         else if (_sections.modules.ptr !is null)
213         {
214             fprintf(stderr, "Shared libraries are not yet supported on Darwin.\n");
215         }
216 
217         debug(PRINTF) printf("  minfodata\n");
218         auto p = cast(immutable(ModuleInfo*)*)minfosect.ptr;
219         immutable len = minfosect.length / (*p).sizeof;
220 
221         _sections._moduleGroup = ModuleGroup(p[0 .. len]);
222     }
223 
224     auto ehsect = getSection(h, slide, "__DATA", "__deh_eh");
225     if (ehsect != null)
226     {
227         debug(PRINTF) printf("  deh_eh\n");
228         auto p = cast(immutable(FuncTable)*)ehsect.ptr;
229         immutable len = ehsect.length / (*p).sizeof;
230 
231         _sections._ehTables = p[0 .. len];
232     }
233 
234     auto tlssect = getSection(h, slide, "__DATA", "__tls_data");
235     if (tlssect != null)
236     {
237         debug(PRINTF) printf("  tls_data %p %p\n", tlssect.ptr, tlssect.ptr + tlssect.length);
238         _sections._tlsImage[0] = (cast(immutable(void)*)tlssect.ptr)[0 .. tlssect.length];
239     }
240 
241     auto tlssect2 = getSection(h, slide, "__DATA", "__tlscoal_nt");
242     if (tlssect2 != null)
243     {
244         debug(PRINTF) printf("  tlscoal_nt %p %p\n", tlssect2.ptr, tlssect2.ptr + tlssect2.length);
245         _sections._tlsImage[1] = (cast(immutable(void)*)tlssect2.ptr)[0 .. tlssect2.length];
246     }
247 }
248 
249 struct SegRef
250 {
251     string seg;
252     string sect;
253 }
254 
255 
256 static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA},
257                                       {SEG_DATA, SECT_BSS},
258                                       {SEG_DATA, SECT_COMMON}];
259 
260 
261 ubyte[] getSection(in mach_header* header, intptr_t slide,
262                    in char* segmentName, in char* sectionName)
263 {
264     assert(header.magic == MH_MAGIC);
265     auto sect = getsectbynamefromheader(header, segmentName, sectionName);
266 
267     if (sect !is null && sect.size > 0)
268         return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size];
269     return null;
270 }