1 /** 2 * Written in the D programming language. 3 * This module provides Win32-specific support for sections. 4 * 5 * Copyright: Copyright Digital Mars 2008 - 2012. 6 * License: Distributed under the 7 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0). 8 * (See accompanying file LICENSE) 9 * Authors: Walter Bright, Sean Kelly, Martin Nowak 10 * Source: $(DRUNTIMESRC rt/_sections_win64.d) 11 */ 12 13 module rt.sections_win64; 14 15 version (CRuntime_Microsoft): 16 17 // debug = PRINTF; 18 debug(PRINTF) import core.stdc.stdio; 19 import core.stdc.stdlib : malloc, free; 20 import core.sys.windows.winbase : FreeLibrary, GetProcAddress, LoadLibraryA, LoadLibraryW; 21 import core.sys.windows.winnt : WCHAR; 22 import rt.deh, rt.minfo; 23 24 struct SectionGroup 25 { 26 static int opApply(scope int delegate(ref SectionGroup) dg) 27 { 28 return dg(_sections); 29 } 30 31 static int opApplyReverse(scope int delegate(ref SectionGroup) dg) 32 { 33 return dg(_sections); 34 } 35 36 @property immutable(ModuleInfo*)[] modules() const nothrow @nogc 37 { 38 return _moduleGroup.modules; 39 } 40 41 @property ref inout(ModuleGroup) moduleGroup() inout return nothrow @nogc 42 { 43 return _moduleGroup; 44 } 45 46 version (DigitalMars) 47 version (Win64) 48 @property immutable(FuncTable)[] ehTables() const nothrow @nogc 49 { 50 auto pbeg = cast(immutable(FuncTable)*)&_deh_beg; 51 auto pend = cast(immutable(FuncTable)*)&_deh_end; 52 return pbeg[0 .. pend - pbeg]; 53 } 54 55 @property inout(void[])[] gcRanges() inout nothrow @nogc 56 { 57 return _gcRanges[]; 58 } 59 60 private: 61 ModuleGroup _moduleGroup; 62 void[][] _gcRanges; 63 } 64 65 shared(bool) conservative; 66 67 /**** 68 * Gets called on program startup just before GC is initialized. 69 */ 70 void initSections() nothrow @nogc 71 { 72 _sections._moduleGroup = ModuleGroup(getModuleInfos()); 73 74 // the ".data" image section includes both object file sections ".data" and ".bss" 75 void[] dataSection = findImageSection(".data"); 76 debug(PRINTF) printf("found .data section: [%p,+%llx]\n", dataSection.ptr, 77 cast(ulong)dataSection.length); 78 79 import rt.sections; 80 conservative = !scanDataSegPrecisely(); 81 82 if (conservative) 83 { 84 _sections._gcRanges = (cast(void[]*) malloc((void[]).sizeof))[0..1]; 85 _sections._gcRanges[0] = dataSection; 86 } 87 else 88 { 89 size_t count = &_DP_end - &_DP_beg; 90 auto ranges = cast(void[]*) malloc(count * (void[]).sizeof); 91 size_t r = 0; 92 void* prev = null; 93 for (size_t i = 0; i < count; i++) 94 { 95 auto off = (&_DP_beg)[i]; 96 if (off == 0) // skip zero entries added by incremental linking 97 continue; // assumes there is no D-pointer at the very beginning of .data 98 void* addr = dataSection.ptr + off; 99 debug(PRINTF) printf(" scan %p\n", addr); 100 // combine consecutive pointers into single range 101 if (prev + (void*).sizeof == addr) 102 ranges[r-1] = ranges[r-1].ptr[0 .. ranges[r-1].length + (void*).sizeof]; 103 else 104 ranges[r++] = (cast(void**)addr)[0..1]; 105 prev = addr; 106 } 107 _sections._gcRanges = ranges[0..r]; 108 } 109 } 110 111 /*** 112 * Gets called on program shutdown just after GC is terminated. 113 */ 114 void finiSections() nothrow @nogc 115 { 116 .free(cast(void*)_sections.modules.ptr); 117 .free(_sections._gcRanges.ptr); 118 } 119 120 /*** 121 * Called once per thread; returns array of thread local storage ranges 122 */ 123 void[] initTLSRanges() nothrow @nogc 124 { 125 void* pbeg; 126 void* pend; 127 // with VS2017 15.3.1, the linker no longer puts TLS segments into a 128 // separate image section. That way _tls_start and _tls_end no 129 // longer generate offsets into .tls, but DATA. 130 // Use the TEB entry to find the start of TLS instead and read the 131 // length from the TLS directory 132 version (D_InlineAsm_X86) 133 { 134 asm @nogc nothrow 135 { 136 mov EAX, _tls_index; 137 mov ECX, FS:[0x2C]; // _tls_array 138 mov EAX, [ECX+4*EAX]; 139 mov pbeg, EAX; 140 add EAX, [_tls_used+4]; // end 141 sub EAX, [_tls_used+0]; // start 142 mov pend, EAX; 143 } 144 } 145 else version (D_InlineAsm_X86_64) 146 { 147 asm @nogc nothrow 148 { 149 xor RAX, RAX; 150 mov EAX, _tls_index; 151 mov RCX, 0x58; 152 mov RCX, GS:[RCX]; // _tls_array (immediate value causes fixup) 153 mov RAX, [RCX+8*RAX]; 154 mov pbeg, RAX; 155 add RAX, [_tls_used+8]; // end 156 sub RAX, [_tls_used+0]; // start 157 mov pend, RAX; 158 } 159 } 160 else 161 static assert(false, "Architecture not supported."); 162 163 return pbeg[0 .. pend - pbeg]; 164 } 165 166 void finiTLSRanges(void[] rng) nothrow @nogc 167 { 168 } 169 170 void scanTLSRanges(void[] rng, scope void delegate(void* pbeg, void* pend) nothrow dg) nothrow 171 { 172 if (conservative) 173 { 174 dg(rng.ptr, rng.ptr + rng.length); 175 } 176 else 177 { 178 for (auto p = &_TP_beg; p < &_TP_end; ) 179 { 180 uint beg = *p++; 181 uint end = beg + cast(uint)((void*).sizeof); 182 while (p < &_TP_end && *p == end) 183 { 184 end += (void*).sizeof; 185 p++; 186 } 187 dg(rng.ptr + beg, rng.ptr + end); 188 } 189 } 190 } 191 192 private: 193 194 /////////////////////////////////////////////////////////////////////////////// 195 // Compiler to runtime interface. 196 /////////////////////////////////////////////////////////////////////////////// 197 198 __gshared SectionGroup _sections; 199 200 extern(C) 201 { 202 extern __gshared void* _minfo_beg; 203 extern __gshared void* _minfo_end; 204 } 205 206 immutable(ModuleInfo*)[] getModuleInfos() nothrow @nogc 207 out (result) 208 { 209 foreach (m; result) 210 assert(m !is null); 211 } 212 do 213 { 214 auto m = (cast(immutable(ModuleInfo*)*)&_minfo_beg)[1 .. &_minfo_end - &_minfo_beg]; 215 /* Because of alignment inserted by the linker, various null pointers 216 * are there. We need to filter them out. 217 */ 218 auto p = m.ptr; 219 auto pend = m.ptr + m.length; 220 221 // count non-null pointers 222 size_t cnt; 223 for (; p < pend; ++p) 224 { 225 if (*p !is null) ++cnt; 226 } 227 228 auto result = (cast(immutable(ModuleInfo)**).malloc(cnt * size_t.sizeof))[0 .. cnt]; 229 230 p = m.ptr; 231 cnt = 0; 232 for (; p < pend; ++p) 233 if (*p !is null) result[cnt++] = *p; 234 235 return cast(immutable)result; 236 } 237 238 extern(C) 239 { 240 /* Symbols created by the compiler/linker and inserted into the 241 * object file that 'bracket' sections. 242 */ 243 extern __gshared 244 { 245 void* __ImageBase; 246 247 void* _deh_beg; 248 void* _deh_end; 249 250 uint _DP_beg; 251 uint _DP_end; 252 uint _TP_beg; 253 uint _TP_end; 254 255 void*[2] _tls_used; // start, end 256 int _tls_index; 257 } 258 } 259 260 /////////////////////////////////////////////////////////////////////////////// 261 // dynamic loading 262 /////////////////////////////////////////////////////////////////////////////// 263 264 /*********************************** 265 * These are a temporary means of providing a GC hook for DLL use. They may be 266 * replaced with some other similar functionality later. 267 */ 268 extern (C) 269 { 270 void* gc_getProxy(); 271 void gc_setProxy(void* p); 272 void gc_clrProxy(); 273 274 alias void function(void*) gcSetFn; 275 alias void function() gcClrFn; 276 } 277 278 /******************************************* 279 * Loads a DLL written in D with the name 'name'. 280 * Returns: 281 * opaque handle to the DLL if successfully loaded 282 * null if failure 283 */ 284 extern (C) void* rt_loadLibrary(const char* name) 285 { 286 return initLibrary(.LoadLibraryA(name)); 287 } 288 289 extern (C) void* rt_loadLibraryW(const WCHAR* name) 290 { 291 return initLibrary(.LoadLibraryW(name)); 292 } 293 294 void* initLibrary(void* mod) 295 { 296 // BUG: LoadLibrary() call calls rt_init(), which fails if proxy is not set! 297 // (What? LoadLibrary() is a Windows API call, it shouldn't call rt_init().) 298 if (mod is null) 299 return mod; 300 gcSetFn gcSet = cast(gcSetFn) GetProcAddress(mod, "gc_setProxy"); 301 if (gcSet !is null) 302 { // BUG: Set proxy, but too late 303 gcSet(gc_getProxy()); 304 } 305 return mod; 306 } 307 308 /************************************* 309 * Unloads DLL that was previously loaded by rt_loadLibrary(). 310 * Input: 311 * ptr the handle returned by rt_loadLibrary() 312 * Returns: 313 * 1 succeeded 314 * 0 some failure happened 315 */ 316 extern (C) int rt_unloadLibrary(void* ptr) 317 { 318 gcClrFn gcClr = cast(gcClrFn) GetProcAddress(ptr, "gc_clrProxy"); 319 if (gcClr !is null) 320 gcClr(); 321 return FreeLibrary(ptr) != 0; 322 } 323 324 /////////////////////////////////////////////////////////////////////////////// 325 // PE/COFF program header iteration 326 /////////////////////////////////////////////////////////////////////////////// 327 328 enum IMAGE_DOS_SIGNATURE = 0x5A4D; // MZ 329 330 struct IMAGE_DOS_HEADER // DOS .EXE header 331 { 332 ushort e_magic; // Magic number 333 ushort[29] e_res2; // Reserved ushorts 334 int e_lfanew; // File address of new exe header 335 } 336 337 struct IMAGE_FILE_HEADER 338 { 339 ushort Machine; 340 ushort NumberOfSections; 341 uint TimeDateStamp; 342 uint PointerToSymbolTable; 343 uint NumberOfSymbols; 344 ushort SizeOfOptionalHeader; 345 ushort Characteristics; 346 } 347 348 struct IMAGE_NT_HEADERS 349 { 350 uint Signature; 351 IMAGE_FILE_HEADER FileHeader; 352 // optional header follows 353 } 354 355 struct IMAGE_SECTION_HEADER 356 { 357 char[8] Name = 0; 358 union { 359 uint PhysicalAddress; 360 uint VirtualSize; 361 } 362 uint VirtualAddress; 363 uint SizeOfRawData; 364 uint PointerToRawData; 365 uint PointerToRelocations; 366 uint PointerToLinenumbers; 367 ushort NumberOfRelocations; 368 ushort NumberOfLinenumbers; 369 uint Characteristics; 370 } 371 372 bool compareSectionName(ref IMAGE_SECTION_HEADER section, string name) nothrow @nogc 373 { 374 if (name[] != section.Name[0 .. name.length]) 375 return false; 376 return name.length == 8 || section.Name[name.length] == 0; 377 } 378 379 void[] findImageSection(string name) nothrow @nogc 380 { 381 if (name.length > 8) // section name from string table not supported 382 return null; 383 IMAGE_DOS_HEADER* doshdr = cast(IMAGE_DOS_HEADER*) &__ImageBase; 384 if (doshdr.e_magic != IMAGE_DOS_SIGNATURE) 385 return null; 386 387 auto nthdr = cast(IMAGE_NT_HEADERS*)(cast(void*)doshdr + doshdr.e_lfanew); 388 auto sections = cast(IMAGE_SECTION_HEADER*)(cast(void*)nthdr + IMAGE_NT_HEADERS.sizeof + nthdr.FileHeader.SizeOfOptionalHeader); 389 for (ushort i = 0; i < nthdr.FileHeader.NumberOfSections; i++) 390 if (compareSectionName (sections[i], name)) 391 return (cast(void*)&__ImageBase + sections[i].VirtualAddress)[0 .. sections[i].VirtualSize]; 392 393 return null; 394 }