示例#1
0
/* Check that MOD is valid and make sure its relocation has been done.  */
static bool
check_module (Dwfl_Module *mod)
{
  if (INTUSE(dwfl_module_getsymtab) (mod) < 0)
    {
      Dwfl_Error error = dwfl_errno ();
      if (error != DWFL_E_NO_SYMTAB)
	{
	  __libdwfl_seterrno (error);
	  return true;
	}
    }

  if (mod->dw == NULL)
    {
      Dwarf_Addr bias;
      if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
	{
	  Dwfl_Error error = dwfl_errno ();
	  if (error != DWFL_E_NO_DWARF)
	    {
	      __libdwfl_seterrno (error);
	      return true;
	    }
	}
    }

  return false;
}
示例#2
0
/* Find the index in MOD->reloc_info.refs containing *ADDR.  */
static int
find_section (Dwfl_Module *mod, Dwarf_Addr *addr)
{
  if (unlikely (mod->reloc_info == NULL) && cache_sections (mod) < 0)
    return -1;

  struct dwfl_relocation *sections = mod->reloc_info;

  /* The sections are sorted by address, so we can use binary search.  */
  size_t l = 0, u = sections->count;
  while (l < u)
    {
      size_t idx = (l + u) / 2;
      if (*addr < sections->refs[idx].start)
	u = idx;
      else if (*addr > sections->refs[idx].end)
	l = idx + 1;
      else
	{
	  /* Consider the limit of a section to be inside it, unless it's
	     inside the next one.  A section limit address can appear in
	     line records.  */
	  if (*addr == sections->refs[idx].end
	      && idx < sections->count
	      && *addr == sections->refs[idx + 1].start)
	    ++idx;

	  *addr -= sections->refs[idx].start;
	  return idx;
	}
    }

  __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_NO_MATCH));
  return -1;
}
示例#3
0
Elf_Scn *
dwfl_module_address_section (Dwfl_Module *mod, Dwarf_Addr *address,
			     Dwarf_Addr *bias)
{
  if (check_module (mod))
    return NULL;

  int idx = find_section (mod, address);
  if (idx < 0)
    return NULL;

  if (mod->reloc_info->refs[idx].relocs != NULL)
    {
      assert (mod->e_type == ET_REL);

      Elf_Scn *tscn = mod->reloc_info->refs[idx].scn;
      Elf_Scn *relocscn = mod->reloc_info->refs[idx].relocs;
      Dwfl_Error result = __libdwfl_relocate_section (mod, mod->main.elf,
						      relocscn, tscn, true);
      if (likely (result == DWFL_E_NOERROR))
	mod->reloc_info->refs[idx].relocs = NULL;
      else
	{
	  __libdwfl_seterrno (result);
	  return NULL;
	}
    }

  *bias = dwfl_adjusted_address (mod, 0);
  return mod->reloc_info->refs[idx].scn;
}
Dwarf *
dwfl_module_getdwarf (Dwfl_Module *mod, Dwarf_Addr *bias)
{
  if (mod == NULL)
    return NULL;

  find_dw (mod);
  if (mod->dwerr == DWFL_E_NOERROR)
    {
      /* If dwfl_module_getelf was used previously, then partial apply
	 relocation to miscellaneous sections in the debug file too.  */
      if (mod->e_type == ET_REL
	  && mod->main.relocated && ! mod->debug.relocated)
	{
	  mod->debug.relocated = true;
	  if (mod->debug.elf != mod->main.elf)
	    (void) __libdwfl_relocate (mod, mod->debug.elf, false);
	}

      *bias = mod->debug.bias;
      return mod->dw;
    }

  __libdwfl_seterrno (mod->dwerr);
  return NULL;
}
int
dwfl_validate_address (Dwfl *dwfl, Dwarf_Addr address, Dwarf_Sword offset)
{
  Dwfl_Module *mod = INTUSE(dwfl_addrmodule) (dwfl, address);
  if (mod == NULL)
    return -1;

  Dwarf_Addr relative = address;
  int idx = INTUSE(dwfl_module_relocate_address) (mod, &relative);
  if (idx < 0)
    return -1;

  if (offset != 0)
    {
      int offset_idx = -1;
      relative = address + offset;
      if (relative >= mod->low_addr && relative <= mod->high_addr)
	{
	  offset_idx = INTUSE(dwfl_module_relocate_address) (mod, &relative);
	  if (offset_idx < 0)
	    return -1;
	}
      if (offset_idx != idx)
	{
	  __libdwfl_seterrno (DWFL_E_ADDR_OUTOFRANGE);
	  return -1;
	}
    }

  return 0;
}
示例#6
0
int
internal_function
__libdwfl_find_build_id (Dwfl_Module *mod, bool set, Elf *elf)
{
  size_t shstrndx = SHN_UNDEF;
  int result = 0;

  Elf_Scn *scn = elf_nextscn (elf, NULL);

  if (scn == NULL)
    {
      /* No sections, have to look for phdrs.  */
      GElf_Ehdr ehdr_mem;
      GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
      size_t phnum;
      if (unlikely (ehdr == NULL)
	  || unlikely (elf_getphdrnum (elf, &phnum) != 0))
	{
	  __libdwfl_seterrno (DWFL_E_LIBELF);
	  return -1;
	}
      for (size_t i = 0; result == 0 && i < phnum; ++i)
	{
	  GElf_Phdr phdr_mem;
	  GElf_Phdr *phdr = gelf_getphdr (elf, i, &phdr_mem);
	  if (likely (phdr != NULL) && phdr->p_type == PT_NOTE)
	    result = check_notes (mod, set,
				  elf_getdata_rawchunk (elf,
							phdr->p_offset,
							phdr->p_filesz,
							ELF_T_NHDR),
				  phdr->p_vaddr + mod->main.bias);
	}
    }
  else
    do
      {
	GElf_Shdr shdr_mem;
	GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
	if (likely (shdr != NULL) && shdr->sh_type == SHT_NOTE)
	  {
	    /* Determine the right sh_addr in this module.  */
	    GElf_Addr vaddr = 0;
	    if (!(shdr->sh_flags & SHF_ALLOC))
	      vaddr = NO_VADDR;
	    else if (mod->e_type != ET_REL)
	      vaddr = shdr->sh_addr + mod->main.bias;
	    else if (__libdwfl_relocate_value (mod, elf, &shstrndx,
					       elf_ndxscn (scn), &vaddr))
	      vaddr = NO_VADDR;
	    result = check_notes (mod, set, elf_getdata (scn, NULL), vaddr);
	  }
      }
    while (result == 0 && (scn = elf_nextscn (elf, scn)) != NULL);

  return result;
}
Dwfl *
dwfl_begin (const Dwfl_Callbacks *callbacks)
{
  if (elf_version (EV_CURRENT) == EV_NONE)
    {
      __libdwfl_seterrno (DWFL_E_LIBELF);
      return NULL;
    }

  Dwfl *dwfl = calloc (1, sizeof *dwfl);
  if (dwfl == NULL)
    __libdwfl_seterrno (DWFL_E_NOMEM);
  else
    {
      dwfl->callbacks = callbacks;
      dwfl->offline_next_address = OFFLINE_REDZONE;
    }

  return dwfl;
}
int
dwfl_module_getsymtab (Dwfl_Module *mod)
{
  if (mod == NULL)
    return -1;

  find_symtab (mod);
  if (mod->symerr == DWFL_E_NOERROR)
    return mod->syments;

  __libdwfl_seterrno (mod->symerr);
  return -1;
}
示例#9
0
int
dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
		     const void *ident)
{
  if (dwfl == NULL)
    return -1;

  if (ndx < 0)
    ndx = dwfl->lookup_tail_ndx;

  if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
			    phdr->p_align < dwfl->segment_align))
    dwfl->segment_align = phdr->p_align;

  if (unlikely (dwfl->lookup_module != NULL))
    {
      free (dwfl->lookup_module);
      dwfl->lookup_module = NULL;
    }

  GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr);
  GElf_Addr end = __libdwfl_segment_end (dwfl,
					 bias + phdr->p_vaddr + phdr->p_memsz);

  /* Coalesce into the last one if contiguous and matching.  */
  if (ndx != dwfl->lookup_tail_ndx
      || ident == NULL
      || ident != dwfl->lookup_tail_ident
      || start != dwfl->lookup_tail_vaddr
      || phdr->p_offset != dwfl->lookup_tail_offset)
    {
      /* Normally just appending keeps us sorted.  */

      size_t i = dwfl->lookup_elts;
      while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
	--i;

      if (unlikely (insert (dwfl, i, start, end, ndx)))
	{
	  __libdwfl_seterrno (DWFL_E_NOMEM);
	  return -1;
	}
    }

  dwfl->lookup_tail_ident = ident;
  dwfl->lookup_tail_vaddr = end;
  dwfl->lookup_tail_offset = end - bias - phdr->p_vaddr + phdr->p_offset;
  dwfl->lookup_tail_ndx = ndx + 1;

  return ndx;
}
Dwarf_Die *
dwfl_nextcu (Dwfl *dwfl, Dwarf_Die *lastcu, Dwarf_Addr *bias)
{
  if (dwfl == NULL)
    return NULL;

  struct dwfl_cu *cu = (struct dwfl_cu *) lastcu;
  Dwfl_Module *mod;

  if (cu == NULL)
    {
      mod = dwfl->modulelist;
      goto nextmod;
    }
  else
    mod = cu->mod;

  Dwfl_Error error;
  do
    {
      error = __libdwfl_nextcu (mod, cu, &cu);
      if (error != DWFL_E_NOERROR)
	break;

      if (cu != NULL)
	{
	  *bias = mod->debug.bias;
	  return &cu->die;
	}

      do
	{
	  mod = mod->next;

	nextmod:
	  if (mod == NULL)
	    return NULL;

	  if (mod->dwerr == DWFL_E_NOERROR
	      && (mod->dw != NULL
		  || INTUSE(dwfl_module_getdwarf) (mod, bias) != NULL))
	    break;
	}
      while (mod->dwerr == DWFL_E_NO_DWARF);
      error = mod->dwerr;
    }
  while (error == DWFL_E_NOERROR);

  __libdwfl_seterrno (error);
  return NULL;
}
internal_function
__libdwfl_set_cfi (Dwfl_Module *mod, Dwarf_CFI **slot, Dwarf_CFI *cfi)
{
  if (cfi != NULL && cfi->ebl == NULL)
    {
      Dwfl_Error error = __libdwfl_module_getebl (mod);
      if (error == DWFL_E_NOERROR)
	cfi->ebl = mod->ebl;
      else
	{
	  if (slot == &mod->eh_cfi)
	    INTUSE(dwarf_cfi_end) (cfi);
	  __libdwfl_seterrno (error);
	  return NULL;
	}
    }

  return *slot = cfi;
}
示例#12
0
int
dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
{
  if (unlikely (dwfl == NULL))
    return -1;

  if (unlikely (dwfl->lookup_module == NULL)
      && mod != NULL
      && unlikely (reify_segments (dwfl)))
    {
      __libdwfl_seterrno (DWFL_E_NOMEM);
      return -1;
    }

  int idx = lookup (dwfl, address, -1);
  if (likely (mod != NULL))
    {
      if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
	*mod = NULL;
      else
	{
	  *mod = dwfl->lookup_module[idx];

	  /* If this segment does not have a module, but the address is
	     the upper boundary of the previous segment's module, use that.  */
	  if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address)
	    {
	      *mod = dwfl->lookup_module[idx - 1];
	      if (*mod != NULL && (*mod)->high_addr != address)
		*mod = NULL;
	    }
	}
    }

  if (likely (idx >= 0))
    /* Translate internal segment table index to user segment index.  */
    idx = dwfl->lookup_segndx[idx];

  return idx;
}
示例#13
0
static int
found_build_id (Dwfl_Module *mod, bool set,
		const void *bits, int len, GElf_Addr vaddr)
{
  if (!set)
    /* When checking bits, we do not compare VADDR because the
       address found in a debuginfo file may not match the main
       file as modified by prelink.  */
    return 1 + (mod->build_id_len == len
		&& !memcmp (bits, mod->build_id_bits, len));

  void *copy = malloc (len);
  if (unlikely (copy == NULL))
    {
      __libdwfl_seterrno (DWFL_E_NOMEM);
      return -1;
    }

  mod->build_id_bits = memcpy (copy, bits, len);
  mod->build_id_vaddr = vaddr;
  mod->build_id_len = len;
  return len;
}
示例#14
0
Dwarf_CFI *
dwfl_module_eh_cfi (Dwfl_Module *mod, Dwarf_Addr *bias)
{
  if (mod == NULL)
    return NULL;

  if (mod->eh_cfi != NULL)
    {
      *bias = dwfl_adjusted_address (mod, 0);
      return mod->eh_cfi;
    }

  __libdwfl_getelf (mod);
  if (mod->elferr != DWFL_E_NOERROR)
    {
      __libdwfl_seterrno (mod->elferr);
      return NULL;
    }

  *bias = dwfl_adjusted_address (mod, 0);
  return __libdwfl_set_cfi (mod, &mod->eh_cfi,
			    INTUSE(dwarf_getcfi_elf) (mod->main.elf));
}
Elf *
dwfl_module_getelf (Dwfl_Module *mod, GElf_Addr *loadbase)
{
  if (mod == NULL)
    return NULL;

  find_file (mod);
  if (mod->elferr == DWFL_E_NOERROR)
    {
      if (mod->e_type == ET_REL && ! mod->main.relocated)
	{
	  /* Before letting them get at the Elf handle,
	     apply all the relocations we know how to.  */

	  mod->main.relocated = true;
	  if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
	    {
	      (void) __libdwfl_relocate (mod, mod->main.elf, false);

	      if (mod->debug.elf == mod->main.elf)
		mod->debug.relocated = true;
	      else if (mod->debug.elf != NULL && ! mod->debug.relocated)
		{
		  mod->debug.relocated = true;
		  (void) __libdwfl_relocate (mod, mod->debug.elf, false);
		}
	    }
	}

      *loadbase = mod->main.bias;
      return mod->main.elf;
    }

  __libdwfl_seterrno (mod->elferr);
  return NULL;
}
static bool
validate (Dwfl_Module *mod, int fd, bool check, GElf_Word debuglink_crc)
{
  /* For alt debug files always check the build-id from the Dwarf and alt.  */
  if (mod->dw != NULL)
    {
      bool valid = false;
      const void *build_id;
      const char *altname;
      ssize_t build_id_len = INTUSE(dwelf_dwarf_gnu_debugaltlink) (mod->dw,
								   &altname,
								   &build_id);
      if (build_id_len > 0)
	{
	  /* We need to open an Elf handle on the file so we can check its
	     build ID note for validation.  Backdoor the handle into the
	     module data structure since we had to open it early anyway.  */
	  Dwfl_Error error = __libdw_open_file (&fd, &mod->alt_elf,
						false, false);
	  if (error != DWFL_E_NOERROR)
	    __libdwfl_seterrno (error);
	  else
	    {
	      const void *alt_build_id;
	      ssize_t alt_len = INTUSE(dwelf_elf_gnu_build_id) (mod->alt_elf,
								&alt_build_id);
	      if (alt_len > 0 && alt_len == build_id_len
		  && memcmp (build_id, alt_build_id, alt_len) == 0)
		valid = true;
	      else
		{
		  /* A mismatch!  */
		  elf_end (mod->alt_elf);
		  mod->alt_elf = NULL;
		  close (fd);
		  fd = -1;
		}
	    }
	}
      return valid;
    }

  /* If we have a build ID, check only that.  */
  if (mod->build_id_len > 0)
    {
      /* We need to open an Elf handle on the file so we can check its
	 build ID note for validation.  Backdoor the handle into the
	 module data structure since we had to open it early anyway.  */

      mod->debug.valid = false;
      Dwfl_Error error = __libdw_open_file (&fd, &mod->debug.elf, false, false);
      if (error != DWFL_E_NOERROR)
	__libdwfl_seterrno (error);
      else if (likely (__libdwfl_find_build_id (mod, false,
						mod->debug.elf) == 2))
	/* Also backdoor the gratuitous flag.  */
	mod->debug.valid = true;
      else
	{
	  /* A mismatch!  */
	  elf_end (mod->debug.elf);
	  mod->debug.elf = NULL;
	  close (fd);
	  fd = -1;
	}

      return mod->debug.valid;
    }

  return !check || check_crc (fd, debuglink_crc);
}
示例#17
0
const char *
dwfl_module_getsym (Dwfl_Module *mod, int ndx,
		    GElf_Sym *sym, GElf_Word *shndxp)
{
  if (unlikely (mod == NULL))
    return NULL;

  if (unlikely (mod->symdata == NULL))
    {
      int result = INTUSE(dwfl_module_getsymtab) (mod);
      if (result < 0)
	return NULL;
    }

  GElf_Word shndx;
  sym = gelf_getsymshndx (mod->symdata, mod->symxndxdata, ndx, sym, &shndx);
  if (unlikely (sym == NULL))
    {
      __libdwfl_seterrno (DWFL_E_LIBELF);
      return NULL;
    }

  if (sym->st_shndx != SHN_XINDEX)
    shndx = sym->st_shndx;

  /* Figure out whether this symbol points into an SHF_ALLOC section.  */
  bool alloc = true;
  if ((shndxp != NULL || mod->e_type != ET_REL)
      && (sym->st_shndx == SHN_XINDEX
	  || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF)))
    {
      GElf_Shdr shdr_mem;
      GElf_Shdr *shdr = gelf_getshdr (elf_getscn (mod->symfile->elf, shndx),
				      &shdr_mem);
      alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC);
    }

  if (shndxp != NULL)
    /* Yield -1 in case of a non-SHF_ALLOC section.  */
    *shndxp = alloc ? shndx : (GElf_Word) -1;

  switch (sym->st_shndx)
    {
    case SHN_ABS:		/* XXX sometimes should use bias?? */
    case SHN_UNDEF:
    case SHN_COMMON:
      break;

    default:
      if (mod->e_type == ET_REL)
	{
	  /* In an ET_REL file, the symbol table values are relative
	     to the section, not to the module's load base.  */
	  size_t symshstrndx = SHN_UNDEF;
	  Dwfl_Error result = __libdwfl_relocate_value (mod, mod->symfile->elf,
							&symshstrndx,
							shndx, &sym->st_value);
	  if (unlikely (result != DWFL_E_NOERROR))
	    {
	      __libdwfl_seterrno (result);
	      return NULL;
	    }
	}
      else if (alloc)
	/* Apply the bias to the symbol value.  */
	sym->st_value += mod->symfile->bias;
      break;
    }

  if (unlikely (sym->st_name >= mod->symstrdata->d_size))
    {
      __libdwfl_seterrno (DWFL_E_BADSTROFF);
      return NULL;
    }
  return (const char *) mod->symstrdata->d_buf + sym->st_name;
}
示例#18
0
static int
cache_sections (Dwfl_Module *mod)
{
  struct secref *refs = NULL;
  size_t nrefs = 0;

  size_t shstrndx;
  if (unlikely (elf_getshdrstrndx (mod->main.elf, &shstrndx) < 0))
    {
    elf_error:
      __libdwfl_seterrno (DWFL_E_LIBELF);
      return -1;
    }

  bool check_reloc_sections = false;
  Elf_Scn *scn = NULL;
  while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
    {
      GElf_Shdr shdr_mem;
      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
      if (shdr == NULL)
	goto elf_error;

      if ((shdr->sh_flags & SHF_ALLOC) && shdr->sh_addr == 0
	  && mod->e_type == ET_REL)
	{
	  /* This section might not yet have been looked at.  */
	  if (__libdwfl_relocate_value (mod, mod->main.elf, &shstrndx,
					elf_ndxscn (scn),
					&shdr->sh_addr) != DWFL_E_NOERROR)
	    continue;
	  shdr = gelf_getshdr (scn, &shdr_mem);
	  if (unlikely (shdr == NULL))
	    goto elf_error;
	}

      if (shdr->sh_flags & SHF_ALLOC)
	{
	  const char *name = elf_strptr (mod->main.elf, shstrndx,
					 shdr->sh_name);
	  if (unlikely (name == NULL))
	    goto elf_error;

	  struct secref *newref = alloca (sizeof *newref);
	  newref->scn = scn;
	  newref->relocs = NULL;
	  newref->name = name;
	  newref->start = dwfl_adjusted_address (mod, shdr->sh_addr);
	  newref->end = newref->start + shdr->sh_size;
	  newref->next = refs;
	  refs = newref;
	  ++nrefs;
	}

      if (mod->e_type == ET_REL
	  && shdr->sh_size != 0
	  && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)
	  && mod->dwfl->callbacks->section_address != NULL)
	{
	  if (shdr->sh_info < elf_ndxscn (scn))
	    {
	      /* We've already looked at the section these relocs apply to.  */
	      Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
	      if (likely (tscn != NULL))
		for (struct secref *sec = refs; sec != NULL; sec = sec->next)
		  if (sec->scn == tscn)
		    {
		      sec->relocs = scn;
		      break;
		    }
	    }
	  else
	    /* We'll have to do a second pass.  */
	    check_reloc_sections = true;
	}
    }

  mod->reloc_info = malloc (offsetof (struct dwfl_relocation, refs[nrefs]));
  if (mod->reloc_info == NULL)
    {
      __libdwfl_seterrno (DWFL_E_NOMEM);
      return -1;
    }

  struct secref **sortrefs = alloca (nrefs * sizeof sortrefs[0]);
  for (size_t i = nrefs; i-- > 0; refs = refs->next)
    sortrefs[i] = refs;
  assert (refs == NULL);

  qsort (sortrefs, nrefs, sizeof sortrefs[0], &compare_secrefs);

  mod->reloc_info->count = nrefs;
  for (size_t i = 0; i < nrefs; ++i)
    {
      mod->reloc_info->refs[i].name = sortrefs[i]->name;
      mod->reloc_info->refs[i].scn = sortrefs[i]->scn;
      mod->reloc_info->refs[i].relocs = sortrefs[i]->relocs;
      mod->reloc_info->refs[i].start = sortrefs[i]->start;
      mod->reloc_info->refs[i].end = sortrefs[i]->end;
    }

  if (unlikely (check_reloc_sections))
    {
      /* There was a reloc section that preceded its target section.
	 So we have to scan again now that we have cached all the
	 possible target sections we care about.  */

      scn = NULL;
      while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL)
	{
	  GElf_Shdr shdr_mem;
	  GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
	  if (shdr == NULL)
	    goto elf_error;

      	  if (shdr->sh_size != 0
	      && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA))
	    {
	      Elf_Scn *tscn = elf_getscn (mod->main.elf, shdr->sh_info);
	      if (likely (tscn != NULL))
		for (size_t i = 0; i < nrefs; ++i)
		  if (mod->reloc_info->refs[i].scn == tscn)
		    {
		      mod->reloc_info->refs[i].relocs = scn;
		      break;
		    }
	    }
	}
    }

  return nrefs;
}
Elf *
elf_from_remote_memory (GElf_Addr ehdr_vma,
			GElf_Addr *loadbasep,
			ssize_t (*read_memory) (void *arg, void *data,
						GElf_Addr address,
						size_t minread,
						size_t maxread),
			void *arg)
{
  /* First read in the file header and check its sanity.  */

  const size_t initial_bufsize = 256;
  unsigned char *buffer = malloc (initial_bufsize);
  if (buffer == NULL)
    {
    no_memory:
      __libdwfl_seterrno (DWFL_E_NOMEM);
      return NULL;
    }

  ssize_t nread = (*read_memory) (arg, buffer, ehdr_vma,
				  sizeof (Elf32_Ehdr), initial_bufsize);
  if (nread <= 0)
    {
    read_error:
      free (buffer);
      __libdwfl_seterrno (nread < 0 ? DWFL_E_ERRNO : DWFL_E_TRUNCATED);
      return NULL;
    }

  if (memcmp (buffer, ELFMAG, SELFMAG) != 0)
    {
    bad_elf:
      __libdwfl_seterrno (DWFL_E_BADELF);
      return NULL;
    }

  /* Extract the information we need from the file header.  */

  union
  {
    Elf32_Ehdr e32;
    Elf64_Ehdr e64;
  } ehdr;
  Elf_Data xlatefrom =
    {
      .d_type = ELF_T_EHDR,
      .d_buf = buffer,
      .d_version = EV_CURRENT,
    };
  Elf_Data xlateto =
    {
      .d_type = ELF_T_EHDR,
      .d_buf = &ehdr,
      .d_size = sizeof ehdr,
      .d_version = EV_CURRENT,
    };

  GElf_Off phoff;
  uint_fast16_t phnum;
  uint_fast16_t phentsize;
  GElf_Off shdrs_end;

  switch (buffer[EI_CLASS])
    {
    case ELFCLASS32:
      xlatefrom.d_size = sizeof (Elf32_Ehdr);
      if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
	{
	libelf_error:
	  __libdwfl_seterrno (DWFL_E_LIBELF);
	  return NULL;
	}
      phoff = ehdr.e32.e_phoff;
      phnum = ehdr.e32.e_phnum;
      phentsize = ehdr.e32.e_phentsize;
      if (phentsize != sizeof (Elf32_Phdr) || phnum == 0)
	goto bad_elf;
      shdrs_end = ehdr.e32.e_shoff + ehdr.e32.e_shnum * ehdr.e32.e_shentsize;
      break;

    case ELFCLASS64:
      xlatefrom.d_size = sizeof (Elf64_Ehdr);
      if (elf64_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL)
	goto libelf_error;
      phoff = ehdr.e64.e_phoff;
      phnum = ehdr.e64.e_phnum;
      phentsize = ehdr.e64.e_phentsize;
      if (phentsize != sizeof (Elf64_Phdr) || phnum == 0)
	goto bad_elf;
      shdrs_end = ehdr.e64.e_shoff + ehdr.e64.e_shnum * ehdr.e64.e_shentsize;
      break;

    default:
      goto bad_elf;
    }


  /* The file header tells where to find the program headers.
     These are what we use to actually choose what to read.  */

  xlatefrom.d_type = xlateto.d_type = ELF_T_PHDR;
  xlatefrom.d_size = phnum * phentsize;

  if ((size_t) nread >= phoff + phnum * phentsize)
    /* We already have all the phdrs from the initial read.  */
    xlatefrom.d_buf = buffer + phoff;
  else
    {
      /* Read in the program headers.  */

      if (initial_bufsize < phnum * phentsize)
	{
	  unsigned char *newbuf = realloc (buffer, phnum * phentsize);
	  if (newbuf == NULL)
	    {
	      free (buffer);
	      goto no_memory;
	    }
	  buffer = newbuf;
	}
      nread = (*read_memory) (arg, buffer, ehdr_vma + phoff,
			      phnum * phentsize, phnum * phentsize);
      if (nread <= 0)
	goto read_error;

      xlatefrom.d_buf = buffer;
    }

  union
  {
    Elf32_Phdr p32[phnum];
    Elf64_Phdr p64[phnum];
  } phdrs;

  xlateto.d_buf = &phdrs;
  xlateto.d_size = sizeof phdrs;

  /* Scan for PT_LOAD segments to find the total size of the file image.  */
  size_t contents_size = 0;
  GElf_Off segments_end = 0;
  GElf_Addr loadbase = ehdr_vma;
  bool found_base = false;
  switch (ehdr.e32.e_ident[EI_CLASS])
    {
      inline void handle_segment (GElf_Addr vaddr, GElf_Off offset,
				  GElf_Xword filesz, GElf_Xword align)
	{
	  GElf_Off segment_end = ((offset + filesz + align - 1) & -align);

	  if (segment_end > (GElf_Off) contents_size)
	    contents_size = segment_end;

	  if (!found_base && (offset & -align) == 0)
	    {
	      loadbase = ehdr_vma - (vaddr & -align);
	      found_base = true;
	    }

	  segments_end = offset + filesz;
	}

    case ELFCLASS32:
      if (elf32_xlatetom (&xlateto, &xlatefrom,
			  ehdr.e32.e_ident[EI_DATA]) == NULL)
	goto libelf_error;
      for (uint_fast16_t i = 0; i < phnum; ++i)
	if (phdrs.p32[i].p_type == PT_LOAD)
	  handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
			  phdrs.p32[i].p_filesz, phdrs.p32[i].p_align);
      break;

    case ELFCLASS64:
      if (elf64_xlatetom (&xlateto, &xlatefrom,
			  ehdr.e64.e_ident[EI_DATA]) == NULL)
	goto libelf_error;
      for (uint_fast16_t i = 0; i < phnum; ++i)
	if (phdrs.p64[i].p_type == PT_LOAD)
	  handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
			  phdrs.p64[i].p_filesz, phdrs.p64[i].p_align);
      break;

    default:
      abort ();
      break;
    }

  /* Trim the last segment so we don't bother with zeros in the last page
     that are off the end of the file.  However, if the extra bit in that
     page includes the section headers, keep them.  */
  if ((GElf_Off) contents_size > segments_end
      && (GElf_Off) contents_size >= shdrs_end)
    {
      contents_size = segments_end;
      if ((GElf_Off) contents_size < shdrs_end)
	contents_size = shdrs_end;
    }
  else
    contents_size = segments_end;

  free (buffer);

  /* Now we know the size of the whole image we want read in.  */
  buffer = calloc (1, contents_size);
  if (buffer == NULL)
    goto no_memory;

  switch (ehdr.e32.e_ident[EI_CLASS])
    {
      inline bool handle_segment (GElf_Addr vaddr, GElf_Off offset,
				  GElf_Xword filesz, GElf_Xword align)
	{
	  GElf_Off start = offset & -align;
	  GElf_Off end = (offset + filesz + align - 1) & -align;
	  if (end > (GElf_Off) contents_size)
	    end = contents_size;
	  nread = (*read_memory) (arg, buffer + start,
				  (loadbase + vaddr) & -align,
				  end - start, end - start);
	  return nread <= 0;
	}

    case ELFCLASS32:
      for (uint_fast16_t i = 0; i < phnum; ++i)
	if (phdrs.p32[i].p_type == PT_LOAD)
	  if (handle_segment (phdrs.p32[i].p_vaddr, phdrs.p32[i].p_offset,
			      phdrs.p32[i].p_filesz, phdrs.p32[i].p_align))
	    goto read_error;

      /* If the segments visible in memory didn't include the section
	 headers, then clear them from the file header.  */
      if (contents_size < shdrs_end)
	{
	  ehdr.e32.e_shoff = 0;
	  ehdr.e32.e_shnum = 0;
	  ehdr.e32.e_shstrndx = 0;
	}

      /* This will normally have been in the first PT_LOAD segment.  But it
	 conceivably could be missing, and we might have just changed it.  */
      xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
      xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e32;
      xlatefrom.d_buf = &ehdr.e32;
      xlateto.d_buf = buffer;
      if (elf32_xlatetof (&xlateto, &xlatefrom,
			  ehdr.e32.e_ident[EI_DATA]) == NULL)
	goto libelf_error;
      break;

    case ELFCLASS64:
      for (uint_fast16_t i = 0; i < phnum; ++i)
	if (phdrs.p32[i].p_type == PT_LOAD)
	  if (handle_segment (phdrs.p64[i].p_vaddr, phdrs.p64[i].p_offset,
			      phdrs.p64[i].p_filesz, phdrs.p64[i].p_align))
	    goto read_error;

      /* If the segments visible in memory didn't include the section
	 headers, then clear them from the file header.  */
      if (contents_size < shdrs_end)
	{
	  ehdr.e64.e_shoff = 0;
	  ehdr.e64.e_shnum = 0;
	  ehdr.e64.e_shstrndx = 0;
	}

      /* This will normally have been in the first PT_LOAD segment.  But it
	 conceivably could be missing, and we might have just changed it.  */
      xlatefrom.d_type = xlateto.d_type = ELF_T_EHDR;
      xlatefrom.d_size = xlateto.d_size = sizeof ehdr.e64;
      xlatefrom.d_buf = &ehdr.e64;
      xlateto.d_buf = buffer;
      if (elf64_xlatetof (&xlateto, &xlatefrom,
			  ehdr.e64.e_ident[EI_DATA]) == NULL)
	goto libelf_error;
      break;

    default:
      abort ();
      break;
    }
