示例#1
0
static Dwarf_CFI *
allocate_cfi (Elf *elf, GElf_Addr vaddr)
{
  Dwarf_CFI *cfi = calloc (1, sizeof *cfi);
  if (cfi == NULL)
    {
      __libdw_seterrno (DWARF_E_NOMEM);
      return NULL;
    }

  cfi->e_ident = (unsigned char *) elf_getident (elf, NULL);
  if (cfi->e_ident == NULL)
    {
      free (cfi);
      __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
      return NULL;
    }

  if ((BYTE_ORDER == LITTLE_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2MSB)
      || (BYTE_ORDER == BIG_ENDIAN && cfi->e_ident[EI_DATA] == ELFDATA2LSB))
    cfi->other_byte_order = true;

  cfi->frame_vaddr = vaddr;
  cfi->textrel = 0;		/* XXX ? */
  cfi->datarel = 0;		/* XXX ? */

  return cfi;
}
internal_function
__libdw_offdie (Dwarf *dbg, Dwarf_Off offset, Dwarf_Die *result,
		bool debug_types)
{
  if (dbg == NULL)
    return NULL;

  Elf_Data *const data = dbg->sectiondata[debug_types ? IDX_debug_types
					  : IDX_debug_info];
  if (offset >= data->d_size)
    {
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return NULL;
    }

  /* Clear the entire DIE structure.  This signals we have not yet
     determined any of the information.  */
  memset (result, '\0', sizeof (Dwarf_Die));

  result->addr = (char *) data->d_buf + offset;

  /* Get the CU.  */
  result->cu = __libdw_findcu (dbg, offset, debug_types);
  if (result->cu == NULL)
    {
      /* This should never happen.  The input file is malformed.  */
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      result = NULL;
    }

  return result;
}
示例#3
0
文件: fde.c 项目: Distrotech/elfutils
static struct dwarf_fde *
intern_fde (Dwarf_CFI *cache, const Dwarf_FDE *entry)
{
  /* Look up the new entry's CIE.  */
  struct dwarf_cie *cie = __libdw_find_cie (cache, entry->CIE_pointer);
  if (cie == NULL)
    return (void *) -1l;

  struct dwarf_fde *fde = malloc (sizeof (struct dwarf_fde));
  if (fde == NULL)
    {
      __libdw_seterrno (DWARF_E_NOMEM);
      return NULL;
    }

  fde->instructions = entry->start;
  fde->instructions_end = entry->end;
  if (unlikely (read_encoded_value (cache, cie->fde_encoding,
				    &fde->instructions, &fde->start))
      || unlikely (read_encoded_value (cache, cie->fde_encoding & 0x0f,
				       &fde->instructions, &fde->end)))
    {
      free (fde);
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return NULL;
    }
  fde->end += fde->start;

  fde->cie = cie;

  if (cie->sized_augmentation_data)
    {
      /* The CIE augmentation says the FDE has a DW_FORM_block
	 before its actual instruction stream.  */
      Dwarf_Word len;
      get_uleb128 (len, fde->instructions, fde->instructions_end);
      if ((Dwarf_Word) (fde->instructions_end - fde->instructions) < len)
	{
	  free (fde);
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
	  return NULL;
	}
      fde->instructions += len;
    }
  else
    /* We had to understand all of the CIE augmentation string.
       We've recorded the number of data bytes in FDEs.  */
    fde->instructions += cie->fde_augmentation_data_size;

  /* Add the new entry to the search tree.  */
  if (tsearch (fde, &cache->fde_tree, &compare_fde) == NULL)
    {
      free (fde);
      __libdw_seterrno (DWARF_E_NOMEM);
      return NULL;
    }

  return fde;
}
Dwarf *
dwarf_begin (int fd, Dwarf_Cmd cmd)
{
  Elf *elf;
  Elf_Cmd elfcmd;
  Dwarf *result = NULL;

  switch (cmd)
    {
    case DWARF_C_READ:
      elfcmd = ELF_C_READ_MMAP;
      break;
    case DWARF_C_WRITE:
      elfcmd = ELF_C_WRITE;
      break;
    case DWARF_C_RDWR:
      elfcmd = ELF_C_RDWR;
      break;
    default:
      /* No valid mode.  */
      __libdw_seterrno (DWARF_E_INVALID_CMD);
      return NULL;
    }

  /* We have to call `elf_version' here since the user might have not
     done it or initialized libelf with a different version.  This
     would break libdwarf since we are using the ELF data structures
     in a certain way.  */
  elf_version (EV_CURRENT);

  /* Get an ELF descriptor.  */
  elf = elf_begin (fd, elfcmd, NULL);
  if (elf == NULL)
    {
      /* Test why the `elf_begin" call failed.  */
      struct stat64 st;

      if (fstat64 (fd, &st) == 0 && ! S_ISREG (st.st_mode))
	__libdw_seterrno (DWARF_E_NO_REGFILE);
      else if (errno == EBADF)
	__libdw_seterrno (DWARF_E_INVALID_FILE);
      else
	__libdw_seterrno (DWARF_E_IO_ERROR);
    }
  else
    {
      /* Do the real work now that we have an ELF descriptor.  */
      result = INTUSE(dwarf_begin_elf) (elf, cmd, NULL);

      /* If this failed, free the resources.  */
      if (result == NULL)
	elf_end (elf);
      else
	result->free_elf = true;
    }

  return result;
}
示例#5
0
internal_function unsigned char *
__libdw_formptr (Dwarf_Attribute *attr, int sec_index,
		 int err_nodata, unsigned char **endpp,
		 Dwarf_Off *offsetp)
{
  if (attr == NULL)
    return NULL;

  const Elf_Data *d = attr->cu->dbg->sectiondata[sec_index];
  if (unlikely (d == NULL))
    {
      __libdw_seterrno (err_nodata);
      return NULL;
    }

  Dwarf_Word offset;
  if (attr->form == DW_FORM_sec_offset)
    {
      if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg,
			       cu_sec_idx (attr->cu), attr->valp,
			       attr->cu->offset_size, &offset, sec_index, 0))
	return NULL;
    }
  else if (attr->cu->version > 3)
    goto invalid;
  else
    switch (attr->form)
      {
      case DW_FORM_data4:
      case DW_FORM_data8:
	if (__libdw_read_offset (attr->cu->dbg, attr->cu->dbg,
				 cu_sec_idx (attr->cu),
				 attr->valp,
				 attr->form == DW_FORM_data4 ? 4 : 8,
				 &offset, sec_index, 0))
	  return NULL;
	break;

      default:
	if (INTUSE(dwarf_formudata) (attr, &offset))
	  return NULL;
      };

  unsigned char *readp = d->d_buf + offset;
  unsigned char *endp = d->d_buf + d->d_size;
  if (unlikely (readp >= endp))
    {
    invalid:
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return NULL;
    }

  if (endpp != NULL)
    *endpp = endp;
  if (offsetp != NULL)
    *offsetp = offset;
  return readp;
}
示例#6
0
int
dwarf_formblock (Dwarf_Attribute *attr, Dwarf_Block *return_block)
{
  if (attr == NULL)
    return -1;

  const unsigned char *datap = attr->valp;
  const unsigned char *endp = attr->cu->endp;

  switch (attr->form)
    {
    case DW_FORM_block1:
      if (unlikely (endp - datap < 1))
	goto invalid;
      return_block->length = *(uint8_t *) attr->valp;
      return_block->data = attr->valp + 1;
      break;

    case DW_FORM_block2:
      if (unlikely (endp - datap < 2))
	goto invalid;
      return_block->length = read_2ubyte_unaligned (attr->cu->dbg, attr->valp);
      return_block->data = attr->valp + 2;
      break;

    case DW_FORM_block4:
      if (unlikely (endp - datap < 4))
	goto invalid;
      return_block->length = read_4ubyte_unaligned (attr->cu->dbg, attr->valp);
      return_block->data = attr->valp + 4;
      break;

    case DW_FORM_block:
    case DW_FORM_exprloc:
      if (unlikely (endp - datap < 1))
	goto invalid;
      get_uleb128 (return_block->length, datap, endp);
      return_block->data = (unsigned char *) datap;
      break;

    default:
      __libdw_seterrno (DWARF_E_NO_BLOCK);
      return -1;
    }

  if (unlikely (return_block->length > (size_t) (endp - return_block->data)))
    {
      /* Block does not fit.  */
    invalid:
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return -1;
    }

  return 0;
}
    internal_function
