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