Blame libdw/libdw_findcu.c

Packit 032894
/* Find CU for given offset.
Packit 032894
   Copyright (C) 2003-2010, 2014, 2016, 2017, 2018 Red Hat, Inc.
Packit 032894
   This file is part of elfutils.
Packit 032894
   Written by Ulrich Drepper <drepper@redhat.com>, 2003.
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 <assert.h>
Packit 032894
#include <search.h>
Packit 032894
#include "libdwP.h"
Packit 032894
Packit 032894
static int
Packit 032894
findcu_cb (const void *arg1, const void *arg2)
Packit 032894
{
Packit 032894
  struct Dwarf_CU *cu1 = (struct Dwarf_CU *) arg1;
Packit 032894
  struct Dwarf_CU *cu2 = (struct Dwarf_CU *) arg2;
Packit 032894
Packit 032894
  /* Find out which of the two arguments is the search value.  It has
Packit 032894
     end offset 0.  */
Packit 032894
  if (cu1->end == 0)
Packit 032894
    {
Packit 032894
      if (cu1->start < cu2->start)
Packit 032894
	return -1;
Packit 032894
      if (cu1->start >= cu2->end)
Packit 032894
	return 1;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      if (cu2->start < cu1->start)
Packit 032894
	return 1;
Packit 032894
      if (cu2->start >= cu1->end)
Packit 032894
	return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
int
Packit 032894
__libdw_finddbg_cb (const void *arg1, const void *arg2)
Packit 032894
{
Packit 032894
  Dwarf *dbg1 = (Dwarf *) arg1;
Packit 032894
  Dwarf *dbg2 = (Dwarf *) arg2;
Packit 032894
Packit 032894
  Elf_Data *dbg1_data = dbg1->sectiondata[IDX_debug_info];
Packit 032894
  unsigned char *dbg1_start = dbg1_data->d_buf;
Packit 032894
  size_t dbg1_size = dbg1_data->d_size;
Packit 032894
Packit 032894
  Elf_Data *dbg2_data = dbg2->sectiondata[IDX_debug_info];
Packit 032894
  unsigned char *dbg2_start = dbg2_data->d_buf;
Packit 032894
  size_t dbg2_size = dbg2_data->d_size;
Packit 032894
Packit 032894
  /* Find out which of the two arguments is the search value.  It has
Packit 032894
     a size of 0.  */
Packit 032894
  if (dbg1_size == 0)
Packit 032894
    {
Packit 032894
      if (dbg1_start < dbg2_start)
Packit 032894
	return -1;
Packit 032894
      if (dbg1_start >= dbg2_start + dbg2_size)
Packit 032894
	return 1;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    {
Packit 032894
      if (dbg2_start < dbg1_start)
Packit 032894
	return 1;
Packit 032894
      if (dbg2_start >= dbg1_start + dbg1_size)
Packit 032894
	return -1;
Packit 032894
    }
Packit 032894
Packit 032894
  return 0;
Packit 032894
}
Packit 032894
Packit 032894
struct Dwarf_CU *
Packit 032894
internal_function
Packit 032894
__libdw_intern_next_unit (Dwarf *dbg, bool debug_types)
Packit 032894
{
Packit 032894
  Dwarf_Off *const offsetp
Packit 032894
    = debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
Packit 032894
  void **tree = debug_types ? &dbg->tu_tree : &dbg->cu_tree;
Packit 032894
Packit 032894
  Dwarf_Off oldoff = *offsetp;
Packit 032894
  uint16_t version;
Packit 032894
  uint8_t unit_type;
Packit 032894
  uint8_t address_size;
Packit 032894
  uint8_t offset_size;
Packit 032894
  Dwarf_Off abbrev_offset;
Packit 032894
  uint64_t unit_id8;
Packit 032894
  Dwarf_Off subdie_offset;
Packit 032894
Packit 032894
  if (__libdw_next_unit (dbg, debug_types, oldoff, offsetp, NULL,
Packit 032894
			 &version, &unit_type, &abbrev_offset,
Packit 032894
			 &address_size, &offset_size,
Packit 032894
			 &unit_id8, &subdie_offset) != 0)
Packit 032894
    /* No more entries.  */
Packit 032894
    return NULL;
Packit 032894
Packit 032894
  /* We only know how to handle the DWARF version 2 through 5 formats.
Packit 032894
     For v4 debug types we only handle version 4.  */
Packit 032894
  if (unlikely (version < 2) || unlikely (version > 5)
Packit 032894
      || (debug_types && unlikely (version != 4)))
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_VERSION);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  /* We only handle 32 or 64 bit (4 or 8 byte) addresses and offsets.
Packit 032894
     Just assume we are dealing with 64bit in case the size is "unknown".
Packit 032894
     Too much code assumes if it isn't 4 then it is 8 (or the other way
Packit 032894
     around).  */
Packit 032894
  if (unlikely (address_size != 4 && address_size != 8))
Packit 032894
    address_size = 8;
Packit 032894
  if (unlikely (offset_size != 4 && offset_size != 8))
Packit 032894
    offset_size = 8;
Packit 032894
Packit 032894
  /* Invalid or truncated debug section data?  */
Packit 032894
  size_t sec_idx = debug_types ? IDX_debug_types : IDX_debug_info;
Packit 032894
  Elf_Data *data = dbg->sectiondata[sec_idx];
Packit 032894
  if (unlikely (*offsetp > data->d_size))
Packit 032894
    *offsetp = data->d_size;
Packit 032894
Packit 032894
  /* Create an entry for this CU.  */
Packit 032894
  struct Dwarf_CU *newp = libdw_typed_alloc (dbg, struct Dwarf_CU);
Packit 032894
Packit 032894
  newp->dbg = dbg;
Packit 032894
  newp->sec_idx = sec_idx;
Packit 032894
  newp->start = oldoff;
Packit 032894
  newp->end = *offsetp;
Packit 032894
  newp->address_size = address_size;
Packit 032894
  newp->offset_size = offset_size;
Packit 032894
  newp->version = version;
Packit 032894
  newp->unit_id8 = unit_id8;
Packit 032894
  newp->subdie_offset = subdie_offset;
Packit 032894
  Dwarf_Abbrev_Hash_init (&newp->abbrev_hash, 41);
Packit 032894
  newp->orig_abbrev_offset = newp->last_abbrev_offset = abbrev_offset;
Packit 032894
  newp->files = NULL;
Packit 032894
  newp->lines = NULL;
Packit 032894
  newp->locs = NULL;
Packit 032894
  newp->split = (Dwarf_CU *) -1;
Packit 032894
  newp->base_address = (Dwarf_Addr) -1;
Packit 032894
  newp->addr_base = (Dwarf_Off) -1;
Packit 032894
  newp->str_off_base = (Dwarf_Off) -1;
Packit 032894
  newp->ranges_base = (Dwarf_Off) -1;
Packit 032894
  newp->locs_base = (Dwarf_Off) -1;
Packit 032894
Packit 032894
  newp->startp = data->d_buf + newp->start;
Packit 032894
  newp->endp = data->d_buf + newp->end;
Packit 032894
Packit 032894
  /* v4 debug type units have version == 4 and unit_type == DW_UT_type.  */
Packit 032894
  if (debug_types)
Packit 032894
    newp->unit_type = DW_UT_type;
Packit 032894
  else if (version < 5)
Packit 032894
    {
Packit 032894
      /* This is a reasonable guess (and needed to get the CUDIE).  */
Packit 032894
      newp->unit_type = DW_UT_compile;
Packit 032894
Packit 032894
      /* But set it correctly from the actual CUDIE tag.  */
Packit 032894
      Dwarf_Die cudie = CUDIE (newp);
Packit 032894
      int tag = INTUSE(dwarf_tag) (&cudie);
Packit 032894
      if (tag == DW_TAG_compile_unit)
Packit 032894
	{
Packit 032894
	  Dwarf_Attribute dwo_id;
Packit 032894
	  if (INTUSE(dwarf_attr) (&cudie, DW_AT_GNU_dwo_id, &dwo_id) != NULL)
Packit 032894
	    {
Packit 032894
	      Dwarf_Word id8;
Packit 032894
	      if (INTUSE(dwarf_formudata) (&dwo_id, &id8) == 0)
Packit 032894
		{
Packit 032894
		  if (INTUSE(dwarf_haschildren) (&cudie) == 0
Packit 032894
		      && INTUSE(dwarf_hasattr) (&cudie,
Packit 032894
						DW_AT_GNU_dwo_name) == 1)
Packit 032894
		    newp->unit_type = DW_UT_skeleton;
Packit 032894
		  else
Packit 032894
		    newp->unit_type = DW_UT_split_compile;
Packit 032894
Packit 032894
		  newp->unit_id8 = id8;
Packit 032894
		}
Packit 032894
	    }
Packit 032894
	}
Packit 032894
      else if (tag == DW_TAG_partial_unit)
Packit 032894
	newp->unit_type = DW_UT_partial;
Packit 032894
      else if (tag == DW_TAG_type_unit)
Packit 032894
	newp->unit_type = DW_UT_type;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    newp->unit_type = unit_type;
Packit 032894
Packit 032894
  /* Store a reference to any type unit ids in the hash for quick lookup.  */
Packit 032894
  if (unit_type == DW_UT_type || unit_type == DW_UT_split_type)
Packit 032894
    Dwarf_Sig8_Hash_insert (&dbg->sig8_hash, unit_id8, newp);
Packit 032894
Packit 032894
  /* Add the new entry to the search tree.  */
Packit 032894
  if (tsearch (newp, tree, findcu_cb) == NULL)
Packit 032894
    {
Packit 032894
      /* Something went wrong.  Undo the operation.  */
Packit 032894
      *offsetp = oldoff;
Packit 032894
      __libdw_seterrno (DWARF_E_NOMEM);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  return newp;
Packit 032894
}
Packit 032894
Packit 032894
struct Dwarf_CU *
Packit 032894
internal_function
Packit 032894
__libdw_findcu (Dwarf *dbg, Dwarf_Off start, bool v4_debug_types)
Packit 032894
{
Packit 032894
  void **tree = v4_debug_types ? &dbg->tu_tree : &dbg->cu_tree;
Packit 032894
  Dwarf_Off *next_offset
Packit 032894
    = v4_debug_types ? &dbg->next_tu_offset : &dbg->next_cu_offset;
Packit 032894
Packit 032894
  /* Maybe we already know that CU.  */
Packit 032894
  struct Dwarf_CU fake = { .start = start, .end = 0 };
Packit 032894
  struct Dwarf_CU **found = tfind (&fake, tree, findcu_cb);
Packit 032894
  if (found != NULL)
Packit 032894
    return *found;
Packit 032894
Packit 032894
  if (start < *next_offset)
Packit 032894
    {
Packit 032894
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
Packit 032894
      return NULL;
Packit 032894
    }
Packit 032894
Packit 032894
  /* No.  Then read more CUs.  */
Packit 032894
  while (1)
Packit 032894
    {
Packit 032894
      struct Dwarf_CU *newp = __libdw_intern_next_unit (dbg, v4_debug_types);
Packit 032894
      if (newp == NULL)
Packit 032894
	return NULL;
Packit 032894
Packit 032894
      /* Is this the one we are looking for?  */
Packit 032894
      if (start < *next_offset || start == newp->start)
Packit 032894
	return newp;
Packit 032894
    }
Packit 032894
  /* NOTREACHED */
Packit 032894
}
Packit 032894
Packit 032894
struct Dwarf_CU *
Packit 032894
internal_function
Packit 032894
__libdw_findcu_addr (Dwarf *dbg, void *addr)
Packit 032894
{
Packit 032894
  void **tree;
Packit 032894
  Dwarf_Off start;
Packit 032894
  if (addr >= dbg->sectiondata[IDX_debug_info]->d_buf
Packit 032894
      && addr < (dbg->sectiondata[IDX_debug_info]->d_buf
Packit 032894
		 + dbg->sectiondata[IDX_debug_info]->d_size))
Packit 032894
    {
Packit 032894
      tree = &dbg->cu_tree;
Packit 032894
      start = addr - dbg->sectiondata[IDX_debug_info]->d_buf;
Packit 032894
    }
Packit 032894
  else if (dbg->sectiondata[IDX_debug_types] != NULL
Packit 032894
	   && addr >= dbg->sectiondata[IDX_debug_types]->d_buf
Packit 032894
	   && addr < (dbg->sectiondata[IDX_debug_types]->d_buf
Packit 032894
		      + dbg->sectiondata[IDX_debug_types]->d_size))
Packit 032894
    {
Packit 032894
      tree = &dbg->tu_tree;
Packit 032894
      start = addr - dbg->sectiondata[IDX_debug_types]->d_buf;
Packit 032894
    }
Packit 032894
  else
Packit 032894
    return NULL;
Packit 032894
Packit 032894
  struct Dwarf_CU fake = { .start = start, .end = 0 };
Packit 032894
  struct Dwarf_CU **found = tfind (&fake, tree, findcu_cb);
Packit 032894
Packit 032894
  if (found != NULL)
Packit 032894
    return *found;
Packit 032894
Packit 032894
  return NULL;
Packit 032894
}
Packit 032894
Packit 032894
Dwarf *
Packit 032894
internal_function
Packit 032894
__libdw_find_split_dbg_addr (Dwarf *dbg, void *addr)
Packit 032894
{
Packit 032894
  /* XXX Assumes split DWARF only has CUs in main IDX_debug_info.  */
Packit 032894
  Elf_Data fake_data = { .d_buf = addr, .d_size = 0 };
Packit 032894
  Dwarf fake = { .sectiondata[IDX_debug_info] = &fake_data };
Packit 032894
  Dwarf **found = tfind (&fake, &dbg->split_tree, __libdw_finddbg_cb);
Packit 032894
Packit 032894
  if (found != NULL)
Packit 032894
    return *found;
Packit 032894
Packit 032894
  return NULL;
Packit 032894
}