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#include <elf.h>
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 "elf64.h"
34
35static unw_word_t
36find_gp (struct elf_dyn_info *edi, Elf64_Phdr *pdyn, Elf64_Addr load_base)
37{
38  Elf64_Off soff, str_soff;
39  Elf64_Ehdr *ehdr = edi->ei.image;
40  Elf64_Shdr *shdr;
41  Elf64_Shdr *str_shdr;
42  Elf64_Addr gp = 0;
43  char *strtab;
44  int i;
45
46  if (pdyn)
47    {
48      /* If we have a PT_DYNAMIC program header, fetch the gp-value
49	 from the DT_PLTGOT entry.  */
50      Elf64_Dyn *dyn = (Elf64_Dyn *) (pdyn->p_offset + (char *) edi->ei.image);
51      for (; dyn->d_tag != DT_NULL; ++dyn)
52	if (dyn->d_tag == DT_PLTGOT)
53	  {
54	    gp = (Elf64_Addr) dyn->d_un.d_ptr + load_base;
55	    goto done;
56	  }
57    }
58
59  /* Without a PT_DYAMIC header, lets try to look for a non-empty .opd
60     section.  If there is such a section, we know it's full of
61     function descriptors, and we can simply pick up the gp from the
62     second word of the first entry in this table.  */
63
64  soff = ehdr->e_shoff;
65  str_soff = soff + (ehdr->e_shstrndx * ehdr->e_shentsize);
66
67  if (soff + ehdr->e_shnum * ehdr->e_shentsize > edi->ei.size)
68    {
69      Debug (1, "section table outside of image? (%lu > %lu)",
70	     soff + ehdr->e_shnum * ehdr->e_shentsize,
71	     edi->ei.size);
72      goto done;
73    }
74
75  shdr = (Elf64_Shdr *) ((char *) edi->ei.image + soff);
76  str_shdr = (Elf64_Shdr *) ((char *) edi->ei.image + str_soff);
77  strtab = (char *) edi->ei.image + str_shdr->sh_offset;
78  for (i = 0; i < ehdr->e_shnum; ++i)
79    {
80      if (strcmp (strtab + shdr->sh_name, ".opd") == 0
81	  && shdr->sh_size >= 16)
82	{
83	  gp = ((Elf64_Addr *) ((char *) edi->ei.image + shdr->sh_offset))[1];
84	  goto done;
85	}
86      shdr = (Elf64_Shdr *) (((char *) shdr) + ehdr->e_shentsize);
87    }
88
89 done:
90  Debug (16, "image at %p, gp = %lx\n", edi->ei.image, gp);
91  return gp;
92}
93
94int
95ia64_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as,
96			 char *path, unw_word_t segbase, unw_word_t mapoff,
97			 unw_word_t ip)
98{
99  Elf64_Phdr *phdr, *ptxt = NULL, *punw = NULL, *pdyn = NULL;
100  Elf64_Ehdr *ehdr;
101  int i;
102
103  if (!_Uelf64_valid_object (&edi->ei))
104    return -UNW_ENOINFO;
105
106  ehdr = edi->ei.image;
107  phdr = (Elf64_Phdr *) ((char *) edi->ei.image + ehdr->e_phoff);
108
109  for (i = 0; i < ehdr->e_phnum; ++i)
110    {
111      switch (phdr[i].p_type)
112	{
113	case PT_LOAD:
114	  if (phdr[i].p_offset == mapoff)
115	    ptxt = phdr + i;
116	  break;
117
118	case PT_IA_64_UNWIND:
119	  punw = phdr + i;
120	  break;
121
122	case PT_DYNAMIC:
123	  pdyn = phdr + i;
124	  break;
125
126	default:
127	  break;
128	}
129    }
130  if (!ptxt || !punw)
131    return 0;
132
133  edi->di_cache.start_ip = segbase;
134  edi->di_cache.end_ip = edi->di_cache.start_ip + ptxt->p_memsz;
135  edi->di_cache.gp = find_gp (edi, pdyn, segbase - ptxt->p_vaddr);
136  edi->di_cache.format = UNW_INFO_FORMAT_TABLE;
137  edi->di_cache.u.ti.name_ptr = 0;
138  edi->di_cache.u.ti.segbase = segbase;
139  edi->di_cache.u.ti.table_len = punw->p_memsz / sizeof (unw_word_t);
140  edi->di_cache.u.ti.table_data = (unw_word_t *)
141    ((char *) edi->ei.image + (punw->p_vaddr - ptxt->p_vaddr));
142  return 1;
143}
144