/* IBM S/390 31-bit specific support for Elf loading and relocation. Copyright 2000, 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation. Contributed by Martin Schwidefsky Derived from obj/obj_i386.c: Copyright 1996, 1997 Linux International. Contributed by Richard Henderson 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 #include #include #include #include /*======================================================================*/ struct s390_plt_entry { long offset; int allocated:1; int initialized:1; }; struct s390_got_entry { long offset; unsigned allocated:1; unsigned reloc_done : 1; }; struct s390_file { struct obj_file root; struct obj_section *plt; struct obj_section *got; }; struct s390_symbol { struct obj_symbol root; struct s390_plt_entry pltent; struct s390_got_entry gotent; }; /*======================================================================*/ struct obj_file * arch_new_file (void) { struct s390_file *f; f = xmalloc(sizeof(*f)); f->got = NULL; f->plt = 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)); 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_Rela *rel, Elf32_Addr v) { struct s390_file *ifile = (struct s390_file *) f; struct s390_symbol *isym = (struct s390_symbol *) sym; struct s390_plt_entry *pe; 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; Elf32_Addr plt = ifile->plt ? ifile->plt->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: *(unsigned int *) loc += v; break; case R_390_16: *(unsigned short *) loc += v; break; case R_390_8: *(unsigned char *) loc += v; break; case R_390_PC32: *(unsigned int *) loc += v - dot; break; case R_390_PC16DBL: *(unsigned short *) loc += (v - dot) >> 1; break; case R_390_PC16: *(unsigned short *) loc += v - dot; break; case R_390_PLT32: case R_390_PLT16DBL: /* find the plt entry and initialize it. */ assert(isym != NULL); pe = (struct s390_plt_entry *) &isym->pltent; assert(pe->allocated); if (pe->initialized == 0) { unsigned int *ip = (unsigned int *)(ifile->plt->contents + pe->offset); ip[0] = 0x0d105810; /* basr 1,0; lg 1,10(1); br 1 */ ip[1] = 0x100607f1; if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL) ip[2] = v - 2; else ip[2] = v; pe->initialized = 1; } /* Insert relative distance to target. */ v = plt + pe->offset - dot; if (ELF32_R_TYPE(rel->r_info) == R_390_PLT32) *(unsigned int *) loc = (unsigned int) v; else if (ELF32_R_TYPE(rel->r_info) == R_390_PLT16DBL) *(unsigned short *) loc = (unsigned short) ((v + 2) >> 1); 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); *(unsigned long *) loc += got - dot; break; case R_390_GOT12: case R_390_GOT16: case R_390_GOT32: assert(isym != NULL); assert(got != 0); if (!isym->gotent.reloc_done) { isym->gotent.reloc_done = 1; *(Elf32_Addr *)(ifile->got->contents + isym->gotent.offset) = v; } if (ELF32_R_TYPE(rel->r_info) == R_390_GOT12) *(unsigned short *) loc |= (*(unsigned short *) loc + isym->gotent.offset) & 0xfff; else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT16) *(unsigned short *) loc += isym->gotent.offset; else if (ELF32_R_TYPE(rel->r_info) == R_390_GOT32) *(unsigned int *) 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, got_offset = 0, plt_offset = 0, gotneeded = 0; for (i = 0; i < f->header.e_shnum; ++i) { struct obj_section *relsec, *symsec, *strsec; Elf32_Rela *rel, *relend; Elf32_Sym *symtab; const char *strtab; relsec = f->sections[i]; if (relsec->header.sh_type != SHT_RELA) continue; symsec = f->sections[relsec->header.sh_link]; strsec = f->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 = (const char *)strsec->contents; for (; rel < relend; ++rel) { struct s390_symbol *intsym; struct s390_plt_entry *pe; struct s390_got_entry *ge; switch (ELF32_R_TYPE(rel->r_info)) { /* These four relocations refer to a plt entry. */ case R_390_PLT16DBL: case R_390_PLT32: obj_find_relsym(intsym, f, f, rel, symtab, strtab); assert(intsym); pe = &intsym->pltent; if (!pe->allocated) { pe->allocated = 1; pe->offset = plt_offset; plt_offset += 12; } break; /* The next three don't need got entries but the address of the got itself. */ case R_390_GOTPC: case R_390_GOTOFF: gotneeded = 1; break; case R_390_GOT12: case R_390_GOT16: case R_390_GOT32: obj_find_relsym(intsym, f, f, rel, symtab, strtab); assert(intsym); ge = (struct s390_got_entry *) &intsym->gotent; if (!ge->allocated) { ge->allocated = 1; ge->offset = got_offset; got_offset += sizeof(void*); } break; default: break; } } } if (got_offset > 0 || gotneeded) { struct obj_section *gotsec; struct obj_symbol *gotsym; gotsec = obj_find_section(f, ".got"); if (gotsec == NULL) gotsec = obj_create_alloced_section(f, ".got", 4, got_offset, SHF_WRITE); else obj_extend_section(gotsec, got_offset); gotsym = obj_add_symbol(f, "_GLOBAL_OFFSET_TABLE_", -1, ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), gotsec->idx, 0, 0); gotsym->secidx = gotsec->idx; /* mark the symbol as defined */ ifile->got = gotsec; } if (plt_offset > 0) ifile->plt = obj_create_alloced_section(f, ".plt", 4, plt_offset, SHF_WRITE); 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; }