Imported Upstream version 1.2
[platform/upstream/libunwind.git] / src / dwarf / Gfde.c
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 {
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.  */
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 {
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
220 HIDDEN int
221 dwarf_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 }