1 /** 2 * Written in the D programming language. 3 * This module provides bionic-specific support for sections. 4 * 5 * Copyright: Copyright Martin Nowak 2012-2013. 6 * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 7 * Authors: Martin Nowak 8 * Source: $(DRUNTIMESRC rt/_sections_android.d) 9 */ 10 11 module rt.sections_android; 12 13 version (CRuntime_Bionic): 14 15 // debug = PRINTF; 16 debug(PRINTF) import core.stdc.stdio; 17 import core.internal.elf.dl : SharedObject; 18 import core.sys.posix.pthread; 19 import core.stdc.stdlib : calloc, malloc, free; 20 import core.stdc.string : memcpy; 21 import rt.deh; 22 import rt.minfo; 23 import rt.util.utility : safeAssert; 24 25 struct SectionGroup 26 { 27 static int opApply(scope int delegate(ref SectionGroup) dg) 28 { 29 return dg(_sections); 30 } 31 32 static int opApplyReverse(scope int delegate(ref SectionGroup) dg) 33 { 34 return dg(_sections); 35 } 36 37 @property immutable(ModuleInfo*)[] modules() const nothrow @nogc 38 { 39 return _moduleGroup.modules; 40 } 41 42 @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc 43 { 44 return _moduleGroup; 45 } 46 47 version (DigitalMars) 48 @property immutable(FuncTable)[] ehTables() const nothrow @nogc 49 { 50 auto pbeg = cast(immutable(FuncTable)*)&__start_deh; 51 auto pend = cast(immutable(FuncTable)*)&__stop_deh; 52 return pbeg[0 .. pend - pbeg]; 53 } 54 55 @property inout(void[])[] gcRanges() inout nothrow @nogc 56 { 57 return _gcRanges[]; 58 } 59 60 private: 61 ModuleGroup _moduleGroup; 62 void[][1] _gcRanges; 63 } 64 65 void initSections() nothrow @nogc 66 { 67 pthread_key_create(&_tlsKey, null); 68 69 SharedObject object; 70 const success = SharedObject.findForAddress(&_sections, object); 71 safeAssert(success, "cannot find ELF object"); 72 73 _staticTLSRange = getStaticTLSRange(object, _tlsAlignment); 74 // prevent the linker from stripping the TLS alignment symbols 75 if (_staticTLSRange is null) // should never happen 76 safeAssert(alignmentForTDATA == alignmentForTBSS, "unreachable"); 77 78 version (LDC) 79 { 80 auto mbeg = cast(immutable ModuleInfo**)&__start___minfo; 81 auto mend = cast(immutable ModuleInfo**)&__stop___minfo; 82 } 83 else 84 { 85 auto mbeg = cast(immutable ModuleInfo**)&__start_minfo; 86 auto mend = cast(immutable ModuleInfo**)&__stop_minfo; 87 } 88 _sections.moduleGroup = ModuleGroup(mbeg[0 .. mend - mbeg]); 89 90 // iterate over ELF segments to determine data segment range 91 import core.sys.linux.elf; 92 foreach (ref phdr; object) 93 { 94 if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_W)) // writeable data segment 95 { 96 safeAssert(_sections._gcRanges[0] is null, "expected a single data segment"); 97 98 void* start = object.baseAddress + phdr.p_vaddr; 99 void* end = start + phdr.p_memsz; 100 debug(PRINTF) printf("data segment: %p - %p\n", start, end); 101 102 // pointer-align up 103 enum mask = size_t.sizeof - 1; 104 start = cast(void*) ((cast(size_t)start + mask) & ~mask); 105 106 _sections._gcRanges[0] = start[0 .. end-start]; 107 } 108 } 109 } 110 111 void finiSections() nothrow @nogc 112 { 113 pthread_key_delete(_tlsKey); 114 } 115 116 void[]* initTLSRanges() nothrow @nogc 117 { 118 return &getTLSBlock(); 119 } 120 121 void finiTLSRanges(void[]* rng) nothrow @nogc 122 { 123 free(rng.ptr); 124 free(rng); 125 } 126 127 void scanTLSRanges(void[]* rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow 128 { 129 dg(rng.ptr, rng.ptr + rng.length); 130 } 131 132 /* NOTE: The Bionic C library ignores thread-local data stored in the normal 133 * .tdata/.tbss ELF sections, which are marked with the SHF_TLS/STT_TLS 134 * flags. So instead we roll our own emulation (e.g., in LDC's LLVM fork) 135 * by keeping static TLS data in the .tdata/.tbss sections but removing 136 * the SHF_TLS/STT_TLS flags, and access the TLS data using this function. 137 * 138 * This function is called by the code emitted by the compiler. It is 139 * expected to translate an address in the TLS static data to the 140 * corresponding address in the TLS dynamic per-thread data. 141 */ 142 extern(C) void* __tls_get_addr(void* p) nothrow @nogc 143 { 144 debug(PRINTF) printf(" __tls_get_addr input - %p\n", p); 145 const offset = cast(size_t) (p - _staticTLSRange.ptr); 146 assert(offset < _staticTLSRange.length, 147 "invalid TLS address or initSections() not called yet"); 148 // The following would only be safe if no TLS variables are accessed 149 // before calling initTLSRanges(): 150 //return (cast(void[]*) pthread_getspecific(_tlsKey)).ptr + offset; 151 return getTLSBlock().ptr + offset; 152 } 153 154 private: 155 156 __gshared pthread_key_t _tlsKey; 157 __gshared void[] _staticTLSRange; 158 __gshared uint _tlsAlignment; 159 __gshared SectionGroup _sections; 160 161 /* The following code relies on the .tdata (non-zero initialized) and .tbss 162 * (zero-initialized) sections to end up adjacent to each other in the final 163 * linked binary. 164 * This allows to merge both (and thus all TLS data) to a single contiguous 165 * memory block. 166 */ 167 168 /* Enforce some minimum alignment for both sections. 169 * The relative offset of the 2nd section from the first (in memory) needs to be 170 * a multiple of the 2nd section's alignment, so that the 2nd section ends up 171 * suitably aligned in each thread's TLS block. 172 */ 173 enum minAlignment = 16; 174 align(minAlignment) 175 { 176 byte alignmentForTDATA = 1; 177 byte alignmentForTBSS; 178 } 179 180 // aligned_alloc is only available since API level 28 181 extern(C) int posix_memalign(void** memptr, size_t alignment, size_t size) nothrow @nogc; 182 183 ref void[] getTLSBlock() nothrow @nogc 184 { 185 auto pary = cast(void[]*) pthread_getspecific(_tlsKey); 186 187 version (LDC) 188 { 189 import ldc.intrinsics; 190 const isUninitialized = llvm_expect(pary is null, false); 191 } 192 else 193 const isUninitialized = pary is null; 194 195 if (isUninitialized) 196 { 197 pary = cast(void[]*) calloc(1, (void[]).sizeof); 198 safeAssert(pary !is null, "cannot allocate TLS block slice"); 199 200 if (pthread_setspecific(_tlsKey, pary) != 0) 201 { 202 import core.stdc.stdio; 203 perror("pthread_setspecific failed with"); 204 assert(0); 205 } 206 207 safeAssert(_staticTLSRange.ptr !is null, "initSections() not called yet"); 208 if (const size = _staticTLSRange.length) 209 { 210 void* p; 211 const error = posix_memalign(&p, _tlsAlignment, size); 212 safeAssert(error == 0, "cannot allocate TLS block"); 213 memcpy(p, _staticTLSRange.ptr, size); 214 *pary = p[0 .. size]; 215 } 216 } 217 218 return *pary; 219 } 220 221 // Returns the static TLS data range (.tdata and .tbss sections). 222 // `alignment` is set to the max overall TLS alignment, to be used to align each 223 // thread's TLS block. 224 void[] getStaticTLSRange(const ref SharedObject object, out uint alignment) nothrow @nogc 225 { 226 import core.internal.elf.io; 227 228 const(char)[] path = object.name(); 229 char[512] pathBuffer = void; 230 if (path[0] != '/') 231 { 232 path = object.getPath(pathBuffer); 233 safeAssert(path !is null, "cannot get path of ELF object"); 234 } 235 debug(PRINTF) printf("ELF file path: %s\n", path.ptr); 236 237 ElfFile file; 238 const success = ElfFile.open(path.ptr, file); 239 safeAssert(success, "cannot open ELF file"); 240 241 void* start, end; 242 alignment = minAlignment; 243 foreach (index, name, sectionHeader; file.namedSections) 244 { 245 if (name == ".tdata" || name == ".tbss") 246 { 247 void* sectionStart = object.baseAddress + sectionHeader.sh_addr; 248 void* sectionEnd = sectionStart + sectionHeader.sh_size; 249 const sectionAlignment = cast(uint) sectionHeader.sh_addralign; 250 debug(PRINTF) printf("section %s: %p - %p, alignment: %u\n", name.ptr, sectionStart, sectionEnd, sectionAlignment); 251 252 if (sectionAlignment > alignment) 253 alignment = sectionAlignment; 254 255 if (!start) 256 { 257 start = sectionStart; 258 end = sectionEnd; 259 } 260 else 261 { 262 const bytesInbetweenSections = sectionStart - end; 263 safeAssert(bytesInbetweenSections >= 0 && bytesInbetweenSections < alignment, 264 "expected .tdata and .tbss sections to be adjacent (hint: try ld.bfd linker)"); 265 safeAssert(sectionAlignment == 0 || ((sectionStart - start) & (sectionAlignment - 1)) == 0, 266 "offset of .tbss section from .tdata isn't a multiple of the .tbss alignment (workaround: align .tdata manually)"); 267 end = sectionEnd; 268 break; // we've found both sections 269 } 270 } 271 } 272 273 // return an empty but non-null slice if there's no TLS data 274 return start ? start[0 .. end-start] : object.baseAddress[0..0]; 275 } 276 277 extern(C) 278 { 279 /* Symbols created by the linker and inserted into the object file that 280 * 'bracket' sections. 281 */ 282 extern __gshared 283 { 284 version (LDC) 285 { 286 void* __start___minfo; 287 void* __stop___minfo; 288 } 289 else 290 { 291 void* __start_deh; 292 void* __stop_deh; 293 void* __start_minfo; 294 void* __stop_minfo; 295 } 296 } 297 }