Blame libdwfl/segment.c

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