Home | History | Annotate | Download | only in bfd
      1 /* ELF strtab with GC and suffix merging support.
      2    Copyright (C) 2001-2016 Free Software Foundation, Inc.
      3    Written by Jakub Jelinek <jakub (at) redhat.com>.
      4 
      5    This file is part of BFD, the Binary File Descriptor library.
      6 
      7    This program is free software; you can redistribute it and/or modify
      8    it under the terms of the GNU General Public License as published by
      9    the Free Software Foundation; either version 3 of the License, or
     10    (at your option) any later version.
     11 
     12    This program is distributed in the hope that it will be useful,
     13    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15    GNU General Public License for more details.
     16 
     17    You should have received a copy of the GNU General Public License
     18    along with this program; if not, write to the Free Software
     19    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
     20    MA 02110-1301, USA.  */
     21 
     22 #include "sysdep.h"
     23 #include "bfd.h"
     24 #include "libbfd.h"
     25 #include "elf-bfd.h"
     26 #include "hashtab.h"
     27 #include "libiberty.h"
     28 
     29 /* An entry in the strtab hash table.  */
     30 
     31 struct elf_strtab_hash_entry
     32 {
     33   struct bfd_hash_entry root;
     34   /* Length of this entry.  This includes the zero terminator.  */
     35   int len;
     36   unsigned int refcount;
     37   union {
     38     /* Index within the merged section.  */
     39     bfd_size_type index;
     40     /* Entry this is a suffix of (if len < 0).  */
     41     struct elf_strtab_hash_entry *suffix;
     42   } u;
     43 };
     44 
     45 /* The strtab hash table.  */
     46 
     47 struct elf_strtab_hash
     48 {
     49   struct bfd_hash_table table;
     50   /* Next available index.  */
     51   size_t size;
     52   /* Number of array entries alloced.  */
     53   size_t alloced;
     54   /* Final strtab size.  */
     55   bfd_size_type sec_size;
     56   /* Array of pointers to strtab entries.  */
     57   struct elf_strtab_hash_entry **array;
     58 };
     59 
     60 /* Routine to create an entry in a section merge hashtab.  */
     61 
     62 static struct bfd_hash_entry *
     63 elf_strtab_hash_newfunc (struct bfd_hash_entry *entry,
     64 			 struct bfd_hash_table *table,
     65 			 const char *string)
     66 {
     67   /* Allocate the structure if it has not already been allocated by a
     68      subclass.  */
     69   if (entry == NULL)
     70     entry = (struct bfd_hash_entry *)
     71         bfd_hash_allocate (table, sizeof (struct elf_strtab_hash_entry));
     72   if (entry == NULL)
     73     return NULL;
     74 
     75   /* Call the allocation method of the superclass.  */
     76   entry = bfd_hash_newfunc (entry, table, string);
     77 
     78   if (entry)
     79     {
     80       /* Initialize the local fields.  */
     81       struct elf_strtab_hash_entry *ret;
     82 
     83       ret = (struct elf_strtab_hash_entry *) entry;
     84       ret->u.index = -1;
     85       ret->refcount = 0;
     86       ret->len = 0;
     87     }
     88 
     89   return entry;
     90 }
     91 
     92 /* Create a new hash table.  */
     93 
     94 struct elf_strtab_hash *
     95 _bfd_elf_strtab_init (void)
     96 {
     97   struct elf_strtab_hash *table;
     98   bfd_size_type amt = sizeof (struct elf_strtab_hash);
     99 
    100   table = (struct elf_strtab_hash *) bfd_malloc (amt);
    101   if (table == NULL)
    102     return NULL;
    103 
    104   if (!bfd_hash_table_init (&table->table, elf_strtab_hash_newfunc,
    105 			    sizeof (struct elf_strtab_hash_entry)))
    106     {
    107       free (table);
    108       return NULL;
    109     }
    110 
    111   table->sec_size = 0;
    112   table->size = 1;
    113   table->alloced = 64;
    114   amt = sizeof (struct elf_strtab_hasn_entry *);
    115   table->array = ((struct elf_strtab_hash_entry **)
    116 		  bfd_malloc (table->alloced * amt));
    117   if (table->array == NULL)
    118     {
    119       free (table);
    120       return NULL;
    121     }
    122 
    123   table->array[0] = NULL;
    124 
    125   return table;
    126 }
    127 
    128 /* Free a strtab.  */
    129 
    130 void
    131 _bfd_elf_strtab_free (struct elf_strtab_hash *tab)
    132 {
    133   bfd_hash_table_free (&tab->table);
    134   free (tab->array);
    135   free (tab);
    136 }
    137 
    138 /* Get the index of an entity in a hash table, adding it if it is not
    139    already present.  */
    140 
    141 size_t
    142 _bfd_elf_strtab_add (struct elf_strtab_hash *tab,
    143 		     const char *str,
    144 		     bfd_boolean copy)
    145 {
    146   register struct elf_strtab_hash_entry *entry;
    147 
    148   /* We handle this specially, since we don't want to do refcounting
    149      on it.  */
    150   if (*str == '\0')
    151     return 0;
    152 
    153   BFD_ASSERT (tab->sec_size == 0);
    154   entry = (struct elf_strtab_hash_entry *)
    155 	  bfd_hash_lookup (&tab->table, str, TRUE, copy);
    156 
    157   if (entry == NULL)
    158     return (size_t) -1;
    159 
    160   entry->refcount++;
    161   if (entry->len == 0)
    162     {
    163       entry->len = strlen (str) + 1;
    164       /* 2G strings lose.  */
    165       BFD_ASSERT (entry->len > 0);
    166       if (tab->size == tab->alloced)
    167 	{
    168 	  bfd_size_type amt = sizeof (struct elf_strtab_hash_entry *);
    169 	  tab->alloced *= 2;
    170 	  tab->array = (struct elf_strtab_hash_entry **)
    171               bfd_realloc_or_free (tab->array, tab->alloced * amt);
    172 	  if (tab->array == NULL)
    173 	    return (size_t) -1;
    174 	}
    175 
    176       entry->u.index = tab->size++;
    177       tab->array[entry->u.index] = entry;
    178     }
    179   return entry->u.index;
    180 }
    181 
    182 void
    183 _bfd_elf_strtab_addref (struct elf_strtab_hash *tab, size_t idx)
    184 {
    185   if (idx == 0 || idx == (size_t) -1)
    186     return;
    187   BFD_ASSERT (tab->sec_size == 0);
    188   BFD_ASSERT (idx < tab->size);
    189   ++tab->array[idx]->refcount;
    190 }
    191 
    192 void
    193 _bfd_elf_strtab_delref (struct elf_strtab_hash *tab, size_t idx)
    194 {
    195   if (idx == 0 || idx == (size_t) -1)
    196     return;
    197   BFD_ASSERT (tab->sec_size == 0);
    198   BFD_ASSERT (idx < tab->size);
    199   BFD_ASSERT (tab->array[idx]->refcount > 0);
    200   --tab->array[idx]->refcount;
    201 }
    202 
    203 unsigned int
    204 _bfd_elf_strtab_refcount (struct elf_strtab_hash *tab, size_t idx)
    205 {
    206   return tab->array[idx]->refcount;
    207 }
    208 
    209 void
    210 _bfd_elf_strtab_clear_all_refs (struct elf_strtab_hash *tab)
    211 {
    212   size_t idx;
    213 
    214   for (idx = 1; idx < tab->size; idx++)
    215     tab->array[idx]->refcount = 0;
    216 }
    217 
    218 /* Save strtab refcounts prior to adding --as-needed library.  */
    219 
    220 struct strtab_save
    221 {
    222   size_t size;
    223   unsigned int refcount[1];
    224 };
    225 
    226 void *
    227 _bfd_elf_strtab_save (struct elf_strtab_hash *tab)
    228 {
    229   struct strtab_save *save;
    230   size_t idx, size;
    231 
    232   size = sizeof (*save) + (tab->size - 1) * sizeof (save->refcount[0]);
    233   save = bfd_malloc (size);
    234   if (save == NULL)
    235     return save;
    236 
    237   save->size = tab->size;
    238   for (idx = 1; idx < tab->size; idx++)
    239     save->refcount[idx] = tab->array[idx]->refcount;
    240   return save;
    241 }
    242 
    243 /* Restore strtab refcounts on finding --as-needed library not needed.  */
    244 
    245 void
    246 _bfd_elf_strtab_restore (struct elf_strtab_hash *tab, void *buf)
    247 {
    248   size_t idx, curr_size = tab->size;
    249   struct strtab_save *save = (struct strtab_save *) buf;
    250 
    251   BFD_ASSERT (tab->sec_size == 0);
    252   BFD_ASSERT (save->size <= curr_size);
    253   tab->size = save->size;
    254   for (idx = 1; idx < save->size; ++idx)
    255     tab->array[idx]->refcount = save->refcount[idx];
    256 
    257   for (; idx < curr_size; ++idx)
    258     {
    259       /* We don't remove entries from the hash table, just set their
    260 	 REFCOUNT to zero.  Setting LEN zero will result in the size
    261 	 growing if the entry is added again.  See _bfd_elf_strtab_add.  */
    262       tab->array[idx]->refcount = 0;
    263       tab->array[idx]->len = 0;
    264     }
    265 }
    266 
    267 bfd_size_type
    268 _bfd_elf_strtab_size (struct elf_strtab_hash *tab)
    269 {
    270   return tab->sec_size ? tab->sec_size : tab->size;
    271 }
    272 
    273 bfd_size_type
    274 _bfd_elf_strtab_offset (struct elf_strtab_hash *tab, size_t idx)
    275 {
    276   struct elf_strtab_hash_entry *entry;
    277 
    278   if (idx == 0)
    279     return 0;
    280   BFD_ASSERT (idx < tab->size);
    281   BFD_ASSERT (tab->sec_size);
    282   entry = tab->array[idx];
    283   BFD_ASSERT (entry->refcount > 0);
    284   entry->refcount--;
    285   return tab->array[idx]->u.index;
    286 }
    287 
    288 bfd_boolean
    289 _bfd_elf_strtab_emit (register bfd *abfd, struct elf_strtab_hash *tab)
    290 {
    291   bfd_size_type off = 1;
    292   size_t i;
    293 
    294   if (bfd_bwrite ("", 1, abfd) != 1)
    295     return FALSE;
    296 
    297   for (i = 1; i < tab->size; ++i)
    298     {
    299       register const char *str;
    300       register unsigned int len;
    301 
    302       BFD_ASSERT (tab->array[i]->refcount == 0);
    303       len = tab->array[i]->len;
    304       if ((int) len < 0)
    305 	continue;
    306 
    307       str = tab->array[i]->root.string;
    308       if (bfd_bwrite (str, len, abfd) != len)
    309 	return FALSE;
    310 
    311       off += len;
    312     }
    313 
    314   BFD_ASSERT (off == tab->sec_size);
    315   return TRUE;
    316 }
    317 
    318 /* Compare two elf_strtab_hash_entry structures.  Called via qsort.  */
    319 
    320 static int
    321 strrevcmp (const void *a, const void *b)
    322 {
    323   struct elf_strtab_hash_entry *A = *(struct elf_strtab_hash_entry **) a;
    324   struct elf_strtab_hash_entry *B = *(struct elf_strtab_hash_entry **) b;
    325   unsigned int lenA = A->len;
    326   unsigned int lenB = B->len;
    327   const unsigned char *s = (const unsigned char *) A->root.string + lenA - 1;
    328   const unsigned char *t = (const unsigned char *) B->root.string + lenB - 1;
    329   int l = lenA < lenB ? lenA : lenB;
    330 
    331   while (l)
    332     {
    333       if (*s != *t)
    334 	return (int) *s - (int) *t;
    335       s--;
    336       t--;
    337       l--;
    338     }
    339   return lenA - lenB;
    340 }
    341 
    342 static inline int
    343 is_suffix (const struct elf_strtab_hash_entry *A,
    344 	   const struct elf_strtab_hash_entry *B)
    345 {
    346   if (A->len <= B->len)
    347     /* B cannot be a suffix of A unless A is equal to B, which is guaranteed
    348        not to be equal by the hash table.  */
    349     return 0;
    350 
    351   return memcmp (A->root.string + (A->len - B->len),
    352 		 B->root.string, B->len - 1) == 0;
    353 }
    354 
    355 /* This function assigns final string table offsets for used strings,
    356    merging strings matching suffixes of longer strings if possible.  */
    357 
    358 void
    359 _bfd_elf_strtab_finalize (struct elf_strtab_hash *tab)
    360 {
    361   struct elf_strtab_hash_entry **array, **a, *e;
    362   bfd_size_type amt, sec_size;
    363   size_t size, i;
    364 
    365   /* Sort the strings by suffix and length.  */
    366   amt = tab->size;
    367   amt *= sizeof (struct elf_strtab_hash_entry *);
    368   array = (struct elf_strtab_hash_entry **) bfd_malloc (amt);
    369   if (array == NULL)
    370     goto alloc_failure;
    371 
    372   for (i = 1, a = array; i < tab->size; ++i)
    373     {
    374       e = tab->array[i];
    375       if (e->refcount)
    376 	{
    377 	  *a++ = e;
    378 	  /* Adjust the length to not include the zero terminator.  */
    379 	  e->len -= 1;
    380 	}
    381       else
    382 	e->len = 0;
    383     }
    384 
    385   size = a - array;
    386   if (size != 0)
    387     {
    388       qsort (array, size, sizeof (struct elf_strtab_hash_entry *), strrevcmp);
    389 
    390       /* Loop over the sorted array and merge suffixes.  Start from the
    391 	 end because we want eg.
    392 
    393 	 s1 -> "d"
    394 	 s2 -> "bcd"
    395 	 s3 -> "abcd"
    396 
    397 	 to end up as
    398 
    399 	 s3 -> "abcd"
    400 	 s2 _____^
    401 	 s1 _______^
    402 
    403 	 ie. we don't want s1 pointing into the old s2.  */
    404       e = *--a;
    405       e->len += 1;
    406       while (--a >= array)
    407 	{
    408 	  struct elf_strtab_hash_entry *cmp = *a;
    409 
    410 	  cmp->len += 1;
    411 	  if (is_suffix (e, cmp))
    412 	    {
    413 	      cmp->u.suffix = e;
    414 	      cmp->len = -cmp->len;
    415 	    }
    416 	  else
    417 	    e = cmp;
    418 	}
    419     }
    420 
    421 alloc_failure:
    422   if (array)
    423     free (array);
    424 
    425   /* Assign positions to the strings we want to keep.  */
    426   sec_size = 1;
    427   for (i = 1; i < tab->size; ++i)
    428     {
    429       e = tab->array[i];
    430       if (e->refcount && e->len > 0)
    431 	{
    432 	  e->u.index = sec_size;
    433 	  sec_size += e->len;
    434 	}
    435     }
    436 
    437   tab->sec_size = sec_size;
    438 
    439   /* Adjust the rest.  */
    440   for (i = 1; i < tab->size; ++i)
    441     {
    442       e = tab->array[i];
    443       if (e->refcount && e->len < 0)
    444 	e->u.index = e->u.suffix->u.index + (e->u.suffix->len + e->len);
    445     }
    446 }
    447