/* Process one elf relocation with addend. */ int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { const Elf_Rela *rela; Elf_Word *where32; Elf_Addr *where; Elf_Size rtype, symidx; Elf_Addr value; Elf_Addr mask; Elf_Addr addr; if (type != ELF_RELOC_RELA) return (-1); rela = (const Elf_Rela *)data; where = (Elf_Addr *)(relocbase + rela->r_offset); where32 = (Elf_Word *)where; rtype = ELF64_R_TYPE_ID(rela->r_info); symidx = ELF_R_SYM(rela->r_info); if (rtype == R_SPARC_NONE || rtype == R_SPARC_RELATIVE) return (0); if (rtype == R_SPARC_JMP_SLOT || rtype == R_SPARC_COPY || rtype >= sizeof(reloc_target_bitmask) / sizeof(*reloc_target_bitmask)) return (-1); if (RELOC_UNALIGNED(rtype)) return (-1); value = rela->r_addend; if (RELOC_RESOLVE_SYMBOL(rtype)) { addr = lookup(lf, symidx, 1); if (addr == 0) return (-1); value += addr; } if (rtype == R_SPARC_OLO10) value = (value & 0x3ff) + ELF64_R_TYPE_DATA(rela->r_info); if (RELOC_PC_RELATIVE(rtype)) value -= (Elf_Addr)where; if (RELOC_BASE_RELATIVE(rtype)) { value = elf_relocaddr(lf, value + relocbase); } mask = RELOC_VALUE_BITMASK(rtype); value >>= RELOC_VALUE_RIGHTSHIFT(rtype); value &= mask; if (RELOC_TARGET_SIZE(rtype) > 32) { *where &= ~mask; *where |= value; } else { *where32 &= ~mask; *where32 |= value; } return (0); }
/* Process one elf relocation with addend. */ int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { const Elf_Rela *rela; Elf_Word *where32; Elf_Addr *where; Elf_Size rtype, symidx; Elf_Addr value; Elf_Addr mask; Elf_Addr addr; int error; if (type != ELF_RELOC_RELA) return (-1); rela = (const Elf_Rela *)data; where = (Elf_Addr *)(relocbase + rela->r_offset); where32 = (Elf_Word *)where; rtype = ELF64_R_TYPE_ID(rela->r_info); symidx = ELF_R_SYM(rela->r_info); if (rtype == R_SPARC_NONE || rtype == R_SPARC_RELATIVE) return (0); if (rtype == R_SPARC_JMP_SLOT || rtype == R_SPARC_COPY || rtype >= nitems(reloc_target_bitmask)) { printf("kldload: unexpected relocation type %ld\n", rtype); return (-1); } if (RELOC_UNALIGNED(rtype)) { printf("kldload: unaligned relocation type %ld\n", rtype); return (-1); } value = rela->r_addend; if (RELOC_RESOLVE_SYMBOL(rtype)) { error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); value += addr; if (RELOC_BARE_SYMBOL(rtype)) value = elf_relocaddr(lf, value); } if (rtype == R_SPARC_OLO10) value = (value & 0x3ff) + ELF64_R_TYPE_DATA(rela->r_info); if (rtype == R_SPARC_HIX22) value ^= 0xffffffffffffffff; if (RELOC_PC_RELATIVE(rtype)) value -= (Elf_Addr)where; if (RELOC_BASE_RELATIVE(rtype)) value = elf_relocaddr(lf, value + relocbase); mask = RELOC_VALUE_BITMASK(rtype); value >>= RELOC_VALUE_RIGHTSHIFT(rtype); value &= mask; if (rtype == R_SPARC_LOX10) value |= 0x1c00; if (RELOC_TARGET_SIZE(rtype) > 32) { *where &= ~mask; *where |= value; } else { *where32 &= ~mask; *where32 |= value; } return (0); }
int _dl_md_reloc(elf_object_t *object, int rel, int relsz) { long i; long numrel; long relrel; int fails = 0; Elf_Addr loff; Elf_Addr prev_value = 0; const Elf_Sym *prev_sym = NULL; Elf_RelA *rels; struct load_list *llist; loff = object->obj_base; numrel = object->Dyn.info[relsz] / sizeof(Elf_RelA); relrel = rel == DT_RELA ? object->relacount : 0; rels = (Elf_RelA *)(object->Dyn.info[rel]); if (rels == NULL) return(0); if (relrel > numrel) { _dl_printf("relacount > numrel: %ld > %ld\n", relrel, numrel); _dl_exit(20); } /* * unprotect some segments if we need it. */ 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, PROT_READ | PROT_WRITE); } } /* tight loop for leading RELATIVE relocs */ for (i = 0; i < relrel; i++, rels++) { Elf_Addr *where; #ifdef DEBUG if (ELF_R_TYPE(rels->r_info) != R_TYPE(RELATIVE)) { _dl_printf("RELACOUNT wrong\n"); _dl_exit(20); } #endif where = (Elf_Addr *)(rels->r_offset + loff); *where = rels->r_addend + loff; } for (; i < numrel; i++, rels++) { Elf_Addr *where, value, ooff, mask; Elf_Word type; const Elf_Sym *sym, *this; const char *symn; type = ELF_R_TYPE(rels->r_info); if (RELOC_ERROR(type)) { _dl_printf("relocation error %d idx %d\n", type, i); _dl_exit(20); } if (type == R_TYPE(NONE)) continue; if (type == R_TYPE(JUMP_SLOT) && rel != DT_JMPREL) continue; where = (Elf_Addr *)(rels->r_offset + loff); if (RELOC_USE_ADDEND(type)) value = rels->r_addend; else value = 0; sym = NULL; symn = NULL; if (RELOC_RESOLVE_SYMBOL(type)) { sym = object->dyn.symtab; sym += ELF_R_SYM(rels->r_info); symn = object->dyn.strtab + sym->st_name; if (sym->st_shndx != SHN_UNDEF && ELF_ST_BIND(sym->st_info) == STB_LOCAL) { value += loff; } else if (sym == prev_sym) { value += prev_value; } else { this = NULL; ooff = _dl_find_symbol_bysym(object, ELF_R_SYM(rels->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND| ((type == R_TYPE(JUMP_SLOT))? SYM_PLT:SYM_NOTPLT), sym, NULL); if (this == NULL) { resolve_failed: if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; continue; } prev_sym = sym; prev_value = (Elf_Addr)(ooff + this->st_value); value += prev_value; } } if (type == R_TYPE(JUMP_SLOT)) { _dl_reloc_plt(where, value); continue; } if (type == R_TYPE(COPY)) { void *dstaddr = where; const void *srcaddr; const Elf_Sym *dstsym = sym, *srcsym = NULL; Elf_Addr soff; soff = _dl_find_symbol(symn, &srcsym, SYM_SEARCH_OTHER|SYM_WARNNOTFOUND|SYM_NOTPLT, dstsym, object, NULL); if (srcsym == NULL) goto resolve_failed; srcaddr = (void *)(soff + srcsym->st_value); _dl_bcopy(srcaddr, dstaddr, dstsym->st_size); continue; } if (RELOC_PC_RELATIVE(type)) value -= (Elf_Addr)where; if (RELOC_BASE_RELATIVE(type)) value += loff; mask = RELOC_VALUE_BITMASK(type); value >>= RELOC_VALUE_RIGHTSHIFT(type); value &= mask; if (RELOC_TARGET_SIZE(type) > 32) { *where &= ~mask; *where |= value; } else { Elf32_Addr *where32 = (Elf32_Addr *)where; *where32 &= ~mask; *where32 |= value; } } /* 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); }