Blame libdwelf/dwelf_strtab.c

Packit Service 97d2fb
/* ELF/DWARF string table handling.
Packit Service 97d2fb
   Copyright (C) 2000, 2001, 2002, 2005, 2016 Red Hat, Inc.
Packit Service 97d2fb
   This file is part of elfutils.
Packit Service 97d2fb
   Written by Ulrich Drepper <drepper@redhat.com>, 2000.
Packit Service 97d2fb
Packit Service 97d2fb
   This file is free software; you can redistribute it and/or modify
Packit Service 97d2fb
   it under the terms of either
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU Lesser General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 3 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or
Packit Service 97d2fb
Packit Service 97d2fb
     * the GNU General Public License as published by the Free
Packit Service 97d2fb
       Software Foundation; either version 2 of the License, or (at
Packit Service 97d2fb
       your option) any later version
Packit Service 97d2fb
Packit Service 97d2fb
   or both in parallel, as here.
Packit Service 97d2fb
Packit Service 97d2fb
   elfutils is distributed in the hope that it will be useful, but
Packit Service 97d2fb
   WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 97d2fb
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 97d2fb
   General Public License for more details.
Packit Service 97d2fb
Packit Service 97d2fb
   You should have received copies of the GNU General Public License and
Packit Service 97d2fb
   the GNU Lesser General Public License along with this program.  If
Packit Service 97d2fb
   not, see <http://www.gnu.org/licenses/>.  */
Packit Service 97d2fb
Packit Service 97d2fb
#ifdef HAVE_CONFIG_H
Packit Service 97d2fb
# include <config.h>
Packit Service 97d2fb
#endif
Packit Service 97d2fb
Packit Service 97d2fb
#include <assert.h>
Packit Service 97d2fb
#include <inttypes.h>
Packit Service 97d2fb
#include <libelf.h>
Packit Service 97d2fb
#include <stddef.h>
Packit Service 97d2fb
#include <stdlib.h>
Packit Service 97d2fb
#include <string.h>
Packit Service 97d2fb
#include <unistd.h>
Packit Service 97d2fb
Packit Service 97d2fb
#include "libdwelfP.h"
Packit Service 97d2fb
#include <system.h>
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
struct Dwelf_Strent
Packit Service 97d2fb
{
Packit Service 97d2fb
  const char *string;
Packit Service 97d2fb
  size_t len;
Packit Service 97d2fb
  struct Dwelf_Strent *next;
Packit Service 97d2fb
  struct Dwelf_Strent *left;
Packit Service 97d2fb
  struct Dwelf_Strent *right;
Packit Service 97d2fb
  size_t offset;
Packit Service 97d2fb
  char reverse[0];
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
struct memoryblock
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct memoryblock *next;
Packit Service 97d2fb
  char memory[0];
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
struct Dwelf_Strtab
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct Dwelf_Strent *root;
Packit Service 97d2fb
  struct memoryblock *memory;
Packit Service 97d2fb
  char *backp;
Packit Service 97d2fb
  size_t left;
Packit Service 97d2fb
  size_t total;
Packit Service 97d2fb
  bool nullstr;
Packit Service 97d2fb
Packit Service 97d2fb
  struct Dwelf_Strent null;
Packit Service 97d2fb
};
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Cache for the pagesize.  */
Packit Service 97d2fb
static size_t ps;
Packit Service 97d2fb
/* We correct this value a bit so that `malloc' is not allocating more
Packit Service 97d2fb
   than a page. */
