Initial import
[external/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            unw_word_t base, 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 = (base) ? 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 = (base) ? 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                                   int need_unwind_info, unw_word_t base,
224                                   void *arg)
225 {
226   unw_word_t fde_end_addr, cie_addr, cie_offset_addr, aug_end_addr = 0;
227   unw_word_t start_ip, ip_range, aug_size, addr = *addrp;
228   int ret, ip_range_encoding;
229   struct dwarf_cie_info dci;
230   uint64_t u64val;
231   uint32_t u32val;
232
233   Debug (12, "FDE @ 0x%lx\n", (long) addr);
234
235   memset (&dci, 0, sizeof (dci));
236
237   if ((ret = dwarf_readu32 (as, a, &addr, &u32val, arg)) < 0)
238     return ret;
239
240   if (u32val != 0xffffffff)
241     {
242       int32_t cie_offset;
243
244       /* In some configurations, an FDE with a 0 length indicates the
245          end of the FDE-table.  */
246       if (u32val == 0)
247         return -UNW_ENOINFO;
248
249       /* the FDE is in the 32-bit DWARF format */
250
251       *addrp = fde_end_addr = addr + u32val;
252       cie_offset_addr = addr;
253
254       if ((ret = dwarf_reads32 (as, a, &addr, &cie_offset, arg)) < 0)
255         return ret;
256
257       if (is_cie_id (cie_offset, base != 0))
258         /* ignore CIEs (happens during linear searches) */
259         return 0;
260
261       if (base != 0)
262         cie_addr = base + cie_offset;
263       else
264         /* DWARF says that the CIE_pointer in the FDE is a
265            .debug_frame-relative offset, but the GCC-generated .eh_frame
266            sections instead store a "pcrelative" offset, which is just
267            as fine as it's self-contained.  */
268         cie_addr = cie_offset_addr - cie_offset;
269     }
270   else
271     {
272       int64_t cie_offset;
273
274       /* the FDE is in the 64-bit DWARF format */
275
276       if ((ret = dwarf_readu64 (as, a, &addr, &u64val, arg)) < 0)
277         return ret;
278
279       *addrp = fde_end_addr = addr + u64val;
280       cie_offset_addr = addr;
281
282       if ((ret = dwarf_reads64 (as, a, &addr, &cie_offset, arg)) < 0)
283         return ret;
284
285       if (is_cie_id (cie_offset, base != 0))
286         /* ignore CIEs (happens during linear searches) */
287         return 0;
288
289       if (base != 0)
290         cie_addr = base + cie_offset;
291       else
292         /* DWARF says that the CIE_pointer in the FDE is a
293            .debug_frame-relative offset, but the GCC-generated .eh_frame
294            sections instead store a "pcrelative" offset, which is just
295            as fine as it's self-contained.  */
296         cie_addr = (unw_word_t) ((uint64_t) cie_offset_addr - cie_offset);
297     }
298
299   Debug (15, "looking for CIE at address %lx\n", (long) cie_addr);
300
301   if ((ret = parse_cie (as, a, cie_addr, pi, &dci, base, arg)) < 0)
302     return ret;
303
304   /* IP-range has same encoding as FDE pointers, except that it's
305      always an absolute value: */
306   ip_range_encoding = dci.fde_encoding & DW_EH_PE_FORMAT_MASK;
307
308   if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.fde_encoding,
309                                          pi, &start_ip, arg)) < 0
310       || (ret = dwarf_read_encoded_pointer (as, a, &addr, ip_range_encoding,
311                                             pi, &ip_range, arg)) < 0)
312     return ret;
313   pi->start_ip = start_ip;
314   pi->end_ip = start_ip + ip_range;
315   pi->handler = dci.handler;
316
317   if (dci.sized_augmentation)
318     {
319       if ((ret = dwarf_read_uleb128 (as, a, &addr, &aug_size, arg)) < 0)
320         return ret;
321       aug_end_addr = addr + aug_size;
322     }
323
324   if ((ret = dwarf_read_encoded_pointer (as, a, &addr, dci.lsda_encoding,
325                                          pi, &pi->lsda, arg)) < 0)
326     return ret;
327
328   Debug (15, "FDE covers IP 0x%lx-0x%lx, LSDA=0x%lx\n",
329          (long) pi->start_ip, (long) pi->end_ip, (long) pi->lsda);
330
331   if (need_unwind_info)
332     {
333       pi->format = UNW_INFO_FORMAT_TABLE;
334       pi->unwind_info_size = sizeof (dci);
335       pi->unwind_info = mempool_alloc (&dwarf_cie_info_pool);
336       if (!pi->unwind_info)
337         return -UNW_ENOMEM;
338
339       if (dci.have_abi_marker)
340         {
341           if ((ret = dwarf_readu16 (as, a, &addr, &dci.abi, arg)) < 0
342               || (ret = dwarf_readu16 (as, a, &addr, &dci.tag, arg)) < 0)
343             return ret;
344           Debug (13, "Found ABI marker = (abi=%u, tag=%u)\n",
345                  dci.abi, dci.tag);
346         }
347
348       if (dci.sized_augmentation)
349         dci.fde_instr_start = aug_end_addr;
350       else
351         dci.fde_instr_start = addr;
352       dci.fde_instr_end = fde_end_addr;
353
354       memcpy (pi->unwind_info, &dci, sizeof (dci));
355     }
356   return 0;
357 }