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 }