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