__libdw_fde_by_offset (Dwarf_CFI *cache, Dwarf_Off offset)
{
    Dwarf_CFI_Entry entry;
    Dwarf_Off next_offset;
    int result = INTUSE(dwarf_next_cfi) (cache->e_ident,
                                         &cache->data->d, CFI_IS_EH (cache),
                                         offset, &next_offset, &entry);
    if (result != 0)
    {
        if (result > 0)
invalid:
            __libdw_seterrno (DWARF_E_INVALID_DWARF);
        return NULL;
    }

    if (unlikely (dwarf_cfi_cie_p (&entry)))
        goto invalid;

    /* We have a new FDE to consider.  */
    struct dwarf_fde *fde = intern_fde (cache, &entry.fde);
    if (fde == (void *) -1l || fde == NULL)
        return NULL;

    /* If this happened to be what we would have read next, notice it.  */
    if (cache->next_offset == offset)
        cache->next_offset = next_offset;

    return fde;
}
示例#8
0
static Dwarf *
scngrp_read (Dwarf *result, Elf *elf, GElf_Ehdr *ehdr, Dwarf_Cmd cmd,
	     Elf_Scn *scngrp)
{
  /* SCNGRP is the section descriptor for a section group which might
     contain debug sections.  */
  Elf_Data *data = elf_getdata (scngrp, NULL);
  if (data == NULL)
    {
      /* We cannot read the section content.  Fail!  */
      free (result);
      return NULL;
    }

  /* The content of the section is a number of 32-bit words which
     represent section indices.  The first word is a flag word.  */
  Elf32_Word *scnidx = (Elf32_Word *) data->d_buf;
  size_t cnt;
  for (cnt = 1; cnt * sizeof (Elf32_Word) <= data->d_size; ++cnt)
    {
      Elf_Scn *scn = elf_getscn (elf, scnidx[cnt]);
      if (scn == NULL)
	{
	  /* A section group refers to a non-existing section.  Should
	     never happen.  */
	  __libdw_seterrno (DWARF_E_INVALID_ELF);
	  free (result);
	  return NULL;
	}

      check_section (result, ehdr, scn, true);
    }

  return valid_p (result);
}
示例#9
0
static bool
attr_ok (Dwarf_Attribute *attr)
{
  if (attr == NULL)
    return false;

  /* Must be one of the attributes listed below.  */
  switch (attr->code)
    {
    case DW_AT_location:
    case DW_AT_data_member_location:
    case DW_AT_vtable_elem_location:
    case DW_AT_string_length:
    case DW_AT_use_location:
    case DW_AT_frame_base:
    case DW_AT_return_addr:
    case DW_AT_static_link:
    case DW_AT_segment:
    case DW_AT_GNU_call_site_value:
    case DW_AT_GNU_call_site_data_value:
    case DW_AT_GNU_call_site_target:
    case DW_AT_GNU_call_site_target_clobbered:
      break;

    default:
      __libdw_seterrno (DWARF_E_NO_LOCLIST);
      return false;
    }

  return true;
}
int
dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts)
{
  int nbkpts = 0;
  *bkpts = NULL;

  /* Fetch the CU's line records to look for this DIE's addresses.  */
  Dwarf_Die cudie = CUDIE (die->cu);
  Dwarf_Lines *lines;
  size_t nlines;
  if (INTUSE(dwarf_getsrclines) (&cudie, &lines, &nlines) < 0)
    {
      int error = INTUSE (dwarf_errno) ();
      if (error == 0)		/* CU has no DW_AT_stmt_list.  */
	return entrypc_bkpt (die, bkpts, &nbkpts);
      __libdw_seterrno (error);
      return -1;
    }

  /* Search each contiguous address range for DWARF prologue_end markers.  */

  Dwarf_Addr base;
  Dwarf_Addr begin;
  Dwarf_Addr end;
  ptrdiff_t offset = INTUSE(dwarf_ranges) (die, 0, &base, &begin, &end);
  if (offset < 0)
    return -1;

  /* Most often there is a single contiguous PC range for the DIE.  */
  if (offset == 1)
    return search_range (begin, end, true, true, lines, nlines, bkpts, &nbkpts)
        ?: entrypc_bkpt (die, bkpts, &nbkpts);

  Dwarf_Addr lowpc = (Dwarf_Addr) -1l;
  Dwarf_Addr highpc = (Dwarf_Addr) -1l;
  while (offset > 0)
    {
      /* We have an address range entry.  */
      if (search_range (begin, end, true, false,
                        lines, nlines, bkpts, &nbkpts) < 0)
	return -1;

      if (begin < lowpc)
	{
	  lowpc = begin;
	  highpc = end;
	}

      offset = INTUSE(dwarf_ranges) (die, offset, &base, &begin, &end);
    }

  /* If we didn't find any proper DWARF markers, then look in the
     lowest-addressed range for an ad hoc marker.  Failing that,
     fall back to just using the entrypc value.  */
  return (nbkpts
	  ?: (lowpc == (Dwarf_Addr) -1l ? 0
	      : search_range (lowpc, highpc, false, true,
	                      lines, nlines, bkpts, &nbkpts))
	  ?: entrypc_bkpt (die, bkpts, &nbkpts));
}
/* Preorder visitor: prune the traversal if this DIE does not contain PC.  */
static int
pc_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
{
  struct args *a = arg;

  if (a->scopes != NULL)
    die->prune = true;
  else
    {
      /* dwarf_haspc returns an error if there are no appropriate attributes.
	 But we use it indiscriminantly instead of presuming which tags can
	 have PC attributes.  So when it fails for that reason, treat it just
	 as a nonmatching return.  */
      int result = INTUSE(dwarf_haspc) (&die->die, a->pc);
      if (result < 0)
	{
	  int error = INTUSE(dwarf_errno) ();
	  if (error != DWARF_E_NOERROR && error != DWARF_E_NO_DEBUG_RANGES)
	    {
	      __libdw_seterrno (error);
	      return -1;
	    }
	  result = 0;
	}
      if (result == 0)
    	die->prune = true;

      if (!die->prune
	  && INTUSE (dwarf_tag) (&die->die) == DW_TAG_inlined_subroutine)
	a->inlined = depth;
    }

  return 0;
}
/* Preorder visitor for second partial traversal after finding a
   concrete inlined instance.  */
