1// Win64-specific support for sections.
2// Copyright (C) 2019-2020 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
23module gcc.sections.win64;
24
25version (CRuntime_Microsoft):
26
27// debug = PRINTF;
28debug(PRINTF) import core.stdc.stdio;
29import core.stdc.stdlib : malloc, free;
30import rt.deh, rt.minfo;
31
32struct SectionGroup
33{
34    static int opApply(scope int delegate(ref SectionGroup) dg)
35    {
36        return dg(_sections);
37    }
38
39    static int opApplyReverse(scope int delegate(ref SectionGroup) dg)
40    {
41        return dg(_sections);
42    }
43
44    @property immutable(ModuleInfo*)[] modules() const nothrow @nogc
45    {
46        return _moduleGroup.modules;
47    }
48
49    @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc
50    {
51        return _moduleGroup;
52    }
53
54    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
62    @property inout(void[])[] gcRanges() inout nothrow @nogc
63    {
64        return _gcRanges[];
65    }
66
67private:
68    ModuleGroup _moduleGroup;
69    void[][] _gcRanges;
70}
71
72shared(bool) conservative;
73
74void 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
115void finiSections() nothrow @nogc
116{
117    .free(cast(void*)_sections.modules.ptr);
118    .free(_sections._gcRanges.ptr);
119}
120
121void[] 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
164void finiTLSRanges(void[] rng) nothrow @nogc
165{
166}
167
168void 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
190private:
191__gshared SectionGroup _sections;
192
193extern(C)
194{
195    extern __gshared void* _minfo_beg;
196    extern __gshared void* _minfo_end;
197}
198
199immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc
200out (result)
201{
202    foreach (m; result)
203        assert(m !is null);
204}
205body
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
231extern(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
255enum IMAGE_DOS_SIGNATURE = 0x5A4D;      // MZ
256
257struct 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
264struct 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
275struct IMAGE_NT_HEADERS
276{
277    uint Signature;
278    IMAGE_FILE_HEADER FileHeader;
279    // optional header follows
280}
281
282struct 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
299bool 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
306void[] 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