// 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 // . 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; }