Coverage Report

Created: 2025-02-15 06:15

/src/libunwind/src/dwarf/Gfde.c
Line
Count
Source (jump to first uncovered line)
1
/* libunwind - a platform-independent unwind library
2
   Copyright (c) 2003-2005 Hewlett-Packard Development Company, L.P.
3
        Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5
This file is part of libunwind.
6
7
Permission is hereby granted, free of charge, to any person obtaining
8
a copy of this software and associated documentation files (the
9
"Software"), to deal in the Software without restriction, including
10
without limitation the rights to use, copy, modify, merge, publish,
11
distribute, sublicense, and/or sell copies of the Software, and to
12
permit persons to whom the Software is furnished to do so, subject to
13
the following conditions:
14
15
The above copyright notice and this permission notice shall be
16
included in all copies or substantial portions of the Software.
17
18
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26
#include "dwarf_i.h"
27
28
static inline int
29
is_cie_id (unw_word_t val, int is_debug_frame)
30
15
{
31
  /* The CIE ID is normally 0xffffffff (for 32-bit ELF) or
32
     0xffffffffffffffff (for 64-bit ELF).  However, .eh_frame
33
     uses 0.  */
34
15
  if (is_debug_frame)
35
0
      return (val == (uint32_t)(-1) || val == (uint64_t)(-1));
36
15
  else
37
15
    return (val == 0);
38
15
}
39
40
/* Note: we don't need to keep track of more than the first four
41
   characters of the augmentation string, because we (a) ignore any
42
   augmentation string contents once we find an unrecognized character
43
   and (b) those characters that we do recognize, can't be
44
   repeated.  */
45
static inline int
46
parse_cie (unw_addr_space_t as, unw_accessors_t *a, unw_word_t addr,
47
           const unw_proc_info_t *pi, struct dwarf_cie_info *dci,
48
           int is_debug_frame, void *arg)
49
15
{
50
15
  uint8_t version, ch, augstr[5], fde_encoding, handler_encoding;
51
15
  uint8_t address_size, segment_size;
52
15
  unw_word_t len, cie_end_addr, aug_size;
53
15
  uint32_t u32val;
54
15
  uint64_t u64val;
55
15
  size_t i;
56
15
  int ret;
57
15
# define STR2(x)        #x
58
15
# define STR(x)         STR2(x)
59
60
  /* Pick appropriate default for FDE-encoding.  DWARF spec says
61
     start-IP (initial_location) and the code-size (address_range) are
62
     "address-unit sized constants".  The `R' augmentation can be used
63
     to override this, but by default, we pick an address-sized unit
64
     for fde_encoding.  */
65
15
  switch (dwarf_addr_size (as))
66
15
    {
67
0
    case 4:     fde_encoding = DW_EH_PE_udata4; break;
68
15
    case 8:     fde_encoding = DW_EH_PE_udata8; break;
69
0
    default:    fde_encoding = DW_EH_PE_omit; break;
70
15
    }
71
72
15
  dci->lsda_encoding = DW_EH_PE_omit;
73
15
  dci->handler = 0;
74
75
15
  if ((ret = dwarf_readu32 (as, a, &addr, &u32val, arg)) < 0)
76
0
    return ret;
77
78
15
  if (u32val != 0xffffffff)
79
15
    {
80
      /* the CIE is in the 32-bit DWARF format */
81
15
      uint32_t cie_id;
82
      /* DWARF says CIE id should be 0xffffffff, but in .eh_frame, it's 0 */
83
15
      const uint32_t expected_id = (is_debug_frame) ? 0xffffffff : 0;
84
85
15
      len = u32val;
86
15
      cie_end_addr = addr + len;
87
15
      if ((ret = dwarf_readu32 (as, a, &addr, &cie_id, arg)) < 0)
88
0
        return ret;
89
15
      if (cie_id != expected_id)
90
0
        {
91
0
          Debug (1, "Unexpected CIE id %x\n", cie_id);
92
0
          return -UNW_EINVAL;
93
0
        }
94
15
    }
95
0
  else
96
0
    {
97
      /* the CIE is in the 64-bit DWARF format */
98
0
      uint64_t cie_id;
99
      /* DWARF says CIE id should be 0xffffffffffffffff, but in
100
         .eh_frame, it's 0 */
101
0
      const uint64_t expected_id = (is_debug_frame) ? 0xffffffffffffffffull : 0;
102
103
0
      if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
104
0
        return ret;
105
0
      len = (unw_word_t) u64val;
106
0
      cie_end_addr = addr + len;
107
0
      if ((ret = dwarf_readu64 (as, a, &addr, &cie_id, arg)) < 0)
108
0
        return ret;
109
0
      if (cie_id != expected_id)
110
0
        {
111
0
          Debug (1, "Unexpected CIE id %llx\n", (long long) cie_id);
112
0
          return -UNW_EINVAL;
113
0
        }
114
0
    }
115
15
  dci->cie_instr_end = cie_end_addr;
116
117
15
  if ((ret = dwarf_readu8 (as, a, &addr, &version, arg)) < 0)
118
0
    return ret;
119
120
  /* GCC emits version 1??? */
121
15
  if (version != 1 && (version < DWARF_CIE_VERSION || version > DWARF_CIE_VERSION_MAX))
122
0
    {
123
0
      Debug (1, "Got CIE version %u, expected version 1 or between "
124
0
             STR (DWARF_CIE_VERSION) " and " STR (DWARF_CIE_VERSION_MAX) "\n", version);
125
0
      return -UNW_EBADVERSION;
126
0
    }
127
128
  /* read and parse the augmentation string: */
129
15
  memset (augstr, 0, sizeof (augstr));
130
15
  for (i = 0;;)
131
45
    {
132
45
      if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)
133
0
        return ret;
134
135
45
      if (!ch)
136
15
        break;  /* end of augmentation string */
137
138
30
      if (i < sizeof (augstr) - 1)
139
30
        augstr[i++] = ch;
140
30
    }
