xref: /netbsd-src/external/gpl3/gcc.old/dist/libphobos/libdruntime/gcc/unwind/pe.d (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
1 // Exception handling and frame unwind runtime interface routines.
2 // Copyright (C) 2011-2020 Free Software Foundation, Inc.
3 
4 // GCC is free software; you can redistribute it and/or modify it under
5 // the terms of the GNU General Public License as published by the Free
6 // Software Foundation; either version 3, or (at your option) any later
7 // version.
8 
9 // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
10 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 // for more details.
13 
14 // Under Section 7 of GPL version 3, you are granted additional
15 // permissions described in the GCC Runtime Library Exception, version
16 // 3.1, as published by the Free Software Foundation.
17 
18 // You should have received a copy of the GNU General Public License and
19 // a copy of the GCC Runtime Library Exception along with this program;
20 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
21 // <http://www.gnu.org/licenses/>.
22 
23 // extern(C) interface for the GNU/GCC pointer encoding library.
24 // This corresponds to unwind-pe.h
25 
26 module gcc.unwind.pe;
27 
28 import gcc.unwind;
29 import gcc.builtins;
30 
31 @nogc:
32 
33 // Pointer encodings, from dwarf2.h.
34 enum
35 {
36     DW_EH_PE_absptr   = 0x00,
37     DW_EH_PE_omit     = 0xff,
38 
39     DW_EH_PE_uleb128  = 0x01,
40     DW_EH_PE_udata2   = 0x02,
41     DW_EH_PE_udata4   = 0x03,
42     DW_EH_PE_udata8   = 0x04,
43     DW_EH_PE_sleb128  = 0x09,
44     DW_EH_PE_sdata2   = 0x0A,
45     DW_EH_PE_sdata4   = 0x0B,
46     DW_EH_PE_sdata8   = 0x0C,
47     DW_EH_PE_signed   = 0x08,
48 
49     DW_EH_PE_pcrel    = 0x10,
50     DW_EH_PE_textrel  = 0x20,
51     DW_EH_PE_datarel  = 0x30,
52     DW_EH_PE_funcrel  = 0x40,
53     DW_EH_PE_aligned  = 0x50,
54 
55     DW_EH_PE_indirect = 0x80
56 }
57 
58 // Given an encoding, return the number of bytes the format occupies.
59 // This is only defined for fixed-size encodings, and so does not
60 // include leb128.
size_of_encoded_value(ubyte encoding)61 uint size_of_encoded_value(ubyte encoding)
62 {
63     if (encoding == DW_EH_PE_omit)
64         return 0;
65 
66     final switch (encoding & 0x07)
67     {
68         case DW_EH_PE_absptr:
69             return (void*).sizeof;
70         case DW_EH_PE_udata2:
71             return 2;
72         case DW_EH_PE_udata4:
73             return 4;
74         case DW_EH_PE_udata8:
75             return 8;
76     }
77     assert(0);
78 }
79 
80 // Given an encoding and an _Unwind_Context, return the base to which
81 // the encoding is relative.  This base may then be passed to
82 // read_encoded_value_with_base for use when the _Unwind_Context is
83 // not available.
base_of_encoded_value(ubyte encoding,_Unwind_Context * context)84 _Unwind_Ptr base_of_encoded_value(ubyte encoding, _Unwind_Context* context)
85 {
86     if (encoding == DW_EH_PE_omit)
87         return cast(_Unwind_Ptr) 0;
88 
89     final switch (encoding & 0x70)
90     {
91         case DW_EH_PE_absptr:
92         case DW_EH_PE_pcrel:
93         case DW_EH_PE_aligned:
94             return cast(_Unwind_Ptr) 0;
95 
96         case DW_EH_PE_textrel:
97             return _Unwind_GetTextRelBase(context);
98         case DW_EH_PE_datarel:
99             return _Unwind_GetDataRelBase(context);
100         case DW_EH_PE_funcrel:
101             return _Unwind_GetRegionStart(context);
102     }
103     assert(0);
104 }
105 
106 // Read an unsigned leb128 value from P, *P is incremented past the value.
107 // We assume that a word is large enough to hold any value so encoded;
108 // if it is smaller than a pointer on some target, pointers should not be
109 // leb128 encoded on that target.
read_uleb128(const (ubyte)** p)110 _uleb128_t read_uleb128(const(ubyte)** p)
111 {
112     auto q = *p;
113     _uleb128_t result = 0;
114     uint shift = 0;
115 
116     while (1)
117     {
118         ubyte b = *q++;
119         result |= cast(_uleb128_t)(b & 0x7F) << shift;
120         if ((b & 0x80) == 0)
121             break;
122         shift += 7;
123     }
124 
125     *p = q;
126     return result;
127 }
128 
129 // Similar, but read a signed leb128 value.
read_sleb128(const (ubyte)** p)130 _sleb128_t read_sleb128(const(ubyte)** p)
131 {
132     auto q = *p;
133     _sleb128_t result = 0;
134     uint shift = 0;
135     ubyte b = void;
136 
137     while (1)
138     {
139         b = *q++;
140         result |= cast(_sleb128_t)(b & 0x7F) << shift;
141         shift += 7;
142         if ((b & 0x80) == 0)
143             break;
144     }
145 
146     // Sign-extend a negative value.
147     if (shift < result.sizeof * 8 && (b & 0x40))
148         result |= -(cast(_sleb128_t)1 << shift);
149 
150     *p = q;
151     return result;
152 }
153 
154 // Load an encoded value from memory at P.  The value is returned in VAL;
155 // The function returns P incremented past the value.  BASE is as given
156 // by base_of_encoded_value for this encoding in the appropriate context.
read_encoded_value_with_base(ubyte encoding,_Unwind_Ptr base,const (ubyte)** p)157 _Unwind_Ptr read_encoded_value_with_base(ubyte encoding, _Unwind_Ptr base,
158                                          const(ubyte)** p)
159 {
160     auto q = *p;
161     _Unwind_Internal_Ptr result;
162 
163     if (encoding == DW_EH_PE_aligned)
164     {
165         _Unwind_Internal_Ptr a = cast(_Unwind_Internal_Ptr)q;
166         a = cast(_Unwind_Internal_Ptr)((a + (void*).sizeof - 1) & - (void*).sizeof);
167         result = *cast(_Unwind_Internal_Ptr*)a;
168         q = cast(ubyte*) cast(_Unwind_Internal_Ptr)(a + (void*).sizeof);
169     }
170     else
171     {
172         switch (encoding & 0x0f)
173         {
174             case DW_EH_PE_uleb128:
175                 result = cast(_Unwind_Internal_Ptr)read_uleb128(&q);
176                 break;
177 
178             case DW_EH_PE_sleb128:
179                 result = cast(_Unwind_Internal_Ptr)read_sleb128(&q);
180                 break;
181 
182             case DW_EH_PE_udata2:
183                 result = cast(_Unwind_Internal_Ptr) *cast(ushort*)q;
184                 q += 2;
185                 break;
186             case DW_EH_PE_udata4:
187                 result = cast(_Unwind_Internal_Ptr) *cast(uint*)q;
188                 q += 4;
189                 break;
190             case DW_EH_PE_udata8:
191                 result = cast(_Unwind_Internal_Ptr) *cast(ulong*)q;
192                 q += 8;
193                 break;
194 
195             case DW_EH_PE_sdata2:
196                 result = cast(_Unwind_Internal_Ptr) *cast(short*)q;
197                 q += 2;
198                 break;
199             case DW_EH_PE_sdata4:
200                 result = cast(_Unwind_Internal_Ptr) *cast(int*)q;
201                 q += 4;
202                 break;
203             case DW_EH_PE_sdata8:
204                 result = cast(_Unwind_Internal_Ptr) *cast(long*)q;
205                 q += 8;
206                 break;
207 
208             case DW_EH_PE_absptr:
209                 if (size_t.sizeof == 8)
210                     goto case DW_EH_PE_udata8;
211                 else
212                     goto case DW_EH_PE_udata4;
213 
214             default:
215                 __builtin_abort();
216         }
217 
218         if (result != 0)
219         {
220             result += ((encoding & 0x70) == DW_EH_PE_pcrel
221                        ? cast(_Unwind_Internal_Ptr)*p : base);
222             if (encoding & DW_EH_PE_indirect)
223                 result = *cast(_Unwind_Internal_Ptr*)result;
224         }
225     }
226 
227     *p = q;
228     return result;
229 }
230 
231 // Like read_encoded_value_with_base, but get the base from the context
232 // rather than providing it directly.
read_encoded_value(_Unwind_Context * context,ubyte encoding,const (ubyte)** p)233 _Unwind_Ptr read_encoded_value(_Unwind_Context* context, ubyte encoding,
234                                const(ubyte)** p)
235 {
236     auto base = base_of_encoded_value(encoding, context);
237     return read_encoded_value_with_base(encoding, base, p);
238 }
239