Blame libdwfl/segment.c

Packit Service 97d2fb
/* Manage address space lookup table for libdwfl.
Packit Service 97d2fb
   Copyright (C) 2008, 2009, 2010, 2013, 2015 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include "libdwflP.h"
Packit Service 97d2fb
Packit Service 97d2fb
GElf_Addr
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_segment_start (Dwfl *dwfl, GElf_Addr start)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (dwfl->segment_align > 1)
Packit Service 97d2fb
    start &= -dwfl->segment_align;
Packit Service 97d2fb
  return start;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
GElf_Addr
Packit Service 97d2fb
internal_function
Packit Service 97d2fb
__libdwfl_segment_end (Dwfl *dwfl, GElf_Addr end)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (dwfl->segment_align > 1)
Packit Service 97d2fb
    end = (end + dwfl->segment_align - 1) & -dwfl->segment_align;
Packit Service 97d2fb
  return end;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
insert (Dwfl *dwfl, size_t i, GElf_Addr start, GElf_Addr end, int segndx)
Packit Service 97d2fb
{
Packit Service 97d2fb
  bool need_start = (i == 0 || dwfl->lookup_addr[i - 1] != start);
Packit Service 97d2fb
  bool need_end = (i + 1 >= dwfl->lookup_elts
Packit Service 97d2fb
		   || dwfl->lookup_addr[i + 1] != end);
Packit Service 97d2fb
  size_t need = need_start + need_end;
Packit Service 97d2fb
  if (need == 0)
Packit Service 97d2fb
    return false;
Packit Service 97d2fb
Packit Service 97d2fb
  if (dwfl->lookup_alloc - dwfl->lookup_elts < need)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      size_t n = dwfl->lookup_alloc == 0 ? 16 : dwfl->lookup_alloc * 2;
Packit Service 97d2fb
      GElf_Addr *naddr = realloc (dwfl->lookup_addr, sizeof naddr[0] * n);
Packit Service 97d2fb
      if (unlikely (naddr == NULL))
Packit Service 97d2fb
	return true;
Packit Service 97d2fb
      int *nsegndx = realloc (dwfl->lookup_segndx, sizeof nsegndx[0] * n);
Packit Service 97d2fb
      if (unlikely (nsegndx == NULL))
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  if (naddr != dwfl->lookup_addr)
Packit Service 97d2fb
	    free (naddr);
Packit Service 97d2fb
	  return true;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      dwfl->lookup_alloc = n;
Packit Service 97d2fb
      dwfl->lookup_addr = naddr;
Packit Service 97d2fb
      dwfl->lookup_segndx = nsegndx;
Packit Service 97d2fb
Packit Service 97d2fb
      if (dwfl->lookup_module != NULL)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Make sure this array is big enough too.  */
Packit Service 97d2fb
	  Dwfl_Module **old = dwfl->lookup_module;
Packit Service 97d2fb
	  dwfl->lookup_module = realloc (dwfl->lookup_module,
Packit Service 97d2fb
					 sizeof dwfl->lookup_module[0] * n);
Packit Service 97d2fb
	  if (unlikely (dwfl->lookup_module == NULL))
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      free (old);
Packit Service 97d2fb
	      return true;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (i < dwfl->lookup_elts))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      const size_t move = dwfl->lookup_elts - i;
Packit Service 97d2fb
      memmove (&dwfl->lookup_addr[i + need], &dwfl->lookup_addr[i],
Packit Service 97d2fb
	       move * sizeof dwfl->lookup_addr[0]);
Packit Service 97d2fb
      memmove (&dwfl->lookup_segndx[i + need], &dwfl->lookup_segndx[i],
Packit Service 97d2fb
	       move * sizeof dwfl->lookup_segndx[0]);
Packit Service 97d2fb
      if (dwfl->lookup_module != NULL)
Packit Service 97d2fb
	memmove (&dwfl->lookup_module[i + need], &dwfl->lookup_module[i],
Packit Service 97d2fb
		 move * sizeof dwfl->lookup_module[0]);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (need_start)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      dwfl->lookup_addr[i] = start;
Packit Service 97d2fb
      dwfl->lookup_segndx[i] = segndx;
Packit Service 97d2fb
      if (dwfl->lookup_module != NULL)
Packit Service 97d2fb
	dwfl->lookup_module[i] = NULL;
Packit Service 97d2fb
      ++i;
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    dwfl->lookup_segndx[i - 1] = segndx;
Packit Service 97d2fb
Packit Service 97d2fb
  if (need_end)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      dwfl->lookup_addr[i] = end;
Packit Service 97d2fb
      dwfl->lookup_segndx[i] = -1;
Packit Service 97d2fb
      if (dwfl->lookup_module != NULL)
Packit Service 97d2fb
	dwfl->lookup_module[i] = NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  dwfl->lookup_elts += need;
Packit Service 97d2fb
Packit Service 97d2fb
  return false;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
