145
|
1 // OSX-specific support for sections.
|
|
2 // Copyright (C) 2019-2020 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 {
|
|
38 static int opApply(scope int delegate(ref SectionGroup) dg)
|
|
39 {
|
|
40 return dg(_sections);
|
|
41 }
|
|
42
|
|
43 static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
|
|
44 {
|
|
45 return dg(_sections);
|
|
46 }
|
|
47
|
|
48 @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
|
|
49 {
|
|
50 return _moduleGroup.modules;
|
|
51 }
|
|
52
|
|
53 @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
|
|
54 {
|
|
55 return _moduleGroup;
|
|
56 }
|
|
57
|
|
58 @property inout(void[])[] gcRanges() inout nothrow @nogc
|
|
59 {
|
|
60 return _gcRanges[];
|
|
61 }
|
|
62
|
|
63 @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 */
|
|
83 void initSections() nothrow @nogc
|
|
84 {
|
|
85 pthread_key_create(&_tlsKey, null);
|
|
86 _dyld_register_func_for_add_image(§ions_osx_onAddImage);
|
|
87 _isRuntimeInitialized = true;
|
|
88 }
|
|
89
|
|
90 /***
|
|
91 * Gets called on program shutdown just after GC is terminated.
|
|
92 */
|
|
93 void finiSections() nothrow @nogc
|
|
94 {
|
|
95 _sections._gcRanges.reset();
|
|
96 pthread_key_delete(_tlsKey);
|
|
97 _isRuntimeInitialized = false;
|
|
98 }
|
|
99
|
|
100 void[]* initTLSRanges() nothrow @nogc
|
|
101 {
|
|
102 return &getTLSBlock();
|
|
103 }
|
|
104
|
|
105 void finiTLSRanges(void[]* rng) nothrow @nogc
|
|
106 {
|
|
107 .free(rng.ptr);
|
|
108 .free(rng);
|
|
109 }
|
|
110
|
|
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)
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 }
|