1
0
Fork 0
mirror of https://github.com/tobast/libunwind-eh_elf.git synced 2024-05-12 09:25:16 +02:00

Add support for unwinding coredump images

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
This commit is contained in:
Arun Sharma 2012-03-12 18:46:57 -07:00
parent d276b7a961
commit b08ae72d6c
21 changed files with 1769 additions and 2 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

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

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

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

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

379
src/coredump/_UCD_create.c Normal file
View 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;
}

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

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