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