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
26module gcc.unwind.pe;
27
28import gcc.unwind;
29import gcc.builtins;
30
31@nogc:
32
33// Pointer encodings, from dwarf2.h.
34enum
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.
61uint 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.
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.
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.
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.
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.
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