Blame libdw/fde.c

Packit Service 97d2fb
/* FDE reading.
Packit Service 97d2fb
   Copyright (C) 2009-2010, 2014, 2015 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include "cfi.h"
Packit Service 97d2fb
#include <search.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include "encoded-value.h"
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
compare_fde (const void *a, const void *b)
Packit Service 97d2fb
{
Packit Service 97d2fb
  const struct dwarf_fde *fde1 = a;
Packit Service 97d2fb
  const struct dwarf_fde *fde2 = b;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Find out which of the two arguments is the search value.
Packit Service 97d2fb
     It has end offset 0.  */
Packit Service 97d2fb
  if (fde1->end == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (fde1->start < fde2->start)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
      if (fde1->start >= fde2->end)
Packit Service 97d2fb
	return 1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (fde2->start < fde1->start)
Packit Service 97d2fb
	return 1;
Packit Service 97d2fb
      if (fde2->start >= fde1->end)
Packit Service 97d2fb
	return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static struct dwarf_fde *
Packit Service 97d2fb
intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Look up the new entry's CIE.  */
Packit Service 97d2fb
  struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
Packit Service 97d2fb
  if (cie == NULL)
Packit Service 97d2fb
    return (void *) -1l;
Packit Service 97d2fb
Packit Service 97d2fb
  struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde));
Packit Service 97d2fb
  if (fde == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_NOMEM);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  fde->instructions = entry->start;
Packit Service 97d2fb
  fde->instructions_end = entry->end;
Packit Service 97d2fb
  if (unlikely (read_encoded_value (cache, cie->fde_encoding,
Packit Service 97d2fb
				    &fde->instructions, &fde->start))
Packit Service 97d2fb
      || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
Packit Service 97d2fb
				       &fde->instructions, &fde->end)))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (fde);
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  fde->end += fde->start;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Make sure the fde actually covers a real code range.  */
Packit Service 97d2fb
  if (fde->start >= fde->end)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (fde);
Packit Service 97d2fb
      return (void *) -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  fde->cie = cie;
Packit Service 97d2fb
Packit Service 97d2fb
  if (cie->sized_augmentation_data)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* The CIE augmentation says the FDE has a DW_FORM_block
Packit Service 97d2fb
	 before its actual instruction stream.  */
Packit Service 97d2fb
      Dwarf_Word len;
Packit Service 97d2fb
      get_uleb128 (len, fde->instructions, fde->instructions_end);
Packit Service 97d2fb
      if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  free (fde);
Packit Service 97d2fb
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
	  return NULL;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      fde->instructions += len;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    /* We had to understand all of the CIE augmentation string.
Packit Service 97d2fb
       We've recorded the number of data bytes in FDEs.  */
Packit Service 97d2fb
    fde->instructions += cie->fde_augmentation_data_size;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Add the new entry to the search tree.  */
Packit Service 97d2fb
  struct dwarf_fde **tres = tsearch (fde, &cache->fde_tree, &compare_fde);
