Blame libdw/dwarf_getcfi_elf.c

Packit 032894
/* Get CFI from ELF file's exception-handling info.
Packit 032894
   Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
Packit 032894
   This file is free software; you can redistribute it and/or modify
Packit 032894
   it under the terms of either
Packit 032894
Packit 032894
     * the GNU Lesser General Public License as published by the Free
Packit 032894
       Software Foundation; either version 3 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or
Packit 032894
Packit 032894
     * the GNU General Public License as published by the Free
Packit 032894
       Software Foundation; either version 2 of the License, or (at
Packit 032894
       your option) any later version
Packit 032894
Packit 032894
   or both in parallel, as here.
Packit 032894
Packit 032894
   elfutils is distributed in the hope that it will be useful, but
Packit 032894
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 032894
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 032894
   General Public License for more details.
Packit 032894
Packit 032894
   You should have received copies of the GNU General Public License and
Packit 032894
   the GNU Lesser General Public License along with this program.  If
Packit 032894
   not, see <http://www.gnu.org/licenses/>.  */
Packit 032894
Packit 032894
#ifdef HAVE_CONFIG_H
Packit 032894
# include <config.h>
Packit 032894
#endif
Packit 032894
Packit 032894
#include <stdlib.h>
Packit 032894
#include <string.h>
Packit 032894
#include <assert.h>
Packit 032894
Packit 032894
#include "libdwP.h"
Packit 032894
#include "cfi.h"
Packit 032894
#include "encoded-value.h"
Packit 032894
#include <dwarf.h>
Packit 032894
Packit 032894
Packit 032894
static Dwarf_CFI *
Packit 032894
allocate_cfi (Elf *elf, GElf_Addr vaddr)
Packit 032894
{
Packit 032894
  Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
Packit 032894
  if (cfi == NULL)
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_NOMEM);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
Packit 032894
  if (cfi->e_ident == NULL)
Packit 032894
    {
Packit 032894
      free (cfi);
Packit 032894
      __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
Packit 032894
      || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
Packit 032894
    cfi->other_byte_order = true;
Packit 032894
Packit 032894
  cfi->frame_vaddr = vaddr;
Packit 032894
  cfi->textrel = 0;		/* XXX ? */
Packit 032894
  cfi->datarel = 0;		/* XXX ? */
Packit 032894
Packit 032894
  return cfi;
Packit 032894
}
Packit 032894
Packit 032894
static const uint8_t *
Packit 032894
parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
Packit 032894
		    const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
Packit 032894
		    size_t *table_entries, uint8_t *table_encoding)
Packit 032894
{
Packit 032894
  const uint8_t *h = hdr;
Packit 032894
Packit 032894
  if (hdr_size < 4 || *h++ != 1)		/* version */
Packit 032894
    return (void *) -1l;
Packit 032894
Packit 032894
  uint8_t eh_frame_ptr_encoding = *h++;
Packit 032894
  uint8_t fde_count_encoding = *h++;
Packit 032894
  uint8_t fde_table_encoding = *h++;
Packit 032894
Packit 032894
  if (eh_frame_ptr_encoding == DW_EH_PE_omit)
Packit 032894
    return (void *) -1l;
Packit 032894
Packit 032894
  /* Dummy used by read_encoded_value.  */
Packit 032894
  Elf_Data_Scn dummy_cfi_hdr_data =
Packit 032894
    {
Packit 032894
      .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
Packit 032894
    };
Packit 032894
  Dwarf_CFI dummy_cfi =
Packit 032894
    {
Packit 032894
      .e_ident = ehdr->e_ident,
Packit 032894
      .datarel = hdr_vaddr,
Packit 032894
      .frame_vaddr = hdr_vaddr,
Packit 032894
      .data = &dummy_cfi_hdr_data,
Packit 032894
    };
Packit 032894
Packit 032894
  if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
Packit 032894
				    eh_frame_vaddr)))
Packit 032894
    return (void *) -1l;
Packit 032894
Packit 032894
  if (fde_count_encoding != DW_EH_PE_omit)
Packit 032894
    {
Packit 032894
      Dwarf_Word fde_count;
Packit 032894
      if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
Packit 032894
					&fde_count)))
Packit 032894
	return (void *) -1l;
