This is the mail archive of the
gdb@sourceware.org
mailing list for the GDB project.
Unwind method using CFI in core dump sections?
- From: Ben Cohen <ben_c at tiscali dot co dot uk>
- To: gdb at sourceware dot org
- Date: Thu, 30 May 2013 13:28:49 +0100 (BST)
- Subject: Unwind method using CFI in core dump sections?
I have found that it is possible to identify the DWARF Call Frame
Information (CFI) if it is available in a core dump, as described
below. It would be useful to add a new unwind method in gdb to do this
so that it is possible to analyse core dumps for which the shared
libraries are not available. (I don't know anything about gdb
internals though.)
If you have a core dump from a system with
different glibc and other library versions then gdb is likely to show
corrupted stack traces unless you have a copy of those libraries, which
contain CFI to tell the debugger how to unwind the stack, and set solib-
search-path. Unfortunately it's not always easy or possible to obtain
a copy of the libraries, for example if they are on customer systems to
which we don't have access.
Actually the CFI is present in the library
in the process's memory image, because C++ uses it to unwind when
processing exceptions. Unfortunately these segments aren't typically
put into a core file, but they could be.
On Linux since 2.6.28,
"echo 127 > /proc/PID/coredump_filter" will tell the kernel to dump
other memory mapping types including the library segments, but it does
not by default. (My employer, Kognitio, has a custom core file
generator that also dumps these segments. Of course, doing this makes
core files larger.)
Given such a core file, gdb could infer the CFI
and use that to unwind when there is no library available. Of course,
just as when using stripped libraries the frames won't have symbols,
but the frame addresses and registers are correct. This would
presumably be a bit like using __jit_debug_descriptor for JIT/runtime-
generated code but a bit more difficult.
To show that this is actually
possible, below is a program I wrote for 32-bit x86 Linux. I
frequently use it to generate stub libraries containing CFI that gdb
can then use to obtain correct stack traces for a core file without
having the appropriate libraries. It is somewhat fragile, partly
because it tries to guess the library names, which gdb presumably
wouldn't have to.
It works for glibc and other libraries (but not for
really old library versions without CFI) including the example C
program and asm library below.
Thanks,
Ben.
Example session using
wxcoretool:
[ubuntu tmp]$ gcc -o wxcoretool wxcoretool.c -Wall
[ubuntu
tmp]$ gcc -Wl,-soname,library.so -shared -fPIC -o library.so library.S
[ubuntu tmp]$ gcc -ggdb -o frametest frametest.c library.so
[ubuntu
tmp]$ echo 127 > /proc/self/coredump_filter
[ubuntu tmp]$ ulimit -c
unlimited
[ubuntu tmp]$ LD_LIBRARY_PATH=. ./frametest
Trace/breakpoint
trap (core dumped)
[ubuntu tmp]$ gdb frametest core --ex backtrace --ex
quit
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software
Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show
copying"
and "show warranty" for details.
This GDB was configured as
"i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...Reading symbols from
/tmp/tmp/frametest...done.
[New LWP 2856]
Core was generated by `.
/frametest'.
Program terminated with signal 5, Trace/breakpoint trap.
#0 0xb77d04fd in library_function () from ./library.so
#0 0xb77d04fd
in library_function () from ./library.so
#1 0x08048564 in f2 (x=2,
y=98 'b') at frametest.c:11
#2 0x0804858e in f1 (x=1, y=97 'a') at
frametest.c:16
#3 0x080485ad in main () at frametest.c:21
[ubuntu
tmp]$ rm library.so
[ubuntu tmp]$ gdb frametest core --ex backtrace --
ex quit
GNU gdb (GDB) 7.5-ubuntu
Copyright (C) 2012 Free Software
Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show
copying"
and "show warranty" for details.
This GDB was configured as
"i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...Reading symbols from
/tmp/tmp/frametest...done.
[New LWP 2856]
warning: Could not load
shared library symbols for ./library.so.
Do you need "set solib-search-
path" or "set sysroot"?
Core was generated by `./frametest'.
Program
terminated with signal 5, Trace/breakpoint trap.
#0 0xb77d04fd in
?? ()
#0 0xb77d04fd in ?? ()
[ubuntu tmp]$ ./wxcoretool -f. core
Creating stub library libc.so.6
Creating stub library library.so
Creating stub library library.so
Failed to open file
Creating stub
library libunknown_14
[ubuntu tmp]$ gdb --ex 'set solib-search-path
lib' \
> --ex 'set sysroot .' \
> --ex 'file frametest' \
>
--ex 'core core' \
> --ex backtrace \
> --ex quit
GNU gdb (GDB)
7.5-ubuntu
Copyright (C) 2012 Free Software Foundation, Inc.
License
GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show
copying"
and "show warranty" for details.
This GDB was configured as
"i686-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.Reading symbols from
/tmp/tmp/frametest...done.
[New LWP 2856]
Core was generated by `.
/frametest'.
Program terminated with signal 5, Trace/breakpoint trap.
#0 0xb77d04fd in ?? ()
#0 0xb77d04fd in ?? ()
#1 0x08048564 in f2
(x=2, y=98 'b') at frametest.c:11
#2 0x0804858e in f1 (x=1, y=97 'a')
at frametest.c:16
#3 0x080485ad in main () at frametest.c:21
##########################################################################
# library: example library with Call Frame Information
#
# Compile
using:
# gcc -Wl,-soname,library.so -shared -fPIC -o library.so
library.S
#
.text
.globl library_function
.type
library_function, @function
library_function:
.cfi_startproc
push %ebp
.cfi_def_cfa_offset 8
.cfi_offset %
ebp, -8
mov $0x0, %ebp
sub $0x80000000,%esp
.cfi_adjust_cfa_offset 0x80000000
int $0x03
add $0x80000000,%esp
.cfi_adjust_cfa_offset -0
x80000000
pop %ebp
ret
.cfi_endproc
.size library_function, .-library_function
.section .
note.GNU-stack,"",@progbits
/**************************************************************************
* frametest: example program
*
* Compile using:
* gcc -ggdb -o
frametest frametest.c library.so
*/
void library_function(int x, char
y);
int f2(int x, char y)
{
library_function(x + 1, y + 1);
}
int f1
(int x, char y)
{
f2(x + 1, y + 1);
}
int main()
{
f1(1, 'a');
}
/**************************************************************************
* wxcoretool: Generate stub libraries with CFI for 32-bit i386 Linux
corefiles
*
* Copyright (C) 2010-2013 Ben Cohen / Kognitio Ltd.
*
*
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 3 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.
*
* Compile using:
* gcc -o wxcoretool wxcoretool.c -
Wall
* Run using:
* wxcoretool -f <target-dir> <core-file>
*
gdb --ex 'set solib-search-path <target-dir>/lib' \
* --ex
'set sysroot <target-dir>' \
* --ex 'file <exec-file>' \
* --ex 'core <core-file>'
*/
#define _GNU_SOURCE
#include
<sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include
<stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include
<elf.h>
int debug = 0;
void check(bool cond, char *msg)
{
if (!
cond)
{
fprintf(stderr, "%s", msg);
exit(1);
}
}
void *xmalloc(int size)
{
void *ret = malloc(size);
check(ret != NULL, "malloc() failed");
return ret;
}
void readat
(int fd, void *buf, size_t count, size_t offset)
{
size_t ret;
size_t bytes_read = 0;
ret = lseek(fd, offset, SEEK_SET);
check
(offset == ret, "lseek() failed\n");
while (bytes_read < count)
{
ret = read(fd, buf + bytes_read, count - bytes_read);
check(ret > 0, "read() failed\n");
bytes_read += ret;
}
}
bool memreadat(char *segment, int segsz, void *buf, size_t
count, size_t offset)
{
if (count + offset > segsz)
return
false;
memcpy(buf, segment + offset, count);
return true;
}
/* We create a stub library. This is more complicated than the minimal
ELF
* library, but GDB is a little fussy. */
void CreateStubLibrary
(char *libname, int eh_frame_offset,
char
*eh_frame, int eh_frame_len,
int LoadOffset)
{
int libfd;
int nSecs = 3;
Elf32_Ehdr ElfHeader;
Elf32_Phdr ProgHeader;
Elf32_Shdr SecHeader[nSecs];
int EHSize
= sizeof(Elf32_Ehdr);
int PHSize = sizeof(Elf32_Phdr);
int
SHSize = sizeof(Elf32_Shdr);
char *eh_frame_str = ".eh_frame";
char *shstrtab_str = ".shstrtab";
int eh_frame_strlen = strlen
(eh_frame_str) + 1;
int shstrtab_strlen = strlen(shstrtab_str) + 1;
int shstrtab_offset = eh_frame_offset + eh_frame_len;
int
shstrtab_len = eh_frame_strlen + shstrtab_strlen;
int EH_offset =
shstrtab_offset + shstrtab_len;
int rc;
printf("Creating stub
library %s\n", libname);
libfd = open(libname, O_WRONLY|O_CREAT,
0555);
if (libfd < 0)
{
printf("Failed to open
file\n");
return;
}
/* Write the ELF header */
memset(&ElfHeader, 0, EHSize);
ElfHeader.e_ident[0] = ELFMAG0;
ElfHeader.e_ident[1] = ELFMAG1;
ElfHeader.e_ident[2] = ELFMAG2;
ElfHeader.e_ident[3] = ELFMAG3;
ElfHeader.e_ident[4] = ELFCLASS32;
ElfHeader.e_ident[5] = ELFDATA2LSB;
ElfHeader.e_ident[6] =
EV_CURRENT;
ElfHeader.e_type = ET_DYN;
ElfHeader.e_machine =
EM_386;
ElfHeader.e_version = EV_CURRENT;
ElfHeader.e_entry =
0;
ElfHeader.e_phoff = EHSize;
ElfHeader.e_shoff = EH_offset;
ElfHeader.e_flags = 0;
ElfHeader.e_ehsize = EHSize;
ElfHeader.e_phentsize = PHSize;
ElfHeader.e_phnum = 1;
ElfHeader.e_shentsize = SHSize;
ElfHeader.e_shnum = nSecs;
ElfHeader.e_shstrndx = 2;
rc = write(libfd, &ElfHeader, EHSize);
if (rc != EHSize)
{
printf("Failed to write to
file\n");
close(libfd);
return;
}
/* Write the
program header */
memset(&ProgHeader, 0, PHSize);
ProgHeader.
p_type = PT_LOAD;
ProgHeader.p_offset = 0;
ProgHeader.p_vaddr =
LoadOffset;
ProgHeader.p_paddr = LoadOffset;
ProgHeader.
p_filesz = EH_offset;
ProgHeader.p_memsz = ProgHeader.p_filesz;
ProgHeader.p_flags = PF_X | PF_R;
ProgHeader.p_align = 0x1000;
rc = write(libfd, &ProgHeader, PHSize * 1);
if (rc != PHSize * 1)
{
printf("Failed to write to file\n");
close
(libfd);
return;
}
/* Write the .eh_frame section at
its original location */
check(eh_frame_offset > EHSize + PHSize *
1, "eh_frame_offset too small\n");
rc = lseek(libfd,
eh_frame_offset, SEEK_SET);
if (rc != eh_frame_offset)
{
printf("Failed to seek\n");
close(libfd);
return;
}
rc = write(libfd, eh_frame, eh_frame_len);
if (rc
!= eh_frame_len)
{
printf("Failed to write to file\n");
close(libfd);
return;
}
/* Write the section
string table, .shstrtab */
rc = write(libfd, shstrtab_str,
shstrtab_strlen);
if (rc != shstrtab_strlen)
{
printf
("Failed to write to file\n");
close(libfd);
return;
}
rc = write(libfd, eh_frame_str, eh_frame_strlen);
if (rc !
= eh_frame_strlen)
{
printf("Failed to write to file\n");
close(libfd);
return;
}
/* Write the section
headers */
memset(&SecHeader, 0, SHSize * nSecs);
SecHeader[0].
sh_name = shstrtab_strlen - 1;
SecHeader[0].sh_type =
SHT_NULL; /* First one: empty */
SecHeader[1].sh_name =
shstrtab_strlen; /* Third one: .eh_frame */
SecHeader[1].sh_type
= SHT_PROGBITS;
SecHeader[1].sh_flags = SHF_ALLOC;
SecHeader[1].
sh_addr = eh_frame_offset + LoadOffset;
SecHeader[1].sh_offset =
eh_frame_offset;
SecHeader[1].sh_size = eh_frame_len;
SecHeader
[1].sh_link = 0;
SecHeader[1].sh_info = 0;
SecHeader[1].
sh_addralign = 4;
SecHeader[1].sh_entsize = 0;
SecHeader[2].
sh_name = 0; /* Second one: .shstrtab */
SecHeader
[2].sh_type = SHT_STRTAB;
SecHeader[2].sh_flags = 0;
SecHeader
[2].sh_addr = 0;
SecHeader[2].sh_offset = shstrtab_offset;
SecHeader[2].sh_size = shstrtab_len;
SecHeader[2].sh_link = 0;
SecHeader[2].sh_info = 0;
SecHeader[2].sh_addralign = 1;
SecHeader[2].sh_entsize = 0;
rc = write(libfd, &SecHeader, SHSize
* nSecs);
if (rc != SHSize * nSecs)
{
printf("Failed to
write to file\n");
close(libfd);
return;
}
close(libfd);
}
bool ProcessEhFrameHdr(char *segment, int size, char
*eh_frame_hdr, int ehsize,
char *libname, int
LoadOffset)
{
signed int eh_frame_ptr;
int fde_count;
char
*eh_frame;
int eh_frame_len;
char *eh_frame_end;
int
fdes_counted;
if (debug)
{
printf("Object eh_frame_hdr:
\n");
printf(" version: %d\n", eh_frame_hdr[0]);
printf(" eh_frame_ptr_enc: %d\n", eh_frame_hdr[1]);
printf(" fde_count_enc: %d\n", eh_frame_hdr[2]);
printf(" table_enc: %d\n", eh_frame_hdr[3]);
}
if (eh_frame_hdr[0] != 1)
{
if (debug)
printf("Bad eh_frame_hdr version\n");
return 0;
}
/*
XXX I can't be bothered to implement them all unless necessary */
check((eh_frame_hdr[1] & 0xF0) == 0x10, "eh_frame_ptr not relative to
pc\n");
check((eh_frame_hdr[1] & 0x0F) == 0x0b, "eh_frame_ptr not
signed int\n");
check(eh_frame_hdr[2] == 0x03, "fde_count_enc not
unsigned int\n");
eh_frame_ptr = ((int *)eh_frame_hdr)[1];
fde_count = ((int *)eh_frame_hdr)[2];
if (fde_count == 0)
{
if (debug)
printf("No fde search table\n");
return 0;
}
eh_frame = eh_frame_hdr + eh_frame_ptr + 4;
if (debug)
{
printf(" eh_frame_ptr: %d\n",
eh_frame_ptr);
printf(" eh_frame: %d\n", eh_frame
- segment);
printf(" fde_count: %d\n", fde_count);
}
check(eh_frame <= segment + size, "eh_frame not in
segment\n");
/* Frame length is that of all the FDEs plus a
zero int at the end */
fdes_counted = 0;
eh_frame_end =
eh_frame;
while (((int *)eh_frame_end)[0] != 0 && fdes_counted <
fde_count)
{
if (((int *)eh_frame_end)[1] != 0)
{
fdes_counted ++;
if (debug)
{
printf(" FDE: %d at %d\n",
((int *)eh_frame_end)[0],
eh_frame_end - segment);
}
}
else
{
if (debug)
{
printf("
CIE: %d at %d\n",
((int *)
eh_frame_end)[0],
eh_frame_end - segment);
}
}
eh_frame_end += ((int *)eh_frame_end)
[0] + 4;
check(eh_frame_end <= segment + size, "eh_frame not in
segment\n");
}
eh_frame_end += 4;
check(eh_frame_end <=
segment + size, "eh_frame not in segment\n");
eh_frame_len =
eh_frame_end - eh_frame;
if (debug)
{
printf("
eh_frame_end: %d\n", eh_frame_end - segment);
printf("
eh_frame_len: %d\n", eh_frame_len);
printf("
fdes_counted: %d\n", fdes_counted);
}
if (strncmp
(libname, "ld-linux.so", 11) != 0)
{
CreateStubLibrary
(libname, eh_frame - segment, eh_frame, eh_frame_len,
LoadOffset);
}
else
{
if
(debug)
{
printf("Not creating stub library for %
s\n", libname);
}
}
return 1;
}
void
ProcessObject(char *segment, int size, char *libname)
{
Elf32_Ehdr
ElfHeader;
int PHOffset, PHCount;
Elf32_Phdr ProgHeader;
bool ret;
int i;
int LoadOffset = 0;
int EhFrameOffset = 0;
int EhFrameSize = 0;
ret = memreadat(segment, size, (void *)
&ElfHeader, sizeof(ElfHeader), 0);
if (!ret)
return;
if
(debug)
{
printf("Object ELF Header:\n");
printf
(" e_ident: %.16s\n", ElfHeader.e_ident);
printf("
e_type: %d\n", ElfHeader.e_type);
printf("
e_machine: %d\n", ElfHeader.e_machine);
printf("
e_version: %d\n", ElfHeader.e_version);
printf("
e_entry: %d\n", ElfHeader.e_entry);
printf("
e_phoff: %d\n", ElfHeader.e_phoff);
printf("
e_shoff: %d\n", ElfHeader.e_shoff);
printf("
e_flags: %d\n", ElfHeader.e_flags);
printf("
e_ehsize: %d\n", ElfHeader.e_ehsize);
printf("
e_phentsize: %d\n", ElfHeader.e_phentsize);
printf("
e_phnum: %d\n", ElfHeader.e_phnum);
printf("
e_shentsize: %d\n", ElfHeader.e_shentsize);
printf("
e_shnum: %d\n", ElfHeader.e_shnum);
printf("
e_shstrndx: %d\n", ElfHeader.e_shstrndx);
}
if (memcmp
(ELFMAG, ElfHeader.e_ident, SELFMAG) != 0)
{
if (debug)
printf("Bad ELF header magic number\n");
return;
}
if (ElfHeader.e_type != ET_DYN)
{
if (debug)
printf("Not a shared object\n");
return;
}
check(ElfHeader.e_machine == EM_386, "Bad ELF architecture\n");
check(ElfHeader.e_version == EV_CURRENT, "Bad ELF version\n");
check
(ElfHeader.e_phoff >= ElfHeader.e_ehsize,
"Bad program header
offset\n");
check(ElfHeader.e_phentsize == sizeof(ProgHeader),
"Bad program header size\n");
PHOffset = ElfHeader.
e_phoff;
PHCount = ElfHeader.e_phnum;
for (i = 0; i < PHCount;
i ++)
{
/* Get the i-th program header; we are interested
in the eh_frame_hdr
* section */
ret = memreadat
(segment,
size,
&ProgHeader,
sizeof(ProgHeader),
PHOffset + i * sizeof(ProgHeader));
if
(!ret)
return;
if (debug)
{
printf("Object Program Header:\n");
printf("
p_type: %d\n", ProgHeader.p_type);
printf("
p_offset: %d\n", ProgHeader.p_offset);
printf("
p_vaddr: %d\n", ProgHeader.p_vaddr);
printf("
p_paddr: %d\n", ProgHeader.p_paddr);
printf("
p_filesz: %d\n", ProgHeader.p_filesz);
printf("
p_memsz: %d\n", ProgHeader.p_memsz);
printf("
p_flags: %d\n", ProgHeader.p_flags);
printf("
p_align: %d\n", ProgHeader.p_align);
}
if
(ProgHeader.p_type == PT_LOAD && ProgHeader.p_offset == 0)
{
LoadOffset = ProgHeader.p_vaddr;
if (debug)
{
printf("Found load offset 0x%x\n",
LoadOffset);
}
}
if (ProgHeader.p_type ==
PT_GNU_EH_FRAME)
{
check(EhFrameSize == 0,
"Multiple PT_GNU_EH_FRAME program sections\n");
EhFrameOffset = ProgHeader.p_offset;
EhFrameSize = ProgHeader.p_filesz;
}
}
if
(EhFrameSize > 0)
{
char *ehsegment = xmalloc(EhFrameSize);
ret = memreadat(segment, size, ehsegment,
EhFrameSize,
EhFrameOffset);
if (ret)
ProcessEhFrameHdr
(segment,
size,
segment + EhFrameOffset,
EhFrameSize,
libname,
LoadOffset);
free(ehsegment);
}
}
char *GuessLibName(char
*segment, int size)
{
char *first_GLIBC;
char *this_string;
char *prev_string;
int retries;
first_GLIBC = (char *)memmem
(segment, size, "\0GLIBC_", 7);
if (first_GLIBC == NULL)
return NULL;
this_string = first_GLIBC;
for (retries = 0;
retries < 15; retries ++)
{
for (prev_string = this_string
- 1;
prev_string > segment && *prev_string != '\0';
prev_string --)
{
if (!isgraph
(*prev_string))
return NULL;
/* arbitrary
max len */
if (this_string - prev_string > 128)
return NULL;
}
if (memcmp
(prev_string + 1, "lib", 3) == 0 ||
memcmp(prev_string + 1,
"ld-linux.so", 11) == 0)
{
char *ret = xmalloc
(this_string - prev_string);
memcpy(ret, prev_string + 1,
this_string - prev_string);
return ret;
}
this_string = prev_string;
}
return NULL;
}
void extract_cfi
(char *corefile)
{
int corefd;
Elf32_Ehdr ElfHeader;
int
PHOffset, PHCount;
Elf32_Phdr ProgHeader;
int i;
/* Open
the corefile for reading */
corefd = open(corefile, O_RDONLY);
check(corefd > 0, "open() failed\n");
/* Get the corefile ELF
header */
readat(corefd, (void *)&ElfHeader, sizeof(ElfHeader), 0);
if (debug)
{
printf("Corefile ELF Header:\n");
printf(" e_ident: %.16s\n", ElfHeader.e_ident);
printf
(" e_type: %d\n", ElfHeader.e_type);
printf("
e_machine: %d\n", ElfHeader.e_machine);
printf("
e_version: %d\n", ElfHeader.e_version);
printf("
e_entry: %d\n", ElfHeader.e_entry);
printf("
e_phoff: %d\n", ElfHeader.e_phoff);
printf("
e_shoff: %d\n", ElfHeader.e_shoff);
printf("
e_flags: %d\n", ElfHeader.e_flags);
printf("
e_ehsize: %d\n", ElfHeader.e_ehsize);
printf("
e_phentsize: %d\n", ElfHeader.e_phentsize);
printf("
e_phnum: %d\n", ElfHeader.e_phnum);
printf("
e_shentsize: %d\n", ElfHeader.e_shentsize);
printf("
e_shnum: %d\n", ElfHeader.e_shnum);
printf("
e_shstrndx: %d\n", ElfHeader.e_shstrndx);
}
check(memcmp
(ELFMAG, ElfHeader.e_ident, SELFMAG) == 0,
"Bad ELF header
magic number\n");
check(ElfHeader.e_type == ET_CORE, "Bad ELF
object file type\n");
check(ElfHeader.e_machine == EM_386, "Bad ELF
architecture\n");
check(ElfHeader.e_version == EV_CURRENT, "Bad ELF
version\n");
check(ElfHeader.e_phoff >= ElfHeader.e_ehsize,
"Bad program header offset\n");
check(ElfHeader.
e_phentsize == sizeof(ProgHeader),
"Bad program header
size\n");
PHOffset = ElfHeader.e_phoff;
PHCount = ElfHeader.
e_phnum;
for (i = 0; i < PHCount; i ++)
{
/* Get the i-
th program header; we are interested in loadXX sections */
readat(corefd,
&ProgHeader,
sizeof
(ProgHeader),
PHOffset + i * sizeof(ProgHeader));
if (debug)
{
printf("Corefile Program
Header:\n");
printf(" p_type: %d\n", ProgHeader.
p_type);
printf(" p_offset: %d\n", ProgHeader.
p_offset);
printf(" p_vaddr: %d\n", ProgHeader.
p_vaddr);
printf(" p_paddr: %d\n", ProgHeader.
p_paddr);
printf(" p_filesz: %d\n", ProgHeader.
p_filesz);
printf(" p_memsz: %d\n", ProgHeader.
p_memsz);
printf(" p_flags: %d\n", ProgHeader.
p_flags);
printf(" p_align: %d\n", ProgHeader.
p_align);
}
if (ProgHeader.p_type == PT_LOAD)
{
char *segment = xmalloc(ProgHeader.p_filesz);
/* Read the section. If it starts with an ELF header then it's
* (almost certainly) the start of an object */
readat(corefd, segment, ProgHeader.p_filesz, ProgHeader.p_offset);
if (memcmp(ELFMAG, segment, SELFMAG) == 0)
{
char *libname = GuessLibName(segment, ProgHeader.
p_filesz);
if (libname == NULL)
{
libname = xmalloc(40);
snprintf
(libname, 40, "libunknown_%d", i);
}
if
(debug)
{
printf("Guessed library
name %s\n", libname);
}
ProcessObject
(segment, ProgHeader.p_filesz, libname);
free(libname);
}
free(segment);
}
}
close
(corefd);
}
int main(int argc, char **argv)
{
char *corefile;
int c;
char *cfi_dir = NULL;
int show_help = 0;
while((c =
getopt(argc, argv, "h?vdf:")) > 0)
{
switch (c)
{
case 'd':
debug = 1;
break;
case 'f':
cfi_dir = optarg;
break;
case '?':
case 'h':
show_help = 1;
break;
default:
fprintf(stderr, "invalid argument %
c.\n", c);
exit(1);
break;
}
}
if (show_help)
{
printf("Usage: wxcoretool [options]
corefile\n\nwhere options are:\n\n"
"-f <dir> : Create
CFI stub libraries in sysroot <dir>.\n"
"-d : Output
debugging information.\n"
"-h or -? : Show help.\n"
);
exit(0);
}
check(argc - optind == 1,
"invalid number of arguments.\n");
corefile = realpath(argv
[optind], NULL);
check(corefile != NULL, "unable to obtain path of
core file.\n");
if (cfi_dir)
{
int res;
char
*cfi_lib_dir = xmalloc(strlen(cfi_dir) + 5);
struct stat
stat_buf;
sprintf(cfi_lib_dir, "%s/lib", cfi_dir);
res = stat(cfi_lib_dir, &stat_buf);
if (res != 0 && errno ==
ENOENT)
{
res = mkdir(cfi_lib_dir, 0755);
check(res == 0, "unable to make directory.\n");
}
else
{
check(res == 0, "unable to stat.
\n");
}
res = chdir(cfi_lib_dir);
check(res ==
0, "unable to change to specified directory.\n");
free
(cfi_lib_dir);
extract_cfi(corefile);
}
exit(0);
}