1 /**
2  * Written in the D programming language.
3  * This module provides Win32-specific support for sections.
4  *
5  * Copyright: Copyright Digital Mars 2008 - 2012.
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
10  * Source: $(DRUNTIMESRC rt/_sections_win64.d)
11  */
12 
13 module rt.sections_win64;
14 
15 version (CRuntime_Microsoft):
16 
17 // debug = PRINTF;
18 debug(PRINTF) import core.stdc.stdio;
19 import core.stdc.stdlib : malloc, free;
20 import core.sys.windows.winbase : FreeLibrary, GetProcAddress, LoadLibraryA, LoadLibraryW;
21 import core.sys.windows.winnt : WCHAR;
22 import rt.deh, rt.minfo;
23 
24 struct SectionGroup
25 {
26     static int opApply(scope int delegate(ref SectionGroup) dg)
27     {
28         return dg(_sections);
29     }
30 
31     static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
32     {
33         return dg(_sections);
34     }
35 
36     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
37     {
38         return _moduleGroup.modules;
39     }
40 
41     @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc
42     {
43         return _moduleGroup;
44     }
45 
46     version (DigitalMars)
47     version (Win64)
48     @property immutable(FuncTable)[] ehTables() const nothrow @nogc
49     {
50         auto pbeg = cast(immutable(FuncTable)*)&_deh_beg;
51         auto pend = cast(immutable(FuncTable)*)&_deh_end;
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[][] _gcRanges;
63 }
64 
65 shared(bool) conservative;
66 
67 /****
68  * Gets called on program startup just before GC is initialized.
69  */
70 void initSections() nothrow @nogc
71 {
72     _sections._moduleGroup = ModuleGroup(getModuleInfos());
73 
74     // the ".data" image section includes both object file sections ".data" and ".bss"
75     void[] dataSection = findImageSection(".data");
76     debug(PRINTF) printf("found .data section: [%p,+%llx]\n", dataSection.ptr,
77                          cast(ulong)dataSection.length);
78 
79     import rt.sections;
80     conservative = !scanDataSegPrecisely();
81 
82     if (conservative)
83     {
84         _sections._gcRanges = (cast(void[]*) malloc((void[]).sizeof))[0..1];
85         _sections._gcRanges[0] = dataSection;
86     }
87     else
88     {
89         size_t count = &_DP_end - &_DP_beg;
90         auto ranges = cast(void[]*) malloc(count * (void[]).sizeof);
91         size_t r = 0;
92         void* prev = null;
93         for (size_t i = 0; i < count; i++)
94         {
95             auto off = (&_DP_beg)[i];
96             if (off == 0) // skip zero entries added by incremental linking
97                 continue; // assumes there is no D-pointer at the very beginning of .data
98             void* addr = dataSection.ptr + off;
99             debug(PRINTF) printf("  scan %p\n", addr);
100             // combine consecutive pointers into single range
101             if (prev + (void*).sizeof == addr)
102                 ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof];
103             else
104                 ranges[r++] = (cast(void**)addr)[0..1];
105             prev = addr;
106         }
107         _sections._gcRanges = ranges[0..r];
108     }
109 }
110 
111 /***
112  * Gets called on program shutdown just after GC is terminated.
113  */
114 void finiSections() nothrow @nogc
115 {
116     .free(cast(void*)_sections.modules.ptr);
117     .free(_sections._gcRanges.ptr);
118 }
119 
120 /***
121  * Called once per thread; returns array of thread local storage ranges
122  */
123 void[] initTLSRanges() nothrow @nogc
124 {
125     void* pbeg;
126     void* pend;
127     // with VS2017 15.3.1, the linker no longer puts TLS segments into a
128     //  separate image section. That way _tls_start and _tls_end no
129     //  longer generate offsets into .tls, but DATA.
130     // Use the TEB entry to find the start of TLS instead and read the
131     //  length from the TLS directory
132     version (D_InlineAsm_X86)
133     {
134         asm @nogc nothrow
135         {
136             mov EAX, _tls_index;
137             mov ECX, FS:[0x2C];     // _tls_array
138             mov EAX, [ECX+4*EAX];
139             mov pbeg, EAX;
140             add EAX, [_tls_used+4]; // end
141             sub EAX, [_tls_used+0]; // start
142             mov pend, EAX;
143         }
144     }
145     else version (D_InlineAsm_X86_64)
146     {
147         asm @nogc nothrow
148         {
149             xor RAX, RAX;
150             mov EAX, _tls_index;
151             mov RCX, 0x58;
152             mov RCX, GS:[RCX];      // _tls_array (immediate value causes fixup)
153             mov RAX, [RCX+8*RAX];
154             mov pbeg, RAX;
155             add RAX, [_tls_used+8]; // end
156             sub RAX, [_tls_used+0]; // start
157             mov pend, RAX;
158         }
159     }
160     else
161         static assert(false, "Architecture not supported.");
162 
163     return pbeg[0 .. pend - pbeg];
164 }
165 
166 void finiTLSRanges(void[] rng) nothrow @nogc
167 {
168 }
169 
170 void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
171 {
172     if (conservative)
173     {
174         dg(rng.ptr, rng.ptr + rng.length);
175     }
176     else
177     {
178         for (auto p = &_TP_beg; p < &_TP_end; )
179         {
180             uint beg = *p++;
181             uint end = beg + cast(uint)((void*).sizeof);
182             while (p < &_TP_end && *p == end)
183             {
184                 end += (void*).sizeof;
185                 p++;
186             }
187             dg(rng.ptr + beg, rng.ptr + end);
188         }
189     }
190 }
191 
192 private:
193 
194 ///////////////////////////////////////////////////////////////////////////////
195 // Compiler to runtime interface.
196 ///////////////////////////////////////////////////////////////////////////////
197 
198 __gshared SectionGroup _sections;
199 
200 extern(C)
201 {
202     extern __gshared void* _minfo_beg;
203     extern __gshared void* _minfo_end;
204 }
205 
206 immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc
207 out (result)
208 {
209     foreach (m; result)
210         assert(m !is null);
211 }
212 do
213 {
214     auto m = (cast(immutable(ModuleInfo*)*)&_minfo_beg)[1 .. &_minfo_end - &_minfo_beg];
215     /* Because of alignment inserted by the linker, various null pointers
216      * are there. We need to filter them out.
217      */
218     auto p = m.ptr;
219     auto pend = m.ptr + m.length;
220 
221     // count non-null pointers
222     size_t cnt;
223     for (; p < pend; ++p)
224     {
225         if (*p !is null) ++cnt;
226     }
227 
228     auto result = (cast(immutable(ModuleInfo)**).malloc(cnt * size_t.sizeof))[0 .. cnt];
229 
230     p = m.ptr;
231     cnt = 0;
232     for (; p < pend; ++p)
233         if (*p !is null) result[cnt++] = *p;
234 
235     return cast(immutable)result;
236 }
237 
238 extern(C)
239 {
240     /* Symbols created by the compiler/linker and inserted into the
241      * object file that 'bracket' sections.
242      */
243     extern __gshared
244     {
245         void* __ImageBase;
246 
247         void* _deh_beg;
248         void* _deh_end;
249 
250         uint _DP_beg;
251         uint _DP_end;
252         uint _TP_beg;
253         uint _TP_end;
254 
255         void*[2] _tls_used; // start, end
256         int _tls_index;
257     }
258 }
259 
260 ///////////////////////////////////////////////////////////////////////////////
261 // dynamic loading
262 ///////////////////////////////////////////////////////////////////////////////
263 
264 /***********************************
265  * These are a temporary means of providing a GC hook for DLL use.  They may be
266  * replaced with some other similar functionality later.
267  */
268 extern (C)
269 {
270     void* gc_getProxy();
271     void  gc_setProxy(void* p);
272     void  gc_clrProxy();
273 
274     alias void  function(void*) gcSetFn;
275     alias void  function()      gcClrFn;
276 }
277 
278 /*******************************************
279  * Loads a DLL written in D with the name 'name'.
280  * Returns:
281  *      opaque handle to the DLL if successfully loaded
282  *      null if failure
283  */
284 extern (C) void* rt_loadLibrary(const char* name)
285 {
286     return initLibrary(.LoadLibraryA(name));
287 }
288 
289 extern (C) void* rt_loadLibraryW(const WCHAR* name)
290 {
291     return initLibrary(.LoadLibraryW(name));
292 }
293 
294 void* initLibrary(void* mod)
295 {
296     // BUG: LoadLibrary() call calls rt_init(), which fails if proxy is not set!
297     // (What? LoadLibrary() is a Windows API call, it shouldn't call rt_init().)
298     if (mod is null)
299         return mod;
300     gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy");
301     if (gcSet !is null)
302     {   // BUG: Set proxy, but too late
303         gcSet(gc_getProxy());
304     }
305     return mod;
306 }
307 
308 /*************************************
309  * Unloads DLL that was previously loaded by rt_loadLibrary().
310  * Input:
311  *      ptr     the handle returned by rt_loadLibrary()
312  * Returns:
313  *      1   succeeded
314  *      0   some failure happened
315  */
316 extern (C) int rt_unloadLibrary(void* ptr)
317 {
318     gcClrFn gcClr  = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy");
319     if (gcClr !is null)
320         gcClr();
321     return FreeLibrary(ptr) != 0;
322 }
323 
324 ///////////////////////////////////////////////////////////////////////////////
325 // PE/COFF program header iteration
326 ///////////////////////////////////////////////////////////////////////////////
327 
328 enum IMAGE_DOS_SIGNATURE = 0x5A4D;      // MZ
329 
330 struct IMAGE_DOS_HEADER // DOS .EXE header
331 {
332     ushort   e_magic;    // Magic number
333     ushort[29] e_res2;   // Reserved ushorts
334     int      e_lfanew;   // File address of new exe header
335 }
336 
337 struct IMAGE_FILE_HEADER
338 {
339     ushort Machine;
340     ushort NumberOfSections;
341     uint   TimeDateStamp;
342     uint   PointerToSymbolTable;
343     uint   NumberOfSymbols;
344     ushort SizeOfOptionalHeader;
345     ushort Characteristics;
346 }
347 
348 struct IMAGE_NT_HEADERS
349 {
350     uint Signature;
351     IMAGE_FILE_HEADER FileHeader;
352     // optional header follows
353 }
354 
355 struct IMAGE_SECTION_HEADER
356 {
357     char[8] Name = 0;
358     union {
359         uint   PhysicalAddress;
360         uint   VirtualSize;
361     }
362     uint   VirtualAddress;
363     uint   SizeOfRawData;
364     uint   PointerToRawData;
365     uint   PointerToRelocations;
366     uint   PointerToLinenumbers;
367     ushort NumberOfRelocations;
368     ushort NumberOfLinenumbers;
369     uint   Characteristics;
370 }
371 
372 bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc
373 {
374     if (name[] != section.Name[0 .. name.length])
375         return false;
376     return name.length == 8 || section.Name[name.length] == 0;
377 }
378 
379 void[] findImageSection(string name) nothrow @nogc
380 {
381     if (name.length > 8) // section name from string table not supported
382         return null;
383     IMAGE_DOS_HEADER* doshdr = cast(IMAGE_DOS_HEADER*) &__ImageBase;
384     if (doshdr.e_magic != IMAGE_DOS_SIGNATURE)
385         return null;
386 
387     auto nthdr = cast(IMAGE_NT_HEADERS*)(cast(void*)doshdr + doshdr.e_lfanew);
388     auto sections = cast(IMAGE_SECTION_HEADER*)(cast(void*)nthdr + IMAGE_NT_HEADERS.sizeof + nthdr.FileHeader.SizeOfOptionalHeader);
389     for (ushort i = 0; i < nthdr.FileHeader.NumberOfSections; i++)
390         if (compareSectionName (sections[i], name))
391             return (cast(void*)&__ImageBase + sections[i].VirtualAddress)[0 .. sections[i].VirtualSize];
392 
393     return null;
394 }