summaryrefslogtreecommitdiff
path: root/ldso/ldso/hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldso/ldso/hash.c')
-rw-r--r--ldso/ldso/hash.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/ldso/ldso/hash.c b/ldso/ldso/hash.c
new file mode 100644
index 000000000..4eab974a8
--- /dev/null
+++ b/ldso/ldso/hash.c
@@ -0,0 +1,284 @@
+/* Run an ELF binary on a linux system.
+
+ Copyright (C) 1993-1996, Eric Youngdale.
+
+ 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 2, 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+
+/* Various symbol table handling functions, including symbol lookup */
+
+/*#include <stdlib.h>*/
+#include "string.h"
+#include <linux/unistd.h>
+#include <linux/elf.h>
+
+#include "libdl/dlfcn.h"
+#include "hash.h"
+#include "linuxelf.h"
+#include "syscall.h"
+#include "string.h"
+#include "sysdep.h"
+
+/*
+ * This is the start of the linked list that describes all of the files present
+ * in the system with pointers to all of the symbol, string, and hash tables,
+ * as well as all of the other good stuff in the binary.
+ */
+
+struct elf_resolve * _dl_loaded_modules = NULL;
+
+/*
+ * This is the list of modules that are loaded when the image is first
+ * started. As we add more via dlopen, they get added into other
+ * chains.
+ */
+struct dyn_elf * _dl_symbol_tables = NULL;
+
+/*
+ * This is the list of modules that are loaded via dlopen. We may need
+ * to search these for RTLD_GLOBAL files.
+ */
+struct dyn_elf * _dl_handles = NULL;
+
+
+/*
+ * This is the hash function that is used by the ELF linker to generate
+ * the hash table that each executable and library is required to
+ * have. We need it to decode the hash table.
+ */
+
+unsigned long _dl_elf_hash(const char * name){
+ unsigned long hash = 0;
+ unsigned long tmp;
+
+ while (*name){
+ hash = (hash << 4) + *name++;
+ if((tmp = hash & 0xf0000000)) hash ^= tmp >> 24;
+ hash &= ~tmp;
+ };
+ return hash;
+}
+
+/*
+ * Check to see if a library has already been added to the hash chain.
+ */
+struct elf_resolve * _dl_check_hashed_files(char * libname){
+ struct elf_resolve * tpnt;
+ int len = _dl_strlen(libname);
+
+ for (tpnt = _dl_loaded_modules; tpnt; tpnt = tpnt->next) {
+ if (_dl_strncmp(tpnt->libname, libname, len) == 0 &&
+ (tpnt->libname[len] == '\0' || tpnt->libname[len] == '.'))
+ return tpnt;
+ }
+
+ return NULL;
+}
+
+/*
+ * We call this function when we have just read an ELF library or executable.
+ * We add the relevant info to the symbol chain, so that we can resolve all
+ * externals properly.
+ */
+
+struct elf_resolve * _dl_add_elf_hash_table(char * libname,
+ char * loadaddr,
+ unsigned int * dynamic_info,
+ unsigned int dynamic_addr,
+ unsigned int dynamic_size){
+ unsigned int * hash_addr;
+ struct elf_resolve * tpnt;
+ int i;
+
+ if (!_dl_loaded_modules) {
+ tpnt = _dl_loaded_modules =
+ (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
+ _dl_memset (tpnt, 0, sizeof (*tpnt));
+ }
+ else {
+ tpnt = _dl_loaded_modules;
+ while(tpnt->next) tpnt = tpnt->next;
+ tpnt->next = (struct elf_resolve *) _dl_malloc(sizeof(struct elf_resolve));
+ _dl_memset (tpnt->next, 0, sizeof (*(tpnt->next)));
+ tpnt->next->prev = tpnt;
+ tpnt = tpnt->next;
+ };
+
+ tpnt->next = NULL;
+ tpnt->init_flag = 0;
+ tpnt->libname = _dl_strdup(libname);
+ tpnt->dynamic_addr = dynamic_addr;
+ tpnt->dynamic_size = dynamic_size;
+ tpnt->libtype = loaded_file;
+
+ if( dynamic_info[DT_HASH] != 0 )
+ {
+ hash_addr = (unsigned int *) (dynamic_info[DT_HASH] + loadaddr);
+ tpnt->nbucket = *hash_addr++;
+ tpnt->nchain = *hash_addr++;
+ tpnt->elf_buckets = hash_addr;
+ hash_addr += tpnt->nbucket;
+ tpnt->chains = hash_addr;
+ }
+ tpnt->loadaddr = loadaddr;
+ for(i=0; i<24; i++) tpnt->dynamic_info[i] = dynamic_info[i];
+ return tpnt;
+}
+
+
+/*
+ * This function resolves externals, and this is either called when we process
+ * relocations or when we call an entry in the PLT table for the first time.
+ */
+
+char * _dl_find_hash(char * name, struct dyn_elf * rpnt1,
+ unsigned int instr_addr, struct elf_resolve * f_tpnt,
+ int copyrel){
+ struct elf_resolve * tpnt;
+ int si;
+ char * pnt;
+ int pass;
+ char * strtab;
+ struct elf32_sym * symtab;
+ unsigned int elf_hash_number, hn;
+ char * weak_result;
+ struct elf_resolve * first_def;
+ struct dyn_elf * rpnt, first;
+ char * data_result = 0; /* nakao */
+
+ weak_result = 0;
+ elf_hash_number = _dl_elf_hash(name);
+
+ /* A quick little hack to make sure that any symbol in the executable
+ will be preferred to one in a shared library. This is necessary so
+ that any shared library data symbols referenced in the executable
+ will be seen at the same address by the executable, shared libraries
+ and dynamically loaded code. -Rob Ryan (robr@cmu.edu) */
+ if(!copyrel && rpnt1) {
+ first=(*_dl_symbol_tables);
+ first.next=rpnt1;
+ rpnt1=(&first);
+ }
+
+ /*
+ * The passes are so that we can first search the regular symbols
+ * for whatever module was specified, and then search anything
+ * loaded with RTLD_GLOBAL. When pass is 1, it means we are just
+ * starting the first dlopened module, and anything above that
+ * is just the next one in the chain.
+ */
+ for(pass = 0; (1==1); pass++)
+ {
+
+ /*
+ * If we are just starting to search for RTLD_GLOBAL, setup
+ * the pointer for the start of the search.
+ */
+ if( pass == 1) {
+ rpnt1 = _dl_handles;
+ }
+
+ /*
+ * Anything after this, we need to skip to the next module.
+ */
+ else if( pass >= 2) {
+ rpnt1 = rpnt1->next_handle;
+ }
+
+ /*
+ * Make sure we still have a module, and make sure that this
+ * module was loaded with RTLD_GLOBAL.
+ */
+ if( pass != 0 )
+ {
+ if( rpnt1 == NULL ) break;
+ if( (rpnt1->flags & RTLD_GLOBAL) == 0) continue;
+ }
+
+ for(rpnt = (rpnt1 ? rpnt1 : _dl_symbol_tables);
+ rpnt; rpnt = rpnt->next) {
+ tpnt = rpnt->dyn;
+
+ /*
+ * The idea here is that if we are using dlsym, we want to
+ * first search the entire chain loaded from dlopen, and
+ * return a result from that if we found anything. If this
+ * fails, then we continue the search into the stuff loaded
+ * when the image was activated. For normal lookups, we start
+ * with rpnt == NULL, so we should never hit this.
+ */
+ if( tpnt->libtype == elf_executable
+ && weak_result != 0 )
+ {
+ break;
+ }
+
+ /*
+ * Avoid calling .urem here.
+ */
+ do_rem(hn, elf_hash_number, tpnt->nbucket);
+ symtab = (struct elf32_sym *) (tpnt->dynamic_info[DT_SYMTAB] +
+ tpnt->loadaddr);
+ strtab = (char *) (tpnt->dynamic_info[DT_STRTAB] + tpnt->loadaddr);
+ /*
+ * This crap is required because the first instance of a
+ * symbol on the chain will be used for all symbol references.
+ * Thus this instance must be resolved to an address that
+ * contains the actual function,
+ */
+
+ first_def = NULL;
+
+ for(si = tpnt->elf_buckets[hn]; si; si = tpnt->chains[si]){
+ pnt = strtab + symtab[si].st_name;
+
+ if(_dl_strcmp(pnt, name) == 0 &&
+ (ELF32_ST_TYPE(symtab[si].st_info) == STT_FUNC ||
+ ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE ||
+ ELF32_ST_TYPE(symtab[si].st_info) == STT_OBJECT) &&
+ symtab[si].st_value != 0) {
+
+ /* Here we make sure that we find a module where the symbol is
+ * actually defined.
+ */
+
+ if(f_tpnt) {
+ if(!first_def) first_def = tpnt;
+ if(first_def == f_tpnt && symtab[si].st_shndx == 0)
+ continue;
+ }
+
+ switch(ELF32_ST_BIND(symtab[si].st_info)){
+ case STB_GLOBAL:
+ if ( tpnt->libtype != elf_executable
+ && ELF32_ST_TYPE(symtab[si].st_info) == STT_NOTYPE) { /* nakao */
+ data_result = tpnt->loadaddr + symtab[si].st_value; /* nakao */
+ break; /* nakao */
+ } else /* nakao */
+ return tpnt->loadaddr + symtab[si].st_value;
+ case STB_WEAK:
+ if (!weak_result) weak_result = tpnt->loadaddr + symtab[si].st_value;
+ break;
+ default: /* Do local symbols need to be examined? */
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (data_result) return data_result; /* nakao */
+ return weak_result;
+}