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