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 }