Packit Service 97d2fb
#define MALLOC_OVERHEAD (2 * sizeof (void *))
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
Dwelf_Strtab *
Packit Service 97d2fb
dwelf_strtab_init (bool nullstr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (ps == 0)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      ps = sysconf (_SC_PAGESIZE);
Packit Service 97d2fb
      assert (sizeof (struct memoryblock) < ps - MALLOC_OVERHEAD);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  Dwelf_Strtab *ret
Packit Service 97d2fb
    = (Dwelf_Strtab *) calloc (1, sizeof (struct Dwelf_Strtab));
Packit Service 97d2fb
  if (ret != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      ret->nullstr = nullstr;
Packit Service 97d2fb
Packit Service 97d2fb
      if (nullstr)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  ret->null.len = 1;
Packit Service 97d2fb
	  ret->null.string = "";
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  return ret;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static int
Packit Service 97d2fb
morememory (Dwelf_Strtab *st, size_t len)
Packit Service 97d2fb
{
Packit Service 97d2fb
  size_t overhead = offsetof (struct memoryblock, memory);
Packit Service 97d2fb
  len += overhead + MALLOC_OVERHEAD;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Allocate nearest multiple of pagesize >= len.  */
Packit Service 97d2fb
  len = ((len / ps) + (len % ps != 0)) * ps - MALLOC_OVERHEAD;
Packit Service 97d2fb
Packit Service 97d2fb
  struct memoryblock *newmem = (struct memoryblock *) malloc (len);
Packit Service 97d2fb
  if (newmem == NULL)
Packit Service 97d2fb
    return 1;
Packit Service 97d2fb
Packit Service 97d2fb
  newmem->next = st->memory;
Packit Service 97d2fb
  st->memory = newmem;
Packit Service 97d2fb
  st->backp = newmem->memory;
Packit Service 97d2fb
  st->left = len - overhead;
Packit Service 97d2fb
Packit Service 97d2fb
  return 0;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
void
Packit Service 97d2fb
dwelf_strtab_free (Dwelf_Strtab *st)
Packit Service 97d2fb
{
Packit Service 97d2fb
  struct memoryblock *mb = st->memory;
Packit Service 97d2fb
Packit Service 97d2fb
  while (mb != NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      void *old = mb;
Packit Service 97d2fb
      mb = mb->next;
Packit Service 97d2fb
      free (old);
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  free (st);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
static Dwelf_Strent *
Packit Service 97d2fb
newstring (Dwelf_Strtab *st, const char *str, size_t len)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Compute the amount of padding needed to make the structure aligned.  */
Packit Service 97d2fb
  size_t align = ((__alignof__ (struct Dwelf_Strent)
Packit Service 97d2fb
		   - (((uintptr_t) st->backp)
Packit Service 97d2fb
		      & (__alignof__ (struct Dwelf_Strent) - 1)))
Packit Service 97d2fb
		  & (__alignof__ (struct Dwelf_Strent) - 1));
Packit Service 97d2fb
Packit Service 97d2fb
  /* Make sure there is enough room in the memory block.  */
Packit Service 97d2fb
  if (st->left < align + sizeof (struct Dwelf_Strent) + len)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      if (morememory (st, sizeof (struct Dwelf_Strent) + len))
Packit Service 97d2fb
	return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
      align = 0;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Create the reserved string.  */
Packit Service 97d2fb
  Dwelf_Strent *newstr = (Dwelf_Strent *) (st->backp + align);
Packit Service 97d2fb
  newstr->string = str;
Packit Service 97d2fb
  newstr->len = len;
Packit Service 97d2fb
  newstr->next = NULL;
Packit Service 97d2fb
  newstr->left = NULL;
Packit Service 97d2fb
  newstr->right = NULL;
Packit Service 97d2fb
  newstr->offset = 0;
Packit Service 97d2fb
  for (int i = len - 2; i >= 0; --i)
Packit Service 97d2fb
    newstr->reverse[i] = str[len - 2 - i];
Packit Service 97d2fb
  newstr->reverse[len - 1] = '\0';
Packit Service 97d2fb
  st->backp += align + sizeof (struct Dwelf_Strent) + len;
Packit Service 97d2fb
  st->left -= align + sizeof (struct Dwelf_Strent) + len;
Packit Service 97d2fb
Packit Service 97d2fb
  return newstr;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* XXX This function should definitely be rewritten to use a balancing
Packit Service 97d2fb
   tree algorith (AVL, red-black trees).  For now a simple, correct
Packit Service 97d2fb
   implementation is enough.  */
Packit Service 97d2fb
static Dwelf_Strent **
Packit Service 97d2fb
searchstring (Dwelf_Strent **sep, Dwelf_Strent *newstr)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* More strings?  */
Packit Service 97d2fb
  if (*sep == NULL)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      *sep = newstr;
Packit Service 97d2fb
      return sep;
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  /* Compare the strings.  */
Packit Service 97d2fb
  int cmpres = memcmp ((*sep)->reverse, newstr->reverse,
Packit Service 97d2fb
		       MIN ((*sep)->len, newstr->len) - 1);
Packit Service 97d2fb
  if (cmpres == 0)
Packit Service 97d2fb
    /* We found a matching string.  */
Packit Service 97d2fb
    return sep;
Packit Service 97d2fb
  else if (cmpres > 0)
Packit Service 97d2fb
    return searchstring (&(*sep)->left, newstr);
Packit Service 97d2fb
  else
Packit Service 97d2fb
    return searchstring (&(*sep)->right, newstr);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
/* Add new string.  The actual string is assumed to be permanent.  */
Packit Service 97d2fb
static Dwelf_Strent *
Packit Service 97d2fb
strtab_add (Dwelf_Strtab *st, const char *str, size_t len)
Packit Service 97d2fb
{
Packit Service 97d2fb
  /* Make sure all "" strings get offset 0 but only if the table was
Packit Service 97d2fb
     created with a special null entry in mind.  */
Packit Service 97d2fb
  if (len == 1 && st->null.string != NULL)
Packit Service 97d2fb
    return &st->null;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Allocate memory for the new string and its associated information.  */
Packit Service 97d2fb
  Dwelf_Strent *newstr = newstring (st, str, len);
Packit Service 97d2fb
  if (newstr == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Search in the array for the place to insert the string.  If there
Packit Service 97d2fb
     is no string with matching prefix and no string with matching
Packit Service 97d2fb
     leading substring, create a new entry.  */
Packit Service 97d2fb
  Dwelf_Strent **sep = searchstring (&st->root, newstr);
Packit Service 97d2fb
  if (*sep != newstr)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      /* This is not the same entry.  This means we have a prefix match.  */
Packit Service 97d2fb
      if ((*sep)->len > newstr->len)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* Check whether we already know this string.  */
Packit Service 97d2fb
	  for (Dwelf_Strent *subs = (*sep)->next; subs != NULL;
Packit Service 97d2fb
	       subs = subs->next)
Packit Service 97d2fb
	    if (subs->len == newstr->len)
Packit Service 97d2fb
	      {
Packit Service 97d2fb
		/* We have an exact match with a substring.  Free the memory
Packit Service 97d2fb
		   we allocated.  */
Packit Service 97d2fb
		st->left += st->backp - (char *) newstr;
Packit Service 97d2fb
		st->backp = (char *) newstr;
Packit Service 97d2fb
Packit Service 97d2fb
		return subs;
Packit Service 97d2fb
	      }
Packit Service 97d2fb
Packit Service 97d2fb
	  /* We have a new substring.  This means we don't need the reverse
Packit Service 97d2fb
	     string of this entry anymore.  */
Packit Service 97d2fb
	  st->backp -= newstr->len;
Packit Service 97d2fb
	  st->left += newstr->len;
Packit Service 97d2fb
Packit Service 97d2fb
	  newstr->next = (*sep)->next;
Packit Service 97d2fb
	  (*sep)->next = newstr;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else if ((*sep)->len != newstr->len)
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* When we get here it means that the string we are about to
Packit Service 97d2fb
	     add has a common prefix with a string we already have but
Packit Service 97d2fb
	     it is longer.  In this case we have to put it first.  */
Packit Service 97d2fb
	  st->total += newstr->len - (*sep)->len;
Packit Service 97d2fb
	  newstr->next = *sep;
Packit Service 97d2fb
	  newstr->left = (*sep)->left;
Packit Service 97d2fb
	  newstr->right = (*sep)->right;
Packit Service 97d2fb
	  *sep = newstr;
Packit Service 97d2fb
	}
Packit Service 97d2fb
      else
Packit Service 97d2fb
	{
Packit Service 97d2fb
	  /* We have an exact match.  Free the memory we allocated.  */
Packit Service 97d2fb
	  st->left += st->backp - (char *) newstr;
Packit Service 97d2fb
	  st->backp = (char *) newstr;
Packit Service 97d2fb
Packit Service 97d2fb
	  newstr = *sep;
Packit Service 97d2fb
	}
Packit Service 97d2fb
    }
Packit Service 97d2fb
  else
Packit Service 97d2fb
    st->total += newstr->len;
Packit Service 97d2fb
Packit Service 97d2fb
  return newstr;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Dwelf_Strent *
Packit Service 97d2fb
dwelf_strtab_add (Dwelf_Strtab *st, const char *str)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return strtab_add (st, str, strlen (str) + 1);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Dwelf_Strent *
Packit Service 97d2fb
dwelf_strtab_add_len (Dwelf_Strtab *st, const char *str, size_t len)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return strtab_add (st, str, len);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
static void
Packit Service 97d2fb
copystrings (Dwelf_Strent *nodep, char **freep, size_t *offsetp)
Packit Service 97d2fb
{
Packit Service 97d2fb
  if (nodep->left != NULL)
Packit Service 97d2fb
    copystrings (nodep->left, freep, offsetp);
Packit Service 97d2fb
Packit Service 97d2fb
  /* Process the current node.  */
Packit Service 97d2fb
  nodep->offset = *offsetp;
Packit Service 97d2fb
  *freep = (char *) mempcpy (*freep, nodep->string, nodep->len);
Packit Service 97d2fb
  *offsetp += nodep->len;
Packit Service 97d2fb
Packit Service 97d2fb
  for (Dwelf_Strent *subs = nodep->next; subs != NULL; subs = subs->next)
Packit Service 97d2fb
    {
Packit Service 97d2fb
      assert (subs->len < nodep->len);
Packit Service 97d2fb
      subs->offset = nodep->offset + nodep->len - subs->len;
Packit Service 97d2fb
      assert (subs->offset != 0 || subs->string[0] == '\0');
Packit Service 97d2fb
    }
Packit Service 97d2fb
Packit Service 97d2fb
  if (nodep->right != NULL)
Packit Service 97d2fb
    copystrings (nodep->right, freep, offsetp);
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
Elf_Data *
Packit Service 97d2fb
dwelf_strtab_finalize (Dwelf_Strtab *st, Elf_Data *data)
Packit Service 97d2fb
{
Packit Service 97d2fb
  size_t nulllen = st->nullstr ? 1 : 0;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Fill in the information.  */
Packit Service 97d2fb
  data->d_buf = malloc (st->total + nulllen);
Packit Service 97d2fb
  if (data->d_buf == NULL)
Packit Service 97d2fb
    return NULL;
Packit Service 97d2fb
Packit Service 97d2fb
  /* The first byte must always be zero if we created the table with a
Packit Service 97d2fb
     null string.  */
Packit Service 97d2fb
  if (st->nullstr)
Packit Service 97d2fb
    *((char *) data->d_buf) = '\0';
Packit Service 97d2fb
Packit Service 97d2fb
  data->d_type = ELF_T_BYTE;
Packit Service 97d2fb
  data->d_size = st->total + nulllen;
Packit Service 97d2fb
  data->d_off = 0;
Packit Service 97d2fb
  data->d_align = 1;
Packit Service 97d2fb
  data->d_version = EV_CURRENT;
Packit Service 97d2fb
Packit Service 97d2fb
  /* Now run through the tree and add all the string while also updating
Packit Service 97d2fb
     the offset members of the elfstrent records.  */
Packit Service 97d2fb
  char *endp = (char *) data->d_buf + nulllen;
Packit Service 97d2fb
  size_t copylen = nulllen;
Packit Service 97d2fb
  if (st->root)
Packit Service 97d2fb
    copystrings (st->root, &endp, &copylen);
Packit Service 97d2fb
  assert (copylen == st->total + nulllen);
Packit Service 97d2fb
Packit Service 97d2fb
  return data;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
size_t
Packit Service 97d2fb
dwelf_strent_off (Dwelf_Strent *se)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return se->offset;
Packit Service 97d2fb
}
Packit Service 97d2fb
Packit Service 97d2fb
Packit Service 97d2fb
const char *
Packit Service 97d2fb
dwelf_strent_str (Dwelf_Strent *se)
Packit Service 97d2fb
{
Packit Service 97d2fb
  return se->string;
Packit Service 97d2fb
}