1
0
Fork 0
mirror of https://github.com/tobast/libunwind-eh_elf.git synced 2024-12-23 03:53:43 +01:00
Conflicts:
	src/ptrace/_UPT_find_proc_info.c
This commit is contained in:
Arun Sharma 2012-05-23 20:51:50 -07:00
commit fa5409387c
37 changed files with 2473 additions and 425 deletions

View file

@ -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

View file

@ -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,

View file

@ -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,

View 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 */

View file

@ -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

View file

@ -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
View 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.

View 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;
}

View 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;
}

View 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;
}

View 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
View 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;
}

View 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);
}

View 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;
}

View 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;
}

View 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
}

View 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
View 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

View 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
View 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"

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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

View file

@ -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;

View file

@ -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,

View 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;
}

View 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

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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);

View file

@ -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 */

View file

@ -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)

View file

@ -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

View 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;
}