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