lookup (Dwfl *dwfl, GElf_Addr address, int hint)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (hint >= 0
Packit Service 97d2fb
      && address >= dwfl->lookup_addr[hint]
Packit Service 97d2fb
      && ((size_t) hint + 1 == dwfl->lookup_elts
Packit Service 97d2fb
	  || address < dwfl->lookup_addr[hint + 1]))
Packit Service 97d2fb
    return hint;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Do binary search on the array indexed by module load address.  */
Packit Service 97d2fb
  size_t l = 0, u = dwfl->lookup_elts;
Packit Service 97d2fb
  while (l < u)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      size_t idx = (l + u) / 2;
Packit Service 97d2fb
      if (address < dwfl->lookup_addr[idx])
Packit Service 97d2fb
	u = idx;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  l = idx + 1;
Packit Service 97d2fb
	  if (l == dwfl->lookup_elts || address < dwfl->lookup_addr[l])
Packit Service 97d2fb
	    return idx;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return -1;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static bool
Packit Service 97d2fb
reify_segments (Dwfl *dwfl)
Packit Service 97d2fb
{
Packit Service 97d2fb
  int hint = -1;
Packit Service 97d2fb
  int highest = -1;
Packit Service 97d2fb
  bool fixup = false;
Packit Service 97d2fb
  for (Dwfl_Module *mod = dwfl->modulelist; mod != NULL; mod = mod->next)
Packit Service 97d2fb
    if (! mod->gc)
Packit Service 97d2fb
      {
Packit Service 97d2fb
	const GElf_Addr start = __libdwfl_segment_start (dwfl, mod->low_addr);
Packit Service 97d2fb
	const GElf_Addr end = __libdwfl_segment_end (dwfl, mod->high_addr);
Packit Service 97d2fb
	bool resized = false;
Packit Service 97d2fb
Packit Service 97d2fb
	int idx = lookup (dwfl, start, hint);
Packit Service 97d2fb
	if (unlikely (idx < 0))
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    /* Module starts below any segment.  Insert a low one.  */
Packit Service 97d2fb
	    if (unlikely (insert (dwfl, 0, start, end, -1)))
Packit Service 97d2fb
	      return true;
Packit Service 97d2fb
	    idx = 0;
Packit Service 97d2fb
	    resized = true;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else if (dwfl->lookup_addr[idx] > start)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    /* The module starts in the middle of this segment.  Split it.  */
Packit Service 97d2fb
	    if (unlikely (insert (dwfl, idx + 1, start, end,
Packit Service 97d2fb
				  dwfl->lookup_segndx[idx])))
Packit Service 97d2fb
	      return true;
Packit Service 97d2fb
	    ++idx;
Packit Service 97d2fb
	    resized = true;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
	else if (dwfl->lookup_addr[idx] < start)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    /* The module starts past the end of this segment.
Packit Service 97d2fb
	       Add a new one.  */
Packit Service 97d2fb
	    if (unlikely (insert (dwfl, idx + 1, start, end, -1)))
Packit Service 97d2fb
	      return true;
Packit Service 97d2fb
	    ++idx;
Packit Service 97d2fb
	    resized = true;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
Packit Service 97d2fb
	if ((size_t) idx + 1 < dwfl->lookup_elts
Packit Service 97d2fb
	    && end < dwfl->lookup_addr[idx + 1])
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    /* The module ends in the middle of this segment.  Split it.  */
Packit Service 97d2fb
	    if (unlikely (insert (dwfl, idx + 1,
Packit Service 97d2fb
				  end, dwfl->lookup_addr[idx + 1], -1)))
Packit Service 97d2fb
	      return true;
Packit Service 97d2fb
	    resized = true;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
Packit Service 97d2fb
	if (dwfl->lookup_module == NULL)
Packit Service 97d2fb
	  {
Packit Service 97d2fb
	    dwfl->lookup_module = calloc (dwfl->lookup_alloc,
Packit Service 97d2fb
					  sizeof dwfl->lookup_module[0]);
Packit Service 97d2fb
	    if (unlikely (dwfl->lookup_module == NULL))
Packit Service 97d2fb
	      return true;
Packit Service 97d2fb
	  }
Packit Service 97d2fb
Packit Service 97d2fb
	/* Cache a backpointer in the module.  */
Packit Service 97d2fb
	mod->segment = idx;
Packit Service 97d2fb
Packit Service 97d2fb
	/* Put MOD in the table for each segment that's inside it.  */
Packit Service 97d2fb
	do
Packit Service 97d2fb
	  dwfl->lookup_module[idx++] = mod;
Packit Service 97d2fb
	while ((size_t) idx < dwfl->lookup_elts
Packit Service 97d2fb
	       && dwfl->lookup_addr[idx] < end);
Packit Service 97d2fb
	assert (dwfl->lookup_module[mod->segment] == mod);
Packit Service 97d2fb
Packit Service 97d2fb
	if (resized && idx - 1 >= highest)
Packit Service 97d2fb
	  /* Expanding the lookup tables invalidated backpointers
Packit Service 97d2fb
	     we've already stored.  Reset those ones.  */
Packit Service 97d2fb
	  fixup = true;
Packit Service 97d2fb
Packit Service 97d2fb
	highest = idx - 1;
Packit Service 97d2fb
	hint = (size_t) idx < dwfl->lookup_elts ? idx : -1;
Packit Service 97d2fb
      }
Packit Service 97d2fb
Packit Service 97d2fb
  if (fixup)
Packit Service 97d2fb
    /* Reset backpointer indices invalidated by table insertions.  */
Packit Service 97d2fb
    for (size_t idx = 0; idx < dwfl->lookup_elts; ++idx)
Packit Service 97d2fb
      if (dwfl->lookup_module[idx] != NULL)
Packit Service 97d2fb
	dwfl->lookup_module[idx]->segment = idx;
Packit Service 97d2fb
Packit Service 97d2fb
  return false;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_addrsegment (Dwfl *dwfl, Dwarf_Addr address, Dwfl_Module **mod)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (unlikely (dwfl == NULL))
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (dwfl->lookup_module == NULL)
Packit Service 97d2fb
      && mod != NULL
Packit Service 97d2fb
      && unlikely (reify_segments (dwfl)))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  int idx = lookup (dwfl, address, -1);
