summaryrefslogtreecommitdiff
path: root/ld/ld_hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'ld/ld_hash.c')
-rw-r--r--ld/ld_hash.c126
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);
+}