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