Imported Upstream version 1.3.1
[platform/upstream/libunwind.git] / src / dwarf / Gfind_unwind_table.c
1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2003-2004 Hewlett-Packard Co
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 <fcntl.h>
27 #include <string.h>
28 #include <unistd.h>
29
30 #include <sys/mman.h>
31
32 #include "libunwind_i.h"
33 #include "dwarf-eh.h"
34 #include "dwarf_i.h"
35
36 #define to_unw_word(p) ((unw_word_t) (uintptr_t) (p))
37
38 int
39 dwarf_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as,
40                          char *path, unw_word_t segbase, unw_word_t mapoff,
41                          unw_word_t ip)
42 {
43   Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
44   unw_word_t addr, eh_frame_start, fde_count, load_base;
45   unw_word_t max_load_addr = 0;
46   unw_word_t start_ip = to_unw_word (-1);
47   unw_word_t end_ip = 0;
48   struct dwarf_eh_frame_hdr *hdr;
49   unw_proc_info_t pi;
50   unw_accessors_t *a;
51   Elf_W(Ehdr) *ehdr;
52 #if UNW_TARGET_ARM
53   const Elf_W(Phdr) *parm_exidx = NULL;
54 #endif
55   int i, ret, found = 0;
56
57   /* XXX: Much of this code is Linux/LSB-specific.  */
58
59   if (!elf_w(valid_object) (&edi->ei))
60     return -UNW_ENOINFO;
61
62   ehdr = edi->ei.image;
63   phdr = (Elf_W(Phdr) *) ((char *) edi->ei.image + ehdr->e_phoff);
64
65   for (i = 0; i < ehdr->e_phnum; ++i)
66     {
67       switch (phdr[i].p_type)
68         {
69         case PT_LOAD:
70           if (phdr[i].p_vaddr < start_ip)
71             start_ip = phdr[i].p_vaddr;
72
73           if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
74             end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
75
76           if (phdr[i].p_offset == mapoff)
77             ptxt = phdr + i;
78           if ((uintptr_t) edi->ei.image + phdr->p_filesz > max_load_addr)
79             max_load_addr = (uintptr_t) edi->ei.image + phdr->p_filesz;
80           break;
81
82         case PT_GNU_EH_FRAME:
83           peh_hdr = phdr + i;
84           break;
85
86         case PT_DYNAMIC:
87           pdyn = phdr + i;
88           break;
89
90 #if UNW_TARGET_ARM
91         case PT_ARM_EXIDX:
92           parm_exidx = phdr + i;
93           break;
94 #endif
95
96         default:
97           break;
98         }
99     }
100
101   if (!ptxt)
102     return 0;
103
104   load_base = segbase - ptxt->p_vaddr;
105   start_ip += load_base;
106   end_ip += load_base;
107
108   if (peh_hdr)
109     {
110       if (pdyn)
111         {
112           /* For dynamicly linked executables and shared libraries,
113              DT_PLTGOT is the value that data-relative addresses are
114              relative to for that object.  We call this the "gp".  */
115                 Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
116                                                  + (char *) edi->ei.image);
117           for (; dyn->d_tag != DT_NULL; ++dyn)
118             if (dyn->d_tag == DT_PLTGOT)
119               {
120                 /* Assume that _DYNAMIC is writable and GLIBC has
121                    relocated it (true for x86 at least).  */
122                 edi->di_cache.gp = dyn->d_un.d_ptr;
123                 break;
124               }
125         }
126       else
127         /* Otherwise this is a static executable with no _DYNAMIC.  Assume
128            that data-relative addresses are relative to 0, i.e.,
129            absolute.  */
130         edi->di_cache.gp = 0;
131
132       hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
133                                            + (char *) edi->ei.image);
134       if (hdr->version != DW_EH_VERSION)
135         {
136           Debug (1, "table `%s' has unexpected version %d\n",
137                  path, hdr->version);
138           return -UNW_ENOINFO;
139         }
140
141       a = unw_get_accessors_int (unw_local_addr_space);
142       addr = to_unw_word (&hdr->eh_frame);
143
144       /* Fill in a dummy proc_info structure.  We just need to fill in
145          enough to ensure that dwarf_read_encoded_pointer() can do it's
146          job.  Since we don't have a procedure-context at this point, all
147          we have to do is fill in the global-pointer.  */
148       memset (&pi, 0, sizeof (pi));
149       pi.gp = edi->di_cache.gp;
150
151       /* (Optionally) read eh_frame_ptr: */
152       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
153                                              &addr, hdr->eh_frame_ptr_enc, &pi,
154                                              &eh_frame_start, NULL)) < 0)
155         return -UNW_ENOINFO;
156
157       /* (Optionally) read fde_count: */
158       if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
159                                              &addr, hdr->fde_count_enc, &pi,
160                                              &fde_count, NULL)) < 0)
161         return -UNW_ENOINFO;
162
163       if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
164         {
165     #if 1
166           abort ();
167     #else
168           unw_word_t eh_frame_end;
169
170           /* If there is no search table or it has an unsupported
171              encoding, fall back on linear search.  */
172           if (hdr->table_enc == DW_EH_PE_omit)
173             Debug (4, "EH lacks search table; doing linear search\n");
174           else
175             Debug (4, "EH table has encoding 0x%x; doing linear search\n",
176                    hdr->table_enc);
177
178           eh_frame_end = max_load_addr; /* XXX can we do better? */
179
180           if (hdr->fde_count_enc == DW_EH_PE_omit)
181             fde_count = ~0UL;
182           if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
183             abort ();
184
185           return linear_search (unw_local_addr_space, ip,
186                                 eh_frame_start, eh_frame_end, fde_count,
187                                 pi, need_unwind_info, NULL);
188     #endif
189         }
190
191       edi->di_cache.start_ip = start_ip;
192       edi->di_cache.end_ip = end_ip;
193       edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
194       edi->di_cache.u.rti.name_ptr = 0;
195       /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
196       edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
197       edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
198                                        + (addr - to_unw_word (edi->ei.image)
199                                           - peh_hdr->p_offset));
200
201       /* For the binary-search table in the eh_frame_hdr, data-relative
202          means relative to the start of that section... */
203       edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
204                                     + (to_unw_word (hdr) -
205                                        to_unw_word (edi->ei.image)
206                                        - peh_hdr->p_offset));
207       found = 1;
208     }
209
210 #if UNW_TARGET_ARM
211   if (parm_exidx)
212     {
213       edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
214       edi->di_arm.start_ip = start_ip;
215       edi->di_arm.end_ip = end_ip;
216       edi->di_arm.u.rti.name_ptr = to_unw_word (path);
217       edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr;
218       edi->di_arm.u.rti.table_len = parm_exidx->p_memsz;
219       found = 1;
220     }
221 #endif
222
223 #ifdef CONFIG_DEBUG_FRAME
224   /* Try .debug_frame. */
225   found = dwarf_find_debug_frame (found, &edi->di_debug, ip, load_base, path,
226                                   start_ip, end_ip);
227 #endif
228
229   return found;
230 }