Blame libdw/dwarf_getaranges.c

Packit 032894
/* Return list address ranges.
Packit 032894
   Copyright (C) 2000-2010, 2016, 2017 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
   Written by Ulrich Drepper <drepper@redhat.com>, 2000.
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 <assert.h>
Packit 032894
#include "libdwP.h"
Packit 032894
#include <dwarf.h>
Packit 032894
Packit 032894
struct arangelist
Packit 032894
{
Packit 032894
  Dwarf_Arange arange;
Packit 032894
  struct arangelist *next;
Packit 032894
};
Packit 032894
Packit 032894
/* Compare by Dwarf_Arange.addr, given pointers into an array of pointeers.  */
Packit 032894
static int
Packit 032894
compare_aranges (const void *a, const void *b)
Packit 032894
{
Packit 032894
  struct arangelist *const *p1 = a, *const *p2 = b;
Packit 032894
  struct arangelist *l1 = *p1, *l2 = *p2;
Packit 032894
  if (l1->arange.addr != l2->arange.addr)
Packit 032894
    return (l1->arange.addr < l2->arange.addr) ? -1 : 1;
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
int
Packit 032894
dwarf_getaranges (Dwarf *dbg, Dwarf_Aranges **aranges, size_t *naranges)
Packit 032894
{
Packit 032894
  if (dbg == NULL)
Packit 032894
    return -1;
Packit 032894
Packit 032894
  if (dbg->aranges != NULL)
Packit 032894
    {
Packit 032894
      *aranges = dbg->aranges;
Packit 032894
      if (naranges != NULL)
Packit 032894
	*naranges = dbg->aranges->naranges;
Packit 032894
      return 0;
Packit 032894
    }
Packit 032894
Packit 032894
  if (dbg->sectiondata[IDX_debug_aranges] == NULL)
Packit 032894
    {
Packit 032894
      /* No such section.  */
Packit 032894
      *aranges = NULL;
Packit 032894
      if (naranges != NULL)
Packit 032894
	*naranges = 0;
Packit 032894
      return 0;
Packit 032894
    }
Packit 032894
Packit 032894
  if (dbg->sectiondata[IDX_debug_aranges]->d_buf == NULL)
Packit 032894
    return -1;
Packit 032894
Packit 032894
  struct arangelist *arangelist = NULL;
Packit 032894
  unsigned int narangelist = 0;
Packit 032894
Packit 032894
  const unsigned char *readp = dbg->sectiondata[IDX_debug_aranges]->d_buf;
Packit 032894
  const unsigned char *readendp
Packit 032894
    = readp + dbg->sectiondata[IDX_debug_aranges]->d_size;
Packit 032894
Packit 032894
  while (readp < readendp)
Packit 032894
    {
Packit 032894
      const unsigned char *hdrstart = readp;
Packit 032894
Packit 032894
      /* Each entry starts with a header:
Packit 032894
Packit 032894
	 1. A 4-byte or 12-byte length containing the length of the
Packit 032894
	 set of entries for this compilation unit, not including the
Packit 032894
	 length field itself. [...]
Packit 032894
Packit 032894
	 2. A 2-byte version identifier containing the value 2 for
Packit 032894
	 DWARF Version 2.1.
Packit 032894
Packit 032894
	 3. A 4-byte or 8-byte offset into the .debug_info section. [...]
Packit 032894
Packit 032894
	 4. A 1-byte unsigned integer containing the size in bytes of
Packit 032894
	 an address (or the offset portion of an address for segmented
Packit 032894
	 addressing) on the target system.
Packit 032894
Packit 032894
	 5. A 1-byte unsigned integer containing the size in bytes of
Packit 032894
	 a segment descriptor on the target system.  */
Packit 032894
      if (unlikely (readp + 4 > readendp))
Packit 032894
	goto invalid;
Packit 032894
Packit 032894
      Dwarf_Word length = read_4ubyte_unaligned_inc (dbg, readp);
Packit 032894
      unsigned int length_bytes = 4;
Packit 032894
      if (length == DWARF3_LENGTH_64_BIT)
Packit 032894
	{
Packit 032894
	  if (unlikely (readp + 8 > readendp))
Packit 032894
	    goto invalid;
Packit 032894
Packit 032894
	  length = read_8ubyte_unaligned_inc (dbg, readp);
Packit 032894
	  length_bytes = 8;
Packit 032894
	}
Packit 032894
      else if (unlikely (length >= DWARF3_LENGTH_MIN_ESCAPE_CODE
Packit 032894
			 && length <= DWARF3_LENGTH_MAX_ESCAPE_CODE))
Packit 032894
	goto invalid;
Packit 032894
Packit 032894
      if (unlikely (readp + 2 > readendp))
Packit 032894
	goto invalid;
Packit 032894
Packit 032894
      unsigned int version = read_2ubyte_unaligned_inc (dbg, readp);
Packit 032894
      if (version != 2)
Packit 032894
	{
Packit 032894
	invalid:
Packit 032894
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit 032894
	fail:
Packit 032894
	  while (arangelist != NULL)
Packit 032894
	    {
Packit 032894
	      struct arangelist *next = arangelist->next;
Packit 032894
	      free (arangelist);
Packit 032894
	      arangelist = next;
Packit 032894
	    }
Packit 032894
	  return -1;
Packit 032894
	}
Packit 032894
Packit 032894
      Dwarf_Word offset = 0;
Packit 032894
      if (__libdw_read_offset_inc (dbg,
Packit 032894
				   IDX_debug_aranges, &readp,
Packit 032894
				   length_bytes, &offset, IDX_debug_info, 4))
Packit 032894
	goto fail;
Packit 032894
Packit 032894
      /* Next up two bytes for address and segment size.  */
Packit 032894
      if (readp + 2 > readendp)
Packit 032894
	goto invalid;
Packit 032894
Packit 032894
      unsigned int address_size = *readp++;
Packit 032894
      if (unlikely (address_size != 4 && address_size != 8))
Packit 032894
	goto invalid;
Packit 032894
Packit 032894
      /* We don't actually support segment selectors.  */
Packit 032894
      unsigned int segment_size = *readp++;
Packit 032894
      if (segment_size != 0)
Packit 032894
	goto invalid;
Packit 032894
Packit 032894
      /* Round the address to the next multiple of 2*address_size.  */
Packit 032894
      readp += ((2 * address_size - ((readp - hdrstart) % (2 * address_size)))
Packit 032894
		% (2 * address_size));
Packit 032894
Packit 032894
      while (1)
Packit 032894
	{
Packit 032894
	  Dwarf_Word range_address;
Packit 032894
	  Dwarf_Word range_length;
Packit 032894
Packit 032894
	  if (__libdw_read_address_inc (dbg, IDX_debug_aranges, &readp,
Packit 032894
					address_size, &range_address))
Packit 032894
	    goto fail;
Packit 032894
Packit 032894
	  if (readp + address_size > readendp)
Packit 032894
	    goto invalid;
Packit 032894
Packit 032894
	  if (address_size == 4)
Packit 032894
	    range_length = read_4ubyte_unaligned_inc (dbg, readp);
Packit 032894
	  else
Packit 032894
	    range_length = read_8ubyte_unaligned_inc (dbg, readp);
Packit 032894
Packit 032894
	  /* Two zero values mark the end.  */
Packit 032894
	  if (range_address == 0 && range_length == 0)
Packit 032894
	    break;
Packit 032894
Packit 032894
	  /* We don't use alloca for these temporary structures because
Packit 032894
	     the total number of them can be quite large.  */
Packit 032894
	  struct arangelist *new_arange = malloc (sizeof *new_arange);
Packit 032894
	  if (unlikely (new_arange == NULL))
Packit 032894
	    {
Packit 032894
	      __libdw_seterrno (DWARF_E_NOMEM);
Packit 032894
	      goto fail;
Packit 032894
	    }
Packit 032894
Packit 032894
	  new_arange->arange.addr = range_address;
Packit 032894
	  new_arange->arange.length = range_length;
Packit 032894
Packit 032894
	  /* We store the actual CU DIE offset, not the CU header offset.  */
Packit 032894
	  Dwarf_CU *cu = __libdw_findcu (dbg, offset, false);
Packit 032894
	  if (unlikely (cu == NULL))
Packit 032894
	    {
Packit 032894
	      /* We haven't gotten a chance to link in the new_arange
Packit 032894
		 into the arangelist, don't leak it.  */
Packit 032894
	      free (new_arange);
Packit 032894
	      goto fail;
Packit 032894
	    }
Packit 032894
	  new_arange->arange.offset = __libdw_first_die_off_from_cu (cu);
Packit 032894
Packit 032894
	  new_arange->next = arangelist;
Packit 032894
	  arangelist = new_arange;
Packit 032894
	  ++narangelist;
Packit 032894
Packit 032894
	  /* Sanity-check the data.  */
Packit 032894
	  if (unlikely (new_arange->arange.offset
Packit 032894
			>= dbg->sectiondata[IDX_debug_info]->d_size))
Packit 032894
	    goto invalid;
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  if (narangelist == 0)
Packit 032894
    {
Packit 032894
      assert (arangelist == NULL);
Packit 032894
      if (naranges != NULL)
Packit 032894
	*naranges = 0;
Packit 032894
      *aranges = NULL;
Packit 032894
      return 0;
Packit 032894
    }
Packit 032894
Packit 032894
  /* Allocate the array for the result.  */
Packit 032894
  void *buf = libdw_alloc (dbg, Dwarf_Aranges,
Packit 032894
			   sizeof (Dwarf_Aranges)
Packit 032894
			   + narangelist * sizeof (Dwarf_Arange), 1);
Packit 032894
Packit 032894
  /* First use the buffer for the pointers, and sort the entries.
Packit 032894
     We'll write the pointers in the end of the buffer, and then
Packit 032894
     copy into the buffer from the beginning so the overlap works.  */
Packit 032894
  assert (sizeof (Dwarf_Arange) >= sizeof (Dwarf_Arange *));
Packit 032894
  struct arangelist **sortaranges
Packit 032894
    = (buf + sizeof (Dwarf_Aranges)
Packit 032894
       + ((sizeof (Dwarf_Arange) - sizeof sortaranges[0]) * narangelist));
Packit 032894
Packit 032894
  /* The list is in LIFO order and usually they come in clumps with
Packit 032894
     ascending addresses.  So fill from the back to probably start with
Packit 032894
     runs already in order before we sort.  */
Packit 032894
  unsigned int i = narangelist;
Packit 032894
  while (i-- > 0)
Packit 032894
    {
Packit 032894
      sortaranges[i] = arangelist;
Packit 032894
      arangelist = arangelist->next;
Packit 032894
    }
Packit 032894
  assert (arangelist == NULL);
Packit 032894
Packit 032894
  /* Sort by ascending address.  */
Packit 032894
  qsort (sortaranges, narangelist, sizeof sortaranges[0], &compare_aranges);
Packit 032894
Packit 032894
  /* Now that they are sorted, put them in the final array.
Packit 032894
     The buffers overlap, so we've clobbered the early elements
Packit 032894
     of SORTARANGES by the time we're reading the later ones.  */
Packit 032894
  *aranges = buf;
Packit 032894
  (*aranges)->dbg = dbg;
Packit 032894
  (*aranges)->naranges = narangelist;
Packit 032894
  dbg->aranges = *aranges;
Packit 032894
  if (naranges != NULL)
Packit 032894
    *naranges = narangelist;
Packit 032894
  for (i = 0; i < narangelist; ++i)
Packit 032894
    {
Packit 032894
      struct arangelist *elt = sortaranges[i];
Packit 032894
      (*aranges)->info[i] = elt->arange;
Packit 032894
      free (elt);
Packit 032894
    }
Packit 032894
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
INTDEF(dwarf_getaranges)