static void check_rela(Elf_Rela *rela, struct module *me) { struct mod_arch_syminfo *info; info = me->arch.syminfo + ELF_R_SYM (rela->r_info); switch (ELF_R_TYPE (rela->r_info)) { case R_390_GOT12: /* 12 bit GOT offset. */ case R_390_GOT16: /* 16 bit GOT offset. */ case R_390_GOT20: /* 20 bit GOT offset. */ case R_390_GOT32: /* 32 bit GOT offset. */ case R_390_GOT64: /* 64 bit GOT offset. */ case R_390_GOTENT: /* 32 bit PC rel. to GOT entry shifted by 1. */ case R_390_GOTPLT12: /* 12 bit offset to jump slot. */ case R_390_GOTPLT16: /* 16 bit offset to jump slot. */ case R_390_GOTPLT20: /* 20 bit offset to jump slot. */ case R_390_GOTPLT32: /* 32 bit offset to jump slot. */ case R_390_GOTPLT64: /* 64 bit offset to jump slot. */ case R_390_GOTPLTENT: /* 32 bit rel. offset to jump slot >> 1. */ if (info->got_offset == -1UL) { info->got_offset = me->arch.got_size; me->arch.got_size += sizeof(void*); } break; case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ case R_390_PLT32: /* 32 bit PC relative PLT address. */ case R_390_PLT64: /* 64 bit PC relative PLT address. */ case R_390_PLTOFF16: /* 16 bit offset from GOT to PLT. */ case R_390_PLTOFF32: /* 32 bit offset from GOT to PLT. */ case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */ if (info->plt_offset == -1UL) { info->plt_offset = me->arch.plt_size; me->arch.plt_size += PLT_ENTRY_SIZE; } break; case R_390_COPY: case R_390_GLOB_DAT: case R_390_JMP_SLOT: case R_390_RELATIVE: /* Only needed if we want to support loading of modules linked with -shared. */ break; } }
int _rtld_relocate_plt_lazy(const Obj_Entry *obj) { if (!obj->relocbase) return 0; for (const Elf_Rel *rel = obj->pltrel; rel < obj->pltrellim; rel++) { Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); assert(ELF_R_TYPE(rel->r_info) == R_TYPE(JUMP_SLOT)); /* Just relocate the GOT slots pointing into the PLT */ *where += (Elf_Addr)obj->relocbase; rdbg(("fixup !main in %s --> %p", obj->path, (void *)*where)); } return 0; }
/* * * Process the PLT relocations. * */ int reloc_plt(Obj_Entry *obj) { const Elf_Rel *rellim; const Elf_Rel *rel; rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where; assert(ELF_R_TYPE(rel->r_info) == R_ARM_JUMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rel->r_offset); *where += (Elf_Addr )obj->relocbase; } return (0); }
/* * Process the special R_386_COPY relocations in the main program. These * copy data from a shared object into a region in the main program's BSS * segment. * * Returns 0 on success, -1 on failure. */ int do_copy_relocations(Obj_Entry *dstobj) { const Elf_Rel *rellim; const Elf_Rel *rel; assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); for (rel = dstobj->rel; rel < rellim; rel++) { if (ELF_R_TYPE(rel->r_info) == R_386_COPY) { void *dstaddr; const Elf_Sym *dstsym; const char *name; unsigned long hash; size_t size; const void *srcaddr; const Elf_Sym *srcsym; Obj_Entry *srcobj; dstaddr = (void *) (dstobj->relocbase + rel->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); name = dstobj->strtab + dstsym->st_name; hash = elf_hash(name); size = dstsym->st_size; for (srcobj = dstobj->next; srcobj != NULL; srcobj = srcobj->next) if ((srcsym = symlook_obj(name, hash, srcobj, false)) != NULL) break; if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" referenced from COPY" " relocation in %s", name, dstobj->path); return -1; } srcaddr = (const void *) (srcobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } } return 0; }
el_status el_applyrela(el_ctx *ctx, Elf_RelA *rel) { uint64_t *p = (uint64_t*) (rel->r_offset + ctx->base_load_vaddr); uint32_t type = ELF_R_TYPE(rel->r_info); uint32_t sym = ELF_R_SYM(rel->r_info); switch (type) { case R_AMD64_NONE: break; case R_AMD64_RELATIVE: EL_DEBUG("Applying R_AMD64_RELATIVE reloc @%p\n", p); *p = rel->r_addend + ctx->base_load_vaddr; break; default: EL_DEBUG("Bad relocation %u\n", type); return EL_BADREL; } return EL_OK; }
int _rtld_relocate_plt_lazy(const Obj_Entry *obj) { const Elf_Rela *rela; if (!obj->relocbase) return 0; for (rela = obj->pltrela; rela < obj->pltrelalim; rela++) { Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); assert(ELF_R_TYPE(rela->r_info) == R_TYPE(JMP_SLOT)); /* Just relocate the GOT slots pointing into the PLT */ *where += (Elf_Addr)obj->relocbase; rdbg(("lazy fixup pltgot %p in %s --> %p", where, obj->path, (void *)*where)); } return 0; }
void grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, grub_size_t *got) { const Elf_Ehdr *e = ehdr; const Elf_Shdr *s; unsigned i; *tramp = 0; *got = 0; /* Find a symbol table. */ for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) if (s->sh_type == SHT_SYMTAB) break; if (i == e->e_shnum) return; for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); i < e->e_shnum; i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) if (s->sh_type == SHT_RELA) { const Elf_Rela *rel, *max; for (rel = (const Elf_Rela *) ((const char *) e + s->sh_offset), max = rel + s->sh_size / s->sh_entsize; rel < max; rel++) if (ELF_R_TYPE (rel->r_info) == GRUB_ELF_R_PPC_REL24) (*tramp)++; } return; }
/* ARGSUSED3 */ void undo_reloc(void *vrel, uchar_t *oaddr, uchar_t *iaddr, Reloc *reloc) { Rela *rel = vrel; const Rel_entry *rep; Xword rtype = ELF_R_TYPE(rel->r_info, M_MACH); ulong_t *_oaddr; ulong_t *_iaddr; switch (rtype) { case R_SPARC_NONE: break; case R_SPARC_COPY: (void) memset((void *)oaddr, 0, (size_t)reloc->r_size); break; case R_SPARC_JMP_SLOT: /* LINTED */ _oaddr = (unsigned long *)oaddr; /* LINTED */ _iaddr = (unsigned long *)iaddr; if (_iaddr) { *_oaddr++ = *_iaddr++; *_oaddr++ = *_iaddr++; *_oaddr = *_iaddr; } else { *_oaddr++ = 0; *_oaddr++ = 0; *_oaddr = 0; } break; default: rep = &reloc_table[rtype]; if (iaddr) (void) memcpy(oaddr, iaddr, rep->re_fsize); else (void) memset(oaddr, 0, rep->re_fsize); } }
/* ARGSUSED3 */ void inc_reloc(void *vnrel, void *vorel, Reloc *reloc, uchar_t *oaddr, uchar_t *iaddr) { Rela *nrel = vnrel; Rela *orel = vorel; if (ELF_R_TYPE(nrel->r_info, M_MACH) == R_AMD64_JUMP_SLOT) { /* LINTED */ ulong_t *_oaddr = (ulong_t *)oaddr; /* LINTED */ ulong_t *_iaddr = (ulong_t *)iaddr; if (_iaddr) *_oaddr = *_iaddr + reloc->r_value; else *_oaddr = reloc->r_value; } *nrel = *orel; nrel->r_offset += reloc->r_value; }
int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; Elf_Addr *where, target, *ptr; if (!obj->irelative) return (0); relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_AARCH64_IRELATIVE) { ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); lock_release(rtld_bind_lock, lockstate); target = call_ifunc_resolver(ptr); wlock_acquire(rtld_bind_lock, lockstate); *where = target; } } obj->irelative = false; return (0); }
static int _rtld_relocate_plt_object(const Obj_Entry *obj, const Elf_Rel *rel, Elf_Addr *tp) { Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rel->r_offset); Elf_Addr new_value; const Elf_Sym *def; const Obj_Entry *defobj; unsigned long info = rel->r_info; assert(ELF_R_TYPE(info) == R_TYPE(JUMP_SLOT)); def = _rtld_find_plt_symdef(ELF_R_SYM(info), obj, &defobj, tp != NULL); if (__predict_false(def == NULL)) return -1; if (__predict_false(def == &_rtld_sym_zero)) return 0; if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { if (tp == NULL) return 0; new_value = _rtld_resolve_ifunc(defobj, def); } else { new_value = (Elf_Addr)(defobj->relocbase + def->st_value); } /* Set the Thumb bit, if needed. */ if (ELF_ST_TYPE(def->st_info) == STT_ARM_TFUNC) new_value |= 1; rdbg(("bind now/fixup in %s --> old=%p new=%p", defobj->strtab + def->st_name, (void *)*where, (void *)new_value)); if (*where != new_value) *where = new_value; if (tp) *tp = new_value; return 0; }
/* * LD_BIND_NOW was set - force relocation for all jump slots */ int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; if (obj->jmpslots_done) return (0); relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where, target; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); switch(ELF_R_TYPE(rela->r_info)) { case R_AARCH64_JUMP_SLOT: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { obj->gnu_ifunc = true; continue; } target = (Elf_Addr)(defobj->relocbase + def->st_value); reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela); break; } } obj->jmpslots_done = true; return (0); }
int reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; Elf_Addr *where, target; if (!obj->irelative) return (0); rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { switch (ELF_R_TYPE(rel->r_info)) { case R_386_IRELATIVE: where = (Elf_Addr *)(obj->relocbase + rel->r_offset); lock_release(rtld_bind_lock, lockstate); target = call_ifunc_resolver(obj->relocbase + *where); wlock_acquire(rtld_bind_lock, lockstate); *where = target; break; } } obj->irelative = false; return (0); }
bool ElfRelocations::ApplyRelaReloc(const ELF::Rela* rela, const ElfSymbols* symbols, SymbolResolver* resolver, Error* error) { const ELF::Word rel_type = ELF_R_TYPE(rela->r_info); const ELF::Word rel_symbol = ELF_R_SYM(rela->r_info); ELF::Addr sym_addr = 0; ELF::Addr reloc = static_cast<ELF::Addr>(rela->r_offset + load_bias_); RLOG(" reloc=%p offset=%p type=%d symbol=%d\n", reloc, rela->r_offset, rel_type, rel_symbol); if (rel_type == 0) return true; bool resolved = false; // If this is a symbolic relocation, compute the symbol's address. if (__builtin_expect(rel_symbol != 0, 0)) { if (!ResolveSymbol(rel_type, rel_symbol, symbols, resolver, reloc, &sym_addr, error)) { return false; } resolved = true; } return ApplyResolvedRelaReloc(rela, sym_addr, resolved, error); }
/* ARGSUSED3 */ void undo_reloc(void *vrel, uchar_t *oaddr, uchar_t *iaddr, Reloc *reloc) { Rela *rel = vrel; Xword rtype = ELF_R_TYPE(rel->r_info, M_MACH); switch (rtype) { case R_AMD64_NONE: break; case R_AMD64_COPY: (void) memset((void *)oaddr, 0, (size_t)reloc->r_size); break; case R_AMD64_JUMP_SLOT: { /* LINTED */ ulong_t *_oaddr = (ulong_t *)oaddr; /* LINTED */ ulong_t *_iaddr = (ulong_t *)iaddr; if (_iaddr) *_oaddr = *_iaddr + reloc->r_value; else *_oaddr = reloc->r_value; } break; default: { size_t re_fsize = reloc_table[rtype].re_fsize; if (iaddr) (void) memcpy(oaddr, iaddr, re_fsize); else (void) memset(oaddr, 0, re_fsize); } } }
/* * Read and process the relocations for one link object, we assume all * relocation sections for loadable segments are stored contiguously in * the file. */ int elf_reloc(Rt_map *lmp, uint_t plt, int *in_nfavl, APlist **textrel) { ulong_t relbgn, relend, relsiz, basebgn, pltbgn, pltend; ulong_t _pltbgn, _pltend; ulong_t dsymndx, roffset, rsymndx, psymndx = 0; uchar_t rtype; long value, pvalue; Sym *symref, *psymref, *symdef, *psymdef; Syminfo *sip; char *name, *pname; Rt_map *_lmp, *plmp; int ret = 1, noplt = 0; int relacount = RELACOUNT(lmp), plthint = 0; Rel *rel; uint_t binfo, pbinfo; APlist *bound = NULL; /* * Although only necessary for lazy binding, initialize the first * global offset entry to go to elf_rtbndr(). dbx(1) seems * to find this useful. */ if ((plt == 0) && PLTGOT(lmp)) { mmapobj_result_t *mpp; /* * Make sure the segment is writable. */ if ((((mpp = find_segment((caddr_t)PLTGOT(lmp), lmp)) != NULL) && ((mpp->mr_prot & PROT_WRITE) == 0)) && ((set_prot(lmp, mpp, 1) == 0) || (aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL))) return (0); elf_plt_init(PLTGOT(lmp), (caddr_t)lmp); } /* * Initialize the plt start and end addresses. */ if ((pltbgn = (ulong_t)JMPREL(lmp)) != 0) pltend = pltbgn + (ulong_t)(PLTRELSZ(lmp)); relsiz = (ulong_t)(RELENT(lmp)); basebgn = ADDR(lmp); if (PLTRELSZ(lmp)) plthint = PLTRELSZ(lmp) / relsiz; /* * If we've been called upon to promote an RTLD_LAZY object to an * RTLD_NOW then we're only interested in scaning the .plt table. * An uninitialized .plt is the case where the associated got entry * points back to the plt itself. Determine the range of the real .plt * entries using the _PROCEDURE_LINKAGE_TABLE_ symbol. */ if (plt) { Slookup sl; Sresult sr; relbgn = pltbgn; relend = pltend; if (!relbgn || (relbgn == relend)) return (1); /* * Initialize the symbol lookup, and symbol result, data * structures. */ SLOOKUP_INIT(sl, MSG_ORIG(MSG_SYM_PLT), lmp, lmp, ld_entry_cnt, elf_hash(MSG_ORIG(MSG_SYM_PLT)), 0, 0, 0, LKUP_DEFT); SRESULT_INIT(sr, MSG_ORIG(MSG_SYM_PLT)); if (elf_find_sym(&sl, &sr, &binfo, NULL) == 0) return (1); symdef = sr.sr_sym; _pltbgn = symdef->st_value; if (!(FLAGS(lmp) & FLG_RT_FIXED) && (symdef->st_shndx != SHN_ABS)) _pltbgn += basebgn; _pltend = _pltbgn + (((PLTRELSZ(lmp) / relsiz)) * M_PLT_ENTSIZE) + M_PLT_RESERVSZ; } else { /* * The relocation sections appear to the run-time linker as a * single table. Determine the address of the beginning and end * of this table. There are two different interpretations of * the ABI at this point: * * o The REL table and its associated RELSZ indicate the * concatenation of *all* relocation sections (this is the * model our link-editor constructs). * * o The REL table and its associated RELSZ indicate the * concatenation of all *but* the .plt relocations. These * relocations are specified individually by the JMPREL and * PLTRELSZ entries. * * Determine from our knowledege of the relocation range and * .plt range, the range of the total relocation table. Note * that one other ABI assumption seems to be that the .plt * relocations always follow any other relocations, the * following range checking drops that assumption. */ relbgn = (ulong_t)(REL(lmp)); relend = relbgn + (ulong_t)(RELSZ(lmp)); if (pltbgn) { if (!relbgn || (relbgn > pltbgn)) relbgn = pltbgn; if (!relbgn || (relend < pltend)) relend = pltend; } } if (!relbgn || (relbgn == relend)) { DBG_CALL(Dbg_reloc_run(lmp, 0, plt, DBG_REL_NONE)); return (1); } DBG_CALL(Dbg_reloc_run(lmp, M_REL_SHT_TYPE, plt, DBG_REL_START)); /* * If we're processing a dynamic executable in lazy mode there is no * need to scan the .rel.plt table, however if we're processing a shared * object in lazy mode the .got addresses associated to each .plt must * be relocated to reflect the location of the shared object. */ if (pltbgn && ((MODE(lmp) & RTLD_NOW) == 0) && (FLAGS(lmp) & FLG_RT_FIXED)) noplt = 1; sip = SYMINFO(lmp); /* * Loop through relocations. */ while (relbgn < relend) { mmapobj_result_t *mpp; uint_t sb_flags = 0; rtype = ELF_R_TYPE(((Rel *)relbgn)->r_info, M_MACH); /* * If this is a RELATIVE relocation in a shared object (the * common case), and if we are not debugging, then jump into a * tighter relocation loop (elf_reloc_relative). */ if ((rtype == R_386_RELATIVE) && ((FLAGS(lmp) & FLG_RT_FIXED) == 0) && (DBG_ENABLED == 0)) { if (relacount) { relbgn = elf_reloc_relative_count(relbgn, relacount, relsiz, basebgn, lmp, textrel, 0); relacount = 0; } else { relbgn = elf_reloc_relative(relbgn, relend, relsiz, basebgn, lmp, textrel, 0); } if (relbgn >= relend) break; rtype = ELF_R_TYPE(((Rel *)relbgn)->r_info, M_MACH); } roffset = ((Rel *)relbgn)->r_offset; /* * If this is a shared object, add the base address to offset. */ if (!(FLAGS(lmp) & FLG_RT_FIXED)) { /* * If we're processing lazy bindings, we have to step * through the plt entries and add the base address * to the corresponding got entry. */ if (plthint && (plt == 0) && (rtype == R_386_JMP_SLOT) && ((MODE(lmp) & RTLD_NOW) == 0)) { relbgn = elf_reloc_relative_count(relbgn, plthint, relsiz, basebgn, lmp, textrel, 0); plthint = 0; continue; } roffset += basebgn; } rsymndx = ELF_R_SYM(((Rel *)relbgn)->r_info); rel = (Rel *)relbgn; relbgn += relsiz; /* * Optimizations. */ if (rtype == R_386_NONE) continue; if (noplt && ((ulong_t)rel >= pltbgn) && ((ulong_t)rel < pltend)) { relbgn = pltend; continue; } /* * If we're promoting plts, determine if this one has already * been written. */ if (plt && ((*(ulong_t *)roffset < _pltbgn) || (*(ulong_t *)roffset > _pltend))) continue; /* * If this relocation is not against part of the image * mapped into memory we skip it. */ if ((mpp = find_segment((caddr_t)roffset, lmp)) == NULL) { elf_reloc_bad(lmp, (void *)rel, rtype, roffset, rsymndx); continue; } binfo = 0; /* * If a symbol index is specified then get the symbol table * entry, locate the symbol definition, and determine its * address. */ if (rsymndx) { /* * If a Syminfo section is provided, determine if this * symbol is deferred, and if so, skip this relocation. */ if (sip && is_sym_deferred((ulong_t)rel, basebgn, lmp, textrel, sip, rsymndx)) continue; /* * Get the local symbol table entry. */ symref = (Sym *)((ulong_t)SYMTAB(lmp) + (rsymndx * SYMENT(lmp))); /* * If this is a local symbol, just use the base address. * (we should have no local relocations in the * executable). */ if (ELF_ST_BIND(symref->st_info) == STB_LOCAL) { value = basebgn; name = NULL; /* * Special case TLS relocations. */ if (rtype == R_386_TLS_DTPMOD32) { /* * Use the TLS modid. */ value = TLSMODID(lmp); } else if (rtype == R_386_TLS_TPOFF) { if ((value = elf_static_tls(lmp, symref, rel, rtype, 0, roffset, 0)) == 0) { ret = 0; break; } } } else { /* * If the symbol index is equal to the previous * symbol index relocation we processed then * reuse the previous values. (Note that there * have been cases where a relocation exists * against a copy relocation symbol, our ld(1) * should optimize this away, but make sure we * don't use the same symbol information should * this case exist). */ if ((rsymndx == psymndx) && (rtype != R_386_COPY)) { /* LINTED */ if (psymdef == 0) { DBG_CALL(Dbg_bind_weak(lmp, (Addr)roffset, (Addr) (roffset - basebgn), name)); continue; } /* LINTED */ value = pvalue; /* LINTED */ name = pname; /* LINTED */ symdef = psymdef; /* LINTED */ symref = psymref; /* LINTED */ _lmp = plmp; /* LINTED */ binfo = pbinfo; if ((LIST(_lmp)->lm_tflags | AFLAGS(_lmp)) & LML_TFLG_AUD_SYMBIND) { value = audit_symbind(lmp, _lmp, /* LINTED */ symdef, dsymndx, value, &sb_flags); } } else { Slookup sl; Sresult sr; /* * Lookup the symbol definition. * Initialize the symbol lookup, and * symbol result, data structures. */ name = (char *)(STRTAB(lmp) + symref->st_name); SLOOKUP_INIT(sl, name, lmp, 0, ld_entry_cnt, 0, rsymndx, symref, rtype, LKUP_STDRELOC); SRESULT_INIT(sr, name); symdef = NULL; if (lookup_sym(&sl, &sr, &binfo, in_nfavl)) { name = (char *)sr.sr_name; _lmp = sr.sr_dmap; symdef = sr.sr_sym; } /* * If the symbol is not found and the * reference was not to a weak symbol, * report an error. Weak references * may be unresolved. */ /* BEGIN CSTYLED */ if (symdef == 0) { if (sl.sl_bind != STB_WEAK) { if (elf_reloc_error(lmp, name, rel, binfo)) continue; ret = 0; break; } else { psymndx = rsymndx; psymdef = 0; DBG_CALL(Dbg_bind_weak(lmp, (Addr)roffset, (Addr) (roffset - basebgn), name)); continue; } } /* END CSTYLED */ /* * If symbol was found in an object * other than the referencing object * then record the binding. */ if ((lmp != _lmp) && ((FLAGS1(_lmp) & FL1_RT_NOINIFIN) == 0)) { if (aplist_test(&bound, _lmp, AL_CNT_RELBIND) == 0) { ret = 0; break; } } /* * Calculate the location of definition; * symbol value plus base address of * containing shared object. */ if (IS_SIZE(rtype)) value = symdef->st_size; else value = symdef->st_value; if (!(FLAGS(_lmp) & FLG_RT_FIXED) && !(IS_SIZE(rtype)) && (symdef->st_shndx != SHN_ABS) && (ELF_ST_TYPE(symdef->st_info) != STT_TLS)) value += ADDR(_lmp); /* * Retain this symbol index and the * value in case it can be used for the * subsequent relocations. */ if (rtype != R_386_COPY) { psymndx = rsymndx; pvalue = value; pname = name; psymdef = symdef; psymref = symref; plmp = _lmp; pbinfo = binfo; } if ((LIST(_lmp)->lm_tflags | AFLAGS(_lmp)) & LML_TFLG_AUD_SYMBIND) { dsymndx = (((uintptr_t)symdef - (uintptr_t)SYMTAB(_lmp)) / SYMENT(_lmp)); value = audit_symbind(lmp, _lmp, symdef, dsymndx, value, &sb_flags); } } /* * If relocation is PC-relative, subtract * offset address. */ if (IS_PC_RELATIVE(rtype)) value -= roffset; /* * Special case TLS relocations. */ if (rtype == R_386_TLS_DTPMOD32) { /* * Relocation value is the TLS modid. */ value = TLSMODID(_lmp); } else if (rtype == R_386_TLS_TPOFF) { if ((value = elf_static_tls(_lmp, symdef, rel, rtype, name, roffset, value)) == 0) { ret = 0; break; } } } } else { /* * Special cases. */ if (rtype == R_386_TLS_DTPMOD32) { /* * TLS relocation value is the TLS modid. */ value = TLSMODID(lmp); } else value = basebgn; name = NULL; } DBG_CALL(Dbg_reloc_in(LIST(lmp), ELF_DBG_RTLD, M_MACH, M_REL_SHT_TYPE, rel, NULL, 0, name)); /* * Make sure the segment is writable. */ if (((mpp->mr_prot & PROT_WRITE) == 0) && ((set_prot(lmp, mpp, 1) == 0) || (aplist_append(textrel, mpp, AL_CNT_TEXTREL) == NULL))) { ret = 0; break; } /* * Call relocation routine to perform required relocation. */ switch (rtype) { case R_386_COPY: if (elf_copy_reloc(name, symref, lmp, (void *)roffset, symdef, _lmp, (const void *)value) == 0) ret = 0; break; case R_386_JMP_SLOT: if (((LIST(lmp)->lm_tflags | AFLAGS(lmp)) & (LML_TFLG_AUD_PLTENTER | LML_TFLG_AUD_PLTEXIT)) && AUDINFO(lmp)->ai_dynplts) { int fail = 0; int pltndx = (((ulong_t)rel - (uintptr_t)JMPREL(lmp)) / relsiz); int symndx = (((uintptr_t)symdef - (uintptr_t)SYMTAB(_lmp)) / SYMENT(_lmp)); (void) elf_plt_trace_write(roffset, lmp, _lmp, symdef, symndx, pltndx, (caddr_t)value, sb_flags, &fail); if (fail) ret = 0; } else { /* * Write standard PLT entry to jump directly * to newly bound function. */ DBG_CALL(Dbg_reloc_apply_val(LIST(lmp), ELF_DBG_RTLD, (Xword)roffset, (Xword)value)); *(ulong_t *)roffset = value; } break; default: /* * Write the relocation out. */ if (do_reloc_rtld(rtype, (uchar_t *)roffset, (Word *)&value, name, NAME(lmp), LIST(lmp)) == 0) ret = 0; DBG_CALL(Dbg_reloc_apply_val(LIST(lmp), ELF_DBG_RTLD, (Xword)roffset, (Xword)value)); } if ((ret == 0) && ((LIST(lmp)->lm_flags & LML_FLG_TRC_WARN) == 0)) break; if (binfo) { DBG_CALL(Dbg_bind_global(lmp, (Addr)roffset, (Off)(roffset - basebgn), (Xword)(-1), PLT_T_FULL, _lmp, (Addr)value, symdef->st_value, name, binfo)); } } return (relocate_finish(lmp, bound, ret)); }
/* Process the non-PLT relocations. */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; SymCache *cache; const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr *where, symval; Elf32_Addr *where32; int r; r = -1; /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = calloc(obj->dynsymcount, sizeof(SymCache)); /* No need to check for NULL here */ } else cache = NULL; relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { /* * First, resolve symbol for relocations which * reference symbols. */ switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_64: case R_X86_64_PC32: case R_X86_64_GLOB_DAT: case R_X86_64_TPOFF64: case R_X86_64_TPOFF32: case R_X86_64_DTPMOD64: case R_X86_64_DTPOFF64: case R_X86_64_DTPOFF32: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) goto done; /* * If symbol is IFUNC, only perform relocation * when caller allowed it by passing * SYMLOOK_IFUNC flag. Skip the relocations * otherwise. * * Also error out in case IFUNC relocations * are specified for TLS, which cannot be * usefully interpreted. */ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_64: case R_X86_64_PC32: case R_X86_64_GLOB_DAT: if ((flags & SYMLOOK_IFUNC) == 0) { obj->non_plt_gnu_ifunc = true; continue; } symval = (Elf_Addr)rtld_resolve_ifunc( defobj, def); break; case R_X86_64_TPOFF64: case R_X86_64_TPOFF32: case R_X86_64_DTPMOD64: case R_X86_64_DTPOFF64: case R_X86_64_DTPOFF32: _rtld_error("%s: IFUNC for TLS reloc", obj->path); goto done; } } else { if ((flags & SYMLOOK_IFUNC) != 0) continue; symval = (Elf_Addr)defobj->relocbase + def->st_value; } break; default: if ((flags & SYMLOOK_IFUNC) != 0) continue; break; } where = (Elf_Addr *)(obj->relocbase + rela->r_offset); where32 = (Elf32_Addr *)where; switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_NONE: break; case R_X86_64_64: *where = symval + rela->r_addend; break; case R_X86_64_PC32: /* * I don't think the dynamic linker should * ever see this type of relocation. But the * binutils-2.6 tools sometimes generate it. */ *where32 = (Elf32_Addr)(unsigned long)(symval + rela->r_addend - (Elf_Addr)where); break; /* missing: R_X86_64_GOT32 R_X86_64_PLT32 */ case R_X86_64_COPY: /* * These are deferred until all other relocations have * been done. All we do here is make sure that the COPY * relocation is not in a shared library. They are * allowed only in executable files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_X86_64_COPY " "relocation in shared library", obj->path); goto done; } break; case R_X86_64_GLOB_DAT: *where = symval; break; case R_X86_64_TPOFF64: /* * We lazily allocate offsets for static TLS * as we see the first relocation that * references the TLS block. This allows us to * support (small amounts of) static TLS in * dynamically loaded modules. If we run out * of space, we generate an error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available " "for static Thread Local Storage", obj->path); goto done; } } *where = (Elf_Addr)(def->st_value - defobj->tlsoffset + rela->r_addend); break; case R_X86_64_TPOFF32: /* * We lazily allocate offsets for static TLS * as we see the first relocation that * references the TLS block. This allows us to * support (small amounts of) static TLS in * dynamically loaded modules. If we run out * of space, we generate an error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available " "for static Thread Local Storage", obj->path); goto done; } } *where32 = (Elf32_Addr)(def->st_value - defobj->tlsoffset + rela->r_addend); break; case R_X86_64_DTPMOD64: *where += (Elf_Addr)defobj->tlsindex; break; case R_X86_64_DTPOFF64: *where += (Elf_Addr)(def->st_value + rela->r_addend); break; case R_X86_64_DTPOFF32: *where32 += (Elf32_Addr)(def->st_value + rela->r_addend); break; case R_X86_64_RELATIVE: *where = (Elf_Addr)(obj->relocbase + rela->r_addend); break; /* * missing: * R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16, * R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8 */ default: _rtld_error("%s: Unsupported relocation type %u" " in non-PLT relocations\n", obj->path, (unsigned int)ELF_R_TYPE(rela->r_info)); goto done; } } r = 0; done: free(cache); return (r); }
/* * 1. _gp_disp symbol are not considered in this file. * 2. There is a local/external column; * local corresponds to (STB_LOCAL & STT_SECTION) and * all others are external. Because if the type of a * symbol is STT_SECTION, it must be STB_LOCAL. Thus * just consider symtype here. */ bool rtems_rtl_elf_relocate_rel (const rtems_rtl_obj_t* obj, const Elf_Rel* rel, const rtems_rtl_obj_sect_t* sect, const char* symname, const Elf_Byte syminfo, const Elf_Word symvalue) { Elf_Addr *where; Elf_Word tmp; Elf_Word addend = (Elf_Word)0; Elf_Word local = 0; uint32_t t; static Elf_Addr *where_hi16; static Elf_Addr ahl; where = (Elf_Addr *)(sect->base + rel->r_offset); addend = *where; if (syminfo == STT_SECTION) local = 1; switch (ELF_R_TYPE(rel->r_info)) { case R_TYPE(NONE): break; case R_TYPE(16): tmp = addend & 0xffff; if ((tmp & 0x8000) == 0x8000) tmp |= 0xffff0000; /* Sign extend */ tmp = symvalue + (int)tmp; if ((tmp & 0xffff0000) != 0) { printf("R_MIPS_16 Overflow\n"); return false; } *where = (tmp & 0xffff) | (*where & 0xffff0000); if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: R_MIPS_16 %p @ %p in %s\n", (void *)*(where), where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(32): tmp = symvalue + addend; if (addend != tmp) *where = tmp; if (rtems_rtl_trace (RTEMS_RTL_TRACE_RELOC)) printf ("rtl: R_MIPS_32 %p @ %p in %s\n", (void *)*(where), where, rtems_rtl_obj_oname (obj)); break; case R_TYPE(26): addend &= 0x03ffffff; addend <<= 2; if (local == 1) { /* STB_LOCAL and STT_SECTION */ tmp = symvalue + (((Elf_Addr)where & 0xf0000000) | addend); tmp >>= 2; } else { /* external */
static int extract_elf_rel(struct module_verify_data *mvdata, int secix, const Elf_Rel *reltab, size_t nrels, const char *sh_name) { struct { #if defined(MODULES_ARE_ELF32) uint32_t r_offset; uint32_t st_value; uint32_t st_size; uint16_t st_shndx; uint8_t r_type; uint8_t st_info; uint8_t st_other; #elif defined(MODULES_ARE_ELF64) uint64_t r_offset; uint64_t st_value; uint64_t st_size; uint32_t r_type; uint16_t st_shndx; uint8_t st_info; uint8_t st_other; #else #error unsupported module type #endif } __attribute__((packed)) relocation; const Elf_Rel *reloc; const Elf_Sym *symbol; size_t loop; /* contribute the relevant bits from a join of { RELA, SYMBOL, SECTION } */ for (loop = 0; loop < nrels; loop++) { int st_shndx; reloc = &reltab[loop]; /* decode the relocation */ relocation.r_offset = reloc->r_offset; relocation.r_type = ELF_R_TYPE(reloc->r_info); /* decode the symbol referenced by the relocation */ symbol = &mvdata->symbols[ELF_R_SYM(reloc->r_info)]; relocation.st_info = symbol->st_info; relocation.st_other = symbol->st_other; relocation.st_value = symbol->st_value; relocation.st_size = symbol->st_size; relocation.st_shndx = symbol->st_shndx; st_shndx = symbol->st_shndx; /* canonicalise the section used by the symbol */ if (st_shndx > SHN_UNDEF && st_shndx < mvdata->nsects) relocation.st_shndx = mvdata->canonmap[st_shndx]; crypto_digest_update_val(mvdata, relocation); /* undefined symbols must be named if referenced */ if (st_shndx == SHN_UNDEF) { const char *name = mvdata->strings + symbol->st_name; crypto_digest_update_data(mvdata, name, strlen(name) + 1); } } _debug("%08zx %02x digested the %s section, nrels %zu\n", mvdata->signed_size, mvdata->csum, sh_name, nrels); return 0; } /* end extract_elf_rel() */
int _rtld_relocate_nonplt_objects(Obj_Entry *obj) { for (const Elf_Rela *rela = obj->rela; rela < obj->relalim; rela++) { Elf_Addr *where; const Elf_Sym *def; const Obj_Entry *defobj; unsigned long symnum; Elf_Addr addend; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); symnum = ELF_R_SYM(rela->r_info); addend = rela->r_addend; switch (ELF_R_TYPE(rela->r_info)) { case R_TYPE(NONE): break; case R_TYPE(ABS64): /* word B + S + A */ case R_TYPE(GLOB_DAT): /* word B + S */ def = _rtld_find_symdef(symnum, obj, &defobj, false); if (def == NULL) return -1; *where = addend + (Elf_Addr)defobj->relocbase + def->st_value; rdbg(("ABS64/GLOB_DAT %s in %s --> %p @ %p in %s", obj->strtab + obj->symtab[symnum].st_name, obj->path, (void *)tmp, where, defobj->path)); break; case R_TYPE(RELATIVE): /* word B + A */ *where = addend + (Elf_Addr)obj->relocbase; rdbg(("RELATIVE in %s --> %p", obj->path, (void *)tmp)); break; case R_TYPE(COPY): /* * These are deferred until all other relocations have * been done. All we do here is make sure that the * COPY relocation is not in a shared library. They * are allowed only in executable files. */ if (obj->isdynamic) { _rtld_error( "%s: Unexpected R_COPY relocation in shared library", obj->path); return -1; } rdbg(("COPY (avoid in main)")); break; case R_TLS_TYPE(TLS_DTPREL): def = _rtld_find_symdef(symnum, obj, &defobj, false); if (def == NULL) return -1; *where = addend + (Elf_Addr)(def->st_value); rdbg(("TLS_DTPOFF32 %s in %s --> %p", obj->strtab + obj->symtab[symnum].st_name, obj->path, (void *)tmp)); break; case R_TLS_TYPE(TLS_DTPMOD): def = _rtld_find_symdef(symnum, obj, &defobj, false); if (def == NULL) return -1; *where = (Elf_Addr)(defobj->tlsindex); rdbg(("TLS_DTPMOD %s in %s --> %p", obj->strtab + obj->symtab[symnum].st_name, obj->path, (void *)tmp)); break; case R_TLS_TYPE(TLS_TPREL): def = _rtld_find_symdef(symnum, obj, &defobj, false); if (def == NULL) return -1; if (!defobj->tls_done && _rtld_tls_offset_allocate(obj)) return -1; *where = (Elf_Addr)def->st_value + defobj->tlsoffset + sizeof(struct tls_tcb); rdbg(("TLS_TPOFF32 %s in %s --> %p", obj->strtab + obj->symtab[symnum].st_name, obj->path, (void *)tmp)); break; default: rdbg(("sym = %lu, type = %lu, offset = %p, " "contents = %p, symbol = %s", symnum, (u_long)ELF_R_TYPE(rela->r_info), (void *)rela->r_offset, *where, obj->strtab + obj->symtab[symnum].st_name)); _rtld_error("%s: Unsupported relocation type %ld " "in non-PLT relocations", obj->path, (u_long) ELF_R_TYPE(rela->r_info)); return -1; } } return 0; }
/* Process the non-PLT relocations. */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; SymCache *cache; const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr *where, symval, add; int r; r = -1; /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = calloc(obj->dynsymcount, sizeof(SymCache)); /* No need to check for NULL here */ } else cache = NULL; rellim = (const Elf_Rel *)((caddr_t) obj->rel + obj->relsize); for (rel = obj->rel; rel < rellim; rel++) { switch (ELF_R_TYPE(rel->r_info)) { case R_386_32: case R_386_PC32: case R_386_GLOB_DAT: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) goto done; if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { switch (ELF_R_TYPE(rel->r_info)) { case R_386_32: case R_386_PC32: case R_386_GLOB_DAT: if ((flags & SYMLOOK_IFUNC) == 0) { obj->non_plt_gnu_ifunc = true; continue; } symval = (Elf_Addr)rtld_resolve_ifunc( defobj, def); break; case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: _rtld_error("%s: IFUNC for TLS reloc", obj->path); goto done; } } else { if ((flags & SYMLOOK_IFUNC) != 0) continue; symval = (Elf_Addr)defobj->relocbase + def->st_value; } break; default: if ((flags & SYMLOOK_IFUNC) != 0) continue; break; } where = (Elf_Addr *)(obj->relocbase + rel->r_offset); switch (ELF_R_TYPE(rel->r_info)) { case R_386_NONE: break; case R_386_32: *where += symval; break; case R_386_PC32: /* * I don't think the dynamic linker should ever * see this type of relocation. But the * binutils-2.6 tools sometimes generate it. */ *where += symval - (Elf_Addr)where; break; case R_386_COPY: /* * These are deferred until all other * relocations have been done. All we do here * is make sure that the COPY relocation is * not in a shared library. They are allowed * only in executable files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_386_COPY " "relocation in shared library", obj->path); goto done; } break; case R_386_GLOB_DAT: *where = symval; break; case R_386_RELATIVE: *where += (Elf_Addr)obj->relocbase; break; case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: /* * We lazily allocate offsets for static TLS * as we see the first relocation that * references the TLS block. This allows us to * support (small amounts of) static TLS in * dynamically loaded modules. If we run out * of space, we generate an error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available " "for static Thread Local Storage", obj->path); goto done; } } add = (Elf_Addr)(def->st_value - defobj->tlsoffset); if (ELF_R_TYPE(rel->r_info) == R_386_TLS_TPOFF) *where += add; else *where -= add; break; case R_386_TLS_DTPMOD32: *where += (Elf_Addr)defobj->tlsindex; break; case R_386_TLS_DTPOFF32: *where += (Elf_Addr) def->st_value; break; default: _rtld_error("%s: Unsupported relocation type %d" " in non-PLT relocations\n", obj->path, ELF_R_TYPE(rel->r_info)); goto done; } } r = 0; done: free(cache); return (r); }
/* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf64_Addr *where, val; Elf32_Addr *where32, val32; Elf_Addr addr; Elf_Addr addend; Elf_Size rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); /* Addend is 32 bit on 32 bit relocs */ switch (rtype) { case R_X86_64_PC32: case R_X86_64_32S: addend = *(Elf32_Addr *)where; break; default: addend = *where; break; } break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } switch (rtype) { case R_X86_64_NONE: /* none */ break; case R_X86_64_64: /* S + A */ addr = lookup(lf, symidx, 1); val = addr + addend; if (addr == 0) return -1; if (*where != val) *where = val; break; case R_X86_64_PC32: /* S + A - P */ addr = lookup(lf, symidx, 1); where32 = (Elf32_Addr *)where; val32 = (Elf32_Addr)(addr + addend - (Elf_Addr)where); if (addr == 0) return -1; if (*where32 != val32) *where32 = val32; break; case R_X86_64_32S: /* S + A sign extend */ addr = lookup(lf, symidx, 1); val32 = (Elf32_Addr)(addr + addend); where32 = (Elf32_Addr *)where; if (addr == 0) return -1; if (*where32 != val32) *where32 = val32; break; case R_X86_64_COPY: /* none */ /* * There shouldn't be copy relocations in kernel * objects. */ printf("kldload: unexpected R_COPY relocation\n"); return -1; break; case R_X86_64_GLOB_DAT: /* S */ case R_X86_64_JMP_SLOT: /* XXX need addend + offset */ addr = lookup(lf, symidx, 1); if (addr == 0) return -1; if (*where != addr) *where = addr; break; case R_X86_64_RELATIVE: /* B + A */ addr = relocbase + addend; val = addr; if (*where != val) *where = val; break; default: printf("kldload: unexpected relocation type %ld\n", rtype); return -1; } return(0); }
int _dl_md_reloc(elf_object_t *object, int rel, int relasz) { long i; long numrela; int fails = 0; Elf64_Addr loff; Elf64_Rela *relas; struct load_list *llist; loff = object->obj_base; numrela = object->Dyn.info[relasz] / sizeof(Elf64_Rela); relas = (Elf64_Rela *)(object->Dyn.info[rel]); if (relas == NULL) return(0); /* * unprotect some segments if we need it. * XXX - we unprotect way to much. only the text can have cow * relocations. */ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { for (llist = object->load_list; llist != NULL; llist = llist->next) { if (!(llist->prot & PROT_WRITE)) { _dl_mprotect(llist->start, llist->size, llist->prot|PROT_WRITE); } } } for (i = 0; i < numrela; i++, relas++) { Elf64_Addr *r_addr; Elf64_Addr ooff; const Elf64_Sym *sym, *this; const char *symn; r_addr = (Elf64_Addr *)(relas->r_offset + loff); if (ELF64_R_SYM(relas->r_info) == 0xffffffff) continue; sym = object->dyn.symtab; sym += ELF64_R_SYM(relas->r_info); symn = object->dyn.strtab + sym->st_name; this = NULL; switch (ELF64_R_TYPE(relas->r_info)) { case R_TYPE(REFQUAD): ooff = _dl_find_symbol_bysym(object, ELF64_R_SYM(relas->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, sym, NULL); if (this == NULL) goto resolve_failed; *r_addr += ooff + this->st_value + relas->r_addend; break; case R_TYPE(RELATIVE): /* * There is a lot of unaligned RELATIVE * relocs generated by gcc in the exception handlers. */ if ((((Elf_Addr) r_addr) & 0x7) != 0) { Elf_Addr tmp; #if 0 _dl_printf("unaligned RELATIVE: %p type: %d %s 0x%lx -> 0x%lx\n", r_addr, ELF_R_TYPE(relas->r_info), object->load_name, *r_addr, *r_addr+loff); #endif _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr)); tmp += loff; _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr)); } else *r_addr += loff; break; case R_TYPE(JMP_SLOT): ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL); if (this == NULL) goto resolve_failed; *r_addr = ooff + this->st_value + relas->r_addend; break; case R_TYPE(GLOB_DAT): ooff = _dl_find_symbol_bysym(object, ELF64_R_SYM(relas->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT, sym, NULL); if (this == NULL) goto resolve_failed; *r_addr = ooff + this->st_value + relas->r_addend; break; case R_TYPE(NONE): break; default: _dl_printf("%s:" " %s: unsupported relocation '%s' %d at %lx\n", _dl_progname, object->load_name, symn, ELF64_R_TYPE(relas->r_info), r_addr ); _dl_exit(1); } continue; resolve_failed: if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; } __asm __volatile("imb" : : : "memory"); /* reprotect the unprotected segments */ if ((object->dyn.textrel == 1) && (rel == DT_REL || rel == DT_RELA)) { for (llist = object->load_list; llist != NULL; llist = llist->next) { if (!(llist->prot & PROT_WRITE)) _dl_mprotect(llist->start, llist->size, llist->prot); } } return (fails); }
void _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) { const Elf_Rel *rel = 0, *rellim; Elf_Addr relsz = 0; const Elf_Sym *symtab = NULL, *sym; Elf_Addr *where; Elf_Addr *got = NULL; Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0; size_t i; for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_REL: rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); break; case DT_RELSZ: relsz = dynp->d_un.d_val; break; case DT_SYMTAB: symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr); break; case DT_PLTGOT: got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr); break; case DT_MIPS_LOCAL_GOTNO: local_gotno = dynp->d_un.d_val; break; case DT_MIPS_SYMTABNO: symtabno = dynp->d_un.d_val; break; case DT_MIPS_GOTSYM: gotsym = dynp->d_un.d_val; break; } } i = (got[1] & GOT1_MASK) ? 2 : 1; /* Relocate the local GOT entries */ got += i; for (; i < local_gotno; i++) { *got++ += relocbase; } sym = symtab + gotsym; /* Now do the global GOT entries */ for (i = gotsym; i < symtabno; i++) { *got = sym->st_value + relocbase; ++sym; ++got; } rellim = (const Elf_Rel *)((caddr_t)rel + relsz); for (; rel < rellim; rel++) { Elf_Word r_symndx, r_type; where = (void *)(relocbase + rel->r_offset); r_symndx = ELF_R_SYM(rel->r_info); r_type = ELF_R_TYPE(rel->r_info); switch (r_type & 0xff) { case R_TYPE(REL32): { const size_t rlen = ELF_R_NXTTYPE_64_P(r_type) ? sizeof(Elf_Sxword) : sizeof(Elf_Sword); Elf_Sxword old = load_ptr(where, rlen); Elf_Sxword val = old; #ifdef __mips_n64 assert(r_type == R_TYPE(REL32) || r_type == (R_TYPE(REL32)|(R_TYPE(64) << 8))); #endif assert(r_symndx < gotsym); sym = symtab + r_symndx; assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL); val += relocbase; store_ptr(where, val, sizeof(Elf_Sword)); dbg("REL32/L(%p) %p -> %p in <self>", where, (void *)old, (void *)val); store_ptr(where, val, rlen); break; } case R_TYPE(GPREL32): case R_TYPE(NONE): break; default: abort(); break; } } }
int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rel *rel; const Elf_Rel *rellim; Elf_Addr *got = obj->pltgot; const Elf_Sym *sym, *def; const Obj_Entry *defobj; Elf_Word i; #ifdef SUPPORT_OLD_BROKEN_LD int broken; #endif /* The relocation for the dynamic loader has already been done. */ if (obj == obj_rtld) return (0); if ((flags & SYMLOOK_IFUNC) != 0) /* XXX not implemented */ return (0); #ifdef SUPPORT_OLD_BROKEN_LD broken = 0; sym = obj->symtab; for (i = 1; i < 12; i++) if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE)) broken = 1; dbg("%s: broken=%d", obj->path, broken); #endif i = (got[1] & GOT1_MASK) ? 2 : 1; /* Relocate the local GOT entries */ got += i; dbg("got:%p for %d entries adding %p", got, obj->local_gotno, obj->relocbase); for (; i < obj->local_gotno; i++) { *got += (Elf_Addr)obj->relocbase; got++; } sym = obj->symtab + obj->gotsym; dbg("got:%p for %d entries", got, obj->symtabno); /* Now do the global GOT entries */ for (i = obj->gotsym; i < obj->symtabno; i++) { dbg(" doing got %d sym %p (%s, %lx)", i - obj->gotsym, sym, sym->st_name + obj->strtab, (u_long) *got); #ifdef SUPPORT_OLD_BROKEN_LD if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && broken && sym->st_shndx == SHN_UNDEF) { /* * XXX DANGER WILL ROBINSON! * You might think this is stupid, as it intentionally * defeats lazy binding -- and you'd be right. * Unfortunately, for lazy binding to work right, we * need to a way to force the GOT slots used for * function pointers to be resolved immediately. This * is supposed to be done automatically by the linker, * by not outputting a PLT slot and setting st_value * to 0 if there are non-PLT references, but older * versions of GNU ld do not do this. */ def = find_symdef(i, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; *got = def->st_value + (Elf_Addr)defobj->relocbase; } else #endif if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) { /* * If there are non-PLT references to the function, * st_value should be 0, forcing us to resolve the * address immediately. * * XXX DANGER WILL ROBINSON! * The linker is not outputting PLT slots for calls to * functions that are defined in the same shared * library. This is a bug, because it can screw up * link ordering rules if the symbol is defined in * more than one module. For now, if there is a * definition, we fail the test above and force a full * symbol lookup. This means that all intra-module * calls are bound immediately. - mycroft, 2003/09/24 */ *got = sym->st_value + (Elf_Addr)obj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning2, i:%d maps to relocbase address:%p", i, obj->relocbase); } } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) { /* Symbols with index SHN_ABS are not relocated. */ if (sym->st_shndx != SHN_ABS) { *got = sym->st_value + (Elf_Addr)obj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning3, i:%d maps to relocbase address:%p", i, obj->relocbase); } } } else { /* TODO: add cache here */ def = find_symdef(i, obj, &defobj, flags, NULL, lockstate); if (def == NULL) { dbg("Warning4, can't find symbole %d", i); return -1; } *got = def->st_value + (Elf_Addr)defobj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning4, i:%d maps to relocbase address:%p", i, obj->relocbase); dbg("via first obj symbol %s", obj->strtab + obj->symtab[i].st_name); dbg("found in obj %p:%s", defobj, defobj->path); } } dbg(" --> now %lx", (u_long) *got); ++sym; ++got; } got = obj->pltgot; rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize); for (rel = obj->rel; rel < rellim; rel++) { Elf_Word r_symndx, r_type; void *where; where = obj->relocbase + rel->r_offset; r_symndx = ELF_R_SYM(rel->r_info); r_type = ELF_R_TYPE(rel->r_info); switch (r_type & 0xff) { case R_TYPE(NONE): break; case R_TYPE(REL32): { /* 32-bit PC-relative reference */ const size_t rlen = ELF_R_NXTTYPE_64_P(r_type) ? sizeof(Elf_Sxword) : sizeof(Elf_Sword); Elf_Sxword old = load_ptr(where, rlen); Elf_Sxword val = old; def = obj->symtab + r_symndx; if (r_symndx >= obj->gotsym) { val += got[obj->local_gotno + r_symndx - obj->gotsym]; dbg("REL32/G(%p) %p --> %p (%s) in %s", where, (void *)old, (void *)val, obj->strtab + def->st_name, obj->path); } else { /* * XXX: ABI DIFFERENCE! * * Old NetBSD binutils would generate shared * libs with section-relative relocations being * already adjusted for the start address of * the section. * * New binutils, OTOH, generate shared libs * with the same relocations being based at * zero, so we need to add in the start address * of the section. * * --rkb, Oct 6, 2001 */ if (def->st_info == ELF_ST_INFO(STB_LOCAL, STT_SECTION) #ifdef SUPPORT_OLD_BROKEN_LD && !broken #endif ) val += (Elf_Addr)def->st_value; val += (Elf_Addr)obj->relocbase; dbg("REL32/L(%p) %p -> %p (%s) in %s", where, (void *)old, (void *)val, obj->strtab + def->st_name, obj->path); } store_ptr(where, val, rlen); break; } #ifdef __mips_n64 case R_TYPE(TLS_DTPMOD64): #else case R_TYPE(TLS_DTPMOD32): #endif { const size_t rlen = sizeof(Elf_Addr); Elf_Addr old = load_ptr(where, rlen); Elf_Addr val = old; def = find_symdef(r_symndx, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; val += (Elf_Addr)defobj->tlsindex; store_ptr(where, val, rlen); dbg("DTPMOD %s in %s %p --> %p in %s", obj->strtab + obj->symtab[r_symndx].st_name, obj->path, (void *)old, (void*)val, defobj->path); break; } #ifdef __mips_n64 case R_TYPE(TLS_DTPREL64): #else case R_TYPE(TLS_DTPREL32): #endif { const size_t rlen = sizeof(Elf_Addr); Elf_Addr old = load_ptr(where, rlen); Elf_Addr val = old; def = find_symdef(r_symndx, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; if (!defobj->tls_done && allocate_tls_offset(obj)) return -1; val += (Elf_Addr)def->st_value - TLS_DTP_OFFSET; store_ptr(where, val, rlen); dbg("DTPREL %s in %s %p --> %p in %s", obj->strtab + obj->symtab[r_symndx].st_name, obj->path, (void*)old, (void *)val, defobj->path); break; } #ifdef __mips_n64 case R_TYPE(TLS_TPREL64): #else case R_TYPE(TLS_TPREL32): #endif { const size_t rlen = sizeof(Elf_Addr); Elf_Addr old = load_ptr(where, rlen); Elf_Addr val = old; def = find_symdef(r_symndx, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; if (!defobj->tls_done && allocate_tls_offset(obj)) return -1; val += (Elf_Addr)(def->st_value + defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE); store_ptr(where, val, rlen); dbg("TPREL %s in %s %p --> %p in %s", obj->strtab + obj->symtab[r_symndx].st_name, obj->path, (void*)old, (void *)val, defobj->path); break; } default: dbg("sym = %lu, type = %lu, offset = %p, " "contents = %p, symbol = %s", (u_long)r_symndx, (u_long)ELF_R_TYPE(rel->r_info), (void *)rel->r_offset, (void *)load_ptr(where, sizeof(Elf_Sword)), obj->strtab + obj->symtab[r_symndx].st_name); _rtld_error("%s: Unsupported relocation type %ld " "in non-PLT relocations", obj->path, (u_long) ELF_R_TYPE(rel->r_info)); return -1; } } return 0; }
/* Relocate a non-PLT object with addend. */ static int reloc_non_plt_obj(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate) { struct fptr **fptrs; Elf_Addr *where = (Elf_Addr *) (obj->relocbase + rela->r_offset); switch (ELF_R_TYPE(rela->r_info)) { case R_IA_64_REL64LSB: /* * We handle rtld's relocations in rtld_start.S */ if (obj != obj_rtld) store64(where, load64(where) + (Elf_Addr) obj->relocbase); break; case R_IA_64_DIR64LSB: { const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr target; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return -1; target = (def->st_shndx != SHN_UNDEF) ? (Elf_Addr)(defobj->relocbase + def->st_value) : 0; store64(where, target + rela->r_addend); break; } case R_IA_64_FPTR64LSB: { /* * We have to make sure that all @fptr references to * the same function are identical so that code can * compare function pointers. */ const Elf_Sym *def; const Obj_Entry *defobj; struct fptr *fptr = 0; Elf_Addr target, gp; int sym_index; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, cache, lockstate); if (def == NULL) { /* * XXX r_debug_state is problematic and find_symdef() * returns NULL for it. This probably has something to * do with symbol versioning (r_debug_state is in the * symbol map). If we return -1 in that case we abort * relocating rtld, which typically is fatal. So, for * now just skip the symbol when we're relocating * rtld. We don't care about r_debug_state unless we * are being debugged. */ if (obj != obj_rtld) return -1; break; } if (def->st_shndx != SHN_UNDEF) { target = (Elf_Addr)(defobj->relocbase + def->st_value); gp = (Elf_Addr)defobj->pltgot; /* rtld is allowed to reference itself only */ assert(!obj->rtld || obj == defobj); fptrs = defobj->priv; if (fptrs == NULL) fptrs = alloc_fptrs((Obj_Entry *) defobj, obj->rtld); sym_index = def - defobj->symtab; /* * Find the @fptr, using fptrs as a helper. */ if (fptrs) fptr = fptrs[sym_index]; if (!fptr) { fptr = alloc_fptr(target, gp); if (fptrs) fptrs[sym_index] = fptr; } } else fptr = NULL; store64(where, (Elf_Addr)fptr); break; } case R_IA_64_IPLTLSB: { /* * Relocation typically used to populate C++ virtual function * tables. It creates a 128-bit function descriptor at the * specified memory address. */ const Elf_Sym *def; const Obj_Entry *defobj; struct fptr *fptr; Elf_Addr target, gp; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return -1; if (def->st_shndx != SHN_UNDEF) { target = (Elf_Addr)(defobj->relocbase + def->st_value); gp = (Elf_Addr)defobj->pltgot; } else { target = 0; gp = 0; } fptr = (void*)where; store64(&fptr->target, target); store64(&fptr->gp, gp); break; } case R_IA_64_DTPMOD64LSB: { const Elf_Sym *def; const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return -1; store64(where, defobj->tlsindex); break; } case R_IA_64_DTPREL64LSB: { const Elf_Sym *def; const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return -1; store64(where, def->st_value + rela->r_addend); break; } case R_IA_64_TPREL64LSB: { const Elf_Sym *def; const Obj_Entry *defobj; def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return -1; /* * We lazily allocate offsets for static TLS as we * see the first relocation that references the * TLS block. This allows us to support (small * amounts of) static TLS in dynamically loaded * modules. If we run out of space, we generate an * error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available for static " "Thread Local Storage", obj->path); return -1; } } store64(where, defobj->tlsoffset + def->st_value + rela->r_addend); break; } case R_IA_64_NONE: break; default: _rtld_error("%s: Unsupported relocation type %u" " in non-PLT relocations\n", obj->path, (unsigned int)ELF_R_TYPE(rela->r_info)); return -1; } return(0); }
/* * Resolve a symbol in an ELF object. There are two cases here. * * If 'resolver' is NULL, only symbols resolvable internally are * resolved, and exceptions are stored, not posted. Otherwise, * only symbols resolvable externally are resolved, 'resolver' is * used to resolve them, and exceptions are posted. */ static inline jint _jc_elf_resolve_sym(_jc_env *env, _jc_elf *elf, _jc_elf_loadable *loadable, const Elf_Rela *rela, _jc_elf_resolver *resolver, void *arg) { const Elf_Word type = ELF_R_TYPE(rela->r_info); _jc_elf_info *const info = elf->info; const Elf_Sym *const sym = &info->symbols[ELF_R_SYM(rela->r_info)]; const char *name = info->strings + sym->st_name; switch (sym->st_shndx) { case SHN_ABS: if (resolver != NULL) break; if (_jc_elf_arch_reloc(env, elf->pathname, loadable->vaddr, rela->r_offset, type, sym->st_value, rela->r_addend) != JNI_OK) return JNI_ERR; break; case SHN_UNDEF: { Elf_Addr value; if (resolver == NULL) break; if ((*resolver)(env, arg, info->strings + sym->st_name, &value) != JNI_OK) return JNI_ERR; if (_jc_elf_arch_reloc(env, elf->pathname, loadable->vaddr, rela->r_offset, type, value, rela->r_addend) != JNI_OK) { _jc_post_exception_info(env); return JNI_ERR; } break; } case SHN_COMMON: _JC_ASSERT(resolver == NULL); _JC_EX_STORE(env, LinkageError, "%s: ELF symbol `%s' is common (not supported)", elf->pathname, name); return JNI_ERR; default: { const _jc_elf_loadable *sym_section; /* Skip if only resolving externals */ if (resolver != NULL) break; /* Sanity check the symbol's section */ if (sym->st_shndx >= info->ehdr->e_shnum || (sym_section = info->shdr2section[sym->st_shndx]) == NULL) { _JC_EX_STORE(env, LinkageError, "%s: invalid section index %d for symbol `%s'", elf->pathname, sym->st_shndx, name); return JNI_ERR; } /* Apply relocation */ if (_jc_elf_arch_reloc(env, elf->pathname, loadable->vaddr, rela->r_offset, type, (Elf_Addr)(sym_section->vaddr + sym->st_value), rela->r_addend) != JNI_OK) return JNI_ERR; break; } } /* Done */ return JNI_OK; }
int kobj_reloc(kobj_t ko, uintptr_t relocbase, const void *data, bool isrela, bool local) { Elf64_Addr *where, val; Elf32_Addr *where32, val32; Elf64_Addr addr; Elf64_Addr addend; uintptr_t rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; if (isrela) { rela = (const Elf_Rela *)data; where = (Elf64_Addr *)(relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); } else { rel = (const Elf_Rel *)data; where = (Elf64_Addr *)(relocbase + rel->r_offset); rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); /* Addend is 32 bit on 32 bit relocs */ switch (rtype) { case R_X86_64_PC32: case R_X86_64_32: case R_X86_64_32S: addend = *(Elf32_Addr *)where; break; default: addend = *where; break; } } switch (rtype) { case R_X86_64_NONE: /* none */ break; case R_X86_64_64: /* S + A */ addr = kobj_sym_lookup(ko, symidx); if (addr == 0) return -1; val = addr + addend; *where = val; break; case R_X86_64_PC32: /* S + A - P */ addr = kobj_sym_lookup(ko, symidx); if (addr == 0) return -1; where32 = (Elf32_Addr *)where; val32 = (Elf32_Addr)(addr + addend - (Elf64_Addr)where); *where32 = val32; break; case R_X86_64_32: /* S + A */ case R_X86_64_32S: /* S + A sign extend */ addr = kobj_sym_lookup(ko, symidx); if (addr == 0) return -1; val32 = (Elf32_Addr)(addr + addend); where32 = (Elf32_Addr *)where; *where32 = val32; break; case R_X86_64_GLOB_DAT: /* S */ case R_X86_64_JUMP_SLOT:/* XXX need addend + offset */ addr = kobj_sym_lookup(ko, symidx); if (addr == 0) return -1; *where = addr; break; case R_X86_64_RELATIVE: /* B + A */ addr = relocbase + addend; val = addr; *where = val; break; default: printf("kobj_reloc: unexpected relocation type %ld\n", rtype); return -1; } return 0; }
/* Process one elf relocation with addend. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int local, elf_lookup_fn lookup) { Elf_Addr *where; Elf_Addr addr; Elf_Addr addend; Elf_Word rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); addend = load_ptr(where); rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } if (local) { if (rtype == R_ARM_RELATIVE) { /* A + B */ addr = elf_relocaddr(lf, relocbase + addend); if (load_ptr(where) != addr) store_ptr(where, addr); } return (0); } switch (rtype) { case R_ARM_NONE: /* none */ break; case R_ARM_ABS32: error = lookup(lf, symidx, 1, &addr); if (error != 0) return -1; store_ptr(where, addr + load_ptr(where)); break; case R_ARM_COPY: /* none */ /* * There shouldn't be copy relocations in kernel * objects. */ printf("kldload: unexpected R_COPY relocation\n"); return -1; break; case R_ARM_JUMP_SLOT: error = lookup(lf, symidx, 1, &addr); if (error == 0) { store_ptr(where, addr); return (0); } return (-1); case R_ARM_RELATIVE: break; default: printf("kldload: unexpected relocation type %d\n", rtype); return -1; } return(0); }
STATIC BOOLEAN WriteSections32 ( SECTION_FILTER_TYPES FilterType ) { UINT32 Idx; Elf_Shdr *SecShdr; UINT32 SecOffset; BOOLEAN (*Filter)(Elf_Shdr *); // // Initialize filter pointer // switch (FilterType) { case SECTION_TEXT: Filter = IsTextShdr; break; case SECTION_HII: Filter = IsHiiRsrcShdr; break; case SECTION_DATA: Filter = IsDataShdr; break; default: return FALSE; } // // First: copy sections. // for (Idx = 0; Idx < mEhdr->e_shnum; Idx++) { Elf_Shdr *Shdr = GetShdrByIndex(Idx); if ((*Filter)(Shdr)) { switch (Shdr->sh_type) { case SHT_PROGBITS: /* Copy. */ memcpy(mCoffFile + mCoffSectionsOffset[Idx], (UINT8*)mEhdr + Shdr->sh_offset, Shdr->sh_size); break; case SHT_NOBITS: memset(mCoffFile + mCoffSectionsOffset[Idx], 0, Shdr->sh_size); break; default: // // Ignore for unkown section type. // VerboseMsg ("%s unknown section type %x. We directly copy this section into Coff file", mInImageName, (unsigned)Shdr->sh_type); break; } } } // // Second: apply relocations. // for (Idx = 0; Idx < mEhdr->e_shnum; Idx++) { // // Determine if this is a relocation section. // Elf_Shdr *RelShdr = GetShdrByIndex(Idx); if ((RelShdr->sh_type != SHT_REL) && (RelShdr->sh_type != SHT_RELA)) { continue; } // // Relocation section found. Now extract section information that the relocations // apply to in the ELF data and the new COFF data. // SecShdr = GetShdrByIndex(RelShdr->sh_info); SecOffset = mCoffSectionsOffset[RelShdr->sh_info]; // // Only process relocations for the current filter type. // if (RelShdr->sh_type == SHT_REL && (*Filter)(SecShdr)) { UINT32 RelOffset; // // Determine the symbol table referenced by the relocation data. // Elf_Shdr *SymtabShdr = GetShdrByIndex(RelShdr->sh_link); UINT8 *Symtab = (UINT8*)mEhdr + SymtabShdr->sh_offset; // // Process all relocation entries for this section. // for (RelOffset = 0; RelOffset < RelShdr->sh_size; RelOffset += RelShdr->sh_entsize) { // // Set pointer to relocation entry // Elf_Rel *Rel = (Elf_Rel *)((UINT8*)mEhdr + RelShdr->sh_offset + RelOffset); // // Set pointer to symbol table entry associated with the relocation entry. // Elf_Sym *Sym = (Elf_Sym *)(Symtab + ELF_R_SYM(Rel->r_info) * SymtabShdr->sh_entsize); Elf_Shdr *SymShdr; UINT8 *Targ; UINT16 Address; // // Check section header index found in symbol table and get the section // header location. // if (Sym->st_shndx == SHN_UNDEF || Sym->st_shndx == SHN_ABS || Sym->st_shndx > mEhdr->e_shnum) { Error (NULL, 0, 3000, "Invalid", "%s bad symbol definition.", mInImageName); } SymShdr = GetShdrByIndex(Sym->st_shndx); // // Convert the relocation data to a pointer into the coff file. // // Note: // r_offset is the virtual address of the storage unit to be relocated. // sh_addr is the virtual address for the base of the section. // Targ = mCoffFile + SecOffset + (Rel->r_offset - SecShdr->sh_addr); // // Determine how to handle each relocation type based on the machine type. // if (mEhdr->e_machine == EM_386) { switch (ELF_R_TYPE(Rel->r_info)) { case R_386_NONE: break; case R_386_32: // // Absolute relocation. // Converts Targ from a absolute virtual address to the absolute // COFF address. // *(UINT32 *)Targ = *(UINT32 *)Targ - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]; break; case R_386_PC32: // // Relative relocation: Symbol - Ip + Addend // *(UINT32 *)Targ = *(UINT32 *)Targ + (mCoffSectionsOffset[Sym->st_shndx] - SymShdr->sh_addr) - (SecOffset - SecShdr->sh_addr); break; default: Error (NULL, 0, 3000, "Invalid", "%s unsupported ELF EM_386 relocation 0x%x.", mInImageName, (unsigned) ELF_R_TYPE(Rel->r_info)); } } else if (mEhdr->e_machine == EM_ARM) { switch (ELF32_R_TYPE(Rel->r_info)) { case R_ARM_RBASE: // No relocation - no action required // break skipped case R_ARM_PC24: case R_ARM_XPC25: case R_ARM_THM_PC22: case R_ARM_THM_JUMP19: case R_ARM_CALL: case R_ARM_JMP24: case R_ARM_THM_JUMP24: case R_ARM_PREL31: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: case R_ARM_THM_JMP6: case R_ARM_THM_ALU_PREL_11_0: case R_ARM_THM_PC12: case R_ARM_REL32_NOI: case R_ARM_ALU_PC_G0_NC: case R_ARM_ALU_PC_G0: case R_ARM_ALU_PC_G1_NC: case R_ARM_ALU_PC_G1: case R_ARM_ALU_PC_G2: case R_ARM_LDR_PC_G1: case R_ARM_LDR_PC_G2: case R_ARM_LDRS_PC_G0: case R_ARM_LDRS_PC_G1: case R_ARM_LDRS_PC_G2: case R_ARM_LDC_PC_G0: case R_ARM_LDC_PC_G1: case R_ARM_LDC_PC_G2: case R_ARM_GOT_PREL: case R_ARM_THM_JUMP11: case R_ARM_THM_JUMP8: case R_ARM_TLS_GD32: case R_ARM_TLS_LDM32: case R_ARM_TLS_IE32: // Thease are all PC-relative relocations and don't require modification // GCC does not seem to have the concept of a application that just needs to get relocated. break; case R_ARM_THM_MOVW_ABS_NC: // MOVW is only lower 16-bits of the addres Address = (UINT16)(Sym->st_value - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]); ThumbMovtImmediatePatch ((UINT16 *)Targ, Address); break; case R_ARM_THM_MOVT_ABS: // MOVT is only upper 16-bits of the addres Address = (UINT16)((Sym->st_value - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]) >> 16); ThumbMovtImmediatePatch ((UINT16 *)Targ, Address); break; case R_ARM_ABS32: case R_ARM_RABS32: // // Absolute relocation. // *(UINT32 *)Targ = *(UINT32 *)Targ - SymShdr->sh_addr + mCoffSectionsOffset[Sym->st_shndx]; break; default: Error (NULL, 0, 3000, "Invalid", "WriteSections (): %s unsupported ELF EM_ARM relocation 0x%x.", mInImageName, (unsigned) ELF32_R_TYPE(Rel->r_info)); } } } } }