| // Win64-specific support for sections. |
| // Copyright (C) 2019-2020 Free Software Foundation, Inc. |
| |
| // GCC is free software; you can redistribute it and/or modify it under |
| // the terms of the GNU General Public License as published by the Free |
| // Software Foundation; either version 3, or (at your option) any later |
| // version. |
| |
| // GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| // for more details. |
| |
| // Under Section 7 of GPL version 3, you are granted additional |
| // permissions described in the GCC Runtime Library Exception, version |
| // 3.1, as published by the Free Software Foundation. |
| |
| // You should have received a copy of the GNU General Public License and |
| // a copy of the GCC Runtime Library Exception along with this program; |
| // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| // <http://www.gnu.org/licenses/>. |
| |
| module gcc.sections.win64; |
| |
| version (CRuntime_Microsoft): |
| |
| // debug = PRINTF; |
| debug(PRINTF) import core.stdc.stdio; |
| import core.stdc.stdlib : malloc, free; |
| import rt.deh, rt.minfo; |
| |
| struct SectionGroup |
| { |
| static int opApply(scope int delegate(ref SectionGroup) dg) |
| { |
| return dg(_sections); |
| } |
| |
| static int opApplyReverse(scope int delegate(ref SectionGroup) dg) |
| { |
| return dg(_sections); |
| } |
| |
| @property immutable(ModuleInfo*)[] modules() const nothrow @nogc |
| { |
| return _moduleGroup.modules; |
| } |
| |
| @property ref inout(ModuleGroup) moduleGroup() inout nothrow @nogc |
| { |
| return _moduleGroup; |
| } |
| |
| version (Win64) |
| @property immutable(FuncTable)[] ehTables() const nothrow @nogc |
| { |
| auto pbeg = cast(immutable(FuncTable)*)&_deh_beg; |
| auto pend = cast(immutable(FuncTable)*)&_deh_end; |
| return pbeg[0 .. pend - pbeg]; |
| } |
| |
| @property inout(void[])[] gcRanges() inout nothrow @nogc |
| { |
| return _gcRanges[]; |
| } |
| |
| private: |
| ModuleGroup _moduleGroup; |
| void[][] _gcRanges; |
| } |
| |
| shared(bool) conservative; |
| |
| void initSections() nothrow @nogc |
| { |
| _sections._moduleGroup = ModuleGroup(getModuleInfos()); |
| |
| // the ".data" image section includes both object file sections ".data" and ".bss" |
| void[] dataSection = findImageSection(".data"); |
| debug(PRINTF) printf("found .data section: [%p,+%llx]\n", dataSection.ptr, |
| cast(ulong)dataSection.length); |
| |
| import rt.sections; |
| conservative = !scanDataSegPrecisely(); |
| |
| if (conservative) |
| { |
| _sections._gcRanges = (cast(void[]*) malloc((void[]).sizeof))[0..1]; |
| _sections._gcRanges[0] = dataSection; |
| } |
| else |
| { |
| size_t count = &_DP_end - &_DP_beg; |
| auto ranges = cast(void[]*) malloc(count * (void[]).sizeof); |
| size_t r = 0; |
| void* prev = null; |
| for (size_t i = 0; i < count; i++) |
| { |
| auto off = (&_DP_beg)[i]; |
| if (off == 0) // skip zero entries added by incremental linking |
| continue; // assumes there is no D-pointer at the very beginning of .data |
| void* addr = dataSection.ptr + off; |
| debug(PRINTF) printf(" scan %p\n", addr); |
| // combine consecutive pointers into single range |
| if (prev + (void*).sizeof == addr) |
| ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof]; |
| else |
| ranges[r++] = (cast(void**)addr)[0..1]; |
| prev = addr; |
| } |
| _sections._gcRanges = ranges[0..r]; |
| } |
| } |
| |
| void finiSections() nothrow @nogc |
| { |
| .free(cast(void*)_sections.modules.ptr); |
| .free(_sections._gcRanges.ptr); |
| } |
| |
| void[] initTLSRanges() nothrow @nogc |
| { |
| void* pbeg; |
| void* pend; |
| // with VS2017 15.3.1, the linker no longer puts TLS segments into a |
| // separate image section. That way _tls_start and _tls_end no |
| // longer generate offsets into .tls, but DATA. |
| // Use the TEB entry to find the start of TLS instead and read the |
| // length from the TLS directory |
| version (D_InlineAsm_X86) |
| { |
| asm @nogc nothrow |
| { |
| mov EAX, _tls_index; |
| mov ECX, FS:[0x2C]; // _tls_array |
| mov EAX, [ECX+4*EAX]; |
| mov pbeg, EAX; |
| add EAX, [_tls_used+4]; // end |
| sub EAX, [_tls_used+0]; // start |
| mov pend, EAX; |
| } |
| } |
| else version (D_InlineAsm_X86_64) |
| { |
| asm @nogc nothrow |
| { |
| xor RAX, RAX; |
| mov EAX, _tls_index; |
| mov RCX, 0x58; |
| mov RCX, GS:[RCX]; // _tls_array (immediate value causes fixup) |
| mov RAX, [RCX+8*RAX]; |
| mov pbeg, RAX; |
| add RAX, [_tls_used+8]; // end |
| sub RAX, [_tls_used+0]; // start |
| mov pend, RAX; |
| } |
| } |
| else |
| static assert(false, "Architecture not supported."); |
| |
| return pbeg[0 .. pend - pbeg]; |
| } |
| |
| void finiTLSRanges(void[] rng) nothrow @nogc |
| { |
| } |
| |
| void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow |
| { |
| if (conservative) |
| { |
| dg(rng.ptr, rng.ptr + rng.length); |
| } |
| else |
| { |
| for (auto p = &_TP_beg; p < &_TP_end; ) |
| { |
| uint beg = *p++; |
| uint end = beg + cast(uint)((void*).sizeof); |
| while (p < &_TP_end && *p == end) |
| { |
| end += (void*).sizeof; |
| p++; |
| } |
| dg(rng.ptr + beg, rng.ptr + end); |
| } |
| } |
| } |
| |
| private: |
| __gshared SectionGroup _sections; |
| |
| extern(C) |
| { |
| extern __gshared void* _minfo_beg; |
| extern __gshared void* _minfo_end; |
| } |
| |
| immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc |
| out (result) |
| { |
| foreach (m; result) |
| assert(m !is null); |
| } |
| body |
| { |
| auto m = (cast(immutable(ModuleInfo*)*)&_minfo_beg)[1 .. &_minfo_end - &_minfo_beg]; |
| /* Because of alignment inserted by the linker, various null pointers |
| * are there. We need to filter them out. |
| */ |
| auto p = m.ptr; |
| auto pend = m.ptr + m.length; |
| |
| // count non-null pointers |
| size_t cnt; |
| for (; p < pend; ++p) |
| { |
| if (*p !is null) ++cnt; |
| } |
| |
| auto result = (cast(immutable(ModuleInfo)**).malloc(cnt * size_t.sizeof))[0 .. cnt]; |
| |
| p = m.ptr; |
| cnt = 0; |
| for (; p < pend; ++p) |
| if (*p !is null) result[cnt++] = *p; |
| |
| return cast(immutable)result; |
| } |
| |
| extern(C) |
| { |
| /* Symbols created by the compiler/linker and inserted into the |
| * object file that 'bracket' sections. |
| */ |
| extern __gshared |
| { |
| void* __ImageBase; |
| |
| void* _deh_beg; |
| void* _deh_end; |
| |
| uint _DP_beg; |
| uint _DP_end; |
| uint _TP_beg; |
| uint _TP_end; |
| |
| void*[2] _tls_used; // start, end |
| int _tls_index; |
| } |
| } |
| |
| ///////////////////////////////////////////////////////////////////// |
| |
| enum IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ |
| |
| struct IMAGE_DOS_HEADER // DOS .EXE header |
| { |
| ushort e_magic; // Magic number |
| ushort[29] e_res2; // Reserved ushorts |
| int e_lfanew; // File address of new exe header |
| } |
| |
| struct IMAGE_FILE_HEADER |
| { |
| ushort Machine; |
| ushort NumberOfSections; |
| uint TimeDateStamp; |
| uint PointerToSymbolTable; |
| uint NumberOfSymbols; |
| ushort SizeOfOptionalHeader; |
| ushort Characteristics; |
| } |
| |
| struct IMAGE_NT_HEADERS |
| { |
| uint Signature; |
| IMAGE_FILE_HEADER FileHeader; |
| // optional header follows |
| } |
| |
| struct IMAGE_SECTION_HEADER |
| { |
| char[8] Name = 0; |
| union { |
| uint PhysicalAddress; |
| uint VirtualSize; |
| } |
| uint VirtualAddress; |
| uint SizeOfRawData; |
| uint PointerToRawData; |
| uint PointerToRelocations; |
| uint PointerToLinenumbers; |
| ushort NumberOfRelocations; |
| ushort NumberOfLinenumbers; |
| uint Characteristics; |
| } |
| |
| bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc |
| { |
| if (name[] != section.Name[0 .. name.length]) |
| return false; |
| return name.length == 8 || section.Name[name.length] == 0; |
| } |
| |
| void[] findImageSection(string name) nothrow @nogc |
| { |
| if (name.length > 8) // section name from string table not supported |
| return null; |
| IMAGE_DOS_HEADER* doshdr = cast(IMAGE_DOS_HEADER*) &__ImageBase; |
| if (doshdr.e_magic != IMAGE_DOS_SIGNATURE) |
| return null; |
| |
| auto nthdr = cast(IMAGE_NT_HEADERS*)(cast(void*)doshdr + doshdr.e_lfanew); |
| auto sections = cast(IMAGE_SECTION_HEADER*)(cast(void*)nthdr + IMAGE_NT_HEADERS.sizeof + nthdr.FileHeader.SizeOfOptionalHeader); |
| for (ushort i = 0; i < nthdr.FileHeader.NumberOfSections; i++) |
| if (compareSectionName (sections[i], name)) |
| return (cast(void*)&__ImageBase + sections[i].VirtualAddress)[0 .. sections[i].VirtualSize]; |
| |
| return null; |
| } |