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