static int
origin_match (unsigned int depth, struct Dwarf_Die_Chain *die, void *arg)
{
  struct args *a = arg;

  if (die->die.addr != a->inlined_origin.addr)
    return 0;

  /* We have a winner!  This is the abstract definition of the inline
     function of which A->scopes[A->nscopes - 1] is a concrete instance.
  */

  unsigned int nscopes = a->nscopes + depth;
  Dwarf_Die *scopes = realloc (a->scopes, nscopes * sizeof scopes[0]);
  if (scopes == NULL)
    {
      free (a->scopes);
      __libdw_seterrno (DWARF_E_NOMEM);
      return -1;
    }

  a->scopes = scopes;
  do
    {
      die = die->parent;
      scopes[a->nscopes++] = die->die;
    }
  while (a->nscopes < nscopes);
  assert (die->parent == NULL);
  return a->nscopes;
}
示例#13
0
static ptrdiff_t
getlocations_addr (Dwarf_Attribute *attr, ptrdiff_t offset,
		   Dwarf_Addr *basep, Dwarf_Addr *startp, Dwarf_Addr *endp,
		   Dwarf_Addr address, const Elf_Data *locs, Dwarf_Op **expr,
		   size_t *exprlen)
{
  unsigned char *readp = locs->d_buf + offset;
  unsigned char *readendp = locs->d_buf + locs->d_size;

 next:
  if (readendp - readp < attr->cu->address_size * 2)
    {
    invalid:
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return -1;
    }

  Dwarf_Addr begin;
  Dwarf_Addr end;

  switch (__libdw_read_begin_end_pair_inc (attr->cu->dbg, IDX_debug_loc,
					   &readp, attr->cu->address_size,
					   &begin, &end, basep))
    {
    case 0: /* got location range. */
      break;
    case 1: /* base address setup. */
      goto next;
    case 2: /* end of loclist */
      return 0;
    default: /* error */
      return -1;
    }

  if (readendp - readp < 2)
    goto invalid;

  /* We have a location expression.  */
  Dwarf_Block block;
  block.length = read_2ubyte_unaligned_inc (attr->cu->dbg, readp);
  block.data = readp;
  if (readendp - readp < (ptrdiff_t) block.length)
    goto invalid;
  readp += block.length;

  *startp = *basep + begin;
  *endp = *basep + end;

  /* If address is minus one we want them all, otherwise only matching.  */
  if (address != (Dwarf_Word) -1 && (address < *startp || address >= *endp))
    goto next;

  if (getlocation (attr->cu, &block, expr, exprlen, IDX_debug_loc) != 0)
    return -1;

  return readp - (unsigned char *) locs->d_buf;
}
示例#14
0
static void
check_section (Dwarf *result, GElf_Ehdr *ehdr, Elf_Scn *scn, bool inscngrp)
{
  GElf_Shdr shdr_mem;
  GElf_Shdr *shdr;

  /* Get the section header data.  */
  shdr = gelf_getshdr (scn, &shdr_mem);
  if (shdr == NULL)
    /* This should never happen.  If it does something is
       wrong in the libelf library.  */
    abort ();


  /* Make sure the section is part of a section group only iff we
     really need it.  If we are looking for the global (= non-section
     group debug info) we have to ignore all the info in section
     groups.  If we are looking into a section group we cannot look at
     a section which isn't part of the section group.  */
  if (! inscngrp && (shdr->sh_flags & SHF_GROUP) != 0)
    /* Ignore the section.  */
    return;


  /* We recognize the DWARF section by their names.  This is not very
     safe and stable but the best we can do.  */
  const char *scnname = elf_strptr (result->elf, ehdr->e_shstrndx,
				    shdr->sh_name);
  if (scnname == NULL)
    {
      /* The section name must be valid.  Otherwise is the ELF file
	 invalid.  */
      __libdw_seterrno (DWARF_E_INVALID_ELF);
      free (result);
      return;
    }


  /* Recognize the various sections.  Most names start with .debug_.  */
  size_t cnt;
  for (cnt = 0; cnt < ndwarf_scnnames; ++cnt)
    if (strcmp (scnname, dwarf_scnnames[cnt]) == 0)
      {
	/* Found it.  Remember where the data is.  */
	if (unlikely (result->sectiondata[cnt] != NULL))
	  /* A section appears twice.  That's bad.  We ignore the section.  */
	  break;

	/* Get the section data.  */
	Elf_Data *data = elf_getdata (scn, NULL);
	if (data != NULL && data->d_size != 0)
	  /* Yep, there is actually data available.  */
	  result->sectiondata[cnt] = data;

	break;
      }
}
示例#15
0
static Dwarf_CFI *
getcfi_scn_eh_frame (Elf *elf, const GElf_Ehdr *ehdr,
		     Elf_Scn *scn, GElf_Shdr *shdr,
		     Elf_Scn *hdr_scn, GElf_Addr hdr_vaddr)
{
  Elf_Data *data = elf_rawdata (scn, NULL);
  if (data == NULL)
    {
      __libdw_seterrno (DWARF_E_INVALID_ELF);
      return NULL;
    }
  Dwarf_CFI *cfi = allocate_cfi (elf, shdr->sh_addr);
  if (cfi != NULL)
    {
      cfi->data = (Elf_Data_Scn *) data;
      if (hdr_scn != NULL)
	{
	  Elf_Data *hdr_data = elf_rawdata (hdr_scn, NULL);
	  if (hdr_data != NULL)
	    {
	      GElf_Addr eh_frame_vaddr;
	      cfi->search_table_vaddr = hdr_vaddr;
	      cfi->search_table
		= parse_eh_frame_hdr (hdr_data->d_buf, hdr_data->d_size,
				      hdr_vaddr, ehdr, &eh_frame_vaddr,
				      &cfi->search_table_entries,
				      &cfi->search_table_encoding);
	      if (cfi->search_table == (void *) -1l)
		{
		  free (cfi);
		  /* XXX might be read error or corrupt phdr */
		  __libdw_seterrno (DWARF_E_INVALID_CFI);
		  return NULL;
		}

	      /* Sanity check.  */
	      if (unlikely (eh_frame_vaddr != shdr->sh_addr))
		cfi->search_table = NULL;
	    }
	}
    }
  return cfi;
}
Dwarf_Line *
dwarf_onesrcline (Dwarf_Lines *lines, size_t idx)
{
  if (lines == NULL)
    return NULL;

  if (idx >= lines->nlines)
    {
      __libdw_seterrno (DWARF_E_INVALID_LINE_IDX);
      return NULL;
    }

  return &lines->info[idx];
}
/* Add one breakpoint location to the result vector.  */
static inline int
add_bkpt (Dwarf_Addr pc, Dwarf_Addr **bkpts, int *pnbkpts)
{
  Dwarf_Addr *newlist = realloc (*bkpts, ++(*pnbkpts) * sizeof newlist[0]);
  if (newlist == NULL)
    {
      free (*bkpts);
      *bkpts = NULL;
      __libdw_seterrno (DWARF_E_NOMEM);
      return -1;
    }
  newlist[*pnbkpts - 1] = pc;
  *bkpts = newlist;
  return *pnbkpts;
}
示例#18
0
/* Check whether all the necessary DWARF information is available.  */
static Dwarf *
valid_p (Dwarf *result)
{
  /* We looked at all the sections.  Now determine whether all the
     sections with debugging information we need are there.

     XXX Which sections are absolutely necessary?  Add tests if
     necessary.  For now we require only .debug_info.  Hopefully this
     is correct.  */
  if (unlikely (result->sectiondata[IDX_debug_info] == NULL))
    {
      __libdw_seterrno (DWARF_E_NO_DWARF);
      result = NULL;
    }

  return result;
}
示例#19
0
int
dwarf_hasattr (Dwarf_Die *die, unsigned int search_name)
{
  if (die == NULL)
    return 0;

  /* Find the abbreviation entry.  */
  Dwarf_Abbrev *abbrevp = __libdw_dieabbrev (die, NULL);
  if (unlikely (abbrevp == DWARF_END_ABBREV))
    {
    invalid_dwarf:
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return 0;
    }

  Dwarf *dbg = die->cu->dbg;

  /* Search the name attribute.  */
  unsigned char *const endp
    = ((unsigned char *) dbg->sectiondata[IDX_debug_abbrev]->d_buf
       + dbg->sectiondata[IDX_debug_abbrev]->d_size);

  const unsigned char *attrp = abbrevp->attrp;
  while (1)
    {
      /* Are we still in bounds?  This test needs to be refined.  */
      if (unlikely (attrp >= endp))
	goto invalid_dwarf;

      /* Get attribute name and form.  */
      unsigned int attr_name;
      get_uleb128 (attr_name, attrp, endp);
      unsigned int attr_form;
      if (unlikely (attrp >= endp))
	goto invalid_dwarf;
      get_uleb128 (attr_form, attrp, endp);

      /* We can stop if we found the attribute with value zero.  */
      if (attr_name == 0 || attr_form == 0)
	return 0;

      if (attr_name == search_name)
	return 1;
    }
}
示例#20
0
/* Search for the sections named ".eh_frame" and ".eh_frame_hdr".  */
static Dwarf_CFI *
getcfi_shdr (Elf *elf, const GElf_Ehdr *ehdr)
{
  size_t shstrndx;
  if (elf_getshdrstrndx (elf, &shstrndx) != 0)
    {
      __libdw_seterrno (DWARF_E_GETEHDR_ERROR);
      return NULL;
    }

  if (shstrndx != 0)
    {
      Elf_Scn *hdr_scn = NULL;
      GElf_Addr hdr_vaddr = 0;
      Elf_Scn *scn = NULL;
      while ((scn = elf_nextscn (elf, scn)) != NULL)
	{
	  GElf_Shdr shdr_mem;
	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
	  if (shdr == NULL)
	    continue;
	  const char *name = elf_strptr (elf, shstrndx, shdr->sh_name);
	  if (name == NULL)
	    continue;
	  if (!strcmp (name, ".eh_frame_hdr"))
	    {
	      hdr_scn = scn;
	      hdr_vaddr = shdr->sh_addr;
	    }
	  else if (!strcmp (name, ".eh_frame"))
	    {
	      if (shdr->sh_type == SHT_PROGBITS)
		return getcfi_scn_eh_frame (elf, ehdr, scn, shdr,
					    hdr_scn, hdr_vaddr);
	      else
		return NULL;
	    }
	}
    }

  return (void *) -1l;
}
示例#21
0
const char *
dwarf_linesrc (Dwarf_Line *line, Dwarf_Word *mtime, Dwarf_Word *length)
{
  if (line == NULL)
    return NULL;

  if (line->file >= line->files->nfiles)
    {
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return NULL;
    }

  if (mtime != NULL)
    *mtime = line->files->info[line->file].mtime;

  if (length != NULL)
    *length = line->files->info[line->file].length;

  return line->files->info[line->file].name;
}
示例#22
0
/* Read up begin/end pair and increment read pointer.
    - If it's normal range record, set up `*beginp' and `*endp' and return 0.
    - If it's base address selection record, set up `*basep' and return 1.
    - If it's end of rangelist, don't set anything and return 2
    - If an error occurs, don't set anything and return -1.  */
internal_function int
__libdw_read_begin_end_pair_inc (Dwarf *dbg, int sec_index,
				 unsigned char **addrp, int width,
				 Dwarf_Addr *beginp, Dwarf_Addr *endp,
				 Dwarf_Addr *basep)
{
  Dwarf_Addr escape = (width == 8 ? (Elf64_Addr) -1
		       : (Elf64_Addr) (Elf32_Addr) -1);
  Dwarf_Addr begin;
  Dwarf_Addr end;

  unsigned char *addr = *addrp;
  bool begin_relocated = READ_AND_RELOCATE (__libdw_relocate_address, begin);
  bool end_relocated = READ_AND_RELOCATE (__libdw_relocate_address, end);
  *addrp = addr;

  /* Unrelocated escape for begin means base address selection.  */
  if (begin == escape && !begin_relocated)
    {
      if (unlikely (end == escape))
	{
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
	  return -1;
	}

      if (basep != NULL)
	*basep = end;
      return 1;
    }

  /* Unrelocated pair of zeroes means end of range list.  */
  if (begin == 0 && end == 0 && !begin_relocated && !end_relocated)
    return 2;

  /* Don't check for begin_relocated == end_relocated.  Serve the data
     to the client even though it may be buggy.  */
  *beginp = begin;
  *endp = end;

  return 0;
}
示例#23
0
int internal_function
__libdw_attr_intval (Dwarf_Die *die, int *linep, int attval)
{
  Dwarf_Attribute attr_mem;
  Dwarf_Word line;

  int res = INTUSE(dwarf_formudata) (INTUSE(dwarf_attr_integrate)
				     (die, attval, &attr_mem),
				     &line);
  if (res == 0)
    {
      if (line > INT_MAX)
	{
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
	  res = -1;
	}
      else
	*linep = line;
    }

  return res;
}
/* Search a contiguous PC range for prologue-end markers.
   If DWARF, look for proper markers.
   Failing that, if ADHOC, look for the ad hoc convention.  */
