_UPT_find_proc_info.c revision 6aec15799d0572a484065aed3d97317df0702b17
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
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 <elf.h>
27#include <fcntl.h>
28#include <string.h>
29#include <unistd.h>
30
31#include <sys/mman.h>
32
33#include "_UPT_internal.h"
34
35#if UNW_TARGET_IA64
36
37#include "elf64.h"
38
39static unw_word_t
40find_gp (struct UPT_info *ui, Elf64_Phdr *pdyn, Elf64_Addr load_base)
41{
42  Elf64_Off soff, str_soff;
43  Elf64_Ehdr *ehdr = ui->ei.image;
44  Elf64_Shdr *shdr;
45  Elf64_Shdr *str_shdr;
46  Elf64_Addr gp = 0;
47  char *strtab;
48  int i;
49
50  if (pdyn)
51    {
52      /* If we have a PT_DYNAMIC program header, fetch the gp-value
53	 from the DT_PLTGOT entry.  */
54      Elf64_Dyn *dyn = (Elf64_Dyn *) (pdyn->p_offset + (char *) ui->ei.image);
55      for (; dyn->d_tag != DT_NULL; ++dyn)
56	if (dyn->d_tag == DT_PLTGOT)
57	  {
58	    gp = (Elf64_Addr) dyn->d_un.d_ptr + load_base;
59	    goto done;
60	  }
61    }
62
63  /* Without a PT_DYAMIC header, lets try to look for a non-empty .opd
64     section.  If there is such a section, we know it's full of
65     function descriptors, and we can simply pick up the gp from the
66     second word of the first entry in this table.  */
67
68  soff = ehdr->e_shoff;
69  str_soff = soff + (ehdr->e_shstrndx * ehdr->e_shentsize);
70
71  if (soff + ehdr->e_shnum * ehdr->e_shentsize > ui->ei.size)
72    {
73      Debug (1, "section table outside of image? (%lu > %lu)",
74	     soff + ehdr->e_shnum * ehdr->e_shentsize,
75	     ui->ei.size);
76      goto done;
77    }
78
79  shdr = (Elf64_Shdr *) ((char *) ui->ei.image + soff);
80  str_shdr = (Elf64_Shdr *) ((char *) ui->ei.image + str_soff);
81  strtab = (char *) ui->ei.image + str_shdr->sh_offset;
82  for (i = 0; i < ehdr->e_shnum; ++i)
83    {
84      if (strcmp (strtab + shdr->sh_name, ".opd") == 0
85	  && shdr->sh_size >= 16)
86	{
87	  gp = ((Elf64_Addr *) ((char *) ui->ei.image + shdr->sh_offset))[1];
88	  goto done;
89	}
90      shdr = (Elf64_Shdr *) (((char *) shdr) + ehdr->e_shentsize);
91    }
92
93 done:
94  Debug (16, "image at %p, gp = %lx\n", ui->ei.image, gp);
95  return gp;
96}
97
98HIDDEN unw_dyn_info_t *
99_UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as,
100			 char *path, unw_word_t segbase, unw_word_t mapoff)
101{
102  Elf64_Phdr *phdr, *ptxt = NULL, *punw = NULL, *pdyn = NULL;
103  Elf64_Ehdr *ehdr;
104  int i;
105
106  if (!_Uelf64_valid_object (&ui->ei))
107    return NULL;
108
109  ehdr = ui->ei.image;
110  phdr = (Elf64_Phdr *) ((char *) ui->ei.image + ehdr->e_phoff);
111
112  for (i = 0; i < ehdr->e_phnum; ++i)
113    {
114      switch (phdr[i].p_type)
115	{
116	case PT_LOAD:
117	  if (phdr[i].p_offset == mapoff)
118	    ptxt = phdr + i;
119	  break;
120
121	case PT_IA_64_UNWIND:
122	  punw = phdr + i;
123	  break;
124
125	case PT_DYNAMIC:
126	  pdyn = phdr + i;
127	  break;
128
129	default:
130	  break;
131	}
132    }
133  if (!ptxt || !punw)
134    return NULL;
135
136  ui->di_cache.start_ip = segbase;
137  ui->di_cache.end_ip = ui->di_cache.start_ip + ptxt->p_memsz;
138  ui->di_cache.gp = find_gp (ui, pdyn, segbase - ptxt->p_vaddr);
139  ui->di_cache.format = UNW_INFO_FORMAT_TABLE;
140  ui->di_cache.u.ti.name_ptr = 0;
141  ui->di_cache.u.ti.segbase = segbase;
142  ui->di_cache.u.ti.table_len = punw->p_memsz / sizeof (unw_word_t);
143  ui->di_cache.u.ti.table_data = (unw_word_t *)
144    ((char *) ui->ei.image + (punw->p_vaddr - ptxt->p_vaddr));
145  return &ui->di_cache;
146}
147
148#elif UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_HPPA \
149|| UNW_TARGET_PPC32 || UNW_TARGET_PPC64 || UNW_TARGET_ARM
150
151#include "dwarf-eh.h"
152#include "dwarf_i.h"
153
154/* We need our own instance of dwarf_read_encoded_pointer() here since
155   the one in dwarf/Gpe.c is not (and should not be) exported.  */
156int
157dwarf_read_encoded_pointer (unw_addr_space_t as, unw_accessors_t *a,
158			    unw_word_t *addr, unsigned char encoding,
159			    const unw_proc_info_t *pi,
160			    unw_word_t *valp, void *arg)
161{
162  return dwarf_read_encoded_pointer_inlined (as, a, addr, encoding,
163					     pi, valp, arg);
164}
165
166HIDDEN unw_dyn_info_t *
167_UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as,
168			 char *path, unw_word_t segbase, unw_word_t mapoff)
169{
170  Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
171  unw_word_t addr, eh_frame_start, fde_count, load_base;
172  struct dwarf_eh_frame_hdr *hdr;
173  unw_proc_info_t pi;
174  unw_accessors_t *a;
175  Elf_W(Ehdr) *ehdr;
176  int i, ret;
177
178  /* XXX: Much of this code is Linux/LSB-specific.  */
179
180  if (!elf_w(valid_object) (&ui->ei))
181    return NULL;
182
183  ehdr = ui->ei.image;
184  phdr = (Elf_W(Phdr) *) ((char *) ui->ei.image + ehdr->e_phoff);
185
186  for (i = 0; i < ehdr->e_phnum; ++i)
187    {
188      switch (phdr[i].p_type)
189	{
190	case PT_LOAD:
191	  if (phdr[i].p_offset == mapoff)
192	    ptxt = phdr + i;
193	  break;
194
195	case PT_GNU_EH_FRAME:
196	  peh_hdr = phdr + i;
197	  break;
198
199	case PT_DYNAMIC:
200	  pdyn = phdr + i;
201	  break;
202
203	default:
204	  break;
205	}
206    }
207  if (!ptxt || !peh_hdr)
208    return NULL;
209
210  if (pdyn)
211    {
212      /* For dynamicly linked executables and shared libraries,
213	 DT_PLTGOT is the value that data-relative addresses are
214	 relative to for that object.  We call this the "gp".  */
215	    Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
216					     + (char *) ui->ei.image);
217      for (; dyn->d_tag != DT_NULL; ++dyn)
218	if (dyn->d_tag == DT_PLTGOT)
219	  {
220	    /* Assume that _DYNAMIC is writable and GLIBC has
221	       relocated it (true for x86 at least).  */
222	    ui->di_cache.gp = dyn->d_un.d_ptr;
223	    break;
224	  }
225    }
226  else
227    /* Otherwise this is a static executable with no _DYNAMIC.  Assume
228       that data-relative addresses are relative to 0, i.e.,
229       absolute.  */
230    ui->di_cache.gp = 0;
231
232  hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
233				       + (char *) ui->ei.image);
234  if (hdr->version != DW_EH_VERSION)
235    {
236      Debug (1, "table `%s' has unexpected version %d\n",
237	     path, hdr->version);
238      return 0;
239    }
240
241  a = unw_get_accessors (unw_local_addr_space);
242  addr = (unw_word_t) (hdr + 1);
243
244  /* Fill in a dummy proc_info structure.  We just need to fill in
245     enough to ensure that dwarf_read_encoded_pointer() can do it's
246     job.  Since we don't have a procedure-context at this point, all
247     we have to do is fill in the global-pointer.  */
248  memset (&pi, 0, sizeof (pi));
249  pi.gp = ui->di_cache.gp;
250
251  /* (Optionally) read eh_frame_ptr: */
252  if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
253					 &addr, hdr->eh_frame_ptr_enc, &pi,
254					 &eh_frame_start, NULL)) < 0)
255    return NULL;
256
257  /* (Optionally) read fde_count: */
258  if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
259					 &addr, hdr->fde_count_enc, &pi,
260					 &fde_count, NULL)) < 0)
261    return NULL;
262
263  if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
264    {
265      abort ();
266#if 0
267      /* If there is no search table or it has an unsupported
268	 encoding, fall back on linear search.  */
269      if (hdr->table_enc == DW_EH_PE_omit)
270	Debug (4, "table `%s' lacks search table; doing linear search\n",
271	       info->dlpi_name);
272      else
273	Debug (4, "table `%s' has encoding 0x%x; doing linear search\n",
274	       info->dlpi_name, hdr->table_enc);
275
276      eh_frame_end = max_load_addr;	/* XXX can we do better? */
277
278      if (hdr->fde_count_enc == DW_EH_PE_omit)
279	fde_count = ~0UL;
280      if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
281	abort ();
282
283      cb_data->single_fde = 1;
284      return linear_search (unw_local_addr_space, ip,
285			    eh_frame_start, eh_frame_end, fde_count,
286			    pi, need_unwind_info, NULL);
287#endif
288    }
289
290  load_base = segbase - ptxt->p_vaddr;
291
292  ui->di_cache.start_ip = segbase;
293  ui->di_cache.end_ip = ui->di_cache.start_ip + ptxt->p_memsz;
294  ui->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
295  ui->di_cache.u.rti.name_ptr = 0;
296  /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
297  ui->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
298  ui->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
299				   + (addr - (unw_word_t) ui->ei.image
300				      - peh_hdr->p_offset));
301
302  /* For the binary-search table in the eh_frame_hdr, data-relative
303     means relative to the start of that section... */
304  ui->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
305				+ ((unw_word_t) hdr - (unw_word_t) ui->ei.image
306				   - peh_hdr->p_offset));
307
308  return &ui->di_cache;
309}
310
311#endif /* UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_HPPA*/
312
313static unw_dyn_info_t *
314get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip)
315{
316  unsigned long segbase, mapoff;
317  char path[PATH_MAX];
318  unw_dyn_info_t *di;
319
320#if UNW_TARGET_IA64 && defined(__linux)
321  if (!ui->ktab.start_ip && _Uia64_get_kernel_table (&ui->ktab) < 0)
322    return NULL;
323
324  if (ip >= ui->ktab.start_ip && ip < ui->ktab.end_ip)
325    return &ui->ktab;
326#endif
327
328  if (ip >= ui->di_cache.start_ip && ip < ui->di_cache.end_ip)
329    return &ui->di_cache;
330
331  if (ui->ei.image)
332    {
333      munmap (ui->ei.image, ui->ei.size);
334      ui->ei.image = NULL;
335      ui->ei.size = 0;
336
337      /* invalidate the cache: */
338      ui->di_cache.start_ip = ui->di_cache.end_ip = 0;
339    }
340
341  if (tdep_get_elf_image (&ui->ei, ui->pid, ip, &segbase, &mapoff) < 0)
342    return NULL;
343
344  /* Here, SEGBASE is the starting-address of the (mmap'ped) segment
345     which covers the IP we're looking for.  */
346  di = _UPTi_find_unwind_table (ui, as, path, segbase, mapoff);
347  if (!di
348      /* This can happen in corner cases where dynamically generated
349         code falls into the same page that contains the data-segment
350         and the page-offset of the code is within the first page of
351         the executable.  */
352      || ip < di->start_ip || ip >= di->end_ip)
353    return NULL;
354
355  return di;
356}
357
358int
359_UPT_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
360		     int need_unwind_info, void *arg)
361{
362  struct UPT_info *ui = arg;
363  unw_dyn_info_t *di;
364
365  di = get_unwind_info (ui, as, ip);
366  if (!di)
367    return -UNW_ENOINFO;
368
369#if UNW_TARGET_IA64
370  if (di == &ui->ktab)
371    {
372      /* The kernel unwind table resides in local memory, so we have
373	 to use the local address space to search it.  Since
374	 _UPT_put_unwind_info() has no easy way of detecting this
375	 case, we simply make a copy of the unwind-info, so
376	 _UPT_put_unwind_info() can always free() the unwind-info
377	 without ill effects.  */
378      int ret = tdep_search_unwind_table (unw_local_addr_space, ip, di, pi,
379					  need_unwind_info, arg);
380      if (ret >= 0)
381	{
382	  if (!need_unwind_info)
383	    pi->unwind_info = NULL;
384	  else
385	    {
386	      void *mem = malloc (pi->unwind_info_size);
387
388	      if (!mem)
389		return -UNW_ENOMEM;
390	      memcpy (mem, pi->unwind_info, pi->unwind_info_size);
391	      pi->unwind_info = mem;
392	    }
393	}
394      return ret;
395    }
396  else
397#endif
398  return tdep_search_unwind_table (as, ip, di, pi, need_unwind_info, arg);
399}
400