Make UEFI boot-platform build again
[haiku.git] / src / libs / libunwind / elfxx.c
blob33fccba273b5bb737a4a4f46bafd2f086af41570
1 /* libunwind - a platform-independent unwind library
2 Copyright (C) 2003-2005 Hewlett-Packard Co
3 Copyright (C) 2007 David Mosberger-Tang
4 Contributed by David Mosberger-Tang <dmosberger@gmail.com>
6 This file is part of libunwind.
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
27 #include "libunwind_i.h"
29 #include <stdio.h>
30 #include <sys/param.h>
32 #ifdef HAVE_LZMA
33 #include <lzma.h>
34 #endif /* HAVE_LZMA */
36 static Elf_W (Shdr)*
37 elf_w (section_table) (struct elf_image *ei)
39 Elf_W (Ehdr) *ehdr = ei->image;
40 Elf_W (Off) soff;
42 soff = ehdr->e_shoff;
43 if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size)
45 Debug (1, "section table outside of image? (%lu > %lu)\n",
46 (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
47 (unsigned long) ei->size);
48 return NULL;
51 return (Elf_W (Shdr) *) ((char *) ei->image + soff);
54 static char*
55 elf_w (string_table) (struct elf_image *ei, int section)
57 Elf_W (Ehdr) *ehdr = ei->image;
58 Elf_W (Off) soff, str_soff;
59 Elf_W (Shdr) *str_shdr;
61 /* this offset is assumed to be OK */
62 soff = ehdr->e_shoff;
64 str_soff = soff + (section * ehdr->e_shentsize);
65 if (str_soff + ehdr->e_shentsize > ei->size)
67 Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
68 (unsigned long) (str_soff + ehdr->e_shentsize),
69 (unsigned long) ei->size);
70 return NULL;
72 str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff);
74 if (str_shdr->sh_offset + str_shdr->sh_size > ei->size)
76 Debug (1, "string table outside of image? (%lu > %lu)\n",
77 (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
78 (unsigned long) ei->size);
79 return NULL;
82 Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
83 return ei->image + str_shdr->sh_offset;
86 static int
87 elf_w (lookup_symbol) (unw_addr_space_t as,
88 unw_word_t ip, struct elf_image *ei,
89 Elf_W (Addr) load_offset,
90 char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
92 size_t syment_size;
93 Elf_W (Ehdr) *ehdr = ei->image;
94 Elf_W (Sym) *sym, *symtab, *symtab_end;
95 Elf_W (Shdr) *shdr;
96 Elf_W (Addr) val;
97 int i, ret = -UNW_ENOINFO;
98 char *strtab;
100 if (!elf_w (valid_object) (ei))
101 return -UNW_ENOINFO;
103 shdr = elf_w (section_table) (ei);
104 if (!shdr)
105 return -UNW_ENOINFO;
107 for (i = 0; i < ehdr->e_shnum; ++i)
109 switch (shdr->sh_type)
111 case SHT_SYMTAB:
112 case SHT_DYNSYM:
113 symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
114 symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
115 syment_size = shdr->sh_entsize;
117 strtab = elf_w (string_table) (ei, shdr->sh_link);
118 if (!strtab)
119 break;
121 Debug (16, "symtab=0x%lx[%d]\n",
122 (long) shdr->sh_offset, shdr->sh_type);
124 for (sym = symtab;
125 sym < symtab_end;
126 sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
128 if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
129 && sym->st_shndx != SHN_UNDEF)
131 val = sym->st_value;
132 if (sym->st_shndx != SHN_ABS)
133 val += load_offset;
134 if (tdep_get_func_addr (as, val, &val) < 0)
135 continue;
136 Debug (16, "0x%016lx info=0x%02x %s\n",
137 (long) val, sym->st_info, strtab + sym->st_name);
139 if ((Elf_W (Addr)) (ip - val) < *min_dist)
141 *min_dist = (Elf_W (Addr)) (ip - val);
142 strncpy (buf, strtab + sym->st_name, buf_len);
143 buf[buf_len - 1] = '\0';
144 ret = (strlen (strtab + sym->st_name) >= buf_len
145 ? -UNW_ENOMEM : 0);
149 break;
151 default:
152 break;
154 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
156 return ret;
159 static Elf_W (Addr)
160 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
161 unsigned long mapoff)
163 Elf_W (Addr) offset = 0;
164 Elf_W (Ehdr) *ehdr;
165 Elf_W (Phdr) *phdr;
166 int i;
168 ehdr = ei->image;
169 phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
171 for (i = 0; i < ehdr->e_phnum; ++i)
172 if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
174 offset = segbase - phdr[i].p_vaddr;
175 break;
178 return offset;
181 #if HAVE_LZMA
182 static size_t
183 xz_uncompressed_size (uint8_t *compressed, size_t length)
185 uint64_t memlimit = UINT64_MAX;
186 size_t ret = 0, pos = 0;
187 lzma_stream_flags options;
188 lzma_index *index;
190 if (length < LZMA_STREAM_HEADER_SIZE)
191 return 0;
193 uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
194 if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
195 return 0;
197 if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
198 return 0;
200 uint8_t *indexdata = footer - options.backward_size;
201 if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
202 &pos, options.backward_size) != LZMA_OK)
203 return 0;
205 if (lzma_index_size (index) == options.backward_size)
207 ret = lzma_index_uncompressed_size (index);
210 lzma_index_end (index, NULL);
211 return ret;
214 static int
215 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
217 Elf_W (Ehdr) *ehdr = ei->image;
218 Elf_W (Shdr) *shdr;
219 char *strtab;
220 int i;
221 uint8_t *compressed = NULL;
222 uint64_t memlimit = UINT64_MAX; /* no memory limit */
223 size_t compressed_len, uncompressed_len;
225 if (!elf_w (valid_object) (ei))
226 return 0;
228 shdr = elf_w (section_table) (ei);
229 if (!shdr)
230 return 0;
232 strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
233 if (!strtab)
234 return 0;
236 for (i = 0; i < ehdr->e_shnum; ++i)
238 if (strcmp (strtab + shdr->sh_name, ".gnu_debugdata") == 0)
240 if (shdr->sh_offset + shdr->sh_size > ei->size)
242 Debug (1, ".gnu_debugdata outside image? (0x%lu > 0x%lu)\n",
243 (unsigned long) shdr->sh_offset + shdr->sh_size,
244 (unsigned long) ei->size);
245 return 0;
248 Debug (16, "found .gnu_debugdata at 0x%lx\n",
249 (unsigned long) shdr->sh_offset);
250 compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
251 compressed_len = shdr->sh_size;
252 break;
255 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
258 /* not found */
259 if (!compressed)
260 return 0;
262 uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
263 if (uncompressed_len == 0)
265 Debug (1, "invalid .gnu_debugdata contents\n");
266 return 0;
269 mdi->size = uncompressed_len;
270 mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
271 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
273 if (mdi->image == MAP_FAILED)
274 return 0;
276 size_t in_pos = 0, out_pos = 0;
277 lzma_ret lret;
278 lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
279 compressed, &in_pos, compressed_len,
280 mdi->image, &out_pos, mdi->size);
281 if (lret != LZMA_OK)
283 Debug (1, "LZMA decompression failed: %d\n", lret);
284 munmap (mdi->image, mdi->size);
285 return 0;
288 return 1;
290 #else
291 static int
292 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
294 return 0;
296 #endif /* !HAVE_LZMA */
298 /* Find the ELF image that contains IP and return the "closest"
299 procedure name, if there is one. With some caching, this could be
300 sped up greatly, but until an application materializes that's
301 sensitive to the performance of this routine, why bother... */
303 HIDDEN int
304 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
305 unsigned long segbase,
306 unsigned long mapoff,
307 unw_word_t ip,
308 char *buf, size_t buf_len, unw_word_t *offp)
310 Elf_W (Addr) load_offset;
311 Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
312 int ret;
314 load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
315 ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
317 /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
318 there as well and replace the previously found if it is closer. */
319 struct elf_image mdi;
320 if (elf_w (extract_minidebuginfo) (ei, &mdi))
322 int ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
323 buf_len, &min_dist);
325 /* Closer symbol was found (possibly truncated). */
326 if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
328 ret = ret_mdi;
331 munmap (mdi.image, mdi.size);
334 if (min_dist >= ei->size)
335 return -UNW_ENOINFO; /* not found */
336 if (offp)
337 *offp = min_dist;
338 return ret;
341 HIDDEN int
342 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
343 char *buf, size_t buf_len, unw_word_t *offp)
345 unsigned long segbase, mapoff;
346 struct elf_image ei;
347 int ret;
349 ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0);
350 if (ret < 0)
351 return ret;
353 ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
355 munmap (ei.image, ei.size);
356 ei.image = NULL;
358 return ret;