int
dwfl_module_getsrc_file (Dwfl_Module *mod,
			 const char *fname, int lineno, int column,
			 Dwfl_Line ***srcsp, size_t *nsrcs)
{
  if (mod == NULL)
    return -1;

  if (mod->dw == NULL)
    {
      Dwarf_Addr bias;
      if (INTUSE(dwfl_module_getdwarf) (mod, &bias) == NULL)
	return -1;
    }

  bool is_basename = strchr (fname, '/') == NULL;

  size_t max_match = *nsrcs ?: ~0u;
  size_t act_match = *nsrcs;
  size_t cur_match = 0;
  Dwfl_Line **match = *nsrcs == 0 ? NULL : *srcsp;

  struct dwfl_cu *cu = NULL;
  Dwfl_Error error;
  while ((error = __libdwfl_nextcu (mod, cu, &cu)) == DWFL_E_NOERROR
	 && cu != NULL
	 && (error = __libdwfl_cu_getsrclines (cu)) == DWFL_E_NOERROR)
    {
      inline const char *INTUSE(dwarf_line_file) (const Dwarf_Line *line)
	{
	  return line->files->info[line->file].name;
	}
      inline Dwarf_Line *dwfl_line (const Dwfl_Line *line)
	{
	  return &dwfl_linecu (line)->die.cu->lines->info[line->idx];
	}
      inline const char *dwfl_line_file (const Dwfl_Line *line)
	{
	  return INTUSE(dwarf_line_file) (dwfl_line (line));
	}

      /* Search through all the line number records for a matching
	 file and line/column number.  If any of the numbers is zero,
	 no match is performed.  */
      const char *lastfile = NULL;
      bool lastmatch = false;
      for (size_t cnt = 0; cnt < cu->die.cu->lines->nlines; ++cnt)
	{
	  Dwarf_Line *line = &cu->die.cu->lines->info[cnt];

	  if (unlikely (line->file >= line->files->nfiles))
	    {
	      __libdwfl_seterrno (DWFL_E (LIBDW, DWARF_E_INVALID_DWARF));
	      return -1;
	    }
	  else
	    {
	      const char *file = INTUSE(dwarf_line_file) (line);
	      if (file != lastfile)
		{
		  /* Match the name with the name the user provided.  */
		  lastfile = file;
		  lastmatch = !strcmp (is_basename ? basename (file) : file,
				       fname);
		}
	    }
	  if (!lastmatch)
	    continue;

	  /* See whether line and possibly column match.  */
	  if (lineno != 0
	      && (lineno > line->line
		  || (column != 0 && column > line->column)))
	    /* Cannot match.  */
	    continue;

	  /* Determine whether this is the best match so far.  */
	  size_t inner;
	  for (inner = 0; inner < cur_match; ++inner)
	    if (dwfl_line_file (match[inner])
		== INTUSE(dwarf_line_file) (line))
	      break;
	  if (inner < cur_match
	      && (dwfl_line (match[inner])->line != line->line
		  || dwfl_line (match[inner])->line != lineno
		  || (column != 0
		      && (dwfl_line (match[inner])->column != line->column
			  || dwfl_line (match[inner])->column != column))))
	    {
	      /* We know about this file already.  If this is a better
		 match for the line number, use it.  */
	      if (dwfl_line (match[inner])->line >= line->line
		  && (dwfl_line (match[inner])->line != line->line
		      || dwfl_line (match[inner])->column >= line->column))
		/* Use the new line.  Otherwise the old one.  */
		match[inner] = &cu->lines->idx[cnt];
	      continue;
	    }

	  if (cur_match < max_match)
	    {
	      if (cur_match == act_match)
		{
		  /* Enlarge the array for the results.  */
		  act_match += 10;
		  Dwfl_Line **newp = realloc (match,
					      act_match
					      * sizeof (Dwfl_Line *));
		  if (newp == NULL)
		    {
		      free (match);
		      __libdwfl_seterrno (DWFL_E_NOMEM);
		      return -1;
		    }
		  match = newp;
		}

	      match[cur_match++] = &cu->lines->idx[cnt];
	    }
	}
    }

  if (cur_match > 0)
    {
      assert (*nsrcs == 0 || *srcsp == match);

      *nsrcs = cur_match;
      *srcsp = match;

      return 0;
    }

  __libdwfl_seterrno (DWFL_E_NO_MATCH);
  return -1;
}
示例#21
0
internal_function
__libdwfl_getsym (Dwfl_Module *mod, int ndx, GElf_Sym *sym, GElf_Addr *addr,
		  GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *biasp,
		  bool *resolved, bool adjust_st_value)
{
  if (unlikely (mod == NULL))
    return NULL;

  if (unlikely (mod->symdata == NULL))
    {
      int result = INTUSE(dwfl_module_getsymtab) (mod);
      if (result < 0)
	return NULL;
    }

  /* All local symbols should come before all global symbols.  If we
     have an auxiliary table make sure all the main locals come first,
     then all aux locals, then all main globals and finally all aux globals.
     And skip the auxiliary table zero undefined entry.  */
  GElf_Word shndx;
  int tndx = ndx;
  int skip_aux_zero = (mod->syments > 0 && mod->aux_syments > 0) ? 1 : 0;
  Elf *elf;
  Elf_Data *symdata;
  Elf_Data *symxndxdata;
  Elf_Data *symstrdata;
  if (mod->aux_symdata == NULL
      || ndx < mod->first_global)
    {
      /* main symbol table (locals).  */
      tndx = ndx;
      elf = mod->symfile->elf;
      symdata = mod->symdata;
      symxndxdata = mod->symxndxdata;
      symstrdata = mod->symstrdata;
    }
  else if (ndx < mod->first_global + mod->aux_first_global - skip_aux_zero)
    {
      /* aux symbol table (locals).  */
      tndx = ndx - mod->first_global + skip_aux_zero;
      elf = mod->aux_sym.elf;
      symdata = mod->aux_symdata;
      symxndxdata = mod->aux_symxndxdata;
      symstrdata = mod->aux_symstrdata;
    }
  else if ((size_t) ndx < mod->syments + mod->aux_first_global - skip_aux_zero)
    {
      /* main symbol table (globals).  */
      tndx = ndx - mod->aux_first_global + skip_aux_zero;
      elf = mod->symfile->elf;
      symdata = mod->symdata;
      symxndxdata = mod->symxndxdata;
      symstrdata = mod->symstrdata;
    }
  else
    {
      /* aux symbol table (globals).  */
      tndx = ndx - mod->syments + skip_aux_zero;
      elf = mod->aux_sym.elf;
      symdata = mod->aux_symdata;
      symxndxdata = mod->aux_symxndxdata;
      symstrdata = mod->aux_symstrdata;
    }
  sym = gelf_getsymshndx (symdata, symxndxdata, tndx, sym, &shndx);

  if (unlikely (sym == NULL))
    {
      __libdwfl_seterrno (DWFL_E_LIBELF);
      return NULL;
    }

  if (sym->st_shndx != SHN_XINDEX)
    shndx = sym->st_shndx;

  /* Figure out whether this symbol points into an SHF_ALLOC section.  */
  bool alloc = true;
  if ((shndxp != NULL || mod->e_type != ET_REL)
      && (sym->st_shndx == SHN_XINDEX
	  || (sym->st_shndx < SHN_LORESERVE && sym->st_shndx != SHN_UNDEF)))
    {
      GElf_Shdr shdr_mem;
      GElf_Shdr *shdr = gelf_getshdr (elf_getscn (elf, shndx), &shdr_mem);
      alloc = unlikely (shdr == NULL) || (shdr->sh_flags & SHF_ALLOC);
    }

  /* In case of an value in an allocated section the main Elf Ebl
     might know where the real value is (e.g. for function
     descriptors).  */

  char *ident;
  GElf_Addr st_value = sym->st_value & ebl_func_addr_mask (mod->ebl);
  *resolved = false;
  if (! adjust_st_value && mod->e_type != ET_REL && alloc
      && (GELF_ST_TYPE (sym->st_info) == STT_FUNC
	  || (GELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
	      && (ident = elf_getident (elf, NULL)) != NULL
	      && ident[EI_OSABI] == ELFOSABI_LINUX)))
    {
      if (likely (__libdwfl_module_getebl (mod) == DWFL_E_NOERROR))
	{
	  if (elf != mod->main.elf)
	    {
	      st_value = dwfl_adjusted_st_value (mod, elf, st_value);
	      st_value = dwfl_deadjust_st_value (mod, mod->main.elf, st_value);
	    }

	  *resolved = ebl_resolve_sym_value (mod->ebl, &st_value);
	  if (! *resolved)
	    st_value = sym->st_value;
	}
    }

  if (shndxp != NULL)
    /* Yield -1 in case of a non-SHF_ALLOC section.  */
    *shndxp = alloc ? shndx : (GElf_Word) -1;

  switch (sym->st_shndx)
    {
    case SHN_ABS:		/* XXX sometimes should use bias?? */
    case SHN_UNDEF:
    case SHN_COMMON:
      break;

    default:
      if (mod->e_type == ET_REL)
	{
	  /* In an ET_REL file, the symbol table values are relative
	     to the section, not to the module's load base.  */
	  size_t symshstrndx = SHN_UNDEF;
	  Dwfl_Error result = __libdwfl_relocate_value (mod, elf,
							&symshstrndx,
							shndx, &st_value);
	  if (unlikely (result != DWFL_E_NOERROR))
	    {
	      __libdwfl_seterrno (result);
	      return NULL;
	    }
	}
      else if (alloc)
	/* Apply the bias to the symbol value.  */
	st_value = dwfl_adjusted_st_value (mod,
					   *resolved ? mod->main.elf : elf,
					   st_value);
      break;
    }

  if (adjust_st_value)
    sym->st_value = st_value;

  if (addr != NULL)
    *addr = st_value;

  if (unlikely (sym->st_name >= symstrdata->d_size))
    {
      __libdwfl_seterrno (DWFL_E_BADSTROFF);
      return NULL;
    }
  if (elfp)
    *elfp = elf;
  if (biasp)
    *biasp = dwfl_adjusted_st_value (mod, elf, 0);
  return (const char *) symstrdata->d_buf + sym->st_name;
}