| /* hash.c -- hash table routines for BFD |
| Copyright (C) 1993-2022 Free Software Foundation, Inc. |
| Written by Steve Chamberlain <sac@cygnus.com> |
| |
| This file is part of BFD, the Binary File Descriptor library. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| MA 02110-1301, USA. */ |
| |
| #include "sysdep.h" |
| #include "bfd.h" |
| #include "libbfd.h" |
| #include "objalloc.h" |
| #include "libiberty.h" |
| |
| /* |
| SECTION |
| Hash Tables |
| |
| @cindex Hash tables |
| BFD provides a simple set of hash table functions. Routines |
| are provided to initialize a hash table, to free a hash table, |
| to look up a string in a hash table and optionally create an |
| entry for it, and to traverse a hash table. There is |
| currently no routine to delete an string from a hash table. |
| |
| The basic hash table does not permit any data to be stored |
| with a string. However, a hash table is designed to present a |
| base class from which other types of hash tables may be |
| derived. These derived types may store additional information |
| with the string. Hash tables were implemented in this way, |
| rather than simply providing a data pointer in a hash table |
| entry, because they were designed for use by the linker back |
| ends. The linker may create thousands of hash table entries, |
| and the overhead of allocating private data and storing and |
| following pointers becomes noticeable. |
| |
| The basic hash table code is in <<hash.c>>. |
| |
| @menu |
| @* Creating and Freeing a Hash Table:: |
| @* Looking Up or Entering a String:: |
| @* Traversing a Hash Table:: |
| @* Deriving a New Hash Table Type:: |
| @end menu |
| |
| INODE |
| Creating and Freeing a Hash Table, Looking Up or Entering a String, Hash Tables, Hash Tables |
| SUBSECTION |
| Creating and freeing a hash table |
| |
| @findex bfd_hash_table_init |
| @findex bfd_hash_table_init_n |
| To create a hash table, create an instance of a <<struct |
| bfd_hash_table>> (defined in <<bfd.h>>) and call |
| <<bfd_hash_table_init>> (if you know approximately how many |
| entries you will need, the function <<bfd_hash_table_init_n>>, |
| which takes a @var{size} argument, may be used). |
| <<bfd_hash_table_init>> returns <<FALSE>> if some sort of |
| error occurs. |
| |
| @findex bfd_hash_newfunc |
| The function <<bfd_hash_table_init>> take as an argument a |
| function to use to create new entries. For a basic hash |
| table, use the function <<bfd_hash_newfunc>>. @xref{Deriving |
| a New Hash Table Type}, for why you would want to use a |
| different value for this argument. |
| |
| @findex bfd_hash_allocate |
| <<bfd_hash_table_init>> will create an objalloc which will be |
| used to allocate new entries. You may allocate memory on this |
| objalloc using <<bfd_hash_allocate>>. |
| |
| @findex bfd_hash_table_free |
| Use <<bfd_hash_table_free>> to free up all the memory that has |
| been allocated for a hash table. This will not free up the |
| <<struct bfd_hash_table>> itself, which you must provide. |
| |
| @findex bfd_hash_set_default_size |
| Use <<bfd_hash_set_default_size>> to set the default size of |
| hash table to use. |
| |
| INODE |
| Looking Up or Entering a String, Traversing a Hash Table, Creating and Freeing a Hash Table, Hash Tables |
| SUBSECTION |
| Looking up or entering a string |
| |
| @findex bfd_hash_lookup |
| The function <<bfd_hash_lookup>> is used both to look up a |
| string in the hash table and to create a new entry. |
| |
| If the @var{create} argument is <<FALSE>>, <<bfd_hash_lookup>> |
| will look up a string. If the string is found, it will |
| returns a pointer to a <<struct bfd_hash_entry>>. If the |
| string is not found in the table <<bfd_hash_lookup>> will |
| return <<NULL>>. You should not modify any of the fields in |
| the returns <<struct bfd_hash_entry>>. |
| |
| If the @var{create} argument is <<TRUE>>, the string will be |
| entered into the hash table if it is not already there. |
| Either way a pointer to a <<struct bfd_hash_entry>> will be |
| returned, either to the existing structure or to a newly |
| created one. In this case, a <<NULL>> return means that an |
| error occurred. |
| |
| If the @var{create} argument is <<TRUE>>, and a new entry is |
| created, the @var{copy} argument is used to decide whether to |
| copy the string onto the hash table objalloc or not. If |
| @var{copy} is passed as <<FALSE>>, you must be careful not to |
| deallocate or modify the string as long as the hash table |
| exists. |
| |
| INODE |
| Traversing a Hash Table, Deriving a New Hash Table Type, Looking Up or Entering a String, Hash Tables |
| SUBSECTION |
| Traversing a hash table |
| |
| @findex bfd_hash_traverse |
| The function <<bfd_hash_traverse>> may be used to traverse a |
| hash table, calling a function on each element. The traversal |
| is done in a random order. |
| |
| <<bfd_hash_traverse>> takes as arguments a function and a |
| generic <<void *>> pointer. The function is called with a |
| hash table entry (a <<struct bfd_hash_entry *>>) and the |
| generic pointer passed to <<bfd_hash_traverse>>. The function |
| must return a <<boolean>> value, which indicates whether to |
| continue traversing the hash table. If the function returns |
| <<FALSE>>, <<bfd_hash_traverse>> will stop the traversal and |
| return immediately. |
| |
| INODE |
| Deriving a New Hash Table Type, , Traversing a Hash Table, Hash Tables |
| SUBSECTION |
| Deriving a new hash table type |
| |
| Many uses of hash tables want to store additional information |
| which each entry in the hash table. Some also find it |
| convenient to store additional information with the hash table |
| itself. This may be done using a derived hash table. |
| |
| Since C is not an object oriented language, creating a derived |
| hash table requires sticking together some boilerplate |
| routines with a few differences specific to the type of hash |
| table you want to create. |
| |
| An example of a derived hash table is the linker hash table. |
| The structures for this are defined in <<bfdlink.h>>. The |
| functions are in <<linker.c>>. |
| |
| You may also derive a hash table from an already derived hash |
| table. For example, the a.out linker backend code uses a hash |
| table derived from the linker hash table. |
| |
| @menu |
| @* Define the Derived Structures:: |
| @* Write the Derived Creation Routine:: |
| @* Write Other Derived Routines:: |
| @end menu |
| |
| INODE |
| Define the Derived Structures, Write the Derived Creation Routine, Deriving a New Hash Table Type, Deriving a New Hash Table Type |
| SUBSUBSECTION |
| Define the derived structures |
| |
| You must define a structure for an entry in the hash table, |
| and a structure for the hash table itself. |
| |
| The first field in the structure for an entry in the hash |
| table must be of the type used for an entry in the hash table |
| you are deriving from. If you are deriving from a basic hash |
| table this is <<struct bfd_hash_entry>>, which is defined in |
| <<bfd.h>>. The first field in the structure for the hash |
| table itself must be of the type of the hash table you are |
| deriving from itself. If you are deriving from a basic hash |
| table, this is <<struct bfd_hash_table>>. |
| |
| For example, the linker hash table defines <<struct |
| bfd_link_hash_entry>> (in <<bfdlink.h>>). The first field, |
| <<root>>, is of type <<struct bfd_hash_entry>>. Similarly, |
| the first field in <<struct bfd_link_hash_table>>, <<table>>, |
| is of type <<struct bfd_hash_table>>. |
| |
| INODE |
| Write the Derived Creation Routine, Write Other Derived Routines, Define the Derived Structures, Deriving a New Hash Table Type |
| SUBSUBSECTION |
| Write the derived creation routine |
| |
| You must write a routine which will create and initialize an |
| entry in the hash table. This routine is passed as the |
| function argument to <<bfd_hash_table_init>>. |
| |
| In order to permit other hash tables to be derived from the |
| hash table you are creating, this routine must be written in a |
| standard way. |
| |
| The first argument to the creation routine is a pointer to a |
| hash table entry. This may be <<NULL>>, in which case the |
| routine should allocate the right amount of space. Otherwise |
| the space has already been allocated by a hash table type |
| derived from this one. |
| |
| After allocating space, the creation routine must call the |
| creation routine of the hash table type it is derived from, |
| passing in a pointer to the space it just allocated. This |
| will initialize any fields used by the base hash table. |
| |
| Finally the creation routine must initialize any local fields |
| for the new hash table type. |
| |
| Here is a boilerplate example of a creation routine. |
| @var{function_name} is the name of the routine. |
| @var{entry_type} is the type of an entry in the hash table you |
| are creating. @var{base_newfunc} is the name of the creation |
| routine of the hash table type your hash table is derived |
| from. |
| |
| EXAMPLE |
| |
| .struct bfd_hash_entry * |
| .@var{function_name} (struct bfd_hash_entry *entry, |
| . struct bfd_hash_table *table, |
| . const char *string) |
| .{ |
| . struct @var{entry_type} *ret = (@var{entry_type} *) entry; |
| . |
| . {* Allocate the structure if it has not already been allocated by a |
| . derived class. *} |
| . if (ret == NULL) |
| . { |
| . ret = bfd_hash_allocate (table, sizeof (* ret)); |
| . if (ret == NULL) |
| . return NULL; |
| . } |
| . |
| . {* Call the allocation method of the base class. *} |
| . ret = ((@var{entry_type} *) |
| . @var{base_newfunc} ((struct bfd_hash_entry *) ret, table, string)); |
| . |
| . {* Initialize the local fields here. *} |
| . |
| . return (struct bfd_hash_entry *) ret; |
| .} |
| |
| DESCRIPTION |
| The creation routine for the linker hash table, which is in |
| <<linker.c>>, looks just like this example. |
| @var{function_name} is <<_bfd_link_hash_newfunc>>. |
| @var{entry_type} is <<struct bfd_link_hash_entry>>. |
| @var{base_newfunc} is <<bfd_hash_newfunc>>, the creation |
| routine for a basic hash table. |
| |
| <<_bfd_link_hash_newfunc>> also initializes the local fields |
| in a linker hash table entry: <<type>>, <<written>> and |
| <<next>>. |
| |
| INODE |
| Write Other Derived Routines, , Write the Derived Creation Routine, Deriving a New Hash Table Type |
| SUBSUBSECTION |
| Write other derived routines |
| |
| You will want to write other routines for your new hash table, |
| as well. |
| |
| You will want an initialization routine which calls the |
| initialization routine of the hash table you are deriving from |
| and initializes any other local fields. For the linker hash |
| table, this is <<_bfd_link_hash_table_init>> in <<linker.c>>. |
| |
| You will want a lookup routine which calls the lookup routine |
| of the hash table you are deriving from and casts the result. |
| The linker hash table uses <<bfd_link_hash_lookup>> in |
| <<linker.c>> (this actually takes an additional argument which |
| it uses to decide how to return the looked up value). |
| |
| You may want a traversal routine. This should just call the |
| traversal routine of the hash table you are deriving from with |
| appropriate casts. The linker hash table uses |
| <<bfd_link_hash_traverse>> in <<linker.c>>. |
| |
| These routines may simply be defined as macros. For example, |
| the a.out backend linker hash table, which is derived from the |
| linker hash table, uses macros for the lookup and traversal |
| routines. These are <<aout_link_hash_lookup>> and |
| <<aout_link_hash_traverse>> in aoutx.h. |
| */ |
| |
| /* The default number of entries to use when creating a hash table. */ |
| #define DEFAULT_SIZE 4051 |
| |
| /* The following function returns a nearest prime number which is |
| greater than N, and near a power of two. Copied from libiberty. |
| Returns zero for ridiculously large N to signify an error. */ |
| |
| static unsigned long |
| higher_prime_number (unsigned long n) |
| { |
| /* These are primes that are near, but slightly smaller than, a |
| power of two. */ |
| static const unsigned long primes[] = |
| { |
| (unsigned long) 31, |
| (unsigned long) 61, |
| (unsigned long) 127, |
| (unsigned long) 251, |
| (unsigned long) 509, |
| (unsigned long) 1021, |
| (unsigned long) 2039, |
| (unsigned long) 4093, |
| (unsigned long) 8191, |
| (unsigned long) 16381, |
| (unsigned long) 32749, |
| (unsigned long) 65521, |
| (unsigned long) 131071, |
| (unsigned long) 262139, |
| (unsigned long) 524287, |
| (unsigned long) 1048573, |
| (unsigned long) 2097143, |
| (unsigned long) 4194301, |
| (unsigned long) 8388593, |
| (unsigned long) 16777213, |
| (unsigned long) 33554393, |
| (unsigned long) 67108859, |
| (unsigned long) 134217689, |
| (unsigned long) 268435399, |
| (unsigned long) 536870909, |
| (unsigned long) 1073741789, |
| (unsigned long) 2147483647, |
| /* 4294967291L */ |
| ((unsigned long) 2147483647) + ((unsigned long) 2147483644), |
| }; |
| |
| const unsigned long *low = &primes[0]; |
| const unsigned long *high = &primes[sizeof (primes) / sizeof (primes[0])]; |
| |
| while (low != high) |
| { |
| const unsigned long *mid = low + (high - low) / 2; |
| if (n >= *mid) |
| low = mid + 1; |
| else |
| high = mid; |
| } |
| |
| if (n >= *low) |
| return 0; |
| |
| return *low; |
| } |
| |
| static unsigned long bfd_default_hash_table_size = DEFAULT_SIZE; |
| |
| /* Create a new hash table, given a number of entries. */ |
| |
| bool |
| bfd_hash_table_init_n (struct bfd_hash_table *table, |
| struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *, |
| struct bfd_hash_table *, |
| const char *), |
| unsigned int entsize, |
| unsigned int size) |
| { |
| unsigned long alloc; |
| |
| alloc = size; |
| alloc *= sizeof (struct bfd_hash_entry *); |
| if (alloc / sizeof (struct bfd_hash_entry *) != size) |
| { |
| bfd_set_error (bfd_error_no_memory); |
| return false; |
| } |
| |
| table->memory = (void *) objalloc_create (); |
| if (table->memory == NULL) |
| { |
| bfd_set_error (bfd_error_no_memory); |
| return false; |
| } |
| table->table = (struct bfd_hash_entry **) |
| objalloc_alloc ((struct objalloc *) table->memory, alloc); |
| if (table->table == NULL) |
| { |
| bfd_hash_table_free (table); |
| bfd_set_error (bfd_error_no_memory); |
| return false; |
| } |
| memset ((void *) table->table, 0, alloc); |
| table->size = size; |
| table->entsize = entsize; |
| table->count = 0; |
| table->frozen = 0; |
| table->newfunc = newfunc; |
| return true; |
| } |
| |
| /* Create a new hash table with the default number of entries. */ |
| |
| bool |
| bfd_hash_table_init (struct bfd_hash_table *table, |
| struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *, |
| struct bfd_hash_table *, |
| const char *), |
| unsigned int entsize) |
| { |
| return bfd_hash_table_init_n (table, newfunc, entsize, |
| bfd_default_hash_table_size); |
| } |
| |
| /* Free a hash table. */ |
| |
| void |
| bfd_hash_table_free (struct bfd_hash_table *table) |
| { |
| objalloc_free ((struct objalloc *) table->memory); |
| table->memory = NULL; |
| } |
| |
| static inline unsigned long |
| bfd_hash_hash (const char *string, unsigned int *lenp) |
| { |
| const unsigned char *s; |
| unsigned long hash; |
| unsigned int len; |
| unsigned int c; |
| |
| BFD_ASSERT (string != NULL); |
| hash = 0; |
| len = 0; |
| s = (const unsigned char *) string; |
| while ((c = *s++) != '\0') |
| { |
| hash += c + (c << 17); |
| hash ^= hash >> 2; |
| } |
| len = (s - (const unsigned char *) string) - 1; |
| hash += len + (len << 17); |
| hash ^= hash >> 2; |
| if (lenp != NULL) |
| *lenp = len; |
| return hash; |
| } |
| |
| /* Look up a string in a hash table. */ |
| |
| struct bfd_hash_entry * |
| bfd_hash_lookup (struct bfd_hash_table *table, |
| const char *string, |
| bool create, |
| bool copy) |
| { |
| unsigned long hash; |
| struct bfd_hash_entry *hashp; |
| unsigned int len; |
| unsigned int _index; |
| |
| hash = bfd_hash_hash (string, &len); |
| _index = hash % table->size; |
| for (hashp = table->table[_index]; |
| hashp != NULL; |
| hashp = hashp->next) |
| { |
| if (hashp->hash == hash |
| && strcmp (hashp->string, string) == 0) |
| return hashp; |
| } |
| |
| if (! create) |
| return NULL; |
| |
| if (copy) |
| { |
| char *new_string; |
| |
| new_string = (char *) objalloc_alloc ((struct objalloc *) table->memory, |
| len + 1); |
| if (!new_string) |
| { |
| bfd_set_error (bfd_error_no_memory); |
| return NULL; |
| } |
| memcpy (new_string, string, len + 1); |
| string = new_string; |
| } |
| |
| return bfd_hash_insert (table, string, hash); |
| } |
| |
| /* Insert an entry in a hash table. */ |
| |
| struct bfd_hash_entry * |
| bfd_hash_insert (struct bfd_hash_table *table, |
| const char *string, |
| unsigned long hash) |
| { |
| struct bfd_hash_entry *hashp; |
| unsigned int _index; |
| |
| hashp = (*table->newfunc) (NULL, table, string); |
| if (hashp == NULL) |
| return NULL; |
| hashp->string = string; |
| hashp->hash = hash; |
| _index = hash % table->size; |
| hashp->next = table->table[_index]; |
| table->table[_index] = hashp; |
| table->count++; |
| |
| if (!table->frozen && table->count > table->size * 3 / 4) |
| { |
| unsigned long newsize = higher_prime_number (table->size); |
| struct bfd_hash_entry **newtable; |
| unsigned int hi; |
| unsigned long alloc = newsize * sizeof (struct bfd_hash_entry *); |
| |
| /* If we can't find a higher prime, or we can't possibly alloc |
| that much memory, don't try to grow the table. */ |
| if (newsize == 0 || alloc / sizeof (struct bfd_hash_entry *) != newsize) |
| { |
| table->frozen = 1; |
| return hashp; |
| } |
| |
| newtable = ((struct bfd_hash_entry **) |
| objalloc_alloc ((struct objalloc *) table->memory, alloc)); |
| if (newtable == NULL) |
| { |
| table->frozen = 1; |
| return hashp; |
| } |
| memset (newtable, 0, alloc); |
| |
| for (hi = 0; hi < table->size; hi ++) |
| while (table->table[hi]) |
| { |
| struct bfd_hash_entry *chain = table->table[hi]; |
| struct bfd_hash_entry *chain_end = chain; |
| |
| while (chain_end->next && chain_end->next->hash == chain->hash) |
| chain_end = chain_end->next; |
| |
| table->table[hi] = chain_end->next; |
| _index = chain->hash % newsize; |
| chain_end->next = newtable[_index]; |
| newtable[_index] = chain; |
| } |
| table->table = newtable; |
| table->size = newsize; |
| } |
| |
| return hashp; |
| } |
| |
| /* Rename an entry in a hash table. */ |
| |
| void |
| bfd_hash_rename (struct bfd_hash_table *table, |
| const char *string, |
| struct bfd_hash_entry *ent) |
| { |
| unsigned int _index; |
| struct bfd_hash_entry **pph; |
| |
| _index = ent->hash % table->size; |
| for (pph = &table->table[_index]; *pph != NULL; pph = &(*pph)->next) |
| if (*pph == ent) |
| break; |
| if (*pph == NULL) |
| abort (); |
| |
| *pph = ent->next; |
| ent->string = string; |
| ent->hash = bfd_hash_hash (string, NULL); |
| _index = ent->hash % table->size; |
| ent->next = table->table[_index]; |
| table->table[_index] = ent; |
| } |
| |
| /* Replace an entry in a hash table. */ |
| |
| void |
| bfd_hash_replace (struct bfd_hash_table *table, |
| struct bfd_hash_entry *old, |
| struct bfd_hash_entry *nw) |
| { |
| unsigned int _index; |
| struct bfd_hash_entry **pph; |
| |
| _index = old->hash % table->size; |
| for (pph = &table->table[_index]; |
| (*pph) != NULL; |
| pph = &(*pph)->next) |
| { |
| if (*pph == old) |
| { |
| *pph = nw; |
| return; |
| } |
| } |
| |
| abort (); |
| } |
| |
| /* Allocate space in a hash table. */ |
| |
| void * |
| bfd_hash_allocate (struct bfd_hash_table *table, |
| unsigned int size) |
| { |
| void * ret; |
| |
| ret = objalloc_alloc ((struct objalloc *) table->memory, size); |
| if (ret == NULL && size != 0) |
| bfd_set_error (bfd_error_no_memory); |
| return ret; |
| } |
| |
| /* Base method for creating a new hash table entry. */ |
| |
| struct bfd_hash_entry * |
| bfd_hash_newfunc (struct bfd_hash_entry *entry, |
| struct bfd_hash_table *table, |
| const char *string ATTRIBUTE_UNUSED) |
| { |
| if (entry == NULL) |
| entry = (struct bfd_hash_entry *) bfd_hash_allocate (table, |
| sizeof (* entry)); |
| return entry; |
| } |
| |
| /* Traverse a hash table. */ |
| |
| void |
| bfd_hash_traverse (struct bfd_hash_table *table, |
| bool (*func) (struct bfd_hash_entry *, void *), |
| void * info) |
| { |
| unsigned int i; |
| |
| table->frozen = 1; |
| for (i = 0; i < table->size; i++) |
| { |
| struct bfd_hash_entry *p; |
| |
| for (p = table->table[i]; p != NULL; p = p->next) |
| if (! (*func) (p, info)) |
| goto out; |
| } |
| out: |
| table->frozen = 0; |
| } |
| |
| unsigned long |
| bfd_hash_set_default_size (unsigned long hash_size) |
| { |
| /* These silly_size values result in around 1G and 32M of memory |
| being allocated for the table of pointers. Note that the number |
| of elements allocated will be almost twice the size of any power |
| of two chosen here. */ |
| unsigned long silly_size = sizeof (size_t) > 4 ? 0x4000000 : 0x400000; |
| if (hash_size > silly_size) |
| hash_size = silly_size; |
| else if (hash_size != 0) |
| hash_size--; |
| hash_size = higher_prime_number (hash_size); |
| BFD_ASSERT (hash_size != 0); |
| bfd_default_hash_table_size = hash_size; |
| return bfd_default_hash_table_size; |
| } |
| |
| /* A few different object file formats (a.out, COFF, ELF) use a string |
| table. These functions support adding strings to a string table, |
| returning the byte offset, and writing out the table. |
| |
| Possible improvements: |
| + look for strings matching trailing substrings of other strings |
| + better data structures? balanced trees? |
| + look at reducing memory use elsewhere -- maybe if we didn't have |
| to construct the entire symbol table at once, we could get by |
| with smaller amounts of VM? (What effect does that have on the |
| string table reductions?) */ |
| |
| /* An entry in the strtab hash table. */ |
| |
| struct strtab_hash_entry |
| { |
| struct bfd_hash_entry root; |
| /* Index in string table. */ |
| bfd_size_type index; |
| /* Next string in strtab. */ |
| struct strtab_hash_entry *next; |
| }; |
| |
| /* The strtab hash table. */ |
| |
| struct bfd_strtab_hash |
| { |
| struct bfd_hash_table table; |
| /* Size of strtab--also next available index. */ |
| bfd_size_type size; |
| /* First string in strtab. */ |
| struct strtab_hash_entry *first; |
| /* Last string in strtab. */ |
| struct strtab_hash_entry *last; |
| /* Whether to precede strings with a two or four byte length, |
| as in the XCOFF .debug section. */ |
| char length_field_size; |
| }; |
| |
| |
| /* Routine to create an entry in a strtab. */ |
| |
| static struct bfd_hash_entry * |
| strtab_hash_newfunc (struct bfd_hash_entry *entry, |
| struct bfd_hash_table *table, |
| const char *string) |
| { |
| struct strtab_hash_entry *ret = (struct strtab_hash_entry *) entry; |
| |
| /* Allocate the structure if it has not already been allocated by a |
| subclass. */ |
| if (ret == NULL) |
| ret = (struct strtab_hash_entry *) bfd_hash_allocate (table, |
| sizeof (* ret)); |
| if (ret == NULL) |
| return NULL; |
| |
| /* Call the allocation method of the superclass. */ |
| ret = (struct strtab_hash_entry *) |
| bfd_hash_newfunc ((struct bfd_hash_entry *) ret, table, string); |
| |
| if (ret) |
| { |
| /* Initialize the local fields. */ |
| ret->index = (bfd_size_type) -1; |
| ret->next = NULL; |
| } |
| |
| return (struct bfd_hash_entry *) ret; |
| } |
| |
| /* Look up an entry in an strtab. */ |
| |
| #define strtab_hash_lookup(t, string, create, copy) \ |
| ((struct strtab_hash_entry *) \ |
| bfd_hash_lookup (&(t)->table, (string), (create), (copy))) |
| |
| /* Create a new strtab. */ |
| |
| struct bfd_strtab_hash * |
| _bfd_stringtab_init (void) |
| { |
| struct bfd_strtab_hash *table; |
| size_t amt = sizeof (* table); |
| |
| table = (struct bfd_strtab_hash *) bfd_malloc (amt); |
| if (table == NULL) |
| return NULL; |
| |
| if (!bfd_hash_table_init (&table->table, strtab_hash_newfunc, |
| sizeof (struct strtab_hash_entry))) |
| { |
| free (table); |
| return NULL; |
| } |
| |
| table->size = 0; |
| table->first = NULL; |
| table->last = NULL; |
| table->length_field_size = 0; |
| |
| return table; |
| } |
| |
| /* Create a new strtab in which the strings are output in the format |
| used in the XCOFF .debug section: a two byte length precedes each |
| string. */ |
| |
| struct bfd_strtab_hash * |
| _bfd_xcoff_stringtab_init (bool isxcoff64) |
| { |
| struct bfd_strtab_hash *ret; |
| |
| ret = _bfd_stringtab_init (); |
| if (ret != NULL) |
| ret->length_field_size = isxcoff64 ? 4 : 2; |
| return ret; |
| } |
| |
| /* Free a strtab. */ |
| |
| void |
| _bfd_stringtab_free (struct bfd_strtab_hash *table) |
| { |
| bfd_hash_table_free (&table->table); |
| free (table); |
| } |
| |
| /* Get the index of a string in a strtab, adding it if it is not |
| already present. If HASH is FALSE, we don't really use the hash |
| table, and we don't eliminate duplicate strings. If COPY is true |
| then store a copy of STR if creating a new entry. */ |
| |
| bfd_size_type |
| _bfd_stringtab_add (struct bfd_strtab_hash *tab, |
| const char *str, |
| bool hash, |
| bool copy) |
| { |
| struct strtab_hash_entry *entry; |
| |
| if (hash) |
| { |
| entry = strtab_hash_lookup (tab, str, true, copy); |
| if (entry == NULL) |
| return (bfd_size_type) -1; |
| } |
| else |
| { |
| entry = (struct strtab_hash_entry *) bfd_hash_allocate (&tab->table, |
| sizeof (* entry)); |
| if (entry == NULL) |
| return (bfd_size_type) -1; |
| if (! copy) |
| entry->root.string = str; |
| else |
| { |
| size_t len = strlen (str) + 1; |
| char *n; |
| |
| n = (char *) bfd_hash_allocate (&tab->table, len); |
| if (n == NULL) |
| return (bfd_size_type) -1; |
| memcpy (n, str, len); |
| entry->root.string = n; |
| } |
| entry->index = (bfd_size_type) -1; |
| entry->next = NULL; |
| } |
| |
| if (entry->index == (bfd_size_type) -1) |
| { |
| entry->index = tab->size; |
| tab->size += strlen (str) + 1; |
| entry->index += tab->length_field_size; |
| tab->size += tab->length_field_size; |
| if (tab->first == NULL) |
| tab->first = entry; |
| else |
| tab->last->next = entry; |
| tab->last = entry; |
| } |
| |
| return entry->index; |
| } |
| |
| /* Get the number of bytes in a strtab. */ |
| |
| bfd_size_type |
| _bfd_stringtab_size (struct bfd_strtab_hash *tab) |
| { |
| return tab->size; |
| } |
| |
| /* Write out a strtab. ABFD must already be at the right location in |
| the file. */ |
| |
| bool |
| _bfd_stringtab_emit (bfd *abfd, struct bfd_strtab_hash *tab) |
| { |
| struct strtab_hash_entry *entry; |
| |
| for (entry = tab->first; entry != NULL; entry = entry->next) |
| { |
| const char *str; |
| size_t len; |
| |
| str = entry->root.string; |
| len = strlen (str) + 1; |
| |
| if (tab->length_field_size == 4) |
| { |
| bfd_byte buf[4]; |
| |
| /* The output length includes the null byte. */ |
| bfd_put_32 (abfd, (bfd_vma) len, buf); |
| if (bfd_bwrite ((void *) buf, (bfd_size_type) 4, abfd) != 4) |
| return false; |
| } |
| else if (tab->length_field_size == 2) |
| { |
| bfd_byte buf[2]; |
| |
| /* The output length includes the null byte. */ |
| bfd_put_16 (abfd, (bfd_vma) len, buf); |
| if (bfd_bwrite ((void *) buf, (bfd_size_type) 2, abfd) != 2) |
| return false; |
| } |
| |
| if (bfd_bwrite ((void *) str, (bfd_size_type) len, abfd) != len) |
| return false; |
| } |
| |
| return true; |
| } |