static inline int
search_range (Dwarf_Addr low, Dwarf_Addr high,
	      bool dwarf, bool adhoc,
              Dwarf_Lines *lines, size_t nlines,
              Dwarf_Addr **bkpts, int *pnbkpts)
{
      size_t l = 0, u = nlines;
      while (l < u)
	{
	  size_t idx = (l + u) / 2;
	  if (lines->info[idx].addr < low)
	    l = idx + 1;
	  else if (lines->info[idx].addr > low)
	    u = idx;
	  else if (lines->info[idx].end_sequence)
	    l = idx + 1;
	  else
	    {
	      l = idx;
	      break;
	    }
	}
      if (l < u)
	{
	  if (dwarf)
	    for (size_t i = l; i < u && lines->info[i].addr < high; ++i)
	      if (lines->info[i].prologue_end
		  && add_bkpt (lines->info[i].addr, bkpts, pnbkpts) < 0)
		return -1;
	  if (adhoc && *pnbkpts == 0)
	    while (++l < nlines && lines->info[l].addr < high)
	      if (!lines->info[l].end_sequence)
		return add_bkpt (lines->info[l].addr, bkpts, pnbkpts);
	  return *pnbkpts;
	}
      __libdw_seterrno (DWARF_E_INVALID_DWARF);
      return -1;
}
示例#25
0
ptrdiff_t
dwarf_getlocations (Dwarf_Attribute *attr, ptrdiff_t offset, Dwarf_Addr *basep,
		    Dwarf_Addr *startp, Dwarf_Addr *endp, Dwarf_Op **expr,
		    size_t *exprlen)
{
  if (! attr_ok (attr))
    return -1;

  /* 1 is an invalid offset, meaning no more locations. */
  if (offset == 1)
    return 0;

  if (offset == 0)
    {
      /* If it has a block form, it's a single location expression.  */
      Dwarf_Block block;
      if (INTUSE(dwarf_formblock) (attr, &block) == 0)
	{
	  if (getlocation (attr->cu, &block, expr, exprlen,
			   cu_sec_idx (attr->cu)) != 0)
	    return -1;

	  /* This is the one and only location covering everything. */
	  *startp = 0;
	  *endp = -1;
	  return 1;
	}

      int error = INTUSE(dwarf_errno) ();
      if (unlikely (error != DWARF_E_NO_BLOCK))
	{
	  __libdw_seterrno (error);
	  return -1;
	}

      int result = check_constant_offset (attr, expr, exprlen);
      if (result != 1)
	{
	  if (result == 0)
	    {
	      /* This is the one and only location covering everything. */
	      *startp = 0;
	      *endp = -1;
	      return 1;
	    }
	  return result;
	}

      /* We must be looking at a true loclistptr, fetch the initial
	 base address and offset.  */
      if (initial_offset_base (attr, &offset, basep) != 0)
	return -1;
    }

  const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
  if (d == NULL)
    {
      __libdw_seterrno (DWARF_E_NO_LOCLIST);
      return -1;
    }

  return getlocations_addr (attr, offset, basep, startp, endp,
			    (Dwarf_Word) -1, d, expr, exprlen);
}
示例#26
0
int
dwarf_getlocation_addr (Dwarf_Attribute *attr, Dwarf_Addr address,
			Dwarf_Op **llbufs, size_t *listlens, size_t maxlocs)
{
  if (! attr_ok (attr))
    return -1;

  if (llbufs == NULL)
    maxlocs = SIZE_MAX;

  /* If it has a block form, it's a single location expression.  */
  Dwarf_Block block;
  if (INTUSE(dwarf_formblock) (attr, &block) == 0)
    {
      if (maxlocs == 0)
	return 0;
      if (llbufs != NULL &&
	  getlocation (attr->cu, &block, &llbufs[0], &listlens[0],
		       cu_sec_idx (attr->cu)) != 0)
	return -1;
      return listlens[0] == 0 ? 0 : 1;
    }

  int error = INTUSE(dwarf_errno) ();
  if (unlikely (error != DWARF_E_NO_BLOCK))
    {
      __libdw_seterrno (error);
      return -1;
    }

  int result = check_constant_offset (attr, &llbufs[0], &listlens[0]);
  if (result != 1)
    return result ?: 1;

  Dwarf_Addr base, start, end;
  Dwarf_Op *expr;
  size_t expr_len;
  ptrdiff_t off = 0;
  size_t got = 0;

  /* This is a true loclistptr, fetch the initial base address and offset.  */
  if (initial_offset_base (attr, &off, &base) != 0)
    return -1;

  const Elf_Data *d = attr->cu->dbg->sectiondata[IDX_debug_loc];
  if (d == NULL)
    {
      __libdw_seterrno (DWARF_E_NO_LOCLIST);
      return -1;
    }

  while (got < maxlocs
         && (off = getlocations_addr (attr, off, &base, &start, &end,
				   address, d, &expr, &expr_len)) > 0)
    {
      /* This one matches the address.  */
      if (llbufs != NULL)
	{
	  llbufs[got] = expr;
	  listlens[got] = expr_len;
	}
      ++got;
    }

  /* We might stop early, so off can be zero or positive on success.  */
  if (off < 0)
    return -1;

  return got;
}
示例#27
0
int
dwarf_getlocation_implicit_value (Dwarf_Attribute *attr, const Dwarf_Op *op,
				  Dwarf_Block *return_block)
{
  if (attr == NULL)
    return -1;

  struct loc_block_s fake = { .addr = (void *) op };
  struct loc_block_s **found = tfind (&fake, &attr->cu->locs, loc_compare);
  if (unlikely (found == NULL))
    {
      __libdw_seterrno (DWARF_E_NO_BLOCK);
      return -1;
    }

  return_block->length = (*found)->length;
  return_block->data = (*found)->data;
  return 0;
}

/* DW_AT_data_member_location can be a constant as well as a loclistptr.
   Only data[48] indicate a loclistptr.  */
static int
check_constant_offset (Dwarf_Attribute *attr,
		       Dwarf_Op **llbuf, size_t *listlen)
{
  if (attr->code != DW_AT_data_member_location)
    return 1;

  switch (attr->form)
    {
      /* Punt for any non-constant form.  */
    default:
      return 1;

    case DW_FORM_data1:
    case DW_FORM_data2:
    case DW_FORM_data4:
    case DW_FORM_data8:
    case DW_FORM_sdata:
    case DW_FORM_udata:
      break;
    }

  /* Check whether we already cached this location.  */
  struct loc_s fake = { .addr = attr->valp };
  struct loc_s **found = tfind (&fake, &attr->cu->locs, loc_compare);

  if (found == NULL)
    {
      Dwarf_Word offset;
      if (INTUSE(dwarf_formudata) (attr, &offset) != 0)
	return -1;

      Dwarf_Op *result = libdw_alloc (attr->cu->dbg,
				      Dwarf_Op, sizeof (Dwarf_Op), 1);

      result->atom = DW_OP_plus_uconst;
      result->number = offset;
      result->number2 = 0;
      result->offset = 0;

      /* Insert a record in the search tree so we can find it again later.  */
      struct loc_s *newp = libdw_alloc (attr->cu->dbg,
					struct loc_s, sizeof (struct loc_s),
					1);
      newp->addr = attr->valp;
      newp->loc = result;
      newp->nloc = 1;

      found = tsearch (newp, &attr->cu->locs, loc_compare);
    }

  assert ((*found)->nloc == 1);

  if (llbuf != NULL)
    {
      *llbuf = (*found)->loc;
      *listlen = 1;
    }

  return 0;
}

