summaryrefslogtreecommitdiffstats
path: root/mdk-stage1/insmod-modutils/obj/obj_hppa.c
diff options
context:
space:
mode:
authorGuillaume Cottenceau <gc@mandriva.com>2001-02-22 17:21:43 +0000
committerGuillaume Cottenceau <gc@mandriva.com>2001-02-22 17:21:43 +0000
commit31d44a623579fbca300f20bc751c7278c4375980 (patch)
treee54cb0772ebc6ffce9fc7ccdcc61d094751d3b54 /mdk-stage1/insmod-modutils/obj/obj_hppa.c
parent446293819c1c265f0799036bde77f98145187ecf (diff)
downloaddrakx-31d44a623579fbca300f20bc751c7278c4375980.tar
drakx-31d44a623579fbca300f20bc751c7278c4375980.tar.gz
drakx-31d44a623579fbca300f20bc751c7278c4375980.tar.bz2
drakx-31d44a623579fbca300f20bc751c7278c4375980.tar.xz
drakx-31d44a623579fbca300f20bc751c7278c4375980.zip
use modutils for non Intel arch's
Diffstat (limited to 'mdk-stage1/insmod-modutils/obj/obj_hppa.c')
-rw-r--r--mdk-stage1/insmod-modutils/obj/obj_hppa.c668
1 files changed, 668 insertions, 0 deletions
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;
+}
+