Unwind method using CFI in core dump sections?

Ben Cohen ben_c@tiscali.co.uk
Thu May 30 13:01:00 GMT 2013


[Resending because my webmail client added line breaks and the program 
won't compile - apologies.]

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.


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  -0x80000000
         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\n"
                "where 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);
}



More information about the Gdb mailing list