1 /** 2 * Compiler implementation of the 3 * $(LINK2 http://www.dlang.org, D programming language). 4 * 5 * Copyright: Copyright (C) 2012-2020 by The D Language Foundation, All Rights Reserved 6 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 7 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/pdata.d, backend/pdata.d) 9 */ 10 11 // This module generates the .pdata and .xdata sections for Win64 12 13 module dmd.backend.pdata; 14 15 version (MARS) 16 { 17 18 import core.stdc.stdio; 19 import core.stdc.stdlib; 20 import core.stdc..string; 21 22 import dmd.backend.cc; 23 import dmd.backend.cdef; 24 import dmd.backend.code; 25 import dmd.backend.code_x86; 26 import dmd.backend.dt; 27 import dmd.backend.el; 28 import dmd.backend.exh; 29 import dmd.backend.global; 30 import dmd.backend.obj; 31 import dmd.backend.rtlsym; 32 import dmd.backend.ty; 33 import dmd.backend.type; 34 35 extern (C++): 36 37 nothrow: 38 39 static if (TARGET_WINDOS) 40 { 41 42 // Determine if this Symbol is stored in a COMDAT 43 bool symbol_iscomdat3(Symbol* s) 44 { 45 version (MARS) 46 { 47 return s.Sclass == SCcomdat || 48 config.flags2 & CFG2comdat && s.Sclass == SCinline || 49 config.flags4 & CFG4allcomdat && s.Sclass == SCglobal; 50 } 51 else 52 { 53 return s.Sclass == SCcomdat || 54 config.flags2 & CFG2comdat && s.Sclass == SCinline || 55 config.flags4 & CFG4allcomdat && (s.Sclass == SCglobal || s.Sclass == SCstatic); 56 } 57 } 58 59 enum ALLOCA_LIMIT = 0x10000; 60 61 /********************************** 62 * The .pdata section is used on Win64 by the VS debugger and dbghelp to get information 63 * to walk the stack and unwind exceptions. 64 * Absent it, it is assumed to be a "leaf function" where [RSP] is the return address. 65 * Creates an instance of struct RUNTIME_FUNCTION: 66 * http://msdn.microsoft.com/en-US/library/ft9x1kdx(v=vs.80).aspx 67 * 68 * Input: 69 * sf function to generate unwind data for 70 */ 71 72 void win64_pdata(Symbol *sf) 73 { 74 // return; // doesn't work yet 75 76 //printf("win64_pdata()\n"); 77 assert(config.exe == EX_WIN64); 78 79 // Generate the pdata name, which is $pdata$funcname 80 size_t sflen = strlen(sf.Sident.ptr); 81 char *pdata_name = cast(char *)(sflen < ALLOCA_LIMIT ? alloca(7 + sflen + 1) : malloc(7 + sflen + 1)); 82 assert(pdata_name); 83 memcpy(pdata_name, "$pdata$".ptr, 7); 84 memcpy(pdata_name + 7, sf.Sident.ptr, sflen + 1); // include terminating 0 85 86 Symbol *spdata = symbol_name(pdata_name,SCstatic,tstypes[TYint]); 87 symbol_keep(spdata); 88 symbol_debug(spdata); 89 90 Symbol *sunwind = win64_unwind(sf); 91 92 /* 3 pointers are emitted: 93 * 1. pointer to start of function sf 94 * 2. pointer past end of function sf 95 * 3. pointer to unwind data 96 */ 97 98 auto dtb = DtBuilder(0); 99 dtb.xoff(sf,0,TYint); // Note the TYint, these are 32 bit fixups 100 dtb.xoff(sf,cast(uint)(retoffset + retsize),TYint); 101 dtb.xoff(sunwind,0,TYint); 102 spdata.Sdt = dtb.finish(); 103 104 spdata.Sseg = symbol_iscomdat3(sf) ? MsCoffObj_seg_pdata_comdat(sf) : MsCoffObj_seg_pdata(); 105 spdata.Salignment = 4; 106 outdata(spdata); 107 108 if (sflen >= ALLOCA_LIMIT) free(pdata_name); 109 } 110 111 /************************************************** 112 * Unwind data symbol goes in the .xdata section. 113 * Input: 114 * sf function to generate unwind data for 115 * Returns: 116 * generated symbol referring to unwind data 117 */ 118 119 Symbol *win64_unwind(Symbol *sf) 120 { 121 // Generate the unwind name, which is $unwind$funcname 122 size_t sflen = strlen(sf.Sident.ptr); 123 char *unwind_name = cast(char *)(sflen < ALLOCA_LIMIT ? alloca(8 + sflen + 1) : malloc(8 + sflen + 1)); 124 assert(unwind_name); 125 memcpy(unwind_name, "$unwind$".ptr, 8); 126 memcpy(unwind_name + 8, sf.Sident.ptr, sflen + 1); // include terminating 0 127 128 Symbol *sunwind = symbol_name(unwind_name,SCstatic,tstypes[TYint]); 129 symbol_keep(sunwind); 130 symbol_debug(sunwind); 131 132 sunwind.Sdt = unwind_data(); 133 sunwind.Sseg = symbol_iscomdat3(sf) ? MsCoffObj_seg_xdata_comdat(sf) : MsCoffObj_seg_xdata(); 134 sunwind.Salignment = 1; 135 outdata(sunwind); 136 137 if (sflen >= ALLOCA_LIMIT) free(unwind_name); 138 return sunwind; 139 } 140 141 /************************* Win64 Unwind Data ******************************************/ 142 143 /************************************************************************ 144 * Creates an instance of struct UNWIND_INFO: 145 * http://msdn.microsoft.com/en-US/library/ddssxxy8(v=vs.80).aspx 146 */ 147 148 enum UWOP 149 { // http://www.osronline.com/ddkx/kmarch/64bitamd_7btz.htm 150 // http://uninformed.org/index.cgi?v=4&a=1&p=17 151 PUSH_NONVOL, // push saved register, OpInfo is register 152 ALLOC_LARGE, // alloc large size on stack, OpInfo is 0 or 1 153 ALLOC_SMALL, // alloc small size on stack, OpInfo is size / 8 - 1 154 SET_FPREG, // set frame pointer 155 SAVE_NONVOL, // save register, OpInfo is reg, frame offset in next FrameOffset 156 SAVE_NONVOL_FAR, // save register, OpInfo is reg, frame offset in next 2 FrameOffsets 157 SAVE_XMM128, // save 64 bits of XMM reg, frame offset in next FrameOffset 158 SAVE_XMM128_FAR, // save 64 bits of XMM reg, frame offset in next 2 FrameOffsets 159 PUSH_MACHFRAME // push interrupt frame, OpInfo is 0 or 1 (pushes error code too) 160 } 161 162 union UNWIND_CODE 163 { 164 /+ 165 struct 166 { 167 ubyte CodeOffset; // offset of start of next instruction 168 ubyte UnwindOp : 4; // UWOP 169 ubyte OpInfo : 4; // extra information depending on UWOP 170 } op; 171 +/ 172 ushort FrameOffset; 173 } 174 175 ushort setUnwindCode(ubyte CodeOffset, ubyte UnwindOp, ubyte OpInfo) 176 { 177 return cast(ushort)(CodeOffset | (UnwindOp << 8) | (OpInfo << 12)); 178 } 179 180 enum 181 { 182 UNW_FLAG_EHANDLER = 1, // function has an exception handler 183 UNW_FLAG_UHANDLER = 2, // function has a termination handler 184 UNW_FLAG_CHAININFO = 4 // not the primary one for the function 185 } 186 187 struct UNWIND_INFO 188 { 189 ubyte Version; //: 3; // 1 190 //ubyte Flags : 5; // UNW_FLAG_xxxx 191 ubyte SizeOfProlog; // bytes in the function prolog 192 ubyte CountOfCodes; // dimension of UnwindCode[] 193 ubyte FrameRegister; //: 4; // if !=0, then frame pointer register 194 //ubyte FrameOffset : 4; // frame register offset from RSP divided by 16 195 UNWIND_CODE[6] UnwindCode; 196 static if (0) 197 { 198 UNWIND_CODE[((CountOfCodes + 1) & ~1) - 1] MoreUnwindCode; 199 union 200 { 201 // UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER 202 struct 203 { 204 uint ExceptionHandler; 205 void[n] Language_specific_handler_data; 206 } 207 208 // UNW_FLAG_CHAININFO 209 RUNTIME_FUNCTION chained_unwind_info; 210 } 211 } 212 } 213 214 215 216 dt_t *unwind_data() 217 { 218 UNWIND_INFO ui; 219 220 /* 4 allocation size strategy: 221 * 0: no unwind instruction 222 * 8..128: UWOP.ALLOC_SMALL 223 * 136..512K-8: UWOP.ALLOC_LARGE, OpInfo = 0 224 * 512K..4GB-8: UWOP.ALLOC_LARGE, OpInfo = 1 225 */ 226 targ_size_t sz = localsize; 227 assert((localsize & 7) == 0); 228 int strategy; 229 if (sz == 0) 230 strategy = 0; 231 else if (sz <= 128) 232 strategy = 1; 233 else if (sz <= 512 * 1024 - 8) 234 strategy = 2; 235 else 236 // 512KB to 4GB-8 237 strategy = 3; 238 239 ui.Version = 1; 240 //ui.Flags = 0; 241 ui.SizeOfProlog = cast(ubyte)startoffset; 242 static if (0) 243 { 244 ui.CountOfCodes = strategy + 1; 245 ui.FrameRegister = 0; 246 //ui.FrameOffset = 0; 247 } 248 else 249 { 250 strategy = 0; 251 ui.CountOfCodes = cast(ubyte)(strategy + 2); 252 ui.FrameRegister = BP; 253 //ui.FrameOffset = 0; //cod3_spoff() / 16; 254 } 255 256 static if (0) 257 { 258 switch (strategy) 259 { 260 case 0: 261 break; 262 263 case 1: 264 ui.UnwindCode[0].FrameOffset = setUnwindCode(prolog_allocoffset, UWOP.ALLOC_SMALL, (sz - 8) / 8); 265 break; 266 267 case 2: 268 ui.UnwindCode[0].FrameOffset = setUnwindCode(prolog_allocoffset, UWOP.ALLOC_LARGE, 0); 269 ui.UnwindCode[1].FrameOffset = (sz - 8) / 8; 270 break; 271 272 case 3: 273 ui.UnwindCode[0].FrameOffset = setUnwindCode(prolog_allocoffset, UWOP.ALLOC_LARGE, 1); 274 ui.UnwindCode[1].FrameOffset = sz & 0x0FFFF; 275 ui.UnwindCode[2].FrameOffset = sz / 0x10000; 276 break; 277 } 278 } 279 280 static if (1) 281 { 282 ui.UnwindCode[ui.CountOfCodes-2].FrameOffset = setUnwindCode(4, UWOP.SET_FPREG, 0); 283 } 284 285 ui.UnwindCode[ui.CountOfCodes-1].FrameOffset = setUnwindCode(1, UWOP.PUSH_NONVOL, BP); 286 287 auto dtb = DtBuilder(0); 288 dtb.nbytes(4 + ((ui.CountOfCodes + 1) & ~1) * 2,cast(char *)&ui); 289 return dtb.finish(); 290 } 291 292 } 293 }