int
internal_function
__libdw_intern_expression (Dwarf *dbg, bool other_byte_order,
			   unsigned int address_size, unsigned int ref_size,
			   void **cache, const Dwarf_Block *block,
			   bool cfap, bool valuep,
			   Dwarf_Op **llbuf, size_t *listlen, int sec_index)
{
  /* Empty location expressions don't have any ops to intern.  */
  if (block->length == 0)
    {
      *listlen = 0;
      return 0;
    }

  /* Check whether we already looked at this list.  */
  struct loc_s fake = { .addr = block->data };
  struct loc_s **found = tfind (&fake, cache, loc_compare);
  if (found != NULL)
    {
      /* We already saw it.  */
      *llbuf = (*found)->loc;
      *listlen = (*found)->nloc;

      if (valuep)
	{
	  assert (*listlen > 1);
	  assert ((*llbuf)[*listlen - 1].atom == DW_OP_stack_value);
	}

      return 0;
    }

  const unsigned char *data = block->data;
  const unsigned char *const end_data = data + block->length;

  const struct { bool other_byte_order; } bo = { other_byte_order };

  struct loclist *loclist = NULL;
  unsigned int n = 0;

  /* Stack allocate at most this many locs.  */
#define MAX_STACK_LOCS 256
  struct loclist stack_locs[MAX_STACK_LOCS];
#define NEW_LOC() ({ struct loclist *ll;			\
		     ll = (likely (n < MAX_STACK_LOCS)		\
			   ? &stack_locs[n]			\
			   : malloc (sizeof (struct loclist)));	\
		     if (unlikely (ll == NULL))			\
		       goto nomem;				\
		     n++;					\
		     ll->next = loclist;			\
		     loclist = ll;				\
		     ll; })

  if (cfap)
    {
      /* Synthesize the operation to push the CFA before the expression.  */
      struct loclist *newloc = NEW_LOC ();
      newloc->atom = DW_OP_call_frame_cfa;
      newloc->number = 0;
      newloc->number2 = 0;
      newloc->offset = -1;
    }

  /* Decode the opcodes.  It is possible in some situations to have a
     block of size zero.  */
  while (data < end_data)
    {
      struct loclist *newloc;
      newloc = NEW_LOC ();
      newloc->number = 0;
      newloc->number2 = 0;
      newloc->offset = data - block->data;

      switch ((newloc->atom = *data++))
	{
	case DW_OP_addr:
	  /* Address, depends on address size of CU.  */
	  if (dbg == NULL)
	    {
	      // XXX relocation?
	      if (address_size == 4)
		{
		  if (unlikely (data + 4 > end_data))
		    goto invalid;
		  else
		    newloc->number = read_4ubyte_unaligned_inc (&bo, data);
		}
	      else
		{
		  if (unlikely (data + 8 > end_data))
		    goto invalid;
		  else
		    newloc->number = read_8ubyte_unaligned_inc (&bo, data);
		}
	    }
	  else if (__libdw_read_address_inc (dbg, sec_index, &data,
					     address_size, &newloc->number))
	    goto invalid;
	  break;

	case DW_OP_call_ref:
	  /* DW_FORM_ref_addr, depends on offset size of CU.  */
	  if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data,
						      ref_size,
						      &newloc->number,
						      IDX_debug_info, 0))
	    goto invalid;
	  break;

	case DW_OP_deref:
	case DW_OP_dup:
	case DW_OP_drop:
	case DW_OP_over:
	case DW_OP_swap:
	case DW_OP_rot:
	case DW_OP_xderef:
	case DW_OP_abs:
	case DW_OP_and:
	case DW_OP_div:
	case DW_OP_minus:
	case DW_OP_mod:
	case DW_OP_mul:
	case DW_OP_neg:
	case DW_OP_not:
	case DW_OP_or:
	case DW_OP_plus:
	case DW_OP_shl:
	case DW_OP_shr:
	case DW_OP_shra:
	case DW_OP_xor:
	case DW_OP_eq:
	case DW_OP_ge:
	case DW_OP_gt:
	case DW_OP_le:
	case DW_OP_lt:
	case DW_OP_ne:
	case DW_OP_lit0 ... DW_OP_lit31:
	case DW_OP_reg0 ... DW_OP_reg31:
	case DW_OP_nop:
	case DW_OP_push_object_address:
	case DW_OP_call_frame_cfa:
	case DW_OP_form_tls_address:
	case DW_OP_GNU_push_tls_address:
	case DW_OP_stack_value:
	  /* No operand.  */
	  break;

	case DW_OP_const1u:
	case DW_OP_pick:
	case DW_OP_deref_size:
	case DW_OP_xderef_size:
	  if (unlikely (data >= end_data))
	    {
	    invalid:
	      __libdw_seterrno (DWARF_E_INVALID_DWARF);
	    returnmem:
	      /* Free any dynamicly allocated loclists, if any.  */
	      while (n > MAX_STACK_LOCS)
		{
		  struct loclist *loc = loclist;
		  loclist = loc->next;
		  free (loc);
		  n--;
		}
	      return -1;
	    }

	  newloc->number = *data++;
	  break;

	case DW_OP_const1s:
	  if (unlikely (data >= end_data))
	    goto invalid;

	  newloc->number = *((int8_t *) data);
	  ++data;
	  break;

	case DW_OP_const2u:
	  if (unlikely (data + 2 > end_data))
	    goto invalid;

	  newloc->number = read_2ubyte_unaligned_inc (&bo, data);
	  break;

	case DW_OP_const2s:
	case DW_OP_skip:
	case DW_OP_bra:
	case DW_OP_call2:
	  if (unlikely (data + 2 > end_data))
	    goto invalid;

	  newloc->number = read_2sbyte_unaligned_inc (&bo, data);
	  break;

	case DW_OP_const4u:
	  if (unlikely (data + 4 > end_data))
	    goto invalid;

	  newloc->number = read_4ubyte_unaligned_inc (&bo, data);
	  break;

	case DW_OP_const4s:
	case DW_OP_call4:
	case DW_OP_GNU_parameter_ref:
	  if (unlikely (data + 4 > end_data))
	    goto invalid;

	  newloc->number = read_4sbyte_unaligned_inc (&bo, data);
	  break;

	case DW_OP_const8u:
	  if (unlikely (data + 8 > end_data))
	    goto invalid;

	  newloc->number = read_8ubyte_unaligned_inc (&bo, data);
	  break;

	case DW_OP_const8s:
	  if (unlikely (data + 8 > end_data))
	    goto invalid;

	  newloc->number = read_8sbyte_unaligned_inc (&bo, data);
	  break;

	case DW_OP_constu:
	case DW_OP_plus_uconst:
	case DW_OP_regx:
	case DW_OP_piece:
	case DW_OP_GNU_convert:
	case DW_OP_GNU_reinterpret:
	  get_uleb128 (newloc->number, data, end_data);
	  break;

	case DW_OP_consts:
	case DW_OP_breg0 ... DW_OP_breg31:
	case DW_OP_fbreg:
	  get_sleb128 (newloc->number, data, end_data);
	  break;

	case DW_OP_bregx:
	  get_uleb128 (newloc->number, data, end_data);
	  if (unlikely (data >= end_data))
	    goto invalid;
	  get_sleb128 (newloc->number2, data, end_data);
	  break;

	case DW_OP_bit_piece:
	case DW_OP_GNU_regval_type:
	  get_uleb128 (newloc->number, data, end_data);
	  if (unlikely (data >= end_data))
	    goto invalid;
	  get_uleb128 (newloc->number2, data, end_data);
	  break;

	case DW_OP_implicit_value:
	case DW_OP_GNU_entry_value:
	  /* This cannot be used in a CFI expression.  */
	  if (unlikely (dbg == NULL))
	    goto invalid;

	  /* start of block inc. len.  */
	  newloc->number2 = (Dwarf_Word) (uintptr_t) data;
	  get_uleb128 (newloc->number, data, end_data); /* Block length.  */
	  if (unlikely ((Dwarf_Word) (end_data - data) < newloc->number))
	    goto invalid;
	  data += newloc->number;		/* Skip the block.  */
	  break;

	case DW_OP_GNU_implicit_pointer:
	  /* DW_FORM_ref_addr, depends on offset size of CU.  */
	  if (dbg == NULL || __libdw_read_offset_inc (dbg, sec_index, &data,
						      ref_size,
						      &newloc->number,
						      IDX_debug_info, 0))
	    goto invalid;
	  if (unlikely (data >= end_data))
	    goto invalid;
	  get_uleb128 (newloc->number2, data, end_data); /* Byte offset.  */
	  break;

	case DW_OP_GNU_deref_type:
	  if (unlikely (data + 1 >= end_data))
	    goto invalid;
	  newloc->number = *data++;
	  get_uleb128 (newloc->number2, data, end_data);
	  break;

	case DW_OP_GNU_const_type:
	  {
	    size_t size;
	    get_uleb128 (newloc->number, data, end_data);
	    if (unlikely (data >= end_data))
	      goto invalid;

	    /* start of block inc. len.  */
	    newloc->number2 = (Dwarf_Word) (uintptr_t) data;
	    size = *data++;
	    if (unlikely ((Dwarf_Word) (end_data - data) < size))
	      goto invalid;
	    data += size;		/* Skip the block.  */
	  }
	  break;

	default:
	  goto invalid;
	}
    }

  if (unlikely (n == 0))
    {
      /* This is not allowed.
	 It would mean an empty location expression, which we handled
	 already as a special case above.  */
      goto invalid;
    }

  if (valuep)
    {
      struct loclist *newloc = NEW_LOC ();
      newloc->atom = DW_OP_stack_value;
      newloc->number = 0;
      newloc->number2 = 0;
      newloc->offset = data - block->data;
    }

  /* Allocate the array.  */
  Dwarf_Op *result;
  if (dbg != NULL)
    result = libdw_alloc (dbg, Dwarf_Op, sizeof (Dwarf_Op), n);
  else
    {
      result = malloc (sizeof *result * n);
      if (result == NULL)
	{
	nomem:
	  __libdw_seterrno (DWARF_E_NOMEM);
	  goto returnmem;
	}
    }

  /* Store the result.  */
  *llbuf = result;
  *listlen = n;

  do
    {
      /* We populate the array from the back since the list is backwards.  */
      --n;
      result[n].atom = loclist->atom;
      result[n].number = loclist->number;
      result[n].number2 = loclist->number2;
      result[n].offset = loclist->offset;

      if (result[n].atom == DW_OP_implicit_value)
	store_implicit_value (dbg, cache, &result[n]);

      struct loclist *loc = loclist;
      loclist = loclist->next;
      if (unlikely (n + 1 > MAX_STACK_LOCS))
	free (loc);
    }
  while (n > 0);

  /* Insert a record in the search tree so that we can find it again later.  */
  struct loc_s *newp;
  if (dbg != NULL)
    newp = libdw_alloc (dbg, struct loc_s, sizeof (struct loc_s), 1);
  else
    {
      newp = malloc (sizeof *newp);
      if (newp == NULL)
	{
	  free (result);
	  goto nomem;
	}
    }

  newp->addr = block->data;
  newp->loc = result;
  newp->nloc = *listlen;
  (void) tsearch (newp, cache, loc_compare);

  /* We did it.  */
  return 0;
}

