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(§ions_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 }