Packit Service 97d2fb
  if (likely (mod != NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (unlikely (idx < 0) || unlikely (dwfl->lookup_module == NULL))
Packit Service 97d2fb
	*mod = NULL;
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  *mod = dwfl->lookup_module[idx];
Packit Service 97d2fb
Packit Service 97d2fb
	  /* If this segment does not have a module, but the address is
Packit Service 97d2fb
	     the upper boundary of the previous segment's module, use that.  */
Packit Service 97d2fb
	  if (*mod == NULL && idx > 0 && dwfl->lookup_addr[idx] == address)
Packit Service 97d2fb
	    {
Packit Service 97d2fb
	      *mod = dwfl->lookup_module[idx - 1];
Packit Service 97d2fb
	      if (*mod != NULL && (*mod)->high_addr != address)
Packit Service 97d2fb
		*mod = NULL;
Packit Service 97d2fb
	    }
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (likely (idx >= 0))
Packit Service 97d2fb
    /* Translate internal segment table index to user segment index.  */
Packit Service 97d2fb
    idx = dwfl->lookup_segndx[idx];
Packit Service 97d2fb
Packit Service 97d2fb
  return idx;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_addrsegment)
Packit Service 97d2fb
Packit Service 97d2fb
int
Packit Service 97d2fb
dwfl_report_segment (Dwfl *dwfl, int ndx, const GElf_Phdr *phdr, GElf_Addr bias,
Packit Service 97d2fb
		     const void *ident)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* This was previously used for coalescing segments, but it was buggy since
Packit Service 97d2fb
     day one.  We don't use it anymore.  */
Packit Service 97d2fb
  (void)ident;
Packit Service 97d2fb
Packit Service 97d2fb
  if (dwfl == NULL)
Packit Service 97d2fb
    return -1;
Packit Service 97d2fb
Packit Service 97d2fb
  if (ndx < 0)
Packit Service 97d2fb
    ndx = dwfl->next_segndx;
Packit Service 97d2fb
Packit Service 97d2fb
  if (phdr->p_align > 1 && (dwfl->segment_align <= 1 ||
Packit Service 97d2fb
			    phdr->p_align < dwfl->segment_align))
Packit Service 97d2fb
    dwfl->segment_align = phdr->p_align;
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (dwfl->lookup_module != NULL))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      free (dwfl->lookup_module);
Packit Service 97d2fb
      dwfl->lookup_module = NULL;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  GElf_Addr start = __libdwfl_segment_start (dwfl, bias + phdr->p_vaddr);
Packit Service 97d2fb
  GElf_Addr end = __libdwfl_segment_end (dwfl,
Packit Service 97d2fb
					 bias + phdr->p_vaddr + phdr->p_memsz);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Normally just appending keeps us sorted.  */
Packit Service 97d2fb
Packit Service 97d2fb
  size_t i = dwfl->lookup_elts;
Packit Service 97d2fb
  while (i > 0 && unlikely (start < dwfl->lookup_addr[i - 1]))
Packit Service 97d2fb
    --i;
Packit Service 97d2fb
Packit Service 97d2fb
  if (unlikely (insert (dwfl, i, start, end, ndx)))
Packit Service 97d2fb
    {
Packit Service 97d2fb
      __libdwfl_seterrno (DWFL_E_NOMEM);
Packit Service 97d2fb
      return -1;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  dwfl->next_segndx = ndx + 1;
Packit Service 97d2fb
Packit Service 97d2fb
  return ndx;
Packit Service 97d2fb
}
Packit Service 97d2fb
INTDEF (dwfl_report_segment)