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>
5
6 This file is part of libunwind.
7
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:
15
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18
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. */
26
27 #include "libunwind_i.h"
28
29 #include <stdio.h>
30 #include <sys/param.h>
31 #include <limits.h>
32
33 #ifdef HAVE_LZMA
34 #include <lzma.h>
35 #endif /* HAVE_LZMA */
36
Elf_W(Shdr)37 static Elf_W (Shdr)*
38 elf_w (section_table) (struct elf_image *ei)
39 {
40 Elf_W (Ehdr) *ehdr = ei->image;
41 Elf_W (Off) soff;
42
43 soff = ehdr->e_shoff;
44 if (soff + ehdr->e_shnum * ehdr->e_shentsize > ei->size)
45 {
46 Debug (1, "section table outside of image? (%lu > %lu)\n",
47 (unsigned long) (soff + ehdr->e_shnum * ehdr->e_shentsize),
48 (unsigned long) ei->size);
49 return NULL;
50 }
51
52 return (Elf_W (Shdr) *) ((char *) ei->image + soff);
53 }
54
55 static char*
elf_w(string_table)56 elf_w (string_table) (struct elf_image *ei, int section)
57 {
58 Elf_W (Ehdr) *ehdr = ei->image;
59 Elf_W (Off) soff, str_soff;
60 Elf_W (Shdr) *str_shdr;
61
62 /* this offset is assumed to be OK */
63 soff = ehdr->e_shoff;
64
65 str_soff = soff + (section * ehdr->e_shentsize);
66 if (str_soff + ehdr->e_shentsize > ei->size)
67 {
68 Debug (1, "string shdr table outside of image? (%lu > %lu)\n",
69 (unsigned long) (str_soff + ehdr->e_shentsize),
70 (unsigned long) ei->size);
71 return NULL;
72 }
73 str_shdr = (Elf_W (Shdr) *) ((char *) ei->image + str_soff);
74
75 if (str_shdr->sh_offset + str_shdr->sh_size > ei->size)
76 {
77 Debug (1, "string table outside of image? (%lu > %lu)\n",
78 (unsigned long) (str_shdr->sh_offset + str_shdr->sh_size),
79 (unsigned long) ei->size);
80 return NULL;
81 }
82
83 Debug (16, "strtab=0x%lx\n", (long) str_shdr->sh_offset);
84 return ei->image + str_shdr->sh_offset;
85 }
86
87 static int
elf_w(lookup_symbol)88 elf_w (lookup_symbol) (unw_addr_space_t as,
89 unw_word_t ip, struct elf_image *ei,
90 Elf_W (Addr) load_offset,
91 char *buf, size_t buf_len, Elf_W (Addr) *min_dist)
92 {
93 size_t syment_size;
94 Elf_W (Ehdr) *ehdr = ei->image;
95 Elf_W (Sym) *sym, *symtab, *symtab_end;
96 Elf_W (Shdr) *shdr;
97 Elf_W (Addr) val;
98 int i, ret = -UNW_ENOINFO;
99 char *strtab;
100
101 if (!elf_w (valid_object) (ei))
102 return -UNW_ENOINFO;
103
104 shdr = elf_w (section_table) (ei);
105 if (!shdr)
106 return -UNW_ENOINFO;
107
108 for (i = 0; i < ehdr->e_shnum; ++i)
109 {
110 switch (shdr->sh_type)
111 {
112 case SHT_SYMTAB:
113 case SHT_DYNSYM:
114 symtab = (Elf_W (Sym) *) ((char *) ei->image + shdr->sh_offset);
115 symtab_end = (Elf_W (Sym) *) ((char *) symtab + shdr->sh_size);
116 syment_size = shdr->sh_entsize;
117
118 strtab = elf_w (string_table) (ei, shdr->sh_link);
119 if (!strtab)
120 break;
121
122 Debug (16, "symtab=0x%lx[%d]\n",
123 (long) shdr->sh_offset, shdr->sh_type);
124
125 for (sym = symtab;
126 sym < symtab_end;
127 sym = (Elf_W (Sym) *) ((char *) sym + syment_size))
128 {
129 if (ELF_W (ST_TYPE) (sym->st_info) == STT_FUNC
130 && sym->st_shndx != SHN_UNDEF)
131 {
132 val = sym->st_value;
133 if (sym->st_shndx != SHN_ABS)
134 val += load_offset;
135 if (tdep_get_func_addr (as, val, &val) < 0)
136 continue;
137 Debug (16, "0x%016lx info=0x%02x %s\n",
138 (long) val, sym->st_info, strtab + sym->st_name);
139
140 if ((Elf_W (Addr)) (ip - val) < *min_dist)
141 {
142 *min_dist = (Elf_W (Addr)) (ip - val);
143 strncpy (buf, strtab + sym->st_name, buf_len);
144 buf[buf_len - 1] = '\0';
145 ret = (strlen (strtab + sym->st_name) >= buf_len
146 ? -UNW_ENOMEM : 0);
147 }
148 }
149 }
150 break;
151
152 default:
153 break;
154 }
155 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
156 }
157 return ret;
158 }
159
160 static Elf_W (Addr)
elf_w(get_load_offset)161 elf_w (get_load_offset) (struct elf_image *ei, unsigned long segbase,
162 unsigned long mapoff)
163 {
164 Elf_W (Addr) offset = 0;
165 Elf_W (Ehdr) *ehdr;
166 Elf_W (Phdr) *phdr;
167 int i;
168 // mapoff is obtained from mmap informations, so is always aligned on a page size.
169 // PT_LOAD program headers p_offset however is not guaranteed to be aligned on a
170 // page size, ld.lld generate libraries where this is not the case. So we must
171 // make sure we compare both values with the same alignment.
172 unsigned long pagesize_alignment_mask = ~(((unsigned long)getpagesize()) - 1UL);
173
174 ehdr = ei->image;
175 phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
176
177 for (i = 0; i < ehdr->e_phnum; ++i)
178 if (phdr[i].p_type == PT_LOAD && (phdr[i].p_offset & pagesize_alignment_mask) == mapoff)
179 {
180 offset = segbase - phdr[i].p_vaddr + (phdr[i].p_offset & (~pagesize_alignment_mask));
181 break;
182 }
183
184 return offset;
185 }
186
187 #if HAVE_LZMA
188 static size_t
xz_uncompressed_size(uint8_t * compressed,size_t length)189 xz_uncompressed_size (uint8_t *compressed, size_t length)
190 {
191 uint64_t memlimit = UINT64_MAX;
192 size_t ret = 0, pos = 0;
193 lzma_stream_flags options;
194 lzma_index *index;
195
196 if (length < LZMA_STREAM_HEADER_SIZE)
197 return 0;
198
199 uint8_t *footer = compressed + length - LZMA_STREAM_HEADER_SIZE;
200 if (lzma_stream_footer_decode (&options, footer) != LZMA_OK)
201 return 0;
202
203 if (length < LZMA_STREAM_HEADER_SIZE + options.backward_size)
204 return 0;
205
206 uint8_t *indexdata = footer - options.backward_size;
207 if (lzma_index_buffer_decode (&index, &memlimit, NULL, indexdata,
208 &pos, options.backward_size) != LZMA_OK)
209 return 0;
210
211 if (lzma_index_size (index) == options.backward_size)
212 {
213 ret = lzma_index_uncompressed_size (index);
214 }
215
216 lzma_index_end (index, NULL);
217 return ret;
218 }
219
220 static int
elf_w(extract_minidebuginfo)221 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
222 {
223 Elf_W (Shdr) *shdr;
224 uint8_t *compressed = NULL;
225 uint64_t memlimit = UINT64_MAX; /* no memory limit */
226 size_t compressed_len, uncompressed_len;
227
228 shdr = elf_w (find_section) (ei, ".gnu_debugdata");
229 if (!shdr)
230 return 0;
231
232 compressed = ((uint8_t *) ei->image) + shdr->sh_offset;
233 compressed_len = shdr->sh_size;
234
235 uncompressed_len = xz_uncompressed_size (compressed, compressed_len);
236 if (uncompressed_len == 0)
237 {
238 Debug (1, "invalid .gnu_debugdata contents\n");
239 return 0;
240 }
241
242 mdi->size = uncompressed_len;
243 mdi->image = mmap (NULL, uncompressed_len, PROT_READ|PROT_WRITE,
244 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
245
246 if (mdi->image == MAP_FAILED)
247 return 0;
248
249 size_t in_pos = 0, out_pos = 0;
250 lzma_ret lret;
251 lret = lzma_stream_buffer_decode (&memlimit, 0, NULL,
252 compressed, &in_pos, compressed_len,
253 mdi->image, &out_pos, mdi->size);
254 if (lret != LZMA_OK)
255 {
256 Debug (1, "LZMA decompression failed: %d\n", lret);
257 munmap (mdi->image, mdi->size);
258 return 0;
259 }
260
261 return 1;
262 }
263 #else
264 static int
elf_w(extract_minidebuginfo)265 elf_w (extract_minidebuginfo) (struct elf_image *ei, struct elf_image *mdi)
266 {
267 return 0;
268 }
269 #endif /* !HAVE_LZMA */
270
271 /* Find the ELF image that contains IP and return the "closest"
272 procedure name, if there is one. With some caching, this could be
273 sped up greatly, but until an application materializes that's
274 sensitive to the performance of this routine, why bother... */
275
276 HIDDEN int
elf_w(get_proc_name_in_image)277 elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
278 unsigned long segbase,
279 unsigned long mapoff,
280 unw_word_t ip,
281 char *buf, size_t buf_len, unw_word_t *offp)
282 {
283 Elf_W (Addr) load_offset;
284 Elf_W (Addr) min_dist = ~(Elf_W (Addr))0;
285 int ret;
286
287 load_offset = elf_w (get_load_offset) (ei, segbase, mapoff);
288 ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, &min_dist);
289
290 /* If the ELF image has MiniDebugInfo embedded in it, look up the symbol in
291 there as well and replace the previously found if it is closer. */
292 struct elf_image mdi;
293 if (elf_w (extract_minidebuginfo) (ei, &mdi))
294 {
295 int ret_mdi = elf_w (lookup_symbol) (as, ip, &mdi, load_offset, buf,
296 buf_len, &min_dist);
297
298 /* Closer symbol was found (possibly truncated). */
299 if (ret_mdi == 0 || ret_mdi == -UNW_ENOMEM)
300 {
301 ret = ret_mdi;
302 }
303
304 munmap (mdi.image, mdi.size);
305 }
306
307 if (min_dist >= ei->size)
308 return -UNW_ENOINFO; /* not found */
309 if (offp)
310 *offp = min_dist;
311 return ret;
312 }
313
314 HIDDEN int
elf_w(get_proc_name)315 elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
316 char *buf, size_t buf_len, unw_word_t *offp)
317 {
318 unsigned long segbase, mapoff;
319 struct elf_image ei;
320 int ret;
321 char file[PATH_MAX];
322
323 ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, file, PATH_MAX);
324 if (ret < 0)
325 return ret;
326
327 ret = elf_w (load_debuglink) (file, &ei, 1);
328 if (ret < 0)
329 return ret;
330
331 ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
332
333 munmap (ei.image, ei.size);
334 ei.image = NULL;
335
336 return ret;
337 }
338
Elf_W(Shdr)339 HIDDEN Elf_W (Shdr)*
340 elf_w (find_section) (struct elf_image *ei, const char* secname)
341 {
342 Elf_W (Ehdr) *ehdr = ei->image;
343 Elf_W (Shdr) *shdr;
344 char *strtab;
345 int i;
346
347 if (!elf_w (valid_object) (ei))
348 return 0;
349
350 shdr = elf_w (section_table) (ei);
351 if (!shdr)
352 return 0;
353
354 strtab = elf_w (string_table) (ei, ehdr->e_shstrndx);
355 if (!strtab)
356 return 0;
357
358 for (i = 0; i < ehdr->e_shnum; ++i)
359 {
360 if (strcmp (strtab + shdr->sh_name, secname) == 0)
361 {
362 if (shdr->sh_offset + shdr->sh_size > ei->size)
363 {
364 Debug (1, "section \"%s\" outside image? (0x%lu > 0x%lu)\n",
365 secname,
366 (unsigned long) shdr->sh_offset + shdr->sh_size,
367 (unsigned long) ei->size);
368 return 0;
369 }
370
371 Debug (16, "found section \"%s\" at 0x%lx\n",
372 secname, (unsigned long) shdr->sh_offset);
373 return shdr;
374 }
375
376 shdr = (Elf_W (Shdr) *) (((char *) shdr) + ehdr->e_shentsize);
377 }
378
379 /* section not found */
380 return 0;
381 }
382
383 /* Load a debug section, following .gnu_debuglink if appropriate
384 * Loads ei from file if not already mapped.
385 * If is_local, will also search sys directories /usr/local/dbg
386 *
387 * Returns 0 on success, failure otherwise.
388 * ei will be mapped to file or the located .gnu_debuglink from file
389 */
390 HIDDEN int
elf_w(load_debuglink)391 elf_w (load_debuglink) (const char* file, struct elf_image *ei, int is_local)
392 {
393 int ret;
394 Elf_W (Shdr) *shdr;
395 Elf_W (Ehdr) *prev_image;
396 off_t prev_size;
397
398 if (!ei->image)
399 {
400 ret = elf_map_image(ei, file);
401 if (ret)
402 return ret;
403 }
404
405 prev_image = ei->image;
406 prev_size = ei->size;
407
408 /* Ignore separate debug files which contain a .gnu_debuglink section. */
409 if (is_local == -1) {
410 return 0;
411 }
412
413 shdr = elf_w (find_section) (ei, ".gnu_debuglink");
414 if (shdr) {
415 if (shdr->sh_size >= PATH_MAX ||
416 (shdr->sh_offset + shdr->sh_size > ei->size))
417 {
418 return 0;
419 }
420
421 {
422 char linkbuf[shdr->sh_size];
423 char *link = ((char *) ei->image) + shdr->sh_offset;
424 char *p;
425 static const char *debugdir = "/usr/lib/debug";
426 char basedir[strlen(file) + 1];
427 char newname[shdr->sh_size + strlen (debugdir) + strlen (file) + 9];
428
429 memcpy(linkbuf, link, shdr->sh_size);
430
431 if (memchr (linkbuf, 0, shdr->sh_size) == NULL)
432 return 0;
433
434 ei->image = NULL;
435
436 Debug(1, "Found debuglink section, following %s\n", linkbuf);
437
438 p = strrchr (file, '/');
439 if (p != NULL)
440 {
441 memcpy (basedir, file, p - file);
442 basedir[p - file] = '\0';
443 }
444 else
445 basedir[0] = 0;
446
447 strcpy (newname, basedir);
448 strcat (newname, "/");
449 strcat (newname, linkbuf);
450 ret = elf_w (load_debuglink) (newname, ei, -1);
451
452 if (ret == -1)
453 {
454 strcpy (newname, basedir);
455 strcat (newname, "/.debug/");
456 strcat (newname, linkbuf);
457 ret = elf_w (load_debuglink) (newname, ei, -1);
458 }
459
460 if (ret == -1 && is_local == 1)
461 {
462 strcpy (newname, debugdir);
463 strcat (newname, basedir);
464 strcat (newname, "/");
465 strcat (newname, linkbuf);
466 ret = elf_w (load_debuglink) (newname, ei, -1);
467 }
468
469 if (ret == -1)
470 {
471 /* No debuglink file found even though .gnu_debuglink existed */
472 ei->image = prev_image;
473 ei->size = prev_size;
474
475 return 0;
476 }
477 else
478 {
479 munmap (prev_image, prev_size);
480 }
481
482 return ret;
483 }
484 }
485
486 return 0;
487 }
488