diff options
Diffstat (limited to 'mdk-stage1/insmod-modutils/obj')
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/Makefile | 31 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_alpha.c | 305 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_arm.c | 318 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_common.c | 399 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_hppa.c | 668 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_hppa64.c | 686 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_i386.c | 245 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_ia64.c | 1065 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_kallsyms.c | 292 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_load.c | 354 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_m68k.c | 147 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_mips.c | 238 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_ppc.c | 255 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_reloc.c | 435 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_s390.c | 245 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_sparc.c | 226 | ||||
-rw-r--r-- | mdk-stage1/insmod-modutils/obj/obj_sparc64.c | 352 |
17 files changed, 6261 insertions, 0 deletions
diff --git a/mdk-stage1/insmod-modutils/obj/Makefile b/mdk-stage1/insmod-modutils/obj/Makefile new file mode 100644 index 000000000..9bcf8bac8 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/Makefile @@ -0,0 +1,31 @@ + #****************************************************************************** + # + # insmod from modutils (generic) + # + # $Id$ + # + # Copyright 1996, 1997 Linux International. + # + #***************************************************************************** + +top_dir = ../.. + +include $(top_dir)/Makefile.common + + +all: libobj.a + +clean: + rm -f *.o *.a + + +FLAGS = -c -Wall -Os -fomit-frame-pointer -I./../include -D_GNU_SOURCE -DELF_MACHINE_H='"elf_$(ARCH).h"' -DARCH_$(ARCH) -DCONFIG_ROOT_CHECK_OFF=0 + +OBJS = obj_kallsyms.o obj_common.o obj_load.o obj_reloc.o obj_$(ARCH).o + +libobj.a: $(OBJS) + ar cru $@ $^ + ranlib $@ + +.c.o: + gcc $(FLAGS) $(GLIBC_INCLUDES) -c $< diff --git a/mdk-stage1/insmod-modutils/obj/obj_alpha.c b/mdk-stage1/insmod-modutils/obj/obj_alpha.c new file mode 100644 index 000000000..4006b3442 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_alpha.c @@ -0,0 +1,305 @@ +/* Alpha specific support for Elf loading and relocation. + 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 <module.h> +#include <obj.h> +#include <util.h> + +/*======================================================================*/ + +struct alpha_got_entry +{ + struct alpha_got_entry *next; + ElfW(Addr) addend; + int offset; + int reloc_done; +}; + +struct alpha_file +{ + struct obj_file root; + struct obj_section *got; +}; + +struct alpha_symbol +{ + struct obj_symbol root; + struct alpha_got_entry *got_entries; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct alpha_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct alpha_symbol *sym; + sym = xmalloc(sizeof(*sym)); + sym->got_entries = NULL; + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + struct alpha_file *af = (struct alpha_file *)f; + struct alpha_symbol *asym = (struct alpha_symbol *)sym; + + unsigned long *lloc = (unsigned long *)(targsec->contents + rel->r_offset); + unsigned int *iloc = (unsigned int *)lloc; + Elf64_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf64_Addr gp = af->got->header.sh_addr + 0x8000; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF64_R_TYPE(rel->r_info)) + { + case R_ALPHA_NONE: + case R_ALPHA_LITUSE: + break; + + case R_ALPHA_REFQUAD: + *lloc += v; + break; + + case R_ALPHA_GPREL32: + v -= gp; + if ((Elf64_Sxword)v > 0x7fffffff + || (Elf64_Sxword)v < -(Elf64_Sxword)0x80000000) + ret = obj_reloc_overflow; + *iloc = v; + break; + + case R_ALPHA_LITERAL: + { + struct alpha_got_entry *gotent; + + assert(asym != NULL); + gotent = asym->got_entries; + while (gotent->addend != rel->r_addend) + gotent = gotent->next; + + if (!gotent->reloc_done) + { + *(unsigned long *)(af->got->contents + gotent->offset) = v; + gotent->reloc_done = 1; + } + + *iloc = (*iloc & ~0xffff) | ((gotent->offset - 0x8000) & 0xffff); + } + break; + + case R_ALPHA_GPDISP: + { + unsigned int *p_ldah, *p_lda; + unsigned int i_ldah, i_lda, hi, lo; + + p_ldah = iloc; + p_lda = (unsigned int *)((char *)iloc + rel->r_addend); + i_ldah = *p_ldah; + i_lda = *p_lda; + + /* Make sure the instructions are righteous. */ + if ((i_ldah >> 26) != 9 || (i_lda >> 26) != 8) + ret = obj_reloc_dangerous; + + /* Extract the existing addend. */ + v = (i_ldah & 0xffff) << 16 | (i_lda & 0xffff); + v = (v ^ 0x80008000) - 0x80008000; + + v += gp - dot; + + if ((Elf64_Sxword)v >= 0x7fff8000 + || (Elf64_Sxword)v < -(Elf64_Sxword)0x80000000) + ret = obj_reloc_overflow; + + /* Modify the instructions and finish up. */ + lo = v & 0xffff; + hi = ((v >> 16) + ((v >> 15) & 1)) & 0xffff; + + *p_ldah = (i_ldah & 0xffff0000) | hi; + *p_lda = (i_lda & 0xffff0000) | lo; + } + break; + + case R_ALPHA_BRADDR: + v -= dot + 4; + if (v % 4) + ret = obj_reloc_dangerous; + else if ((Elf64_Sxword)v > 0x3fffff + || (Elf64_Sxword)v < -(Elf64_Sxword)0x400000) + ret = obj_reloc_overflow; + v /= 4; + + *iloc = (*iloc & ~0x1fffff) | (v & 0x1fffff); + break; + + case R_ALPHA_HINT: + v -= dot + 4; + if (v % 4) + ret = obj_reloc_dangerous; + v /= 4; + + *iloc = (*iloc & ~0x3fff) | (v & 0x3fff); + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct alpha_file *af = (struct alpha_file *)f; + int i, n, offset = 0; + + n = af->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf64_Rela *rel, *relend; + Elf64_Sym *symtab; + const char *strtab; + + relsec = af->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = af->root.sections[relsec->header.sh_link]; + strsec = af->root.sections[symsec->header.sh_link]; + + rel = (Elf64_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf64_Rela)); + symtab = (Elf64_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + struct alpha_got_entry *ent; + Elf64_Sym *extsym; + struct alpha_symbol *intsym; + const char *name; + + if (ELF64_R_TYPE(rel->r_info) != R_ALPHA_LITERAL) + continue; + + extsym = &symtab[ELF64_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct alpha_symbol *)obj_find_symbol(&af->root, name); + + for (ent = intsym->got_entries; ent ; ent = ent->next) + if (ent->addend == rel->r_addend) + goto found; + + ent = xmalloc(sizeof(*ent)); + ent->addend = rel->r_addend; + ent->offset = offset; + ent->reloc_done = 0; + ent->next = intsym->got_entries; + intsym->got_entries = ent; + offset += 8; + + found:; + } + } + + if (offset > 0x10000) + { + error(".got section overflow: %#x > 0x10000", offset); + return 0; + } + + /* We always want a .got section so that we always have a GP for + use with GPDISP and GPREL32 relocs. Besides, if the section + is empty we don't use up space anyway. */ + af->got = obj_create_alloced_section(&af->root, ".got", 8, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + struct alpha_file *af = (struct alpha_file *)f; + + mod->gp = af->got->header.sh_addr + 0x8000; + + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_arm.c b/mdk-stage1/insmod-modutils/obj/obj_arm.c new file mode 100644 index 000000000..7a843f947 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_arm.c @@ -0,0 +1,318 @@ +/* ARM specific support for Elf loading and relocation. + Copyright 1996, 1997, 1998 Linux International. + + Contributed by Phil Blundell <philb@gnu.org> + and wms <woody@corelcomputer.com> + based on the i386 code 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 <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct arm_plt_entry +{ + int offset; + int allocated:1; + int inited:1; // has been set up +}; + +struct arm_got_entry +{ + int offset; + int allocated : 1; + unsigned reloc_done : 1; +}; + +struct arm_file +{ + struct obj_file root; + struct obj_section *plt; + struct obj_section *got; +}; + +struct arm_symbol +{ + struct obj_symbol root; + struct arm_plt_entry pltent; + struct arm_got_entry gotent; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct arm_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct arm_symbol *sym; + sym = xmalloc(sizeof(*sym)); + memset(&sym->gotent, 0, sizeof(sym->gotent)); + memset(&sym->pltent, 0, sizeof(sym->pltent)); + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rel *rel, + Elf32_Addr v) +{ + struct arm_file *afile = (struct arm_file *)f; + struct arm_symbol *asym = (struct arm_symbol *)sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf32_Addr got = afile->got ? afile->got->header.sh_addr : 0; + Elf32_Addr plt = afile->plt ? afile->plt->header.sh_addr : 0; + + struct arm_plt_entry *pe; + unsigned long *ip; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_ARM_NONE: + break; + + case R_ARM_ABS32: + *loc += v; + break; + + case R_ARM_GOT32: + /* needs an entry in the .got: set it, once */ + if (! asym->gotent.reloc_done) + { + asym->gotent.reloc_done = 1; + *(Elf32_Addr *)(afile->got->contents + asym->gotent.offset) = v; + } + /* make the reloc with_respect_to_.got */ + *loc += asym->gotent.offset; + break; + + /* relative reloc, always to _GLOBAL_OFFSET_TABLE_ (which is .got) + similar to branch, but is full 32 bits relative */ + case R_ARM_GOTPC: + assert(got); + *loc += got - dot; + break; + + case R_ARM_PC24: + case R_ARM_PLT32: + /* find the plt entry and initialize it if necessary */ + assert(asym != NULL); + pe = (struct arm_plt_entry*) &asym->pltent; + if (! pe->inited) + { + ip = (unsigned long *) (afile->plt->contents + pe->offset); + ip[0] = 0xe51ff004; /* ldr pc,[pc,#-4] */ + ip[1] = v; /* sym@ */ + pe->inited = 1; + } + + /* relative distance to target */ + v -= dot; + /* if the target is too far away.... */ + if ((int)v < -0x02000000 || (int)v >= 0x02000000) + { + /* go via the plt */ + v = plt + pe->offset - dot; + } + if (v & 3) + ret = obj_reloc_dangerous; + + /* Convert to words. */ + v >>= 2; + + /* merge the offset into the instruction. */ + *loc = (*loc & ~0x00ffffff) | ((v + *loc) & 0x00ffffff); + break; + + /* address relative to the got */ + case R_ARM_GOTOFF: + assert(got); + *loc += v - got; + break; + + default: + printf("Warning: unhandled reloc %d\n",ELF32_R_TYPE(rel->r_info)); + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct arm_file *afile = (struct arm_file *) f; + int i; + struct obj_section *sec, *syms, *strs; + ElfW(Rel) *rel, *relend; + ElfW(Sym) *symtab, *extsym; + const char *strtab, *name; + struct arm_symbol *intsym; + struct arm_plt_entry *pe; + struct arm_got_entry *ge; + int got_offset = 0, plt_offset = 0; + + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type != SHT_RELM) + continue; + syms = f->sections[sec->header.sh_link]; + strs = f->sections[syms->header.sh_link]; + + rel = (ElfW(RelM) *) sec->contents; + relend = rel + (sec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *) syms->contents; + strtab = (const char *) strs->contents; + + for (; rel < relend; ++rel) + { + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + + switch(ELF32_R_TYPE(rel->r_info)) { + case R_ARM_PC24: + case R_ARM_PLT32: + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct arm_symbol *) obj_find_symbol(f, name); + + pe = &intsym->pltent; + + if (! pe->allocated) + { + pe->allocated = 1; + pe->offset = plt_offset; + plt_offset += 8; + pe->inited = 0; + } + break; + + /* these two don_t need got entries, but they need + the .got to exist */ + case R_ARM_GOTOFF: + case R_ARM_GOTPC: + if (got_offset==0) got_offset = 4; + break; + + case R_ARM_GOT32: + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct arm_symbol *) obj_find_symbol(f, name); + + ge = (struct arm_got_entry *) &intsym->gotent; + if (! ge->allocated) + { + ge->allocated = 1; + ge->offset = got_offset; + got_offset += sizeof(void*); + } + break; + + default: + continue; + } + } + } + + /* if there was a _GLOBAL_OFFSET_TABLE_, then the .got section + exists already; find it and use it */ + if (got_offset) + { + struct obj_section* sec = obj_find_section(f, ".got"); + if (sec) + obj_extend_section(sec, got_offset); + else + { + sec = obj_create_alloced_section(f, ".got", 8, got_offset); + assert(sec); + } + afile->got = sec; + } + + if (plt_offset) + afile->plt = obj_create_alloced_section(f, ".plt", 8, plt_offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} 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); + } +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_hppa.c b/mdk-stage1/insmod-modutils/obj/obj_hppa.c new file mode 100644 index 000000000..4207e692e --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_hppa.c @@ -0,0 +1,668 @@ +/* + * PA-RISC specific support for Elf loading and relocation. + * Copyright 2000 David Huggins-Daines <dhd@linuxcare.com>, Linuxcare Inc. + * Copyright 2000 Richard Hirst <rhirst@linuxcare.com>, Linuxcare Inc. + * + * Based on the IA-64 support, which is: + * Copyright 2000 Mike Stephens <mike.stephens@intel.com> + * + * 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. + */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> +#include <modstat.h> /* For ksyms */ + + +typedef struct _hppa_stub_t +{ + struct _hppa_stub_t *next; + int offset; + int reloc_done; +} hppa_stub_t; + +typedef struct _hppa_symbol_t +{ + struct obj_symbol root; + hppa_stub_t *stub; +} hppa_symbol_t; + +typedef struct _hppa_file_t +{ + struct obj_file root; + struct obj_section *stub; + Elf32_Addr dp; +} hppa_file_t; + +/* The ABI defines various more esoteric types, but these are the only + ones we actually need. */ +enum hppa_fsel +{ + e_fsel, + e_lsel, + e_rsel, + e_lrsel, + e_rrsel +}; + +/* This could be a call to obj_create_alloced_section() followed + * by an overwrite of sec->header.sh_flags. + */ + +struct obj_section * +obj_hppa_create_alloced_section (struct obj_file *f, const char *name, + unsigned long align, unsigned long size, + unsigned long sh_flags) +{ + 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 = sh_flags; + 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_file * +arch_new_file (void) +{ + hppa_file_t *f; + f = xmalloc(sizeof(*f)); + f->stub = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + hppa_symbol_t *sym; + sym = xmalloc(sizeof(*sym)); + sym->stub = NULL; + return &sym->root; +} + +/* This is called for architecture specific sections we might need to + do special things to. */ +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +/* ================================================================= + + These functions are from libhppa.h in the GNU BFD library. + (c) 1990, 91, 92, 93, 94, 95, 96, 98, 99, 2000 + Free Software Foundation, Inc. + + ================================================================= */ + +/* The *sign_extend functions are used to assemble various bitfields + taken from an instruction and return the resulting immediate + value. */ + +static inline int +sign_extend (x, len) + int x, len; +{ + int signbit = (1 << (len - 1)); + int mask = (signbit << 1) - 1; + return ((x & mask) ^ signbit) - signbit; +} + +static inline int +low_sign_extend (x, len) + int x, len; +{ + return (x >> 1) - ((x & 1) << (len - 1)); +} + + +/* The re_assemble_* functions prepare an immediate value for + insertion into an opcode. pa-risc uses all sorts of weird bitfields + in the instruction to hold the value. */ + +static inline int +sign_unext (x, len) + int x, len; +{ + int len_ones; + + len_ones = (1 << len) - 1; + + return x & len_ones; +} + +static inline int +low_sign_unext (x, len) + int x, len; +{ + int temp; + int sign; + + sign = (x >> (len-1)) & 1; + + temp = sign_unext (x, len-1); + + return (temp << 1) | sign; +} + +static inline int +re_assemble_3 (as3) + int as3; +{ + return (( (as3 & 4) << (13-2)) + | ((as3 & 3) << (13+1))); +} + +static inline int +re_assemble_12 (as12) + int as12; +{ + return (( (as12 & 0x800) >> 11) + | ((as12 & 0x400) >> (10 - 2)) + | ((as12 & 0x3ff) << (1 + 2))); +} + +static inline int +re_assemble_14 (as14) + int as14; +{ + return (( (as14 & 0x1fff) << 1) + | ((as14 & 0x2000) >> 13)); +} + +static inline int +re_assemble_16 (as16) + int as16; +{ + int s, t; + + /* Unusual 16-bit encoding, for wide mode only. */ + t = (as16 << 1) & 0xffff; + s = (as16 & 0x8000); + return (t ^ s ^ (s >> 1)) | (s >> 15); +} + +static inline int +re_assemble_17 (as17) + int as17; +{ + return (( (as17 & 0x10000) >> 16) + | ((as17 & 0x0f800) << (16 - 11)) + | ((as17 & 0x00400) >> (10 - 2)) + | ((as17 & 0x003ff) << (1 + 2))); +} + +static inline int +re_assemble_21 (as21) + int as21; +{ + return (( (as21 & 0x100000) >> 20) + | ((as21 & 0x0ffe00) >> 8) + | ((as21 & 0x000180) << 7) + | ((as21 & 0x00007c) << 14) + | ((as21 & 0x000003) << 12)); +} + +static inline int +re_assemble_22 (as22) + int as22; +{ + return (( (as22 & 0x200000) >> 21) + | ((as22 & 0x1f0000) << (21 - 16)) + | ((as22 & 0x00f800) << (16 - 11)) + | ((as22 & 0x000400) >> (10 - 2)) + | ((as22 & 0x0003ff) << (1 + 2))); +} + + +/* Handle field selectors for PA instructions. + The L and R (and LS, RS etc.) selectors are used in pairs to form a + full 32 bit address. eg. + + LDIL L'start,%r1 ; put left part into r1 + LDW R'start(%r1),%r2 ; add r1 and right part to form address + + This function returns sign extended values in all cases. +*/ + +static inline unsigned int +hppa_field_adjust (value, addend, r_field) + unsigned int value; + int addend; + enum hppa_fsel r_field; +{ + unsigned int sym_val; + + sym_val = value - addend; + switch (r_field) + { + case e_fsel: + /* F: No change. */ + break; + + case e_lsel: + /* L: Select top 21 bits. */ + value = value >> 11; + break; + + case e_rsel: + /* R: Select bottom 11 bits. */ + value = value & 0x7ff; + break; + + case e_lrsel: + /* LR: L with rounding of the addend to nearest 8k. */ + value = sym_val + ((addend + 0x1000) & -0x2000); + value = value >> 11; + break; + + case e_rrsel: + /* RR: R with rounding of the addend to nearest 8k. + We need to return a value such that 2048 * LR'x + RR'x == x + ie. RR'x = s+a - (s + (((a + 0x1000) & -0x2000) & -0x800)) + . = s+a - ((s & -0x800) + ((a + 0x1000) & -0x2000)) + . = (s & 0x7ff) + a - ((a + 0x1000) & -0x2000) */ + value = (sym_val & 0x7ff) + (((addend & 0x1fff) ^ 0x1000) - 0x1000); + break; + + default: + abort(); + } + return value; +} + +/* Insert VALUE into INSN using R_FORMAT to determine exactly what + bits to change. */ + +static inline int +hppa_rebuild_insn (insn, value, r_format) + int insn; + int value; + int r_format; +{ + switch (r_format) + { + case 11: + return (insn & ~ 0x7ff) | low_sign_unext (value, 11); + + case 12: + return (insn & ~ 0x1ffd) | re_assemble_12 (value); + + + case 10: + return (insn & ~ 0x3ff1) | re_assemble_14 (value & -8); + + case -11: + return (insn & ~ 0x3ff9) | re_assemble_14 (value & -4); + + case 14: + return (insn & ~ 0x3fff) | re_assemble_14 (value); + + + case -10: + return (insn & ~ 0xfff1) | re_assemble_16 (value & -8); + + case -16: + return (insn & ~ 0xfff9) | re_assemble_16 (value & -4); + + case 16: + return (insn & ~ 0xffff) | re_assemble_16 (value); + + + case 17: + return (insn & ~ 0x1f1ffd) | re_assemble_17 (value); + + case 21: + return (insn & ~ 0x1fffff) | re_assemble_21 (value); + + case 22: + return (insn & ~ 0x3ff1ffd) | re_assemble_22 (value); + + case 32: + return value; + + default: + abort (); + } + return insn; +} + +/* ==================================================================== + + End of functions from GNU BFD. + + ==================================================================== */ + +/* This is where we get the opportunity to create any extra dynamic + sections we might need. In our case we do not need a GOT because + our code is not PIC, but we do need to create a stub section. + + This is significantly less complex than what we do for shared + libraries because, obviously, modules are not shared. Also we have + no issues related to symbol visibility, lazy linking, etc. + The kernels dp is fixed (at symbol data_start), and we can fix up any + DPREL refs in the module to use that same dp value. + All PCREL17F refs result in a stub with the following format: + + ldil L'func_addr,%r1 + be,n R'func_addr(%sr4,%r1) + + Note, all PCREL17F get a stub, regardless of whether they are + local or external. With local ones, and external ones to other + modules, there is a good chance we could manage without the stub. + I'll leave that for a future optimisation. + */ + +#define LDIL_R1 0x20200000 /* ldil L'XXX,%r1 */ +#define BE_N_SR4_R1 0xe0202002 /* be,n R'XXX(%sr4,%r1) */ + +#define STUB_SIZE 8 + +int +arch_create_got(struct obj_file *f) +{ + hppa_file_t *hfile = (hppa_file_t *)f; + int i, n; + int stub_offset = 0; + + /* Create stub section. + * XXX set flags, see obj_ia64.c + */ + hfile->stub = obj_create_alloced_section(f, ".stub", STUB_SIZE, 0); + + /* Actually this is a lot like check_relocs() in a BFD backend. We + walk all sections and all their relocations and look for ones + that need special treatment. */ + n = hfile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf32_Rela *rel, *relend; + Elf32_Sym *symtab; + char const *strtab; + + relsec = hfile->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = hfile->root.sections[relsec->header.sh_link]; + strsec = hfile->root.sections[symsec->header.sh_link]; + + rel = (Elf32_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf32_Rela)); + symtab = (Elf32_Sym *)symsec->contents; + strtab = (char const *)strsec->contents; + + for (; rel < relend; rel++) + { + int need_stub = 0; + + switch (ELF32_R_TYPE(rel->r_info)) + { + default: + continue; + + case R_PARISC_PCREL17F: + need_stub = 1; + break; + } + + if (need_stub) + { + Elf32_Sym *extsym; + hppa_symbol_t *hsym; + char const *name; + int local; + unsigned long symndx; + + symndx = ELF32_R_SYM(rel->r_info); + extsym = symtab + symndx; + if (ELF32_ST_BIND(extsym->st_info) == STB_LOCAL) + hsym = (hppa_symbol_t *) f->local_symtab[symndx]; + else + { + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + hsym = (hppa_symbol_t *)obj_find_symbol(f, name); + } + local = hsym->root.secidx <= SHN_HIRESERVE; + + if (need_stub) + { + hppa_stub_t *stub; + + if (hsym->stub == NULL) + { + stub = (hppa_stub_t *) xmalloc(sizeof(hppa_stub_t)); + stub->offset = stub_offset; + stub->reloc_done = 0; + hsym->stub = stub; + stub_offset += STUB_SIZE; + need_stub = 0; + } + } + } + } + } + if (stub_offset) + { + hfile->stub->contents = xmalloc(stub_offset); + hfile->stub->header.sh_size = stub_offset; + } + return 1; +} + + +enum obj_reloc +arch_apply_relocation(struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + hppa_file_t *hfile = (hppa_file_t *) f; + hppa_symbol_t *hsym = (hppa_symbol_t *) sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = (targsec->header.sh_addr + rel->r_offset) & ~0x03; + Elf32_Addr dp = hfile->dp; + Elf32_Word r_info = ELF32_R_TYPE(rel->r_info); + + enum obj_reloc ret = obj_reloc_ok; + enum hppa_fsel fsel = e_fsel; /* Avoid compiler warning */ + unsigned int r_format; + + /* Fix up the value, and determine whether we can handle this + relocation. */ + switch (r_info) + { + case R_PARISC_PLABEL32: + case R_PARISC_DIR32: + case R_PARISC_DIR21L: + case R_PARISC_DIR14R: + /* Easy. */ + break; + + case R_PARISC_DPREL21L: + case R_PARISC_DPREL14R: + v -= dp; + break; + + case R_PARISC_PCREL17F: + /* Find an import stub. */ + assert(hsym->stub != NULL); + assert(hfile->stub != NULL); + /* XXX Optimise. We may not need a stub for short branches */ + if (!hsym->stub->reloc_done) { + /* Need to create the .stub entry */ + Elf32_Addr *pstub, stubv; + + pstub = (Elf32_Addr *)(hfile->stub->contents + hsym->stub->offset); + pstub[0] = LDIL_R1; + pstub[1] = BE_N_SR4_R1; + stubv = hppa_field_adjust(v, rel->r_addend, e_lrsel); + pstub[0] = hppa_rebuild_insn(pstub[0], stubv, 21); + stubv = hppa_field_adjust(v, rel->r_addend, e_rrsel); + stubv >>= 2; /* Branch; divide by 4 */ + pstub[1] = hppa_rebuild_insn(pstub[1], stubv, 17); + hsym->stub->reloc_done = 1; + } + v = hsym->stub->offset + hfile->stub->header.sh_addr; + break; + + default: + return obj_reloc_unhandled; + } + + /* Find the field selector. */ + switch (r_info) + { + case R_PARISC_DIR32: + case R_PARISC_PLABEL32: + case R_PARISC_PCREL17F: + fsel = e_fsel; + break; + + case R_PARISC_DPREL21L: + case R_PARISC_DIR21L: + fsel = e_lrsel; + break; + + case R_PARISC_DPREL14R: + case R_PARISC_DIR14R: + fsel = e_rrsel; + break; + } + + v = hppa_field_adjust(v, rel->r_addend, fsel); + + switch (r_info) + { + case R_PARISC_PCREL17F: + case R_PARISC_PCREL17R: + case R_PARISC_PCREL22F: + v = v - dot - 8; + case R_PARISC_DIR17F: + case R_PARISC_DIR17R: + /* This is a branch. Divide the offset by four. */ + v >>= 2; + break; + default: + break; + } + + /* Find the format. */ + switch (r_info) + { + case R_PARISC_DIR32: + case R_PARISC_PLABEL32: + r_format = 32; + break; + + case R_PARISC_DPREL21L: + case R_PARISC_DIR21L: + r_format = 21; + break; + + case R_PARISC_PCREL17F: + r_format = 17; + break; + + case R_PARISC_DPREL14R: + case R_PARISC_DIR14R: + r_format = 14; + break; + + default: + abort(); + } + + *loc = hppa_rebuild_insn(*loc, v, r_format); + + return ret; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *f, struct obj_section *sec) +{ + struct module_symbol *s; + int i; + hppa_file_t *hfile = (hppa_file_t *)f; + + /* Initialise dp to the kernels dp (symbol data_start) + */ + for (i = 0, s = ksyms; i < nksyms; i++, s++) + if (!strcmp((char *)s->name, "data_start")) + break; + if (i >= nksyms) { + error("Cannot initialise dp, 'data_start' not found\n"); + return 1; + } + hfile->dp = s->value; + + return 0; +} + diff --git a/mdk-stage1/insmod-modutils/obj/obj_hppa64.c b/mdk-stage1/insmod-modutils/obj/obj_hppa64.c new file mode 100644 index 000000000..fe32911ff --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_hppa64.c @@ -0,0 +1,686 @@ +/* + * hppa parisc64 specific support for Elf loading and relocation. + * Copyright 2000 Richard Hirst <rhirst@linuxcare.com>, Linuxcare Inc. + * + * Based on ia64 specific support which was + * Copyright 2000 Mike Stephens <mike.stephens@intel.com> + * + * 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 <stdlib.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE ~FALSE +#endif + +/*======================================================================*/ + +typedef struct _hppa64_opd_t +{ + int offset; + int reloc_done; +} hppa64_opd_t; + +typedef struct _hppa64_stub_t +{ + struct _hppa64_stub_t *next; + Elf64_Addr addend; + int offset; + int reloc_done; +} hppa64_stub_t; + +typedef struct _hppa64_got_t +{ + struct _hppa64_got_t *next; + Elf64_Addr addend; + int offset; + int reloc_done; +} hppa64_got_t; + +typedef struct _hppa64_symbol_t +{ + struct obj_symbol root; + hppa64_got_t *gotent; + hppa64_opd_t *opdent; + hppa64_stub_t *stubent; +} hppa64_symbol_t; + +typedef struct _hppa64_file_t +{ + struct obj_file root; + struct obj_section *got; + struct obj_section *opd; + struct obj_section *stub; + Elf64_Addr gp; + Elf64_Addr text; + Elf64_Addr data; + Elf64_Addr bss; +} hppa64_file_t; + +/* + * XXX This stub assumes it can reach the .got entry with a +/- 8K offset + * from dp. Perhaps we should use a .plt for these entries to give a + * greater chance of that being true. + * + * 53 7b 00 00 ldd 0(dp),dp + * R_PARISC_LTOFF14R <.got entry offset from dp> + * 53 61 00 20 ldd 10(dp),r1 + * e8 20 d0 00 bve (r1) + * 53 7b 00 30 ldd 18(dp),dp + * + * We need a different stub for millicode calls, which doesn't screw + * dp: + * + * 53 61 00 00 ldd 0(dp),r1 + * R_PARISC_LTOFF14R <.got entry offset from dp> + * 50 21 00 20 ldd 10(r1),r1 + * e8 20 d0 00 bve (r1) + * 08 00 02 40 nop + * + */ + +/* NOTE: to keep the code cleaner we make all stubs the same size. + */ + +#define SIZEOF_STUB 16 + +unsigned char hppa64_stub_extern[] = +{ + 0x53, 0x7b, 0x00, 0x00, + 0x53, 0x61, 0x00, 0x20, + 0xe8, 0x20, 0xd0, 0x00, + 0x53, 0x7b, 0x00, 0x30, +}; + +unsigned char hppa64_stub_millicode[] = +{ + 0x53, 0x61, 0x00, 0x00, + 0x50, 0x21, 0x00, 0x20, + 0xe8, 0x20, 0xd0, 0x00, + 0x08, 0x00, 0x02, 0x40, +}; + +/*======================================================================*/ + +enum obj_reloc +patch_14r(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + if (v & 0x80000000) + v |= ~0x7ff; + else + v &= 0x7ff; + i &= ~ 0x3fff; + i |= (v & 0x1fff) << 1 | + (v & 0x2000) >> 13; + *p = i; + + return obj_reloc_ok; +} + +enum obj_reloc +patch_21l(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + v &= 0xfffff800; + if (v & 0x80000000) + v += 0x800; + i &= ~ 0x1fffff; + i |= (v & 0x80000000) >> 31 | + (v & 0x7ff00000) >> 19 | + (v & 0x000c0000) >> 4 | + (v & 0x0003e000) << 3 | + (v & 0x00001800) << 1; + *p = i; + + return obj_reloc_ok; +} + + +/* All 14 bits this time... This is used to patch the .got offset in + * a stub for PCREL22F. + */ + +enum obj_reloc +patch_14r2(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + if ((Elf64_Sxword)v64 > 0x1fffL || + (Elf64_Sxword)v64 < -0x2000L) + return obj_reloc_overflow; + i &= ~ 0x3fff; + i |= (v & 0x2000) >> 13 | + (v & 0x1fff) << 1; + *p = i; + + return obj_reloc_ok; +} + + +enum obj_reloc +patch_22f(Elf64_Xword v64, Elf64_Word *p) +{ + Elf64_Word i = *p; + Elf64_Word v = (Elf64_Word)v64; + + if ((Elf64_Sxword)v64 > 0x800000-1 || + (Elf64_Sxword)v64 < -0x800000) + return obj_reloc_overflow; + + i &= ~ 0x03ff1ffd; + i |= (v & 0x00800000) >> 23 | + (v & 0x007c0000) << 3 | + (v & 0x0003e000) << 3 | + (v & 0x00001000) >> 10 | + (v & 0x00000ffc) << 1; + *p = i; + + return obj_reloc_ok; +} + + +struct obj_section * +obj_hppa64_create_alloced_section (struct obj_file *f, const char *name, + unsigned long align, unsigned long size, unsigned long sh_flags) +{ + 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 = sh_flags; + 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_file * +arch_new_file (void) +{ + hppa64_file_t *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + f->opd = NULL; + f->stub = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + hppa64_symbol_t *sym; + sym = xmalloc(sizeof(*sym)); + sym->gotent = NULL; + sym->opdent = NULL; + sym->stubent = NULL; + return &sym->root; +} + +/* This may not be needed, but does no harm (copied from ia64). + */ + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + switch (sec->header.sh_type) + { + case SHT_PARISC_EXT : + sec->contents = NULL; + break; + + case SHT_PARISC_UNWIND : + if (sec->header.sh_size > 0) + { + sec->contents = xmalloc(sec->header.sh_size); + gzf_lseek(fp, sec->header.sh_offset, SEEK_SET); + if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size) + { + error("error reading ELF section data: %m"); + return -1; + } + } + else + sec->contents = NULL; + break; + default: + error("Unknown section header type: %08x", sec->header.sh_type); + return -1; + } + return 0; +} + +int +arch_create_got(struct obj_file *f) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + int i; + int n; + int got_offset = 0; + int opd_offset = 64; + int stub_offset = 0; + + n = hfile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf64_Rela *rel, *relend; + Elf64_Sym *symtab; + const char *strtab; + + relsec = hfile->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = hfile->root.sections[relsec->header.sh_link]; + strsec = hfile->root.sections[symsec->header.sh_link]; + + rel = (Elf64_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf64_Rela)); + symtab = (Elf64_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + int need_got = FALSE; + int need_opd = FALSE; + int need_stub = FALSE; + + switch (ELF64_R_TYPE(rel->r_info)) + { + default: + { + unsigned r_info = ELF64_R_TYPE(rel->r_info); + printf("r_info 0x%x not handled\n", r_info); + } + continue; + case R_PARISC_LTOFF14R: + case R_PARISC_LTOFF21L: + /* These are simple indirect references to symbols through the + * DLT. We need to create a DLT entry for any symbols which + * appears in a DLTIND relocation. + */ + need_got = TRUE; + break; + case R_PARISC_PCREL22F: + /* These are function calls. Depending on their precise + * target we may need to make a stub for them. The stub + * uses the dlt, so we need to create dlt entries for + * these symbols too. + */ + need_got = TRUE; + need_stub = TRUE; + break; + case R_PARISC_DIR64: + break; + case R_PARISC_FPTR64: + /* This is a simple OPD entry (only created for local symbols, + * see below). + */ + need_opd = TRUE; + break; + } + + if (need_got || need_opd || need_stub) + { + Elf64_Sym *extsym; + hppa64_symbol_t *isym; + const char *name; + int local; + unsigned long symndx; + + symndx = ELF64_R_SYM(rel->r_info); + extsym = &symtab[symndx]; + if (ELF64_ST_BIND(extsym->st_info) == STB_LOCAL) + { + isym = (hppa64_symbol_t *) f->local_symtab[symndx]; + } + else + { + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + isym = (hppa64_symbol_t *)obj_find_symbol(f, name); + } + local = isym->root.secidx <= SHN_HIRESERVE; + + if (need_stub) + { + hppa64_stub_t *stub; + + for (stub = isym->stubent; stub != NULL; stub = stub->next) + if (stub->addend == rel->r_addend) + break; + if (stub == NULL) + { + stub = (hppa64_stub_t *) xmalloc(sizeof(hppa64_stub_t)); + stub->next = isym->stubent; + stub->addend = rel->r_addend; + stub->offset = stub_offset; + stub->reloc_done = FALSE; + isym->stubent = stub; + { + stub_offset += SIZEOF_STUB; + } + need_stub = FALSE; + } + } + if (need_got) + { + hppa64_got_t *got; + + for (got = isym->gotent; got != NULL; got = got->next) + if (got->addend == rel->r_addend) + break; + if (got == NULL) + { + got = (hppa64_got_t *) xmalloc(sizeof(hppa64_got_t)); + got->next = isym->gotent; + got->addend = rel->r_addend; + got->offset = got_offset; + got->reloc_done = FALSE; + isym->gotent = got; + got_offset += 8; + need_got = FALSE; + } + } + if (need_opd && local) + { + hppa64_opd_t *opd; + + if (isym->opdent == NULL) + { + opd = (hppa64_opd_t *) xmalloc(sizeof(hppa64_opd_t)); + opd->offset = opd_offset; + opd->reloc_done = FALSE; + isym->opdent = opd; + opd_offset += 32; + need_opd = FALSE; + } + } + } + } + } + + hfile->got = obj_hppa64_create_alloced_section(f, ".got", 8, got_offset, + (SHF_ALLOC | SHF_WRITE | SHF_PARISC_SHORT)); + assert(hfile->got != NULL); + + hfile->opd = obj_hppa64_create_alloced_section(f, ".opd", 16, opd_offset, + (SHF_ALLOC | SHF_WRITE | SHF_PARISC_SHORT)); + assert(hfile->opd != NULL); + + if (stub_offset > 0) + { + hfile->stub = obj_hppa64_create_alloced_section(f, ".stub", 16, + stub_offset, (SHF_ALLOC | SHF_EXECINSTR | SHF_PARISC_SHORT)); + assert(hfile->stub != NULL); + } + + return 1; +} + + +/* This is a small simple version which seems to work fine. ia64 has + * a much more complex algorithm. We point dp at the end of the .got, + * which is the start of the .opd. + */ + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + int n = f->header.e_shnum; + int i; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + + /* Pick a sensible value for gp */ + hfile->gp = hfile->got->header.sh_addr + hfile->got->header.sh_size; + + return 1; +} + + +enum obj_reloc +arch_apply_relocation(struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + hppa64_file_t *hfile = (hppa64_file_t *) f; + hppa64_symbol_t *isym = (hppa64_symbol_t *) sym; + + Elf64_Word *loc = (Elf64_Word *)(targsec->contents + rel->r_offset); + Elf64_Addr dot = (targsec->header.sh_addr + rel->r_offset) & ~0x03; + + Elf64_Addr got = hfile->got->header.sh_addr; + Elf64_Addr gp = hfile->gp; + + Elf64_Xword r_info = ELF64_R_TYPE(rel->r_info); + + enum obj_reloc ret = obj_reloc_ok; + + switch (r_info) + { + default: + ret = obj_reloc_unhandled; + break; + case R_PARISC_LTOFF14R: + case R_PARISC_LTOFF21L: + { + hppa64_got_t *ge; + + assert(isym != NULL); + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(hfile->got->contents + ge->offset) = v; + } + v = got + ge->offset - gp; + if (r_info == R_PARISC_LTOFF14R) + ret = patch_14r(v, loc); + else + ret = patch_21l(v, loc); + } + break; + case R_PARISC_PCREL22F: + { + hppa64_got_t *ge; + + assert(isym != NULL); + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(hfile->got->contents + ge->offset) = v; + } + if ((isym->root.secidx > SHN_HIRESERVE) || + ((Elf64_Sxword) (v - dot - 8) > 0x800000-1) || + ((Elf64_Sxword) (v - dot - 8) < -0x800000)) + { + hppa64_stub_t *se; + + for (se = isym->stubent; se != NULL && se->addend != rel->r_addend; ) + se = se->next; + assert(se != NULL); + if (!se->reloc_done) + { + /* This requires that we can get from dp to the entry in +/- 8K, + * or +/- 1000 entries. patch_14r2() will check that. + * Only need these dlt entries for calls to external/far + * functions, so should probably put them in a seperate section + * before dlt and point dp at the section. Change to that + * scheme if we hit problems with big modules. + */ + unsigned char *stub; + + if (!strncmp(isym->root.name, "$$", 2)) + stub = hppa64_stub_millicode; + else + stub = hppa64_stub_extern; + se->reloc_done = TRUE; + memcpy((Elf64_Addr *)(hfile->stub->contents + se->offset), + stub, SIZEOF_STUB); + v = (Elf64_Addr)(hfile->got->header.sh_addr + ge->offset) - gp; + ret = patch_14r2(v, (Elf64_Word *)(hfile->stub->contents + se->offset)); + } + v = hfile->stub->header.sh_addr + se->offset; + } + v = v - dot - 8; + if (ret == obj_reloc_ok) + ret = patch_22f(v, loc); + } + break; + case R_PARISC_DIR64: + { + loc[0] = v >> 32; + loc[1] = v; + } + break; + case R_PARISC_FPTR64: + { + assert(isym != NULL); + if (isym->root.secidx <= SHN_HIRESERVE) /* local */ + { + assert(isym->opdent != NULL); + if (!isym->opdent->reloc_done) + { + isym->opdent->reloc_done = TRUE; + *(Elf64_Addr *)(hfile->opd->contents + isym->opdent->offset + 16) = v; + *(Elf64_Addr *)(hfile->opd->contents + isym->opdent->offset + 24) = gp; + } + v = hfile->opd->header.sh_addr + isym->opdent->offset; + } + loc[0] = v >> 32; + loc[1] = v; + } + break; + } + return ret; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + Elf64_Addr *opd = (Elf64_Addr *)(hfile->opd->contents); + + opd[0] = 0; + opd[1] = 0; + if ((opd[2] = mod->init) != 0) + { + opd[3] = hfile->gp; + mod->init = hfile->opd->header.sh_addr; + } + + opd[4] = 0; + opd[5] = 0; + if ((opd[6] = mod->cleanup) != 0) + { + opd[7] = hfile->gp; + mod->cleanup = hfile->opd->header.sh_addr + 32; + } + + return 1; +} + +/* XXX Is this relevant to parisc? */ + +int +arch_archdata (struct obj_file *f, struct obj_section *archdata_sec) +{ + hppa64_file_t *hfile = (hppa64_file_t *)f; + struct archdata { + unsigned tgt_long unw_table; + unsigned tgt_long segment_base; + unsigned tgt_long unw_start; + unsigned tgt_long unw_end; + unsigned tgt_long gp; + } *ad; + int i; + struct obj_section *sec; + + free(archdata_sec->contents); + archdata_sec->contents = xmalloc(sizeof(struct archdata)); + memset(archdata_sec->contents, 0, sizeof(struct archdata)); + archdata_sec->header.sh_size = sizeof(struct archdata); + + ad = (struct archdata *)(archdata_sec->contents); + ad->gp = hfile->gp; + ad->unw_start = 0; + ad->unw_end = 0; + ad->unw_table = 0; + ad->segment_base = f->sections[1]->header.sh_addr; + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type == SHT_PARISC_UNWIND) + { + ad->unw_start = sec->header.sh_addr; + ad->unw_end = sec->header.sh_addr + sec->header.sh_size; + break; + } + } + + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_i386.c b/mdk-stage1/insmod-modutils/obj/obj_i386.c new file mode 100644 index 000000000..28df3448c --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_i386.c @@ -0,0 +1,245 @@ +/* i386 specific support for Elf loading and relocation. + 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 <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct i386_got_entry +{ + int offset; + unsigned offset_done : 1; + unsigned reloc_done : 1; +}; + +struct i386_file +{ + struct obj_file root; + struct obj_section *got; +}; + +struct i386_symbol +{ + struct obj_symbol root; + struct i386_got_entry gotent; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct i386_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct i386_symbol *sym; + sym = xmalloc(sizeof(*sym)); + memset(&sym->gotent, 0, sizeof(sym->gotent)); + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rel *rel, + Elf32_Addr v) +{ + struct i386_file *ifile = (struct i386_file *)f; + struct i386_symbol *isym = (struct i386_symbol *)sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf32_Addr got = ifile->got ? ifile->got->header.sh_addr : 0; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_386_NONE: + break; + + case R_386_32: + *loc += v; + break; + + case R_386_PLT32: + case R_386_PC32: + *loc += v - dot; + break; + + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + *loc = v; + break; + + case R_386_RELATIVE: + *loc += f->baseaddr; + break; + + case R_386_GOTPC: + assert(got != 0); + *loc += got - dot; + break; + + case R_386_GOT32: + assert(isym != NULL); + if (!isym->gotent.reloc_done) + { + isym->gotent.reloc_done = 1; + *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v; + } + *loc += isym->gotent.offset; + break; + + case R_386_GOTOFF: + assert(got != 0); + *loc += v - got; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct i386_file *ifile = (struct i386_file *)f; + int i, n, offset = 0, gotneeded = 0; + + n = ifile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf32_Rel *rel, *relend; + Elf32_Sym *symtab; + const char *strtab; + + relsec = ifile->root.sections[i]; + if (relsec->header.sh_type != SHT_REL) + continue; + + symsec = ifile->root.sections[relsec->header.sh_link]; + strsec = ifile->root.sections[symsec->header.sh_link]; + + rel = (Elf32_Rel *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf32_Rel)); + symtab = (Elf32_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + Elf32_Sym *extsym; + struct i386_symbol *intsym; + const char *name; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_386_GOTPC: + case R_386_GOTOFF: + gotneeded = 1; + default: + continue; + + case R_386_GOT32: + break; + } + + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct i386_symbol *)obj_find_symbol(&ifile->root, name); + + if (!intsym->gotent.offset_done) + { + intsym->gotent.offset_done = 1; + intsym->gotent.offset = offset; + offset += 4; + } + } + } + + if (offset > 0 || gotneeded) + ifile->got = obj_create_alloced_section(&ifile->root, ".got", 4, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_ia64.c b/mdk-stage1/insmod-modutils/obj/obj_ia64.c new file mode 100644 index 000000000..4f92c5d27 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_ia64.c @@ -0,0 +1,1065 @@ +/* + * ia64 specific support for Elf loading and relocation. + * Copyright 2000 Mike Stephens <mike.stephens@intel.com> + * + * 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 <stdlib.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE ~FALSE +#endif + +/*======================================================================*/ + +typedef struct _ia64_opd_t +{ + int offset; + int reloc_done; +} ia64_opd_t; + +typedef struct _ia64_plt_t +{ + struct _ia64_plt_t *next; + Elf64_Addr addend; + int text_offset; + int data_offset; + int reloc_done; +} ia64_plt_t; + +typedef struct _ia64_got_t +{ + struct _ia64_got_t *next; + Elf64_Addr addend; + int offset; + int reloc_done; +} ia64_got_t; + +typedef struct _ia64_symbol_t +{ + struct obj_symbol root; + ia64_got_t *gotent; + ia64_opd_t *opdent; + ia64_plt_t *pltent; +} ia64_symbol_t; + +typedef struct _ia64_file_t +{ + struct obj_file root; + struct obj_section *got; + struct obj_section *opd; + struct obj_section *pltt; + struct obj_section *pltd; + Elf64_Addr gp; + Elf64_Addr text; + Elf64_Addr data; + Elf64_Addr bss; +} ia64_file_t; + +/* + * aa=gp rel address of the function descriptor in the .IA_64.pltoff section + */ +unsigned char ia64_plt_local[] = +{ + 0x0b, 0x78, 0x00, 0x02, 0x00, 0x24, /* [MMI] addl r15=aa,gp;; */ + 0x00, 0x41, 0x3c, 0x30, 0x28, 0xc0, /* ld8 r16=[r15],8 */ + 0x01, 0x08, 0x00, 0x84, /* mov r14=gp;; */ + 0x11, 0x08, 0x00, 0x1e, 0x18, 0x10, /* [MIB] ld8 gp=[r15] */ + 0x60, 0x80, 0x04, 0x80, 0x03, 0x00, /* mov b6=r16 */ + 0x60, 0x00, 0x80, 0x00 /* br.few b6;; */ +}; + +unsigned char ia64_plt_extern[] = +{ + 0x0b, 0x80, 0x00, 0x02, 0x00, 0x24, /* [MMI] addl r16=aa,gp;; */ + 0xf0, 0x00, 0x40, 0x30, 0x20, 0x00, /* ld8 r15=[r16] */ + 0x00, 0x00, 0x04, 0x00, /* nop.i 0x0;; */ + 0x0b, 0x80, 0x20, 0x1e, 0x18, 0x14, /* [MMI] ld8 r16=[r15],8;; */ + 0x10, 0x00, 0x3c, 0x30, 0x20, 0xc0, /* ld8 gp=[r15] */ + 0x00, 0x09, 0x00, 0x07, /* mov b6=r16;; */ + 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, /* [MIB] nop.m 0x0 */ + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* nop.i 0x0 */ + 0x60, 0x00, 0x80, 0x00 /* br.few b6;; */ +}; + +/*======================================================================*/ + +/* + * return the instruction at slot in bundle + */ +Elf64_Xword +obj_ia64_ins_extract_from_bundle(Elf64_Addr *bundle, Elf64_Xword slot) +{ + switch (slot) + { + case 0 : + return (*bundle >> 5) & 0x1ffffffffff; + + case 1 : + return (((*bundle >> 46) & 0x3ffff) | + (*(bundle + 1) << 18)) & 0x1ffffffffff; + + case 2 : + return (*(bundle + 1) >> 23) & 0x1ffffffffff; + + default: + } + return (-1); +} + +/* + * insert a instruction at slot in bundle + */ +void +obj_ia64_ins_insert_in_bundle(Elf64_Addr *bundle, Elf64_Xword slot, Elf64_Xword ins) +{ + Elf64_Xword i; + Elf64_Xword in = ins & 0x1ffffffffff; + + switch (slot) + { + case 0 : + i = *bundle & 0xffffc0000000001f; + *bundle = i | (in << 5); + break; + + case 1 : + i = *bundle & 0x00003fffffffffff; + *bundle = i | (in << 46); + + ++bundle; + i = *bundle & 0xffffffffff800000; + *bundle = i | (in >> 18); + break; + + case 2 : + ++bundle; + i = *bundle & 0x00000000007fffff; + *bundle = i | (in << 23); + break; + } +} + +/* + * add a immediate 14 value to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_imm14(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee07f01fff; + ins |= ((v & 0x2000) << 23) | ((v & 0x1f80) << 20) | ((v & 0x007f) << 13); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + if (((Elf64_Sxword) v > 8191) || ((Elf64_Sxword) v < -8192)) + return obj_reloc_overflow; + return obj_reloc_ok; +} + +/* + * add a immediate 22 value to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_imm22(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffe000301fff; + ins |= ((v & 0x200000) << 15) | ((v & 0x1f0000) << 6) | + ((v & 0x00ff80) << 20) | ((v & 0x00007f) << 13); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + if (((Elf64_Sxword) v > 2097151) || ((Elf64_Sxword) v < -2097152)) + return obj_reloc_overflow; + return obj_reloc_ok; +} + +/* + * add a immediate 21 value (form 1) to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_pcrel21b(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee00001fff; + ins |= ((v & 0x1000000) << 12) | ((v & 0x0fffff0) << 9); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + return obj_reloc_ok; +} + +/* + * add a immediate 21 value (form 2) to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_pcrel21m(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee000fe03f; + ins |= ((v & 0x1000000) << 12) | ((v & 0x0fff800) << 9) | + ((v & 0x00007f0) << 2); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + return obj_reloc_ok; +} + +/* + * add a immediate 21 value (form 3) to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_pcrel21f(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffeffc00003f; + ins |= ((v & 0x1000000) << 12) | ((v & 0x0fffff0) << 2); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + return obj_reloc_ok; +} + +/* + * add a immediate 64 value to the instruction at slot in bundle + */ +enum obj_reloc +obj_ia64_ins_imm64(Elf64_Xword v, Elf64_Addr *bundle, Elf64_Xword slot) +{ + Elf64_Xword ins; + + assert(slot == 1); + ins = obj_ia64_ins_extract_from_bundle(bundle, slot); + ins &= 0xffffffee000101ff; + ins |= ((v & 0x8000000000000000) >> 28) | ((v & 0x0000000000200000)) | + ((v & 0x00000000001f0000) << 6) | ((v & 0x000000000000ff80) << 20) | + ((v & 0x000000000000007f) << 13); + obj_ia64_ins_insert_in_bundle(bundle, slot, ins); + obj_ia64_ins_insert_in_bundle(bundle, ++slot, ((v & 0x7fffffffffc00000) >> 22)); + return obj_reloc_ok; +} + +/* + * create a plt entry + */ +enum obj_reloc +obj_ia64_generate_plt(Elf64_Addr v, + Elf64_Addr gp, + ia64_file_t *ifile, + ia64_symbol_t *isym, + ia64_plt_t *pltent) +{ + *(Elf64_Addr *)(ifile->pltd->contents + pltent->data_offset) = v; + if (isym->root.secidx <= SHN_HIRESERVE) + { + /* local entry */ + *(Elf64_Addr *)(ifile->pltd->contents + pltent->data_offset + 8) = gp; + memcpy((Elf64_Addr *)(ifile->pltt->contents + pltent->text_offset), + ia64_plt_local, sizeof(ia64_plt_local)); + } + else + { + /* external entry */ + memcpy((Elf64_Addr *)(ifile->pltt->contents + pltent->text_offset), + ia64_plt_extern, sizeof(ia64_plt_extern)); + } + return obj_ia64_ins_imm22( + (ifile->pltd->header.sh_addr + pltent->data_offset - gp), + (Elf64_Addr *)(ifile->pltt->contents + pltent->text_offset), 0); +} + +struct obj_section * +obj_ia64_create_alloced_section (struct obj_file *f, const char *name, + unsigned long align, unsigned long size, unsigned long sh_flags) +{ + 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 = sh_flags; + 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_file * +arch_new_file (void) +{ + ia64_file_t *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + f->opd = NULL; + f->pltt = NULL; + f->pltd = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + ia64_symbol_t *sym; + sym = xmalloc(sizeof(*sym)); + sym->gotent = NULL; + sym->opdent = NULL; + sym->pltent = NULL; + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + switch (sec->header.sh_type) + { + case SHT_IA_64_EXT : + sec->contents = NULL; + break; + + case SHT_IA_64_UNWIND : + if (sec->header.sh_size > 0) + { + sec->contents = xmalloc(sec->header.sh_size); + gzf_lseek(fp, sec->header.sh_offset, SEEK_SET); + if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size) + { + error("error reading ELF section data: %m"); + return -1; + } + } + else + sec->contents = NULL; + break; + + default: + error("Unknown section header type: %08x", sec->header.sh_type); + return -1; + } + return 0; +} + +int +arch_create_got(struct obj_file *f) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + int i; + int n; + int got_offset = 0; + int opd_offset = 32; + int plt_text_offset = 0; + int plt_data_offset = 0; + + n = ifile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf64_Rela *rel, *relend; + Elf64_Sym *symtab; + const char *strtab; + + relsec = ifile->root.sections[i]; + if (relsec->header.sh_type != SHT_RELA) + continue; + + symsec = ifile->root.sections[relsec->header.sh_link]; + strsec = ifile->root.sections[symsec->header.sh_link]; + + rel = (Elf64_Rela *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf64_Rela)); + symtab = (Elf64_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + int need_got = FALSE; + int need_opd = FALSE; + int need_plt = FALSE; + + switch (ELF64_R_TYPE(rel->r_info)) + { + default: + continue; + + case R_IA64_FPTR64I : /* @fptr(sym + add), mov imm64 */ + case R_IA64_FPTR32LSB : /* @fptr(sym + add), data4 LSB */ + case R_IA64_FPTR64LSB : /* @fptr(sym + add), data8 LSB */ + need_opd = TRUE; + break; + + case R_IA64_LTOFF22 : /* @ltoff(sym + add), add imm22 */ + case R_IA64_LTOFF22X : + case R_IA64_LTOFF64I : /* @ltoff(sym + add), mov imm64 */ + need_got = TRUE; + break; + + case R_IA64_LTOFF_FPTR22 : /* @ltoff(@fptr(s+a)), imm22 */ + case R_IA64_LTOFF_FPTR64I : /* @ltoff(@fptr(s+a)), imm64 */ + case R_IA64_LTOFF_FPTR32LSB : + case R_IA64_LTOFF_FPTR64LSB : + need_got = TRUE; + need_opd = TRUE; + break; + + case R_IA64_PLTOFF22 : /* @pltoff(sym + add), add imm22 */ + case R_IA64_PLTOFF64I : /* @pltoff(sym + add), mov imm64 */ + case R_IA64_PLTOFF64LSB : /* @pltoff(sym + add), data8 LSB */ + + case R_IA64_PCREL21B : /* @pcrel(sym + add), ptb, call */ + case R_IA64_PCREL21M : /* @pcrel(sym + add), chk.s */ + case R_IA64_PCREL21F : /* @pcrel(sym + add), fchkf */ + need_plt = TRUE; + break; + } + + if (need_got || need_opd || need_plt) + { + Elf64_Sym *extsym; + ia64_symbol_t *isym; + const char *name; + int local; + unsigned long symndx; + + symndx = ELF64_R_SYM(rel->r_info); + extsym = &symtab[symndx]; + if (ELF64_ST_BIND(extsym->st_info) == STB_LOCAL) + { + isym = (ia64_symbol_t *) f->local_symtab[symndx]; + } + else + { + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + isym = (ia64_symbol_t *)obj_find_symbol(f, name); + } + local = isym->root.secidx <= SHN_HIRESERVE; + + if (need_plt) + { + ia64_plt_t *plt; + + for (plt = isym->pltent; plt != NULL; plt = plt->next) + if (plt->addend == rel->r_addend) + break; + if (plt == NULL) + { + plt = (ia64_plt_t *) xmalloc(sizeof(ia64_plt_t)); + plt->next = isym->pltent; + plt->addend = rel->r_addend; + plt->text_offset = plt_text_offset; + plt->data_offset = plt_data_offset; + plt->reloc_done = FALSE; + isym->pltent = plt; + if (local) + { + plt_text_offset += sizeof(ia64_plt_local); + plt_data_offset += 16; + } + else + { + plt_text_offset += sizeof(ia64_plt_extern); + plt_data_offset += 8; + } + need_plt = FALSE; + } + } + if (need_got) + { + ia64_got_t *got; + + for (got = isym->gotent; got != NULL; got = got->next) + if (got->addend == rel->r_addend) + break; + if (got == NULL) + { + got = (ia64_got_t *) xmalloc(sizeof(ia64_got_t)); + got->next = isym->gotent; + got->addend = rel->r_addend; + got->offset = got_offset; + got->reloc_done = FALSE; + isym->gotent = got; + got_offset += 8; + need_got = FALSE; + } + } + if (need_opd && local) + { + ia64_opd_t *opd; + + if (isym->opdent == NULL) + { + opd = (ia64_opd_t *) xmalloc(sizeof(ia64_opd_t)); + opd->offset = opd_offset; + opd->reloc_done = FALSE; + isym->opdent = opd; + opd_offset += 16; + need_opd = FALSE; + } + } + } + } + } + + ifile->got = obj_ia64_create_alloced_section(f, ".got", 8, got_offset, + (SHF_ALLOC | SHF_WRITE | SHF_IA_64_SHORT)); + assert(ifile->got != NULL); + + ifile->opd = obj_ia64_create_alloced_section(f, ".opd", 16, opd_offset, + (SHF_ALLOC | SHF_WRITE | SHF_IA_64_SHORT)); + assert(ifile->opd != NULL); + + if (plt_text_offset > 0) + { + ifile->pltt = obj_ia64_create_alloced_section(f, ".plt", 16, + plt_text_offset, (SHF_ALLOC | SHF_EXECINSTR | SHF_IA_64_SHORT)); + ifile->pltd = obj_ia64_create_alloced_section(f, ".IA_64.pltoff", + 16, plt_data_offset, (SHF_ALLOC | SHF_WRITE | SHF_IA_64_SHORT)); + assert(ifile->pltt != NULL); + assert(ifile->pltd != NULL); + } + + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + Elf64_Addr min_addr = (Elf64_Addr) -1; + Elf64_Addr max_addr = 0; + Elf64_Addr min_short_addr = (Elf64_Addr) -1; + Elf64_Addr max_short_addr = 0; + Elf64_Addr gp; + Elf64_Addr text = (Elf64_Addr) -1; + Elf64_Addr data = (Elf64_Addr) -1; + Elf64_Addr bss = (Elf64_Addr) -1; + int n = f->header.e_shnum; + int i; + + /* + * Finalize the addresses of the sections, find the min and max + * address of all sections marked short, and collect min and max + * address of any type, for use in selecting a nice gp. + * + * The algorithm used for selecting set the GP value was taken from + * the ld/bfd code contributed by David Mosberger-Tang <davidm@hpl.hp.com> + */ + f->baseaddr = base; + for (i = 0; i < n; ++i) + { + Elf64_Shdr *header = &f->sections[i]->header; + Elf64_Addr lo; + Elf64_Addr hi; + + header->sh_addr += base; + if (header->sh_flags & SHF_ALLOC) + { + lo = header->sh_addr; + hi = header->sh_addr + header->sh_size; + if (hi < lo) + hi = (Elf64_Addr) -1; + + if (min_addr > lo) + min_addr = lo; + if (max_addr < hi) + max_addr = hi; + if (header->sh_flags & SHF_IA_64_SHORT) + { + if (min_short_addr > lo) + min_short_addr = lo; + if (max_short_addr < hi) + max_short_addr = hi; + } + if ((header->sh_type & SHT_NOBITS) && (lo < bss)) + bss = lo; + else if ((header->sh_flags & SHF_EXECINSTR) && (lo < text)) + text = lo; + else if (lo < data) + data = lo; + } + } + /* Pick a sensible value for gp */ + + /* Start with just the address of the .got */ + gp = ifile->got->header.sh_addr; + + /* + * If it is possible to address the entire image, but we + * don't with the choice above, adjust. + */ + if ((max_addr - min_addr < 0x400000) && (max_addr - gp <= 0x200000) && + (gp - min_addr > 0x200000)) + { + gp = min_addr + 0x200000; + } + else if (max_short_addr != 0) + { + /* If we don't cover all the short data, adjust */ + if (max_short_addr - gp >= 0x200000) + gp = min_short_addr + 0x200000; + + /* If we're addressing stuff past the end, adjust back */ + if (gp > max_addr) + gp = max_addr - 0x200000 + 8; + } + + /* + * Validate whether all SHF_IA_64_SHORT sections are within + * range of the chosen GP. + */ + if (max_short_addr != 0) + { + if (max_short_addr - min_short_addr >= 0x400000) + { + error("short data segment overflowed (0x%lx >= 0x400000)", + (unsigned long)(max_short_addr - min_short_addr)); + return 0; + } + else if (((gp > min_short_addr) && (gp - min_short_addr > 0x200000)) || + ((gp < max_short_addr) && (max_short_addr - gp >= 0x200000))) + { + error("GP does not cover short data segment"); + return 0; + } + } + ifile->gp = gp; + ifile->text = text; + ifile->data = data; + ifile->bss = bss; + return 1; +} + +/* Targets can be unaligned, use memcpy instead of assignment */ +#define COPY_64LSB(loc, v) \ + do { \ + Elf64_Xword reloc = (v); \ + memcpy((void *)(loc), &reloc, 8); \ + } while(0) +#define COPY_32LSB(loc, v) \ + do { \ + Elf32_Xword reloc = (v); \ + memcpy((void *)(loc), &reloc, 4); \ + if ((v) != reloc) \ + ret = obj_reloc_overflow; \ + } while(0) + +enum obj_reloc +arch_apply_relocation(struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + ia64_file_t *ifile = (ia64_file_t *) f; + ia64_symbol_t *isym = (ia64_symbol_t *) sym; + + Elf64_Addr loc = (Elf64_Addr)(targsec->contents + rel->r_offset); + Elf64_Addr dot = (targsec->header.sh_addr + rel->r_offset) & ~0x03; + + Elf64_Addr got = ifile->got->header.sh_addr; + Elf64_Addr gp = ifile->gp; + + Elf64_Addr *bundle = (Elf64_Addr *)(loc & ~0x03); + Elf64_Xword slot = loc & 0x03; + + Elf64_Xword r_info = ELF64_R_TYPE(rel->r_info); + + enum obj_reloc ret = obj_reloc_ok; + + /* We cannot load modules compiled with -mconstant-gp */ +#ifndef EF_IA_64_CONS_GP +#define EF_IA_64_CONS_GP 0x00000040 +#endif +#ifndef EF_IA_64_NOFUNCDESC_CONS_GP +#define EF_IA_64_NOFUNCDESC_CONS_GP 0x00000080 +#endif + if (f->header.e_flags & (EF_IA_64_CONS_GP | EF_IA_64_NOFUNCDESC_CONS_GP)) + return obj_reloc_constant_gp; + + switch (r_info) + { + case R_IA64_NONE : /* none */ + case R_IA64_LDXMOV : /* Use of LTOFF22X. */ + break; + + case R_IA64_IMM14 : /* symbol + addend, add imm14 */ + ret = obj_ia64_ins_imm14(v, bundle, slot); + break; + + case R_IA64_IMM22 : /* symbol + addend, add imm22 */ + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_IMM64 : /* symbol + addend, movl imm64 */ + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_DIR32LSB : /* symbol + addend, data4 LSB */ + COPY_32LSB(loc, v); + break; + + case R_IA64_DIR64LSB : /* symbol + addend, data8 LSB */ + COPY_64LSB(loc, v); + break; + + case R_IA64_GPREL22 : /* @gprel(sym + add), add imm22 */ + v -= gp; + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_GPREL64I : /* @gprel(sym + add), mov imm64 */ + v -= gp; + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_GPREL32LSB : /* @gprel(sym + add), data4 LSB */ + COPY_32LSB(loc, v-gp); + break; + + case R_IA64_GPREL64LSB : /* @gprel(sym + add), data8 LSB */ + COPY_64LSB(loc, v-gp); + break; + + case R_IA64_LTOFF22 : /* @ltoff(sym + add), add imm22 */ + case R_IA64_LTOFF22X : /* LTOFF22, relaxable. */ + case R_IA64_LTOFF64I : /* @ltoff(sym + add), mov imm64 */ + { + ia64_got_t *ge; + + assert(isym != NULL); + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->got->contents + ge->offset) = v; + } + v = got + ge->offset - gp; + if (r_info == R_IA64_LTOFF64I) + ret = obj_ia64_ins_imm64(v, bundle, slot); + else + ret = obj_ia64_ins_imm22(v, bundle, slot); + } + break; + + case R_IA64_PLTOFF22 : /* @pltoff(sym + add), add imm22 */ + case R_IA64_PLTOFF64I : /* @pltoff(sym + add), mov imm64 */ + case R_IA64_PLTOFF64LSB : /* @pltoff(sym + add), data8 LSB */ + { + ia64_plt_t *pe; + + assert(isym != NULL); + for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend; ) + pe = pe->next; + assert(pe != NULL); + if (!pe->reloc_done) + { + pe->reloc_done = TRUE; + ret = obj_ia64_generate_plt(v, gp, ifile, isym, pe); + } + v = ifile->pltt->header.sh_addr + pe->text_offset - gp; + switch (r_info) + { + case R_IA64_PLTOFF22 : + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_PLTOFF64I : + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_PLTOFF64LSB : + COPY_64LSB(loc, v); + break; + } + } + break; + + case R_IA64_FPTR64I : /* @fptr(sym + add), mov imm64 */ + case R_IA64_FPTR32LSB : /* @fptr(sym + add), data4 LSB */ + case R_IA64_FPTR64LSB : /* @fptr(sym + add), data8 LSB */ + assert(isym != NULL); + if (isym->root.secidx <= SHN_HIRESERVE) + { + assert(isym->opdent != NULL); + if (!isym->opdent->reloc_done) + { + isym->opdent->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset) = v; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset + 8) = gp; + } + v = ifile->opd->header.sh_addr + isym->opdent->offset; + } + switch (r_info) + { + case R_IA64_FPTR64I : + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_FPTR32LSB : + COPY_32LSB(loc, v); + break; + + case R_IA64_FPTR64LSB : /* @fptr(sym + add), data8 LSB */ + /* Target can be unaligned */ + COPY_64LSB(loc, v); + break; + } + break; + + case R_IA64_PCREL21B : /* @pcrel(sym + add), ptb, call */ + case R_IA64_PCREL21M : /* @pcrel(sym + add), chk.s */ + case R_IA64_PCREL21F : /* @pcrel(sym + add), fchkf */ + assert(isym != NULL); + if ((isym->root.secidx > SHN_HIRESERVE) || + ((Elf64_Sxword) (v - dot) > 16777215) || + ((Elf64_Sxword) (v - dot) < -16777216)) + { + ia64_plt_t *pe; + + for (pe = isym->pltent; pe != NULL && pe->addend != rel->r_addend; ) + pe = pe->next; + assert(pe != NULL); + if (!pe->reloc_done) + { + pe->reloc_done = TRUE; + ret = obj_ia64_generate_plt(v, gp, ifile, isym, pe); + } + v = ifile->pltt->header.sh_addr + pe->text_offset; + } + v -= dot; + switch (r_info) + { + case R_IA64_PCREL21B : + ret = obj_ia64_ins_pcrel21b(v, bundle, slot); + break; + + case R_IA64_PCREL21M : + ret = obj_ia64_ins_pcrel21m(v, bundle, slot); + break; + + case R_IA64_PCREL21F : + ret = obj_ia64_ins_pcrel21f(v, bundle, slot); + break; + } + break; + + case R_IA64_PCREL32LSB : /* @pcrel(sym + add), data4 LSB */ + COPY_32LSB(loc, v-dot); + break; + + case R_IA64_PCREL64LSB : /* @pcrel(sym + add), data8 LSB */ + COPY_64LSB(loc, v-dot); + break; + + case R_IA64_LTOFF_FPTR22 : /* @ltoff(@fptr(s+a)), imm22 */ + case R_IA64_LTOFF_FPTR64I : /* @ltoff(@fptr(s+a)), imm64 */ + case R_IA64_LTOFF_FPTR32LSB : /* @ltoff(@fptr(s+a)), data4 */ + case R_IA64_LTOFF_FPTR64LSB : /* @ltoff(@fptr(s+a)), data8 */ + { + ia64_got_t *ge; + + assert(isym != NULL); + if (isym->root.secidx <= SHN_HIRESERVE) + { + assert(isym->opdent != NULL); + if (!isym->opdent->reloc_done) + { + isym->opdent->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset) = v; + *(Elf64_Addr *)(ifile->opd->contents + isym->opdent->offset + 8) = gp; + } + v = ifile->opd->header.sh_addr + isym->opdent->offset; + } + for (ge = isym->gotent; ge != NULL && ge->addend != rel->r_addend; ) + ge = ge->next; + assert(ge != NULL); + if (!ge->reloc_done) + { + ge->reloc_done = TRUE; + *(Elf64_Addr *)(ifile->got->contents + ge->offset) = v; + } + v = got + ge->offset - gp; + switch (r_info) + { + case R_IA64_LTOFF_FPTR22 : + ret = obj_ia64_ins_imm22(v, bundle, slot); + break; + + case R_IA64_LTOFF_FPTR64I : + ret = obj_ia64_ins_imm64(v, bundle, slot); + break; + + case R_IA64_LTOFF_FPTR32LSB : + COPY_32LSB(loc, v); + break; + + case R_IA64_LTOFF_FPTR64LSB : + COPY_64LSB(loc, v); + break; + } + } + break; + + case R_IA64_SEGREL32LSB : /* @segrel(sym + add), data4 LSB */ + case R_IA64_SEGREL64LSB : /* @segrel(sym + add), data8 LSB */ + if (targsec->header.sh_type & SHT_NOBITS) + v = ifile->bss - v; + else if (targsec->header.sh_flags & SHF_EXECINSTR) + v = ifile->text - v; + else + v = ifile->data - v; + if (r_info == R_IA64_SEGREL32LSB) + COPY_32LSB(loc, v); + else + COPY_64LSB(loc, v); + break; + + case R_IA64_SECREL32LSB : /* @secrel(sym + add), data4 LSB */ + COPY_32LSB(loc, targsec->header.sh_addr - v); + break; + + case R_IA64_SECREL64LSB : /* @secrel(sym + add), data8 LSB */ + COPY_64LSB(loc, targsec->header.sh_addr - v); + break; + + /* + * We don't handle the big-endian relocates + * + * R_IA64_DIR32MSB symbol + addend, data4 MSB + * R_IA64_DIR64MSB symbol + addend, data8 MSB + * R_IA64_GPREL32MSB @gprel(sym + add), data4 MSB + * R_IA64_GPREL64MSB @gprel(sym + add), data8 MSB + * R_IA64_PLTOFF64MSB @pltoff(sym + add), data8 MSB + * R_IA64_FPTR32MSB @fptr(sym + add), data4 MSB + * R_IA64_FPTR64MSB @fptr(sym + add), data8 MSB + * R_IA64_PCREL32MSB @pcrel(sym + add), data4 MSB + * R_IA64_PCREL64MSB @pcrel(sym + add), data8 MSB + * R_IA64_SEGREL32MSB @segrel(sym + add), data4 MSB + * R_IA64_SEGREL64MSB @segrel(sym + add), data8 MSB + * R_IA64_SECREL32MSB @secrel(sym + add), data4 MSB + * R_IA64_SECREL64MSB @secrel(sym + add), data8 MSB + * R_IA64_REL32MSB data 4 + REL + * R_IA64_REL64MSB data 8 + REL + * R_IA64_LTV32MSB symbol + addend, data4 MSB + * R_IA64_LTV64MSB symbol + addend, data8 MSB + * R_IA64_IPLTMSB dynamic reloc, imported PLT, MSB + */ + default: + case R_IA64_REL32LSB : /* data 4 + REL */ + case R_IA64_REL64LSB : /* data 8 + REL */ + case R_IA64_LTV32LSB : /* symbol + addend, data4 LSB */ + case R_IA64_LTV64LSB : /* symbol + addend, data8 LSB */ + case R_IA64_IPLTLSB : /* dynamic reloc, imported PLT, LSB */ + ret = obj_reloc_unhandled; + break; + } + return ret; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + Elf64_Addr *opd = (Elf64_Addr *)(ifile->opd->contents); + + if ((opd[0] = mod->init) != 0) + { + opd[1] = ifile->gp; + mod->init = ifile->opd->header.sh_addr; + } + + if ((opd[2] = mod->cleanup) != 0) + { + opd[3] = ifile->gp; + mod->cleanup = ifile->opd->header.sh_addr + 16; + } + + return 1; +} + +int +arch_archdata (struct obj_file *f, struct obj_section *archdata_sec) +{ + ia64_file_t *ifile = (ia64_file_t *)f; + struct archdata { + unsigned tgt_long unw_table; + unsigned tgt_long segment_base; + unsigned tgt_long unw_start; + unsigned tgt_long unw_end; + unsigned tgt_long gp; + } *ad; + int i; + struct obj_section *sec; + + free(archdata_sec->contents); + archdata_sec->contents = xmalloc(sizeof(struct archdata)); + memset(archdata_sec->contents, 0, sizeof(struct archdata)); + archdata_sec->header.sh_size = sizeof(struct archdata); + + ad = (struct archdata *)(archdata_sec->contents); + ad->gp = ifile->gp; + ad->unw_start = 0; + ad->unw_end = 0; + ad->unw_table = 0; + ad->segment_base = f->sections[1]->header.sh_addr; + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type == SHT_IA_64_UNWIND) + { + ad->unw_start = sec->header.sh_addr; + ad->unw_end = sec->header.sh_addr + sec->header.sh_size; + break; + } + } + + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_kallsyms.c b/mdk-stage1/insmod-modutils/obj/obj_kallsyms.c new file mode 100644 index 000000000..8385fb892 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_kallsyms.c @@ -0,0 +1,292 @@ +/* Build a section containing all non-stack symbols. + Copyright 2000 Keith Owens <kaos@ocs.com.au> + + 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 <malloc.h> + +#include "obj.h" +#include "kallsyms.h" +#include "util.h" + +/*======================================================================*/ + +#define EXPAND_BY 4096 /* Arbitrary */ + +/* Append a string to the big list of strings */ + +static void +append_string (const char *s, char **strings, + ElfW(Word) *strings_size, ElfW(Word) *strings_left) +{ + int l = strlen(s) + 1; + while (l > *strings_left) { + *strings = xrealloc(*strings, *strings_size += EXPAND_BY); + *strings_left += EXPAND_BY; + } + memcpy((char *)*strings+*strings_size-*strings_left, s, l); + *strings_left -= l; +} + + +/* Append a symbol to the big list of symbols */ + +static void +append_symbol (const struct kallsyms_symbol *s, + struct kallsyms_symbol **symbols, + ElfW(Word) *symbols_size, ElfW(Word) *symbols_left) +{ + int l = sizeof(*s); + while (l > *symbols_left) { + *symbols = xrealloc(*symbols, *symbols_size += EXPAND_BY); + *symbols_left += EXPAND_BY; + } + memcpy((char *)*symbols+*symbols_size-*symbols_left, s, l); + *symbols_left -= l; +} + +/* qsort compare routine to sort symbols */ + +static const char *sym_strings; + +static int +symbol_compare (const void *a, const void *b) +{ + struct kallsyms_symbol *c = (struct kallsyms_symbol *) a; + struct kallsyms_symbol *d = (struct kallsyms_symbol *) b; + + if (c->symbol_addr > d->symbol_addr) + return(1); + if (c->symbol_addr < d->symbol_addr) + return(-1); + return(strcmp(sym_strings+c->name_off, sym_strings+d->name_off)); +} + + +/* Extract all symbols from the input obj_file, ignore ones that are + * no use for debugging, build an output obj_file containing only the + * kallsyms section. + * + * The kallsyms section is a bit unusual. It deliberately has no + * relocatable data, all "pointers" are represented as byte offsets + * into the the section. This means it can be stored anywhere without + * relocation problems. In particular it can be stored within a kernel + * image, it can be stored separately from the kernel image, it can be + * appended to a module just before loading, it can be stored in a + * separate area etc. + * + * Format of the kallsyms section. + * + * Header: + * Size of header. + * Total size of kallsyms data, including strings. + * Number of loaded sections. + * Offset to first section entry from start of header. + * Size of each section entry, excluding the name string. + * Number of symbols. + * Offset to first symbol entry from start of header. + * Size of each symbol entry, excluding the name string. + * + * Section entry - one per loaded section. + * Start of section[1]. + * Size of section. + * Offset to name of section, from start of strings. + * Section flags. + * + * Symbol entry - one per symbol in the input file[2]. + * Offset of section that owns this symbol, from start of section data. + * Address of symbol within the real section[1]. + * Offset to name of symbol, from start of strings. + * + * Notes: [1] This is an exception to the "represent pointers as + * offsets" rule, it is a value, not an offset. The start + * address of a section or a symbol is extracted from the + * obj_file data which may contain absolute or relocatable + * addresses. If the addresses are relocatable then the + * caller must adjust the section and/or symbol entries in + * kallsyms after relocation. + * [2] Only symbols that fall within loaded sections are stored. + */ + +int +obj_kallsyms (struct obj_file *fin, struct obj_file **fout_result) +{ + struct obj_file *fout; + int i, loaded = 0, *fin_to_allsym_map; + struct obj_section *isec, *osec; + struct kallsyms_header *a_hdr; + struct kallsyms_section *a_sec; + ElfW(Off) sec_off; + struct kallsyms_symbol *symbols = NULL, a_sym; + ElfW(Word) symbols_size = 0, symbols_left = 0; + char *strings = NULL, *p; + ElfW(Word) strings_size = 0, strings_left = 0; + ElfW(Off) file_offset; + static char strtab[] = "\000" KALLSYMS_SEC_NAME; + + /* Create the kallsyms section. */ + fout = arch_new_file(); + memset(fout, 0, sizeof(*fout)); + fout->symbol_cmp = strcmp; + fout->symbol_hash = obj_elf_hash; + fout->load_order_search_start = &fout->load_order; + + /* Copy file characteristics from input file and modify to suit */ + memcpy(&fout->header, &fin->header, sizeof(fout->header)); + fout->header.e_type = ET_REL; /* Output is relocatable */ + fout->header.e_entry = 0; /* No entry point */ + fout->header.e_phoff = 0; /* No program header */ + file_offset = sizeof(fout->header); /* Step over Elf header */ + fout->header.e_shoff = file_offset; /* Section headers next */ + fout->header.e_phentsize = 0; /* No program header */ + fout->header.e_phnum = 0; /* No program header */ + fout->header.e_shnum = KALLSYMS_IDX+1; /* Initial, strtab, kallsyms */ + fout->header.e_shstrndx = KALLSYMS_IDX-1; /* strtab */ + file_offset += fout->header.e_shentsize * fout->header.e_shnum; + + /* Populate the section data for kallsyms itself */ + fout->sections = xmalloc(sizeof(*(fout->sections))*fout->header.e_shnum); + memset(fout->sections, 0, sizeof(*(fout->sections))*fout->header.e_shnum); + + fout->sections[0] = osec = arch_new_section(); + memset(osec, 0, sizeof(*osec)); + osec->header.sh_type = SHT_NULL; + osec->header.sh_link = SHN_UNDEF; + + fout->sections[KALLSYMS_IDX-1] = osec = arch_new_section(); + memset(osec, 0, sizeof(*osec)); + osec->name = ".strtab"; + osec->header.sh_type = SHT_STRTAB; + osec->header.sh_link = SHN_UNDEF; + osec->header.sh_offset = file_offset; + osec->header.sh_size = sizeof(strtab); + osec->contents = xmalloc(sizeof(strtab)); + memcpy(osec->contents, strtab, sizeof(strtab)); + file_offset += osec->header.sh_size; + + fout->sections[KALLSYMS_IDX] = osec = arch_new_section(); + memset(osec, 0, sizeof(*osec)); + osec->name = KALLSYMS_SEC_NAME; + osec->header.sh_name = 1; /* Offset in strtab */ + osec->header.sh_type = SHT_PROGBITS; /* Load it */ + osec->header.sh_flags = SHF_ALLOC; /* Read only data */ + osec->header.sh_link = SHN_UNDEF; + osec->header.sh_addralign = sizeof(ElfW(Word)); + file_offset = (file_offset + osec->header.sh_addralign - 1) + & -(osec->header.sh_addralign); + osec->header.sh_offset = file_offset; + + /* How many loaded sections are there? */ + for (i = 0; i < fin->header.e_shnum; ++i) { + if (fin->sections[i]->header.sh_flags & SHF_ALLOC) + ++loaded; + } + + /* Initial contents, header + one entry per input section. No strings. */ + osec->header.sh_size = sizeof(*a_hdr) + loaded*sizeof(*a_sec); + a_hdr = (struct kallsyms_header *) osec->contents = + xmalloc(osec->header.sh_size); + memset(osec->contents, 0, osec->header.sh_size); + a_hdr->size = sizeof(*a_hdr); + a_hdr->sections = loaded; + a_hdr->section_off = a_hdr->size; + a_hdr->section_size = sizeof(*a_sec); + a_hdr->symbol_off = osec->header.sh_size; + a_hdr->symbol_size = sizeof(a_sym); + a_hdr->start = (ElfW(Addr))(~0); + + /* Map input section numbers to kallsyms section offsets. */ + sec_off = 0; /* Offset to first kallsyms section entry */ + fin_to_allsym_map = xmalloc(sizeof(*fin_to_allsym_map)*fin->header.e_shnum); + for (i = 0; i < fin->header.e_shnum; ++i) { + isec = fin->sections[i]; + if (isec->header.sh_flags & SHF_ALLOC) { + fin_to_allsym_map[isec->idx] = sec_off; + sec_off += a_hdr->section_size; + } + else + fin_to_allsym_map[isec->idx] = -1; /* Ignore this section */ + } + + /* Copy the loaded section data. */ + a_sec = (struct kallsyms_section *) ((char *) a_hdr + a_hdr->section_off); + for (i = 0; i < fin->header.e_shnum; ++i) { + isec = fin->sections[i]; + if (!(isec->header.sh_flags & SHF_ALLOC)) + continue; + a_sec->start = isec->header.sh_addr; + a_sec->size = isec->header.sh_size; + a_sec->flags = isec->header.sh_flags; + a_sec->name_off = strings_size - strings_left; + append_string(isec->name, &strings, &strings_size, &strings_left); + if (a_sec->start < a_hdr->start) + a_hdr->start = a_sec->start; + if (a_sec->start+a_sec->size > a_hdr->end) + a_hdr->end = a_sec->start+a_sec->size; + ++a_sec; + } + + /* Build the kallsyms symbol table from the symbol hashes. */ + for (i = 0; i < HASH_BUCKETS; ++i) { + struct obj_symbol *sym = fin->symtab[i]; + for (sym = fin->symtab[i]; sym ; sym = sym->next) { + if (!sym || sym->secidx >= fin->header.e_shnum) + continue; + if ((a_sym.section_off = fin_to_allsym_map[sym->secidx]) == -1) + continue; + if (strcmp(sym->name, "gcc2_compiled.") == 0 || + strncmp(sym->name, "__insmod_", 9) == 0) + continue; + a_sym.symbol_addr = sym->value; + if (fin->header.e_type == ET_REL) + a_sym.symbol_addr += fin->sections[sym->secidx]->header.sh_addr; + a_sym.name_off = strings_size - strings_left; + append_symbol(&a_sym, &symbols, &symbols_size, &symbols_left); + append_string(sym->name, &strings, &strings_size, &strings_left); + ++a_hdr->symbols; + } + } + free(fin_to_allsym_map); + + /* Sort the symbols into ascending order by address and name */ + sym_strings = strings; /* For symbol_compare */ + qsort((char *) symbols, (unsigned) a_hdr->symbols, + sizeof(* symbols), symbol_compare); + sym_strings = NULL; + + /* Put the lot together */ + osec->header.sh_size = a_hdr->total_size = + a_hdr->symbol_off + + a_hdr->symbols*a_hdr->symbol_size + + strings_size - strings_left; + a_hdr = (struct kallsyms_header *) osec->contents = + xrealloc(a_hdr, a_hdr->total_size); + p = (char *)a_hdr + a_hdr->symbol_off; + memcpy(p, symbols, a_hdr->symbols*a_hdr->symbol_size); + free(symbols); + p += a_hdr->symbols*a_hdr->symbol_size; + a_hdr->string_off = p - (char *)a_hdr; + memcpy(p, strings, strings_size - strings_left); + free(strings); + + *fout_result = fout; + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_load.c b/mdk-stage1/insmod-modutils/obj/obj_load.c new file mode 100644 index 000000000..4db20a998 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_load.c @@ -0,0 +1,354 @@ +/* Elf file reader. + Copyright 1996, 1997 Linux International. + + Contributed by Richard Henderson <rth@tamu.edu> + obj_free() added by Björn Ekwall <bj0rn@blox.se> March 1999 + Support for kallsyms Keith Owens <kaos@ocs.com.au> April 2000 + + 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 <alloca.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "obj.h" +#include "util.h" + +/*======================================================================*/ + +struct obj_file * +obj_load (int fp, Elf32_Half e_type, const char *filename) +{ + struct obj_file *f; + ElfW(Shdr) *section_headers; + int shnum, i; + char *shstrtab; + + /* Read the file header. */ + + f = arch_new_file(); + memset(f, 0, sizeof(*f)); + f->symbol_cmp = strcmp; + f->symbol_hash = obj_elf_hash; + f->load_order_search_start = &f->load_order; + + gzf_lseek(fp, 0, SEEK_SET); + if (gzf_read(fp, &f->header, sizeof(f->header)) != sizeof(f->header)) + { + error("error reading ELF header %s: %m", filename); + return NULL; + } + + if (f->header.e_ident[EI_MAG0] != ELFMAG0 + || f->header.e_ident[EI_MAG1] != ELFMAG1 + || f->header.e_ident[EI_MAG2] != ELFMAG2 + || f->header.e_ident[EI_MAG3] != ELFMAG3) + { + error("%s is not an ELF file", filename); + return NULL; + } + if (f->header.e_ident[EI_CLASS] != ELFCLASSM + || f->header.e_ident[EI_DATA] != ELFDATAM + || f->header.e_ident[EI_VERSION] != EV_CURRENT + || !MATCH_MACHINE(f->header.e_machine)) + { + error("ELF file %s not for this architecture", filename); + return NULL; + } + if (f->header.e_type != e_type && e_type != ET_NONE) + { + switch (e_type) { + case ET_REL: + error("ELF file %s not a relocatable object", filename); + break; + case ET_EXEC: + error("ELF file %s not an executable object", filename); + break; + default: + error("ELF file %s has wrong type, expecting %d got %d", + filename, e_type, f->header.e_type); + break; + } + return NULL; + } + + /* Read the section headers. */ + + if (f->header.e_shentsize != sizeof(ElfW(Shdr))) + { + error("section header size mismatch %s: %lu != %lu", + filename, + (unsigned long)f->header.e_shentsize, + (unsigned long)sizeof(ElfW(Shdr))); + return NULL; + } + + shnum = f->header.e_shnum; + f->sections = xmalloc(sizeof(struct obj_section *) * shnum); + memset(f->sections, 0, sizeof(struct obj_section *) * shnum); + + section_headers = alloca(sizeof(ElfW(Shdr)) * shnum); + gzf_lseek(fp, f->header.e_shoff, SEEK_SET); + if (gzf_read(fp, section_headers, sizeof(ElfW(Shdr))*shnum) != sizeof(ElfW(Shdr))*shnum) + { + error("error reading ELF section headers %s: %m", filename); + return NULL; + } + + /* Read the section data. */ + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec; + + f->sections[i] = sec = arch_new_section(); + memset(sec, 0, sizeof(*sec)); + + sec->header = section_headers[i]; + sec->idx = i; + + switch (sec->header.sh_type) + { + case SHT_NULL: + case SHT_NOTE: + case SHT_NOBITS: + /* ignore */ + break; + + case SHT_PROGBITS: + case SHT_SYMTAB: + case SHT_STRTAB: + case SHT_RELM: + if (sec->header.sh_size > 0) + { + sec->contents = xmalloc(sec->header.sh_size); + gzf_lseek(fp, sec->header.sh_offset, SEEK_SET); + if (gzf_read(fp, sec->contents, sec->header.sh_size) != sec->header.sh_size) + { + error("error reading ELF section data %s: %m", filename); + return NULL; + } + } + else + sec->contents = NULL; + break; + +#if SHT_RELM == SHT_REL + case SHT_RELA: + if (sec->header.sh_size) { + error("RELA relocations not supported on this architecture %s", filename); + return NULL; + } + break; +#else + case SHT_REL: + if (sec->header.sh_size) { + error("REL relocations not supported on this architecture %s", filename); + return NULL; + } + break; +#endif + + default: + if (sec->header.sh_type >= SHT_LOPROC) + { + if (arch_load_proc_section(sec, fp) < 0) + return NULL; + break; + } + + error("can't handle sections of type %ld %s", + (long)sec->header.sh_type, filename); + return NULL; + } + } + + /* Do what sort of interpretation as needed by each section. */ + + shstrtab = f->sections[f->header.e_shstrndx]->contents; + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + sec->name = shstrtab + sec->header.sh_name; + } + + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + + /* .modinfo and .modstring should be contents only but gcc has no + * attribute for that. The kernel may have marked these sections as + * ALLOC, ignore the allocate bit. + */ + if (strcmp(sec->name, ".modinfo") == 0 || + strcmp(sec->name, ".modstring") == 0) + sec->header.sh_flags &= ~SHF_ALLOC; + + if (sec->header.sh_flags & SHF_ALLOC) + obj_insert_section_load_order(f, sec); + + switch (sec->header.sh_type) + { + case SHT_SYMTAB: + { + unsigned long nsym, j; + char *strtab; + ElfW(Sym) *sym; + + if (sec->header.sh_entsize != sizeof(ElfW(Sym))) + { + error("symbol size mismatch %s: %lu != %lu", + filename, + (unsigned long)sec->header.sh_entsize, + (unsigned long)sizeof(ElfW(Sym))); + return NULL; + } + + nsym = sec->header.sh_size / sizeof(ElfW(Sym)); + strtab = f->sections[sec->header.sh_link]->contents; + sym = (ElfW(Sym) *) sec->contents; + + /* Allocate space for a table of local symbols. */ + j = f->local_symtab_size = sec->header.sh_info; + f->local_symtab = xmalloc(j *= sizeof(struct obj_symbol *)); + memset(f->local_symtab, 0, j); + + /* Insert all symbols into the hash table. */ + for (j = 1, ++sym; j < nsym; ++j, ++sym) + { + const char *name; + if (sym->st_name) + name = strtab+sym->st_name; + else + name = f->sections[sym->st_shndx]->name; + + obj_add_symbol(f, name, j, sym->st_info, sym->st_shndx, + sym->st_value, sym->st_size); + + } + } + break; + } + } + + /* second pass to add relocation data to symbols */ + for (i = 0; i < shnum; ++i) + { + struct obj_section *sec = f->sections[i]; + switch (sec->header.sh_type) + { + case SHT_RELM: + { + unsigned long nrel, j; + ElfW(RelM) *rel; + struct obj_section *symtab; + char *strtab; + if (sec->header.sh_entsize != sizeof(ElfW(RelM))) + { + error("relocation entry size mismatch %s: %lu != %lu", + filename, + (unsigned long)sec->header.sh_entsize, + (unsigned long)sizeof(ElfW(RelM))); + return NULL; + } + + nrel = sec->header.sh_size / sizeof(ElfW(RelM)); + rel = (ElfW(RelM) *) sec->contents; + symtab = f->sections[sec->header.sh_link]; + strtab = f->sections[symtab->header.sh_link]->contents; + + /* Save the relocate type in each symbol entry. */ + for (j = 0; j < nrel; ++j, ++rel) + { + ElfW(Sym) *extsym; + struct obj_symbol *intsym; + unsigned long symndx; + symndx = ELFW(R_SYM)(rel->r_info); + if (symndx) + { + extsym = ((ElfW(Sym) *) symtab->contents) + 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); + } + intsym->r_type = ELFW(R_TYPE)(rel->r_info); + } + } + } + break; + } + } + + f->filename = xstrdup(filename); + + return f; +} + +void obj_free(struct obj_file *f) +{ + struct obj_section *sec; + struct obj_symbol *sym; + struct obj_symbol *next; + int i; + int n; + + if (f->sections) { + n = f->header.e_shnum; + for (i = 0; i < n; ++i) { + if ((sec = f->sections[i]) != NULL) { + if (sec->contents) + free(sec->contents); + free(sec); + } + } + free(f->sections); + } + + for (i = 0; i < HASH_BUCKETS; ++i) { + for (sym = f->symtab[i]; sym; sym = next) { + next = sym->next; + free(sym); + } + } + + if (f->local_symtab) + free(f->local_symtab); + + if (f->filename) + free((char *)(f->filename)); + + if (f->persist) + free((char *)(f->persist)); + + free(f); +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_m68k.c b/mdk-stage1/insmod-modutils/obj/obj_m68k.c new file mode 100644 index 000000000..cb485aed7 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_m68k.c @@ -0,0 +1,147 @@ +/* m68k specific support for Elf loading and relocation. + 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 <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + return xmalloc(sizeof(struct obj_file)); +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + char *loc = targsec->contents + rel->r_offset; + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_68K_NONE: + break; + + case R_68K_8: + if (v > 0xff) + ret = obj_reloc_overflow; + *(char *)loc = v; + break; + case R_68K_16: + if (v > 0xffff) + ret = obj_reloc_overflow; + *(short *)loc = v; + break; + case R_68K_32: + *(int *)loc = v; + break; + + case R_68K_PC8: + v -= dot; + if ((Elf32_Sword)v > 0x7f || (Elf32_Sword)v < -(Elf32_Sword)0x80) + ret = obj_reloc_overflow; + *(char *)loc = v; + break; + case R_68K_PC16: + v -= dot; + if ((Elf32_Sword)v > 0x7fff || (Elf32_Sword)v < -(Elf32_Sword)0x8000) + ret = obj_reloc_overflow; + *(short *)loc = v; + break; + case R_68K_PC32: + *(int *)loc = v - dot; + break; + + case R_68K_RELATIVE: + *(int *)loc += ef->baseaddr; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *ef) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_mips.c b/mdk-stage1/insmod-modutils/obj/obj_mips.c new file mode 100644 index 000000000..c2315b659 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_mips.c @@ -0,0 +1,238 @@ +/* MIPS specific support for Elf loading and relocation. + Copyright 1997, 1998 Linux International. + Contributed by Ralf Baechle <ralf@gnu.ai.mit.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 <stdlib.h> +#include <assert.h> + +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct mips_hi16 +{ + struct mips_hi16 *next; + Elf32_Addr *addr; + Elf32_Addr value; +}; + +struct mips_file +{ + struct obj_file root; + struct mips_hi16 *mips_hi16_list; +}; + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct mips_file *mf; + + mf = xmalloc(sizeof(*mf)); + mf->mips_hi16_list = NULL; + + return (struct obj_file *) mf; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + switch (sec->header.sh_type) + { + case SHT_MIPS_DEBUG: + case SHT_MIPS_REGINFO: + /* Actually these two sections are as useless as something can be ... */ + sec->contents = NULL; + break; + + case SHT_MIPS_LIBLIST: + case SHT_MIPS_CONFLICT: + case SHT_MIPS_GPTAB: + case SHT_MIPS_UCODE: + case SHT_MIPS_OPTIONS: + case SHT_MIPS_DWARF: + case SHT_MIPS_EVENTS: + /* These shouldn't ever be in a module file. */ + error("Unhandled section header type: %08x", sec->header.sh_type); + + default: + /* We don't even know the type. This time it might as well be a + supernova. */ + error("Unknown section header type: %08x", sec->header.sh_type); + return -1; + } + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rel *rel, + Elf32_Addr v) +{ + struct mips_file *mf = (struct mips_file *)f; + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + enum obj_reloc ret = obj_reloc_ok; + + /* _gp_disp is a magic symbol for PIC which is not supported for + the kernel and loadable modules. */ + if (strcmp(sym->name, "_gp_disp") == 0) + ret = obj_reloc_unhandled; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_MIPS_NONE: + break; + + case R_MIPS_32: + *loc += v; + break; + + case R_MIPS_26: + if (v % 4) + ret = obj_reloc_dangerous; + if ((v & 0xf0000000) != ((dot + 4) & 0xf0000000)) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x03ffffff) | ((*loc + (v >> 2)) & 0x03ffffff); + break; + + case R_MIPS_HI16: + { + struct mips_hi16 *n; + + /* We cannot relocate this one now because we don't know the value + of the carry we need to add. Save the information, and let LO16 + do the actual relocation. */ + n = (struct mips_hi16 *) xmalloc (sizeof *n); + n->addr = loc; + n->value = v; + n->next = mf->mips_hi16_list; + mf->mips_hi16_list = n; + break; + } + + case R_MIPS_LO16: + { + unsigned long insnlo = *loc; + Elf32_Addr val, vallo; + + /* Sign extend the addend we extract from the lo insn. */ + vallo = ((insnlo & 0xffff) ^ 0x8000) - 0x8000; + + if (mf->mips_hi16_list != NULL) + { + struct mips_hi16 *l; + + l = mf->mips_hi16_list; + while (l != NULL) + { + struct mips_hi16 *next; + unsigned long insn; + + /* The value for the HI16 had best be the same. */ + assert(v == l->value); + + /* Do the HI16 relocation. Note that we actually don't + need to know anything about the LO16 itself, except where + to find the low 16 bits of the addend needed by the LO16. */ + insn = *l->addr; + val = ((insn & 0xffff) << 16) + vallo; + val += v; + + /* Account for the sign extension that will happen in the + low bits. */ + val = ((val >> 16) + ((val & 0x8000) != 0)) & 0xffff; + + insn = (insn &~ 0xffff) | val; + *l->addr = insn; + + next = l->next; + free(l); + l = next; + } + + mf->mips_hi16_list = NULL; + } + + /* Ok, we're done with the HI16 relocs. Now deal with the LO16. */ + val = v + vallo; + insnlo = (insnlo & ~0xffff) | (val & 0xffff); + *loc = insnlo; + break; + } + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_ppc.c b/mdk-stage1/insmod-modutils/obj/obj_ppc.c new file mode 100644 index 000000000..89bb8e46b --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_ppc.c @@ -0,0 +1,255 @@ +/* PowerPC specific support for Elf loading and relocation. + Copyright 1996, 1997 Linux International. + + Adapted by Paul Mackerras <paulus@cs.anu.edu.au> from the + obj-sparc.c and obj-alpha.c files. + + 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 <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> +#include <assert.h> + + +/*======================================================================*/ + +/* + * Unfortunately, the bl (branch-and-link) instruction used for + * procedure calls on the PowerPC can only reach +/- 32MB from the + * current instruction. If the module is loaded far enough away from + * the main kernel text (or other modules) that this limit is + * exceeded, we have to redirect procedure calls via a procedure + * linkage table (PLT). Each entry in the PLT contains instructions + * to put the address of the procedure in a register and jump to it. + */ + +typedef unsigned int instruction; /* a powerpc instruction (4 bytes) */ + +struct ppc_plt_entry +{ + struct ppc_plt_entry *next; + ElfW(Addr) addend; + int offset; + int inited; +}; + +struct ppc_file +{ + struct obj_file file; + struct obj_section *plt; +}; + +struct ppc_symbol +{ + struct obj_symbol sym; + struct ppc_plt_entry *plt_entries; +}; + +struct obj_file * +arch_new_file (void) +{ + struct ppc_file *f; + + f = xmalloc(sizeof(struct ppc_file)); + f->plt = NULL; + return &f->file; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct ppc_symbol *p; + + p = xmalloc(sizeof(struct ppc_symbol)); + p->plt_entries = NULL; + return &p->sym; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + struct ppc_file *pf = (struct ppc_file *) ef; + struct ppc_symbol *psym = (struct ppc_symbol *) sym; + struct ppc_plt_entry *pe; + instruction *ip; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_PPC_ADDR16_HA: + *(unsigned short *)loc = (v + 0x8000) >> 16; + break; + + case R_PPC_ADDR16_HI: + *(unsigned short *)loc = v >> 16; + break; + + case R_PPC_ADDR16_LO: + *(unsigned short *)loc = v; + break; + + case R_PPC_REL24: + /* find the plt entry and initialize it if necessary */ + assert(psym != NULL); + for (pe = psym->plt_entries; pe != NULL && pe->addend != rel->r_addend; ) + pe = pe->next; + assert(pe != NULL); + if (!pe->inited) + { + ip = (instruction *) (pf->plt->contents + pe->offset); + ip[0] = 0x3d600000 + ((v + 0x8000) >> 16); /* lis r11,sym@ha */ + ip[1] = 0x396b0000 + (v & 0xffff); /* addi r11,r11,sym@l */ + ip[2] = 0x7d6903a6; /* mtctr r11 */ + ip[3] = 0x4e800420; /* bctr */ + pe->inited = 1; + } + + v -= dot; + if ((int)v < -0x02000000 || (int)v >= 0x02000000) + { + /* go via the plt */ + v = pf->plt->header.sh_addr + pe->offset - dot; + } + if (v & 3) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x03fffffc) | (v & 0x03fffffc); + break; + + case R_PPC_REL32: + *loc = v - dot; + break; + + case R_PPC_ADDR32: + *loc = v; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct ppc_file *pf = (struct ppc_file *) f; + int i, offset; + struct obj_section *sec, *syms, *strs; + ElfW(Rela) *rel, *relend; + ElfW(Sym) *symtab, *extsym; + const char *strtab, *name; + struct ppc_symbol *intsym; + struct ppc_plt_entry *pe; + + offset = 0; + for (i = 0; i < f->header.e_shnum; ++i) + { + sec = f->sections[i]; + if (sec->header.sh_type != SHT_RELM) + continue; + syms = f->sections[sec->header.sh_link]; + strs = f->sections[syms->header.sh_link]; + + rel = (ElfW(RelM) *) sec->contents; + relend = rel + (sec->header.sh_size / sizeof(ElfW(RelM))); + symtab = (ElfW(Sym) *) syms->contents; + strtab = (const char *) strs->contents; + + for (; rel < relend; ++rel) + { + if (ELF32_R_TYPE(rel->r_info) != R_PPC_REL24) + continue; + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct ppc_symbol *) obj_find_symbol(f, name); + + for (pe = intsym->plt_entries; pe != NULL; pe = pe->next) + if (pe->addend == rel->r_addend) + break; + if (pe == NULL) + { + pe = xmalloc(sizeof(struct ppc_plt_entry)); + pe->next = intsym->plt_entries; + pe->addend = rel->r_addend; + pe->offset = offset; + pe->inited = 0; + intsym->plt_entries = pe; + offset += 16; + } + } + } + + pf->plt = obj_create_alloced_section(f, ".plt", 16, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} 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; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_s390.c b/mdk-stage1/insmod-modutils/obj/obj_s390.c new file mode 100644 index 000000000..3da72e771 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_s390.c @@ -0,0 +1,245 @@ +/* S/390 specific support for Elf loading and relocation. + 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 <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct s390_got_entry +{ + int offset; + unsigned offset_done : 1; + unsigned reloc_done : 1; +}; + +struct s390_file +{ + struct obj_file root; + struct obj_section *got; +}; + +struct s390_symbol +{ + struct obj_symbol root; + struct s390_got_entry gotent; +}; + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + struct s390_file *f; + f = xmalloc(sizeof(*f)); + f->got = NULL; + return &f->root; +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + struct s390_symbol *sym; + sym = xmalloc(sizeof(*sym)); + memset(&sym->gotent, 0, sizeof(sym->gotent)); + return &sym->root; +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *f, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + struct s390_file *ifile = (struct s390_file *)f; + struct s390_symbol *isym = (struct s390_symbol *)sym; + + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + Elf32_Addr got = ifile->got ? ifile->got->header.sh_addr : 0; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_390_NONE: + break; + + case R_390_32: + *loc += v; + break; + + case R_390_PLT32: + case R_390_PC32: + *loc += v - dot; + break; + + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + *loc = v; + break; + + case R_390_RELATIVE: + *loc += f->baseaddr; + break; + + case R_390_GOTPC: + assert(got != 0); + *loc += got - dot; + break; + + case R_390_GOT32: + assert(isym != NULL); + if (!isym->gotent.reloc_done) + { + isym->gotent.reloc_done = 1; + *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v; + } + *loc += isym->gotent.offset; + break; + + case R_390_GOTOFF: + assert(got != 0); + *loc += v - got; + break; + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *f) +{ + struct s390_file *ifile = (struct s390_file *)f; + int i, n, offset = 0, gotneeded = 0; + + n = ifile->root.header.e_shnum; + for (i = 0; i < n; ++i) + { + struct obj_section *relsec, *symsec, *strsec; + Elf32_Rel *rel, *relend; + Elf32_Sym *symtab; + const char *strtab; + + relsec = ifile->root.sections[i]; + if (relsec->header.sh_type != SHT_REL) + continue; + + symsec = ifile->root.sections[relsec->header.sh_link]; + strsec = ifile->root.sections[symsec->header.sh_link]; + + rel = (Elf32_Rel *)relsec->contents; + relend = rel + (relsec->header.sh_size / sizeof(Elf32_Rel)); + symtab = (Elf32_Sym *)symsec->contents; + strtab = (const char *)strsec->contents; + + for (; rel < relend; ++rel) + { + Elf32_Sym *extsym; + struct s390_symbol *intsym; + const char *name; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_390_GOTPC: + case R_390_GOTOFF: + gotneeded = 1; + default: + continue; + + case R_390_GOT32: + break; + } + + extsym = &symtab[ELF32_R_SYM(rel->r_info)]; + if (extsym->st_name) + name = strtab + extsym->st_name; + else + name = f->sections[extsym->st_shndx]->name; + intsym = (struct s390_symbol *)obj_find_symbol(&ifile->root, name); + + if (!intsym->gotent.offset_done) + { + intsym->gotent.offset_done = 1; + intsym->gotent.offset = offset; + offset += 4; + } + } + } + + if (offset > 0 || gotneeded) + ifile->got = obj_create_alloced_section(&ifile->root, ".got", 4, offset); + + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *m) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_sparc.c b/mdk-stage1/insmod-modutils/obj/obj_sparc.c new file mode 100644 index 000000000..1a03c9090 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_sparc.c @@ -0,0 +1,226 @@ +/* Sparc specific support for Elf loading and relocation. + 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 <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + return xmalloc(sizeof(struct obj_file)); +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf32_Rela *rel, + Elf32_Addr v) +{ + Elf32_Addr *loc = (Elf32_Addr *)(targsec->contents + rel->r_offset); + Elf32_Addr dot = targsec->header.sh_addr + rel->r_offset; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF32_R_TYPE(rel->r_info)) + { + case R_SPARC_NONE: + break; + case R_SPARC_8: + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + case R_SPARC_16: + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_32: + *loc = v; + break; + case R_SPARC_DISP8: + v -= dot; + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + case R_SPARC_DISP16: + v -= dot; + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_DISP32: + v -= dot; + *loc = v; + break; + case R_SPARC_WDISP30: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffffff) | (v >> 2); + break; + case R_SPARC_WDISP22: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffff) | ((v >> 2) & 0x3fffff); + break; + case R_SPARC_HI22: + *loc = (*loc & ~0x3fffff) | (v >> 10); + break; + case R_SPARC_22: + if (v > 0x3fffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3fffff) | (v & 0x3fffff); + break; + case R_SPARC_13: + if (v > 0x1fff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1fff) | (v & 0x1fff); + break; + case R_SPARC_LO10: + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + + case R_SPARC_PC10: + v -= dot; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_PC22: + v -= dot; + *loc = (*loc & ~0x3fffff) | (v >> 10); + break; + + case R_SPARC_UA32: + *(((char *)loc) + 0) = (char)(v >> 24); + *(((char *)loc) + 1) = (char)(v >> 16); + *(((char *)loc) + 2) = (char)(v >> 8); + *(((char *)loc) + 3) = (char)v; + break; + +#ifdef R_SPARC_10 + case R_SPARC_10: + if (v > 0x3ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_11: + if (v > 0x7ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7ff) | (v & 0x7ff); + break; + case R_SPARC_WDISP16: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x303fff) | ((v << 4) & 0x300000) | ((v >> 2) & 0x3fff); + break; + case R_SPARC_WDISP19: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x7ffff) | ((v >> 2) & 0x7ffff); + break; + case R_SPARC_7: + if (v > 0x7f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7f) | (v & 0x7f); + break; + case R_SPARC_5: + if (v > 0x1f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1f) | (v & 0x1f); + break; + case R_SPARC_6: + if (v > 0x3f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3f) | (v & 0x3f); + break; +#endif /* R_SPARC_10 */ + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *ef) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf32_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} diff --git a/mdk-stage1/insmod-modutils/obj/obj_sparc64.c b/mdk-stage1/insmod-modutils/obj/obj_sparc64.c new file mode 100644 index 000000000..84e8d18a0 --- /dev/null +++ b/mdk-stage1/insmod-modutils/obj/obj_sparc64.c @@ -0,0 +1,352 @@ +/* Sparc64 specific support for Elf loading and relocation. + Copyright 1997 Linux International. + + Contributed by Jakub Jelinek <jj@sunsite.mff.cuni.cz> + + 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 <stddef.h> +#include <module.h> +#include <obj.h> +#include <util.h> + + +/*======================================================================*/ + +struct obj_file * +arch_new_file (void) +{ + return xmalloc(sizeof(struct obj_file)); +} + +struct obj_section * +arch_new_section (void) +{ + return xmalloc(sizeof(struct obj_section)); +} + +struct obj_symbol * +arch_new_symbol (void) +{ + return xmalloc(sizeof(struct obj_symbol)); +} + +#ifdef BROKEN_SPARC64_RELOCS + +#undef R_SPARC_PLT32 +#undef R_SPARC_HIPLT22 +#undef R_SPARC_LOPLT10 +#undef R_SPARC_PCPLT32 +#undef R_SPARC_PCPLT22 +#undef R_SPARC_PCPLT10 +#undef R_SPARC_10 +#undef R_SPARC_11 +#undef R_SPARC_64 +#undef R_SPARC_OLO10 +#undef R_SPARC_HH22 +#undef R_SPARC_HM10 +#undef R_SPARC_LM22 +#undef R_SPARC_PC_HH22 +#undef R_SPARC_PC_HM10 +#undef R_SPARC_PC_LM22 +#undef R_SPARC_WDISP16 +#undef R_SPARC_WDISP19 +#undef R_SPARC_GLOB_JMP +#undef R_SPARC_7 +#undef R_SPARC_5 +#undef R_SPARC_6 + +#define R_SPARC_10 24 +#define R_SPARC_11 25 +#define R_SPARC_64 26 +#define R_SPARC_OLO10 27 +#define R_SPARC_HH22 28 +#define R_SPARC_HM10 29 +#define R_SPARC_LM22 30 +#define R_SPARC_PC_HH22 31 +#define R_SPARC_PC_HM10 32 +#define R_SPARC_PC_LM22 33 +#define R_SPARC_WDISP16 34 +#define R_SPARC_WDISP19 35 +#define R_SPARC_GLOB_JMP 36 +#define R_SPARC_7 37 +#define R_SPARC_5 38 +#define R_SPARC_6 39 + +#else + +#ifndef R_SPARC_64 + +#define R_SPARC_64 32 +#define R_SPARC_OLO10 33 +#define R_SPARC_HH22 34 +#define R_SPARC_HM10 35 +#define R_SPARC_LM22 36 +#define R_SPARC_PC_HH22 37 +#define R_SPARC_PC_HM10 38 +#define R_SPARC_PC_LM22 39 + +#endif + +#endif + +int +arch_load_proc_section(struct obj_section *sec, int fp) +{ + /* Assume it's just a debugging section that we can safely + ignore ... */ + sec->contents = NULL; + + return 0; +} + +#define ELF64_R_TYPE_ID(info) ((info) & 0xff) +#define ELF64_R_TYPE_DATA(info) ((info) >> 8) + +enum obj_reloc +arch_apply_relocation (struct obj_file *ef, + struct obj_section *targsec, + struct obj_section *symsec, + struct obj_symbol *sym, + Elf64_Rela *rel, + Elf64_Addr v) +{ + unsigned int *loc = (unsigned int *)(targsec->contents + rel->r_offset); + unsigned int dot = targsec->header.sh_addr + rel->r_offset; + + enum obj_reloc ret = obj_reloc_ok; + + switch (ELF64_R_TYPE_ID(rel->r_info)) + { + case R_SPARC_NONE: + break; + + case R_SPARC_64: + case R_SPARC_UA64: + if (! ((long) loc & 3)) { + /* Common in .eh_frame */ + ((unsigned int *) loc) [0] = v >> 32; + ((unsigned int *) loc) [1] = v; + break; + } + ((unsigned char *) loc) [0] = v >> 56; + ((unsigned char *) loc) [1] = v >> 48; + ((unsigned char *) loc) [2] = v >> 40; + ((unsigned char *) loc) [3] = v >> 32; + ((unsigned char *) loc) [4] = v >> 24; + ((unsigned char *) loc) [5] = v >> 16; + ((unsigned char *) loc) [6] = v >> 8; + ((unsigned char *) loc) [7] = v; + break; + case R_SPARC_32: + case R_SPARC_UA32: + if (! ((long) loc & 3)) { + *loc = v; + break; + } + ((unsigned char *) loc) [0] = v >> 24; + ((unsigned char *) loc) [1] = v >> 16; + ((unsigned char *) loc) [2] = v >> 8; + ((unsigned char *) loc) [3] = v; + break; + case R_SPARC_16: + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_8: + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + + case R_SPARC_DISP32: + v -= dot; + *loc = v; + break; + case R_SPARC_DISP16: + v -= dot; + if (v > 0xffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xffff) | (v & 0xffff); + break; + case R_SPARC_DISP8: + v -= dot; + if (v > 0xff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0xff) | (v & 0xff); + break; + case R_SPARC_WDISP30: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffffff) | ((v >> 2) & 0x3fffffff); + break; + + /* MEDLOW code model relocs */ + case R_SPARC_LO10: + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_HI22: + *loc = (*loc & ~0x3fffff) | (v >> 10); + break; + case R_SPARC_OLO10: + *loc = (*loc & ~0x1fff) | (((v & 0x3ff) + ELF64_R_TYPE_DATA (rel->r_info)) & 0x1fff); + break; + + /* MEDMID code model relocs */ + case R_SPARC_H44: + *loc = (*loc & ~0x3fffff) | (v >> 22); + break; + case R_SPARC_M44: + *loc = (*loc & ~0x3ff) | ((v >> 12) & 0x3ff); + break; + case R_SPARC_L44: + *loc = (*loc & ~0xfff) | (v & 0xfff); + break; + + /* MEDANY code model relocs */ + case R_SPARC_HH22: + *loc = (*loc & ~0x3fffff) | (v >> 42); + break; + case R_SPARC_HM10: + *loc = (*loc & ~0x3ff) | ((v >> 32) & 0x3ff); + break; + case R_SPARC_LM22: + *loc = (*loc & ~0x3fffff) | ((v >> 10) & 0x3fffff); + break; + + case R_SPARC_WDISP22: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x3fffff) | ((v >> 2) & 0x3fffff); + break; + case R_SPARC_22: + if (v > 0x3fffff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3fffff) | (v & 0x3fffff); + break; + case R_SPARC_13: + if (v > 0x1fff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1fff) | (v & 0x1fff); + break; + + case R_SPARC_PC10: + v -= dot; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_PC22: + v -= dot; + *loc = (*loc & ~0x3fffff) | ((v >> 10) & 0x3fffff); + break; + +#ifdef R_SPARC_10 + case R_SPARC_10: + if (v > 0x3ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3ff) | (v & 0x3ff); + break; + case R_SPARC_11: + if (v > 0x7ff) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7ff) | (v & 0x7ff); + break; + +#ifdef R_SPARC_64 + case R_SPARC_PC_HH22: + v -= dot; + *loc = (*loc & ~0x3fffff) | (v >> 42); + break; + case R_SPARC_PC_HM10: + v -= dot; + *loc = (*loc & ~0x3ff) | ((v >> 32) & 0x3ff); + break; + case R_SPARC_PC_LM22: + v -= dot; + *loc = (*loc & ~0x3fffff) | ((v >> 10) & 0x3fffff); + break; +#endif + + case R_SPARC_WDISP16: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x303fff) | ((v << 4) & 0x300000) | ((v >> 2) & 0x3fff); + break; + case R_SPARC_WDISP19: + v -= dot; + if (v % 4) + ret = obj_reloc_dangerous; + *loc = (*loc & ~0x7ffff) | ((v >> 2) & 0x7ffff); + break; + case R_SPARC_7: + if (v > 0x7f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x7f) | (v & 0x7f); + break; + case R_SPARC_5: + if (v > 0x1f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x1f) | (v & 0x1f); + break; + case R_SPARC_6: + if (v > 0x3f) + ret = obj_reloc_overflow; + *loc = (*loc & ~0x3f) | (v & 0x3f); + break; +#endif /* R_SPARC_10 */ + + default: + ret = obj_reloc_unhandled; + break; + } + + return ret; +} + +int +arch_create_got (struct obj_file *ef) +{ + return 1; +} + +int +arch_init_module (struct obj_file *f, struct module *mod) +{ + return 1; +} + +int +arch_finalize_section_address(struct obj_file *f, Elf64_Addr base) +{ + int i, n = f->header.e_shnum; + + f->baseaddr = base; + for (i = 0; i < n; ++i) + f->sections[i]->header.sh_addr += base; + return 1; +} + +int +arch_archdata (struct obj_file *fin, struct obj_section *sec) +{ + return 0; +} |