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"
30 #include <sys/param.h>
34 #endif /* HAVE_LZMA */
37 elf_w (section_table
) (struct elf_image
*ei
)
39 Elf_W (Ehdr
) *ehdr
= ei
->image
;
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
);
51 return (Elf_W (Shdr
) *) ((char *) ei
->image
+ soff
);
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 */
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
);
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
);
82 Debug (16, "strtab=0x%lx\n", (long) str_shdr
->sh_offset
);
83 return ei
->image
+ str_shdr
->sh_offset
;
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
)
93 Elf_W (Ehdr
) *ehdr
= ei
->image
;
94 Elf_W (Sym
) *sym
, *symtab
, *symtab_end
;
97 int i
, ret
= -UNW_ENOINFO
;
100 if (!elf_w (valid_object
) (ei
))
103 shdr
= elf_w (section_table
) (ei
);
107 for (i
= 0; i
< ehdr
->e_shnum
; ++i
)
109 switch (shdr
->sh_type
)
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
);
121 Debug (16, "symtab=0x%lx[%d]\n",
122 (long) shdr
->sh_offset
, shdr
->sh_type
);
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
)
132 if (sym
->st_shndx
!= SHN_ABS
)
134 if (tdep_get_func_addr (as
, val
, &val
) < 0)
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
154 shdr
= (Elf_W (Shdr
) *) (((char *) shdr
) + ehdr
->e_shentsize
);
160 elf_w (get_load_offset
) (struct elf_image
*ei
, unsigned long segbase
,
161 unsigned long mapoff
)
163 Elf_W (Addr
) offset
= 0;
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
;
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
;
190 if (length
< LZMA_STREAM_HEADER_SIZE
)
193 uint8_t *footer
= compressed
+ length
- LZMA_STREAM_HEADER_SIZE
;
194 if (lzma_stream_footer_decode (&options
, footer
) != LZMA_OK
)
197 if (length
< LZMA_STREAM_HEADER_SIZE
+ options
.backward_size
)
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
)
205 if (lzma_index_size (index
) == options
.backward_size
)
207 ret
= lzma_index_uncompressed_size (index
);
210 lzma_index_end (index
, NULL
);
215 elf_w (extract_minidebuginfo
) (struct elf_image
*ei
, struct elf_image
*mdi
)
217 Elf_W (Ehdr
) *ehdr
= ei
->image
;
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
))
228 shdr
= elf_w (section_table
) (ei
);
232 strtab
= elf_w (string_table
) (ei
, ehdr
->e_shstrndx
);
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
);
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
;
255 shdr
= (Elf_W (Shdr
) *) (((char *) shdr
) + ehdr
->e_shentsize
);
262 uncompressed_len
= xz_uncompressed_size (compressed
, compressed_len
);
263 if (uncompressed_len
== 0)
265 Debug (1, "invalid .gnu_debugdata contents\n");
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
)
276 size_t in_pos
= 0, out_pos
= 0;
278 lret
= lzma_stream_buffer_decode (&memlimit
, 0, NULL
,
279 compressed
, &in_pos
, compressed_len
,
280 mdi
->image
, &out_pos
, mdi
->size
);
283 Debug (1, "LZMA decompression failed: %d\n", lret
);
284 munmap (mdi
->image
, mdi
->size
);
292 elf_w (extract_minidebuginfo
) (struct elf_image
*ei
, struct elf_image
*mdi
)
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... */
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
,
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;
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
,
325 /* Closer symbol was found (possibly truncated). */
326 if (ret_mdi
== 0 || ret_mdi
== -UNW_ENOMEM
)
331 munmap (mdi
.image
, mdi
.size
);
334 if (min_dist
>= ei
->size
)
335 return -UNW_ENOINFO
; /* not found */
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
;
349 ret
= tdep_get_elf_image (&ei
, pid
, ip
, &segbase
, &mapoff
, NULL
, 0);
353 ret
= elf_w (get_proc_name_in_image
) (as
, &ei
, segbase
, mapoff
, ip
, buf
, buf_len
, offp
);
355 munmap (ei
.image
, ei
.size
);