1 /* Generic string table handling.
2    Copyright (C) 2000, 2001, 2002, 2005 Red Hat, Inc.
3    This file is part of elfutils.
4    Written by Ulrich Drepper <drepper@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 <sys/param.h>
42 
43 #include "libebl.h"
44 
45 #ifndef MIN
46 # define MIN(a, b) ((a) < (b) ? (a) : (b))
47 #endif
48 
49 
50 struct Ebl_GStrent
51 {
52   const char *string;
53   size_t len;
54   struct Ebl_GStrent *next;
55   struct Ebl_GStrent *left;
56   struct Ebl_GStrent *right;
57   size_t offset;
58   unsigned int width;
59   char reverse[0];
60 };
61 
62 
63 struct memoryblock
64 {
65   struct memoryblock *next;
66   char memory[0];
67 };
68 
69 
70 struct Ebl_GStrtab
71 {
72   struct Ebl_GStrent *root;
73   struct memoryblock *memory;
74   char *backp;
75   size_t left;
76   size_t total;
77   unsigned int width;
78   bool nullstr;
79 
80   struct Ebl_GStrent 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_GStrtab *
ebl_gstrtabinit(unsigned int width,bool nullstr)90 ebl_gstrtabinit (unsigned int width, bool nullstr)
91 {
92   struct Ebl_GStrtab *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_GStrtab *) calloc (1, sizeof (struct Ebl_GStrtab));
101   if (ret != NULL)
102     {
103       ret->width = width;
104       ret->nullstr = nullstr;
105 
106       if (nullstr)
107 	{
108 	  ret->null.len = 1;
109 	  ret->null.string = (char *) calloc (1, width);
110 	}
111     }
112 
113   return ret;
114 }
115 
116 
117 static void
morememory(struct Ebl_GStrtab * st,size_t len)118 morememory (struct Ebl_GStrtab *st, size_t len)
119 {
120   struct memoryblock *newmem;
121 
122   if (len < ps)
123     len = ps;
124   newmem = (struct memoryblock *) malloc (len);
125   if (newmem == NULL)
126     abort ();
127 
128   newmem->next = st->memory;
129   st->memory = newmem;
130   st->backp = newmem->memory;
131   st->left = len - offsetof (struct memoryblock, memory);
132 }
133 
134 
135 void
ebl_gstrtabfree(struct Ebl_GStrtab * st)136 ebl_gstrtabfree (struct Ebl_GStrtab *st)
137 {
138   struct memoryblock *mb = st->memory;
139 
140   while (mb != NULL)
141     {
142       void *old = mb;
143       mb = mb->next;
144       free (old);
145     }
146 
147   if (st->null.string != NULL)
148     free ((char *) st->null.string);
149 
150   free (st);
151 }
152 
153 
154 static struct Ebl_GStrent *
newstring(struct Ebl_GStrtab * st,const char * str,size_t len)155 newstring (struct Ebl_GStrtab *st, const char *str, size_t len)
156 {
157   /* Compute the amount of padding needed to make the structure aligned.  */
158   size_t align = ((__alignof__ (struct Ebl_GStrent)
159 		   - (((uintptr_t) st->backp)
160 		      & (__alignof__ (struct Ebl_GStrent) - 1)))
161 		  & (__alignof__ (struct Ebl_GStrent) - 1));
162 
163   /* Make sure there is enough room in the memory block.  */
164   if (st->left < align + sizeof (struct Ebl_GStrent) + len * st->width)
165     {
166       morememory (st, sizeof (struct Ebl_GStrent) + len * st->width);
167       align = 0;
168     }
169 
170   /* Create the reserved string.  */
171   struct Ebl_GStrent *newstr = (struct Ebl_GStrent *) (st->backp + align);
172   newstr->string = str;
173   newstr->len = len;
174   newstr->width = st->width;
175   newstr->next = NULL;
176   newstr->left = NULL;
177   newstr->right = NULL;
178   newstr->offset = 0;
179   for (int i = len - 2; i >= 0; --i)
180     for (int j = st->width - 1; j >= 0; --j)
181       newstr->reverse[i * st->width + j] = str[(len - 2 - i) * st->width + j];
182   for (size_t j = 0; j < st->width; ++j)
183     newstr->reverse[(len - 1) * st->width + j] = '\0';
184   st->backp += align + sizeof (struct Ebl_GStrent) + len * st->width;
185   st->left -= align + sizeof (struct Ebl_GStrent) + len * st->width;
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_GStrent **
searchstring(struct Ebl_GStrent ** sep,struct Ebl_GStrent * newstr)195 searchstring (struct Ebl_GStrent **sep, struct Ebl_GStrent *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 = memcmp ((*sep)->reverse, newstr->reverse,
208 		   (MIN ((*sep)->len, newstr->len) - 1) * (*sep)->width);
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_GStrent *
ebl_gstrtabadd(struct Ebl_GStrtab * st,const char * str,size_t len)221 ebl_gstrtabadd (struct Ebl_GStrtab *st, const char *str, size_t len)
222 {
223   struct Ebl_GStrent *newstr;
224   struct Ebl_GStrent **sep;
225 
226   /* Compute the string length if the caller doesn't know it.  */
227   if (len == 0)
228     {
229       size_t j;
230 
231       do
232 	for (j = 0; j < st->width; ++j)
233 	  if (str[len * st->width + j] != '\0')
234 	    break;
235       while (j == st->width && ++len);
236     }
237 
238   /* Make sure all "" strings get offset 0 but only if the table was
239      created with a special null entry in mind.  */
240   if (len == 1 && st->null.string != NULL)
241     return &st->null;
242 
243   /* Allocate memory for the new string and its associated information.  */
244   newstr = newstring (st, str, len);
245 
246   /* Search in the array for the place to insert the string.  If there
247      is no string with matching prefix and no string with matching
248      leading substring, create a new entry.  */
249   sep = searchstring (&st->root, newstr);
250   if (*sep != newstr)
251     {
252       /* This is not the same entry.  This means we have a prefix match.  */
253       if ((*sep)->len > newstr->len)
254 	{
255 	  struct Ebl_GStrent *subs;
256 
257 	  /* Check whether we already know this string.  */
258 	  for (subs = (*sep)->next; subs != NULL; subs = subs->next)
259 	    if (subs->len == newstr->len)
260 	      {
261 		/* We have an exact match with a substring.  Free the memory
262 		   we allocated.  */
263 		st->left += (st->backp - (char *) newstr) * st->width;
264 		st->backp = (char *) newstr;
265 
266 		return subs;
267 	      }
268 
269 	  /* We have a new substring.  This means we don't need the reverse
270 	     string of this entry anymore.  */
271 	  st->backp -= newstr->len;
272 	  st->left += newstr->len;
273 
274 	  newstr->next = (*sep)->next;
275 	  (*sep)->next = newstr;
276 	}
277       else if ((*sep)->len != newstr->len)
278 	{
279 	  /* When we get here it means that the string we are about to
280 	     add has a common prefix with a string we already have but
281 	     it is longer.  In this case we have to put it first.  */
282 	  st->total += newstr->len - (*sep)->len;
283 	  newstr->next = *sep;
284 	  newstr->left = (*sep)->left;
285 	  newstr->right = (*sep)->right;
286 	  *sep = newstr;
287 	}
288       else
289 	{
290 	  /* We have an exact match.  Free the memory we allocated.  */
291 	  st->left += (st->backp - (char *) newstr) * st->width;
292 	  st->backp = (char *) newstr;
293 
294 	  newstr = *sep;
295 	}
296     }
297   else
298     st->total += newstr->len;
299 
300   return newstr;
301 }
302 
303 
304 static void
copystrings(struct Ebl_GStrent * nodep,char ** freep,size_t * offsetp)305 copystrings (struct Ebl_GStrent *nodep, char **freep, size_t *offsetp)
306 {
307   struct Ebl_GStrent *subs;
308 
309   if (nodep->left != NULL)
310     copystrings (nodep->left, freep, offsetp);
311 
312   /* Process the current node.  */
313   nodep->offset = *offsetp;
314   *freep = (char *) mempcpy (*freep, nodep->string, nodep->len * nodep->width);
315   *offsetp += nodep->len * nodep->width;
316 
317   for (subs = nodep->next; subs != NULL; subs = subs->next)
318     {
319       assert (subs->len < nodep->len);
320       subs->offset = nodep->offset + (nodep->len - subs->len) * nodep->width;
321       assert (subs->offset != 0 || subs->string[0] == '\0');
322     }
323 
324   if (nodep->right != NULL)
325     copystrings (nodep->right, freep, offsetp);
326 }
327 
328 
329 void
ebl_gstrtabfinalize(struct Ebl_GStrtab * st,Elf_Data * data)330 ebl_gstrtabfinalize (struct Ebl_GStrtab *st, Elf_Data *data)
331 {
332   size_t copylen;
333   char *endp;
334   size_t nulllen = st->nullstr ? st->width : 0;
335 
336   /* Fill in the information.  */
337   data->d_buf = malloc (st->total + nulllen);
338   if (data->d_buf == NULL)
339     abort ();
340 
341   /* The first byte must always be zero if we created the table with a
342      null string.  */
343   if (st->nullstr)
344     memset (data->d_buf, '\0', st->width);
345 
346   data->d_type = ELF_T_BYTE;
347   data->d_size = st->total + nulllen;
348   data->d_off = 0;
349   data->d_align = 1;
350   data->d_version = EV_CURRENT;
351 
352   /* Now run through the tree and add all the string while also updating
353      the offset members of the elfstrent records.  */
354   endp = (char *) data->d_buf + nulllen;
355   copylen = nulllen;
356   copystrings (st->root, &endp, &copylen);
357   assert (copylen == st->total * st->width + nulllen);
358 }
359 
360 
361 size_t
ebl_gstrtaboffset(struct Ebl_GStrent * se)362 ebl_gstrtaboffset (struct Ebl_GStrent *se)
363 {
364   return se->offset;
365 }
366