Blame libdwfl/cu.c

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