Blame libdw/dwarf_next_cfi.c

Packit Service 97d2fb
/* Advance to next CFI entry.
Packit Service 97d2fb
   Copyright (C) 2009-2010, 2014 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 "encoded-value.h"
Packit Service 97d2fb
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwarf_next_cfi (const unsigned char e_ident[],
Packit Service 97d2fb
		Elf_Data *data,
Packit Service 97d2fb
		bool eh_frame_p,
Packit Service 97d2fb
		Dwarf_Off off,
Packit Service 97d2fb
		Dwarf_Off *next_off,
Packit Service 97d2fb
		Dwarf_CFI_Entry *entry)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Dummy struct for memory-access.h macros.  */
Packit Service 97d2fb
  BYTE_ORDER_DUMMY (dw, e_ident);
Packit Service 97d2fb
Packit Service 97d2fb
  /* If we reached the end before don't do anything.  */
Packit Service 97d2fb
  if (off == (Dwarf_Off) -1l
Packit Service 97d2fb
      /* Make sure there is enough space in the .debug_frame section
Packit Service 97d2fb
	 for at least the initial word.  We cannot test the rest since
Packit Service 97d2fb
	 we don't know yet whether this is a 64-bit object or not.  */
Packit Service 97d2fb
      || unlikely (off + 4 >= data->d_size))
Packit Service 97d2fb
    {
Packit Service 97d2fb
    done:
Packit Service 97d2fb
      *next_off = (Dwarf_Off) -1l;
Packit Service 97d2fb
      return 1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* This points into the .debug_frame section at the start of the entry.  */
Packit Service 97d2fb
  const uint8_t *bytes = data->d_buf + off;
Packit Service 97d2fb
  const uint8_t *limit = data->d_buf + data->d_size;
Packit Service 97d2fb
Packit Service 97d2fb
  /* The format of a CFI entry is described in DWARF3 6.4.1:
Packit Service 97d2fb
   */
Packit Service 97d2fb
Packit Service 97d2fb
  uint64_t length = read_4ubyte_unaligned_inc (&dw, bytes);
Packit Service 97d2fb
  size_t offset_size = 4;
Packit Service 97d2fb
  if (length == DWARF3_LENGTH_64_BIT)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* This is the 64-bit DWARF format.  */
Packit Service 97d2fb
      offset_size = 8;
Packit Service 97d2fb
      if (unlikely (limit - bytes < 8))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	invalid:
Packit Service 97d2fb
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit Service 97d2fb
	  return -1;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      length = read_8ubyte_unaligned_inc (&dw, bytes);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Not explicitly in the DWARF spec, but mentioned in the LSB exception
Packit Service 97d2fb
     frames (.eh_frame) spec. If Length contains the value 0, then this
Packit Service 97d2fb
     CIE shall be considered a terminator and processing shall end.  */
Packit Service 97d2fb
  if (length == 0)
Packit Service 97d2fb
    goto done;
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely ((uint64_t) (limit - bytes) < length)
Packit Service 97d2fb
      || unlikely (length < offset_size + 1))
Packit Service 97d2fb
    goto invalid;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Now we know how large the entry is.  Note the trick in the
Packit Service 97d2fb
     computation.  If the offset_size is 4 the '- 4' term undoes the
Packit Service 97d2fb
     '2 *'.  If offset_size is 8 this term computes the size of the
Packit Service 97d2fb
     escape value plus the 8 byte offset.  */
Packit Service 97d2fb
  *next_off = off + (2 * offset_size - 4) + length;
Packit Service 97d2fb
Packit Service 97d2fb
  limit = bytes + length;
Packit Service 97d2fb
Packit Service 97d2fb
  const uint8_t *const cie_pointer_start = bytes;
Packit Service 97d2fb
  if (offset_size == 8)
Packit Service 97d2fb
    entry->cie.CIE_id = read_8ubyte_unaligned_inc (&dw, bytes);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      entry->cie.CIE_id = read_4ubyte_unaligned_inc (&dw, bytes);
