322 lines
8.7 KiB
D
322 lines
8.7 KiB
D
// Win64-specific support for sections.
|
|
// Copyright (C) 2019-2021 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;
|
|
}
|