Home | History | Annotate | Download | only in libdw
      1 /* Get CFI from ELF file's exception-handling info.
      2    Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
      3    This file is part of elfutils.
      4 
      5    This file is free software; you can redistribute it and/or modify
      6    it under the terms of either
      7 
      8      * the GNU Lesser General Public License as published by the Free
      9        Software Foundation; either version 3 of the License, or (at
     10        your option) any later version
     11 
     12    or
     13 
     14      * the GNU General Public License as published by the Free
     15        Software Foundation; either version 2 of the License, or (at
     16        your option) any later version
     17 
     18    or both in parallel, as here.
     19 
     20    elfutils is distributed in the hope that it will be useful, but
     21    WITHOUT ANY WARRANTY; without even the implied warranty of
     22    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     23    General Public License for more details.
     24 
     25    You should have received copies of the GNU General Public License and
     26    the GNU Lesser General Public License along with this program.  If
     27    not, see <http://www.gnu.org/licenses/>.  */
     28 
     29 #ifdef HAVE_CONFIG_H
     30 # include <config.h>
     31 #endif
     32 
     33 #include <stdlib.h>
     34 #include <string.h>
     35 #include <assert.h>
     36 
     37 #include "libdwP.h"
     38 #include "cfi.h"
     39 #include "encoded-value.h"
     40 #include <dwarf.h>
     41 
     42 
     43 static Dwarf_CFI *
     44 allocate_cfi (Elf *elf, GElf_Addr vaddr)
     45 {
     46   Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
     47   if (cfi == NULL)
     48     {
     49       __libdw_seterrno (DWARF_E_NOMEM);
     50       return NULL;
     51     }
     52 
     53   cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
     54   if (cfi->e_ident == NULL)
     55     {
     56       free (cfi);
     57       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
     58       return NULL;
     59     }
     60 
     61   if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
     62       || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
     63     cfi->other_byte_order = true;
     64 
     65   cfi->frame_vaddr = vaddr;
     66   cfi->textrel = 0;		/* XXX ? */
     67   cfi->datarel = 0;		/* XXX ? */
     68 
     69   return cfi;
     70 }
     71 
     72 static const uint8_t *
     73 parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
     74 		    const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
     75 		    size_t *table_entries, uint8_t *table_encoding)
     76 {
     77   const uint8_t *h = hdr;
     78 
     79   if (hdr_size < 4 || *h++ != 1)		/* version */
     80     return (void *) -1l;
     81 
     82   uint8_t eh_frame_ptr_encoding = *h++;
     83   uint8_t fde_count_encoding = *h++;
     84   uint8_t fde_table_encoding = *h++;
     85 
     86   if (eh_frame_ptr_encoding == DW_EH_PE_omit)
     87     return (void *) -1l;
     88 
     89   /* Dummy used by read_encoded_value.  */
     90   Elf_Data_Scn dummy_cfi_hdr_data =
     91     {
     92       .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
     93     };
     94   Dwarf_CFI dummy_cfi =
     95     {
     96       .e_ident = ehdr->e_ident,
     97       .datarel = hdr_vaddr,
     98       .frame_vaddr = hdr_vaddr,
     99       .data = &dummy_cfi_hdr_data,
    100     };
    101 
    102   if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
    103 				    eh_frame_vaddr)))
    104     return (void *) -1l;
    105 
    106   if (fde_count_encoding != DW_EH_PE_omit)
    107     {
    108       Dwarf_Word fde_count;
    109       if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
    110 					&fde_count)))
    111 	return (void *) -1l;
    112       if (fde_count != 0 && (size_t) fde_count == fde_count
    113 	  && fde_table_encoding != DW_EH_PE_omit
    114 	  && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
    115 	{
    116 	  *table_entries = fde_count;
    117 	  *table_encoding = fde_table_encoding;
    118 	  return h;
    119 	}
    120     }
    121 
    122   return NULL;
    123 }
    124 
    125 static Dwarf_CFI *
    126 getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
    127 {
    128   Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
    129 					 ELF_T_BYTE);
    130   if (data == NULL || data->d_buf == NULL)
    131     {
    132     invalid_hdr:
    133       /* XXX might be read error or corrupt phdr */
    134       __libdw_seterrno (DWARF_E_INVALID_CFI);
    135       return NULL;
    136     }
    137 
    138   size_t vsize, dmax;
    139   Dwarf_Addr eh_frame_ptr;
    140   size_t search_table_entries = 0;
    141   uint8_t search_table_encoding = 0;
    142   const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
    143 						    phdr->p_vaddr, ehdr,
    144 						    &eh_frame_ptr,
    145 						    &search_table_entries,
    146 						    &search_table_encoding);
    147 
    148   /* Make sure there is enough room for the entries in the table,
    149      each entry consists of 2 encoded values.  */
    150   vsize = encoded_value_size (data, ehdr->e_ident, search_table_encoding,
    151 			      NULL);
    152   dmax = phdr->p_filesz - (search_table - (const uint8_t *) data->d_buf);
    153   if (unlikely (search_table == (void *) -1l
    154 		|| vsize == 0
    155 		|| search_table_entries > (dmax / vsize) / 2))
    156     goto invalid_hdr;
    157 
    158   Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
    159   Dwarf_Word eh_frame_size = 0;
    160 
    161   /* XXX we have no way without section headers to know the size
    162      of the .eh_frame data.  Calculate the largest it might possibly be.
    163      This won't be wasteful if the file is already mmap'd, but if it isn't
    164      it might be quite excessive.  */
    165   size_t filesize;
    166   if (elf_rawfile (elf, &filesize) != NULL)
    167     eh_frame_size = filesize - eh_frame_offset;
    168 
    169   data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
    170   if (data == NULL)
    171     {
    172       __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
    173       return NULL;
    174     }
    175   Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
    176   if (cfi != NULL)
    177     {
    178       cfi->data = (Elf_Data_Scn *) data;
    179 
    180       if (search_table != NULL)
    181 	{
    182 	  cfi->search_table = search_table;
    183 	  cfi->search_table_len = phdr->p_filesz;
    184 	  cfi->search_table_vaddr = phdr->p_vaddr;
    185 	  cfi->search_table_encoding = search_table_encoding;
    186 	  cfi->search_table_entries = search_table_entries;
    187 	}
    188     }
    189   return cfi;
    190 }
    191 
    192 /* Search the phdrs for PT_GNU_EH_FRAME.  */
    193 static Dwarf_CFI *
    194 getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
    195 {
    196   size_t phnum;
    197   if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
    198     return NULL;
    199 
    200   for (size_t i = 0; i < phnum; ++i)
    201     {
    202       GElf_Phdr phdr_mem;
    203       GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
    204       if (unlikely (phdr == NULL))
    205 	return NULL;
    206       if (phdr->p_type == PT_GNU_EH_FRAME)
    207 	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
    208     }
    209 
    210   __libdw_seterrno (DWARF_E_NO_DWARF);
    211   return NULL;
    212 }
    213 
    214 static Dwarf_CFI *
    215 getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
    216 		     Elf_Scn *scn, GElf_Shdr *shdr,
    217 		     Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
    218 {
    219   Elf_Data *data = elf_rawdata (scn, NULL);
    220   if (data == NULL || data->d_buf == NULL)
    221     {
    222       __libdw_seterrno (DWARF_E_INVALID_ELF);
    223       return NULL;
    224     }
    225   Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
    226   if (cfi != NULL)
    227     {
    228       cfi->data = (Elf_Data_Scn *) data;
    229       if (hdr_scn != NULL)
    230 	{
    231 	  Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
    232 	  if (hdr_data != NULL && hdr_data->d_buf != NULL)
    233 	    {
    234 	      size_t vsize, dmax;
    235 	      GElf_Addr eh_frame_vaddr;
    236 	      cfi->search_table_vaddr = hdr_vaddr;
    237 	      cfi->search_table
    238 		= parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
    239 				      hdr_vaddr, ehdr, &eh_frame_vaddr,
    240 				      &cfi->search_table_entries,
    241 				      &cfi->search_table_encoding);
    242 	      cfi->search_table_len = hdr_data->d_size;
    243 
    244 	      /* Make sure there is enough room for the entries in the table,
    245 		 each entry consists of 2 encoded values.  */
    246 	      vsize = encoded_value_size (hdr_data, ehdr->e_ident,
    247 					  cfi->search_table_encoding, NULL);
    248 	      dmax = hdr_data->d_size - (cfi->search_table
    249 					 - (const uint8_t *) hdr_data->d_buf);
    250 	      if (unlikely (cfi->search_table == (void *) -1l
    251 			    || vsize == 0
    252 			    || cfi->search_table_entries > (dmax / vsize) / 2))
    253 		{
    254 		  free (cfi);
    255 		  /* XXX might be read error or corrupt phdr */
    256 		  __libdw_seterrno (DWARF_E_INVALID_CFI);
    257 		  return NULL;
    258 		}
    259 
    260 	      /* Sanity check.  */
    261 	      if (unlikely (eh_frame_vaddr != shdr->sh_addr))
    262 		cfi->search_table = NULL;
    263 	    }
    264 	}
    265     }
    266   return cfi;
    267 }
    268 
    269 /* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
    270 static Dwarf_CFI *
    271 getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
    272 {
    273   size_t shstrndx;
    274   if (elf_getshdrstrndx (elf, &shstrndx) != 0)
    275     {
    276       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
    277       return NULL;
    278     }
    279 
    280   if (shstrndx != 0)
    281     {
    282       Elf_Scn *hdr_scn = NULL;
    283       GElf_Addr hdr_vaddr = 0;
    284       Elf_Scn *scn = NULL;
    285       while ((scn = elf_nextscn (elf, scn)) != NULL)
    286 	{
    287 	  GElf_Shdr shdr_mem;
    288 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    289 	  if (shdr == NULL)
    290 	    continue;
    291 	  const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
    292 	  if (name == NULL)
    293 	    continue;
    294 	  if (!strcmp (name, ".eh_frame_hdr"))
    295 	    {
    296 	      hdr_scn = scn;
    297 	      hdr_vaddr = shdr->sh_addr;
    298 	    }
    299 	  else if (!strcmp (name, ".eh_frame"))
    300 	    {
    301 	      if (shdr->sh_type == SHT_PROGBITS)
    302 		return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
    303 					    hdr_scn, hdr_vaddr);
    304 	      else
    305 		return NULL;
    306 	    }
    307 	}
    308     }
    309 
    310   return (void *) -1l;
    311 }
    312 
    313 Dwarf_CFI *
    314 dwarf_getcfi_elf (Elf *elf)
    315 {
    316   if (elf_kind (elf) != ELF_K_ELF)
    317     {
    318       __libdw_seterrno (DWARF_E_NOELF);
    319       return NULL;
    320     }
    321 
    322   GElf_Ehdr ehdr_mem;
    323   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
    324   if (unlikely (ehdr == NULL))
    325     {
    326       __libdw_seterrno (DWARF_E_INVALID_ELF);
    327       return NULL;
    328     }
    329 
    330   Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
    331   if (result == (void *) -1l)
    332     result = getcfi_phdr (elf, ehdr);
    333 
    334   return result;
    335 }
    336 INTDEF (dwarf_getcfi_elf)
    337