Packit 032894
      if (fde_count != 0 && (size_t) fde_count == fde_count
Packit 032894
	  && fde_table_encoding != DW_EH_PE_omit
Packit 032894
	  && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
Packit 032894
	{
Packit 032894
	  *table_entries = fde_count;
Packit 032894
	  *table_encoding = fde_table_encoding;
Packit 032894
	  return h;
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  return NULL;
Packit 032894
}
Packit 032894
Packit 032894
static Dwarf_CFI *
Packit 032894
getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
Packit 032894
{
Packit 032894
  Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
Packit 032894
					 ELF_T_BYTE);
Packit 032894
  if (data == NULL || data->d_buf == NULL)
Packit 032894
    {
Packit 032894
    invalid_hdr:
Packit 032894
      /* XXX might be read error or corrupt phdr */
Packit 032894
      __libdw_seterrno (DWARF_E_INVALID_CFI);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  size_t vsize, dmax;
Packit 032894
  Dwarf_Addr eh_frame_ptr;
Packit 032894
  size_t search_table_entries = 0;
Packit 032894
  uint8_t search_table_encoding = 0;
Packit 032894
  const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
Packit 032894
						    phdr->p_vaddr, ehdr,
Packit 032894
						    &eh_frame_ptr,
Packit 032894
						    &search_table_entries,
Packit 032894
						    &search_table_encoding);
Packit 032894
Packit 032894
  /* Make sure there is enough room for the entries in the table,
Packit 032894
     each entry consists of 2 encoded values.  */
Packit 032894
  vsize = encoded_value_size (data, ehdr->e_ident, search_table_encoding,
Packit 032894
			      NULL);
Packit 032894
  dmax = phdr->p_filesz - (search_table - (const uint8_t *) data->d_buf);
Packit 032894
  if (unlikely (search_table == (void *) -1l
Packit 032894
		|| vsize == 0
Packit 032894
		|| search_table_entries > (dmax / vsize) / 2))
Packit 032894
    goto invalid_hdr;
Packit 032894
Packit 032894
  Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
Packit 032894
  Dwarf_Word eh_frame_size = 0;
Packit 032894
Packit 032894
  /* XXX we have no way without section headers to know the size
Packit 032894
     of the .eh_frame data.  Calculate the largest it might possibly be.
Packit 032894
     This won't be wasteful if the file is already mmap'd, but if it isn't
Packit 032894
     it might be quite excessive.  */
Packit 032894
  size_t filesize;
Packit 032894
  if (elf_rawfile (elf, &filesize) != NULL)
Packit 032894
    eh_frame_size = filesize - eh_frame_offset;
Packit 032894
Packit 032894
  data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
Packit 032894
  if (data == NULL)
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
  Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
Packit 032894
  if (cfi != NULL)
Packit 032894
    {
Packit 032894
      cfi->data = (Elf_Data_Scn *) data;
Packit 032894
Packit 032894
      if (search_table != NULL)
Packit 032894
	{
Packit 032894
	  cfi->search_table = search_table;
Packit 032894
	  cfi->search_table_len = phdr->p_filesz;
Packit 032894
	  cfi->search_table_vaddr = phdr->p_vaddr;
Packit 032894
	  cfi->search_table_encoding = search_table_encoding;
Packit 032894
	  cfi->search_table_entries = search_table_entries;
Packit 032894
	}
Packit 032894
    }
Packit 032894
  return cfi;
Packit 032894
}
Packit 032894
Packit 032894
/* Search the phdrs for PT_GNU_EH_FRAME.  */
Packit 032894
static Dwarf_CFI *
Packit 032894
getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
Packit 032894
{
Packit 032894
  size_t phnum;
Packit 032894
  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
Packit 032894
    return NULL;
Packit 032894
Packit 032894
  for (size_t i = 0; i < phnum; ++i)
Packit 032894
    {
Packit 032894
      GElf_Phdr phdr_mem;
Packit 032894
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
Packit 032894
      if (unlikely (phdr == NULL))
Packit 032894
	return NULL;
Packit 032894
      if (phdr->p_type == PT_GNU_EH_FRAME)
Packit 032894
	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
Packit 032894
    }
Packit 032894
Packit 032894
  __libdw_seterrno (DWARF_E_NO_DWARF);
Packit 032894
  return NULL;
Packit 032894
}
Packit 032894
Packit 032894
static Dwarf_CFI *
Packit 032894
getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
Packit 032894
		     Elf_Scn *scn, GElf_Shdr *shdr,
Packit 032894
		     Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
Packit 032894
{
Packit 032894
  Elf_Data *data = elf_rawdata (scn, NULL);
Packit 032894
  if (data == NULL || data->d_buf == NULL)
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_INVALID_ELF);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
  Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
Packit 032894
  if (cfi != NULL)
