Blame libdw/libdw_findcu.c

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