1 // Win64-specific support for sections.
2 // Copyright (C) 2019 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.win64;
24 
25 version (CRuntime_Microsoft):
26 
27 // debug = PRINTF;
28 debug(PRINTF) import core.stdc.stdio;
29 import core.stdc.stdlib : malloc, free;
30 import rt.deh, rt.minfo;
31 
32 struct SectionGroup
33 {
opApplySectionGroup34     static int opApply(scope int delegate(ref SectionGroup) dg)
35     {
36         return dg(_sections);
37     }
38 
opApplyReverseSectionGroup39     static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
40     {
41         return dg(_sections);
42     }
43 
immutableSectionGroup44     @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
45     {
46         return _moduleGroup.modules;
47     }
48 
inoutSectionGroup49     @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
50     {
51         return _moduleGroup;
52     }
53 
versionSectionGroup54     version (Win64)
55     @property immutable(FuncTable)[] ehTables() const nothrow @nogc
56     {
57         auto pbeg = cast(immutable(FuncTable)*)&_deh_beg;
58         auto pend = cast(immutable(FuncTable)*)&_deh_end;
59         return pbeg[0 .. pend - pbeg];
60     }
61 
inoutSectionGroup62     @property inout(void[])[] gcRanges() inout nothrow @nogc
63     {
64         return _gcRanges[];
65     }
66 
67 private:
68     ModuleGroup _moduleGroup;
69     void[][] _gcRanges;
70 }
71 
72 shared(bool) conservative;
73 
initSections()74 void initSections() nothrow @nogc
75 {
76     _sections._moduleGroup = ModuleGroup(getModuleInfos());
77 
78     // the ".data" image section includes both object file sections ".data" and ".bss"
79     void[] dataSection = findImageSection(".data");
80     debug(PRINTF) printf("found .data section: [%p,+%llx]\n", dataSection.ptr,
81                          cast(ulong)dataSection.length);
82 
83     import rt.sections;
84     conservative = !scanDataSegPrecisely();
85 
86     if (conservative)
87     {
88         _sections._gcRanges = (cast(void[]*) malloc((void[]).sizeof))[0..1];
89         _sections._gcRanges[0] = dataSection;
90     }
91     else
92     {
93         size_t count = &_DP_end - &_DP_beg;
94         auto ranges = cast(void[]*) malloc(count * (void[]).sizeof);
95         size_t r = 0;
96         void* prev = null;
97         for (size_t i = 0; i < count; i++)
98         {
99             auto off = (&_DP_beg)[i];
100             if (off == 0) // skip zero entries added by incremental linking
101                 continue; // assumes there is no D-pointer at the very beginning of .data
102             void* addr = dataSection.ptr + off;
103             debug(PRINTF) printf("  scan %p\n", addr);
104             // combine consecutive pointers into single range
105             if (prev + (void*).sizeof == addr)
106                 ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof];
107             else
108                 ranges[r++] = (cast(void**)addr)[0..1];
109             prev = addr;
110         }
111         _sections._gcRanges = ranges[0..r];
112     }
113 }
114 
finiSections()115 void finiSections() nothrow @nogc
116 {
117     .free(cast(void*)_sections.modules.ptr);
118     .free(_sections._gcRanges.ptr);
119 }
120 
initTLSRanges()121 void[] initTLSRanges() nothrow @nogc
122 {
123     void* pbeg;
124     void* pend;
125     // with VS2017 15.3.1, the linker no longer puts TLS segments into a
126     //  separate image section. That way _tls_start and _tls_end no
127     //  longer generate offsets into .tls, but DATA.
128     // Use the TEB entry to find the start of TLS instead and read the
129     //  length from the TLS directory
130     version (D_InlineAsm_X86)
131     {
132         asm @nogc nothrow
133         {
134             mov EAX, _tls_index;
135             mov ECX, FS:[0x2C];     // _tls_array
136             mov EAX, [ECX+4*EAX];
137             mov pbeg, EAX;
138             add EAX, [_tls_used+4]; // end
139             sub EAX, [_tls_used+0]; // start
140             mov pend, EAX;
141         }
142     }
143     else version (D_InlineAsm_X86_64)
144     {
145         asm @nogc nothrow
146         {
147             xor RAX, RAX;
148             mov EAX, _tls_index;
149             mov RCX, 0x58;
150             mov RCX, GS:[RCX];      // _tls_array (immediate value causes fixup)
151             mov RAX, [RCX+8*RAX];
152             mov pbeg, RAX;
153             add RAX, [_tls_used+8]; // end
154             sub RAX, [_tls_used+0]; // start
155             mov pend, RAX;
156         }
157     }
158     else
159         static assert(false, "Architecture not supported.");
160 
161     return pbeg[0 .. pend - pbeg];
162 }
163 
finiTLSRanges(void[]rng)164 void finiTLSRanges(void[] rng) nothrow @nogc
165 {
166 }
167 
scanTLSRanges(void[]rng,scope void delegate (void * pbeg,void * pend)nothrow dg)168 void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow
169 {
170     if (conservative)
171     {
172         dg(rng.ptr, rng.ptr + rng.length);
173     }
174     else
175     {
176         for (auto p = &_TP_beg; p < &_TP_end; )
177         {
178             uint beg = *p++;
179             uint end = beg + cast(uint)((void*).sizeof);
180             while (p < &_TP_end && *p == end)
181             {
182                 end += (void*).sizeof;
183                 p++;
184             }
185             dg(rng.ptr + beg, rng.ptr + end);
186         }
187     }
188 }
189 
190 private:
191 __gshared SectionGroup _sections;
192 
193 extern(C)
194 {
195     extern __gshared void* _minfo_beg;
196     extern __gshared void* _minfo_end;
197 }
198 
immutable(ModuleInfo *)199 immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc
200 out (result)
201 {
202     foreach (m; result)
203         assert(m !is null);
204 }
205 body
206 {
207     auto m = (cast(immutable(ModuleInfo*)*)&_minfo_beg)[1 .. &_minfo_end - &_minfo_beg];
208     /* Because of alignment inserted by the linker, various null pointers
209      * are there. We need to filter them out.
210      */
211     auto p = m.ptr;
212     auto pend = m.ptr + m.length;
213 
214     // count non-null pointers
215     size_t cnt;
216     for (; p < pend; ++p)
217     {
218         if (*p !is null) ++cnt;
219     }
220 
221     auto result = (cast(immutable(ModuleInfo)**).malloc(cnt * size_t.sizeof))[0 .. cnt];
222 
223     p = m.ptr;
224     cnt = 0;
225     for (; p < pend; ++p)
226         if (*p !is null) result[cnt++] = *p;
227 
228     return cast(immutable)result;
229 }
230 
231 extern(C)
232 {
233     /* Symbols created by the compiler/linker and inserted into the
234      * object file that 'bracket' sections.
235      */
236     extern __gshared
237     {
238         void* __ImageBase;
239 
240         void* _deh_beg;
241         void* _deh_end;
242 
243         uint _DP_beg;
244         uint _DP_end;
245         uint _TP_beg;
246         uint _TP_end;
247 
248         void*[2] _tls_used; // start, end
249         int _tls_index;
250     }
251 }
252 
253 /////////////////////////////////////////////////////////////////////
254 
255 enum IMAGE_DOS_SIGNATURE = 0x5A4D;      // MZ
256 
257 struct IMAGE_DOS_HEADER // DOS .EXE header
258 {
259     ushort   e_magic;    // Magic number
260     ushort[29] e_res2;   // Reserved ushorts
261     int      e_lfanew;   // File address of new exe header
262 }
263 
264 struct IMAGE_FILE_HEADER
265 {
266     ushort Machine;
267     ushort NumberOfSections;
268     uint   TimeDateStamp;
269     uint   PointerToSymbolTable;
270     uint   NumberOfSymbols;
271     ushort SizeOfOptionalHeader;
272     ushort Characteristics;
273 }
274 
275 struct IMAGE_NT_HEADERS
276 {
277     uint Signature;
278     IMAGE_FILE_HEADER FileHeader;
279     // optional header follows
280 }
281 
282 struct IMAGE_SECTION_HEADER
283 {
284     char[8] Name = 0;
285     union {
286         uint   PhysicalAddress;
287         uint   VirtualSize;
288     }
289     uint   VirtualAddress;
290     uint   SizeOfRawData;
291     uint   PointerToRawData;
292     uint   PointerToRelocations;
293     uint   PointerToLinenumbers;
294     ushort NumberOfRelocations;
295     ushort NumberOfLinenumbers;
296     uint   Characteristics;
297 }
298 
compareSectionName(ref IMAGE_SECTION_HEADER section,string name)299 bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc
300 {
301     if (name[] != section.Name[0 .. name.length])
302         return false;
303     return name.length == 8 || section.Name[name.length] == 0;
304 }
305 
findImageSection(string name)306 void[] findImageSection(string name) nothrow @nogc
307 {
308     if (name.length > 8) // section name from string table not supported
309         return null;
310     IMAGE_DOS_HEADER* doshdr = cast(IMAGE_DOS_HEADER*) &__ImageBase;
311     if (doshdr.e_magic != IMAGE_DOS_SIGNATURE)
312         return null;
313 
314     auto nthdr = cast(IMAGE_NT_HEADERS*)(cast(void*)doshdr + doshdr.e_lfanew);
315     auto sections = cast(IMAGE_SECTION_HEADER*)(cast(void*)nthdr + IMAGE_NT_HEADERS.sizeof + nthdr.FileHeader.SizeOfOptionalHeader);
316     for (ushort i = 0; i < nthdr.FileHeader.NumberOfSections; i++)
317         if (compareSectionName (sections[i], name))
318             return (cast(void*)&__ImageBase + sections[i].VirtualAddress)[0 .. sections[i].VirtualSize];
319 
320     return null;
321 }
322