Packit Service 97d2fb
      /* Canonicalize the 32-bit CIE_ID value to 64 bits.  */
Packit Service 97d2fb
      if (!eh_frame_p && entry->cie.CIE_id == DW_CIE_ID_32)
Packit Service 97d2fb
	entry->cie.CIE_id = DW_CIE_ID_64;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  if (eh_frame_p)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Canonicalize the .eh_frame CIE pointer to .debug_frame format.  */
Packit Service 97d2fb
      if (entry->cie.CIE_id == 0)
Packit Service 97d2fb
	entry->cie.CIE_id = DW_CIE_ID_64;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* In .eh_frame format, a CIE pointer is the distance from where
Packit Service 97d2fb
	     it appears back to the beginning of the CIE.  */
Packit Service 97d2fb
	  ptrdiff_t pos = cie_pointer_start - (const uint8_t *) data->d_buf;
Packit Service 97d2fb
	  if (unlikely (entry->cie.CIE_id > (Dwarf_Off) pos)
Packit Service 97d2fb
	      || unlikely (pos <= (ptrdiff_t) offset_size))
Packit Service 97d2fb
	    goto invalid;
Packit Service 97d2fb
	  entry->cie.CIE_id = pos - entry->cie.CIE_id;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (entry->cie.CIE_id == DW_CIE_ID_64)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* Read the version stamp.  Always an 8-bit value.  */
Packit Service 97d2fb
      uint8_t version = *bytes++;
Packit Service 97d2fb
Packit Service 97d2fb
      if (version != 1 && (unlikely (version < 3) || unlikely (version > 4)))
Packit Service 97d2fb
	goto invalid;
Packit Service 97d2fb
Packit Service 97d2fb
      entry->cie.augmentation = (const char *) bytes;
Packit Service 97d2fb
Packit Service 97d2fb
      bytes = memchr (bytes, '\0', limit - bytes);
Packit Service 97d2fb
      if (unlikely (bytes == NULL))
Packit Service 97d2fb
	goto invalid;
Packit Service 97d2fb
      ++bytes;
Packit Service 97d2fb
Packit Service 97d2fb
      /* The address size for CFI is implicit in the ELF class.  */
Packit Service 97d2fb
      uint_fast8_t address_size = e_ident[EI_CLASS] == ELFCLASS32 ? 4 : 8;
Packit Service 97d2fb
      uint_fast8_t segment_size = 0;
Packit Service 97d2fb
      if (version >= 4)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (unlikely (limit - bytes < 5))
Packit Service 97d2fb
	    goto invalid;
Packit Service 97d2fb
	  /* XXX We don't actually support address_size not matching the class.
Packit Service 97d2fb
	     To do so, we'd have to return it here so that intern_new_cie
Packit Service 97d2fb
	     could use it choose a specific fde_encoding.  */
Packit Service 97d2fb
	  if (unlikely (*bytes != address_size))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdw_seterrno (DWARF_E_VERSION);
Packit Service 97d2fb
	      return -1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  address_size = *bytes++;
Packit Service 97d2fb
	  segment_size = *bytes++;
Packit Service 97d2fb
	  /* We don't actually support segment selectors.  We'd have to
Packit Service 97d2fb
	     roll this into the fde_encoding bits or something.  */
Packit Service 97d2fb
	  if (unlikely (segment_size != 0))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      __libdw_seterrno (DWARF_E_VERSION);
Packit Service 97d2fb
	      return -1;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      const char *ap = entry->cie.augmentation;
Packit Service 97d2fb
Packit Service 97d2fb
      /* g++ v2 "eh" has pointer immediately following augmentation string,
Packit Service 97d2fb
	 so it must be handled first.  */
Packit Service 97d2fb
      if (unlikely (ap[0] == 'e' && ap[1] == 'h'))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  ap += 2;
Packit Service 97d2fb
	  bytes += address_size;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      if (bytes >= limit)
