Blame libdwfl/cu.c

Packit 032894
/* Keeping track of DWARF compilation units in libdwfl.
Packit 032894
   Copyright (C) 2005-2010, 2015, 2016, 2017 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 "libdwflP.h"
Packit 032894
#include "../libdw/libdwP.h"
Packit 032894
#include "../libdw/memory-access.h"
Packit 032894
#include <search.h>
Packit 032894
Packit 032894
Packit 032894
static inline Dwarf_Arange *
Packit 032894
dwar (Dwfl_Module *mod, unsigned int idx)
Packit 032894
{
Packit 032894
  return &mod->dw->aranges->info[mod->aranges[idx].arange];
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static Dwfl_Error
Packit 032894
addrarange (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_arange **arange)
Packit 032894
{
Packit 032894
  if (mod->aranges == NULL)
Packit 032894
    {
Packit 032894
      struct dwfl_arange *aranges = NULL;
Packit 032894
      Dwarf_Aranges *dwaranges = NULL;
Packit 032894
      size_t naranges;
Packit 032894
      if (INTUSE(dwarf_getaranges) (mod->dw, &dwaranges, &naranges) != 0)
Packit 032894
	return DWFL_E_LIBDW;
Packit 032894
Packit 032894
      /* If the module has no aranges (when no code is included) we
Packit 032894
	 allocate nothing.  */
Packit 032894
      if (naranges != 0)
Packit 032894
	{
Packit 032894
	  aranges = malloc (naranges * sizeof *aranges);
Packit 032894
	  if (unlikely (aranges == NULL))
Packit 032894
	    return DWFL_E_NOMEM;
Packit 032894
Packit 032894
	  /* libdw has sorted its list by address, which is how we want it.
Packit 032894
	     But the sorted list is full of not-quite-contiguous runs pointing
Packit 032894
	     to the same CU.  We don't care about the little gaps inside the
Packit 032894
	     module, we'll consider them part of the surrounding CU anyway.
Packit 032894
	     Collect our own array with just one record for each run of ranges
Packit 032894
	     pointing to one CU.  */
Packit 032894
Packit 032894
	  naranges = 0;
Packit 032894
	  Dwarf_Off lastcu = 0;
Packit 032894
	  for (size_t i = 0; i < dwaranges->naranges; ++i)
Packit 032894
	    if (i == 0 || dwaranges->info[i].offset != lastcu)
Packit 032894
	      {
Packit 032894
		aranges[naranges].arange = i;
Packit 032894
		aranges[naranges].cu = NULL;
Packit 032894
		++naranges;
Packit 032894
		lastcu = dwaranges->info[i].offset;
Packit 032894
	      }
Packit 032894
	}
Packit 032894
Packit 032894
      /* Store the final array, which is probably much smaller than before.  */
Packit 032894
      mod->naranges = naranges;
Packit 032894
      if (naranges > 0)
Packit 032894
        mod->aranges = (realloc (aranges, naranges * sizeof aranges[0])
Packit 032894
			?: aranges);
Packit 032894
      else if (aranges != NULL)
Packit 032894
	free (aranges);
Packit 032894
      mod->lazycu += naranges;
Packit 032894
    }
Packit 032894
Packit 032894
  /* The address must be inside the module to begin with.  */
Packit 032894
  addr = dwfl_deadjust_dwarf_addr (mod, addr);
Packit 032894
Packit 032894
  /* The ranges are sorted by address, so we can use binary search.  */
Packit 032894
  size_t l = 0, u = mod->naranges;
Packit 032894
  while (l < u)
