mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-12-23 03:53:43 +01:00
Merge branch 'coredump2' of https://github.com/adsharma/libunwind
Conflicts: src/ptrace/_UPT_find_proc_info.c
This commit is contained in:
commit
fa5409387c
37 changed files with 2473 additions and 425 deletions
|
@ -1,4 +1,6 @@
|
|||
include_HEADERS = include/libunwind-dynamic.h include/libunwind-ptrace.h
|
||||
include_HEADERS = include/libunwind-dynamic.h \
|
||||
include/libunwind-ptrace.h \
|
||||
include/libunwind-coredump.h
|
||||
|
||||
if ARCH_ARM
|
||||
include_HEADERS += include/libunwind-arm.h
|
||||
|
|
|
@ -37,13 +37,14 @@ dnl Checks for header files.
|
|||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS(asm/ptrace_offsets.h endian.h sys/endian.h execinfo.h \
|
||||
ia64intrin.h sys/uc_access.h unistd.h signal.h sys/types.h \
|
||||
sys/procfs.h sys/ptrace.h)
|
||||
sys/procfs.h sys/ptrace.h byteswap.h)
|
||||
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_TYPE_SIGNAL
|
||||
AC_TYPE_SIZE_T
|
||||
AC_CHECK_SIZEOF(off_t)
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
|
||||
|
@ -54,6 +55,12 @@ AC_CHECK_TYPES([sighandler_t], [], [],
|
|||
# include <signal.h>
|
||||
#endif
|
||||
])
|
||||
AC_CHECK_TYPES([struct elf_prstatus, struct prstatus], [], [],
|
||||
[$ac_includes_default
|
||||
#if HAVE_SYS_PROCFS_H
|
||||
# include <sys/procfs.h>
|
||||
#endif
|
||||
])
|
||||
|
||||
AC_CHECK_DECLS([PTRACE_POKEUSER, PTRACE_POKEDATA,
|
||||
PTRACE_TRACEME, PTRACE_CONT, PTRACE_SINGLESTEP,
|
||||
|
|
|
@ -37,6 +37,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|||
#include <libunwind.h>
|
||||
|
||||
struct dwarf_cursor; /* forward-declaration */
|
||||
struct elf_dyn_info;
|
||||
|
||||
#include "dwarf-config.h"
|
||||
#ifndef UNW_REMOTE_ONLY
|
||||
|
@ -369,6 +370,7 @@ struct dwarf_callback_data
|
|||
#define dwarf_find_proc_info UNW_OBJ (dwarf_find_proc_info)
|
||||
#define dwarf_find_debug_frame UNW_OBJ (dwarf_find_debug_frame)
|
||||
#define dwarf_search_unwind_table UNW_OBJ (dwarf_search_unwind_table)
|
||||
#define dwarf_find_unwind_table UNW_OBJ (dwarf_find_unwind_table)
|
||||
#define dwarf_put_unwind_info UNW_OBJ (dwarf_put_unwind_info)
|
||||
#define dwarf_put_unwind_info UNW_OBJ (dwarf_put_unwind_info)
|
||||
#define dwarf_eval_expr UNW_OBJ (dwarf_eval_expr)
|
||||
|
@ -396,6 +398,9 @@ extern int dwarf_search_unwind_table (unw_addr_space_t as,
|
|||
unw_dyn_info_t *di,
|
||||
unw_proc_info_t *pi,
|
||||
int need_unwind_info, void *arg);
|
||||
extern int dwarf_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as,
|
||||
char *path, unw_word_t segbase, unw_word_t mapoff,
|
||||
unw_word_t ip);
|
||||
extern void dwarf_put_unwind_info (unw_addr_space_t as,
|
||||
unw_proc_info_t *pi, void *arg);
|
||||
extern int dwarf_eval_expr (struct dwarf_cursor *c, unw_word_t *addr,
|
||||
|
|
73
include/libunwind-coredump.h
Normal file
73
include/libunwind-coredump.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#ifndef libunwind_coredump_h
|
||||
#define libunwind_coredump_h
|
||||
|
||||
#include <libunwind.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Helper routines which make it easy to use libunwind on a coredump.
|
||||
They're available only if UNW_REMOTE_ONLY is _not_ defined and they
|
||||
aren't really part of the libunwind API. They are implemented in a
|
||||
archive library called libunwind-coredump.a. */
|
||||
|
||||
struct UCD_info;
|
||||
|
||||
extern struct UCD_info *_UCD_create(const char *filename);
|
||||
extern void _UCD_destroy(struct UCD_info *);
|
||||
|
||||
extern int _UCD_get_num_threads(struct UCD_info *);
|
||||
extern void _UCD_select_thread(struct UCD_info *, int);
|
||||
extern pid_t _UCD_get_pid(struct UCD_info *);
|
||||
extern int _UCD_get_cursig(struct UCD_info *);
|
||||
extern int _UCD_add_backing_file_at_segment(struct UCD_info *, int phdr_no, const char *filename);
|
||||
extern int _UCD_add_backing_file_at_vaddr(struct UCD_info *,
|
||||
unsigned long vaddr,
|
||||
const char *filename);
|
||||
|
||||
extern int _UCD_find_proc_info (unw_addr_space_t, unw_word_t,
|
||||
unw_proc_info_t *, int, void *);
|
||||
extern void _UCD_put_unwind_info (unw_addr_space_t, unw_proc_info_t *, void *);
|
||||
extern int _UCD_get_dyn_info_list_addr (unw_addr_space_t, unw_word_t *,
|
||||
void *);
|
||||
extern int _UCD_access_mem (unw_addr_space_t, unw_word_t, unw_word_t *, int,
|
||||
void *);
|
||||
extern int _UCD_access_reg (unw_addr_space_t, unw_regnum_t, unw_word_t *,
|
||||
int, void *);
|
||||
extern int _UCD_access_fpreg (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
|
||||
int, void *);
|
||||
extern int _UCD_get_proc_name (unw_addr_space_t, unw_word_t, char *, size_t,
|
||||
unw_word_t *, void *);
|
||||
extern int _UCD_resume (unw_addr_space_t, unw_cursor_t *, void *);
|
||||
extern unw_accessors_t _UCD_accessors;
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* libunwind_coredump_h */
|
|
@ -303,6 +303,32 @@ struct elf_image
|
|||
size_t size; /* (file-) size of the image */
|
||||
};
|
||||
|
||||
struct elf_dyn_info
|
||||
{
|
||||
struct elf_image ei;
|
||||
unw_dyn_info_t di_cache;
|
||||
unw_dyn_info_t di_debug; /* additional table info for .debug_frame */
|
||||
#if UNW_TARGET_IA64
|
||||
unw_dyn_info_t ktab;
|
||||
#endif
|
||||
#if UNW_TARGET_ARM
|
||||
unw_dyn_info_t di_arm; /* additional table info for .ARM.exidx */
|
||||
#endif
|
||||
};
|
||||
|
||||
static void inline invalidate_edi (struct elf_dyn_info *edi)
|
||||
{
|
||||
if (edi->ei.image)
|
||||
munmap (edi->ei.image, edi->ei.size);
|
||||
memset (edi, 0, sizeof (*edi));
|
||||
edi->di_cache.format = -1;
|
||||
edi->di_debug.format = -1;
|
||||
#if UNW_TARGET_ARM
|
||||
edi->di_arm.format = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Provide a place holder for architecture to override for fast access
|
||||
to memory when known not to need to validate and know the access
|
||||
will be local to the process. A suitable override will improve
|
||||
|
|
|
@ -8,7 +8,7 @@ COMMON_SO_LDFLAGS = -XCClinker -nostartfiles
|
|||
lib_LIBRARIES =
|
||||
lib_LTLIBRARIES =
|
||||
if !REMOTE_ONLY
|
||||
lib_LIBRARIES += libunwind-ptrace.a
|
||||
lib_LIBRARIES += libunwind-ptrace.a libunwind-coredump.a
|
||||
lib_LTLIBRARIES += libunwind.la
|
||||
endif
|
||||
|
||||
|
@ -26,6 +26,23 @@ libunwind_ptrace_a_SOURCES = \
|
|||
ptrace/_UPT_reg_offset.c ptrace/_UPT_resume.c
|
||||
noinst_HEADERS += ptrace/_UPT_internal.h
|
||||
|
||||
### libunwind-coredump:
|
||||
libunwind_coredump_a_SOURCES = \
|
||||
coredump/_UCD_accessors.c \
|
||||
coredump/_UCD_create.c \
|
||||
coredump/_UCD_destroy.c \
|
||||
coredump/_UCD_access_mem.c \
|
||||
coredump/_UCD_elf_map_image.c \
|
||||
coredump/_UCD_find_proc_info.c \
|
||||
coredump/_UCD_get_proc_name.c \
|
||||
\
|
||||
coredump/_UPT_elf.c \
|
||||
coredump/_UPT_access_fpreg.c \
|
||||
coredump/_UPT_get_dyn_info_list_addr.c \
|
||||
coredump/_UPT_put_unwind_info.c \
|
||||
coredump/_UPT_resume.c
|
||||
noinst_HEADERS += coredump/_UCD_internal.h
|
||||
|
||||
### libunwind-setjmp:
|
||||
libunwind_setjmp_la_LDFLAGS = $(COMMON_SO_LDFLAGS) \
|
||||
-version-info $(SETJMP_SO_VERSION)
|
||||
|
@ -109,12 +126,14 @@ libunwind_dwarf_common_la_SOURCES = dwarf/global.c
|
|||
|
||||
libunwind_dwarf_local_la_SOURCES = \
|
||||
dwarf/Lexpr.c dwarf/Lfde.c dwarf/Lparser.c dwarf/Lpe.c dwarf/Lstep.c \
|
||||
dwarf/Lfind_proc_info-lsb.c
|
||||
dwarf/Lfind_proc_info-lsb.c \
|
||||
dwarf/Lfind_unwind_table.c
|
||||
libunwind_dwarf_local_la_LIBADD = libunwind-dwarf-common.la
|
||||
|
||||
libunwind_dwarf_generic_la_SOURCES = \
|
||||
dwarf/Gexpr.c dwarf/Gfde.c dwarf/Gparser.c dwarf/Gpe.c dwarf/Gstep.c \
|
||||
dwarf/Gfind_proc_info-lsb.c
|
||||
dwarf/Gfind_proc_info-lsb.c \
|
||||
dwarf/Gfind_unwind_table.c
|
||||
libunwind_dwarf_generic_la_LIBADD = libunwind-dwarf-common.la
|
||||
|
||||
if USE_DWARF
|
||||
|
@ -172,7 +191,7 @@ libunwind_la_SOURCES_ia64 = $(libunwind_la_SOURCES_ia64_common) \
|
|||
ia64/Lglobal.c ia64/Linit.c ia64/Linit_local.c ia64/Linit_remote.c \
|
||||
ia64/Linstall_cursor.S ia64/Lis_signal_frame.c ia64/Lparser.c \
|
||||
ia64/Lrbs.c ia64/Lregs.c ia64/Lresume.c ia64/Lscript.c ia64/Lstep.c \
|
||||
ia64/Ltables.c
|
||||
ia64/Ltables.c ia64/Lfind_unwind_tables.c
|
||||
|
||||
# The list of files that go into libunwind-ia64:
|
||||
libunwind_ia64_la_SOURCES_ia64 = $(libunwind_la_SOURCES_ia64_common) \
|
||||
|
@ -181,7 +200,7 @@ libunwind_ia64_la_SOURCES_ia64 = $(libunwind_la_SOURCES_ia64_common) \
|
|||
ia64/Gglobal.c ia64/Ginit.c ia64/Ginit_local.c ia64/Ginit_remote.c \
|
||||
ia64/Ginstall_cursor.S ia64/Gis_signal_frame.c ia64/Gparser.c \
|
||||
ia64/Grbs.c ia64/Gregs.c ia64/Gresume.c ia64/Gscript.c ia64/Gstep.c \
|
||||
ia64/Gtables.c
|
||||
ia64/Gtables.c ia64/Gfind_unwind_tables.c
|
||||
|
||||
# The list of files that go both into libunwind and libunwind-hppa:
|
||||
noinst_HEADERS += hppa/init.h hppa/offsets.h hppa/unwind_i.h
|
||||
|
@ -346,6 +365,7 @@ if OS_LINUX
|
|||
libunwind_la_SOURCES_x86_os_local = x86/Los-linux.c
|
||||
libunwind_la_SOURCES_x86_64_os = x86_64/Gos-linux.c
|
||||
libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-linux.c
|
||||
libunwind_coredump_a_SOURCES += coredump/_UCD_access_reg_linux.c
|
||||
endif
|
||||
|
||||
if OS_HPUX
|
||||
|
@ -361,6 +381,7 @@ if OS_FREEBSD
|
|||
libunwind_la_SOURCES_x86_os_local = x86/Los-freebsd.c
|
||||
libunwind_la_SOURCES_x86_64_os = x86_64/Gos-freebsd.c
|
||||
libunwind_la_SOURCES_x86_64_os_local = x86_64/Los-freebsd.c
|
||||
libunwind_coredump_a_SOURCES += coredump/_UCD_access_reg_freebsd.c
|
||||
endif
|
||||
|
||||
if ARCH_ARM
|
||||
|
|
8
src/coredump/README
Normal file
8
src/coredump/README
Normal file
|
@ -0,0 +1,8 @@
|
|||
This code is based on "unwinding via ptrace" code from ptrace/
|
||||
directory.
|
||||
|
||||
Files with names starting with _UCD_ are substantially changed
|
||||
from their ptrace/_UPT_... progenitors.
|
||||
|
||||
Files which still have _UPT_... names are either verbiatim copies
|
||||
from ptrace/, or unimplemented stubs.
|
102
src/coredump/_UCD_access_mem.c
Normal file
102
src/coredump/_UCD_access_mem.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *val,
|
||||
int write, void *arg)
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
Debug(0, "%s: write is not supported\n", __func__);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
struct UCD_info *ui = arg;
|
||||
|
||||
unw_word_t addr_last = addr + sizeof(*val)-1;
|
||||
coredump_phdr_t *phdr;
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
phdr = &ui->phdrs[i];
|
||||
if (phdr->p_vaddr <= addr && addr_last < phdr->p_vaddr + phdr->p_memsz)
|
||||
{
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
Debug(1, "%s: addr 0x%llx is unmapped\n",
|
||||
__func__, (unsigned long long)addr
|
||||
);
|
||||
return -UNW_EINVAL;
|
||||
|
||||
found: ;
|
||||
|
||||
const char *filename;
|
||||
off_t fileofs;
|
||||
int fd;
|
||||
if (addr_last >= phdr->p_vaddr + phdr->p_filesz)
|
||||
{
|
||||
/* This part of mapped address space is not present in coredump file */
|
||||
/* Do we have it in the backup file? */
|
||||
if (phdr->backing_fd < 0)
|
||||
{
|
||||
Debug(1, "%s: access to not-present data in phdr[%d]: addr:0x%llx\n",
|
||||
__func__, i, (unsigned long long)addr
|
||||
);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
filename = phdr->backing_filename;
|
||||
fileofs = addr - phdr->p_vaddr;
|
||||
fd = phdr->backing_fd;
|
||||
goto read;
|
||||
}
|
||||
|
||||
filename = ui->coredump_filename;
|
||||
fileofs = phdr->p_offset + (addr - phdr->p_vaddr);
|
||||
fd = ui->coredump_fd;
|
||||
read:
|
||||
if (lseek(fd, fileofs, SEEK_SET) != fileofs)
|
||||
goto read_error;
|
||||
if (read(fd, val, sizeof(*val)) != sizeof(*val))
|
||||
goto read_error;
|
||||
|
||||
Debug(1, "%s: 0x%llx <- [addr:0x%llx fileofs:0x%llx]\n",
|
||||
__func__,
|
||||
(unsigned long long)(*val),
|
||||
(unsigned long long)addr,
|
||||
(unsigned long long)fileofs
|
||||
);
|
||||
return 0;
|
||||
|
||||
read_error:
|
||||
Debug(1, "%s: access out of file: addr:0x%llx fileofs:%llx file:'%s'\n",
|
||||
__func__,
|
||||
(unsigned long long)addr,
|
||||
(unsigned long long)fileofs,
|
||||
filename
|
||||
);
|
||||
return -UNW_EINVAL;
|
||||
}
|
118
src/coredump/_UCD_access_reg_freebsd.c
Normal file
118
src/coredump/_UCD_access_reg_freebsd.c
Normal file
|
@ -0,0 +1,118 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_access_reg (unw_addr_space_t as,
|
||||
unw_regnum_t regnum, unw_word_t *valp,
|
||||
int write, void *arg)
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
Debug(0, "%s: write is not supported\n", __func__);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
struct UCD_info *ui = arg;
|
||||
|
||||
#if defined(UNW_TARGET_X86)
|
||||
switch (regnum) {
|
||||
case UNW_X86_EAX:
|
||||
*valp = ui->prstatus->pr_reg.r_eax;
|
||||
break;
|
||||
case UNW_X86_EDX:
|
||||
*valp = ui->prstatus->pr_reg.r_edx;
|
||||
break;
|
||||
case UNW_X86_ECX:
|
||||
*valp = ui->prstatus->pr_reg.r_ecx;
|
||||
break;
|
||||
case UNW_X86_EBX:
|
||||
*valp = ui->prstatus->pr_reg.r_ebx;
|
||||
break;
|
||||
case UNW_X86_ESI:
|
||||
*valp = ui->prstatus->pr_reg.r_esi;
|
||||
break;
|
||||
case UNW_X86_EDI:
|
||||
*valp = ui->prstatus->pr_reg.r_edi;
|
||||
break;
|
||||
case UNW_X86_EBP:
|
||||
*valp = ui->prstatus->pr_reg.r_ebp;
|
||||
break;
|
||||
case UNW_X86_ESP:
|
||||
*valp = ui->prstatus->pr_reg.r_esp;
|
||||
break;
|
||||
case UNW_X86_EIP:
|
||||
*valp = ui->prstatus->pr_reg.r_eip;
|
||||
break;
|
||||
case UNW_X86_EFLAGS:
|
||||
*valp = ui->prstatus->pr_reg.r_eflags;
|
||||
break;
|
||||
case UNW_X86_TRAPNO:
|
||||
*valp = ui->prstatus->pr_reg.r_trapno;
|
||||
break;
|
||||
default:
|
||||
Debug(0, "%s: bad regnum:%d\n", __func__, regnum);
|
||||
return -UNW_EINVAL;
|
||||
};
|
||||
#elif defined(UNW_TARGET_X86_64)
|
||||
switch (regnum) {
|
||||
case UNW_X86_64_RAX:
|
||||
*valp = ui->prstatus->pr_reg.r_rax;
|
||||
break;
|
||||
case UNW_X86_64_RDX:
|
||||
*valp = ui->prstatus->pr_reg.r_rdx;
|
||||
break;
|
||||
case UNW_X86_64_RCX:
|
||||
*valp = ui->prstatus->pr_reg.r_rcx;
|
||||
break;
|
||||
case UNW_X86_64_RBX:
|
||||
*valp = ui->prstatus->pr_reg.r_rbx;
|
||||
break;
|
||||
case UNW_X86_64_RSI:
|
||||
*valp = ui->prstatus->pr_reg.r_rsi;
|
||||
break;
|
||||
case UNW_X86_64_RDI:
|
||||
*valp = ui->prstatus->pr_reg.r_rdi;
|
||||
break;
|
||||
case UNW_X86_64_RBP:
|
||||
*valp = ui->prstatus->pr_reg.r_rbp;
|
||||
break;
|
||||
case UNW_X86_64_RSP:
|
||||
*valp = ui->prstatus->pr_reg.r_rsp;
|
||||
break;
|
||||
case UNW_X86_64_RIP:
|
||||
*valp = ui->prstatus->pr_reg.r_rip;
|
||||
break;
|
||||
default:
|
||||
Debug(0, "%s: bad regnum:%d\n", __func__, regnum);
|
||||
return -UNW_EINVAL;
|
||||
};
|
||||
#else
|
||||
#error Port me
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
90
src/coredump/_UCD_access_reg_linux.c
Normal file
90
src/coredump/_UCD_access_reg_linux.c
Normal file
|
@ -0,0 +1,90 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_access_reg (unw_addr_space_t as,
|
||||
unw_regnum_t regnum, unw_word_t *valp,
|
||||
int write, void *arg)
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
Debug(0, "%s: write is not supported\n", __func__);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
#if defined(UNW_TARGET_X86)
|
||||
static const uint8_t remap_regs[] =
|
||||
{
|
||||
/* names from libunwind-x86.h */
|
||||
[UNW_X86_EAX] = offsetof(struct user_regs_struct, eax) / sizeof(long),
|
||||
[UNW_X86_EDX] = offsetof(struct user_regs_struct, edx) / sizeof(long),
|
||||
[UNW_X86_ECX] = offsetof(struct user_regs_struct, ecx) / sizeof(long),
|
||||
[UNW_X86_EBX] = offsetof(struct user_regs_struct, ebx) / sizeof(long),
|
||||
[UNW_X86_ESI] = offsetof(struct user_regs_struct, esi) / sizeof(long),
|
||||
[UNW_X86_EDI] = offsetof(struct user_regs_struct, edi) / sizeof(long),
|
||||
[UNW_X86_EBP] = offsetof(struct user_regs_struct, ebp) / sizeof(long),
|
||||
[UNW_X86_ESP] = offsetof(struct user_regs_struct, esp) / sizeof(long),
|
||||
[UNW_X86_EIP] = offsetof(struct user_regs_struct, eip) / sizeof(long),
|
||||
[UNW_X86_EFLAGS] = offsetof(struct user_regs_struct, eflags) / sizeof(long),
|
||||
[UNW_X86_TRAPNO] = offsetof(struct user_regs_struct, orig_eax) / sizeof(long),
|
||||
};
|
||||
#elif defined(UNW_TARGET_X86_64)
|
||||
static const int8_t remap_regs[] =
|
||||
{
|
||||
[UNW_X86_64_RAX] = offsetof(struct user_regs_struct, rax) / sizeof(long),
|
||||
[UNW_X86_64_RDX] = offsetof(struct user_regs_struct, rdx) / sizeof(long),
|
||||
[UNW_X86_64_RCX] = offsetof(struct user_regs_struct, rcx) / sizeof(long),
|
||||
[UNW_X86_64_RBX] = offsetof(struct user_regs_struct, rbx) / sizeof(long),
|
||||
[UNW_X86_64_RSI] = offsetof(struct user_regs_struct, rsi) / sizeof(long),
|
||||
[UNW_X86_64_RDI] = offsetof(struct user_regs_struct, rdi) / sizeof(long),
|
||||
[UNW_X86_64_RBP] = offsetof(struct user_regs_struct, rbp) / sizeof(long),
|
||||
[UNW_X86_64_RSP] = offsetof(struct user_regs_struct, rsp) / sizeof(long),
|
||||
[UNW_X86_64_RIP] = offsetof(struct user_regs_struct, rip) / sizeof(long),
|
||||
};
|
||||
#else
|
||||
#error Port me
|
||||
#endif
|
||||
|
||||
struct UCD_info *ui = arg;
|
||||
if (regnum < 0 || regnum >= (unw_regnum_t)ARRAY_SIZE(remap_regs))
|
||||
{
|
||||
Debug(0, "%s: bad regnum:%d\n", __func__, regnum);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
regnum = remap_regs[regnum];
|
||||
|
||||
/* pr_reg is a long[] array, but it contains struct user_regs_struct's
|
||||
* image.
|
||||
*/
|
||||
Debug(1, "pr_reg[%d]:%ld (0x%lx)", regnum,
|
||||
(long)ui->prstatus->pr_reg[regnum],
|
||||
(long)ui->prstatus->pr_reg[regnum]
|
||||
);
|
||||
*valp = ui->prstatus->pr_reg[regnum];
|
||||
|
||||
return 0;
|
||||
}
|
36
src/coredump/_UCD_accessors.c
Normal file
36
src/coredump/_UCD_accessors.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
PROTECTED unw_accessors_t _UCD_accessors =
|
||||
{
|
||||
.find_proc_info = _UCD_find_proc_info,
|
||||
.put_unwind_info = _UCD_put_unwind_info,
|
||||
.get_dyn_info_list_addr = _UCD_get_dyn_info_list_addr,
|
||||
.access_mem = _UCD_access_mem,
|
||||
.access_reg = _UCD_access_reg,
|
||||
.access_fpreg = _UCD_access_fpreg,
|
||||
.resume = _UCD_resume,
|
||||
.get_proc_name = _UCD_get_proc_name
|
||||
};
|
417
src/coredump/_UCD_create.c
Normal file
417
src/coredump/_UCD_create.c
Normal file
|
@ -0,0 +1,417 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
/* Endian detection */
|
||||
#include <limits.h>
|
||||
#if defined(HAVE_BYTESWAP_H)
|
||||
#include <byteswap.h>
|
||||
#endif
|
||||
#if defined(HAVE_ENDIAN_H)
|
||||
# include <endian.h>
|
||||
#elif defined(HAVE_SYS_ENDIAN_H)
|
||||
# include <sys/endian.h>
|
||||
#endif
|
||||
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define WE_ARE_BIG_ENDIAN 1
|
||||
# define WE_ARE_LITTLE_ENDIAN 0
|
||||
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define WE_ARE_BIG_ENDIAN 0
|
||||
# define WE_ARE_LITTLE_ENDIAN 1
|
||||
#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN
|
||||
# define WE_ARE_BIG_ENDIAN 1
|
||||
# define WE_ARE_LITTLE_ENDIAN 0
|
||||
#elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN
|
||||
# define WE_ARE_BIG_ENDIAN 0
|
||||
# define WE_ARE_LITTLE_ENDIAN 1
|
||||
#elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
|
||||
# define WE_ARE_BIG_ENDIAN 1
|
||||
# define WE_ARE_LITTLE_ENDIAN 0
|
||||
#elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
|
||||
# define WE_ARE_BIG_ENDIAN 0
|
||||
# define WE_ARE_LITTLE_ENDIAN 1
|
||||
#elif defined(__386__)
|
||||
# define WE_ARE_BIG_ENDIAN 0
|
||||
# define WE_ARE_LITTLE_ENDIAN 1
|
||||
#else
|
||||
# error "Can't determine endianness"
|
||||
#endif
|
||||
|
||||
#include <elf.h>
|
||||
#include <sys/procfs.h> /* struct elf_prstatus */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
#define NOTE_DATA(_hdr) STRUCT_MEMBER_P((_hdr), sizeof (Elf32_Nhdr) + ALIGN((_hdr)->n_namesz, 4))
|
||||
#define NOTE_SIZE(_hdr) (sizeof (Elf32_Nhdr) + ALIGN((_hdr)->n_namesz, 4) + (_hdr)->n_descsz)
|
||||
#define NOTE_NEXT(_hdr) STRUCT_MEMBER_P((_hdr), NOTE_SIZE(_hdr))
|
||||
#define NOTE_FITS_IN(_hdr, _size) ((_size) >= sizeof (Elf32_Nhdr) && (_size) >= NOTE_SIZE (_hdr))
|
||||
#define NOTE_FITS(_hdr, _end) NOTE_FITS_IN((_hdr), (unsigned long)((char *)(_end) - (char *)(_hdr)))
|
||||
|
||||
struct UCD_info *
|
||||
_UCD_create(const char *filename)
|
||||
{
|
||||
union
|
||||
{
|
||||
Elf32_Ehdr h32;
|
||||
Elf64_Ehdr h64;
|
||||
} elf_header;
|
||||
#define elf_header32 elf_header.h32
|
||||
#define elf_header64 elf_header.h64
|
||||
bool _64bits;
|
||||
|
||||
struct UCD_info *ui = memset(malloc(sizeof(*ui)), 0, sizeof(*ui));
|
||||
ui->edi.di_cache.format = -1;
|
||||
ui->edi.di_debug.format = -1;
|
||||
#if UNW_TARGET_IA64
|
||||
ui->edi.ktab.format = -1;
|
||||
#endif
|
||||
|
||||
int fd = ui->coredump_fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
goto err;
|
||||
ui->coredump_filename = strdup(filename);
|
||||
|
||||
/* No sane ELF32 file is going to be smaller then ELF64 _header_,
|
||||
* so let's just read 64-bit sized one.
|
||||
*/
|
||||
if (read(fd, &elf_header64, sizeof(elf_header64)) != sizeof(elf_header64))
|
||||
{
|
||||
Debug(0, "'%s' is not an ELF file\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (memcmp(&elf_header32, "\x7f""ELF", 4) != 0)
|
||||
{
|
||||
Debug(0, "'%s' is not an ELF file\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (elf_header32.e_ident[EI_CLASS] != ELFCLASS32
|
||||
&& elf_header32.e_ident[EI_CLASS] != ELFCLASS64)
|
||||
{
|
||||
Debug(0, "'%s' is not a 32/64 bit ELF file\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (WE_ARE_LITTLE_ENDIAN != (elf_header32.e_ident[EI_DATA] == ELFDATA2LSB))
|
||||
{
|
||||
Debug(0, "'%s' is endian-incompatible\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
_64bits = (elf_header32.e_ident[EI_CLASS] == ELFCLASS64);
|
||||
if (_64bits && sizeof(elf_header64.e_entry) > sizeof(off_t))
|
||||
{
|
||||
Debug(0, "Can't process '%s': 64-bit file "
|
||||
"while only %ld bits are supported",
|
||||
filename, 8L * sizeof(off_t));
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* paranoia check */
|
||||
if (_64bits
|
||||
? 0 /* todo: (elf_header64.e_ehsize != NN || elf_header64.e_phentsize != NN) */
|
||||
: (elf_header32.e_ehsize != 52 || elf_header32.e_phentsize != 32)
|
||||
)
|
||||
{
|
||||
Debug(0, "'%s' has wrong e_ehsize or e_phentsize\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
off_t ofs = (_64bits ? elf_header64.e_phoff : elf_header32.e_phoff);
|
||||
if (lseek(fd, ofs, SEEK_SET) != ofs)
|
||||
{
|
||||
Debug(0, "Can't read phdrs from '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
unsigned size = ui->phdrs_count = (_64bits ? elf_header64.e_phnum : elf_header32.e_phnum);
|
||||
coredump_phdr_t *phdrs = ui->phdrs = memset(malloc(size * sizeof(phdrs[0])), 0, size * sizeof(phdrs[0]));
|
||||
if (_64bits)
|
||||
{
|
||||
coredump_phdr_t *cur = phdrs;
|
||||
unsigned i = 0;
|
||||
while (i < size)
|
||||
{
|
||||
Elf64_Phdr hdr64;
|
||||
if (read(fd, &hdr64, sizeof(hdr64)) != sizeof(hdr64))
|
||||
{
|
||||
Debug(0, "Can't read phdrs from '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
cur->p_type = hdr64.p_type ;
|
||||
cur->p_flags = hdr64.p_flags ;
|
||||
cur->p_offset = hdr64.p_offset;
|
||||
cur->p_vaddr = hdr64.p_vaddr ;
|
||||
/*cur->p_paddr = hdr32.p_paddr ; always 0 */
|
||||
//TODO: check that and abort if it isn't?
|
||||
cur->p_filesz = hdr64.p_filesz;
|
||||
cur->p_memsz = hdr64.p_memsz ;
|
||||
cur->p_align = hdr64.p_align ;
|
||||
/* cur->backing_filename = NULL; - done by memset */
|
||||
cur->backing_fd = -1;
|
||||
cur->backing_filesize = hdr64.p_filesz;
|
||||
i++;
|
||||
cur++;
|
||||
}
|
||||
} else {
|
||||
coredump_phdr_t *cur = phdrs;
|
||||
unsigned i = 0;
|
||||
while (i < size)
|
||||
{
|
||||
Elf32_Phdr hdr32;
|
||||
if (read(fd, &hdr32, sizeof(hdr32)) != sizeof(hdr32))
|
||||
{
|
||||
Debug(0, "Can't read phdrs from '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
cur->p_type = hdr32.p_type ;
|
||||
cur->p_flags = hdr32.p_flags ;
|
||||
cur->p_offset = hdr32.p_offset;
|
||||
cur->p_vaddr = hdr32.p_vaddr ;
|
||||
/*cur->p_paddr = hdr32.p_paddr ; always 0 */
|
||||
cur->p_filesz = hdr32.p_filesz;
|
||||
cur->p_memsz = hdr32.p_memsz ;
|
||||
cur->p_align = hdr32.p_align ;
|
||||
/* cur->backing_filename = NULL; - done by memset */
|
||||
cur->backing_fd = -1;
|
||||
cur->backing_filesize = hdr32.p_memsz;
|
||||
i++;
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned i = 0;
|
||||
coredump_phdr_t *cur = phdrs;
|
||||
while (i < size)
|
||||
{
|
||||
Debug(2, "phdr[%03d]: type:%d", i, cur->p_type);
|
||||
if (cur->p_type == PT_NOTE)
|
||||
{
|
||||
Elf32_Nhdr *note_hdr, *note_end;
|
||||
unsigned n_threads;
|
||||
|
||||
ui->note_phdr = malloc(cur->p_filesz);
|
||||
if (lseek(fd, cur->p_offset, SEEK_SET) != (off_t)cur->p_offset
|
||||
|| (uoff_t)read(fd, ui->note_phdr, cur->p_filesz) != cur->p_filesz)
|
||||
{
|
||||
Debug(0, "Can't read PT_NOTE from '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
note_end = STRUCT_MEMBER_P (ui->note_phdr, cur->p_filesz);
|
||||
|
||||
/* Count number of threads */
|
||||
n_threads = 0;
|
||||
note_hdr = (Elf32_Nhdr *)ui->note_phdr;
|
||||
while (NOTE_FITS (note_hdr, note_end))
|
||||
{
|
||||
if (note_hdr->n_type == NT_PRSTATUS)
|
||||
n_threads++;
|
||||
|
||||
note_hdr = NOTE_NEXT (note_hdr);
|
||||
}
|
||||
|
||||
ui->n_threads = n_threads;
|
||||
ui->threads = malloc(sizeof (void *) * n_threads);
|
||||
|
||||
n_threads = 0;
|
||||
note_hdr = (Elf32_Nhdr *)ui->note_phdr;
|
||||
while (NOTE_FITS (note_hdr, note_end))
|
||||
{
|
||||
if (note_hdr->n_type == NT_PRSTATUS)
|
||||
ui->threads[n_threads++] = NOTE_DATA (note_hdr);
|
||||
|
||||
note_hdr = NOTE_NEXT (note_hdr);
|
||||
}
|
||||
}
|
||||
if (cur->p_type == PT_LOAD)
|
||||
{
|
||||
Debug(2, " ofs:%08llx va:%08llx filesize:%08llx memsize:%08llx flg:%x",
|
||||
(unsigned long long) cur->p_offset,
|
||||
(unsigned long long) cur->p_vaddr,
|
||||
(unsigned long long) cur->p_filesz,
|
||||
(unsigned long long) cur->p_memsz,
|
||||
cur->p_flags
|
||||
);
|
||||
if (cur->p_filesz < cur->p_memsz)
|
||||
Debug(2, " partial");
|
||||
if (cur->p_flags & PF_X)
|
||||
Debug(2, " executable");
|
||||
}
|
||||
Debug(2, "\n");
|
||||
i++;
|
||||
cur++;
|
||||
}
|
||||
|
||||
if (ui->n_threads == 0)
|
||||
{
|
||||
Debug(0, "No NT_PRSTATUS note found in '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ui->prstatus = ui->threads[0];
|
||||
|
||||
return ui;
|
||||
|
||||
err:
|
||||
_UCD_destroy(ui);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int _UCD_get_num_threads(struct UCD_info *ui)
|
||||
{
|
||||
return ui->n_threads;
|
||||
}
|
||||
|
||||
void _UCD_select_thread(struct UCD_info *ui, int n)
|
||||
{
|
||||
if (n >= 0 && n < ui->n_threads)
|
||||
ui->prstatus = ui->threads[n];
|
||||
}
|
||||
|
||||
pid_t _UCD_get_pid(struct UCD_info *ui)
|
||||
{
|
||||
return ui->prstatus->pr_pid;
|
||||
}
|
||||
|
||||
int _UCD_get_cursig(struct UCD_info *ui)
|
||||
{
|
||||
return ui->prstatus->pr_cursig;
|
||||
}
|
||||
|
||||
int _UCD_add_backing_file_at_segment(struct UCD_info *ui, int phdr_no, const char *filename)
|
||||
{
|
||||
if ((unsigned)phdr_no >= ui->phdrs_count)
|
||||
{
|
||||
Debug(0, "There is no segment %d in this coredump\n", phdr_no);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct coredump_phdr *phdr = &ui->phdrs[phdr_no];
|
||||
if (phdr->backing_filename)
|
||||
{
|
||||
Debug(0, "Backing file already added to segment %d\n", phdr_no);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
Debug(0, "Can't open '%s'\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
phdr->backing_fd = fd;
|
||||
phdr->backing_filename = strdup(filename);
|
||||
|
||||
struct stat statbuf;
|
||||
if (fstat(fd, &statbuf) != 0)
|
||||
{
|
||||
Debug(0, "Can't stat '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
phdr->backing_filesize = (uoff_t)statbuf.st_size;
|
||||
|
||||
if (phdr->p_flags != (PF_X | PF_R))
|
||||
Debug(1, "Note: phdr[%u] is not r-x: flags are 0x%x\n", phdr_no, phdr->p_flags);
|
||||
|
||||
if (phdr->backing_filesize > phdr->p_memsz)
|
||||
{
|
||||
/* This is expected */
|
||||
Debug(2, "Note: phdr[%u] is %lld bytes, file is larger: %lld bytes\n",
|
||||
phdr_no,
|
||||
(unsigned long long)phdr->p_memsz,
|
||||
(unsigned long long)phdr->backing_filesize
|
||||
);
|
||||
}
|
||||
//TODO: else loudly complain? Maybe even fail?
|
||||
|
||||
if (phdr->p_filesz != 0)
|
||||
{
|
||||
//TODO: loop and compare in smaller blocks
|
||||
char *core_buf = malloc(phdr->p_filesz);
|
||||
char *file_buf = malloc(phdr->p_filesz);
|
||||
if (lseek(ui->coredump_fd, phdr->p_offset, SEEK_SET) != (off_t)phdr->p_offset
|
||||
|| (uoff_t)read(ui->coredump_fd, core_buf, phdr->p_filesz) != phdr->p_filesz
|
||||
)
|
||||
{
|
||||
Debug(0, "Error reading from coredump file\n");
|
||||
err_read:
|
||||
free(core_buf);
|
||||
free(file_buf);
|
||||
goto err;
|
||||
}
|
||||
if ((uoff_t)read(fd, file_buf, phdr->p_filesz) != phdr->p_filesz)
|
||||
{
|
||||
Debug(0, "Error reading from '%s'\n", filename);
|
||||
goto err_read;
|
||||
}
|
||||
int r = memcmp(core_buf, file_buf, phdr->p_filesz);
|
||||
free(core_buf);
|
||||
free(file_buf);
|
||||
if (r != 0)
|
||||
{
|
||||
Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file do not match\n",
|
||||
phdr_no, (unsigned long long)phdr->p_filesz
|
||||
);
|
||||
} else {
|
||||
Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file match\n",
|
||||
phdr_no, (unsigned long long)phdr->p_filesz
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Success */
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (phdr->backing_fd >= 0)
|
||||
{
|
||||
close(phdr->backing_fd);
|
||||
phdr->backing_fd = -1;
|
||||
}
|
||||
free(phdr->backing_filename);
|
||||
phdr->backing_filename = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _UCD_add_backing_file_at_vaddr(struct UCD_info *ui,
|
||||
unsigned long vaddr,
|
||||
const char *filename)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
struct coredump_phdr *phdr = &ui->phdrs[i];
|
||||
if (phdr->p_vaddr != vaddr)
|
||||
continue;
|
||||
/* It seems to match. Add it. */
|
||||
return _UCD_add_backing_file_at_segment(ui, i, filename);
|
||||
}
|
||||
return -1;
|
||||
}
|
50
src/coredump/_UCD_destroy.c
Normal file
50
src/coredump/_UCD_destroy.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
void
|
||||
_UCD_destroy (struct UCD_info *ui)
|
||||
{
|
||||
if (!ui)
|
||||
return;
|
||||
|
||||
if (ui->coredump_fd >= 0)
|
||||
close(ui->coredump_fd);
|
||||
free(ui->coredump_filename);
|
||||
|
||||
invalidate_edi (&ui->edi);
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
struct coredump_phdr *phdr = &ui->phdrs[i];
|
||||
free(phdr->backing_filename);
|
||||
if (phdr->backing_fd >= 0)
|
||||
close(phdr->backing_fd);
|
||||
}
|
||||
|
||||
free(ui->note_phdr);
|
||||
|
||||
free(ui);
|
||||
}
|
98
src/coredump/_UCD_elf_map_image.c
Normal file
98
src/coredump/_UCD_elf_map_image.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
static coredump_phdr_t *
|
||||
CD_elf_map_image(struct UCD_info *ui, coredump_phdr_t *phdr)
|
||||
{
|
||||
struct elf_image *ei = &ui->edi.ei;
|
||||
|
||||
if (phdr->backing_fd < 0)
|
||||
{
|
||||
/* Note: coredump file contains only phdr->p_filesz bytes.
|
||||
* We want to map bigger area (phdr->p_memsz bytes) to make sure
|
||||
* these pages are allocated, but non-accessible.
|
||||
*/
|
||||
/* addr, length, prot, flags, fd, fd_offset */
|
||||
ei->image = mmap(NULL, phdr->p_memsz, PROT_READ, MAP_PRIVATE, ui->coredump_fd, phdr->p_offset);
|
||||
if (ei->image == MAP_FAILED)
|
||||
{
|
||||
ei->image = NULL;
|
||||
return NULL;
|
||||
}
|
||||
ei->size = phdr->p_filesz;
|
||||
size_t remainder_len = phdr->p_memsz - phdr->p_filesz;
|
||||
if (remainder_len > 0)
|
||||
{
|
||||
void *remainder_base = (char*) ei->image + phdr->p_filesz;
|
||||
munmap(remainder_base, remainder_len);
|
||||
}
|
||||
} else {
|
||||
/* We have a backing file for this segment.
|
||||
* This file is always longer than phdr->p_memsz,
|
||||
* and if phdr->p_filesz !=0, first phdr->p_filesz bytes in coredump
|
||||
* are the same as first bytes in the file. (Thus no need to map coredump)
|
||||
* We map the entire file:
|
||||
* unwinding may need data which is past phdr->p_memsz bytes.
|
||||
*/
|
||||
/* addr, length, prot, flags, fd, fd_offset */
|
||||
ei->image = mmap(NULL, phdr->backing_filesize, PROT_READ, MAP_PRIVATE, phdr->backing_fd, 0);
|
||||
if (ei->image == MAP_FAILED)
|
||||
{
|
||||
ei->image = NULL;
|
||||
return NULL;
|
||||
}
|
||||
ei->size = phdr->backing_filesize;
|
||||
}
|
||||
|
||||
/* Check ELF header for sanity */
|
||||
if (!elf_w(valid_object)(ei))
|
||||
{
|
||||
munmap(ei->image, ei->size);
|
||||
ei->image = NULL;
|
||||
ei->size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return phdr;
|
||||
}
|
||||
|
||||
HIDDEN coredump_phdr_t *
|
||||
_UCD_get_elf_image(struct UCD_info *ui, unw_word_t ip)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
coredump_phdr_t *phdr = &ui->phdrs[i];
|
||||
if (phdr->p_vaddr <= ip && ip < phdr->p_vaddr + phdr->p_memsz)
|
||||
{
|
||||
phdr = CD_elf_map_image(ui, phdr);
|
||||
return phdr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
163
src/coredump/_UCD_find_proc_info.c
Normal file
163
src/coredump/_UCD_find_proc_info.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
static int
|
||||
get_unwind_info(struct UCD_info *ui, unw_addr_space_t as, unw_word_t ip)
|
||||
{
|
||||
unsigned long segbase, mapoff;
|
||||
|
||||
#if UNW_TARGET_IA64 && defined(__linux)
|
||||
if (!ui->edi.ktab.start_ip && _Uia64_get_kernel_table (&ui->edi.ktab) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
if (ui->edi.ktab.format != -1 && ip >= ui->edi.ktab.start_ip && ip < ui->edi.ktab.end_ip)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if ((ui->edi.di_cache.format != -1
|
||||
&& ip >= ui->edi.di_cache.start_ip && ip < ui->edi.di_cache.end_ip)
|
||||
#if UNW_TARGET_ARM
|
||||
|| (ui->edi.di_debug.format != -1
|
||||
&& ip >= ui->edi.di_arm.start_ip && ip < ui->edi.di_arm.end_ip)
|
||||
#endif
|
||||
|| (ui->edi.di_debug.format != -1
|
||||
&& ip >= ui->edi.di_debug.start_ip && ip < ui->edi.di_debug.end_ip))
|
||||
return 0;
|
||||
|
||||
invalidate_edi (&ui->edi);
|
||||
|
||||
/* Used to be tdep_get_elf_image() in ptrace unwinding code */
|
||||
coredump_phdr_t *phdr = _UCD_get_elf_image(ui, ip);
|
||||
if (!phdr)
|
||||
{
|
||||
Debug(1, "%s returns error: _UCD_get_elf_image failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
/* segbase: where it is mapped in virtual memory */
|
||||
/* mapoff: offset in the file */
|
||||
segbase = phdr->p_vaddr;
|
||||
/*mapoff = phdr->p_offset; WRONG! phdr->p_offset is the offset in COREDUMP file */
|
||||
mapoff = 0;
|
||||
///FIXME. text segment is USUALLY, not always, at offset 0 in the binary/.so file.
|
||||
// ensure that at initialization.
|
||||
|
||||
/* Here, SEGBASE is the starting-address of the (mmap'ped) segment
|
||||
which covers the IP we're looking for. */
|
||||
if (dwarf_find_unwind_table(&ui->edi, as, phdr->backing_filename, segbase, mapoff, ip) < 0)
|
||||
{
|
||||
Debug(1, "%s returns error: dwarf_find_unwind_table failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
/* This can happen in corner cases where dynamically generated
|
||||
code falls into the same page that contains the data-segment
|
||||
and the page-offset of the code is within the first page of
|
||||
the executable. */
|
||||
if (ui->edi.di_cache.format != -1
|
||||
&& (ip < ui->edi.di_cache.start_ip || ip >= ui->edi.di_cache.end_ip))
|
||||
ui->edi.di_cache.format = -1;
|
||||
|
||||
if (ui->edi.di_debug.format != -1
|
||||
&& (ip < ui->edi.di_debug.start_ip || ip >= ui->edi.di_debug.end_ip))
|
||||
ui->edi.di_debug.format = -1;
|
||||
|
||||
if (ui->edi.di_cache.format == -1
|
||||
#if UNW_TARGET_ARM
|
||||
&& ui->edi.di_arm.format == -1
|
||||
#endif
|
||||
&& ui->edi.di_debug.format == -1)
|
||||
{
|
||||
Debug(1, "%s returns error: all formats are -1\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
Debug(1, "%s returns success\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_UCD_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
||||
int need_unwind_info, void *arg)
|
||||
{
|
||||
struct UCD_info *ui = arg;
|
||||
|
||||
Debug(1, "%s: entering\n", __func__);
|
||||
|
||||
int ret = -UNW_ENOINFO;
|
||||
|
||||
if (get_unwind_info(ui, as, ip) < 0) {
|
||||
Debug(1, "%s returns error: get_unwind_info failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
#if UNW_TARGET_IA64
|
||||
if (ui->edi.ktab.format != -1)
|
||||
{
|
||||
/* The kernel unwind table resides in local memory, so we have
|
||||
to use the local address space to search it. Since
|
||||
_UCD_put_unwind_info() has no easy way of detecting this
|
||||
case, we simply make a copy of the unwind-info, so
|
||||
_UCD_put_unwind_info() can always free() the unwind-info
|
||||
without ill effects. */
|
||||
ret = tdep_search_unwind_table (unw_local_addr_space, ip, &ui->edi.ktab, pi,
|
||||
need_unwind_info, arg);
|
||||
if (ret >= 0)
|
||||
{
|
||||
if (!need_unwind_info)
|
||||
pi->unwind_info = NULL;
|
||||
else
|
||||
{
|
||||
void *mem = malloc (pi->unwind_info_size);
|
||||
|
||||
if (!mem)
|
||||
return -UNW_ENOMEM;
|
||||
memcpy (mem, pi->unwind_info, pi->unwind_info_size);
|
||||
pi->unwind_info = mem;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_cache.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_cache,
|
||||
pi, need_unwind_info, arg);
|
||||
|
||||
#if UNW_TARGET_ARM
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_arm.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_arm, pi,
|
||||
need_unwind_info, arg);
|
||||
#endif
|
||||
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_debug.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_debug, pi,
|
||||
need_unwind_info, arg);
|
||||
|
||||
Debug(1, "%s: returns %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
70
src/coredump/_UCD_get_proc_name.c
Normal file
70
src/coredump/_UCD_get_proc_name.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
|
||||
/* Find the ELF image that contains IP and return the "closest"
|
||||
procedure name, if there is one. With some caching, this could be
|
||||
sped up greatly, but until an application materializes that's
|
||||
sensitive to the performance of this routine, why bother... */
|
||||
static int
|
||||
elf_w (CD_get_proc_name) (struct UCD_info *ui, unw_addr_space_t as, unw_word_t ip,
|
||||
char *buf, size_t buf_len, unw_word_t *offp)
|
||||
{
|
||||
unsigned long segbase, mapoff;
|
||||
int ret;
|
||||
|
||||
/* Used to be tdep_get_elf_image() in ptrace unwinding code */
|
||||
coredump_phdr_t *cphdr = _UCD_get_elf_image(ui, ip);
|
||||
if (!cphdr)
|
||||
{
|
||||
Debug(1, "%s returns error: _UCD_get_elf_image failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
/* segbase: where it is mapped in virtual memory */
|
||||
/* mapoff: offset in the file */
|
||||
segbase = cphdr->p_vaddr;
|
||||
/*mapoff = phdr->p_offset; WRONG! phdr->p_offset is the offset in COREDUMP file */
|
||||
mapoff = 0;
|
||||
|
||||
ret = elf_w (get_proc_name_in_image) (as, &ui->edi.ei, segbase, mapoff, ip, buf, buf_len, offp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
_UCD_get_proc_name (unw_addr_space_t as, unw_word_t ip,
|
||||
char *buf, size_t buf_len, unw_word_t *offp, void *arg)
|
||||
{
|
||||
struct UCD_info *ui = arg;
|
||||
|
||||
#if ELF_CLASS == ELFCLASS64
|
||||
return _Uelf64_CD_get_proc_name (ui, as, ip, buf, buf_len, offp);
|
||||
#elif ELF_CLASS == ELFCLASS32
|
||||
return _Uelf32_CD_get_proc_name (ui, as, ip, buf, buf_len, offp);
|
||||
#else
|
||||
return -UNW_ENOINFO;
|
||||
#endif
|
||||
}
|
106
src/coredump/_UCD_internal.h
Normal file
106
src/coredump/_UCD_internal.h
Normal file
|
@ -0,0 +1,106 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#ifndef _UCD_internal_h
|
||||
#define _UCD_internal_h
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PROCFS_H
|
||||
#include <sys/procfs.h> /* struct elf_prstatus */
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libunwind-coredump.h>
|
||||
|
||||
#include "libunwind_i.h"
|
||||
|
||||
|
||||
#if SIZEOF_OFF_T == 4
|
||||
typedef uint32_t uoff_t;
|
||||
#elif SIZEOF_OFF_T == 8
|
||||
typedef uint64_t uoff_t;
|
||||
#else
|
||||
# error Unknown size of off_t!
|
||||
#endif
|
||||
|
||||
|
||||
/* Similar to ELF phdrs. p_paddr element is absent,
|
||||
* since it's always 0 in coredumps.
|
||||
*/
|
||||
struct coredump_phdr
|
||||
{
|
||||
uint32_t p_type;
|
||||
uint32_t p_flags;
|
||||
uoff_t p_offset;
|
||||
uoff_t p_vaddr;
|
||||
uoff_t p_filesz;
|
||||
uoff_t p_memsz;
|
||||
uoff_t p_align;
|
||||
/* Data for backing file. If backing_fd < 0, there is no file */
|
||||
uoff_t backing_filesize;
|
||||
char *backing_filename; /* for error meesages only */
|
||||
int backing_fd;
|
||||
};
|
||||
|
||||
typedef struct coredump_phdr coredump_phdr_t;
|
||||
|
||||
#if defined(HAVE_STRUCT_ELF_PRSTATUS)
|
||||
#define PRSTATUS_STRUCT elf_prstatus
|
||||
#elif defined(HAVE_STRUCT_PRSTATUS)
|
||||
#define PRSTATUS_STRUCT prstatus
|
||||
#else
|
||||
#define PRSTATUS_STRUCT non_existent
|
||||
#endif
|
||||
|
||||
struct UCD_info
|
||||
{
|
||||
int big_endian; /* bool */
|
||||
int coredump_fd;
|
||||
char *coredump_filename; /* for error meesages only */
|
||||
coredump_phdr_t *phdrs; /* array, allocated */
|
||||
unsigned phdrs_count;
|
||||
void *note_phdr; /* allocated or NULL */
|
||||
struct PRSTATUS_STRUCT *prstatus; /* points inside note_phdr */
|
||||
int n_threads;
|
||||
struct PRSTATUS_STRUCT **threads;
|
||||
|
||||
struct elf_dyn_info edi;
|
||||
};
|
||||
|
||||
extern coredump_phdr_t * _UCD_get_elf_image(struct UCD_info *ui, unw_word_t ip);
|
||||
|
||||
#define ALIGN(x,a) (((x)+(a)-1UL)&~((a)-1UL))
|
||||
#define STRUCT_MEMBER_P(struct_p, struct_offset) ((void *) ((char*) (struct_p) + (long) (struct_offset)))
|
||||
#define STRUCT_MEMBER(member_type, struct_p, struct_offset) (*(member_type*) STRUCT_MEMBER_P ((struct_p), (struct_offset)))
|
||||
|
||||
#endif
|
57
src/coredump/_UCD_lib.h
Normal file
57
src/coredump/_UCD_lib.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#ifndef _UCD_lib_h
|
||||
#define _UCD_lib_h
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <sys/param.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#endif
|
34
src/coredump/_UPT_access_fpreg.c
Normal file
34
src/coredump/_UPT_access_fpreg.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
|
||||
int write, void *arg)
|
||||
{
|
||||
print_error (__func__);
|
||||
print_error (" not implemented\n");
|
||||
return -UNW_EINVAL;
|
||||
}
|
5
src/coredump/_UPT_elf.c
Normal file
5
src/coredump/_UPT_elf.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
/* We need to get a separate copy of the ELF-code into
|
||||
libunwind-coredump since it cannot (and must not) have any ELF
|
||||
dependencies on libunwind. */
|
||||
#include "libunwind_i.h" /* get ELFCLASS defined */
|
||||
#include "../elfxx.c"
|
108
src/coredump/_UPT_get_dyn_info_list_addr.c
Normal file
108
src/coredump/_UPT_get_dyn_info_list_addr.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003-2005 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
#if UNW_TARGET_IA64 && defined(__linux)
|
||||
# include "elf64.h"
|
||||
# include "os-linux.h"
|
||||
|
||||
static inline int
|
||||
get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg,
|
||||
int *countp)
|
||||
{
|
||||
unsigned long lo, hi, off;
|
||||
struct UPT_info *ui = arg;
|
||||
struct map_iterator mi;
|
||||
char path[PATH_MAX];
|
||||
unw_dyn_info_t *di;
|
||||
unw_word_t res;
|
||||
int count = 0;
|
||||
|
||||
maps_init (&mi, ui->pid);
|
||||
while (maps_next (&mi, &lo, &hi, &off))
|
||||
{
|
||||
if (off)
|
||||
continue;
|
||||
|
||||
invalidate_edi (&ui->edi);
|
||||
|
||||
if (elf_map_image (&ui->ei, path) < 0)
|
||||
/* ignore unmappable stuff like "/SYSV00001b58 (deleted)" */
|
||||
continue;
|
||||
|
||||
Debug (16, "checking object %s\n", path);
|
||||
|
||||
di = dwarf_find_unwind_table (&ui->edi, as, path, lo, off);
|
||||
if (di)
|
||||
{
|
||||
res = _Uia64_find_dyn_list (as, di, arg);
|
||||
if (res && count++ == 0)
|
||||
{
|
||||
Debug (12, "dyn_info_list_addr = 0x%lx\n", (long) res);
|
||||
*dil_addr = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
maps_close (&mi);
|
||||
*countp = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg,
|
||||
int *countp)
|
||||
{
|
||||
# warning Implement get_list_addr(), please.
|
||||
*countp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
_UCD_get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dil_addr,
|
||||
void *arg)
|
||||
{
|
||||
int count, ret;
|
||||
|
||||
Debug (12, "looking for dyn_info list\n");
|
||||
|
||||
if ((ret = get_list_addr (as, dil_addr, arg, &count)) < 0)
|
||||
return ret;
|
||||
|
||||
/* If multiple dynamic-info list addresses are found, we would have
|
||||
to determine which was is the one actually in use (since the
|
||||
dynamic name resolution algorithm will pick one "winner").
|
||||
Perhaps we'd have to track them all until we find one that's
|
||||
non-empty. Hopefully, this case simply will never arise, since
|
||||
only libunwind defines the dynamic info list head. */
|
||||
assert (count <= 1);
|
||||
|
||||
return (count > 0) ? 0 : -UNW_ENOINFO;
|
||||
}
|
36
src/coredump/_UPT_put_unwind_info.c
Normal file
36
src/coredump/_UPT_put_unwind_info.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
void
|
||||
_UCD_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg)
|
||||
{
|
||||
if (!pi->unwind_info)
|
||||
return;
|
||||
free (pi->unwind_info);
|
||||
pi->unwind_info = NULL;
|
||||
}
|
35
src/coredump/_UPT_resume.c
Normal file
35
src/coredump/_UPT_resume.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_resume (unw_addr_space_t as, unw_cursor_t *c, void *arg)
|
||||
{
|
||||
print_error (__func__);
|
||||
print_error (" not implemented\n");
|
||||
return -UNW_EINVAL;
|
||||
}
|
228
src/dwarf/Gfind_unwind_table.c
Normal file
228
src/dwarf/Gfind_unwind_table.c
Normal file
|
@ -0,0 +1,228 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003-2004 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "libunwind_i.h"
|
||||
#include "dwarf-eh.h"
|
||||
#include "dwarf_i.h"
|
||||
|
||||
int
|
||||
dwarf_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as,
|
||||
char *path, unw_word_t segbase, unw_word_t mapoff,
|
||||
unw_word_t ip)
|
||||
{
|
||||
Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
|
||||
unw_word_t addr, eh_frame_start, fde_count, load_base;
|
||||
unw_word_t max_load_addr = 0;
|
||||
unw_word_t start_ip = (unw_word_t) -1;
|
||||
unw_word_t end_ip = 0;
|
||||
struct dwarf_eh_frame_hdr *hdr;
|
||||
unw_proc_info_t pi;
|
||||
unw_accessors_t *a;
|
||||
Elf_W(Ehdr) *ehdr;
|
||||
#if UNW_TARGET_ARM
|
||||
const Elf_W(Phdr) *parm_exidx = NULL;
|
||||
#endif
|
||||
int i, ret, found = 0;
|
||||
|
||||
/* XXX: Much of this code is Linux/LSB-specific. */
|
||||
|
||||
if (!elf_w(valid_object) (&edi->ei))
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
ehdr = edi->ei.image;
|
||||
phdr = (Elf_W(Phdr) *) ((char *) edi->ei.image + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; ++i)
|
||||
{
|
||||
switch (phdr[i].p_type)
|
||||
{
|
||||
case PT_LOAD:
|
||||
if (phdr[i].p_vaddr < start_ip)
|
||||
start_ip = phdr[i].p_vaddr;
|
||||
|
||||
if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
|
||||
end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
|
||||
|
||||
if (phdr[i].p_offset == mapoff)
|
||||
ptxt = phdr + i;
|
||||
if ((uintptr_t) edi->ei.image + phdr->p_filesz > max_load_addr)
|
||||
max_load_addr = (uintptr_t) edi->ei.image + phdr->p_filesz;
|
||||
break;
|
||||
|
||||
case PT_GNU_EH_FRAME:
|
||||
peh_hdr = phdr + i;
|
||||
break;
|
||||
|
||||
case PT_DYNAMIC:
|
||||
pdyn = phdr + i;
|
||||
break;
|
||||
|
||||
#if UNW_TARGET_ARM
|
||||
case PT_ARM_EXIDX:
|
||||
parm_exidx = phdr + i;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ptxt)
|
||||
return 0;
|
||||
|
||||
load_base = segbase - ptxt->p_vaddr;
|
||||
start_ip += load_base;
|
||||
end_ip += load_base;
|
||||
|
||||
if (peh_hdr)
|
||||
{
|
||||
if (pdyn)
|
||||
{
|
||||
/* For dynamicly linked executables and shared libraries,
|
||||
DT_PLTGOT is the value that data-relative addresses are
|
||||
relative to for that object. We call this the "gp". */
|
||||
Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
|
||||
+ (char *) edi->ei.image);
|
||||
for (; dyn->d_tag != DT_NULL; ++dyn)
|
||||
if (dyn->d_tag == DT_PLTGOT)
|
||||
{
|
||||
/* Assume that _DYNAMIC is writable and GLIBC has
|
||||
relocated it (true for x86 at least). */
|
||||
edi->di_cache.gp = dyn->d_un.d_ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
/* Otherwise this is a static executable with no _DYNAMIC. Assume
|
||||
that data-relative addresses are relative to 0, i.e.,
|
||||
absolute. */
|
||||
edi->di_cache.gp = 0;
|
||||
|
||||
hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
|
||||
+ (char *) edi->ei.image);
|
||||
if (hdr->version != DW_EH_VERSION)
|
||||
{
|
||||
Debug (1, "table `%s' has unexpected version %d\n",
|
||||
path, hdr->version);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
a = unw_get_accessors (unw_local_addr_space);
|
||||
addr = (unw_word_t) (hdr + 1);
|
||||
|
||||
/* Fill in a dummy proc_info structure. We just need to fill in
|
||||
enough to ensure that dwarf_read_encoded_pointer() can do it's
|
||||
job. Since we don't have a procedure-context at this point, all
|
||||
we have to do is fill in the global-pointer. */
|
||||
memset (&pi, 0, sizeof (pi));
|
||||
pi.gp = edi->di_cache.gp;
|
||||
|
||||
/* (Optionally) read eh_frame_ptr: */
|
||||
if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
|
||||
&addr, hdr->eh_frame_ptr_enc, &pi,
|
||||
&eh_frame_start, NULL)) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
/* (Optionally) read fde_count: */
|
||||
if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
|
||||
&addr, hdr->fde_count_enc, &pi,
|
||||
&fde_count, NULL)) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
|
||||
{
|
||||
#if 1
|
||||
abort ();
|
||||
#else
|
||||
unw_word_t eh_frame_end;
|
||||
|
||||
/* If there is no search table or it has an unsupported
|
||||
encoding, fall back on linear search. */
|
||||
if (hdr->table_enc == DW_EH_PE_omit)
|
||||
Debug (4, "EH lacks search table; doing linear search\n");
|
||||
else
|
||||
Debug (4, "EH table has encoding 0x%x; doing linear search\n",
|
||||
hdr->table_enc);
|
||||
|
||||
eh_frame_end = max_load_addr; /* XXX can we do better? */
|
||||
|
||||
if (hdr->fde_count_enc == DW_EH_PE_omit)
|
||||
fde_count = ~0UL;
|
||||
if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
|
||||
abort ();
|
||||
|
||||
return linear_search (unw_local_addr_space, ip,
|
||||
eh_frame_start, eh_frame_end, fde_count,
|
||||
pi, need_unwind_info, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
edi->di_cache.start_ip = start_ip;
|
||||
edi->di_cache.end_ip = end_ip;
|
||||
edi->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
||||
edi->di_cache.u.rti.name_ptr = 0;
|
||||
/* two 32-bit values (ip_offset/fde_offset) per table-entry: */
|
||||
edi->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
|
||||
edi->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
|
||||
+ (addr - (unw_word_t) edi->ei.image
|
||||
- peh_hdr->p_offset));
|
||||
|
||||
/* For the binary-search table in the eh_frame_hdr, data-relative
|
||||
means relative to the start of that section... */
|
||||
edi->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
|
||||
+ ((unw_word_t) hdr - (unw_word_t) edi->ei.image
|
||||
- peh_hdr->p_offset));
|
||||
found = 1;
|
||||
}
|
||||
|
||||
#if UNW_TARGET_ARM
|
||||
if (parm_exidx)
|
||||
{
|
||||
edi->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
|
||||
edi->di_arm.start_ip = start_ip;
|
||||
edi->di_arm.end_ip = end_ip;
|
||||
edi->di_arm.u.rti.name_ptr = (unw_word_t) path;
|
||||
edi->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr;
|
||||
edi->di_arm.u.rti.table_len = parm_exidx->p_memsz;
|
||||
found = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FRAME
|
||||
/* Try .debug_frame. */
|
||||
found = dwarf_find_debug_frame (found, &edi->edi.di_debug, ip, segbase, path,
|
||||
start_ip, end_ip);
|
||||
#endif
|
||||
|
||||
return found;
|
||||
}
|
5
src/dwarf/Lfind_unwind_table.c
Normal file
5
src/dwarf/Lfind_unwind_table.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
|
||||
#include "Gfind_unwind_table.c"
|
||||
#endif
|
34
src/elfxx.c
34
src/elfxx.c
|
@ -127,22 +127,19 @@ elf_w (lookup_symbol) (unw_addr_space_t as,
|
|||
sensitive to the performance of this routine, why bother... */
|
||||
|
||||
HIDDEN int
|
||||
elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
|
||||
elf_w (get_proc_name_in_image) (unw_addr_space_t as, struct elf_image *ei,
|
||||
unsigned long segbase,
|
||||
unsigned long mapoff,
|
||||
unw_word_t ip,
|
||||
char *buf, size_t buf_len, unw_word_t *offp)
|
||||
{
|
||||
unsigned long segbase, mapoff;
|
||||
Elf_W (Addr) load_offset = 0;
|
||||
struct elf_image ei;
|
||||
Elf_W (Ehdr) *ehdr;
|
||||
Elf_W (Phdr) *phdr;
|
||||
int i, ret;
|
||||
|
||||
ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ehdr = ei.image;
|
||||
phdr = (Elf_W (Phdr) *) ((char *) ei.image + ehdr->e_phoff);
|
||||
ehdr = ei->image;
|
||||
phdr = (Elf_W (Phdr) *) ((char *) ei->image + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; ++i)
|
||||
if (phdr[i].p_type == PT_LOAD && phdr[i].p_offset == mapoff)
|
||||
|
@ -151,7 +148,24 @@ elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
|
|||
break;
|
||||
}
|
||||
|
||||
ret = elf_w (lookup_symbol) (as, ip, &ei, load_offset, buf, buf_len, offp);
|
||||
ret = elf_w (lookup_symbol) (as, ip, ei, load_offset, buf, buf_len, offp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
HIDDEN int
|
||||
elf_w (get_proc_name) (unw_addr_space_t as, pid_t pid, unw_word_t ip,
|
||||
char *buf, size_t buf_len, unw_word_t *offp)
|
||||
{
|
||||
unsigned long segbase, mapoff;
|
||||
struct elf_image ei;
|
||||
int ret;
|
||||
|
||||
ret = tdep_get_elf_image (&ei, pid, ip, &segbase, &mapoff, NULL, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = elf_w (get_proc_name_in_image) (as, &ei, segbase, mapoff, ip, buf, buf_len, offp);
|
||||
|
||||
munmap (ei.image, ei.size);
|
||||
ei.image = NULL;
|
||||
|
|
12
src/elfxx.h
12
src/elfxx.h
|
@ -43,6 +43,18 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|||
|
||||
#include "libunwind_i.h"
|
||||
|
||||
extern int elf_w (get_proc_name) (unw_addr_space_t as,
|
||||
pid_t pid, unw_word_t ip,
|
||||
char *buf, size_t len,
|
||||
unw_word_t *offp);
|
||||
|
||||
extern int elf_w (get_proc_name_in_image) (unw_addr_space_t as,
|
||||
struct elf_image *ei,
|
||||
unsigned long segbase,
|
||||
unsigned long mapoff,
|
||||
unw_word_t ip,
|
||||
char *buf, size_t buf_len, unw_word_t *offp);
|
||||
|
||||
extern int elf_w (get_proc_name) (unw_addr_space_t as,
|
||||
pid_t pid, unw_word_t ip,
|
||||
char *buf, size_t len,
|
||||
|
|
146
src/ia64/Gfind_unwind_table.c
Normal file
146
src/ia64/Gfind_unwind_table.c
Normal file
|
@ -0,0 +1,146 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003-2004 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "libunwind_i.h"
|
||||
|
||||
#if UNW_TARGET_IA64
|
||||
|
||||
#include "elf64.h"
|
||||
|
||||
static unw_word_t
|
||||
find_gp (struct elf_dyn_info *edi, Elf64_Phdr *pdyn, Elf64_Addr load_base)
|
||||
{
|
||||
Elf64_Off soff, str_soff;
|
||||
Elf64_Ehdr *ehdr = edi->ei.image;
|
||||
Elf64_Shdr *shdr;
|
||||
Elf64_Shdr *str_shdr;
|
||||
Elf64_Addr gp = 0;
|
||||
char *strtab;
|
||||
int i;
|
||||
|
||||
if (pdyn)
|
||||
{
|
||||
/* If we have a PT_DYNAMIC program header, fetch the gp-value
|
||||
from the DT_PLTGOT entry. */
|
||||
Elf64_Dyn *dyn = (Elf64_Dyn *) (pdyn->p_offset + (char *) edi->ei.image);
|
||||
for (; dyn->d_tag != DT_NULL; ++dyn)
|
||||
if (dyn->d_tag == DT_PLTGOT)
|
||||
{
|
||||
gp = (Elf64_Addr) dyn->d_un.d_ptr + load_base;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Without a PT_DYAMIC header, lets try to look for a non-empty .opd
|
||||
section. If there is such a section, we know it's full of
|
||||
function descriptors, and we can simply pick up the gp from the
|
||||
second word of the first entry in this table. */
|
||||
|
||||
soff = ehdr->e_shoff;
|
||||
str_soff = soff + (ehdr->e_shstrndx * ehdr->e_shentsize);
|
||||
|
||||
if (soff + ehdr->e_shnum * ehdr->e_shentsize > edi->ei.size)
|
||||
{
|
||||
Debug (1, "section table outside of image? (%lu > %lu)",
|
||||
soff + ehdr->e_shnum * ehdr->e_shentsize,
|
||||
edi->ei.size);
|
||||
goto done;
|
||||
}
|
||||
|
||||
shdr = (Elf64_Shdr *) ((char *) edi->ei.image + soff);
|
||||
str_shdr = (Elf64_Shdr *) ((char *) edi->ei.image + str_soff);
|
||||
strtab = (char *) edi->ei.image + str_shdr->sh_offset;
|
||||
for (i = 0; i < ehdr->e_shnum; ++i)
|
||||
{
|
||||
if (strcmp (strtab + shdr->sh_name, ".opd") == 0
|
||||
&& shdr->sh_size >= 16)
|
||||
{
|
||||
gp = ((Elf64_Addr *) ((char *) edi->ei.image + shdr->sh_offset))[1];
|
||||
goto done;
|
||||
}
|
||||
shdr = (Elf64_Shdr *) (((char *) shdr) + ehdr->e_shentsize);
|
||||
}
|
||||
|
||||
done:
|
||||
Debug (16, "image at %p, gp = %lx\n", edi->ei.image, gp);
|
||||
return gp;
|
||||
}
|
||||
|
||||
int
|
||||
ia64_find_unwind_table (struct elf_dyn_info *edi, unw_addr_space_t as,
|
||||
char *path, unw_word_t segbase, unw_word_t mapoff,
|
||||
unw_word_t ip)
|
||||
{
|
||||
Elf64_Phdr *phdr, *ptxt = NULL, *punw = NULL, *pdyn = NULL;
|
||||
Elf64_Ehdr *ehdr;
|
||||
int i;
|
||||
|
||||
if (!_Uelf64_valid_object (&edi->ei))
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
ehdr = edi->ei.image;
|
||||
phdr = (Elf64_Phdr *) ((char *) edi->ei.image + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; ++i)
|
||||
{
|
||||
switch (phdr[i].p_type)
|
||||
{
|
||||
case PT_LOAD:
|
||||
if (phdr[i].p_offset == mapoff)
|
||||
ptxt = phdr + i;
|
||||
break;
|
||||
|
||||
case PT_IA_64_UNWIND:
|
||||
punw = phdr + i;
|
||||
break;
|
||||
|
||||
case PT_DYNAMIC:
|
||||
pdyn = phdr + i;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ptxt || !punw)
|
||||
return 0;
|
||||
|
||||
edi->di_cache.start_ip = segbase;
|
||||
edi->di_cache.end_ip = edi->di_cache.start_ip + ptxt->p_memsz;
|
||||
edi->di_cache.gp = find_gp (edi, pdyn, segbase - ptxt->p_vaddr);
|
||||
edi->di_cache.format = UNW_INFO_FORMAT_TABLE;
|
||||
edi->di_cache.u.ti.name_ptr = 0;
|
||||
edi->di_cache.u.ti.segbase = segbase;
|
||||
edi->di_cache.u.ti.table_len = punw->p_memsz / sizeof (unw_word_t);
|
||||
edi->di_cache.u.ti.table_data = (unw_word_t *)
|
||||
((char *) edi->ei.image + (punw->p_vaddr - ptxt->p_vaddr));
|
||||
return 1;
|
||||
}
|
5
src/ia64/Lfind_unwind_table.c
Normal file
5
src/ia64/Lfind_unwind_table.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
#define UNW_LOCAL_ONLY
|
||||
#include <libunwind.h>
|
||||
#if defined(UNW_LOCAL_ONLY) && !defined(UNW_REMOTE_ONLY)
|
||||
#include "Gfind_unwind_table.c"
|
||||
#endif
|
|
@ -37,10 +37,10 @@ _UPT_create (pid_t pid)
|
|||
|
||||
memset (ui, 0, sizeof (*ui));
|
||||
ui->pid = pid;
|
||||
ui->di_cache.format = -1;
|
||||
ui->di_debug.format = -1;
|
||||
ui->edi.di_cache.format = -1;
|
||||
ui->edi.di_debug.format = -1;
|
||||
#if UNW_TARGET_IA64
|
||||
ui->ktab.format = -1;;
|
||||
ui->edi.ktab.format = -1;
|
||||
#endif
|
||||
return ui;
|
||||
}
|
||||
|
|
|
@ -29,10 +29,6 @@ void
|
|||
_UPT_destroy (void *ptr)
|
||||
{
|
||||
struct UPT_info *ui = (struct UPT_info *) ptr;
|
||||
if (ui->ei.image)
|
||||
{
|
||||
munmap(ui->ei.image, ui->ei.size);
|
||||
}
|
||||
|
||||
invalidate_edi (&ui->edi);
|
||||
free (ptr);
|
||||
}
|
||||
|
|
|
@ -32,400 +32,58 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|||
|
||||
#include "_UPT_internal.h"
|
||||
|
||||
#if UNW_TARGET_IA64
|
||||
|
||||
#include "elf64.h"
|
||||
|
||||
static unw_word_t
|
||||
find_gp (struct UPT_info *ui, Elf64_Phdr *pdyn, Elf64_Addr load_base)
|
||||
{
|
||||
Elf64_Off soff, str_soff;
|
||||
Elf64_Ehdr *ehdr = ui->ei.image;
|
||||
Elf64_Shdr *shdr;
|
||||
Elf64_Shdr *str_shdr;
|
||||
Elf64_Addr gp = 0;
|
||||
char *strtab;
|
||||
int i;
|
||||
|
||||
if (pdyn)
|
||||
{
|
||||
/* If we have a PT_DYNAMIC program header, fetch the gp-value
|
||||
from the DT_PLTGOT entry. */
|
||||
Elf64_Dyn *dyn = (Elf64_Dyn *) (pdyn->p_offset + (char *) ui->ei.image);
|
||||
for (; dyn->d_tag != DT_NULL; ++dyn)
|
||||
if (dyn->d_tag == DT_PLTGOT)
|
||||
{
|
||||
gp = (Elf64_Addr) dyn->d_un.d_ptr + load_base;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Without a PT_DYAMIC header, lets try to look for a non-empty .opd
|
||||
section. If there is such a section, we know it's full of
|
||||
function descriptors, and we can simply pick up the gp from the
|
||||
second word of the first entry in this table. */
|
||||
|
||||
soff = ehdr->e_shoff;
|
||||
str_soff = soff + (ehdr->e_shstrndx * ehdr->e_shentsize);
|
||||
|
||||
if (soff + ehdr->e_shnum * ehdr->e_shentsize > ui->ei.size)
|
||||
{
|
||||
Debug (1, "section table outside of image? (%lu > %lu)",
|
||||
soff + ehdr->e_shnum * ehdr->e_shentsize,
|
||||
ui->ei.size);
|
||||
goto done;
|
||||
}
|
||||
|
||||
shdr = (Elf64_Shdr *) ((char *) ui->ei.image + soff);
|
||||
str_shdr = (Elf64_Shdr *) ((char *) ui->ei.image + str_soff);
|
||||
strtab = (char *) ui->ei.image + str_shdr->sh_offset;
|
||||
for (i = 0; i < ehdr->e_shnum; ++i)
|
||||
{
|
||||
if (strcmp (strtab + shdr->sh_name, ".opd") == 0
|
||||
&& shdr->sh_size >= 16)
|
||||
{
|
||||
gp = ((Elf64_Addr *) ((char *) ui->ei.image + shdr->sh_offset))[1];
|
||||
goto done;
|
||||
}
|
||||
shdr = (Elf64_Shdr *) (((char *) shdr) + ehdr->e_shentsize);
|
||||
}
|
||||
|
||||
done:
|
||||
Debug (16, "image at %p, gp = %lx\n", ui->ei.image, gp);
|
||||
return gp;
|
||||
}
|
||||
|
||||
HIDDEN int
|
||||
_UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as,
|
||||
char *path, unw_word_t segbase, unw_word_t mapoff,
|
||||
unw_word_t ip)
|
||||
{
|
||||
Elf64_Phdr *phdr, *ptxt = NULL, *punw = NULL, *pdyn = NULL;
|
||||
Elf64_Ehdr *ehdr;
|
||||
int i;
|
||||
|
||||
if (!_Uelf64_valid_object (&ui->ei))
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
ehdr = ui->ei.image;
|
||||
phdr = (Elf64_Phdr *) ((char *) ui->ei.image + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; ++i)
|
||||
{
|
||||
switch (phdr[i].p_type)
|
||||
{
|
||||
case PT_LOAD:
|
||||
if (phdr[i].p_offset == mapoff)
|
||||
ptxt = phdr + i;
|
||||
break;
|
||||
|
||||
case PT_IA_64_UNWIND:
|
||||
punw = phdr + i;
|
||||
break;
|
||||
|
||||
case PT_DYNAMIC:
|
||||
pdyn = phdr + i;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ptxt || !punw)
|
||||
return 0;
|
||||
|
||||
ui->di_cache.start_ip = segbase;
|
||||
ui->di_cache.end_ip = ui->di_cache.start_ip + ptxt->p_memsz;
|
||||
ui->di_cache.gp = find_gp (ui, pdyn, segbase - ptxt->p_vaddr);
|
||||
ui->di_cache.format = UNW_INFO_FORMAT_TABLE;
|
||||
ui->di_cache.u.ti.name_ptr = 0;
|
||||
ui->di_cache.u.ti.segbase = segbase;
|
||||
ui->di_cache.u.ti.table_len = punw->p_memsz / sizeof (unw_word_t);
|
||||
ui->di_cache.u.ti.table_data = (unw_word_t *)
|
||||
((char *) ui->ei.image + (punw->p_vaddr - ptxt->p_vaddr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#elif UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_HPPA \
|
||||
|| UNW_TARGET_PPC32 || UNW_TARGET_PPC64 || UNW_TARGET_ARM || UNW_TARGET_MIPS
|
||||
|
||||
#include "dwarf-eh.h"
|
||||
#include "dwarf_i.h"
|
||||
|
||||
/* We need our own instance of dwarf_read_encoded_pointer() here since
|
||||
the one in dwarf/Gpe.c is not (and should not be) exported. */
|
||||
int
|
||||
dwarf_read_encoded_pointer (unw_addr_space_t as, unw_accessors_t *a,
|
||||
unw_word_t *addr, unsigned char encoding,
|
||||
const unw_proc_info_t *pi,
|
||||
unw_word_t *valp, void *arg)
|
||||
{
|
||||
return dwarf_read_encoded_pointer_inlined (as, a, addr, encoding,
|
||||
pi, valp, arg);
|
||||
}
|
||||
|
||||
HIDDEN int
|
||||
_UPTi_find_unwind_table (struct UPT_info *ui, unw_addr_space_t as,
|
||||
char *path, unw_word_t segbase, unw_word_t mapoff,
|
||||
unw_word_t ip)
|
||||
{
|
||||
Elf_W(Phdr) *phdr, *ptxt = NULL, *peh_hdr = NULL, *pdyn = NULL;
|
||||
unw_word_t addr, eh_frame_start, fde_count, load_base;
|
||||
unw_word_t max_load_addr = 0;
|
||||
unw_word_t start_ip = (unw_word_t) -1;
|
||||
unw_word_t end_ip = 0;
|
||||
struct dwarf_eh_frame_hdr *hdr;
|
||||
unw_proc_info_t pi;
|
||||
unw_accessors_t *a;
|
||||
Elf_W(Ehdr) *ehdr;
|
||||
#if UNW_TARGET_ARM
|
||||
const Elf_W(Phdr) *parm_exidx = NULL;
|
||||
#endif
|
||||
int i, ret, found = 0;
|
||||
|
||||
/* XXX: Much of this code is Linux/LSB-specific. */
|
||||
|
||||
if (!elf_w(valid_object) (&ui->ei))
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
ehdr = ui->ei.image;
|
||||
phdr = (Elf_W(Phdr) *) ((char *) ui->ei.image + ehdr->e_phoff);
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; ++i)
|
||||
{
|
||||
switch (phdr[i].p_type)
|
||||
{
|
||||
case PT_LOAD:
|
||||
if (phdr[i].p_vaddr < start_ip)
|
||||
start_ip = phdr[i].p_vaddr;
|
||||
|
||||
if (phdr[i].p_vaddr + phdr[i].p_memsz > end_ip)
|
||||
end_ip = phdr[i].p_vaddr + phdr[i].p_memsz;
|
||||
|
||||
if (phdr[i].p_offset == mapoff)
|
||||
ptxt = phdr + i;
|
||||
if ((uintptr_t) ui->ei.image + phdr->p_filesz > max_load_addr)
|
||||
max_load_addr = (uintptr_t) ui->ei.image + phdr->p_filesz;
|
||||
break;
|
||||
|
||||
case PT_GNU_EH_FRAME:
|
||||
peh_hdr = phdr + i;
|
||||
break;
|
||||
|
||||
case PT_DYNAMIC:
|
||||
pdyn = phdr + i;
|
||||
break;
|
||||
|
||||
#if UNW_TARGET_ARM
|
||||
case PT_ARM_EXIDX:
|
||||
parm_exidx = phdr + i;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ptxt)
|
||||
return 0;
|
||||
|
||||
load_base = segbase - ptxt->p_vaddr;
|
||||
start_ip += load_base;
|
||||
end_ip += load_base;
|
||||
|
||||
if (peh_hdr)
|
||||
{
|
||||
if (pdyn)
|
||||
{
|
||||
/* For dynamicly linked executables and shared libraries,
|
||||
DT_PLTGOT is the value that data-relative addresses are
|
||||
relative to for that object. We call this the "gp". */
|
||||
Elf_W(Dyn) *dyn = (Elf_W(Dyn) *)(pdyn->p_offset
|
||||
+ (char *) ui->ei.image);
|
||||
for (; dyn->d_tag != DT_NULL; ++dyn)
|
||||
if (dyn->d_tag == DT_PLTGOT)
|
||||
{
|
||||
/* Assume that _DYNAMIC is writable and GLIBC has
|
||||
relocated it (true for x86 at least). */
|
||||
ui->di_cache.gp = dyn->d_un.d_ptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
/* Otherwise this is a static executable with no _DYNAMIC. Assume
|
||||
that data-relative addresses are relative to 0, i.e.,
|
||||
absolute. */
|
||||
ui->di_cache.gp = 0;
|
||||
|
||||
hdr = (struct dwarf_eh_frame_hdr *) (peh_hdr->p_offset
|
||||
+ (char *) ui->ei.image);
|
||||
if (hdr->version != DW_EH_VERSION)
|
||||
{
|
||||
Debug (1, "table `%s' has unexpected version %d\n",
|
||||
path, hdr->version);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
a = unw_get_accessors (unw_local_addr_space);
|
||||
addr = (unw_word_t) (hdr + 1);
|
||||
|
||||
/* Fill in a dummy proc_info structure. We just need to fill in
|
||||
enough to ensure that dwarf_read_encoded_pointer() can do it's
|
||||
job. Since we don't have a procedure-context at this point, all
|
||||
we have to do is fill in the global-pointer. */
|
||||
memset (&pi, 0, sizeof (pi));
|
||||
pi.gp = ui->di_cache.gp;
|
||||
|
||||
/* (Optionally) read eh_frame_ptr: */
|
||||
if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
|
||||
&addr, hdr->eh_frame_ptr_enc, &pi,
|
||||
&eh_frame_start, NULL)) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
/* (Optionally) read fde_count: */
|
||||
if ((ret = dwarf_read_encoded_pointer (unw_local_addr_space, a,
|
||||
&addr, hdr->fde_count_enc, &pi,
|
||||
&fde_count, NULL)) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
if (hdr->table_enc != (DW_EH_PE_datarel | DW_EH_PE_sdata4))
|
||||
{
|
||||
#if 1
|
||||
abort ();
|
||||
#else
|
||||
unw_word_t eh_frame_end;
|
||||
|
||||
/* If there is no search table or it has an unsupported
|
||||
encoding, fall back on linear search. */
|
||||
if (hdr->table_enc == DW_EH_PE_omit)
|
||||
Debug (4, "EH lacks search table; doing linear search\n");
|
||||
else
|
||||
Debug (4, "EH table has encoding 0x%x; doing linear search\n",
|
||||
hdr->table_enc);
|
||||
|
||||
eh_frame_end = max_load_addr; /* XXX can we do better? */
|
||||
|
||||
if (hdr->fde_count_enc == DW_EH_PE_omit)
|
||||
fde_count = ~0UL;
|
||||
if (hdr->eh_frame_ptr_enc == DW_EH_PE_omit)
|
||||
abort ();
|
||||
|
||||
return linear_search (unw_local_addr_space, ip,
|
||||
eh_frame_start, eh_frame_end, fde_count,
|
||||
pi, need_unwind_info, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
ui->di_cache.start_ip = start_ip;
|
||||
ui->di_cache.end_ip = end_ip;
|
||||
ui->di_cache.format = UNW_INFO_FORMAT_REMOTE_TABLE;
|
||||
ui->di_cache.u.rti.name_ptr = 0;
|
||||
/* two 32-bit values (ip_offset/fde_offset) per table-entry: */
|
||||
ui->di_cache.u.rti.table_len = (fde_count * 8) / sizeof (unw_word_t);
|
||||
ui->di_cache.u.rti.table_data = ((load_base + peh_hdr->p_vaddr)
|
||||
+ (addr - (unw_word_t) ui->ei.image
|
||||
- peh_hdr->p_offset));
|
||||
|
||||
/* For the binary-search table in the eh_frame_hdr, data-relative
|
||||
means relative to the start of that section... */
|
||||
ui->di_cache.u.rti.segbase = ((load_base + peh_hdr->p_vaddr)
|
||||
+ ((unw_word_t) hdr - (unw_word_t) ui->ei.image
|
||||
- peh_hdr->p_offset));
|
||||
found = 1;
|
||||
}
|
||||
|
||||
#if UNW_TARGET_ARM
|
||||
if (parm_exidx)
|
||||
{
|
||||
ui->di_arm.format = UNW_INFO_FORMAT_ARM_EXIDX;
|
||||
ui->di_arm.start_ip = start_ip;
|
||||
ui->di_arm.end_ip = end_ip;
|
||||
ui->di_arm.u.rti.name_ptr = (unw_word_t) path;
|
||||
ui->di_arm.u.rti.table_data = load_base + parm_exidx->p_vaddr;
|
||||
ui->di_arm.u.rti.table_len = parm_exidx->p_memsz;
|
||||
found = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_FRAME
|
||||
/* Try .debug_frame. */
|
||||
found = dwarf_find_debug_frame (found, &ui->di_debug, ip, segbase, path,
|
||||
start_ip, end_ip);
|
||||
#endif
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
#endif /* UNW_TARGET_X86 || UNW_TARGET_X86_64 || UNW_TARGET_HPPA*/
|
||||
|
||||
static int
|
||||
get_unwind_info (struct UPT_info *ui, unw_addr_space_t as, unw_word_t ip)
|
||||
get_unwind_info (struct elf_dyn_info *edi, pid_t pid, unw_addr_space_t as, unw_word_t ip)
|
||||
{
|
||||
unsigned long segbase, mapoff;
|
||||
char path[PATH_MAX];
|
||||
|
||||
#if UNW_TARGET_IA64 && defined(__linux)
|
||||
if (!ui->ktab.start_ip && _Uia64_get_kernel_table (&ui->ktab) < 0)
|
||||
if (!edi->ktab.start_ip && _Uia64_get_kernel_table (&edi->ktab) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
if (ui->ktab.format != -1 && ip >= ui->ktab.start_ip && ip < ui->ktab.end_ip)
|
||||
if (edi->ktab.format != -1 && ip >= edi->ktab.start_ip && ip < edi->ktab.end_ip)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if ((ui->di_cache.format != -1
|
||||
&& ip >= ui->di_cache.start_ip && ip < ui->di_cache.end_ip)
|
||||
if ((edi->di_cache.format != -1
|
||||
&& ip >= edi->di_cache.start_ip && ip < edi->di_cache.end_ip)
|
||||
#if UNW_TARGET_ARM
|
||||
|| (ui->di_debug.format != -1
|
||||
&& ip >= ui->di_arm.start_ip && ip < ui->di_arm.end_ip)
|
||||
|| (edi->di_debug.format != -1
|
||||
&& ip >= edi->di_arm.start_ip && ip < edi->di_arm.end_ip)
|
||||
#endif
|
||||
|| (ui->di_debug.format != -1
|
||||
&& ip >= ui->di_debug.start_ip && ip < ui->di_debug.end_ip))
|
||||
|| (edi->di_debug.format != -1
|
||||
&& ip >= edi->di_debug.start_ip && ip < edi->di_debug.end_ip))
|
||||
return 0;
|
||||
|
||||
if (ui->ei.image)
|
||||
{
|
||||
munmap (ui->ei.image, ui->ei.size);
|
||||
ui->ei.image = NULL;
|
||||
ui->ei.size = 0;
|
||||
invalidate_edi(edi);
|
||||
|
||||
/* invalidate the cache: */
|
||||
ui->di_cache.start_ip = ui->di_cache.end_ip = 0;
|
||||
ui->di_debug.start_ip = ui->di_debug.end_ip = 0;
|
||||
ui->di_cache.format = -1;
|
||||
ui->di_debug.format = -1;
|
||||
#if UNW_TARGET_ARM
|
||||
ui->di_arm.start_ip = ui->di_arm.end_ip = 0;
|
||||
ui->di_arm.format = -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (tdep_get_elf_image (&ui->ei, ui->pid, ip, &segbase, &mapoff, path,
|
||||
if (tdep_get_elf_image (&edi->ei, pid, ip, &segbase, &mapoff, path,
|
||||
sizeof(path)) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
/* Here, SEGBASE is the starting-address of the (mmap'ped) segment
|
||||
which covers the IP we're looking for. */
|
||||
if (_UPTi_find_unwind_table (ui, as, path, segbase, mapoff, ip) < 0)
|
||||
if (dwarf_find_unwind_table (edi, as, path, segbase, mapoff, ip) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
/* This can happen in corner cases where dynamically generated
|
||||
code falls into the same page that contains the data-segment
|
||||
and the page-offset of the code is within the first page of
|
||||
the executable. */
|
||||
if (ui->di_cache.format != -1
|
||||
&& (ip < ui->di_cache.start_ip || ip >= ui->di_cache.end_ip))
|
||||
ui->di_cache.format = -1;
|
||||
if (edi->di_cache.format != -1
|
||||
&& (ip < edi->di_cache.start_ip || ip >= edi->di_cache.end_ip))
|
||||
edi->di_cache.format = -1;
|
||||
|
||||
if (ui->di_debug.format != -1
|
||||
&& (ip < ui->di_debug.start_ip || ip >= ui->di_debug.end_ip))
|
||||
ui->di_debug.format = -1;
|
||||
if (edi->di_debug.format != -1
|
||||
&& (ip < edi->di_debug.start_ip || ip >= edi->di_debug.end_ip))
|
||||
edi->di_debug.format = -1;
|
||||
|
||||
if (ui->di_cache.format == -1
|
||||
if (edi->di_cache.format == -1
|
||||
#if UNW_TARGET_ARM
|
||||
&& ui->di_arm.format == -1
|
||||
&& edi->di_arm.format == -1
|
||||
#endif
|
||||
&& ui->di_debug.format == -1)
|
||||
&& edi->di_debug.format == -1)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
return 0;
|
||||
|
@ -438,11 +96,11 @@ _UPT_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
|||
struct UPT_info *ui = arg;
|
||||
int ret = -UNW_ENOINFO;
|
||||
|
||||
if (get_unwind_info (ui, as, ip) < 0)
|
||||
if (get_unwind_info (&ui->edi, ui->pid, as, ip) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
#if UNW_TARGET_IA64
|
||||
if (ui->ktab.format != -1)
|
||||
if (ui->edi.ktab.format != -1)
|
||||
{
|
||||
/* The kernel unwind table resides in local memory, so we have
|
||||
to use the local address space to search it. Since
|
||||
|
@ -450,7 +108,7 @@ _UPT_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
|||
case, we simply make a copy of the unwind-info, so
|
||||
_UPT_put_unwind_info() can always free() the unwind-info
|
||||
without ill effects. */
|
||||
ret = tdep_search_unwind_table (unw_local_addr_space, ip, &ui->ktab, pi,
|
||||
ret = tdep_search_unwind_table (unw_local_addr_space, ip, &ui->edi.ktab, pi,
|
||||
need_unwind_info, arg);
|
||||
if (ret >= 0)
|
||||
{
|
||||
|
@ -469,18 +127,18 @@ _UPT_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
|||
}
|
||||
#endif
|
||||
|
||||
if (ret == -UNW_ENOINFO && ui->di_cache.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->di_cache,
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_cache.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_cache,
|
||||
pi, need_unwind_info, arg);
|
||||
|
||||
#if UNW_TARGET_ARM
|
||||
if (ret == -UNW_ENOINFO && ui->di_arm.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->di_arm, pi,
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_arm.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_arm, pi,
|
||||
need_unwind_info, arg);
|
||||
#endif
|
||||
|
||||
if (ret == -UNW_ENOINFO && ui->di_debug.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->di_debug, pi,
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_debug.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_debug, pi,
|
||||
need_unwind_info, arg);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -46,24 +46,17 @@ get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg,
|
|||
if (off)
|
||||
continue;
|
||||
|
||||
if (ui->ei.image)
|
||||
{
|
||||
munmap (ui->ei.image, ui->ei.size);
|
||||
ui->ei.image = NULL;
|
||||
ui->ei.size = 0;
|
||||
/* invalidate the cache: */
|
||||
ui->di_cache.start_ip = ui->di_cache.end_ip = 0;
|
||||
}
|
||||
invalidate_edi(&ui->edi);
|
||||
|
||||
if (elf_map_image (&ui->ei, path) < 0)
|
||||
if (elf_map_image (&ui->edi.ei, path) < 0)
|
||||
/* ignore unmappable stuff like "/SYSV00001b58 (deleted)" */
|
||||
continue;
|
||||
|
||||
Debug (16, "checking object %s\n", path);
|
||||
|
||||
if (_UPTi_find_unwind_table (ui, as, path, lo, off, 0) > 0)
|
||||
if (dwarf_find_unwind_table (&ui->edi, as, path, lo, off, 0) > 0)
|
||||
{
|
||||
res = _Uia64_find_dyn_list (as, &ui->di_cache, arg);
|
||||
res = _Uia64_find_dyn_list (as, &ui->edi.di_cache, arg);
|
||||
if (res && count++ == 0)
|
||||
{
|
||||
Debug (12, "dyn_info_list_addr = 0x%lx\n", (long) res);
|
||||
|
|
|
@ -51,24 +51,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|||
struct UPT_info
|
||||
{
|
||||
pid_t pid; /* the process-id of the child we're unwinding */
|
||||
struct elf_image ei;
|
||||
unw_dyn_info_t di_cache;
|
||||
unw_dyn_info_t di_debug; /* additional table info for .debug_frame */
|
||||
#if UNW_TARGET_IA64
|
||||
unw_dyn_info_t ktab;
|
||||
#endif
|
||||
#if UNW_TARGET_ARM
|
||||
unw_dyn_info_t di_arm; /* additional table info for .ARM.exidx */
|
||||
#endif
|
||||
struct elf_dyn_info edi;
|
||||
};
|
||||
|
||||
extern int _UPT_reg_offset[UNW_REG_LAST + 1];
|
||||
|
||||
extern int _UPTi_find_unwind_table (struct UPT_info *ui,
|
||||
unw_addr_space_t as,
|
||||
char *path,
|
||||
unw_word_t segbase,
|
||||
unw_word_t mapoff,
|
||||
unw_word_t ip);
|
||||
|
||||
#endif /* _UPT_internal_h */
|
||||
|
|
|
@ -45,7 +45,8 @@ endif #ARCH_IA64
|
|||
Gtest-trace Ltest-trace \
|
||||
test-async-sig test-flush-cache test-init-remote \
|
||||
test-mem test-setjmp test-ptrace \
|
||||
Ltest-nomalloc Ltest-nocalloc rs-race
|
||||
Ltest-nomalloc Ltest-nocalloc rs-race \
|
||||
test-coredump-unwind
|
||||
noinst_PROGRAMS_cdep = forker mapper test-ptrace-misc \
|
||||
Gperf-simple Lperf-simple
|
||||
|
||||
|
@ -112,6 +113,7 @@ Ltest_trace_SOURCES = Ltest-trace.c ident.c
|
|||
|
||||
LIBUNWIND = $(top_builddir)/src/libunwind-$(arch).la
|
||||
LIBUNWIND_ptrace = $(top_builddir)/src/libunwind-ptrace.a
|
||||
LIBUNWIND_coredump = $(top_builddir)/src/libunwind-coredump.a
|
||||
|
||||
if USE_ELF32
|
||||
LIBUNWIND_ELF = $(top_builddir)/src/libunwind-elf32.la
|
||||
|
@ -162,3 +164,4 @@ Lperf_trace_LDADD = $(LIBUNWIND_local)
|
|||
test_setjmp_LDADD = $(LIBUNWIND_setjmp)
|
||||
ia64_test_setjmp_LDADD = $(LIBUNWIND_setjmp)
|
||||
|
||||
test_coredump_unwind_LDADD = $(LIBUNWIND_coredump) $(LIBUNWIND)
|
||||
|
|
|
@ -119,9 +119,11 @@ check_local_unw_abi () {
|
|||
match _U${plat}_is_fpreg
|
||||
match _UL${plat}_search_unwind_table
|
||||
match _UL${plat}_dwarf_search_unwind_table
|
||||
match _UL${plat}_dwarf_find_unwind_table
|
||||
;;
|
||||
hppa)
|
||||
match _UL${plat}_dwarf_search_unwind_table
|
||||
match _UL${plat}_dwarf_find_unwind_table
|
||||
match _U${plat}_get_elf_image
|
||||
match _U${plat}_setcontext
|
||||
;;
|
||||
|
@ -133,16 +135,19 @@ check_local_unw_abi () {
|
|||
match _U${plat}_get_elf_image
|
||||
match _U${plat}_is_fpreg
|
||||
match _UL${plat}_dwarf_search_unwind_table
|
||||
match _UL${plat}_dwarf_find_unwind_table
|
||||
;;
|
||||
x86_64)
|
||||
match _U${plat}_get_elf_image
|
||||
match _U${plat}_is_fpreg
|
||||
match _UL${plat}_dwarf_search_unwind_table
|
||||
match _UL${plat}_dwarf_find_unwind_table
|
||||
match _U${plat}_setcontext
|
||||
;;
|
||||
*)
|
||||
match _U${plat}_is_fpreg
|
||||
match _UL${plat}_dwarf_search_unwind_table
|
||||
match _UL${plat}_dwarf_find_unwind_table
|
||||
;;
|
||||
esac
|
||||
|
||||
|
@ -182,9 +187,11 @@ check_generic_unw_abi () {
|
|||
match _U${plat}_get_elf_image
|
||||
match _U${plat}_search_unwind_table
|
||||
match _U${plat}_dwarf_search_unwind_table
|
||||
match _U${plat}_dwarf_find_unwind_table
|
||||
;;
|
||||
hppa)
|
||||
match _U${plat}_dwarf_search_unwind_table
|
||||
match _U${plat}_dwarf_find_unwind_table
|
||||
match _U${plat}_get_elf_image
|
||||
;;
|
||||
ia64)
|
||||
|
@ -203,15 +210,18 @@ check_generic_unw_abi () {
|
|||
match _U${plat}_get_elf_image
|
||||
match _U${plat}_is_fpreg
|
||||
match _U${plat}_dwarf_search_unwind_table
|
||||
match _U${plat}_dwarf_find_unwind_table
|
||||
;;
|
||||
x86_64)
|
||||
match _U${plat}_get_elf_image
|
||||
match _U${plat}_is_fpreg
|
||||
match _U${plat}_dwarf_search_unwind_table
|
||||
match _U${plat}_dwarf_find_unwind_table
|
||||
;;
|
||||
*)
|
||||
match _U${plat}_is_fpreg
|
||||
match _U${plat}_dwarf_search_unwind_table
|
||||
match _U${plat}_dwarf_find_unwind_table
|
||||
;;
|
||||
esac
|
||||
|
||||
|
|
326
tests/test-coredump-unwind.c
Normal file
326
tests/test-coredump-unwind.c
Normal file
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Example program for unwinding core dumps.
|
||||
*
|
||||
* Compile a-la:
|
||||
* gcc -Os -Wall \
|
||||
* -Wl,--start-group \
|
||||
* -lunwind -lunwind-x86 -lunwind-coredump \
|
||||
* example-core-unwind.c \
|
||||
* -Wl,--end-group \
|
||||
* -oexample-core-unwind
|
||||
*
|
||||
* Run:
|
||||
* objdump -sx COREDUMP
|
||||
* eu-unstrip -n --core COREDUMP
|
||||
* figure out which segments in COREDUMP correspond to which mapped executable files
|
||||
* (binary and libraries), then supply them like this:
|
||||
* ./example-core-unwind COREDUMP 3:/bin/crashed_program 6:/lib/libc.so.6 [...]
|
||||
*/
|
||||
|
||||
#undef _GNU_SOURCE
|
||||
#define _GNU_SOURCE 1
|
||||
#undef __USE_GNU
|
||||
#define __USE_GNU 1
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/param.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
/* For SIGSEGV handler code */
|
||||
#include <execinfo.h>
|
||||
#include <sys/ucontext.h>
|
||||
|
||||
#include <libunwind-coredump.h>
|
||||
|
||||
|
||||
/* Utility logging functions */
|
||||
|
||||
enum {
|
||||
LOGMODE_NONE = 0,
|
||||
LOGMODE_STDIO = (1 << 0),
|
||||
LOGMODE_SYSLOG = (1 << 1),
|
||||
LOGMODE_BOTH = LOGMODE_SYSLOG + LOGMODE_STDIO,
|
||||
};
|
||||
const char *msg_prefix = "";
|
||||
const char *msg_eol = "\n";
|
||||
int logmode = LOGMODE_STDIO;
|
||||
int xfunc_error_retval = EXIT_FAILURE;
|
||||
|
||||
void xfunc_die(void)
|
||||
{
|
||||
exit(xfunc_error_retval);
|
||||
}
|
||||
|
||||
static void verror_msg_helper(const char *s,
|
||||
va_list p,
|
||||
const char* strerr,
|
||||
int flags)
|
||||
{
|
||||
char *msg;
|
||||
int prefix_len, strerr_len, msgeol_len, used;
|
||||
|
||||
if (!logmode)
|
||||
return;
|
||||
|
||||
used = vasprintf(&msg, s, p);
|
||||
if (used < 0)
|
||||
return;
|
||||
|
||||
/* This is ugly and costs +60 bytes compared to multiple
|
||||
* fprintf's, but is guaranteed to do a single write.
|
||||
* This is needed for e.g. when multiple children
|
||||
* can produce log messages simultaneously. */
|
||||
|
||||
prefix_len = msg_prefix[0] ? strlen(msg_prefix) + 2 : 0;
|
||||
strerr_len = strerr ? strlen(strerr) : 0;
|
||||
msgeol_len = strlen(msg_eol);
|
||||
/* +3 is for ": " before strerr and for terminating NUL */
|
||||
char *msg1 = (char*) realloc(msg, prefix_len + used + strerr_len + msgeol_len + 3);
|
||||
if (!msg1)
|
||||
{
|
||||
free(msg);
|
||||
return;
|
||||
}
|
||||
msg = msg1;
|
||||
/* TODO: maybe use writev instead of memmoving? Need full_writev? */
|
||||
if (prefix_len)
|
||||
{
|
||||
char *p;
|
||||
memmove(msg + prefix_len, msg, used);
|
||||
used += prefix_len;
|
||||
p = stpcpy(msg, msg_prefix);
|
||||
p[0] = ':';
|
||||
p[1] = ' ';
|
||||
}
|
||||
if (strerr)
|
||||
{
|
||||
if (s[0])
|
||||
{
|
||||
msg[used++] = ':';
|
||||
msg[used++] = ' ';
|
||||
}
|
||||
strcpy(&msg[used], strerr);
|
||||
used += strerr_len;
|
||||
}
|
||||
strcpy(&msg[used], msg_eol);
|
||||
|
||||
if (flags & LOGMODE_STDIO)
|
||||
{
|
||||
fflush(stdout);
|
||||
write(STDERR_FILENO, msg, used + msgeol_len);
|
||||
}
|
||||
msg[used] = '\0'; /* remove msg_eol (usually "\n") */
|
||||
if (flags & LOGMODE_SYSLOG)
|
||||
{
|
||||
syslog(LOG_ERR, "%s", msg + prefix_len);
|
||||
}
|
||||
free(msg);
|
||||
}
|
||||
|
||||
void log_msg(const char *s, ...)
|
||||
{
|
||||
va_list p;
|
||||
va_start(p, s);
|
||||
verror_msg_helper(s, p, NULL, logmode);
|
||||
va_end(p);
|
||||
}
|
||||
/* It's a macro, not function, since it collides with log() from math.h */
|
||||
#undef log
|
||||
#define log(...) log_msg(__VA_ARGS__)
|
||||
|
||||
void error_msg(const char *s, ...)
|
||||
{
|
||||
va_list p;
|
||||
va_start(p, s);
|
||||
verror_msg_helper(s, p, NULL, logmode);
|
||||
va_end(p);
|
||||
}
|
||||
|
||||
void error_msg_and_die(const char *s, ...)
|
||||
{
|
||||
va_list p;
|
||||
va_start(p, s);
|
||||
verror_msg_helper(s, p, NULL, logmode);
|
||||
va_end(p);
|
||||
xfunc_die();
|
||||
}
|
||||
|
||||
void perror_msg(const char *s, ...)
|
||||
{
|
||||
va_list p;
|
||||
va_start(p, s);
|
||||
/* Guard against "<error message>: Success" */
|
||||
verror_msg_helper(s, p, errno ? strerror(errno) : NULL, logmode);
|
||||
va_end(p);
|
||||
}
|
||||
|
||||
void perror_msg_and_die(const char *s, ...)
|
||||
{
|
||||
va_list p;
|
||||
va_start(p, s);
|
||||
/* Guard against "<error message>: Success" */
|
||||
verror_msg_helper(s, p, errno ? strerror(errno) : NULL, logmode);
|
||||
va_end(p);
|
||||
xfunc_die();
|
||||
}
|
||||
|
||||
void die_out_of_memory(void)
|
||||
{
|
||||
error_msg_and_die("Out of memory, exiting");
|
||||
}
|
||||
|
||||
/* End of utility logging functions */
|
||||
|
||||
|
||||
|
||||
static
|
||||
void handle_sigsegv(int sig, siginfo_t *info, void *ucontext)
|
||||
{
|
||||
long ip;
|
||||
ucontext_t *uc;
|
||||
|
||||
uc = ucontext;
|
||||
#if defined(__linux__)
|
||||
#ifdef TARGET_X86
|
||||
ip = uc->uc_mcontext.gregs[REG_EIP];
|
||||
#elif defined(TARGET_X86_64)
|
||||
ip = uc->uc_mcontext.gregs[REG_RIP];
|
||||
#elif defined(TARGET_ARM)
|
||||
ip = uc->uc_mcontext.arm_ip;
|
||||
#endif
|
||||
#else
|
||||
#error Port me
|
||||
#endif
|
||||
dprintf(2, "signal:%d address:0x%lx ip:0x%lx\n",
|
||||
sig,
|
||||
/* this is void*, but using %p would print "(null)"
|
||||
* even for ptrs which are not exactly 0, but, say, 0x123:
|
||||
*/
|
||||
(long)info->si_addr,
|
||||
ip);
|
||||
|
||||
{
|
||||
/* glibc extension */
|
||||
void *array[50];
|
||||
int size;
|
||||
size = backtrace(array, 50);
|
||||
backtrace_symbols_fd(array, size, 2);
|
||||
}
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static void install_sigsegv_handler(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = handle_sigsegv;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGILL, &sa, NULL);
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
unw_addr_space_t as;
|
||||
struct UCD_info *ui;
|
||||
unw_cursor_t c;
|
||||
int ret;
|
||||
|
||||
install_sigsegv_handler();
|
||||
|
||||
const char *progname = strrchr(argv[0], '/');
|
||||
if (progname)
|
||||
progname++;
|
||||
else
|
||||
progname = argv[0];
|
||||
|
||||
if (!argv[1])
|
||||
error_msg_and_die("Usage: %s COREDUMP [SEGMENT_NO:BINARY_FILE]...", progname);
|
||||
|
||||
msg_prefix = progname;
|
||||
|
||||
as = unw_create_addr_space(&_UCD_accessors, 0);
|
||||
if (!as)
|
||||
error_msg_and_die("unw_create_addr_space() failed");
|
||||
|
||||
ui = _UCD_create(argv[1]);
|
||||
if (!ui)
|
||||
error_msg_and_die("_UCD_create('%s') failed", argv[1]);
|
||||
ret = unw_init_remote(&c, as, ui);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("unw_init_remote() failed: ret=%d\n", ret);
|
||||
|
||||
argv += 2;
|
||||
while (*argv)
|
||||
{
|
||||
char *colon = strchr(*argv, ':');
|
||||
if (!colon)
|
||||
error_msg_and_die("Bad format: '%s'", *argv);
|
||||
*colon = '\0';
|
||||
unsigned n = atoi(*argv);
|
||||
*colon = ':';
|
||||
if (_UCD_add_backing_file_at_segment(ui, n, colon + 1) < 0)
|
||||
error_msg_and_die("Can't add backing file '%s'", *argv);
|
||||
argv++;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unw_word_t ip;
|
||||
ret = unw_get_reg(&c, UNW_REG_IP, &ip);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("unw_get_reg(UNW_REG_IP) failed: ret=%d\n", ret);
|
||||
|
||||
unw_proc_info_t pi;
|
||||
ret = unw_get_proc_info(&c, &pi);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret);
|
||||
printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx\n",
|
||||
(long) ip,
|
||||
(long) pi.start_ip, (long) pi.end_ip,
|
||||
(long) pi.handler, (long) pi.lsda);
|
||||
|
||||
log("step");
|
||||
ret = unw_step(&c);
|
||||
log("step done:%d", ret);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("FAILURE: unw_step() returned %d", ret);
|
||||
if (ret == 0)
|
||||
break;
|
||||
}
|
||||
log("stepping ended");
|
||||
|
||||
_UCD_destroy(ui);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue