Home | History | Annotate | Download | only in coredump
      1 /* libunwind - a platform-independent unwind library
      2 
      3 This file is part of libunwind.
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining
      6 a copy of this software and associated documentation files (the
      7 "Software"), to deal in the Software without restriction, including
      8 without limitation the rights to use, copy, modify, merge, publish,
      9 distribute, sublicense, and/or sell copies of the Software, and to
     10 permit persons to whom the Software is furnished to do so, subject to
     11 the following conditions:
     12 
     13 The above copyright notice and this permission notice shall be
     14 included in all copies or substantial portions of the Software.
     15 
     16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 # include "config.h"
     26 #endif
     27 
     28 /* Endian detection */
     29 #include <limits.h>
     30 #if defined(HAVE_BYTESWAP_H)
     31 #include <byteswap.h>
     32 #endif
     33 #if defined(HAVE_ENDIAN_H)
     34 # include <endian.h>
     35 #elif defined(HAVE_SYS_ENDIAN_H)
     36 # include <sys/endian.h>
     37 #endif
     38 #if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN
     39 # define WE_ARE_BIG_ENDIAN    1
     40 # define WE_ARE_LITTLE_ENDIAN 0
     41 #elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN
     42 # define WE_ARE_BIG_ENDIAN    0
     43 # define WE_ARE_LITTLE_ENDIAN 1
     44 #elif defined(_BYTE_ORDER) && _BYTE_ORDER == _BIG_ENDIAN
     45 # define WE_ARE_BIG_ENDIAN    1
     46 # define WE_ARE_LITTLE_ENDIAN 0
     47 #elif defined(_BYTE_ORDER) && _BYTE_ORDER == _LITTLE_ENDIAN
     48 # define WE_ARE_BIG_ENDIAN    0
     49 # define WE_ARE_LITTLE_ENDIAN 1
     50 #elif defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
     51 # define WE_ARE_BIG_ENDIAN    1
     52 # define WE_ARE_LITTLE_ENDIAN 0
     53 #elif defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN
     54 # define WE_ARE_BIG_ENDIAN    0
     55 # define WE_ARE_LITTLE_ENDIAN 1
     56 #elif defined(__386__)
     57 # define WE_ARE_BIG_ENDIAN    0
     58 # define WE_ARE_LITTLE_ENDIAN 1
     59 #else
     60 # error "Can't determine endianness"
     61 #endif
     62 
     63 #include <elf.h>
     64 #include <sys/procfs.h> /* struct elf_prstatus */
     65 
     66 #include "_UCD_lib.h"
     67 #include "_UCD_internal.h"
     68 
     69 #define NOTE_DATA(_hdr) STRUCT_MEMBER_P((_hdr), sizeof (Elf32_Nhdr) + UNW_ALIGN((_hdr)->n_namesz, 4))
     70 #define NOTE_SIZE(_hdr) (sizeof (Elf32_Nhdr) + UNW_ALIGN((_hdr)->n_namesz, 4) + (_hdr)->n_descsz)
     71 #define NOTE_NEXT(_hdr) STRUCT_MEMBER_P((_hdr), NOTE_SIZE(_hdr))
     72 #define NOTE_FITS_IN(_hdr, _size) ((_size) >= sizeof (Elf32_Nhdr) && (_size) >= NOTE_SIZE (_hdr))
     73 #define NOTE_FITS(_hdr, _end) NOTE_FITS_IN((_hdr), (unsigned long)((char *)(_end) - (char *)(_hdr)))
     74 
     75 struct UCD_info *
     76 _UCD_create(const char *filename)
     77 {
     78   union
     79     {
     80       Elf32_Ehdr h32;
     81       Elf64_Ehdr h64;
     82     } elf_header;
     83 #define elf_header32 elf_header.h32
     84 #define elf_header64 elf_header.h64
     85   bool _64bits;
     86 
     87   struct UCD_info *ui = memset(malloc(sizeof(*ui)), 0, sizeof(*ui));
     88   ui->edi.di_cache.format = -1;
     89   ui->edi.di_debug.format = -1;
     90 #if UNW_TARGET_IA64
     91   ui->edi.ktab.format = -1;
     92 #endif
     93 
     94   int fd = ui->coredump_fd = open(filename, O_RDONLY);
     95   if (fd < 0)
     96     goto err;
     97   ui->coredump_filename = strdup(filename);
     98 
     99   /* No sane ELF32 file is going to be smaller then ELF64 _header_,
    100    * so let's just read 64-bit sized one.
    101    */
    102   if (read(fd, &elf_header64, sizeof(elf_header64)) != sizeof(elf_header64))
    103     {
    104       Debug(0, "'%s' is not an ELF file\n", filename);
    105       goto err;
    106     }
    107 
    108   if (memcmp(&elf_header32, ELFMAG, SELFMAG) != 0)
    109     {
    110       Debug(0, "'%s' is not an ELF file\n", filename);
    111       goto err;
    112     }
    113 
    114   if (elf_header32.e_ident[EI_CLASS] != ELFCLASS32
    115    && elf_header32.e_ident[EI_CLASS] != ELFCLASS64)
    116     {
    117       Debug(0, "'%s' is not a 32/64 bit ELF file\n", filename);
    118       goto err;
    119     }
    120 
    121   if (WE_ARE_LITTLE_ENDIAN != (elf_header32.e_ident[EI_DATA] == ELFDATA2LSB))
    122     {
    123       Debug(0, "'%s' is endian-incompatible\n", filename);
    124       goto err;
    125     }
    126 
    127   _64bits = (elf_header32.e_ident[EI_CLASS] == ELFCLASS64);
    128   if (_64bits && sizeof(elf_header64.e_entry) > sizeof(off_t))
    129     {
    130       Debug(0, "Can't process '%s': 64-bit file "
    131                "while only %ld bits are supported",
    132             filename, 8L * sizeof(off_t));
    133       goto err;
    134     }
    135 
    136   /* paranoia check */
    137   if (_64bits
    138             ? 0 /* todo: (elf_header64.e_ehsize != NN || elf_header64.e_phentsize != NN) */
    139             : (elf_header32.e_ehsize != 52 || elf_header32.e_phentsize != 32)
    140   )
    141     {
    142       Debug(0, "'%s' has wrong e_ehsize or e_phentsize\n", filename);
    143       goto err;
    144     }
    145 
    146   off_t ofs = (_64bits ? elf_header64.e_phoff : elf_header32.e_phoff);
    147   if (lseek(fd, ofs, SEEK_SET) != ofs)
    148     {
    149       Debug(0, "Can't read phdrs from '%s'\n", filename);
    150       goto err;
    151     }
    152   unsigned size = ui->phdrs_count = (_64bits ? elf_header64.e_phnum : elf_header32.e_phnum);
    153   coredump_phdr_t *phdrs = ui->phdrs = memset(malloc(size * sizeof(phdrs[0])), 0, size * sizeof(phdrs[0]));
    154   if (_64bits)
    155     {
    156       coredump_phdr_t *cur = phdrs;
    157       unsigned i = 0;
    158       while (i < size)
    159         {
    160           Elf64_Phdr hdr64;
    161           if (read(fd, &hdr64, sizeof(hdr64)) != sizeof(hdr64))
    162             {
    163               Debug(0, "Can't read phdrs from '%s'\n", filename);
    164               goto err;
    165             }
    166           cur->p_type   = hdr64.p_type  ;
    167           cur->p_flags  = hdr64.p_flags ;
    168           cur->p_offset = hdr64.p_offset;
    169           cur->p_vaddr  = hdr64.p_vaddr ;
    170           /*cur->p_paddr  = hdr32.p_paddr ; always 0 */
    171 //TODO: check that and abort if it isn't?
    172           cur->p_filesz = hdr64.p_filesz;
    173           cur->p_memsz  = hdr64.p_memsz ;
    174           cur->p_align  = hdr64.p_align ;
    175           /* cur->backing_filename = NULL; - done by memset */
    176           cur->backing_fd = -1;
    177           cur->backing_filesize = hdr64.p_filesz;
    178           i++;
    179           cur++;
    180         }
    181     } else {
    182       coredump_phdr_t *cur = phdrs;
    183       unsigned i = 0;
    184       while (i < size)
    185         {
    186           Elf32_Phdr hdr32;
    187           if (read(fd, &hdr32, sizeof(hdr32)) != sizeof(hdr32))
    188             {
    189               Debug(0, "Can't read phdrs from '%s'\n", filename);
    190               goto err;
    191             }
    192           cur->p_type   = hdr32.p_type  ;
    193           cur->p_flags  = hdr32.p_flags ;
    194           cur->p_offset = hdr32.p_offset;
    195           cur->p_vaddr  = hdr32.p_vaddr ;
    196           /*cur->p_paddr  = hdr32.p_paddr ; always 0 */
    197           cur->p_filesz = hdr32.p_filesz;
    198           cur->p_memsz  = hdr32.p_memsz ;
    199           cur->p_align  = hdr32.p_align ;
    200           /* cur->backing_filename = NULL; - done by memset */
    201           cur->backing_fd = -1;
    202           cur->backing_filesize = hdr32.p_memsz;
    203           i++;
    204           cur++;
    205         }
    206     }
    207 
    208     unsigned i = 0;
    209     coredump_phdr_t *cur = phdrs;
    210     while (i < size)
    211       {
    212         Debug(2, "phdr[%03d]: type:%d", i, cur->p_type);
    213         if (cur->p_type == PT_NOTE)
    214           {
    215             Elf32_Nhdr *note_hdr, *note_end;
    216             unsigned n_threads;
    217 
    218             ui->note_phdr = malloc(cur->p_filesz);
    219             if (lseek(fd, cur->p_offset, SEEK_SET) != (off_t)cur->p_offset
    220              || (uoff_t)read(fd, ui->note_phdr, cur->p_filesz) != cur->p_filesz)
    221               {
    222                     Debug(0, "Can't read PT_NOTE from '%s'\n", filename);
    223                     goto err;
    224               }
    225 
    226             note_end = STRUCT_MEMBER_P (ui->note_phdr, cur->p_filesz);
    227 
    228             /* Count number of threads */
    229             n_threads = 0;
    230             note_hdr = (Elf32_Nhdr *)ui->note_phdr;
    231             while (NOTE_FITS (note_hdr, note_end))
    232               {
    233                 if (note_hdr->n_type == NT_PRSTATUS)
    234                   n_threads++;
    235 
    236                 note_hdr = NOTE_NEXT (note_hdr);
    237               }
    238 
    239             ui->n_threads = n_threads;
    240             ui->threads = malloc(sizeof (void *) * n_threads);
    241 
    242             n_threads = 0;
    243             note_hdr = (Elf32_Nhdr *)ui->note_phdr;
    244             while (NOTE_FITS (note_hdr, note_end))
    245               {
    246                 if (note_hdr->n_type == NT_PRSTATUS)
    247                   ui->threads[n_threads++] = NOTE_DATA (note_hdr);
    248 
    249                 note_hdr = NOTE_NEXT (note_hdr);
    250               }
    251           }
    252         if (cur->p_type == PT_LOAD)
    253           {
    254             Debug(2, " ofs:%08llx va:%08llx filesize:%08llx memsize:%08llx flg:%x",
    255                                 (unsigned long long) cur->p_offset,
    256                                 (unsigned long long) cur->p_vaddr,
    257                                 (unsigned long long) cur->p_filesz,
    258                                 (unsigned long long) cur->p_memsz,
    259                                 cur->p_flags
    260             );
    261             if (cur->p_filesz < cur->p_memsz)
    262               Debug(2, " partial");
    263             if (cur->p_flags & PF_X)
    264               Debug(2, " executable");
    265           }
    266         Debug(2, "\n");
    267         i++;
    268         cur++;
    269       }
    270 
    271     if (ui->n_threads == 0)
    272       {
    273         Debug(0, "No NT_PRSTATUS note found in '%s'\n", filename);
    274         goto err;
    275       }
    276 
    277     ui->prstatus = ui->threads[0];
    278 
    279   return ui;
    280 
    281  err:
    282   _UCD_destroy(ui);
    283   return NULL;
    284 }
    285 
    286 int _UCD_get_num_threads(struct UCD_info *ui)
    287 {
    288   return ui->n_threads;
    289 }
    290 
    291 void _UCD_select_thread(struct UCD_info *ui, int n)
    292 {
    293   if (n >= 0 && n < ui->n_threads)
    294     ui->prstatus = ui->threads[n];
    295 }
    296 
    297 pid_t _UCD_get_pid(struct UCD_info *ui)
    298 {
    299   return ui->prstatus->pr_pid;
    300 }
    301 
    302 int _UCD_get_cursig(struct UCD_info *ui)
    303 {
    304   return ui->prstatus->pr_cursig;
    305 }
    306 
    307 int _UCD_add_backing_file_at_segment(struct UCD_info *ui, int phdr_no, const char *filename)
    308 {
    309   if ((unsigned)phdr_no >= ui->phdrs_count)
    310     {
    311       Debug(0, "There is no segment %d in this coredump\n", phdr_no);
    312       return -1;
    313     }
    314 
    315   struct coredump_phdr *phdr = &ui->phdrs[phdr_no];
    316   if (phdr->backing_filename)
    317     {
    318       Debug(0, "Backing file already added to segment %d\n", phdr_no);
    319       return -1;
    320     }
    321 
    322   int fd = open(filename, O_RDONLY);
    323   if (fd < 0)
    324     {
    325       Debug(0, "Can't open '%s'\n", filename);
    326       return -1;
    327     }
    328 
    329   phdr->backing_fd = fd;
    330   phdr->backing_filename = strdup(filename);
    331 
    332   struct stat statbuf;
    333   if (fstat(fd, &statbuf) != 0)
    334     {
    335       Debug(0, "Can't stat '%s'\n", filename);
    336       goto err;
    337     }
    338   phdr->backing_filesize = (uoff_t)statbuf.st_size;
    339 
    340   if (phdr->p_flags != (PF_X | PF_R))
    341     Debug(1, "Note: phdr[%u] is not r-x: flags are 0x%x\n", phdr_no, phdr->p_flags);
    342 
    343   if (phdr->backing_filesize > phdr->p_memsz)
    344     {
    345       /* This is expected */
    346       Debug(2, "Note: phdr[%u] is %lld bytes, file is larger: %lld bytes\n",
    347                         phdr_no,
    348                         (unsigned long long)phdr->p_memsz,
    349                         (unsigned long long)phdr->backing_filesize
    350       );
    351     }
    352 //TODO: else loudly complain? Maybe even fail?
    353 
    354   if (phdr->p_filesz != 0)
    355     {
    356 //TODO: loop and compare in smaller blocks
    357       char *core_buf = malloc(phdr->p_filesz);
    358       char *file_buf = malloc(phdr->p_filesz);
    359       if (lseek(ui->coredump_fd, phdr->p_offset, SEEK_SET) != (off_t)phdr->p_offset
    360        || (uoff_t)read(ui->coredump_fd, core_buf, phdr->p_filesz) != phdr->p_filesz
    361       )
    362         {
    363           Debug(0, "Error reading from coredump file\n");
    364  err_read:
    365           free(core_buf);
    366           free(file_buf);
    367           goto err;
    368         }
    369       if ((uoff_t)read(fd, file_buf, phdr->p_filesz) != phdr->p_filesz)
    370         {
    371           Debug(0, "Error reading from '%s'\n", filename);
    372           goto err_read;
    373         }
    374       int r = memcmp(core_buf, file_buf, phdr->p_filesz);
    375       free(core_buf);
    376       free(file_buf);
    377       if (r != 0)
    378         {
    379           Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file do not match\n",
    380                                 phdr_no, (unsigned long long)phdr->p_filesz
    381           );
    382         } else {
    383           Debug(1, "Note: phdr[%u] first %lld bytes in core dump and in file match\n",
    384                                 phdr_no, (unsigned long long)phdr->p_filesz
    385           );
    386         }
    387     }
    388 
    389   /* Success */
    390   return 0;
    391 
    392  err:
    393   if (phdr->backing_fd >= 0)
    394     {
    395       close(phdr->backing_fd);
    396       phdr->backing_fd = -1;
    397     }
    398   free(phdr->backing_filename);
    399   phdr->backing_filename = NULL;
    400   return -1;
    401 }
    402 
    403 int _UCD_add_backing_file_at_vaddr(struct UCD_info *ui,
    404                                    unsigned long vaddr,
    405                                    const char *filename)
    406 {
    407   unsigned i;
    408   for (i = 0; i < ui->phdrs_count; i++)
    409     {
    410       struct coredump_phdr *phdr = &ui->phdrs[i];
    411       if (phdr->p_vaddr != vaddr)
    412         continue;
    413       /* It seems to match. Add it. */
    414       return _UCD_add_backing_file_at_segment(ui, i, filename);
    415     }
    416   return -1;
    417 }
    418