Home | History | Annotate | Download | only in libdw
      1 /* Get CFI from ELF file's exception-handling info.
      2    Copyright (C) 2009-2010 Red Hat, Inc.
      3    This file is part of Red Hat elfutils.
      4 
      5    Red Hat elfutils is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by the
      7    Free Software Foundation; version 2 of the License.
      8 
      9    Red Hat elfutils is distributed in the hope that it will be useful, but
     10    WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    General Public License for more details.
     13 
     14    You should have received a copy of the GNU General Public License along
     15    with Red Hat elfutils; if not, write to the Free Software Foundation,
     16    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
     17 
     18    In addition, as a special exception, Red Hat, Inc. gives You the
     19    additional right to link the code of Red Hat elfutils with code licensed
     20    under any Open Source Initiative certified open source license
     21    (http://www.opensource.org/licenses/index.php) which requires the
     22    distribution of source code with any binary distribution and to
     23    distribute linked combinations of the two.  Non-GPL Code permitted under
     24    this exception must only link to the code of Red Hat elfutils through
     25    those well defined interfaces identified in the file named EXCEPTION
     26    found in the source code files (the "Approved Interfaces").  The files
     27    of Non-GPL Code may instantiate templates or use macros or inline
     28    functions from the Approved Interfaces without causing the resulting
     29    work to be covered by the GNU General Public License.  Only Red Hat,
     30    Inc. may make changes or additions to the list of Approved Interfaces.
     31    Red Hat's grant of this exception is conditioned upon your not adding
     32    any new exceptions.  If you wish to add a new Approved Interface or
     33    exception, please contact Red Hat.  You must obey the GNU General Public
     34    License in all respects for all of the Red Hat elfutils code and other
     35    code used in conjunction with Red Hat elfutils except the Non-GPL Code
     36    covered by this exception.  If you modify this file, you may extend this
     37    exception to your version of the file, but you are not obligated to do
     38    so.  If you do not wish to provide this exception without modification,
     39    you must delete this exception statement from your version and license
     40    this file solely under the GPL without exception.
     41 
     42    Red Hat elfutils is an included package of the Open Invention Network.
     43    An included package of the Open Invention Network is a package for which
     44    Open Invention Network licensees cross-license their patents.  No patent
     45    license is granted, either expressly or impliedly, by designation as an
     46    included package.  Should you wish to participate in the Open Invention
     47    Network licensing program, please visit www.openinventionnetwork.com
     48    <http://www.openinventionnetwork.com>.  */
     49 
     50 #ifdef HAVE_CONFIG_H
     51 # include <config.h>
     52 #endif
     53 
     54 #include <stdlib.h>
     55 #include <string.h>
     56 #include <assert.h>
     57 
     58 #include "libdwP.h"
     59 #include "cfi.h"
     60 #include "encoded-value.h"
     61 #include <dwarf.h>
     62 
     63 
     64 static Dwarf_CFI *
     65 allocate_cfi (Elf *elf, GElf_Addr vaddr)
     66 {
     67   Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
     68   if (cfi == NULL)
     69     {
     70       __libdw_seterrno (DWARF_E_NOMEM);
     71       return NULL;
     72     }
     73 
     74   cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
     75   if (cfi->e_ident == NULL)
     76     {
     77       free (cfi);
     78       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
     79       return NULL;
     80     }
     81 
     82   if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
     83       || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
     84     cfi->other_byte_order = true;
     85 
     86   cfi->frame_vaddr = vaddr;
     87   cfi->textrel = 0;		/* XXX ? */
     88   cfi->datarel = 0;		/* XXX ? */
     89 
     90   return cfi;
     91 }
     92 
     93 static const uint8_t *
     94 parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
     95 		    const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
     96 		    size_t *table_entries, uint8_t *table_encoding)
     97 {
     98   const uint8_t *h = hdr;
     99 
    100   if (*h++ != 1)		/* version */
    101     return (void *) -1l;
    102 
    103   uint8_t eh_frame_ptr_encoding = *h++;
    104   uint8_t fde_count_encoding = *h++;
    105   uint8_t fde_table_encoding = *h++;
    106 
    107   if (eh_frame_ptr_encoding == DW_EH_PE_omit)
    108     return (void *) -1l;
    109 
    110   /* Dummy used by read_encoded_value.  */
    111   Elf_Data_Scn dummy_cfi_hdr_data =
    112     {
    113       .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
    114     };
    115   Dwarf_CFI dummy_cfi =
    116     {
    117       .e_ident = ehdr->e_ident,
    118       .datarel = hdr_vaddr,
    119       .frame_vaddr = hdr_vaddr,
    120       .data = &dummy_cfi_hdr_data,
    121     };
    122 
    123   if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
    124 				    eh_frame_vaddr)))
    125     return (void *) -1l;
    126 
    127   if (fde_count_encoding != DW_EH_PE_omit)
    128     {
    129       Dwarf_Word fde_count;
    130       if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
    131 					&fde_count)))
    132 	return (void *) -1l;
    133       if (fde_count != 0 && (size_t) fde_count == fde_count
    134 	  && fde_table_encoding != DW_EH_PE_omit
    135 	  && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
    136 	{
    137 	  *table_entries = fde_count;
    138 	  *table_encoding = fde_table_encoding;
    139 	  return h;
    140 	}
    141     }
    142 
    143   return NULL;
    144 }
    145 
    146 static Dwarf_CFI *
    147 getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
    148 {
    149   if (unlikely (phdr->p_filesz < 4))
    150     goto invalid;
    151 
    152   Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
    153 					 ELF_T_BYTE);
    154   if (data == NULL)
    155     {
    156     invalid_hdr:
    157     invalid:
    158       /* XXX might be read error or corrupt phdr */
    159       __libdw_seterrno (DWARF_E_INVALID_CFI);
    160       return NULL;
    161     }
    162 
    163   Dwarf_Addr eh_frame_ptr;
    164   size_t search_table_entries;
    165   uint8_t search_table_encoding;
    166   const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
    167 						    phdr->p_vaddr, ehdr,
    168 						    &eh_frame_ptr,
    169 						    &search_table_entries,
    170 						    &search_table_encoding);
    171   if (search_table == (void *) -1l)
    172     goto invalid_hdr;
    173 
    174   Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
    175   Dwarf_Word eh_frame_size = 0;
    176 
    177   /* XXX we have no way without section headers to know the size
    178      of the .eh_frame data.  Calculate the largest it might possibly be.
    179      This won't be wasteful if the file is already mmap'd, but if it isn't
    180      it might be quite excessive.  */
    181   size_t filesize;
    182   if (elf_rawfile (elf, &filesize) != NULL)
    183     eh_frame_size = filesize - eh_frame_offset;
    184 
    185   data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
    186   if (data == NULL)
    187     {
    188       __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
    189       return NULL;
    190     }
    191   Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
    192   if (cfi != NULL)
    193     {
    194       cfi->data = (Elf_Data_Scn *) data;
    195 
    196       if (search_table != NULL)
    197 	{
    198 	  cfi->search_table = search_table;
    199 	  cfi->search_table_vaddr = phdr->p_vaddr;
    200 	  cfi->search_table_encoding = search_table_encoding;
    201 	  cfi->search_table_entries = search_table_entries;
    202 	}
    203     }
    204   return cfi;
    205 }
    206 
    207 /* Search the phdrs for PT_GNU_EH_FRAME.  */
    208 static Dwarf_CFI *
    209 getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
    210 {
    211   size_t phnum;
    212   if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
    213     return NULL;
    214 
    215   for (size_t i = 0; i < phnum; ++i)
    216     {
    217       GElf_Phdr phdr_mem;
    218       GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
    219       if (unlikely (phdr == NULL))
    220 	return NULL;
    221       if (phdr->p_type == PT_GNU_EH_FRAME)
    222 	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
    223     }
    224 
    225   __libdw_seterrno (DWARF_E_NO_DWARF);
    226   return NULL;
    227 }
    228 
    229 static Dwarf_CFI *
    230 getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
    231 		     Elf_Scn *scn, GElf_Shdr *shdr,
    232 		     Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
    233 {
    234   Elf_Data *data = elf_rawdata (scn, NULL);
    235   if (data == NULL)
    236     {
    237       __libdw_seterrno (DWARF_E_INVALID_ELF);
    238       return NULL;
    239     }
    240   Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
    241   if (cfi != NULL)
    242     {
    243       cfi->data = (Elf_Data_Scn *) data;
    244       if (hdr_scn != NULL)
    245 	{
    246 	  Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
    247 	  if (hdr_data != NULL)
    248 	    {
    249 	      GElf_Addr eh_frame_vaddr;
    250 	      cfi->search_table_vaddr = hdr_vaddr;
    251 	      cfi->search_table
    252 		= parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
    253 				      hdr_vaddr, ehdr, &eh_frame_vaddr,
    254 				      &cfi->search_table_entries,
    255 				      &cfi->search_table_encoding);
    256 	      if (cfi->search_table == (void *) -1l)
    257 		{
    258 		  free (cfi);
    259 		  /* XXX might be read error or corrupt phdr */
    260 		  __libdw_seterrno (DWARF_E_INVALID_CFI);
    261 		  return NULL;
    262 		}
    263 
    264 	      /* Sanity check.  */
    265 	      if (unlikely (eh_frame_vaddr != shdr->sh_addr))
    266 		cfi->search_table = NULL;
    267 	    }
    268 	}
    269     }
    270   return cfi;
    271 }
    272 
    273 /* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
    274 static Dwarf_CFI *
    275 getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
    276 {
    277   size_t shstrndx;
    278   if (elf_getshdrstrndx (elf, &shstrndx) != 0)
    279     {
    280       __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
    281       return NULL;
    282     }
    283 
    284   if (shstrndx != 0)
    285     {
    286       Elf_Scn *hdr_scn = NULL;
    287       GElf_Addr hdr_vaddr = 0;
    288       Elf_Scn *scn = NULL;
    289       while ((scn = elf_nextscn (elf, scn)) != NULL)
    290 	{
    291 	  GElf_Shdr shdr_mem;
    292 	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
    293 	  if (shdr == NULL)
    294 	    continue;
    295 	  const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
    296 	  if (name == NULL)
    297 	    continue;
    298 	  if (!strcmp (name, ".eh_frame_hdr"))
    299 	    {
    300 	      hdr_scn = scn;
    301 	      hdr_vaddr = shdr->sh_addr;
    302 	    }
    303 	  else if (!strcmp (name, ".eh_frame"))
    304 	    return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
    305 					hdr_scn, hdr_vaddr);
    306 	}
    307     }
    308 
    309   return (void *) -1l;
    310 }
    311 
    312 Dwarf_CFI *
    313 dwarf_getcfi_elf (elf)
    314      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