1 // OSX-specific support for sections.
2 // Copyright (C) 2019 Free Software Foundation, Inc.
3 
4 // GCC is free software; you can redistribute it and/or modify it under
5 // the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 3, or (at your option) any later
7 // version.
8 
9 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
10 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 // for more details.
13 
14 // Under Section 7 of GPL version 3, you are granted additional
15 // permissions described in the GCC Runtime Library Exception, version
16 // 3.1, as published by the Free Software Foundation.
17 
18 // You should have received a copy of the GNU General Public License and
19 // a copy of the GCC Runtime Library Exception along with this program;
20 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
21 // <http://www.gnu.org/licenses/>.
22 
23 module gcc.sections.osx;
24 
25 version (OSX):
26 
27 // debug = PRINTF;
28 import core.stdc.stdio;
29 import core.stdc.string, core.stdc.stdlib;
30 import core.sys.posix.pthread;
31 import core.sys.darwin.mach.dyld;
32 import core.sys.darwin.mach.getsect;
33 import rt.deh, rt.minfo;
34 import rt.util.container.array;
35 
36 struct SectionGroup
37 {
opApplySectionGroup38     static int opApply(scope int delegate(ref SectionGroup) dg)
39     {
40         return dg(_sections);
41     }
42 
opApplyReverseSectionGroup43     static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
44     {
45         return dg(_sections);
46     }
47 
immutableSectionGroup48     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
49     {
50         return _moduleGroup.modules;
51     }
52 
inoutSectionGroup53     @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
54     {
55         return _moduleGroup;
56     }
57 
inoutSectionGroup58     @property inout(void[])[] gcRanges() inout nothrow @nogc
59     {
60         return _gcRanges[];
61     }
62 
immutableSectionGroup63     @property immutable(FuncTable)[] ehTables() const nothrow @nogc
64     {
65         return _ehTables[];
66     }
67 
68 private:
69     immutable(FuncTable)[] _ehTables;
70     ModuleGroup _moduleGroup;
71     Array!(void[]) _gcRanges;
72     immutable(void)[][2] _tlsImage;
73 }
74 
75 /****
76  * Boolean flag set to true while the runtime is initialized.
77  */
78 __gshared bool _isRuntimeInitialized;
79 
80 /****
81  * Gets called on program startup just before GC is initialized.
82  */
initSections()83 void initSections() nothrow @nogc
84 {
85     pthread_key_create(&_tlsKey, null);
86     _dyld_register_func_for_add_image(&sections_osx_onAddImage);
87     _isRuntimeInitialized = true;
88 }
89 
90 /***
91  * Gets called on program shutdown just after GC is terminated.
92  */
finiSections()93 void finiSections() nothrow @nogc
94 {
95     _sections._gcRanges.reset();
96     pthread_key_delete(_tlsKey);
97     _isRuntimeInitialized = false;
98 }
99 
initTLSRanges()100 void[]* initTLSRanges() nothrow @nogc
101 {
102     return &getTLSBlock();
103 }
104 
finiTLSRanges(void[]* rng)105 void finiTLSRanges(void[]* rng) nothrow @nogc
106 {
107     .free(rng.ptr);
108     .free(rng);
109 }
110 
scanTLSRanges(void[]* rng,scope void delegate (void * pbeg,void * pend)nothrow dg)111 void scanTLSRanges(void[]* rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
112 {
113     dg(rng.ptr, rng.ptr + rng.length);
114 }
115 
116 // NOTE: The Mach-O object file format does not allow for thread local
117 //       storage declarations. So instead we roll our own by putting tls
118 //       into the __tls_data and the __tlscoal_nt sections.
119 //
120 //       This function is called by the code emitted by the compiler.  It
121 //       is expected to translate an address into the TLS static data to
122 //       the corresponding address in the TLS dynamic per-thread data.
123 
124 // NB: the compiler mangles this function as '___tls_get_addr' even though it is extern(D)
___tls_get_addr(void * p)125 extern(D) void* ___tls_get_addr( void* p )
126 {
127     immutable off = tlsOffset(p);
128     auto tls = getTLSBlockAlloc();
129     assert(off < tls.length);
130     return tls.ptr + off;
131 }
132 
133 private:
134 
135 __gshared pthread_key_t _tlsKey;
136 
tlsOffset(void * p)137 size_t tlsOffset(void* p)
138 in
139 {
140     assert(_sections._tlsImage[0].ptr !is null ||
141            _sections._tlsImage[1].ptr !is null);
142 }
143 body
144 {
145     // NOTE: p is an address in the TLS static data emitted by the
146     //       compiler.  If it isn't, something is disastrously wrong.
147     immutable off0 = cast(size_t)(p - _sections._tlsImage[0].ptr);
148     if (off0 < _sections._tlsImage[0].length)
149     {
150         return off0;
151     }
152     immutable off1 = cast(size_t)(p - _sections._tlsImage[1].ptr);
153     if (off1 < _sections._tlsImage[1].length)
154     {
155         size_t sz = (_sections._tlsImage[0].length + 15) & ~cast(size_t)15;
156         return sz + off1;
157     }
158     assert(0);
159 }
160 
getTLSBlock()161 ref void[] getTLSBlock() nothrow @nogc
162 {
163     auto pary = cast(void[]*)pthread_getspecific(_tlsKey);
164     if (pary is null)
165     {
166         pary = cast(void[]*).calloc(1, (void[]).sizeof);
167         if (pthread_setspecific(_tlsKey, pary) != 0)
168         {
169             import core.stdc.stdio;
170             perror("pthread_setspecific failed with");
171             assert(0);
172         }
173     }
174     return *pary;
175 }
176 
getTLSBlockAlloc()177 ref void[] getTLSBlockAlloc()
178 {
179     auto pary = &getTLSBlock();
180     if (!pary.length)
181     {
182         auto imgs = _sections._tlsImage;
183         immutable sz0 = (imgs[0].length + 15) & ~cast(size_t)15;
184         immutable sz2 = sz0 + imgs[1].length;
185         auto p = .malloc(sz2);
186         memcpy(p, imgs[0].ptr, imgs[0].length);
187         memcpy(p + sz0, imgs[1].ptr, imgs[1].length);
188         *pary = p[0 .. sz2];
189     }
190     return *pary;
191 }
192 
193 __gshared SectionGroup _sections;
194 
sections_osx_onAddImage(in mach_header * h,intptr_t slide)195 extern (C) void sections_osx_onAddImage(in mach_header* h, intptr_t slide)
196 {
197     foreach (e; dataSegs)
198     {
199         auto sect = getSection(h, slide, e.seg.ptr, e.sect.ptr);
200         if (sect != null)
201             _sections._gcRanges.insertBack((cast(void*)sect.ptr)[0 .. sect.length]);
202     }
203 
204     auto minfosect = getSection(h, slide, "__DATA", "__minfodata");
205     if (minfosect != null)
206     {
207         // no support for multiple images yet
208         // take the sections from the last static image which is the executable
209         if (_isRuntimeInitialized)
210         {
211             fprintf(stderr, "Loading shared libraries isn't yet supported on OSX.\n");
212             return;
213         }
214         else if (_sections.modules.ptr !is null)
215         {
216             fprintf(stderr, "Shared libraries are not yet supported on OSX.\n");
217         }
218 
219         debug(PRINTF) printf("  minfodata\n");
220         auto p = cast(immutable(ModuleInfo*)*)minfosect.ptr;
221         immutable len = minfosect.length / (*p).sizeof;
222 
223         _sections._moduleGroup = ModuleGroup(p[0 .. len]);
224     }
225 
226     auto ehsect = getSection(h, slide, "__DATA", "__deh_eh");
227     if (ehsect != null)
228     {
229         debug(PRINTF) printf("  deh_eh\n");
230         auto p = cast(immutable(FuncTable)*)ehsect.ptr;
231         immutable len = ehsect.length / (*p).sizeof;
232 
233         _sections._ehTables = p[0 .. len];
234     }
235 
236     auto tlssect = getSection(h, slide, "__DATA", "__tls_data");
237     if (tlssect != null)
238     {
239         debug(PRINTF) printf("  tls_data %p %p\n", tlssect.ptr, tlssect.ptr + tlssect.length);
240         _sections._tlsImage[0] = (cast(immutable(void)*)tlssect.ptr)[0 .. tlssect.length];
241     }
242 
243     auto tlssect2 = getSection(h, slide, "__DATA", "__tlscoal_nt");
244     if (tlssect2 != null)
245     {
246         debug(PRINTF) printf("  tlscoal_nt %p %p\n", tlssect2.ptr, tlssect2.ptr + tlssect2.length);
247         _sections._tlsImage[1] = (cast(immutable(void)*)tlssect2.ptr)[0 .. tlssect2.length];
248     }
249 }
250 
251 struct SegRef
252 {
253     string seg;
254     string sect;
255 }
256 
257 static immutable SegRef[] dataSegs = [{SEG_DATA, SECT_DATA},
258                                       {SEG_DATA, SECT_BSS},
259                                       {SEG_DATA, SECT_COMMON}];
260 
getSection(in mach_header * header,intptr_t slide,in char * segmentName,in char * sectionName)261 ubyte[] getSection(in mach_header* header, intptr_t slide,
262                    in char* segmentName, in char* sectionName)
263 {
264     version (X86)
265     {
266         assert(header.magic == MH_MAGIC);
267         auto sect = getsectbynamefromheader(header,
268                                             segmentName,
269                                             sectionName);
270     }
271     else version (X86_64)
272     {
273         assert(header.magic == MH_MAGIC_64);
274         auto sect = getsectbynamefromheader_64(cast(mach_header_64*)header,
275                                             segmentName,
276                                             sectionName);
277     }
278     else
279         static assert(0, "unimplemented");
280 
281     if (sect !is null && sect.size > 0)
282         return (cast(ubyte*)sect.addr + slide)[0 .. cast(size_t)sect.size];
283     return null;
284 }
285