141
142
15
  if (version > 3)
143
0
    {
144
0
      if((ret = dwarf_readu8(as, a, &addr, &address_size, arg)) < 0)
145
0
  return ret;
146
147
0
      if((ret = dwarf_readu8(as, a, &addr, &segment_size, arg)) < 0)
148
0
  return ret;
149
0
    }
150
151
15
  if ((ret = dwarf_read_uleb128 (as, a, &addr, &dci->code_align, arg)) < 0
152
15
      || (ret = dwarf_read_sleb128 (as, a, &addr, &dci->data_align, arg)) < 0)
153
0
    return ret;
154
155
  /* Read the return-address column either as a u8 or as a uleb128.  */
156
15
  if (version == 1)
157
15
    {
158
15
      if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)
159
0
        return ret;
160
15
      dci->ret_addr_column = ch;
161
15
    }
162
0
  else if ((ret = dwarf_read_uleb128 (as, a, &addr, &dci->ret_addr_column,
163
0
                                      arg)) < 0)
164
0
    return ret;
165
166
15
  i = 0;
167
15
  if (augstr[0] == 'z')
168
15
    {
169
15
      dci->sized_augmentation = 1;
170
15
      if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)
171
0
        return ret;
172
15
      i++;
173
15
    }
174
175
30
  for (; i < sizeof (augstr) && augstr[i]; ++i)
176
15
    switch (augstr[i])
177
15
      {
178
0
      case 'L':
179
        /* read the LSDA pointer-encoding format.  */
180
0
        if ((ret = dwarf_readu8 (as, a, &addr, &ch, arg)) < 0)
181
0
          return ret;
182
0
        dci->lsda_encoding = ch;
183
0
        break;
184
185
15
      case 'R':
186
        /* read the FDE pointer-encoding format.  */
187
15
        if ((ret = dwarf_readu8 (as, a, &addr, &fde_encoding, arg)) < 0)
188
0
          return ret;
189
15
        break;
190
191
15
      case 'P':
192
        /* read the personality-routine pointer-encoding format.  */
193
0
        if ((ret = dwarf_readu8 (as, a, &addr, &handler_encoding, arg)) < 0)
194
0
          return ret;
195
0
        if ((ret = dwarf_read_encoded_pointer (as, a, &addr, handler_encoding,
196
0
                                               pi, &dci->handler, arg)) < 0)
197
0
          return ret;
198
0
        break;
199
200
0
      case 'S':
201
        /* This is a signal frame. */
202
0
        dci->signal_frame = 1;
203
204
        /* Temporarily set it to one so dwarf_parse_fde() knows that
205
           it should fetch the actual ABI/TAG pair from the FDE.  */
206
0
        dci->have_abi_marker = 1;
207
0
        break;
208
209
0
      default:
210
0
        Debug (1, "Unexpected augmentation string `%s'\n", augstr);
211
0
        if (dci->sized_augmentation)
212
          /* If we have the size of the augmentation body, we can skip
213
             over the parts that we don't understand, so we're OK. */
214
0
          goto done;
215
0
        else
216
0
          return -UNW_EINVAL;
217
15
      }
218
15
 done:
219
15
  dci->fde_encoding = fde_encoding;
220
15
  dci->cie_instr_start = addr;
221
15
  Debug (15, "CIE parsed OK, augmentation = \"%s\", handler=0x%lx\n",
222
15
         augstr, (long) dci->handler);
223
15
  return 0;
224
15
}
225
226
/* Extract proc-info from the FDE starting at address ADDR.
227
228
   Pass BASE as zero for eh_frame behaviour, or a pointer to
229
   debug_frame base for debug_frame behaviour.  */
230
231
HIDDEN int
232
dwarf_extract_proc_info_from_fde (unw_addr_space_t as, unw_accessors_t *a,
233
                                  unw_word_t *addrp, unw_proc_info_t *pi,
234
                                  unw_word_t base,
235
                                  int need_unwind_info, int is_debug_frame,
236
                                  void *arg)