Packit 032894
    {
Packit 032894
      cfi->data = (Elf_Data_Scn *) data;
Packit 032894
      if (hdr_scn != NULL)
Packit 032894
	{
Packit 032894
	  Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
Packit 032894
	  if (hdr_data != NULL && hdr_data->d_buf != NULL)
Packit 032894
	    {
Packit 032894
	      size_t vsize, dmax;
Packit 032894
	      GElf_Addr eh_frame_vaddr;
Packit 032894
	      cfi->search_table_vaddr = hdr_vaddr;
Packit 032894
	      cfi->search_table
Packit 032894
		= parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
Packit 032894
				      hdr_vaddr, ehdr, &eh_frame_vaddr,
Packit 032894
				      &cfi->search_table_entries,
Packit 032894
				      &cfi->search_table_encoding);
Packit 032894
	      cfi->search_table_len = hdr_data->d_size;
Packit 032894
Packit 032894
	      /* Make sure there is enough room for the entries in the table,
Packit 032894
		 each entry consists of 2 encoded values.  */
Packit 032894
	      vsize = encoded_value_size (hdr_data, ehdr->e_ident,
Packit 032894
					  cfi->search_table_encoding, NULL);
Packit 032894
	      dmax = hdr_data->d_size - (cfi->search_table
Packit 032894
					 - (const uint8_t *) hdr_data->d_buf);
Packit 032894
	      if (unlikely (cfi->search_table == (void *) -1l
Packit 032894
			    || vsize == 0
Packit 032894
			    || cfi->search_table_entries > (dmax / vsize) / 2))
Packit 032894
		{
Packit 032894
		  free (cfi);
Packit 032894
		  /* XXX might be read error or corrupt phdr */
Packit 032894
		  __libdw_seterrno (DWARF_E_INVALID_CFI);
Packit 032894
		  return NULL;
Packit 032894
		}
Packit 032894
Packit 032894
	      /* Sanity check.  */
Packit 032894
	      if (unlikely (eh_frame_vaddr != shdr->sh_addr))
Packit 032894
		cfi->search_table = NULL;
Packit 032894
	    }
Packit 032894
	}
Packit 032894
    }
Packit 032894
  return cfi;
Packit 032894
}
Packit 032894
Packit 032894
/* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
Packit 032894
static Dwarf_CFI *
Packit 032894
getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
Packit 032894
{
Packit 032894
  size_t shstrndx;
Packit 032894
  if (elf_getshdrstrndx (elf, &shstrndx) != 0)
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  if (shstrndx != 0)
Packit 032894
    {
Packit 032894
      Elf_Scn *hdr_scn = NULL;
Packit 032894
      GElf_Addr hdr_vaddr = 0;
Packit 032894
      Elf_Scn *scn = NULL;
Packit 032894
      while ((scn = elf_nextscn (elf, scn)) != NULL)
Packit 032894
	{
Packit 032894
	  GElf_Shdr shdr_mem;
Packit 032894
	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
Packit 032894
	  if (shdr == NULL)
Packit 032894
	    continue;
Packit 032894
	  const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
Packit 032894
	  if (name == NULL)
Packit 032894
	    continue;
Packit 032894
	  if (!strcmp (name, ".eh_frame_hdr"))
Packit 032894
	    {
Packit 032894
	      hdr_scn = scn;
Packit 032894
	      hdr_vaddr = shdr->sh_addr;
Packit 032894
	    }
Packit 032894
	  else if (!strcmp (name, ".eh_frame"))
Packit 032894
	    {
Packit 032894
	      if (shdr->sh_type != SHT_NOBITS)
Packit 032894
		return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
Packit 032894
					    hdr_scn, hdr_vaddr);
Packit 032894
	      else
Packit 032894
		return NULL;
Packit 032894
	    }
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  return (void *) -1l;
Packit 032894
}
Packit 032894
Packit 032894
Dwarf_CFI *
Packit 032894
dwarf_getcfi_elf (Elf *elf)
Packit 032894
{
Packit 032894
  if (elf_kind (elf) != ELF_K_ELF)
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_NOELF);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  GElf_Ehdr ehdr_mem;
Packit 032894
  GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
Packit 032894
  if (unlikely (ehdr == NULL))
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_INVALID_ELF);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  Dwarf_CFI *result = getcfi_shdr (elf, ehdr);
Packit 032894
  if (result == (void *) -1l)
Packit 032894
    result = getcfi_phdr (elf, ehdr);
Packit 032894
Packit 032894
  return result;
Packit 032894
}
Packit 032894
INTDEF (dwarf_getcfi_elf)