Home | History | Annotate | Download | only in libebl
      1 /* ELF string table handling.
      2    Copyright (C) 2000, 2001, 2002 Red Hat, Inc.
      3    This file is part of elfutils.
      4    Written by Ulrich Drepper <drepper (at) redhat.com>, 2000.
      5 
      6    This file is free software; you can redistribute it and/or modify
      7    it under the terms of either
      8 
      9      * the GNU Lesser General Public License as published by the Free
     10        Software Foundation; either version 3 of the License, or (at
     11        your option) any later version
     12 
     13    or
     14 
     15      * the GNU General Public License as published by the Free
     16        Software Foundation; either version 2 of the License, or (at
     17        your option) any later version
     18 
     19    or both in parallel, as here.
     20 
     21    elfutils is distributed in the hope that it will be useful, but
     22    WITHOUT ANY WARRANTY; without even the implied warranty of
     23    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     24    General Public License for more details.
     25 
     26    You should have received copies of the GNU General Public License and
     27    the GNU Lesser General Public License along with this program.  If
     28    not, see <http://www.gnu.org/licenses/>.  */
     29 
     30 #ifdef HAVE_CONFIG_H
     31 # include <config.h>
     32 #endif
     33 
     34 #include <assert.h>
     35 #include <inttypes.h>
     36 #include <libelf.h>
     37 #include <stddef.h>
     38 #include <stdlib.h>
     39 #include <string.h>
     40 #include <unistd.h>
     41 #include <wchar.h>
     42 #include <sys/param.h>
     43 
     44 #include "libebl.h"
     45 #include <system.h>
     46 
     47 #ifndef MIN
     48 # define MIN(a, b) ((a) < (b) ? (a) : (b))
     49 #endif
     50 
     51 
     52 struct Ebl_WStrent
     53 {
     54   const wchar_t *string;
     55   size_t len;
     56   struct Ebl_WStrent *next;
     57   struct Ebl_WStrent *left;
     58   struct Ebl_WStrent *right;
     59   size_t offset;
     60   wchar_t reverse[0];
     61 };
     62 
     63 
     64 struct memoryblock
     65 {
     66   struct memoryblock *next;
     67   char memory[0];
     68 };
     69 
     70 
     71 struct Ebl_WStrtab
     72 {
     73   struct Ebl_WStrent *root;
     74   struct memoryblock *memory;
     75   char *backp;
     76   size_t left;
     77   size_t total;
     78   bool nullstr;
     79 
     80   struct Ebl_WStrent null;
     81 };
     82 
     83 
     84 /* Cache for the pagesize.  We correct this value a bit so that `malloc'
     85    is not allocating more than a page.  */
     86 static size_t ps;
     87 
     88 
     89 struct Ebl_WStrtab *
     90 ebl_wstrtabinit (bool nullstr)
     91 {
     92   struct Ebl_WStrtab *ret;
     93 
     94   if (ps == 0)
     95     {
     96       ps = sysconf (_SC_PAGESIZE) - 2 * sizeof (void *);
     97       assert (sizeof (struct memoryblock) < ps);
     98     }
     99 
    100   ret = (struct Ebl_WStrtab *) calloc (1, sizeof (struct Ebl_WStrtab));
    101   if (ret != NULL)
    102     {
    103       ret->nullstr = nullstr;
    104       if (nullstr)
    105 	{
    106 	  ret->null.len = 1;
    107 	  ret->null.string = L"";
    108 	}
    109     }
    110   return ret;
    111 }
    112 
    113 
    114 static int
    115 morememory (struct Ebl_WStrtab *st, size_t len)
    116 {
    117   struct memoryblock *newmem;
    118 
    119   if (len < ps)
    120     len = ps;
    121   newmem = (struct memoryblock *) malloc (len);
    122   if (newmem == NULL)
    123     return 1;
    124 
    125   newmem->next = st->memory;
    126   st->memory = newmem;
    127   st->backp = newmem->memory;
    128   st->left = len - offsetof (struct memoryblock, memory);
    129 
    130   return 0;
    131 }
    132 
    133 
    134 void
    135 ebl_wstrtabfree (struct Ebl_WStrtab *st)
    136 {
    137   struct memoryblock *mb = st->memory;
    138 
    139   while (mb != NULL)
    140     {
    141       void *old = mb;
    142       mb = mb->next;
    143       free (old);
    144     }
    145 
    146   free (st);
    147 }
    148 
    149 
    150 static struct Ebl_WStrent *
    151 newstring (struct Ebl_WStrtab *st, const wchar_t *str, size_t len)
    152 {
    153   struct Ebl_WStrent *newstr;
    154   size_t align;
    155   int i;
    156 
    157   /* Compute the amount of padding needed to make the structure aligned.  */
    158   align = ((__alignof__ (struct Ebl_WStrent)
    159 	    - (((uintptr_t) st->backp)
    160 	       & (__alignof__ (struct Ebl_WStrent) - 1)))
    161 	   & (__alignof__ (struct Ebl_WStrent) - 1));
    162 
    163   /* Make sure there is enough room in the memory block.  */
    164   if (st->left < align + sizeof (struct Ebl_WStrent) + len * sizeof (wchar_t))
    165     {
    166       if (morememory (st,
    167 		      sizeof (struct Ebl_WStrent) + len * sizeof (wchar_t)))
    168 	return NULL;
    169 
    170       align = 0;
    171     }
    172 
    173   /* Create the reserved string.  */
    174   newstr = (struct Ebl_WStrent *) (st->backp + align);
    175   newstr->string = str;
    176   newstr->len = len;
    177   newstr->next = NULL;
    178   newstr->left = NULL;
    179   newstr->right = NULL;
    180   newstr->offset = 0;
    181   for (i = len - 2; i >= 0; --i)
    182     newstr->reverse[i] = str[len - 2 - i];
    183   newstr->reverse[len - 1] = L'\0';
    184   st->backp += align + sizeof (struct Ebl_WStrent) + len * sizeof (wchar_t);
    185   st->left -= align + sizeof (struct Ebl_WStrent) + len * sizeof (wchar_t);
    186 
    187   return newstr;
    188 }
    189 
    190 
    191 /* XXX This function should definitely be rewritten to use a balancing
    192    tree algorith (AVL, red-black trees).  For now a simple, correct
    193    implementation is enough.  */
    194 static struct Ebl_WStrent **
    195 searchstring (struct Ebl_WStrent **sep, struct Ebl_WStrent *newstr)
    196 {
    197   int cmpres;
    198 
    199   /* More strings?  */
    200   if (*sep == NULL)
    201     {
    202       *sep = newstr;
    203       return sep;
    204     }
    205 
    206   /* Compare the strings.  */
    207   cmpres = wmemcmp ((*sep)->reverse, newstr->reverse,
    208 		    MIN ((*sep)->len, newstr->len) - 1);
    209   if (cmpres == 0)
    210     /* We found a matching string.  */
    211     return sep;
    212   else if (cmpres > 0)
    213     return searchstring (&(*sep)->left, newstr);
    214   else
    215     return searchstring (&(*sep)->right, newstr);
    216 }
    217 
    218 
    219 /* Add new string.  The actual string is assumed to be permanent.  */
    220 struct Ebl_WStrent *
    221 ebl_wstrtabadd (struct Ebl_WStrtab *st, const wchar_t *str, size_t len)
    222 {
    223   struct Ebl_WStrent *newstr;
    224   struct Ebl_WStrent **sep;
    225 
    226   /* Compute the string length if the caller doesn't know it.  */
    227   if (len == 0)
    228     len = wcslen (str) + 1;
    229 
    230   /* Make sure all "" strings get offset 0 but only if the table was
    231      created with a special null entry in mind.  */
    232   if (len == 1 && st->null.string != NULL)
    233     return &st->null;
    234 
    235   /* Allocate memory for the new string and its associated information.  */
    236   newstr = newstring (st, str, len);
    237   if (newstr == NULL)
    238     return NULL;
    239 
    240   /* Search in the array for the place to insert the string.  If there
    241      is no string with matching prefix and no string with matching
    242      leading substring, create a new entry.  */
    243   sep = searchstring (&st->root, newstr);
    244   if (*sep != newstr)
    245     {
    246       /* This is not the same entry.  This means we have a prefix match.  */
    247       if ((*sep)->len > newstr->len)
    248 	{
    249 	  struct Ebl_WStrent *subs;
    250 
    251 	  /* Check whether we already know this string.  */
    252 	  for (subs = (*sep)->next; subs != NULL; subs = subs->next)
    253 	    if (subs->len == newstr->len)
    254 	      {
    255 		/* We have an exact match with a substring.  Free the memory
    256 		   we allocated.  */
    257 		st->left += st->backp - (char *) newstr;
    258 		st->backp = (char *) newstr;
    259 
    260 		return subs;
    261 	      }
    262 
    263 	  /* We have a new substring.  This means we don't need the reverse
    264 	     string of this entry anymore.  */
    265 	  st->backp -= newstr->len;
    266 	  st->left += newstr->len;
    267 
    268 	  newstr->next = (*sep)->next;
    269 	  (*sep)->next = newstr;
    270 	}
    271       else if ((*sep)->len != newstr->len)
    272 	{
    273 	  /* When we get here it means that the string we are about to
    274 	     add has a common prefix with a string we already have but
    275 	     it is longer.  In this case we have to put it first.  */
    276 	  st->total += newstr->len - (*sep)->len;
    277 	  newstr->next = *sep;
    278 	  newstr->left = (*sep)->left;
    279 	  newstr->right = (*sep)->right;
    280 	  *sep = newstr;
    281 	}
    282       else
    283 	{
    284 	  /* We have an exact match.  Free the memory we allocated.  */
    285 	  st->left += st->backp - (char *) newstr;
    286 	  st->backp = (char *) newstr;
    287 
    288 	  newstr = *sep;
    289 	}
    290     }
    291   else
    292     st->total += newstr->len;
    293 
    294   return newstr;
    295 }
    296 
    297 
    298 static void
    299 copystrings (struct Ebl_WStrent *nodep, wchar_t **freep, size_t *offsetp)
    300 {
    301   struct Ebl_WStrent *subs;
    302 
    303   if (nodep->left != NULL)
    304     copystrings (nodep->left, freep, offsetp);
    305 
    306   /* Process the current node.  */
    307   nodep->offset = *offsetp;
    308   *freep = wmempcpy (*freep, nodep->string, nodep->len);
    309   *offsetp += nodep->len * sizeof (wchar_t);
    310 
    311   for (subs = nodep->next; subs != NULL; subs = subs->next)
    312     {
    313       assert (subs->len < nodep->len);
    314       subs->offset = nodep->offset + nodep->len - subs->len;
    315       assert (subs->offset != 0 || subs->string[0] == '\0');
    316     }
    317 
    318   if (nodep->right != NULL)
    319     copystrings (nodep->right, freep, offsetp);
    320 }
    321 
    322 
    323 void
    324 ebl_wstrtabfinalize (struct Ebl_WStrtab *st, Elf_Data *data)
    325 {
    326   size_t copylen;
    327   wchar_t *endp;
    328   size_t nulllen = st->nullstr ? 1 : 0;
    329 
    330   /* Fill in the information.  */
    331   data->d_buf = malloc ((st->total + nulllen) * sizeof (wchar_t));
    332   if (data->d_buf == NULL)
    333     abort ();
    334 
    335   /* The first byte must always be zero if we created the table with a
    336      null string.  */
    337   if (st->nullstr)
    338     *((wchar_t *) data->d_buf) = L'\0';
    339 
    340   data->d_type = ELF_T_BYTE;
    341   data->d_size = st->total + nulllen;
    342   data->d_off = 0;
    343   data->d_align = 1;
    344   data->d_version = EV_CURRENT;
    345 
    346   /* Now run through the tree and add all the string while also updating
    347      the offset members of the elfstrent records.  */
    348   endp = (wchar_t *) data->d_buf + nulllen;
    349   copylen = sizeof (wchar_t) * nulllen;
    350   copystrings (st->root, &endp, &copylen);
    351   assert (copylen == (st->total + nulllen) * sizeof (wchar_t));
    352 }
    353 
    354 
    355 size_t
    356 ebl_wstrtaboffset (struct Ebl_WStrent *se)
    357 {
    358   return se->offset;
    359 }
    360