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 }