237
15
{
238
15
  unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0;
239
15
  unw_word_t start_ip, ip_range, aug_size, addr = *addrp;
240
15
  int ret;
241
15
  uint8_t ip_range_encoding;
242
15
  struct dwarf_cie_info dci;
243
15
  uint64_t u64val;
244
15
  uint32_t u32val;
245
246
15
  Debug (12, "FDE @ 0x%lx\n", (long) addr);
247
248
15
  memset (&dci, 0, sizeof (dci));
249
250
15
  if ((ret = dwarf_readu32 (as, a, &addr, &u32val, arg)) < 0)
251
0
    return ret;
252
253
15
  if (u32val != 0xffffffff)
254
15
    {
255
15
      int32_t cie_offset = 0;
256
257
      /* In some configurations, an FDE with a 0 length indicates the
258
         end of the FDE-table.  */
259
15
      if (u32val == 0)
260
0
        return -UNW_ENOINFO;
261
262
      /* the FDE is in the 32-bit DWARF format */
263
264
15
      *addrp = fde_end_addr = addr + u32val;
265
15
      cie_offset_addr = addr;
266
267
15
      if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
268
0
        return ret;
269
270
15
      if (is_cie_id (cie_offset, is_debug_frame))
271
        /* ignore CIEs (happens during linear searches) */
272
0
        return 0;
273
274
15
      if (is_debug_frame)
275
0
        cie_addr = base + cie_offset;
276
15
      else
277
        /* DWARF says that the CIE_pointer in the FDE is a
278
           .debug_frame-relative offset, but the GCC-generated .eh_frame
279
           sections instead store a "pcrelative" offset, which is just
280
           as fine as it's self-contained.  */
281
15
        cie_addr = cie_offset_addr - cie_offset;
282
15
    }
283
0
  else
284
0
    {
285
0
      int64_t cie_offset = 0;
286
287
      /* the FDE is in the 64-bit DWARF format */
288
289
0
      if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
290
0
        return ret;
291
292
0
      *addrp = fde_end_addr = (unw_word_t) (addr + u64val);
293
0
      cie_offset_addr = addr;
294
295
0
      if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
296
0
        return ret;
297
298
0
      if (is_cie_id ((unw_word_t) cie_offset, is_debug_frame))
299
        /* ignore CIEs (happens during linear searches) */
300
0
        return 0;
301
302
0
      if (is_debug_frame)
303
0
        cie_addr = (unw_word_t) (base + cie_offset);
304
0
      else
305
        /* DWARF says that the CIE_pointer in the FDE is a
306
           .debug_frame-relative offset, but the GCC-generated .eh_frame
307
           sections instead store a "pcrelative" offset, which is just
308
           as fine as it's self-contained.  */
309
0
        cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset);
310
0
    }
311
312
15
  Debug (15, "looking for CIE at address %lx\n", (long) cie_addr);
313
314
15
  if ((ret = parse_cie (as, a, cie_addr, pi, &dci, is_debug_frame, arg)) < 0)
315
0
    return ret;
316
317
  /* IP-range has same encoding as FDE pointers, except that it's
318
     always an absolute value: */
319
15
  ip_range_encoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK;
320
321
15
  if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.fde_encoding,
322
15
                                         pi, &start_ip, arg)) < 0
323
15
      || (ret = dwarf_read_encoded_pointer (as, a, &addr, ip_range_encoding,
324
15
                                            pi, &ip_range, arg)) < 0)
325
0
    return ret;
326
15
  pi->start_ip = start_ip;
327
15
  pi->end_ip = start_ip + ip_range;
328
15
  pi->handler = dci.handler;
329
330
15
  if (dci.sized_augmentation)
331
15
    {
332
15
      if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)
333
0
        return ret;
334
15
      aug_end_addr = addr + aug_size;
335
15
    }
336
337
15
  if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.lsda_encoding,
338
15
                                         pi, &pi->lsda, arg)) < 0)
339
0
    return ret;
340
341
15
  Debug (15, "FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n",
342
15
         (long) pi->start_ip, (long) pi->end_ip, (long) pi->lsda);
343
344
15
  if (need_unwind_info)
345
15
    {
346
15
      pi->format = UNW_INFO_FORMAT_TABLE;
347
15
      pi->unwind_info_size = sizeof (dci);
348
15
      pi->unwind_info = mempool_alloc (&dwarf_cie_info_pool);
349
15
      if (!pi->unwind_info)
350
0
        return -UNW_ENOMEM;
351
352
15
      if (dci.have_abi_marker)
353
0
        {
354
0
          if ((ret = dwarf_readu16 (as, a, &addr, &dci.abi, arg)) < 0
355
0
              || (ret = dwarf_readu16 (as, a, &addr, &dci.tag, arg)) < 0)
356
0
            return ret;
357
0
          Debug (13, "Found ABI marker = (abi=%u, tag=%u)\n",
358
0
                 dci.abi, dci.tag);
359
0
        }
360
361
15
      if (dci.sized_augmentation)
362
15
        dci.fde_instr_start = aug_end_addr;
363
0
      else
364
0
        dci.fde_instr_start = addr;
365
15
      dci.fde_instr_end = fde_end_addr;
366
367
15
      memcpy (pi->unwind_info, &dci, sizeof (dci));
368
15
    }
369
15
  return 0;
370
15
}