Packit Service 97d2fb
  if (tres == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (fde);
Packit Service 97d2fb
      __libdw_seterrno (DWARF_E_NOMEM);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else if (*tres != fde)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* There is already an FDE in the cache that covers the same
Packit Service 97d2fb
	 address range.  That is odd.  Ignore this FDE.  And just use
Packit Service 97d2fb
	 the one in the cache for consistency.  */
Packit Service 97d2fb
      free (fde);
Packit Service 97d2fb
      return *tres;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return fde;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
struct dwarf_fde *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
Packit Service 97d2fb
{
Packit Service 97d2fb
  Dwarf_CFI_Entry entry;
Packit Service 97d2fb
  Dwarf_Off next_offset;
Packit Service 97d2fb
  int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
Packit Service 97d2fb
				       &cache->data->d, CFI_IS_EH (cache),
Packit Service 97d2fb
				       offset, &next_offset, &entry);
Packit Service 97d2fb
  if (result != 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (result > 0)
Packit Service 97d2fb
      invalid:
Packit Service 97d2fb
	__libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
      return NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (dwarf_cfi_cie_p (&entry)))
Packit Service 97d2fb
    goto invalid;
Packit Service 97d2fb
Packit Service 97d2fb
  /* We have a new FDE to consider.  */
Packit Service 97d2fb
  struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
Packit Service 97d2fb
  if (fde == (void *) -1l || fde == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  /* If this happened to be what we would have read next, notice it.  */
Packit Service 97d2fb
  if (cache->next_offset == offset)
Packit Service 97d2fb
    cache->next_offset = next_offset;
Packit Service 97d2fb
Packit Service 97d2fb
  return fde;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
/* Use a binary search table in .eh_frame_hdr format, yield an FDE offset.  */
Packit Service 97d2fb
static Dwarf_Off
Packit Service 97d2fb
binary_search_fde (Dwarf_CFI *cache, Dwarf_Addr address)
Packit Service 97d2fb
{
Packit Service 97d2fb
  const size_t size = 2 * encoded_value_size (&cache->data->d, cache->e_ident,
Packit Service 97d2fb
					      cache->search_table_encoding,
Packit Service 97d2fb
					      NULL);
Packit Service 97d2fb
  if (unlikely (size == 0))
Packit Service 97d2fb
    return (Dwarf_Off) -1l;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Dummy used by read_encoded_value.  */
Packit Service 97d2fb
  Elf_Data_Scn dummy_cfi_hdr_data =
Packit Service 97d2fb
    {
Packit Service 97d2fb
      .d = { .d_buf = (void *) cache->search_table,
Packit Service 97d2fb
	     .d_size = cache->search_table_len }
Packit Service 97d2fb
    };
Packit Service 97d2fb
Packit Service 97d2fb
  Dwarf_CFI dummy_cfi =
Packit Service 97d2fb
    {
Packit Service 97d2fb
      .e_ident = cache->e_ident,
Packit Service 97d2fb
      .datarel = cache->search_table_vaddr,
Packit Service 97d2fb
      .frame_vaddr = cache->search_table_vaddr,
Packit Service 97d2fb
      .data = &dummy_cfi_hdr_data
Packit Service 97d2fb
    };
Packit Service 97d2fb
Packit Service 97d2fb
  size_t l = 0, u = cache->search_table_entries;
Packit Service 97d2fb
  while (l < u)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      size_t idx = (l + u) / 2;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Max idx * size is checked against search_table len when
Packit Service 97d2fb
	 loading eh_frame_hdr.  */
Packit Service 97d2fb
      const uint8_t *p = &cache->search_table[idx * size];
Packit Service 97d2fb
      Dwarf_Addr start;
Packit Service 97d2fb
      if (unlikely (read_encoded_value (&dummy_cfi,
Packit Service 97d2fb
					cache->search_table_encoding, &p,
Packit Service 97d2fb
					&start)))
Packit Service 97d2fb
	break;
Packit Service 97d2fb
      if (address < start)
Packit Service 97d2fb
	u = idx;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  l = idx + 1;
Packit Service 97d2fb
Packit Service 97d2fb
	  Dwarf_Addr fde;
Packit Service 97d2fb
	  if (unlikely (read_encoded_value (&dummy_cfi,
Packit Service 97d2fb
					    cache->search_table_encoding, &p,
Packit Service 97d2fb
					    &fde)))
Packit Service 97d2fb
	    break;
Packit Service 97d2fb
Packit Service 97d2fb
	  /* If this is the last entry, its upper bound is assumed to be
Packit Service 97d2fb
	     the end of the module.
Packit Service 97d2fb
	     XXX really should be end of containing PT_LOAD segment */
Packit Service 97d2fb
	  if (l < cache->search_table_entries)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      /* Look at the start address in the following entry.  */
Packit Service 97d2fb
	      Dwarf_Addr end;
Packit Service 97d2fb
	      if (unlikely (read_encoded_value
Packit Service 97d2fb
			    (&dummy_cfi, cache->search_table_encoding, &p,
Packit Service 97d2fb
			     &end)))
Packit Service 97d2fb
		break;
Packit Service 97d2fb
	      if (address >= end)
Packit Service 97d2fb
		continue;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
Packit Service 97d2fb
	  return fde - cache->frame_vaddr;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return (Dwarf_Off) -1l;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
struct dwarf_fde *
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdw_find_fde (Dwarf_CFI *cache, Dwarf_Addr address)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Look for a cached FDE covering this address.  */
Packit Service 97d2fb
Packit Service 97d2fb
  const struct dwarf_fde fde_key = { .start = address, .end = 0 };
Packit Service 97d2fb
  struct dwarf_fde **found = tfind (&fde_key, &cache->fde_tree, &compare_fde);
Packit Service 97d2fb
  if (found != NULL)
Packit Service 97d2fb
    return *found;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Use .eh_frame_hdr binary search table if possible.  */
Packit Service 97d2fb
  if (cache->search_table != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Off offset = binary_search_fde (cache, address);
Packit Service 97d2fb
      if (offset == (Dwarf_Off) -1l)
Packit Service 97d2fb
	goto no_match;
Packit Service 97d2fb
      struct dwarf_fde *fde = __libdw_fde_by_offset (cache, offset);
Packit Service 97d2fb
      if (likely (fde != NULL))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Sanity check the address range.  */
Packit Service 97d2fb
	  if (unlikely (address < fde->start))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
	      return NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  /* .eh_frame_hdr does not indicate length covered by FDE.  */
Packit Service 97d2fb
	  if (unlikely (address >= fde->end))
Packit Service 97d2fb
	    goto no_match;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      return fde;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* It's not there.  Read more CFI entries until we find it.  */
Packit Service 97d2fb
  while (1)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      Dwarf_Off last_offset = cache->next_offset;
Packit Service 97d2fb
      Dwarf_CFI_Entry entry;
Packit Service 97d2fb
      int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
Packit Service 97d2fb
					   &cache->data->d, CFI_IS_EH (cache),
Packit Service 97d2fb
					   last_offset, &cache->next_offset,
Packit Service 97d2fb
					   &entry);
Packit Service 97d2fb
      if (result > 0)
Packit Service 97d2fb
	break;
Packit Service 97d2fb
      if (result < 0)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (cache->next_offset == last_offset)
Packit Service 97d2fb
	    /* We couldn't progress past the bogus FDE.  */
Packit Service 97d2fb
	    break;
Packit Service 97d2fb
	  /* Skip the loser and look at the next entry.  */
Packit Service 97d2fb
	  continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (dwarf_cfi_cie_p (&entry))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* This is a CIE, not an FDE.  We eagerly intern these
Packit Service 97d2fb
	     because the next FDE will usually refer to this CIE.  */
Packit Service 97d2fb
	  __libdw_intern_cie (cache, last_offset, &entry.cie);
Packit Service 97d2fb
	  continue;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      /* We have a new FDE to consider.  */
Packit Service 97d2fb
      struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
Packit Service 97d2fb
Packit Service 97d2fb
      if (fde == (void *) -1l)	/* Bad FDE, but we can keep looking.  */
Packit Service 97d2fb
	continue;
Packit Service 97d2fb
Packit Service 97d2fb
      if (fde == NULL)		/* Bad data.  */
Packit Service 97d2fb
	return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
      /* Is this the one we're looking for?  */
Packit Service 97d2fb
      if (fde->start <= address && fde->end > address)
Packit Service 97d2fb
	return fde;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
 no_match:
Packit Service 97d2fb
  /* We found no FDE covering this address.  */
Packit Service 97d2fb
  __libdw_seterrno (DWARF_E_NO_MATCH);
Packit Service 97d2fb
  return NULL;
Packit Service 97d2fb
}