static int
getlocation (struct Dwarf_CU *cu, const Dwarf_Block *block,
	     Dwarf_Op **llbuf, size_t *listlen, int sec_index)
{
  /* Empty location expressions don't have any ops to intern.
     Note that synthetic empty_cu doesn't have an associated DWARF dbg.  */
  if (block->length == 0)
    {
      *listlen = 0;
      return 0;
    }

  return __libdw_intern_expression (cu->dbg, cu->dbg->other_byte_order,
				    cu->address_size, (cu->version == 2
						       ? cu->address_size
						       : cu->offset_size),
				    &cu->locs, block,
				    false, false,
				    llbuf, listlen, sec_index);
}

int
dwarf_getlocation (Dwarf_Attribute *attr, Dwarf_Op **llbuf, size_t *listlen)
{
  if (! attr_ok (attr))
    return -1;

  int result = check_constant_offset (attr, llbuf, listlen);
  if (result != 1)
    return result;

  /* If it has a block form, it's a single location expression.  */
  Dwarf_Block block;
  if (INTUSE(dwarf_formblock) (attr, &block) != 0)
    return -1;

  return getlocation (attr->cu, &block, llbuf, listlen, cu_sec_idx (attr->cu));
}

static int
attr_base_address (Dwarf_Attribute *attr, Dwarf_Addr *basep)
{
  /* Fetch the CU's base address.  */
  Dwarf_Die cudie = CUDIE (attr->cu);

  /* Find the base address of the compilation unit.  It will
     normally be specified by DW_AT_low_pc.  In DWARF-3 draft 4,
     the base address could be overridden by DW_AT_entry_pc.  It's
     been removed, but GCC emits DW_AT_entry_pc and not DW_AT_lowpc
     for compilation units with discontinuous ranges.  */
  Dwarf_Attribute attr_mem;
  if (unlikely (INTUSE(dwarf_lowpc) (&cudie, basep) != 0)
      && INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (&cudie,
						     DW_AT_entry_pc,
						     &attr_mem),
				 basep) != 0)
    {
      if (INTUSE(dwarf_errno) () != 0)
	return -1;

      /* The compiler provided no base address when it should
	 have.  Buggy GCC does this when it used absolute
	 addresses in the location list and no DW_AT_ranges.  */
      *basep = 0;
    }
  return 0;
}
int
dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
{
  if (unlikely (cudie == NULL
		|| INTUSE(dwarf_tag) (cudie) != DW_TAG_compile_unit))
    return -1;

  int res = -1;

  /* Get the information if it is not already known.  */
  struct Dwarf_CU *const cu = cudie->cu;
  if (cu->lines == NULL)
    {
      /* Failsafe mode: no data found.  */
      cu->lines = (void *) -1l;
      cu->files = (void *) -1l;

      /* The die must have a statement list associated.  */
      Dwarf_Attribute stmt_list_mem;
      Dwarf_Attribute *stmt_list = INTUSE(dwarf_attr) (cudie, DW_AT_stmt_list,
						       &stmt_list_mem);

      /* Get the offset into the .debug_line section.  NB: this call
	 also checks whether the previous dwarf_attr call failed.  */
      Dwarf_Word offset;
      if (INTUSE(dwarf_formudata) (stmt_list, &offset) != 0)
	goto out;

      Dwarf *dbg = cu->dbg;
      if (dbg->sectiondata[IDX_debug_line] == NULL)
	{
	  __libdw_seterrno (DWARF_E_NO_DEBUG_LINE);
	  goto out;
	}
      const uint8_t *linep = dbg->sectiondata[IDX_debug_line]->d_buf + offset;
      const uint8_t *lineendp = (dbg->sectiondata[IDX_debug_line]->d_buf
				 + dbg->sectiondata[IDX_debug_line]->d_size);

      /* Get the compilation directory.  */
      Dwarf_Attribute compdir_attr_mem;
      Dwarf_Attribute *compdir_attr = INTUSE(dwarf_attr) (cudie,
							  DW_AT_comp_dir,
							  &compdir_attr_mem);
      const char *comp_dir = INTUSE(dwarf_formstring) (compdir_attr);

      if (unlikely (linep + 4 > lineendp))
	{
	invalid_data:
	  __libdw_seterrno (DWARF_E_INVALID_DEBUG_LINE);
	  goto out;
	}
      Dwarf_Word unit_length = read_4ubyte_unaligned_inc (dbg, linep);
      unsigned int length = 4;
      if (unlikely (unit_length == DWARF3_LENGTH_64_BIT))
	{
	  if (unlikely (linep + 8 > lineendp))
	    goto invalid_data;
	  unit_length = read_8ubyte_unaligned_inc (dbg, linep);
	  length = 8;
	}

      /* Check whether we have enough room in the section.  */
      if (unit_length < 2 + length + 5 * 1
	  || unlikely (linep + unit_length > lineendp))
	goto invalid_data;
      lineendp = linep + unit_length;

      /* The next element of the header is the version identifier.  */
      uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
      if (unlikely (version > DWARF_VERSION))
	{
	  __libdw_seterrno (DWARF_E_VERSION);
	  goto out;
	}

      /* Next comes the header length.  */
      Dwarf_Word header_length;
      if (length == 4)
	header_length = read_4ubyte_unaligned_inc (dbg, linep);
      else
	header_length = read_8ubyte_unaligned_inc (dbg, linep);
      const unsigned char *header_start = linep;

      /* Next the minimum instruction length.  */
      uint_fast8_t minimum_instr_len = *linep++;

        /* Then the flag determining the default value of the is_stmt
	   register.  */
      uint_fast8_t default_is_stmt = *linep++;

      /* Now the line base.  */
      int_fast8_t line_base = *((int_fast8_t *) linep);
      ++linep;

      /* And the line range.  */
      uint_fast8_t line_range = *linep++;

      /* The opcode base.  */
      uint_fast8_t opcode_base = *linep++;

      /* Remember array with the standard opcode length (-1 to account for
	 the opcode with value zero not being mentioned).  */
      const uint8_t *standard_opcode_lengths = linep - 1;
      linep += opcode_base - 1;
      if (unlikely (linep >= lineendp))
	goto invalid_data;

      /* First comes the list of directories.  Add the compilation
	 directory first since the index zero is used for it.  */
      struct dirlist
      {
	const char *dir;
	size_t len;
	struct dirlist *next;
      } comp_dir_elem =
	{
	  .dir = comp_dir,
	  .len = comp_dir ? strlen (comp_dir) : 0,
	  .next = NULL
	};
      struct dirlist *dirlist = &comp_dir_elem;
      unsigned int ndirlist = 1;

      // XXX Directly construct array to conserve memory?
      while (*linep != 0)
	{
	  struct dirlist *new_dir =
	    (struct dirlist *) alloca (sizeof (*new_dir));

	  new_dir->dir = (char *) linep;
	  uint8_t *endp = memchr (linep, '\0', lineendp - linep);
	  if (endp == NULL)
	    goto invalid_data;
	  new_dir->len = endp - linep;
	  new_dir->next = dirlist;
	  dirlist = new_dir;
	  ++ndirlist;
	  linep = endp + 1;
	}
      /* Skip the final NUL byte.  */
      ++linep;

      /* Rearrange the list in array form.  */
      struct dirlist **dirarray
	= (struct dirlist **) alloca (ndirlist * sizeof (*dirarray));
      for (unsigned int n = ndirlist; n-- > 0; dirlist = dirlist->next)
	dirarray[n] = dirlist;

      /* Now read the files.  */
      struct filelist null_file =
	{
	  .info =
	  {
	    .name = "???",
	    .mtime = 0,
	    .length = 0
	  },
	  .next = NULL
	};
      struct filelist *filelist = &null_file;
      unsigned int nfilelist = 1;

      if (unlikely (linep >= lineendp))
	goto invalid_data;
      while (*linep != 0)
	{
	  struct filelist *new_file =
	    (struct filelist *) alloca (sizeof (*new_file));

	  /* First comes the file name.  */
	  char *fname = (char *) linep;
	  uint8_t *endp = memchr (fname, '\0', lineendp - linep);
	  if (endp == NULL)
	    goto invalid_data;
	  size_t fnamelen = endp - (uint8_t *) fname;
	  linep = endp + 1;

	  /* Then the index.  */
	  Dwarf_Word diridx;
	  get_uleb128 (diridx, linep);
	  if (unlikely (diridx >= ndirlist))
	    {
	      __libdw_seterrno (DWARF_E_INVALID_DIR_IDX);
	      goto out;
	    }

	  if (*fname == '/')
	    /* It's an absolute path.  */
	    new_file->info.name = fname;
	  else
	    {
	      new_file->info.name = libdw_alloc (dbg, char, 1,
						 dirarray[diridx]->len + 1
						 + fnamelen + 1);
              char *cp = new_file->info.name;

              if (dirarray[diridx]->dir != NULL)
		{
		  /* This value could be NULL in case the DW_AT_comp_dir
		     was not present.  We cannot do much in this case.
		     The easiest thing is to convert the path in an
                   absolute path.  */
		  cp = stpcpy (cp, dirarray[diridx]->dir);
		}
              *cp++ = '/';
              strcpy (cp, fname);
	      assert (strlen (new_file->info.name)
		      < dirarray[diridx]->len + 1 + fnamelen + 1);
            }

	  /* Next comes the modification time.  */
	  get_uleb128 (new_file->info.mtime, linep);

	  /* Finally the length of the file.  */
	  get_uleb128 (new_file->info.length, linep);

	  new_file->next = filelist;
	  filelist = new_file;
	  ++nfilelist;
	}
      /* Skip the final NUL byte.  */
      ++linep;

      /* Consistency check.  */
      if (unlikely (linep != header_start + header_length))
	{
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
	  goto out;
	}

        /* We are about to process the statement program.  Initialize the
	   state machine registers (see 6.2.2 in the v2.1 specification).  */
      Dwarf_Word address = 0;
      size_t file = 1;
      size_t line = 1;
      size_t column = 0;
      uint_fast8_t is_stmt = default_is_stmt;
      int basic_block = 0;
      int prologue_end = 0;
      int epilogue_begin = 0;

        /* Process the instructions.  */
      struct linelist *linelist = NULL;
      unsigned int nlinelist = 0;
      while (linep < lineendp)
	{
	  struct linelist *new_line;
	  unsigned int opcode;
	  unsigned int u128;
	  int s128;

	  /* Read the opcode.  */
	  opcode = *linep++;

	  /* Is this a special opcode?  */
	  if (likely (opcode >= opcode_base))
	    {
	      /* Yes.  Handling this is quite easy since the opcode value
		 is computed with

		 opcode = (desired line increment - line_base)
		           + (line_range * address advance) + opcode_base
	      */
	      int line_increment = (line_base
				    + (opcode - opcode_base) % line_range);
	      unsigned int address_increment = (minimum_instr_len
						* ((opcode - opcode_base)
						   / line_range));

	      /* Perform the increments.  */
	      line += line_increment;
	      address += address_increment;

	      /* Add a new line with the current state machine values.  */
	      NEW_LINE (0);

	      /* Reset the flags.  */
	      basic_block = 0;
	      prologue_end = 0;
	      epilogue_begin = 0;
	    }
	  else if (opcode == 0)
	    {
	      /* This an extended opcode.  */
	      if (unlikely (linep + 2 > lineendp))
		goto invalid_data;

	      /* The length.  */
	      unsigned int len = *linep++;

	      if (unlikely (linep + len > lineendp))
		goto invalid_data;

	      /* The sub-opcode.  */
	      opcode = *linep++;

	      switch (opcode)
		{
		case DW_LNE_end_sequence:
		  /* Add a new line with the current state machine values.
		     The is the end of the sequence.  */
		  NEW_LINE (1);

		  /* Reset the registers.  */
		  address = 0;
		  file = 1;
		  line = 1;
		  column = 0;
		  is_stmt = default_is_stmt;
		  basic_block = 0;
		  prologue_end = 0;
		  epilogue_begin = 0;
		  break;

		case DW_LNE_set_address:
		  /* The value is an address.  The size is defined as
		     apporiate for the target machine.  We use the
		     address size field from the CU header.  */
		  if (cu->address_size == 4)
		    address = read_4ubyte_unaligned_inc (dbg, linep);
		  else
		    address = read_8ubyte_unaligned_inc (dbg, linep);
		  break;

		case DW_LNE_define_file:
		  {
		    char *fname = (char *) linep;
		    uint8_t *endp = memchr (linep, '\0', lineendp - linep);
		    if (endp == NULL)
		      goto invalid_data;
		    size_t fnamelen = endp - linep;
		    linep = endp + 1;

		    unsigned int diridx;
		    get_uleb128 (diridx, linep);
		    Dwarf_Word mtime;
		    get_uleb128 (mtime, linep);
		    Dwarf_Word filelength;
		    get_uleb128 (filelength, linep);

		    struct filelist *new_file =
		      (struct filelist *) alloca (sizeof (*new_file));
		    if (fname[0] == '/')
		      new_file->info.name = fname;
		    else
		      {
			new_file->info.name =
			  libdw_alloc (dbg, char, 1, (dirarray[diridx]->len + 1
						      + fnamelen + 1));
			char *cp = new_file->info.name;

			if (dirarray[diridx]->dir != NULL)
			  /* This value could be NULL in case the
			     DW_AT_comp_dir was not present.  We
			     cannot do much in this case.  The easiest
			     thing is to convert the path in an
			     absolute path.  */
			  cp = stpcpy (cp, dirarray[diridx]->dir);
			*cp++ = '/';
			strcpy (cp, fname);
		      }

		    new_file->info.mtime = mtime;
		    new_file->info.length = filelength;
		    new_file->next = filelist;
		    filelist = new_file;
		    ++nfilelist;
		  }
		  break;

		default:
		  /* Unknown, ignore it.  */
		  linep += len - 1;
		  break;
		}
	    }
	  else if (opcode <= DW_LNS_set_epilogue_begin)
	    {
	      /* This is a known standard opcode.  */
	      switch (opcode)
		{
		case DW_LNS_copy:
		  /* Takes no argument.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 0))
		    goto invalid_data;

		  /* Add a new line with the current state machine values.  */
		  NEW_LINE (0);

		  /* Reset the flags.  */
		  basic_block = 0;
		  /* XXX Whether the following two lines are necessary is
		     unclear.  I guess the current v2.1 specification has
		     a bug in that it says clearing these two registers is
		     not necessary.  */
		  prologue_end = 0;
		  epilogue_begin = 0;
		  break;

		case DW_LNS_advance_pc:
		  /* Takes one uleb128 parameter which is added to the
		     address.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 1))
		    goto invalid_data;

		  get_uleb128 (u128, linep);
		  address += minimum_instr_len * u128;
		  break;

		case DW_LNS_advance_line:
		  /* Takes one sleb128 parameter which is added to the
		     line.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 1))
		    goto invalid_data;

		  get_sleb128 (s128, linep);
		  line += s128;
		  break;

		case DW_LNS_set_file:
		  /* Takes one uleb128 parameter which is stored in file.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 1))
		    goto invalid_data;

		  get_uleb128 (u128, linep);
		  file = u128;
		  break;

		case DW_LNS_set_column:
		  /* Takes one uleb128 parameter which is stored in column.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 1))
		    goto invalid_data;

		  get_uleb128 (u128, linep);
		  column = u128;
		  break;

		case DW_LNS_negate_stmt:
		  /* Takes no argument.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 0))
		    goto invalid_data;

		  is_stmt = 1 - is_stmt;
		  break;

		case DW_LNS_set_basic_block:
		  /* Takes no argument.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 0))
		    goto invalid_data;

		  basic_block = 1;
		  break;

		case DW_LNS_const_add_pc:
		  /* Takes no argument.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 0))
		    goto invalid_data;

		  address += (minimum_instr_len
			      * ((255 - opcode_base) / line_range));
		  break;

		case DW_LNS_fixed_advance_pc:
		  /* Takes one 16 bit parameter which is added to the
		     address.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 1))
		    goto invalid_data;

		  address += read_2ubyte_unaligned_inc (dbg, linep);
		  break;

		case DW_LNS_set_prologue_end:
		  /* Takes no argument.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 0))
		    goto invalid_data;

		  prologue_end = 1;
		  break;

		case DW_LNS_set_epilogue_begin:
		  /* Takes no argument.  */
		  if (unlikely (standard_opcode_lengths[opcode] != 0))
		    goto invalid_data;

		  epilogue_begin = 1;
		  break;
		}
	    }
	  else
	    {
	      /* This is a new opcode the generator but not we know about.
		 Read the parameters associated with it but then discard
		 everything.  Read all the parameters for this opcode.  */
	      for (int n = standard_opcode_lengths[opcode]; n > 0; --n)
		get_uleb128 (u128, linep);

	      /* Next round, ignore this opcode.  */
	      continue;
	    }
	}
static int
get_offsets (Dwarf *dbg)
{
  size_t allocated = 0;
  size_t cnt = 0;
  struct pubnames_s *mem = NULL;
  const size_t entsize = sizeof (struct pubnames_s);
  unsigned char *const startp = dbg->sectiondata[IDX_debug_pubnames]->d_buf;
  unsigned char *readp = startp;
  unsigned char *endp = readp + dbg->sectiondata[IDX_debug_pubnames]->d_size;

  while (readp + 14 < endp)
    {
      /* If necessary, allocate more entries.  */
      if (cnt >= allocated)
	{
	  allocated = MAX (10, 2 * allocated);
	  struct pubnames_s *newmem
	    = (struct pubnames_s *) realloc (mem, allocated * entsize);
	  if (newmem == NULL)
	    {
	      __libdw_seterrno (DWARF_E_NOMEM);
	    err_return:
	      free (mem);
	      return -1;
	    }

	  mem = newmem;
	}

      /* Read the set header.  */
      int len_bytes = 4;
      Dwarf_Off len = read_4ubyte_unaligned_inc (dbg, readp);
      if (len == DWARF3_LENGTH_64_BIT)
	{
	  len = read_8ubyte_unaligned_inc (dbg, readp);
	  len_bytes = 8;
	}
      else if (unlikely (len >= DWARF3_LENGTH_MIN_ESCAPE_CODE
			 && len <= DWARF3_LENGTH_MAX_ESCAPE_CODE))
	{
	invalid_dwarf:
	  __libdw_seterrno (DWARF_E_INVALID_DWARF);
	  goto err_return;
	}

      /* Now we know the offset of the first offset/name pair.  */
      mem[cnt].set_start = readp + 2 + 2 * len_bytes - startp;
      mem[cnt].address_len = len_bytes;
      if (mem[cnt].set_start >= dbg->sectiondata[IDX_debug_pubnames]->d_size)
	/* Something wrong, the first entry is beyond the end of
	   the section.  */
	break;

      /* Read the version.  It better be two for now.  */
      uint16_t version = read_2ubyte_unaligned (dbg, readp);
      if (unlikely (version != 2))
	{
	  __libdw_seterrno (DWARF_E_INVALID_VERSION);
	  goto err_return;
	}

      /* Get the CU offset.  */
      if (len_bytes == 4)
	mem[cnt].cu_offset = read_4ubyte_unaligned (dbg, readp + 2);
      else
	mem[cnt].cu_offset = read_8ubyte_unaligned (dbg, readp + 2);

      /* Determine the size of the CU header.  */
      if (unlikely (dbg->sectiondata[IDX_debug_info] == NULL
		    || dbg->sectiondata[IDX_debug_info]->d_buf == NULL
		    || (mem[cnt].cu_offset + 3
			>= dbg->sectiondata[IDX_debug_info]->d_size)))
	goto invalid_dwarf;

      unsigned char *infop
	= ((unsigned char *) dbg->sectiondata[IDX_debug_info]->d_buf
	   + mem[cnt].cu_offset);
      if (read_4ubyte_unaligned_noncvt (infop) == DWARF3_LENGTH_64_BIT)
	mem[cnt].cu_header_size = 23;
      else
	mem[cnt].cu_header_size = 11;

      ++cnt;

      /* Advance to the next set.  */
      readp += len;
    }

  if (mem == NULL)
    {
      __libdw_seterrno (DWARF_E_NO_ENTRY);
      return -1;
    }

  dbg->pubnames_sets = (struct pubnames_s *) realloc (mem, cnt * entsize);
  dbg->pubnames_nsets = cnt;

  return 0;
}
示例#30
0
static const uint8_t *
parse_eh_frame_hdr (const uint8_t *hdr, size_t hdr_size, GElf_Addr hdr_vaddr,
		    const GElf_Ehdr *ehdr, GElf_Addr *eh_frame_vaddr,
		    size_t *table_entries, uint8_t *table_encoding)
{
  const uint8_t *h = hdr;

  if (*h++ != 1)		/* version */
    return (void *) -1l;

  uint8_t eh_frame_ptr_encoding = *h++;
  uint8_t fde_count_encoding = *h++;
  uint8_t fde_table_encoding = *h++;

  if (eh_frame_ptr_encoding == DW_EH_PE_omit)
    return (void *) -1l;

  /* Dummy used by read_encoded_value.  */
  Elf_Data_Scn dummy_cfi_hdr_data =
    {
      .d = { .d_buf = (void *) hdr, .d_size = hdr_size }
    };
  Dwarf_CFI dummy_cfi =
    {
      .e_ident = ehdr->e_ident,
      .datarel = hdr_vaddr,
      .frame_vaddr = hdr_vaddr,
      .data = &dummy_cfi_hdr_data,
    };

  if (unlikely (read_encoded_value (&dummy_cfi, eh_frame_ptr_encoding, &h,
				    eh_frame_vaddr)))
    return (void *) -1l;

  if (fde_count_encoding != DW_EH_PE_omit)
    {
      Dwarf_Word fde_count;
      if (unlikely (read_encoded_value (&dummy_cfi, fde_count_encoding, &h,
					&fde_count)))
	return (void *) -1l;
      if (fde_count != 0 && (size_t) fde_count == fde_count
	  && fde_table_encoding != DW_EH_PE_omit
	  && (fde_table_encoding &~ DW_EH_PE_signed) != DW_EH_PE_uleb128)
	{
	  *table_entries = fde_count;
	  *table_encoding = fde_table_encoding;
	  return h;
	}
    }

  return NULL;
}

static Dwarf_CFI *
getcfi_gnu_eh_frame (Elf *elf, const GElf_Ehdr *ehdr, const GElf_Phdr *phdr)
{
  if (unlikely (phdr->p_filesz < 4))
    goto invalid;

  Elf_Data *data = elf_getdata_rawchunk (elf, phdr->p_offset, phdr->p_filesz,
					 ELF_T_BYTE);
  if (data == NULL)
    {
    invalid_hdr:
    invalid:
      /* XXX might be read error or corrupt phdr */
      __libdw_seterrno (DWARF_E_INVALID_CFI);
      return NULL;
    }

  Dwarf_Addr eh_frame_ptr;
  size_t search_table_entries;
  uint8_t search_table_encoding;
  const uint8_t *search_table = parse_eh_frame_hdr (data->d_buf, phdr->p_filesz,
						    phdr->p_vaddr, ehdr,
						    &eh_frame_ptr,
						    &search_table_entries,
						    &search_table_encoding);
  if (search_table == (void *) -1l)
    goto invalid_hdr;

  Dwarf_Off eh_frame_offset = eh_frame_ptr - phdr->p_vaddr + phdr->p_offset;
  Dwarf_Word eh_frame_size = 0;

  /* XXX we have no way without section headers to know the size
     of the .eh_frame data.  Calculate the largest it might possibly be.
     This won't be wasteful if the file is already mmap'd, but if it isn't
     it might be quite excessive.  */
  size_t filesize;
  if (elf_rawfile (elf, &filesize) != NULL)
    eh_frame_size = filesize - eh_frame_offset;

  data = elf_getdata_rawchunk (elf, eh_frame_offset, eh_frame_size, ELF_T_BYTE);
  if (data == NULL)
    {
      __libdw_seterrno (DWARF_E_INVALID_ELF); /* XXX might be read error */
      return NULL;
    }
  Dwarf_CFI *cfi = allocate_cfi (elf, eh_frame_ptr);
  if (cfi != NULL)
    {
      cfi->data = (Elf_Data_Scn *) data;

      if (search_table != NULL)
	{
	  cfi->search_table = search_table;
	  cfi->search_table_vaddr = phdr->p_vaddr;
	  cfi->search_table_encoding = search_table_encoding;
	  cfi->search_table_entries = search_table_entries;
	}
    }
  return cfi;
}

/* Search the phdrs for PT_GNU_EH_FRAME.  */
static Dwarf_CFI *
getcfi_phdr (Elf *elf, const GElf_Ehdr *ehdr)
{
  size_t phnum;
  if (unlikely (elf_getphdrnum (elf, &phnum) != 0))
    return NULL;

  for (size_t i = 0; i < phnum; ++i)
    {
      GElf_Phdr phdr_mem;
      GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
      if (unlikely (phdr == NULL))
	return NULL;
      if (phdr->p_type == PT_GNU_EH_FRAME)
	return getcfi_gnu_eh_frame (elf, ehdr, phdr);
    }

  __libdw_seterrno (DWARF_E_NO_DWARF);
  return NULL;
}