diff options
Diffstat (limited to 'mdk-stage1/insmod-modutils/obj/obj_reloc.c')
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_reloc.c | 435 |
1 files changed, 435 insertions, 0 deletions
diff --git a/mdk-stage1/insmod-modutils/obj/obj_reloc.c b/mdk-stage1/insmod-modutils/obj/obj_reloc.c new file mode 100644 index 000000000..f5f2de90d --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_reloc.c @@ -0,0 +1,435 @@ +/* Elf relocation 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 <string.h> +#include <assert.h> +#include <alloca.h> + +#include <obj.h> +#include <util.h> + +/*======================================================================*/ + +int +obj_string_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + const char *string) +{ + struct obj_string_patch_struct *p; + struct obj_section *strsec; + size_t len = strlen(string)+1; + char *loc; + + p = xmalloc(sizeof(*p)); + p->next = f->string_patches; + p->reloc_secidx = secidx; + p->reloc_offset = offset; + f->string_patches = p; + + strsec = obj_find_section(f, ".kstrtab"); + if (strsec == NULL) + { + strsec = obj_create_alloced_section(f, ".kstrtab", 1, len); + p->string_offset = 0; + loc = strsec->contents; + } + else + { + p->string_offset = strsec->header.sh_size; + loc = obj_extend_section(strsec, len); + } + memcpy(loc, string, len); + + return 1; +} + +int +obj_symbol_patch(struct obj_file *f, int secidx, ElfW(Addr) offset, + struct obj_symbol *sym) +{ + struct obj_symbol_patch_struct *p; + + p = xmalloc(sizeof(*p)); + p->next = f->symbol_patches; + p->reloc_secidx = secidx; + p->reloc_offset = offset; + p->sym = sym; + f->symbol_patches = p; + + return 1; +} + +int +obj_check_undefineds(struct obj_file *f, int quiet) +{ + unsigned long i; + int ret = 1; + + for (i = 0; i < HASH_BUCKETS; ++i) + { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym ; sym = sym->next) + if (sym->secidx == SHN_UNDEF) + { + if (ELFW(ST_BIND)(sym->info) == STB_WEAK) + { + sym->secidx = SHN_ABS; + sym->value = 0; + } + else if (sym->r_type) /* assumes R_arch_NONE is 0 on all arch */ + { + if (!quiet) + error("unresolved symbol %s", sym->name); + ret = 0; + } + } + } + + return ret; +} + +void +obj_clear_undefineds(struct obj_file *f) +{ + unsigned long i; + struct obj_symbol *sym; + for (i = 0; i < HASH_BUCKETS; ++i) + { + for (sym = f->symtab[i]; sym ; sym = sym->next) + if (sym->secidx == SHN_UNDEF) + { + sym->secidx = SHN_ABS; + sym->value = 0; + } + } +} + +void +obj_allocate_commons(struct obj_file *f) +{ + struct common_entry + { + struct common_entry *next; + struct obj_symbol *sym; + } *common_head = NULL; + + unsigned long i; + + for (i = 0; i < HASH_BUCKETS; ++i) + { + struct obj_symbol *sym; + for (sym = f->symtab[i]; sym ; sym = sym->next) + if (sym->secidx == SHN_COMMON) + { + /* Collect all COMMON symbols and sort them by size so as to + minimize space wasted by alignment requirements. */ + { + struct common_entry **p, *n; + for (p = &common_head; *p ; p = &(*p)->next) + if (sym->size <= (*p)->sym->size) + break; + + n = alloca(sizeof(*n)); + n->next = *p; + n->sym = sym; + *p = n; + } + } + } + + for (i = 1; i < f->local_symtab_size; ++i) + { + struct obj_symbol *sym = f->local_symtab[i]; + if (sym && sym->secidx == SHN_COMMON) + { + struct common_entry **p, *n; + for (p = &common_head; *p ; p = &(*p)->next) + if (sym == (*p)->sym) + break; + else if (sym->size < (*p)->sym->size) + { + n = alloca(sizeof(*n)); + n->next = *p; + n->sym = sym; + *p = n; + break; + } + } + } + + if (common_head) + { + /* Find the bss section. */ + for (i = 0; i < f->header.e_shnum; ++i) + if (f->sections[i]->header.sh_type == SHT_NOBITS) + break; + + /* If for some reason there hadn't been one, create one. */ + if (i == f->header.e_shnum) + { + struct obj_section *sec; + + f->sections = xrealloc(f->sections, (i+1) * sizeof(sec)); + f->sections[i] = sec = arch_new_section(); + f->header.e_shnum = i+1; + + memset(sec, 0, sizeof(*sec)); + sec->header.sh_type = SHT_PROGBITS; + sec->header.sh_flags = SHF_WRITE|SHF_ALLOC; + sec->name = ".bss"; + sec->idx = i; + } + + /* Allocate the COMMONS. */ + { + ElfW(Addr) bss_size = f->sections[i]->header.sh_size; + ElfW(Addr) max_align = f->sections[i]->header.sh_addralign; + struct common_entry *c; + + for (c = common_head; c ; c = c->next) + { + ElfW(Addr) align = c->sym->value; + + if (align > max_align) + max_align = align; + if (bss_size & (align - 1)) + bss_size = (bss_size | (align - 1)) + 1; + + c->sym->secidx = i; + c->sym->value = bss_size; + + bss_size += c->sym->size; + } + + f->sections[i]->header.sh_size = bss_size; + f->sections[i]->header.sh_addralign = max_align; + } + } + + /* For the sake of patch relocation and parameter initialization, + allocate zeroed data for NOBITS sections now. Note that after + this we cannot assume NOBITS are really empty. */ + for (i = 0; i < f->header.e_shnum; ++i) + { + struct obj_section *s = f->sections[i]; + if (s->header.sh_type == SHT_NOBITS) + { + if (s->header.sh_size) + s->contents = memset(xmalloc(s->header.sh_size), + 0, s->header.sh_size); + else + s->contents = NULL; + s->header.sh_type = SHT_PROGBITS; + } + } +} + +unsigned long +obj_load_size (struct obj_file *f) +{ + unsigned long dot = 0; + struct obj_section *sec; + + /* Finalize the positions of the sections relative to one another. */ + + for (sec = f->load_order; sec ; sec = sec->load_next) + { + ElfW(Addr) align; + + align = sec->header.sh_addralign; + if (align && (dot & (align - 1))) + dot = (dot | (align - 1)) + 1; + + sec->header.sh_addr = dot; + dot += sec->header.sh_size; + } + + return dot; +} + +int +obj_relocate (struct obj_file *f, ElfW(Addr) base) +{ + int i, n = f->header.e_shnum; + int ret = 1; + + /* Finalize the addresses of the sections. */ + + arch_finalize_section_address(f, base); + + /* And iterate over all of the relocations. */ + + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *targsec, *strsec; + ElfW(RelM) *rel, *relend; + ElfW(Sym) *symtab; + const char *strtab; + + relsec = f->sections[i]; + if (relsec->header.sh_type != SHT_RELM) + continue; + + symsec = f->sections[relsec->header.sh_link]; + targsec = f->sections[relsec->header.sh_info]; + strsec = f->sections[symsec->header.sh_link]; + + rel = (ElfW(RelM) *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + ElfW(Addr) value = 0; + struct obj_symbol *intsym = NULL; + unsigned long symndx; + ElfW(Sym) *extsym = 0; + const char *errmsg; + + /* Attempt to find a value to use for this relocation. */ + + symndx = ELFW(R_SYM)(rel->r_info); + if (symndx) + { + /* Note we've already checked for undefined symbols. */ + + extsym = &symtab[symndx]; + if (ELFW(ST_BIND)(extsym->st_info) == STB_LOCAL) + { + /* Local symbols we look up in the local table to be sure + we get the one that is really intended. */ + intsym = f->local_symtab[symndx]; + } + else + { + /* Others we look up in the hash table. */ + const char *name; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = obj_find_symbol(f, name); + } + + value = obj_symbol_final_value(f, intsym); + } + +#if SHT_RELM == SHT_RELA +#if defined(__alpha__) && defined(AXP_BROKEN_GAS) + /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9. */ + if (!extsym || !extsym->st_name || + ELFW(ST_BIND)(extsym->st_info) != STB_LOCAL) +#endif + value += rel->r_addend; +#endif + + /* Do it! */ + switch (arch_apply_relocation(f,targsec,symsec,intsym,rel,value)) + { + case obj_reloc_ok: + break; + + case obj_reloc_overflow: + errmsg = "Relocation overflow"; + goto bad_reloc; + case obj_reloc_dangerous: + errmsg = "Dangerous relocation"; + goto bad_reloc; + case obj_reloc_unhandled: + errmsg = "Unhandled relocation"; + goto bad_reloc; + case obj_reloc_constant_gp: + errmsg = "Modules compiled with -mconstant-gp cannot be loaded"; + goto bad_reloc; + bad_reloc: + if (extsym) + { + error("%s of type %ld for %s", errmsg, + (long)ELFW(R_TYPE)(rel->r_info), + strtab + extsym->st_name); + } + else + { + error("%s of type %ld", errmsg, + (long)ELFW(R_TYPE)(rel->r_info)); + } + ret = 0; + break; + } + } + } + + /* Finally, take care of the patches. */ + + if (f->string_patches) + { + struct obj_string_patch_struct *p; + struct obj_section *strsec; + ElfW(Addr) strsec_base; + strsec = obj_find_section(f, ".kstrtab"); + strsec_base = strsec->header.sh_addr; + + for (p = f->string_patches; p ; p = p->next) + { + struct obj_section *targsec = f->sections[p->reloc_secidx]; + *(ElfW(Addr) *)(targsec->contents + p->reloc_offset) + = strsec_base + p->string_offset; + } + } + + if (f->symbol_patches) + { + struct obj_symbol_patch_struct *p; + + for (p = f->symbol_patches; p; p = p->next) + { + struct obj_section *targsec = f->sections[p->reloc_secidx]; + *(ElfW(Addr) *)(targsec->contents + p->reloc_offset) + = obj_symbol_final_value(f, p->sym); + } + } + + return ret; +} + +int +obj_create_image (struct obj_file *f, char *image) +{ + struct obj_section *sec; + ElfW(Addr) base = f->baseaddr; + + for (sec = f->load_order; sec ; sec = sec->load_next) + { + char *secimg; + + if (sec->contents == 0) + continue; + + secimg = image + (sec->header.sh_addr - base); + + /* Note that we allocated data for NOBITS sections earlier. */ + memcpy(secimg, sec->contents, sec->header.sh_size); + } + + return 1; +} |