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