Packit Service 97d2fb
	goto invalid;
Packit Service 97d2fb
      get_uleb128 (entry->cie.code_alignment_factor, bytes, limit);
Packit Service 97d2fb
Packit Service 97d2fb
      if (bytes >= limit)
Packit Service 97d2fb
	goto invalid;
Packit Service 97d2fb
      get_sleb128 (entry->cie.data_alignment_factor, bytes, limit);
Packit Service 97d2fb
Packit Service 97d2fb
      if (bytes >= limit)
Packit Service 97d2fb
	goto invalid;
Packit Service 97d2fb
Packit Service 97d2fb
      if (version >= 3)		/* DWARF 3+ */
Packit Service 97d2fb
	get_uleb128 (entry->cie.return_address_register, bytes, limit);
Packit Service 97d2fb
      else			/* DWARF 2 */
Packit Service 97d2fb
	entry->cie.return_address_register = *bytes++;
Packit Service 97d2fb
Packit Service 97d2fb
      /* If we have sized augmentation data,
Packit Service 97d2fb
	 we don't need to grok it all.  */
Packit Service 97d2fb
      entry->cie.fde_augmentation_data_size = 0;
Packit Service 97d2fb
      bool sized_augmentation = *ap == 'z';
Packit Service 97d2fb
      if (sized_augmentation)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (bytes >= limit)
Packit Service 97d2fb
	    goto invalid;
Packit Service 97d2fb
	  get_uleb128 (entry->cie.augmentation_data_size, bytes, limit);
Packit Service 97d2fb
	  if ((Dwarf_Word) (limit - bytes) < entry->cie.augmentation_data_size)
Packit Service 97d2fb
	    goto invalid;
Packit Service 97d2fb
	  entry->cie.augmentation_data = bytes;
Packit Service 97d2fb
	  bytes += entry->cie.augmentation_data_size;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  entry->cie.augmentation_data = bytes;
Packit Service 97d2fb
Packit Service 97d2fb
	  for (; *ap != '\0'; ++ap)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      uint8_t encoding;
Packit Service 97d2fb
	      switch (*ap)
Packit Service 97d2fb
		{
Packit Service 97d2fb
		case 'L':		/* Skip LSDA pointer encoding byte.  */
Packit Service 97d2fb
		case 'R':		/* Skip FDE address encoding byte.  */
Packit Service 97d2fb
		  encoding = *bytes++;
Packit Service 97d2fb
		  entry->cie.fde_augmentation_data_size
Packit Service 97d2fb
		    += encoded_value_size (data, e_ident, encoding, NULL);
Packit Service 97d2fb
		  continue;
Packit Service 97d2fb
		case 'P':   /* Skip encoded personality routine pointer. */
Packit Service 97d2fb
		  encoding = *bytes++;
Packit Service 97d2fb
		  bytes += encoded_value_size (data, e_ident, encoding, bytes);
Packit Service 97d2fb
		  continue;
Packit Service 97d2fb
		case 'S':		/* Skip signal-frame flag.  */
Packit Service 97d2fb
		  continue;
Packit Service 97d2fb
		default:
Packit Service 97d2fb
		  /* Unknown augmentation string.  initial_instructions might
Packit Service 97d2fb
		     actually start with some augmentation data.  */
Packit Service 97d2fb
		  break;
Packit Service 97d2fb
		}
Packit Service 97d2fb
	      break;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	  entry->cie.augmentation_data_size
Packit Service 97d2fb
	    = bytes - entry->cie.augmentation_data;
Packit Service 97d2fb
	}
Packit Service 97d2fb
Packit Service 97d2fb
      entry->cie.initial_instructions = bytes;
Packit Service 97d2fb
      entry->cie.initial_instructions_end = limit;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    {
Packit Service 97d2fb
      entry->fde.start = bytes;
Packit Service 97d2fb
      entry->fde.end = limit;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwarf_next_cfi)