Packit 032894
    {
Packit 032894
      size_t idx = (l + u) / 2;
Packit 032894
      Dwarf_Addr start = dwar (mod, idx)->addr;
Packit 032894
      if (addr < start)
Packit 032894
	{
Packit 032894
	  u = idx;
Packit 032894
	  continue;
Packit 032894
	}
Packit 032894
      else if (addr > start)
Packit 032894
	{
Packit 032894
	  if (idx + 1 < mod->naranges)
Packit 032894
	    {
Packit 032894
	      if (addr >= dwar (mod, idx + 1)->addr)
Packit 032894
		{
Packit 032894
		  l = idx + 1;
Packit 032894
		  continue;
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	  else
Packit 032894
	    {
Packit 032894
	      /* It might be in the last range.  */
Packit 032894
	      const Dwarf_Arange *last
Packit 032894
		= &mod->dw->aranges->info[mod->dw->aranges->naranges - 1];
Packit 032894
	      if (addr > last->addr + last->length)
Packit 032894
		break;
Packit 032894
	    }
Packit 032894
	}
Packit 032894
Packit 032894
      *arange = &mod->aranges[idx];
Packit 032894
      return DWFL_E_NOERROR;
Packit 032894
    }
Packit 032894
Packit 032894
  return DWFL_E_ADDR_OUTOFRANGE;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
static void
Packit 032894
nofree (void *arg)
Packit 032894
{
Packit 032894
  struct dwfl_cu *cu = arg;
Packit 032894
  if (cu == (void *) -1l)
Packit 032894
    return;
Packit 032894
Packit 032894
  assert (cu->mod->lazycu == 0);
Packit 032894
}
Packit 032894
Packit 032894
/* One reason fewer to keep the lazy lookup table for CUs.  */
Packit 032894
static inline void
Packit 032894
less_lazy (Dwfl_Module *mod)
Packit 032894
{
Packit 032894
  if (--mod->lazycu > 0)
Packit 032894
    return;
Packit 032894
Packit 032894
  /* We know about all the CUs now, we don't need this table.  */
Packit 032894
  tdestroy (mod->lazy_cu_root, nofree);
Packit 032894
  mod->lazy_cu_root = NULL;
Packit 032894
}
Packit 032894
Packit 032894
static inline Dwarf_Off
Packit 032894
cudie_offset (const struct dwfl_cu *cu)
Packit 032894
{
Packit 032894
  return __libdw_first_die_off_from_cu (cu->die.cu);
Packit 032894
}
Packit 032894
Packit 032894
static int
Packit 032894
compare_cukey (const void *a, const void *b)
Packit 032894
{
Packit 032894
  Dwarf_Off a_off = cudie_offset (a);
Packit 032894
  Dwarf_Off b_off = cudie_offset (b);
Packit 032894
  return (a_off < b_off) ? -1 : ((a_off > b_off) ? 1 : 0);
Packit 032894
}
Packit 032894
Packit 032894
/* Intern the CU if necessary.  */
Packit 032894
static Dwfl_Error
Packit 032894
intern_cu (Dwfl_Module *mod, Dwarf_Off cuoff, struct dwfl_cu **result)
Packit 032894
{
Packit 032894
  if (unlikely (cuoff + 4 >= mod->dw->sectiondata[IDX_debug_info]->d_size))
Packit 032894
    {
Packit 032894
      if (likely (mod->lazycu == 1))
Packit 032894
	{
Packit 032894
	  /* This is the EOF marker.  Now we have interned all the CUs.
Packit 032894
	     One increment in MOD->lazycu counts not having hit EOF yet.  */
Packit 032894
	  *result = (void *) -1;
Packit 032894
	  less_lazy (mod);
Packit 032894
	  return DWFL_E_NOERROR;
Packit 032894
	}
Packit 032894
      else
Packit 032894
	{
Packit 032894
	  /* Unexpected EOF, most likely a bogus aranges.  */
Packit 032894
	  return (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
Packit 032894
	}
Packit 032894
    }
Packit 032894
Packit 032894
  /* Make sure the cuoff points to a real DIE.  */
Packit 032894
  Dwarf_Die cudie;
Packit 032894
  Dwarf_Die *die = INTUSE(dwarf_offdie) (mod->dw, cuoff, &cudie);
Packit 032894
  if (die == NULL)
Packit 032894
    return DWFL_E_LIBDW;
Packit 032894
Packit 032894
  struct dwfl_cu key;
Packit 032894
  key.die.cu = die->cu;
Packit 032894
  struct dwfl_cu **found = tsearch (&key, &mod->lazy_cu_root, &compare_cukey);
Packit 032894
  if (unlikely (found == NULL))
Packit 032894
    return DWFL_E_NOMEM;
Packit 032894
Packit 032894
  if (*found == &key || *found == NULL)
Packit 032894
    {
Packit 032894
      /* This is a new entry, meaning we haven't looked at this CU.  */
Packit 032894
Packit 032894
      *found = NULL;
Packit 032894
Packit 032894
      struct dwfl_cu *cu = malloc (sizeof *cu);
Packit 032894
      if (unlikely (cu == NULL))
Packit 032894
	return DWFL_E_NOMEM;
Packit 032894
Packit 032894
      cu->mod = mod;
Packit 032894
      cu->next = NULL;
Packit 032894
      cu->lines = NULL;
Packit 032894
      cu->die = cudie;
Packit 032894
Packit 032894
      struct dwfl_cu **newvec = realloc (mod->cu, ((mod->ncu + 1)
Packit 032894
						   * sizeof (mod->cu[0])));
Packit 032894
      if (newvec == NULL)
Packit 032894
	{
Packit 032894
	  free (cu);
Packit 032894
	  return DWFL_E_NOMEM;
Packit 032894
	}
Packit 032894
      mod->cu = newvec;
Packit 032894
Packit 032894
      mod->cu[mod->ncu++] = cu;
Packit 032894
      if (cu->die.cu->start == 0)
Packit 032894
	mod->first_cu = cu;
Packit 032894
Packit 032894
      *found = cu;
Packit 032894
    }
Packit 032894
Packit 032894
  *result = *found;
Packit 032894
  return DWFL_E_NOERROR;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Traverse all the CUs in the module.  */
Packit 032894
Packit 032894
Dwfl_Error
Packit 032894
internal_function
Packit 032894
__libdwfl_nextcu (Dwfl_Module *mod, struct dwfl_cu *lastcu,
Packit 032894
		  struct dwfl_cu **cu)
Packit 032894
{
Packit 032894
  Dwarf_Off cuoff;
Packit 032894
  struct dwfl_cu **nextp;
Packit 032894
Packit 032894
  if (lastcu == NULL)
Packit 032894
    {
Packit 032894
      /* Start the traversal.  */
Packit 032894
      cuoff = 0;
Packit 032894
      nextp = &mod->first_cu;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      /* Continue following LASTCU.  */
Packit 032894
      cuoff = lastcu->die.cu->end;
Packit 032894
      nextp = &lastcu->next;
Packit 032894
    }
Packit 032894
Packit 032894
  if (*nextp == NULL)
Packit 032894
    {
Packit 032894
      size_t cuhdrsz;
Packit 032894
      Dwarf_Off nextoff;
Packit 032894
      int end = INTUSE(dwarf_nextcu) (mod->dw, cuoff, &nextoff, &cuhdrsz,
Packit 032894
				      NULL, NULL, NULL);
Packit 032894
      if (end < 0)
Packit 032894
	return DWFL_E_LIBDW;
Packit 032894
      if (end > 0)
Packit 032894
	{
Packit 032894
	  *cu = NULL;
Packit 032894
	  return DWFL_E_NOERROR;
Packit 032894
	}
Packit 032894
Packit 032894
      Dwfl_Error result = intern_cu (mod, cuoff + cuhdrsz, nextp);
Packit 032894
      if (result != DWFL_E_NOERROR)
Packit 032894
	return result;
Packit 032894
Packit 032894
      if (*nextp != (void *) -1
Packit 032894
	  && (*nextp)->next == NULL && nextoff == (Dwarf_Off) -1l)
Packit 032894
	(*nextp)->next = (void *) -1l;
Packit 032894
    }
Packit 032894
Packit 032894
  *cu = *nextp == (void *) -1l ? NULL : *nextp;
Packit 032894
  return DWFL_E_NOERROR;
Packit 032894
}
Packit 032894
Packit 032894
Packit 032894
/* Intern the CU arange points to, if necessary.  */
Packit 032894
Packit 032894
static Dwfl_Error
Packit 032894
arangecu (Dwfl_Module *mod, struct dwfl_arange *arange, struct dwfl_cu **cu)
Packit 032894
{
Packit 032894
  if (arange->cu == NULL)
Packit 032894
    {
Packit 032894
      const Dwarf_Arange *dwarange = &mod->dw->aranges->info[arange->arange];
Packit 032894
      Dwfl_Error result = intern_cu (mod, dwarange->offset, &arange->cu);
Packit 032894
      if (result != DWFL_E_NOERROR)
Packit 032894
	return result;
Packit 032894
      assert (arange->cu != NULL && arange->cu != (void *) -1l);
Packit 032894
      less_lazy (mod);		/* Each arange with null ->cu counts once.  */
Packit 032894
    }
Packit 032894
Packit 032894
  *cu = arange->cu;
Packit 032894
  return DWFL_E_NOERROR;
Packit 032894
}
Packit 032894
Packit 032894
Dwfl_Error
Packit 032894
internal_function
Packit 032894
__libdwfl_addrcu (Dwfl_Module *mod, Dwarf_Addr addr, struct dwfl_cu **cu)
Packit 032894
{
Packit 032894
  struct dwfl_arange *arange;
Packit 032894
  return addrarange (mod, addr, &arange) ?: arangecu (mod, arange, cu);
Packit 032894
}