summaryrefslogtreecommitdiffstats
path: root/mdk-stage1/insmod-modutils/obj/obj_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'mdk-stage1/insmod-modutils/obj/obj_common.c')
-rw-r--r--mdk-stage1/insmod-modutils/obj/obj_common.c399
1 files changed, 399 insertions, 0 deletions
diff --git a/mdk-stage1/insmod-modutils/obj/obj_common.c b/mdk-stage1/insmod-modutils/obj/obj_common.c
new file mode 100644
index 000000000..2a6606c94
--- /dev/null
+++ b/mdk-stage1/insmod-modutils/obj/obj_common.c
@@ -0,0 +1,399 @@
+/* Elf file, section, and symbol manipulation routines.
+ Copyright 1996, 1997 Linux International.
+
+ Contributed by Richard Henderson <rth@tamu.edu>
+
+ This file is part of the Linux modutils.
+
+ 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ident "$Id$"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <obj.h>
+#include <util.h>
+#include <module.h>
+
+/*======================================================================*/
+
+/* Standard ELF hash function. */
+inline unsigned long
+obj_elf_hash_n(const char *name, unsigned long n)
+{
+ unsigned long h = 0;
+ unsigned long g;
+ unsigned char ch;
+
+ while (n > 0)
+ {
+ ch = *name++;
+ h = (h << 4) + ch;
+ if ((g = (h & 0xf0000000)) != 0)
+ {
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ n--;
+ }
+ return h;
+}
+
+unsigned long
+obj_elf_hash (const char *name)
+{
+ return obj_elf_hash_n(name, strlen(name));
+}
+
+void
+obj_set_symbol_compare (struct obj_file *f,
+ int (*cmp)(const char *, const char *),
+ unsigned long (*hash)(const char *))
+{
+ if (cmp)
+ f->symbol_cmp = cmp;
+ if (hash)
+ {
+ struct obj_symbol *tmptab[HASH_BUCKETS], *sym, *next;
+ int i;
+
+ f->symbol_hash = hash;
+
+ memcpy(tmptab, f->symtab, sizeof(tmptab));
+ memset(f->symtab, 0, sizeof(f->symtab));
+
+ for (i = 0; i < HASH_BUCKETS; ++i)
+ for (sym = tmptab[i]; sym ; sym = next)
+ {
+ unsigned long h = hash(sym->name) % HASH_BUCKETS;
+ next = sym->next;
+ sym->next = f->symtab[h];
+ f->symtab[h] = sym;
+ }
+ }
+}
+
+struct obj_symbol *
+obj_add_symbol (struct obj_file *f, const char *name, unsigned long symidx,
+ int info, int secidx, ElfW(Addr) value, unsigned long size)
+{
+ struct obj_symbol *sym;
+ unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+ int n_type = ELFW(ST_TYPE)(info);
+ int n_binding = ELFW(ST_BIND)(info);
+
+ for (sym = f->symtab[hash]; sym; sym = sym->next)
+ if (f->symbol_cmp(sym->name, name) == 0)
+ {
+ int o_secidx = sym->secidx;
+ int o_info = sym->info;
+ int o_type = ELFW(ST_TYPE)(o_info);
+ int o_binding = ELFW(ST_BIND)(o_info);
+
+ /* A redefinition! Is it legal? */
+
+ if (secidx == SHN_UNDEF)
+ return sym;
+ else if (o_secidx == SHN_UNDEF)
+ goto found;
+ else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL)
+ {
+ /* Cope with local and global symbols of the same name
+ in the same object file, as might have been created
+ by ld -r. The only reason locals are now seen at this
+ level at all is so that we can do semi-sensible things
+ with parameters. */
+
+ struct obj_symbol *nsym, **p;
+
+ nsym = arch_new_symbol();
+ nsym->next = sym->next;
+ nsym->ksymidx = -1;
+
+ /* Excise the old (local) symbol from the hash chain. */
+ for (p = &f->symtab[hash]; *p != sym; p = &(*p)->next)
+ continue;
+ *p = sym = nsym;
+ goto found;
+ }
+ else if (n_binding == STB_LOCAL)
+ {
+ /* Another symbol of the same name has already been defined.
+ Just add this to the local table. */
+ sym = arch_new_symbol();
+ sym->next = NULL;
+ sym->ksymidx = -1;
+ f->local_symtab[symidx] = sym;
+ goto found;
+ }
+ else if (n_binding == STB_WEAK)
+ return sym;
+ else if (o_binding == STB_WEAK)
+ goto found;
+ /* Don't unify COMMON symbols with object types the programmer
+ doesn't expect. */
+ else if (secidx == SHN_COMMON
+ && (o_type == STT_NOTYPE || o_type == STT_OBJECT))
+ return sym;
+ else if (o_secidx == SHN_COMMON
+ && (n_type == STT_NOTYPE || n_type == STT_OBJECT))
+ goto found;
+ else
+ {
+ /* Don't report an error if the symbol is coming from
+ the kernel or some external module. */
+ if (secidx <= SHN_HIRESERVE)
+ error("%s multiply defined", name);
+ return sym;
+ }
+ }
+
+ /* Completely new symbol. */
+ sym = arch_new_symbol();
+ sym->next = f->symtab[hash];
+ f->symtab[hash] = sym;
+ sym->ksymidx = -1;
+
+ if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1) {
+ if (symidx >= f->local_symtab_size)
+ error("local symbol %s with index %ld exceeds local_symtab_size %ld",
+ name, (long) symidx, (long) f->local_symtab_size);
+ else
+ f->local_symtab[symidx] = sym;
+ }
+
+found:
+ sym->name = name;
+ sym->value = value;
+ sym->size = size;
+ sym->secidx = secidx;
+ sym->info = info;
+ sym->r_type = 0; /* should be R_arch_NONE for all arch */
+
+ return sym;
+}
+
+struct obj_symbol *
+obj_find_symbol (struct obj_file *f, const char *name)
+{
+ struct obj_symbol *sym;
+ unsigned long hash = f->symbol_hash(name) % HASH_BUCKETS;
+
+ for (sym = f->symtab[hash]; sym; sym = sym->next)
+ if (f->symbol_cmp(sym->name, name) == 0)
+ return sym;
+
+ return NULL;
+}
+
+ElfW(Addr)
+obj_symbol_final_value (struct obj_file *f, struct obj_symbol *sym)
+{
+ if (sym)
+ {
+ if (sym->secidx >= SHN_LORESERVE)
+ return sym->value;
+
+ return sym->value + f->sections[sym->secidx]->header.sh_addr;
+ }
+ else
+ {
+ /* As a special case, a NULL sym has value zero. */
+ return 0;
+ }
+}
+
+struct obj_section *
+obj_find_section (struct obj_file *f, const char *name)
+{
+ int i, n = f->header.e_shnum;
+
+ for (i = 0; i < n; ++i)
+ if (strcmp(f->sections[i]->name, name) == 0)
+ return f->sections[i];
+
+ return NULL;
+}
+
+static int
+obj_load_order_prio(struct obj_section *a)
+{
+ unsigned long af, ac;
+
+ af = a->header.sh_flags;
+
+ ac = 0;
+ if (a->name[0] != '.' || strlen(a->name) != 10 ||
+ strcmp(a->name + 5, ".init")) ac |= 32;
+ if (af & SHF_ALLOC) ac |= 16;
+ if (!(af & SHF_WRITE)) ac |= 8;
+ if (af & SHF_EXECINSTR) ac |= 4;
+ if (a->header.sh_type != SHT_NOBITS) ac |= 2;
+#if defined(ARCH_ia64)
+ if (af & SHF_IA_64_SHORT) ac -= 1;
+#endif
+
+ return ac;
+}
+
+void
+obj_insert_section_load_order (struct obj_file *f, struct obj_section *sec)
+{
+ struct obj_section **p;
+ int prio = obj_load_order_prio(sec);
+ for (p = f->load_order_search_start; *p ; p = &(*p)->load_next)
+ if (obj_load_order_prio(*p) < prio)
+ break;
+ sec->load_next = *p;
+ *p = sec;
+}
+
+struct obj_section *
+obj_create_alloced_section (struct obj_file *f, const char *name,
+ unsigned long align, unsigned long size)
+{
+ int newidx = f->header.e_shnum++;
+ struct obj_section *sec;
+
+ f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec));
+ f->sections[newidx] = sec = arch_new_section();
+
+ memset(sec, 0, sizeof(*sec));
+ sec->header.sh_type = SHT_PROGBITS;
+ sec->header.sh_flags = SHF_WRITE|SHF_ALLOC;
+ sec->header.sh_size = size;
+ sec->header.sh_addralign = align;
+ sec->name = name;
+ sec->idx = newidx;
+ if (size)
+ sec->contents = xmalloc(size);
+
+ obj_insert_section_load_order(f, sec);
+
+ return sec;
+}
+
+struct obj_section *
+obj_create_alloced_section_first (struct obj_file *f, const char *name,
+ unsigned long align, unsigned long size)
+{
+ int newidx = f->header.e_shnum++;
+ struct obj_section *sec;
+
+ f->sections = xrealloc(f->sections, (newidx+1) * sizeof(sec));
+ f->sections[newidx] = sec = arch_new_section();
+
+ memset(sec, 0, sizeof(*sec));
+ sec->header.sh_type = SHT_PROGBITS;
+ sec->header.sh_flags = SHF_WRITE|SHF_ALLOC;
+ sec->header.sh_size = size;
+ sec->header.sh_addralign = align;
+ sec->name = name;
+ sec->idx = newidx;
+ if (size)
+ sec->contents = xmalloc(size);
+
+ sec->load_next = f->load_order;
+ f->load_order = sec;
+ if (f->load_order_search_start == &f->load_order)
+ f->load_order_search_start = &sec->load_next;
+
+ return sec;
+}
+
+void *
+obj_extend_section (struct obj_section *sec, unsigned long more)
+{
+ unsigned long oldsize = sec->header.sh_size;
+ sec->contents = xrealloc(sec->contents, sec->header.sh_size += more);
+ return sec->contents + oldsize;
+}
+
+/* Convert an object pointer (address) to a native pointer and vice versa.
+ * It gets interesting when the object has 64 bit pointers but modutils
+ * is running 32 bit. This is nasty code but it stops the compiler giving
+ * spurious warning messages. "I know what I am doing" ...
+ */
+
+void *
+obj_addr_to_native_ptr (ElfW(Addr) addr)
+{
+ unsigned int convert = (sizeof(void *) << 8) + sizeof(addr); /* to, from */
+ union obj_ptr_4 p4;
+ union obj_ptr_8 p8;
+ switch (convert) {
+ case 0x0404:
+ p4.addr = addr;
+ return(p4.ptr);
+ break;
+ case 0x0408:
+ p4.addr = addr;
+ if (p4.addr != addr) {
+ error("obj_addr_to_native_ptr truncation %" tgt_long_fmt "x",
+ (tgt_long) addr);
+ exit(1);
+ }
+ return(p4.ptr);
+ break;
+ case 0x0804:
+ p8.addr = addr;
+ return(p8.ptr);
+ break;
+ case 0x0808:
+ p8.addr = addr;
+ return(p8.ptr);
+ break;
+ default:
+ error("obj_addr_to_native_ptr unknown conversion 0x%04x", convert);
+ exit(1);
+ }
+}
+
+ElfW(Addr)
+obj_native_ptr_to_addr (void *ptr)
+{
+ unsigned int convert = (sizeof(ElfW(Addr)) << 8) + sizeof(ptr); /* to, from */
+ union obj_ptr_4 p4;
+ union obj_ptr_8 p8;
+ switch (convert) {
+ case 0x0404:
+ p4.ptr = ptr;
+ return(p4.addr);
+ break;
+ case 0x0408:
+ p8.ptr = ptr;
+ p4.addr = p8.addr;
+ if (p4.addr != p8.addr) {
+ error("obj_native_ptr_to_addr truncation %" tgt_long_fmt "x",
+ (tgt_long) p8.addr);
+ exit(1);
+ }
+ return(p4.addr);
+ break;
+ case 0x0804:
+ p4.ptr = ptr;
+ return(p4.addr); /* compiler expands to 8 */
+ break;
+ case 0x0808:
+ p8.ptr = ptr;
+ return(p8.addr);
+ break;
+ default:
+ error("obj_native_ptr_to_addr unknown conversion 0x%04x", convert);
+ exit(1);
+ }
+}