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 }