mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-11-22 07:37:38 +01:00
Add support for unwinding coredump images
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
parent
d276b7a961
commit
b08ae72d6c
21 changed files with 1769 additions and 2 deletions
|
@ -1,4 +1,6 @@
|
|||
include_HEADERS = include/libunwind-dynamic.h include/libunwind-ptrace.h
|
||||
include_HEADERS = include/libunwind-dynamic.h \
|
||||
include/libunwind-ptrace.h \
|
||||
include/libunwind-coredump.h
|
||||
|
||||
if ARCH_ARM
|
||||
include_HEADERS += include/libunwind-arm.h
|
||||
|
|
|
@ -44,6 +44,7 @@ AC_C_CONST
|
|||
AC_C_INLINE
|
||||
AC_TYPE_SIGNAL
|
||||
AC_TYPE_SIZE_T
|
||||
AC_CHECK_SIZEOF(off_t)
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
|
||||
|
|
69
include/libunwind-coredump.h
Normal file
69
include/libunwind-coredump.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/* 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_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 */
|
|
@ -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,24 @@ 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_reg.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)
|
||||
|
|
8
src/coredump/README
Normal file
8
src/coredump/README
Normal file
|
@ -0,0 +1,8 @@
|
|||
This code is based on "unwinding via ptrace" code from ptrace/
|
||||
directory.
|
||||
|
||||
Files with names starting with _UCD_ are substantially changed
|
||||
from their ptrace/_UPT_... progenitors.
|
||||
|
||||
Files which still have _UPT_... names are either verbiatim copies
|
||||
from ptrace/, or unimplemented stubs.
|
102
src/coredump/_UCD_access_mem.c
Normal file
102
src/coredump/_UCD_access_mem.c
Normal file
|
@ -0,0 +1,102 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *val,
|
||||
int write, void *arg)
|
||||
{
|
||||
if (write)
|
||||
{
|
||||
Debug(0, "%s: write is not supported\n", __func__);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
|
||||
struct UCD_info *ui = arg;
|
||||
|
||||
unw_word_t addr_last = addr + sizeof(*val)-1;
|
||||
coredump_phdr_t *phdr;
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
phdr = &ui->phdrs[i];
|
||||
if (phdr->p_vaddr <= addr && addr_last < phdr->p_vaddr + phdr->p_memsz)
|
||||
{
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
Debug(1, "%s: addr 0x%llx is unmapped\n",
|
||||
__func__, (unsigned long long)addr
|
||||
);
|
||||
return -UNW_EINVAL;
|
||||
|
||||
found: ;
|
||||
|
||||
const char *filename;
|
||||
off_t fileofs;
|
||||
int fd;
|
||||
if (addr_last >= phdr->p_vaddr + phdr->p_filesz)
|
||||
{
|
||||
/* This part of mapped address space is not present in coredump file */
|
||||
/* Do we have it in the backup file? */
|
||||
if (phdr->backing_fd < 0)
|
||||
{
|
||||
Debug(1, "%s: access to not-present data in phdr[%d]: addr:0x%llx\n",
|
||||
__func__, i, (unsigned long long)addr
|
||||
);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
filename = phdr->backing_filename;
|
||||
fileofs = addr - phdr->p_vaddr;
|
||||
fd = phdr->backing_fd;
|
||||
goto read;
|
||||
}
|
||||
|
||||
filename = ui->coredump_filename;
|
||||
fileofs = phdr->p_offset + (addr - phdr->p_vaddr);
|
||||
fd = ui->coredump_fd;
|
||||
read:
|
||||
if (lseek(fd, fileofs, SEEK_SET) != fileofs)
|
||||
goto read_error;
|
||||
if (read(fd, val, sizeof(*val)) != sizeof(*val))
|
||||
goto read_error;
|
||||
|
||||
Debug(1, "%s: 0x%llx <- [addr:0x%llx fileofs:0x%llx]\n",
|
||||
__func__,
|
||||
(unsigned long long)(*val),
|
||||
(unsigned long long)addr,
|
||||
(unsigned long long)fileofs
|
||||
);
|
||||
return 0;
|
||||
|
||||
read_error:
|
||||
Debug(1, "%s: access out of file: addr:0x%llx fileofs:%llx file:'%s'\n",
|
||||
__func__,
|
||||
(unsigned long long)addr,
|
||||
(unsigned long long)fileofs,
|
||||
filename
|
||||
);
|
||||
return -UNW_EINVAL;
|
||||
}
|
92
src/coredump/_UCD_access_reg.c
Normal file
92
src/coredump/_UCD_access_reg.c
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* 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_RAX] = offsetof(struct user_regs_struct, rax) / sizeof(long),
|
||||
[UNW_X86_RDX] = offsetof(struct user_regs_struct, rdx) / sizeof(long),
|
||||
[UNW_X86_RCX] = offsetof(struct user_regs_struct, rcx) / sizeof(long),
|
||||
[UNW_X86_RBX] = offsetof(struct user_regs_struct, rbx) / sizeof(long),
|
||||
[UNW_X86_RSI] = offsetof(struct user_regs_struct, rsi) / sizeof(long),
|
||||
[UNW_X86_RDI] = offsetof(struct user_regs_struct, rdi) / sizeof(long),
|
||||
[UNW_X86_RBP] = offsetof(struct user_regs_struct, rbp) / sizeof(long),
|
||||
[UNW_X86_RSP] = offsetof(struct user_regs_struct, rsp) / sizeof(long),
|
||||
[UNW_X86_RIP] = offsetof(struct user_regs_struct, rip) / sizeof(long),
|
||||
[UNW_X86_RFLAGS] = offsetof(struct user_regs_struct, rflags) / sizeof(long),
|
||||
[UNW_X86_TRAPNO] = offsetof(struct user_regs_struct, orig_rax) / sizeof(long),
|
||||
};
|
||||
#else
|
||||
#error Port me
|
||||
#endif
|
||||
|
||||
struct UCD_info *ui = arg;
|
||||
if (regnum < 0 || regnum >= (unw_regnum_t)ARRAY_SIZE(remap_regs))
|
||||
{
|
||||
Debug(0, "%s: bad regnum:%d\n", __func__, regnum);
|
||||
return -UNW_EINVAL;
|
||||
}
|
||||
regnum = remap_regs[regnum];
|
||||
|
||||
/* pr_reg is a long[] array, but it contains struct user_regs_struct's
|
||||
* image.
|
||||
*/
|
||||
Debug(1 "pr_reg[%d]:%ld (0x%lx)", regnum,
|
||||
(long)ui->prstatus->pr_reg[regnum],
|
||||
(long)ui->prstatus->pr_reg[regnum]
|
||||
);
|
||||
*valp = ui->prstatus->pr_reg[regnum];
|
||||
|
||||
return 0;
|
||||
}
|
36
src/coredump/_UCD_accessors.c
Normal file
36
src/coredump/_UCD_accessors.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
PROTECTED unw_accessors_t _UCD_accessors =
|
||||
{
|
||||
.find_proc_info = _UCD_find_proc_info,
|
||||
.put_unwind_info = _UCD_put_unwind_info,
|
||||
.get_dyn_info_list_addr = _UCD_get_dyn_info_list_addr,
|
||||
.access_mem = _UCD_access_mem,
|
||||
.access_reg = _UCD_access_reg,
|
||||
.access_fpreg = _UCD_access_fpreg,
|
||||
.resume = _UCD_resume,
|
||||
.get_proc_name = _UCD_get_proc_name
|
||||
};
|
379
src/coredump/_UCD_create.c
Normal file
379
src/coredump/_UCD_create.c
Normal file
|
@ -0,0 +1,379 @@
|
|||
/* 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. */
|
||||
|
||||
/* Endian detection */
|
||||
#include <limits.h>
|
||||
#include <byteswap.h>
|
||||
#include <endian.h>
|
||||
#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"
|
||||
|
||||
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 %d bits are supported",
|
||||
filename, 8 * 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)
|
||||
{
|
||||
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 is three 32-bit words: */
|
||||
/* Elf32_Word n_namesz; Length of the note's name */
|
||||
/* Elf32_Word n_descsz; Length of the note's descriptor */
|
||||
/* Elf32_Word n_type; Type */
|
||||
/* followed by name (padded to 32 bits(?)) and then descr */
|
||||
Elf32_Nhdr *note_hdr = ui->note_phdr;
|
||||
if (cur->p_filesz >= 3*4
|
||||
&& note_hdr->n_type == NT_PRSTATUS
|
||||
&& cur->p_filesz >= (3*4 + note_hdr->n_namesz + note_hdr->n_descsz + sizeof(*ui->prstatus))
|
||||
)
|
||||
{
|
||||
ui->prstatus = (void*) ((((long)note_hdr + sizeof(*note_hdr) + note_hdr->n_namesz) + 3) & ~3L);
|
||||
#if 0
|
||||
printf("pid:%d\n", ui->prstatus->pr_pid);
|
||||
printf("ebx:%ld\n", (long)ui->prstatus->pr_reg[0]);
|
||||
printf("ecx:%ld\n", (long)ui->prstatus->pr_reg[1]);
|
||||
printf("edx:%ld\n", (long)ui->prstatus->pr_reg[2]);
|
||||
printf("esi:%ld\n", (long)ui->prstatus->pr_reg[3]);
|
||||
printf("edi:%ld\n", (long)ui->prstatus->pr_reg[4]);
|
||||
printf("ebp:%ld\n", (long)ui->prstatus->pr_reg[5]);
|
||||
printf("eax:%ld\n", (long)ui->prstatus->pr_reg[6]);
|
||||
printf("xds:%ld\n", (long)ui->prstatus->pr_reg[7]);
|
||||
printf("xes:%ld\n", (long)ui->prstatus->pr_reg[8]);
|
||||
printf("xfs:%ld\n", (long)ui->prstatus->pr_reg[9]);
|
||||
printf("xgs:%ld\n", (long)ui->prstatus->pr_reg[10]);
|
||||
printf("orig_eax:%ld\n", (long)ui->prstatus->pr_reg[11]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
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->prstatus)
|
||||
{
|
||||
Debug(0, "No NT_PRSTATUS note found in '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ui;
|
||||
|
||||
err:
|
||||
_UCD_destroy(ui);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int _UCD_add_backing_file_at_segment(struct UCD_info *ui, int phdr_no, const char *filename)
|
||||
{
|
||||
if ((unsigned)phdr_no >= ui->phdrs_count)
|
||||
{
|
||||
Debug(0, "There is no segment %d in this coredump\n", phdr_no);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct coredump_phdr *phdr = &ui->phdrs[phdr_no];
|
||||
if (phdr->backing_filename)
|
||||
{
|
||||
Debug(0, "Backing file already added to segment %d\n", phdr_no);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
{
|
||||
Debug(0, "Can't open '%s'\n", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
phdr->backing_fd = fd;
|
||||
phdr->backing_filename = strdup(filename);
|
||||
|
||||
struct stat statbuf;
|
||||
if (fstat(fd, &statbuf) != 0)
|
||||
{
|
||||
Debug(0, "Can't stat '%s'\n", filename);
|
||||
goto err;
|
||||
}
|
||||
phdr->backing_filesize = (uoff_t)statbuf.st_size;
|
||||
|
||||
if (phdr->p_flags != (PF_X | PF_R))
|
||||
Debug(1, "Note: phdr[%u] is not r-x: flags are 0x%x\n", phdr_no, phdr->p_flags);
|
||||
|
||||
if (phdr->backing_filesize > phdr->p_memsz)
|
||||
{
|
||||
/* This is expected */
|
||||
Debug(2, "Note: phdr[%u] is %lld bytes, file is larger: %lld bytes\n",
|
||||
phdr_no,
|
||||
(unsigned long long)phdr->p_memsz,
|
||||
(unsigned long long)phdr->backing_filesize
|
||||
);
|
||||
}
|
||||
//TODO: else loudly complain? Maybe even fail?
|
||||
|
||||
if (phdr->p_filesz != 0)
|
||||
{
|
||||
//TODO: loop and compare in smaller blocks
|
||||
char *core_buf = malloc(phdr->p_filesz);
|
||||
char *file_buf = malloc(phdr->p_filesz);
|
||||
if (lseek(ui->coredump_fd, phdr->p_offset, SEEK_SET) != (off_t)phdr->p_offset
|
||||
|| (uoff_t)read(ui->coredump_fd, core_buf, phdr->p_filesz) != phdr->p_filesz
|
||||
)
|
||||
{
|
||||
Debug(0, "Error reading from coredump file\n");
|
||||
err_read:
|
||||
free(core_buf);
|
||||
free(file_buf);
|
||||
goto err;
|
||||
}
|
||||
if ((uoff_t)read(fd, file_buf, phdr->p_filesz) != phdr->p_filesz)
|
||||
{
|
||||
Debug(0, "Error reading from '%s'\n", filename);
|
||||
goto err_read;
|
||||
}
|
||||
int r = memcmp(core_buf, file_buf, phdr->p_filesz);
|
||||
free(core_buf);
|
||||
free(file_buf);
|
||||
if (r != 0)
|
||||
{
|
||||
Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file do not match\n",
|
||||
phdr_no, (unsigned long long)phdr->p_filesz
|
||||
);
|
||||
} else {
|
||||
Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file match\n",
|
||||
phdr_no, (unsigned long long)phdr->p_filesz
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* Success */
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (phdr->backing_fd >= 0)
|
||||
{
|
||||
close(phdr->backing_fd);
|
||||
phdr->backing_fd = -1;
|
||||
}
|
||||
free(phdr->backing_filename);
|
||||
phdr->backing_filename = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int _UCD_add_backing_file_at_vaddr(struct UCD_info *ui,
|
||||
unsigned long vaddr,
|
||||
const char *filename)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
struct coredump_phdr *phdr = &ui->phdrs[i];
|
||||
if (phdr->p_vaddr != vaddr)
|
||||
continue;
|
||||
/* It seems to match. Add it. */
|
||||
return _UCD_add_backing_file_at_segment(ui, i, filename);
|
||||
}
|
||||
return -1;
|
||||
}
|
50
src/coredump/_UCD_destroy.c
Normal file
50
src/coredump/_UCD_destroy.c
Normal file
|
@ -0,0 +1,50 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
void
|
||||
_UCD_destroy (struct UCD_info *ui)
|
||||
{
|
||||
if (!ui)
|
||||
return;
|
||||
|
||||
if (ui->coredump_fd >= 0)
|
||||
close(ui->coredump_fd);
|
||||
free(ui->coredump_filename);
|
||||
|
||||
invalidate_edi (&ui->edi);
|
||||
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
struct coredump_phdr *phdr = &ui->phdrs[i];
|
||||
free(phdr->backing_filename);
|
||||
if (phdr->backing_fd >= 0)
|
||||
close(phdr->backing_fd);
|
||||
}
|
||||
|
||||
free(ui->note_phdr);
|
||||
|
||||
free(ui);
|
||||
}
|
98
src/coredump/_UCD_elf_map_image.c
Normal file
98
src/coredump/_UCD_elf_map_image.c
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
static coredump_phdr_t *
|
||||
CD_elf_map_image(struct UCD_info *ui, coredump_phdr_t *phdr)
|
||||
{
|
||||
struct elf_image *ei = &ui->edi.ei;
|
||||
|
||||
if (phdr->backing_fd < 0)
|
||||
{
|
||||
/* Note: coredump file contains only phdr->p_filesz bytes.
|
||||
* We want to map bigger area (phdr->p_memsz bytes) to make sure
|
||||
* these pages are allocated, but non-accessible.
|
||||
*/
|
||||
/* addr, length, prot, flags, fd, fd_offset */
|
||||
ei->image = mmap(NULL, phdr->p_memsz, PROT_READ, MAP_PRIVATE, ui->coredump_fd, phdr->p_offset);
|
||||
if (ei->image == MAP_FAILED)
|
||||
{
|
||||
ei->image = NULL;
|
||||
return NULL;
|
||||
}
|
||||
ei->size = phdr->p_filesz;
|
||||
size_t remainder_len = phdr->p_memsz - phdr->p_filesz;
|
||||
if (remainder_len > 0)
|
||||
{
|
||||
void *remainder_base = (char*) ei->image + phdr->p_filesz;
|
||||
munmap(remainder_base, remainder_len);
|
||||
}
|
||||
} else {
|
||||
/* We have a backing file for this segment.
|
||||
* This file is always longer than phdr->p_memsz,
|
||||
* and if phdr->p_filesz !=0, first phdr->p_filesz bytes in coredump
|
||||
* are the same as first bytes in the file. (Thus no need to map coredump)
|
||||
* We map the entire file:
|
||||
* unwinding may need data which is past phdr->p_memsz bytes.
|
||||
*/
|
||||
/* addr, length, prot, flags, fd, fd_offset */
|
||||
ei->image = mmap(NULL, phdr->backing_filesize, PROT_READ, MAP_PRIVATE, phdr->backing_fd, 0);
|
||||
if (ei->image == MAP_FAILED)
|
||||
{
|
||||
ei->image = NULL;
|
||||
return NULL;
|
||||
}
|
||||
ei->size = phdr->backing_filesize;
|
||||
}
|
||||
|
||||
/* Check ELF header for sanity */
|
||||
if (!elf_w(valid_object)(ei))
|
||||
{
|
||||
munmap(ei->image, ei->size);
|
||||
ei->image = NULL;
|
||||
ei->size = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return phdr;
|
||||
}
|
||||
|
||||
HIDDEN coredump_phdr_t *
|
||||
_UCD_get_elf_image(struct UCD_info *ui, unw_word_t ip)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < ui->phdrs_count; i++)
|
||||
{
|
||||
coredump_phdr_t *phdr = &ui->phdrs[i];
|
||||
if (phdr->p_vaddr <= ip && ip < phdr->p_vaddr + phdr->p_memsz)
|
||||
{
|
||||
phdr = CD_elf_map_image(ui, phdr);
|
||||
return phdr;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
163
src/coredump/_UCD_find_proc_info.c
Normal file
163
src/coredump/_UCD_find_proc_info.c
Normal file
|
@ -0,0 +1,163 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
static int
|
||||
get_unwind_info(struct UCD_info *ui, unw_addr_space_t as, unw_word_t ip)
|
||||
{
|
||||
unsigned long segbase, mapoff;
|
||||
|
||||
#if UNW_TARGET_IA64 && defined(__linux)
|
||||
if (!ui->edi.ktab.start_ip && _Uia64_get_kernel_table (&ui->edi.ktab) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
if (ui->edi.ktab.format != -1 && ip >= ui->edi.ktab.start_ip && ip < ui->edi.ktab.end_ip)
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
if ((ui->edi.di_cache.format != -1
|
||||
&& ip >= ui->edi.di_cache.start_ip && ip < ui->edi.di_cache.end_ip)
|
||||
#if UNW_TARGET_ARM
|
||||
|| (ui->edi.di_debug.format != -1
|
||||
&& ip >= ui->edi.di_arm.start_ip && ip < ui->edi.di_arm.end_ip)
|
||||
#endif
|
||||
|| (ui->edi.di_debug.format != -1
|
||||
&& ip >= ui->edi.di_debug.start_ip && ip < ui->edi.di_debug.end_ip))
|
||||
return 0;
|
||||
|
||||
invalidate_edi (&ui->edi);
|
||||
|
||||
/* Used to be tdep_get_elf_image() in ptrace unwinding code */
|
||||
coredump_phdr_t *phdr = _UCD_get_elf_image(ui, ip);
|
||||
if (!phdr)
|
||||
{
|
||||
Debug(1, "%s returns error: _UCD_get_elf_image failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
/* segbase: where it is mapped in virtual memory */
|
||||
/* mapoff: offset in the file */
|
||||
segbase = phdr->p_vaddr;
|
||||
/*mapoff = phdr->p_offset; WRONG! phdr->p_offset is the offset in COREDUMP file */
|
||||
mapoff = 0;
|
||||
///FIXME. text segment is USUALLY, not always, at offset 0 in the binary/.so file.
|
||||
// ensure that at initialization.
|
||||
|
||||
/* Here, SEGBASE is the starting-address of the (mmap'ped) segment
|
||||
which covers the IP we're looking for. */
|
||||
if (dwarf_find_unwind_table(&ui->edi, as, phdr->backing_filename, segbase, mapoff, ip) < 0)
|
||||
{
|
||||
Debug(1, "%s returns error: dwarf_find_unwind_table failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
/* This can happen in corner cases where dynamically generated
|
||||
code falls into the same page that contains the data-segment
|
||||
and the page-offset of the code is within the first page of
|
||||
the executable. */
|
||||
if (ui->edi.di_cache.format != -1
|
||||
&& (ip < ui->edi.di_cache.start_ip || ip >= ui->edi.di_cache.end_ip))
|
||||
ui->edi.di_cache.format = -1;
|
||||
|
||||
if (ui->edi.di_debug.format != -1
|
||||
&& (ip < ui->edi.di_debug.start_ip || ip >= ui->edi.di_debug.end_ip))
|
||||
ui->edi.di_debug.format = -1;
|
||||
|
||||
if (ui->edi.di_cache.format == -1
|
||||
#if UNW_TARGET_ARM
|
||||
&& ui->edi.di_arm.format == -1
|
||||
#endif
|
||||
&& ui->edi.di_debug.format == -1)
|
||||
{
|
||||
Debug(1, "%s returns error: all formats are -1\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
Debug(1, "%s returns success\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_UCD_find_proc_info (unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
|
||||
int need_unwind_info, void *arg)
|
||||
{
|
||||
struct UCD_info *ui = arg;
|
||||
|
||||
Debug(1, "%s: entering\n", __func__);
|
||||
|
||||
int ret = -UNW_ENOINFO;
|
||||
|
||||
if (get_unwind_info(ui, as, ip) < 0) {
|
||||
Debug(1, "%s returns error: get_unwind_info failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
#if UNW_TARGET_IA64
|
||||
if (ui->edi.ktab.format != -1)
|
||||
{
|
||||
/* The kernel unwind table resides in local memory, so we have
|
||||
to use the local address space to search it. Since
|
||||
_UCD_put_unwind_info() has no easy way of detecting this
|
||||
case, we simply make a copy of the unwind-info, so
|
||||
_UCD_put_unwind_info() can always free() the unwind-info
|
||||
without ill effects. */
|
||||
ret = tdep_search_unwind_table (unw_local_addr_space, ip, &ui->edi.ktab, pi,
|
||||
need_unwind_info, arg);
|
||||
if (ret >= 0)
|
||||
{
|
||||
if (!need_unwind_info)
|
||||
pi->unwind_info = NULL;
|
||||
else
|
||||
{
|
||||
void *mem = malloc (pi->unwind_info_size);
|
||||
|
||||
if (!mem)
|
||||
return -UNW_ENOMEM;
|
||||
memcpy (mem, pi->unwind_info, pi->unwind_info_size);
|
||||
pi->unwind_info = mem;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_cache.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_cache,
|
||||
pi, need_unwind_info, arg);
|
||||
|
||||
#if UNW_TARGET_ARM
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_arm.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_arm, pi,
|
||||
need_unwind_info, arg);
|
||||
#endif
|
||||
|
||||
if (ret == -UNW_ENOINFO && ui->edi.di_debug.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &ui->edi.di_debug, pi,
|
||||
need_unwind_info, arg);
|
||||
|
||||
Debug(1, "%s: returns %d\n", __func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
70
src/coredump/_UCD_get_proc_name.c
Normal file
70
src/coredump/_UCD_get_proc_name.c
Normal file
|
@ -0,0 +1,70 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
|
||||
/* Find the ELF image that contains IP and return the "closest"
|
||||
procedure name, if there is one. With some caching, this could be
|
||||
sped up greatly, but until an application materializes that's
|
||||
sensitive to the performance of this routine, why bother... */
|
||||
static int
|
||||
elf_w (CD_get_proc_name) (struct UCD_info *ui, unw_addr_space_t as, unw_word_t ip,
|
||||
char *buf, size_t buf_len, unw_word_t *offp)
|
||||
{
|
||||
unsigned long segbase, mapoff;
|
||||
int ret;
|
||||
|
||||
/* Used to be tdep_get_elf_image() in ptrace unwinding code */
|
||||
coredump_phdr_t *cphdr = _UCD_get_elf_image(ui, ip);
|
||||
if (!cphdr)
|
||||
{
|
||||
Debug(1, "%s returns error: _UCD_get_elf_image failed\n", __func__);
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
/* segbase: where it is mapped in virtual memory */
|
||||
/* mapoff: offset in the file */
|
||||
segbase = cphdr->p_vaddr;
|
||||
/*mapoff = phdr->p_offset; WRONG! phdr->p_offset is the offset in COREDUMP file */
|
||||
mapoff = 0;
|
||||
|
||||
ret = elf_w (get_proc_name_in_image) (as, &ui->edi.ei, segbase, mapoff, ip, buf, buf_len, offp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
_UCD_get_proc_name (unw_addr_space_t as, unw_word_t ip,
|
||||
char *buf, size_t buf_len, unw_word_t *offp, void *arg)
|
||||
{
|
||||
struct UCD_info *ui = arg;
|
||||
|
||||
#if ELF_CLASS == ELFCLASS64
|
||||
return _Uelf64_CD_get_proc_name (ui, as, ip, buf, buf_len, offp);
|
||||
#elif ELF_CLASS == ELFCLASS32
|
||||
return _Uelf32_CD_get_proc_name (ui, as, ip, buf, buf_len, offp);
|
||||
#else
|
||||
return -UNW_ENOINFO;
|
||||
#endif
|
||||
}
|
93
src/coredump/_UCD_internal.h
Normal file
93
src/coredump/_UCD_internal.h
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* 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;
|
||||
|
||||
|
||||
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 elf_prstatus *prstatus; /* points inside note_phdr */
|
||||
|
||||
struct elf_dyn_info edi;
|
||||
};
|
||||
|
||||
extern coredump_phdr_t * _UCD_get_elf_image(struct UCD_info *ui, unw_word_t ip);
|
||||
|
||||
#endif
|
53
src/coredump/_UCD_lib.h
Normal file
53
src/coredump/_UCD_lib.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
Copyright (C) 2010 ABRT team
|
||||
Copyright (C) 2010 RedHat Inc
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef _UCD_lib_h
|
||||
#define _UCD_lib_h
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <setjmp.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <sys/param.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <syslog.h>
|
||||
|
||||
#endif
|
34
src/coredump/_UPT_access_fpreg.c
Normal file
34
src/coredump/_UPT_access_fpreg.c
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
|
||||
int write, void *arg)
|
||||
{
|
||||
print_error (__func__);
|
||||
print_error (" not implemented\n");
|
||||
return -UNW_EINVAL;
|
||||
}
|
5
src/coredump/_UPT_elf.c
Normal file
5
src/coredump/_UPT_elf.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
/* We need to get a separate copy of the ELF-code into
|
||||
libunwind-coredump since it cannot (and must not) have any ELF
|
||||
dependencies on libunwind. */
|
||||
#include "libunwind_i.h" /* get ELFCLASS defined */
|
||||
#include "../elfxx.c"
|
108
src/coredump/_UPT_get_dyn_info_list_addr.c
Normal file
108
src/coredump/_UPT_get_dyn_info_list_addr.c
Normal file
|
@ -0,0 +1,108 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003-2005 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
#if UNW_TARGET_IA64 && defined(__linux)
|
||||
# include "elf64.h"
|
||||
# include "os-linux.h"
|
||||
|
||||
static inline int
|
||||
get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg,
|
||||
int *countp)
|
||||
{
|
||||
unsigned long lo, hi, off;
|
||||
struct UPT_info *ui = arg;
|
||||
struct map_iterator mi;
|
||||
char path[PATH_MAX];
|
||||
unw_dyn_info_t *di;
|
||||
unw_word_t res;
|
||||
int count = 0;
|
||||
|
||||
maps_init (&mi, ui->pid);
|
||||
while (maps_next (&mi, &lo, &hi, &off))
|
||||
{
|
||||
if (off)
|
||||
continue;
|
||||
|
||||
invalidate_edi (&ui->edi);
|
||||
|
||||
if (elf_map_image (&ui->ei, path) < 0)
|
||||
/* ignore unmappable stuff like "/SYSV00001b58 (deleted)" */
|
||||
continue;
|
||||
|
||||
Debug (16, "checking object %s\n", path);
|
||||
|
||||
di = dwarf_find_unwind_table (&ui->edi, as, path, lo, off);
|
||||
if (di)
|
||||
{
|
||||
res = _Uia64_find_dyn_list (as, di, arg);
|
||||
if (res && count++ == 0)
|
||||
{
|
||||
Debug (12, "dyn_info_list_addr = 0x%lx\n", (long) res);
|
||||
*dil_addr = res;
|
||||
}
|
||||
}
|
||||
}
|
||||
maps_close (&mi);
|
||||
*countp = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int
|
||||
get_list_addr (unw_addr_space_t as, unw_word_t *dil_addr, void *arg,
|
||||
int *countp)
|
||||
{
|
||||
# warning Implement get_list_addr(), please.
|
||||
*countp = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int
|
||||
_UCD_get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dil_addr,
|
||||
void *arg)
|
||||
{
|
||||
int count, ret;
|
||||
|
||||
Debug (12, "looking for dyn_info list\n");
|
||||
|
||||
if ((ret = get_list_addr (as, dil_addr, arg, &count)) < 0)
|
||||
return ret;
|
||||
|
||||
/* If multiple dynamic-info list addresses are found, we would have
|
||||
to determine which was is the one actually in use (since the
|
||||
dynamic name resolution algorithm will pick one "winner").
|
||||
Perhaps we'd have to track them all until we find one that's
|
||||
non-empty. Hopefully, this case simply will never arise, since
|
||||
only libunwind defines the dynamic info list head. */
|
||||
assert (count <= 1);
|
||||
|
||||
return (count > 0) ? 0 : -UNW_ENOINFO;
|
||||
}
|
36
src/coredump/_UPT_put_unwind_info.c
Normal file
36
src/coredump/_UPT_put_unwind_info.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
void
|
||||
_UCD_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *pi, void *arg)
|
||||
{
|
||||
if (!pi->unwind_info)
|
||||
return;
|
||||
free (pi->unwind_info);
|
||||
pi->unwind_info = NULL;
|
||||
}
|
35
src/coredump/_UPT_resume.c
Normal file
35
src/coredump/_UPT_resume.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* libunwind - a platform-independent unwind library
|
||||
Copyright (C) 2003 Hewlett-Packard Co
|
||||
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
||||
|
||||
This file is part of libunwind.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
|
||||
#include "_UCD_lib.h"
|
||||
#include "_UCD_internal.h"
|
||||
|
||||
int
|
||||
_UCD_resume (unw_addr_space_t as, unw_cursor_t *c, void *arg)
|
||||
{
|
||||
print_error (__func__);
|
||||
print_error (" not implemented\n");
|
||||
return -UNW_EINVAL;
|
||||
}
|
315
src/coredump/example-core-unwind.c
Normal file
315
src/coredump/example-core-unwind.c
Normal file
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* 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 <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;
|
||||
ip = uc->uc_mcontext.gregs[REG_EIP];
|
||||
dprintf(2, "signal:%d address:0x%lx ip:0x%lx\n",
|
||||
sig,
|
||||
/* this is void*, but using %p would print "(null)"
|
||||
* even for ptrs which are not exactly 0, but, say, 0x123:
|
||||
*/
|
||||
(long)info->si_addr,
|
||||
ip);
|
||||
|
||||
{
|
||||
/* glibc extension */
|
||||
void *array[50];
|
||||
int size;
|
||||
size = backtrace(array, 50);
|
||||
backtrace_symbols_fd(array, size, 2);
|
||||
}
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static void install_sigsegv_handler(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = handle_sigsegv;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sigaction(SIGSEGV, &sa, NULL);
|
||||
sigaction(SIGILL, &sa, NULL);
|
||||
sigaction(SIGFPE, &sa, NULL);
|
||||
sigaction(SIGBUS, &sa, NULL);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
unw_addr_space_t as;
|
||||
struct UCD_info *ui;
|
||||
unw_cursor_t c;
|
||||
int ret;
|
||||
|
||||
install_sigsegv_handler();
|
||||
|
||||
const char *progname = strrchr(argv[0], '/');
|
||||
if (progname)
|
||||
progname++;
|
||||
else
|
||||
progname = argv[0];
|
||||
|
||||
if (!argv[1])
|
||||
error_msg_and_die("Usage: %s COREDUMP [SEGMENT_NO:BINARY_FILE]...", progname);
|
||||
|
||||
msg_prefix = progname;
|
||||
|
||||
as = unw_create_addr_space(&_UCD_accessors, 0);
|
||||
if (!as)
|
||||
error_msg_and_die("unw_create_addr_space() failed");
|
||||
|
||||
ui = _UCD_create(argv[1]);
|
||||
if (!ui)
|
||||
error_msg_and_die("_UCD_create('%s') failed", argv[1]);
|
||||
ret = unw_init_remote(&c, as, ui);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("unw_init_remote() failed: ret=%d\n", ret);
|
||||
|
||||
argv += 2;
|
||||
while (*argv)
|
||||
{
|
||||
char *colon = strchr(*argv, ':');
|
||||
if (!colon)
|
||||
error_msg_and_die("Bad format: '%s'", *argv);
|
||||
*colon = '\0';
|
||||
unsigned n = atoi(*argv);
|
||||
*colon = ':';
|
||||
if (_UCD_add_backing_file_at_segment(ui, n, colon + 1) < 0)
|
||||
error_msg_and_die("Can't add backing file '%s'", *argv);
|
||||
argv++;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
unw_word_t ip;
|
||||
ret = unw_get_reg(&c, UNW_REG_IP, &ip);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("unw_get_reg(UNW_REG_IP) failed: ret=%d\n", ret);
|
||||
|
||||
unw_proc_info_t pi;
|
||||
ret = unw_get_proc_info(&c, &pi);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("unw_get_proc_info(ip=0x%lx) failed: ret=%d\n", (long) ip, ret);
|
||||
printf("\tip=0x%08lx proc=%08lx-%08lx handler=0x%08lx lsda=0x%08lx\n",
|
||||
(long) ip,
|
||||
(long) pi.start_ip, (long) pi.end_ip,
|
||||
(long) pi.handler, (long) pi.lsda);
|
||||
|
||||
log("step");
|
||||
ret = unw_step(&c);
|
||||
log("step done:%d", ret);
|
||||
if (ret < 0)
|
||||
error_msg_and_die("FAILURE: unw_step() returned %d", ret);
|
||||
if (ret == 0)
|
||||
break;
|
||||
}
|
||||
log("stepping ended");
|
||||
|
||||
_UCD_destroy(ui);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue