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 == - (unw_word_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, &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      if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
256        return ret;
257
258      if (is_cie_id (cie_offset, is_debug_frame))
259        /* ignore CIEs (happens during linear searches) */
260        return 0;
261
262      if (is_debug_frame)
263        cie_addr = base + cie_offset;
264      else
265        /* DWARF says that the CIE_pointer in the FDE is a
266           .debug_frame-relative offset, but the GCC-generated .eh_frame
267           sections instead store a "pcrelative" offset, which is just
268           as fine as it's self-contained.  */
269        cie_addr = cie_offset_addr - cie_offset;
270    }
271  else
272    {
273      int64_t cie_offset;
274
275      /* the FDE is in the 64-bit DWARF format */
276
277      if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
278        return ret;
279
280      *addrp = fde_end_addr = addr + u64val;
281      cie_offset_addr = addr;
282
283      if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
284        return ret;
285
286      if (is_cie_id (cie_offset, is_debug_frame))
287        /* ignore CIEs (happens during linear searches) */
288        return 0;
289
290      if (is_debug_frame)
291        cie_addr = base + cie_offset;
292      else
293        /* DWARF says that the CIE_pointer in the FDE is a
294           .debug_frame-relative offset, but the GCC-generated .eh_frame
295           sections instead store a "pcrelative" offset, which is just
296           as fine as it's self-contained.  */
297        cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset);
298    }
299
300  Debug (15, "looking for CIE at address %lx\n", (long) cie_addr);
301
302  if ((ret = parse_cie (as, a, cie_addr, pi, &dci, is_debug_frame, arg)) < 0)
303    return ret;
304
305  /* IP-range has same encoding as FDE pointers, except that it's
306     always an absolute value: */
307  ip_range_encoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK;
308
309  if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.fde_encoding,
310                                         pi, &start_ip, arg)) < 0
311      || (ret = dwarf_read_encoded_pointer (as, a, &addr, ip_range_encoding,
312                                            pi, &ip_range, arg)) < 0)
313    return ret;
314  pi->start_ip = start_ip;
315  pi->end_ip = start_ip + ip_range;
316  pi->handler = dci.handler;
317
318  if (dci.sized_augmentation)
319    {
320      if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)
321        return ret;
322      aug_end_addr = addr + aug_size;
323    }
324
325  if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.lsda_encoding,
326                                         pi, &pi->lsda, arg)) < 0)
327    return ret;
328
329  Debug (15, "FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n",
330         (long) pi->start_ip, (long) pi->end_ip, (long) pi->lsda);
331
332  if (need_unwind_info)
333    {
334      pi->format = UNW_INFO_FORMAT_TABLE;
335      pi->unwind_info_size = sizeof (dci);
336      pi->unwind_info = mempool_alloc (&dwarf_cie_info_pool);
337      if (!pi->unwind_info)
338        return -UNW_ENOMEM;
339
340      if (dci.have_abi_marker)
341        {
342          if ((ret = dwarf_readu16 (as, a, &addr, &dci.abi, arg)) < 0
343              || (ret = dwarf_readu16 (as, a, &addr, &dci.tag, arg)) < 0)
344            return ret;
345          Debug (13, "Found ABI marker = (abi=%u, tag=%u)\n",
346                 dci.abi, dci.tag);
347        }
348
349      if (dci.sized_augmentation)
350        dci.fde_instr_start = aug_end_addr;
351      else
352        dci.fde_instr_start = addr;
353      dci.fde_instr_end = fde_end_addr;
354
355      memcpy (pi->unwind_info, &dci, sizeof (dci));
356    }
357  return 0;
358}
359