_UPT_find_proc_info.c revision 25ee9f81727616f3269032c52483e4421d451291
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 elf_dyn_info *edi, Elf64_Phdr *pdyn, Elf64_Addr load_base)
41{
42  Elf64_Off soff, str_soff;
43  Elf64_Ehdr *ehdr = edi->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 *) edi->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 > edi->ei.size)
72    {
73      Debug (1, "section table outside of image? (%lu > %lu)",
74	     soff + ehdr->e_shnum * ehdr->e_shentsize,
75	     edi->ei.size);
76      goto done;
77    }
78
79  shdr = (Elf64_Shdr *) ((char *) edi->ei.image + soff);
80  str_shdr = (Elf64_Shdr *) ((char *) edi->ei.image + str_soff);
81  strtab = (char *) edi->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 *) edi->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", edi->ei.image, gp);
95  return gp;
96}
97
98HIDDEN int
99_UPTi_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as,
100			 char *path, unw_word_t segbase, unw_word_t mapoff,
101			 unw_word_t ip)
102{
103  Elf64_Phdr *phdr, *ptxt = NULL, *punw = NULL, *pdyn = NULL;
104  Elf64_Ehdr *ehdr;
105  int i;
106
107  if (!_Uelf64_valid_object (&edi->ei))
108    return -UNW_ENOINFO;
109
110  ehdr = edi->ei.image;
111  phdr = (Elf64_Phdr *) ((char *) edi->ei.image + ehdr->e_phoff);
112
113  for (i = 0; i < ehdr->e_phnum; ++i)
114    {
115      switch (phdr[i].p_type)
116	{
117	case PT_LOAD:
118	  if (phdr[i].p_offset == mapoff)
119	    ptxt = phdr + i;
120	  break;
121
122	case PT_IA_64_UNWIND:
123	  punw = phdr + i;
124	  break;
125
126	case PT_DYNAMIC:
127	  pdyn = phdr + i;
128	  break;
129
130	default:
131	  break;
132	}
133    }
134  if (!ptxt || !punw)
135    return 0;
136
137  edi->di_cache.start_ip = segbase;
138  edi->di_cache.end_ip = edi->di_cache.start_ip + ptxt->p_memsz;
139  edi->di_cache.gp = find_gp (edi, pdyn, segbase - ptxt->p_vaddr);
140  edi->di_cache.format = UNW_INFO_FORMAT_TABLE;
141  edi->di_cache.u.ti.name_ptr = 0;
142  edi->di_cache.u.ti.segbase = segbase;
143  edi->di_cache.u.ti.table_len = punw->p_memsz / sizeof (unw_word_t);
144  edi->di_cache.u.ti.table_data = (unw_word_t *)
145    ((char *) edi->ei.image + (punw->p_vaddr - ptxt->p_vaddr));
146  return 1;
147}
148
149#elif UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_HPPA \
150|| UNW_TARGET_PPC32 || UNW_TARGET_PPC64 || UNW_TARGET_ARM
151
152#include "dwarf-eh.h"
153#include "dwarf_i.h"
154
155/* We need our own instance of dwarf_read_encoded_pointer() here since
156   the one in dwarf/Gpe.c is not (and should not be) exported.  */
157int
158dwarf_read_encoded_pointer (unw_addr_space_t as, unw_accessors_t *a,
159			    unw_word_t *addr, unsigned char encoding,
160			    const unw_proc_info_t *pi,
161			    unw_word_t *valp, void *arg)
162{
163  return dwarf_read_encoded_pointer_inlined (as, a, addr, encoding,
164					     pi, valp, arg);
165}
166
167HIDDEN int
168_UPTi_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as,
169			 char *path, unw_word_t segbase, unw_word_t mapoff,
170			 unw_word_t ip)
171{
172  Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
173  unw_word_t addr, eh_frame_start, fde_count, load_base;
174  unw_word_t max_load_addr = 0;
175  unw_word_t start_ip = (unw_word_t) -1;
176  unw_word_t end_ip = 0;
177  struct dwarf_eh_frame_hdr *hdr;
178  unw_proc_info_t pi;
179  unw_accessors_t *a;
180  Elf_W(Ehdr) *ehdr;
181#if UNW_TARGET_ARM
182  const Elf_W(Phdr) *parm_exidx = NULL;
183#endif
184  int i, ret, found = 0;
185
186  /* XXX: Much of this code is Linux/LSB-specific.  */
187
188  if (!elf_w(valid_object) (&edi->ei))
189    return -UNW_ENOINFO;
190
191  ehdr = edi->ei.image;
192  phdr = (Elf_W(Phdr) *) ((char *) edi->ei.image + ehdr->e_phoff);
193
194  for (i = 0; i < ehdr->e_phnum; ++i)
195    {
196      switch (phdr[i].p_type)
197	{
198	case PT_LOAD:
199	  if (phdr[i].p_vaddr < start_ip)
200	    start_ip = phdr[i].p_vaddr;
201
202	  if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
203	    end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
204
205	  if (phdr[i].p_offset == mapoff)
206	    ptxt = phdr + i;
207	  if ((uintptr_t) edi->ei.image + phdr->p_filesz > max_load_addr)
208	    max_load_addr = (uintptr_t) edi->ei.image + phdr->p_filesz;
209	  break;
210
211	case PT_GNU_EH_FRAME:
212	  peh_hdr = phdr + i;
213	  break;
214
215	case PT_DYNAMIC:
216	  pdyn = phdr + i;
217	  break;
218
219#if UNW_TARGET_ARM
220	case PT_ARM_EXIDX:
221	  parm_exidx = phdr + i;
222	  break;
223#endif
224
225	default:
226	  break;
227	}
228    }
229
230  if (!ptxt)
231    return 0;
232
233  load_base = segbase - ptxt->p_vaddr;
234  start_ip += load_base;
235  end_ip += load_base;
236
237  if (peh_hdr)
238    {
239      if (pdyn)
240	{
241	  /* For dynamicly linked executables and shared libraries,
242	     DT_PLTGOT is the value that data-relative addresses are
243	     relative to for that object.  We call this the "gp".  */
244		Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
245						 + (char *) edi->ei.image);
246	  for (; dyn->d_tag != DT_NULL; ++dyn)
247	    if (dyn->d_tag == DT_PLTGOT)
248	      {
249		/* Assume that _DYNAMIC is writable and GLIBC has
250		   relocated it (true for x86 at least).  */
251		edi->di_cache.gp = dyn->d_un.d_ptr;
252		break;
253	      }
254	}
255      else
256	/* Otherwise this is a static executable with no _DYNAMIC.  Assume
257	   that data-relative addresses are relative to 0, i.e.,
258	   absolute.  */
259	edi->di_cache.gp = 0;
260
261      hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
262					   + (char *) edi->ei.image);
263      if (hdr->version != DW_EH_VERSION)
264	{
265	  Debug (1, "table `%s' has unexpected version %d\n",
266		 path, hdr->version);
267	  return -UNW_ENOINFO;
268	}
269
270      a = unw_get_accessors (unw_local_addr_space);
271      addr = (unw_word_t) (hdr + 1);
272
273      /* Fill in a dummy proc_info structure.  We just need to fill in
274	 enough to ensure that dwarf_read_encoded_pointer() can do it's
275	 job.  Since we don't have a procedure-context at this point, all
276	 we have to do is fill in the global-pointer.  */
277      memset (&pi, 0, sizeof (pi));
278      pi.gp = edi->di_cache.gp;
279
280      /* (Optionally) read eh_frame_ptr: */
281      if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
282					     &addr, hdr->eh_frame_ptr_enc, &pi,
283					     &eh_frame_start, NULL)) < 0)
284	return -UNW_ENOINFO;
285
286      /* (Optionally) read fde_count: */
287      if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
288					     &addr, hdr->fde_count_enc, &pi,
289					     &fde_count, NULL)) < 0)
290	return -UNW_ENOINFO;
291
292      if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
293	{
294    #if 1
295	  abort ();
296    #else
297	  unw_word_t eh_frame_end;
298
299	  /* If there is no search table or it has an unsupported
300	     encoding, fall back on linear search.  */
301	  if (hdr->table_enc == DW_EH_PE_omit)
302	    Debug (4, "EH lacks search table; doing linear search\n");
303	  else
304	    Debug (4, "EH table has encoding 0x%x; doing linear search\n",
305		   hdr->table_enc);
306
307	  eh_frame_end = max_load_addr;	/* XXX can we do better? */
308
309	  if (hdr->fde_count_enc == DW_EH_PE_omit)
310	    fde_count = ~0UL;
311	  if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
312	    abort ();
313
314	  return linear_search (unw_local_addr_space, ip,
315				eh_frame_start, eh_frame_end, fde_count,
316				pi, need_unwind_info, NULL);
317    #endif
318	}
319
320      edi->di_cache.start_ip = start_ip;
321      edi->di_cache.end_ip = end_ip;
322      edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
323      edi->di_cache.u.rti.name_ptr = 0;
324      /* two 32-bit values (ip_offset/fde_offset) per table-entry: */
325      edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
326      edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
327				       + (addr - (unw_word_t) edi->ei.image
328					  - peh_hdr->p_offset));
329
330      /* For the binary-search table in the eh_frame_hdr, data-relative
331	 means relative to the start of that section... */
332      edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
333				    + ((unw_word_t) hdr - (unw_word_t) edi->ei.image
334				       - peh_hdr->p_offset));
335      found = 1;
336    }
337
338#if UNW_TARGET_ARM
339  if (parm_exidx)
340    {
341      edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
342      edi->di_arm.start_ip = start_ip;
343      edi->di_arm.end_ip = end_ip;
344      edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
345      edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr;
346      edi->di_arm.u.rti.table_len = parm_exidx->p_memsz;
347      found = 1;
348    }
349#endif
350
351#ifdef CONFIG_DEBUG_FRAME
352  /* Try .debug_frame. */
353  found = dwarf_find_debug_frame (found, &edi->edi.di_debug, ip, segbase, path,
354				  start_ip, end_ip);
355#endif
356
357  return found;
358}
359
360#endif /* UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_HPPA*/
361
362static int
363get_unwind_info (struct elf_dyn_info *edi, pid_t pid, unw_addr_space_t as, unw_word_t ip)
364{
365  unsigned long segbase, mapoff;
366  char path[PATH_MAX];
367
368#if UNW_TARGET_IA64 && defined(__linux)
369  if (!edi->ktab.start_ip && _Uia64_get_kernel_table (&edi->ktab) < 0)
370    return -UNW_ENOINFO;
371
372  if (edi->ktab.format != -1 && ip >= edi->ktab.start_ip && ip < edi->ktab.end_ip)
373    return 0;
374#endif
375
376  if ((edi->di_cache.format != -1
377       && ip >= edi->di_cache.start_ip && ip < edi->di_cache.end_ip)
378#if UNW_TARGET_ARM
379      || (edi->di_debug.format != -1
380       && ip >= edi->di_arm.start_ip && ip < edi->di_arm.end_ip)
381#endif
382      || (edi->di_debug.format != -1
383       && ip >= edi->di_debug.start_ip && ip < edi->di_debug.end_ip))
384    return 0;
385
386  invalidate_edi(edi);
387
388  if (tdep_get_elf_image (&edi->ei, pid, ip, &segbase, &mapoff, path,
389                          sizeof(path)) < 0)
390    return -UNW_ENOINFO;
391
392  /* Here, SEGBASE is the starting-address of the (mmap'ped) segment
393     which covers the IP we're looking for.  */
394  if (_UPTi_find_unwind_table (edi, as, path, segbase, mapoff, ip) < 0)
395    return -UNW_ENOINFO;
396
397  /* This can happen in corner cases where dynamically generated
398     code falls into the same page that contains the data-segment
399     and the page-offset of the code is within the first page of
400     the executable.  */
401  if (edi->di_cache.format != -1
402      && (ip < edi->di_cache.start_ip || ip >= edi->di_cache.end_ip))
403     edi->di_cache.format = -1;
404
405  if (edi->di_debug.format != -1
406      && (ip < edi->di_debug.start_ip || ip >= edi->di_debug.end_ip))
407     edi->di_debug.format = -1;
408
409  if (edi->di_cache.format == -1
410#if UNW_TARGET_ARM
411      && edi->di_arm.format == -1
412#endif
413      && edi->di_debug.format == -1)
414    return -UNW_ENOINFO;
415
416  return 0;
417}
418
419int
420_UPT_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
421		     int need_unwind_info, void *arg)
422{
423  struct UPT_info *ui = arg;
424  int ret = -UNW_ENOINFO;
425
426  if (get_unwind_info (&ui->edi, ui->pid, as, ip) < 0)
427    return -UNW_ENOINFO;
428
429#if UNW_TARGET_IA64
430  if (ui->edi.ktab.format != -1)
431    {
432      /* The kernel unwind table resides in local memory, so we have
433	 to use the local address space to search it.  Since
434	 _UPT_put_unwind_info() has no easy way of detecting this
435	 case, we simply make a copy of the unwind-info, so
436	 _UPT_put_unwind_info() can always free() the unwind-info
437	 without ill effects.  */
438      ret = tdep_search_unwind_table (unw_local_addr_space, ip, &ui->edi.ktab, pi,
439				      need_unwind_info, arg);
440      if (ret >= 0)
441	{
442	  if (!need_unwind_info)
443	    pi->unwind_info = NULL;
444	  else
445	    {
446	      void *mem = malloc (pi->unwind_info_size);
447
448	      if (!mem)
449		return -UNW_ENOMEM;
450	      memcpy (mem, pi->unwind_info, pi->unwind_info_size);
451	      pi->unwind_info = mem;
452	    }
453	}
454    }
455#endif
456
457  if (ret == -UNW_ENOINFO && ui->edi.di_cache.format != -1)
458    ret = tdep_search_unwind_table (as, ip, &ui->edi.di_cache,
459				    pi, need_unwind_info, arg);
460
461#if UNW_TARGET_ARM
462  if (ret == -UNW_ENOINFO && ui->edi.di_arm.format != -1)
463    ret = tdep_search_unwind_table (as, ip, &ui->edi.di_arm, pi,
464                                    need_unwind_info, arg);
465#endif
466
467  if (ret == -UNW_ENOINFO && ui->edi.di_debug.format != -1)
468    ret = tdep_search_unwind_table (as, ip, &ui->edi.di_debug, pi,
469				    need_unwind_info, arg);
470
471  return ret;
472}
473