diff options
Diffstat (limited to 'ld/ld_hash.c')
-rw-r--r-- | ld/ld_hash.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/ld/ld_hash.c b/ld/ld_hash.c new file mode 100644 index 0000000000000..fc4125126028d --- /dev/null +++ b/ld/ld_hash.c @@ -0,0 +1,126 @@ +/*- + * Copyright (c) 2012 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "ld.h" +#include "ld_hash.h" +#include "ld_layout.h" +#include "ld_output.h" +#include "ld_symbols.h" + +ELFTC_VCSID("$Id: ld_hash.c 2917 2013-02-16 07:16:02Z kaiwang27 $"); + +/* + * The number of buckets to use for a certain number of symbols. + * If there are less than 3 symbols, 1 bucket will be used. If + * there are less than 17 symbols, 3 buckets will be used, and so + * forth. The bucket numbers are defined by GNU ld. We use the + * same rules here so we generate hash sections with the same + * size as those generated by GNU ld. + */ +static unsigned hash_buckets[] = { + 1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209, + 16411, 32771, 65537, 131101, 262147 +}; + +void +ld_hash_create_svr4_hash_section(struct ld *ld) +{ + struct ld_output *lo; + struct ld_output_section *os; + struct ld_output_data_buffer *odb; + struct ld_symbol *lsb; + char hash_name[] = ".hash"; + uint32_t *buf, *buckets, *chains, nbuckets, nchains; + int i, j; + + lo = ld->ld_output; + assert(lo != NULL); + + HASH_FIND_STR(lo->lo_ostbl, hash_name, os); + if (os == NULL) + os = ld_layout_insert_output_section(ld, hash_name, SHF_ALLOC); + os->os_type = SHT_HASH; + os->os_flags = SHF_ALLOC; + os->os_entsize = 4; + if (lo->lo_ec == ELFCLASS32) + os->os_align = 4; + else + os->os_align = 8; + + if ((os->os_link = strdup(".dynsym")) == NULL) + ld_fatal_std(ld, "strdup"); + + lo->lo_hash = os; + + assert(ld->ld_dynsym != NULL && ld->ld_dynsym->sy_size > 0); + + nchains = ld->ld_dynsym->sy_size; + nbuckets = 0; + for (i = 1; + (size_t) i < sizeof(hash_buckets) / sizeof(hash_buckets[0]); + i++) { + if (nchains < hash_buckets[i]) { + nbuckets = hash_buckets[i - 1]; + break; + } + } + if (nbuckets == 0) + nbuckets = hash_buckets[i - 1]; + + if ((buf = calloc(nbuckets + nchains + 2, sizeof(uint32_t))) == NULL) + ld_fatal_std(ld, "calloc"); + + buf[0] = nbuckets; + buf[1] = nchains; + buckets = &buf[2]; + chains = &buf[2 + nbuckets]; + + assert(ld->ld_dyn_symbols != NULL); + + i = 1; + STAILQ_FOREACH(lsb, ld->ld_dyn_symbols, lsb_dyn) { + if (lsb->lsb_name == NULL) { + i++; + continue; + } + + j = elf_hash(lsb->lsb_name) % nbuckets; + chains[i] = buckets[j]; + buckets[j] = i; + i++; + } + + if ((odb = calloc(1, sizeof(*odb))) == NULL) + ld_fatal_std(ld, "calloc"); + + odb->odb_buf = (void *) buf; + odb->odb_size = (nbuckets + nchains + 2) * sizeof(uint32_t); + odb->odb_align = os->os_align; + odb->odb_type = ELF_T_WORD; /* enable libelf translation */ + + (void) ld_output_create_section_element(ld, os, OET_DATA_BUFFER, + odb, NULL); +} |