/* * Resolve a symbol at run-time. */ Elf_Addr _dl_bind(elf_object_t *object, int reloff) { Elf_RelA *rela; Elf_Addr *addr, ooff; const Elf_Sym *sym, *this; const char *symn; sigset_t savedmask; rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); addr = (Elf_Addr *)(object->obj_base + rela->r_offset); if (object->plt_size != 0 && !(*addr >= object->plt_start && *addr < (object->plt_start + object->plt_size ))) { /* something is broken, relocation has already occurred */ #if 0 DL_DEB(("*addr doesn't point into plt %p obj %s\n", *addr, object->load_name)); #endif return *addr; } sym = object->dyn.symtab; sym += ELF64_R_SYM(rela->r_info); symn = object->dyn.strtab + sym->st_name; this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL); if (this == NULL) { _dl_printf("lazy binding failed!\n"); *((int *)0) = 0; /* XXX */ } /* if PLT is protected, allow the write */ if (object->plt_size != 0) { _dl_thread_bind_lock(0, &savedmask); _dl_mprotect(addr, sizeof(Elf_Addr), PROT_READ|PROT_WRITE); } *addr = ooff + this->st_value + rela->r_addend; /* if PLT is (to be protected, change back to RO/X */ if (object->plt_size != 0) { _dl_mprotect(addr, sizeof(Elf_Addr), PROT_READ); _dl_thread_bind_lock(1, &savedmask); } return *addr; }
/* * Resolve a symbol at run-time. */ uint64_t _dl_bind(elf_object_t *object, int reloff) { const elf_object_t *sobj; const Elf_Sym *sym, *this; Elf_Addr *addr, ooff; const char *symn; Elf_Addr value; Elf_RelA *rela; sigset_t savedmask; rela = (Elf_RelA *)object->dyn.jmprel + reloff; sym = object->dyn.symtab; sym += ELF_R_SYM(rela->r_info); symn = object->dyn.strtab + sym->st_name; addr = (Elf_Addr *)(object->obj_base + rela->r_offset); this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj); if (this == NULL) { _dl_printf("lazy binding failed!\n"); *(volatile int *)0 = 0; /* XXX */ } DL_DEB(("%s: %s\n", symn, sobj->load_name)); value = ooff + this->st_value + rela->r_addend; if (sobj->traced && _dl_trace_plt(sobj, symn)) return ((uint64_t)value << 32) | (Elf_Addr)sobj->dyn.pltgot; /* if PLT+GOT is protected, allow the write */ if (object->got_size != 0) { _dl_thread_bind_lock(0, &savedmask); /* mprotect the actual modified region, not the whole plt */ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 2, PROT_READ|PROT_WRITE|PROT_EXEC); } addr[0] = value; addr[1] = (Elf_Addr)sobj->dyn.pltgot; /* if PLT is (to be protected, change back to RO */ if (object->got_size != 0) { /* mprotect the actual modified region, not the whole plt */ _dl_mprotect((void*)addr, sizeof (Elf_Addr) * 3, PROT_READ|PROT_EXEC); _dl_thread_bind_lock(1, &savedmask); } return ((uint64_t)addr[0] << 32) | addr[1]; }
Elf_Addr _dl_bind(elf_object_t *object, int reloff) { Elf_RelA *rel; Elf_Addr *r_addr, ooff, value; const Elf_Sym *sym, *this; const char *symn; const elf_object_t *sobj; sigset_t savedmask; rel = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff); sym = object->dyn.symtab; sym += ELF_R_SYM(rel->r_info); symn = object->dyn.strtab + sym->st_name; r_addr = (Elf_Addr *)(object->obj_base + rel->r_offset); this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, &sobj); if (this == NULL) { _dl_printf("lazy binding failed!\n"); *(volatile int *)0 = 0; /* XXX */ } value = ooff + this->st_value + rel->r_addend; if (sobj->traced && _dl_trace_plt(sobj, symn)) return value; /* if GOT is protected, allow the write */ if (object->got_size != 0) { _dl_thread_bind_lock(0, &savedmask); _dl_mprotect((void*)object->got_start, object->got_size, PROT_READ|PROT_WRITE); } *r_addr = value; /* put the GOT back to RO */ if (object->got_size != 0) { _dl_mprotect((void*)object->got_start, object->got_size, PROT_READ); _dl_thread_bind_lock(1, &savedmask); } return (value); }
/* * mprotect a segment to the indicated protection. If 'addr' is non-zero, * then it's the start address, else the value of 'start_sym' is the start. * The value of 'end_sym' is the end address. The start is rounded down * and the end is rounded up to page boundaries. Returns 'addr' or the * address of the start symbol. */ void * _dl_protect_segment(elf_object_t *object, Elf_Addr addr, const char *start_sym, const char *end_sym, int prot) { const Elf_Sym *this; Elf_Addr ooff, start, end; if (addr == 0) { this = NULL; ooff = _dl_find_symbol(start_sym, &this, SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL); /* If not found, nothing to do */ if (this == NULL) return (NULL); addr = ooff + this->st_value; } this = NULL; ooff = _dl_find_symbol(end_sym, &this, SYM_SEARCH_OBJ | SYM_NOWARNNOTFOUND | SYM_PLT, NULL, object, NULL); if (this == NULL) addr = 0; else { end = ooff + this->st_value; if (addr < end) { start = ELF_TRUNC(addr, _dl_pagesz); end = ELF_ROUND(end, _dl_pagesz); _dl_mprotect((void *)start, end - start, prot); } } return ((void *)addr); }
Elf_Addr _dl_bind(elf_object_t *object, int symidx) { Elf_Addr *gotp = object->dyn.pltgot; Elf_Addr *addr, ooff; const Elf_Sym *sym, *this; const char *symn; sigset_t savedmask; int n; sym = object->dyn.symtab; sym += symidx; symn = object->dyn.strtab + sym->st_name; n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM] - object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; this = NULL; ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT, sym, object, NULL); if (this == NULL) { _dl_printf("lazy binding failed\n"); *((int *)0) = 0; /* XXX */ } addr = &gotp[n + symidx]; /* if GOT is protected, allow the write */ if (object->got_size != 0) { _dl_thread_bind_lock(0, &savedmask); _dl_mprotect(addr, sizeof(Elf_Addr), PROT_READ|PROT_WRITE); } *addr = ooff + this->st_value; /* if GOT is (to be protected, change back to RO */ if (object->got_size != 0) { _dl_mprotect(addr, sizeof (Elf_Addr), PROT_READ); _dl_thread_bind_lock(1, &savedmask); } return *addr; }
void _dl_protect_relro (struct elf_resolve *l) { ElfW(Addr) base = (ElfW(Addr)) DL_RELOC_ADDR(l->loadaddr, l->relro_addr); ElfW(Addr) start = (base & PAGE_ALIGN); ElfW(Addr) end = ((base + l->relro_size) & PAGE_ALIGN); _dl_if_debug_dprint("RELRO protecting %s: start:%x, end:%x\n", l->libname, start, end); if (start != end && _dl_mprotect ((void *) start, end - start, PROT_READ) < 0) { _dl_dprintf(2, "%s: cannot apply additional memory protection after relocation", l->libname); _dl_exit(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); }
/* * Relocate the Global Offset Table (GOT). */ int _dl_md_reloc_got(elf_object_t *object, int lazy) { int fails = 0; Elf_Addr *pltgot; extern void _dl_bind_start(void); /* XXX */ Elf_Addr ooff; Elf_Addr plt_addr; const Elf_Sym *this; pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; object->got_addr = 0; object->got_size = 0; this = NULL; ooff = _dl_find_symbol("__got_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_addr = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__got_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_size = ooff + this->st_value - object->got_addr; plt_addr = 0; object->plt_size = 0; this = NULL; ooff = _dl_find_symbol("__plt_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) plt_addr = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__plt_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->plt_size = ooff + this->st_value - plt_addr; if (object->got_addr == 0) object->got_start = 0; else { object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz); object->got_size += object->got_addr - object->got_start; object->got_size = ELF_ROUND(object->got_size, _dl_pagesz); } if (plt_addr == 0) object->plt_start = 0; else { object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz); object->plt_size += plt_addr - object->plt_start; object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz); } if (object->obj_type == OBJTYPE_LDR || !lazy || pltgot == NULL) { fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); } else { if (object->obj_base != 0) { int i, size; Elf_Addr *addr; Elf_RelA *rela; size = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL]); for (i = 0; i < size; i++) { addr = (Elf_Addr *)(object->obj_base + rela[i].r_offset); *addr += object->obj_base; } } } if (pltgot != NULL) { pltgot[2] = (Elf_Addr)_dl_bind_start; pltgot[3] = (Elf_Addr)object; } if (object->got_size != 0) _dl_mprotect((void*)object->got_start, object->got_size, PROT_READ); if (object->plt_size != 0) _dl_mprotect((void*)object->plt_start, object->plt_size, PROT_READ|PROT_EXEC); return (fails); }
int _dl_md_reloc_got(elf_object_t *object, int lazy) { Elf_RelA *rela; Elf_Addr ooff; int i, numrela, fails = 0; const Elf_Sym *this; if (object->dyn.pltrel != DT_RELA) return (0); object->got_addr = 0; object->got_size = 0; this = NULL; ooff = _dl_find_symbol("__got_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL ); if (this != NULL) object->got_addr = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__got_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_size = ooff + this->st_value - object->got_addr; if (object->got_addr == 0) object->got_start = 0; else { object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz); object->got_size += object->got_addr - object->got_start; object->got_size = ELF_ROUND(object->got_size, _dl_pagesz); } if (object->traced) lazy = 1; if (!lazy) { fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); } else { register Elf_Addr ltp __asm ("%r19"); Elf_Addr *got = NULL; rela = (Elf_RelA *)(object->dyn.jmprel); numrela = object->dyn.pltrelsz / sizeof(Elf_RelA); ooff = object->obj_base; /* * Find the PLT stub by looking at all the * relocations. The PLT stub should be at the end of * the .plt section so we start with the last * relocation, since the linker should have emitted * them in order. */ for (i = numrela - 1; i >= 0; i--) { got = (Elf_Addr *)(ooff + rela[i].r_offset + PLT_ENTRY_SIZE + PLT_STUB_SIZE); if (got[-2] == PLT_STUB_MAGIC1 || got[-1] == PLT_STUB_MAGIC2) break; got = NULL; } if (got == NULL) return (1); /* * Patch up the PLT stub such that it doesn't clobber * %r22, which is used to pass on the errno values * from failed system calls to __cerrno() in libc. */ got[-7] = PLT_STUB_INSN1; got[-6] = PLT_STUB_INSN2; __asm volatile("fdc 0(%0)" :: "r" (&got[-7])); __asm volatile("fdc 0(%0)" :: "r" (&got[-6])); __asm volatile("sync"); __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-7])); __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-6])); __asm volatile("sync"); /* * Fill in the PLT stub such that it invokes the * _dl_bind_start() trampoline to fix up the * relocation. */ got[1] = (Elf_Addr)object; got[-2] = (Elf_Addr)&_dl_bind_start; got[-1] = ltp; /* * Even though we didn't modify any instructions it * seems we still need to syncronize the caches. * There may be instructions in the same cache line * and they end up being corrupted otherwise. */ __asm volatile("fdc 0(%0)" :: "r" (&got[-2])); __asm volatile("fdc 0(%0)" :: "r" (&got[-1])); __asm volatile("sync"); __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-2])); __asm volatile("fic 0(%%sr0,%0)" :: "r" (&got[-1])); __asm volatile("sync"); for (i = 0; i < numrela; i++, rela++) { Elf_Addr *r_addr = (Elf_Addr *)(ooff + rela->r_offset); if (ELF_R_TYPE(rela->r_info) != RELOC_IPLT) { _dl_printf("unexpected reloc 0x%x\n", ELF_R_TYPE(rela->r_info)); return (1); } if (ELF_R_SYM(rela->r_info)) { r_addr[0] = (Elf_Addr)got - PLT_STUB_GOTOFF; r_addr[1] = (Elf_Addr) (rela - (Elf_RelA *)object->dyn.jmprel); } else { r_addr[0] = ooff + rela->r_addend; r_addr[1] = (Elf_Addr)object->dyn.pltgot; } } } if (object->got_size != 0) _dl_mprotect((void *)object->got_start, object->got_size, GOT_PERMS|PROT_EXEC); return (fails); }
int _dl_md_reloc(elf_object_t *object, int rel, int relsz) { int i; int numrel; int fails = 0; struct load_list *load_list; Elf64_Addr loff; Elf64_Addr ooff; Elf64_Addr got_start, got_end; Elf64_Rel *relocs; const Elf64_Sym *sym, *this; loff = object->obj_base; numrel = object->Dyn.info[relsz] / sizeof(Elf64_Rel); relocs = (Elf64_Rel *)(object->Dyn.info[rel]); if (relocs == NULL) return(0); /* * Change protection of all write protected segments in the * object so we can do relocations in the .rodata section. * After relocation restore protection. */ load_list = object->load_list; while (load_list != NULL) { if ((load_list->prot & PROT_WRITE) == 0) _dl_mprotect(load_list->start, load_list->size, load_list->prot|PROT_WRITE); load_list = load_list->next; } /* XXX We need the got limits to know if reloc is in got. */ /* XXX Relocs against the got should not include the STUB address! */ this = NULL; got_start = 0; got_end = 0; ooff = _dl_find_symbol("__got_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) got_start = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__got_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) got_end = ooff + this->st_value; DL_DEB(("relocating %d\n", numrel)); for (i = 0; i < numrel; i++, relocs++) { Elf64_Addr r_addr = relocs->r_offset + loff; const char *symn; int type; if (ELF64_R_SYM(relocs->r_info) == 0xffffff) continue; ooff = 0; sym = object->dyn.symtab; sym += ELF64_R_SYM(relocs->r_info); symn = object->dyn.strtab + sym->st_name; type = ELF64_R_TYPE(relocs->r_info); this = NULL; if (ELF64_R_SYM(relocs->r_info) && !(ELF64_ST_BIND(sym->st_info) == STB_LOCAL && ELF64_ST_TYPE (sym->st_info) == STT_NOTYPE)) { ooff = _dl_find_symbol(symn, &this, SYM_SEARCH_ALL | SYM_WARNNOTFOUND | SYM_PLT, sym, object, NULL); if (this == NULL) { if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; continue; } } switch (ELF64_R_TYPE(relocs->r_info)) { /* XXX Handle non aligned relocs. .eh_frame * XXX in libstdc++ seems to have them... */ u_int64_t robj; case R_MIPS_REL32_64: if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL && (ELF64_ST_TYPE(sym->st_info) == STT_SECTION || ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE) ) { if ((long)r_addr & 7) { _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); robj += loff + sym->st_value; _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); } else { *(u_int64_t *)r_addr += loff + sym->st_value; } } else if (this && ((long)r_addr & 7)) { _dl_bcopy((char *)r_addr, &robj, sizeof(robj)); robj += this->st_value + ooff; _dl_bcopy(&robj, (char *)r_addr, sizeof(robj)); } else if (this) { *(u_int64_t *)r_addr += this->st_value + ooff; } break; case R_MIPS_NONE: break; default: _dl_printf("%s: unsupported relocation '%d'\n", _dl_progname, ELF64_R_TYPE(relocs->r_info)); _dl_exit(1); } } DL_DEB(("done %d fails\n", fails)); load_list = object->load_list; while (load_list != NULL) { if ((load_list->prot & PROT_WRITE) == 0) _dl_mprotect(load_list->start, load_list->size, load_list->prot); load_list = load_list->next; } return(fails); }
/* * Relocate the Global Offset Table (GOT). Currently we don't * do lazy evaluation here because the GNU linker doesn't * follow the ABI spec which says that if an external symbol * is referenced by other relocations than CALL16 and 26 it * should not be given a stub and have a zero value in the * symbol table. By not doing so, we can't use pointers to * external functions and use them in comparisons... */ int _dl_md_reloc_got(elf_object_t *object, int lazy) { int i, n; Elf64_Addr loff; Elf64_Addr ooff; Elf64_Addr *gotp; const Elf64_Sym *symp; const Elf64_Sym *this; const char *strt; if (object->status & STAT_GOT_DONE) return (0); loff = object->obj_base; strt = object->dyn.strtab; gotp = object->dyn.pltgot; n = object->Dyn.info[DT_MIPS_LOCAL_GOTNO - DT_LOPROC + DT_NUM]; DL_DEB(("loff: '%p'\n", loff)); /* * Set up pointers for run time (lazy) resolving. */ gotp[0] = (long)_dl_bind_start; gotp[1] = (long)object; /* First do all local references. */ for (i = 2; i < n; i++) { gotp[i] += loff; } gotp += n; symp = object->dyn.symtab; symp += object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; n = object->Dyn.info[DT_MIPS_SYMTABNO - DT_LOPROC + DT_NUM] - object->Dyn.info[DT_MIPS_GOTSYM - DT_LOPROC + DT_NUM]; this = NULL; object->plt_size = 0; object->got_size = 0; ooff = _dl_find_symbol("__got_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_start = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__got_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_size = ooff + this->st_value - object->got_start; /* * Then do all global references according to the ABI. * Quickstart is not yet implemented. */ while (n--) { if (symp->st_shndx == SHN_UNDEF && ELF64_ST_TYPE(symp->st_info) == STT_FUNC) { if (symp->st_value == 0 || !lazy) { this = NULL; ooff = _dl_find_symbol(strt + symp->st_name, &this, SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, symp, object, NULL); if (this) *gotp = this->st_value + ooff; } else *gotp = symp->st_value + loff; } else if (symp->st_shndx == SHN_COMMON || symp->st_shndx == SHN_UNDEF) { this = NULL; ooff = _dl_find_symbol(strt + symp->st_name, &this, SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, symp, object, NULL); if (this) *gotp = this->st_value + ooff; } else if (ELF64_ST_TYPE(symp->st_info) == STT_FUNC && symp->st_value != *gotp) { *gotp += loff; } else { /* Resolve all others immediately */ this = NULL; ooff = _dl_find_symbol(strt + symp->st_name, &this, SYM_SEARCH_ALL|SYM_NOWARNNOTFOUND|SYM_PLT, symp, object, NULL); if (this) *gotp = this->st_value + ooff; else *gotp = symp->st_value + loff; } gotp++; symp++; } object->status |= STAT_GOT_DONE; DL_DEB(("got: %x, %x\n", object->got_start, object->got_size)); if (object->got_size != 0) _dl_mprotect((void*)object->got_start, object->got_size, PROT_READ); return (0); }
int _dl_md_reloc(elf_object_t *object, int rel, int relasz) { int i; int numrela; int relrel; int fails = 0; struct load_list *llist; Elf32_Addr loff; Elf32_Rela *relas; loff = object->obj_base; numrela = object->Dyn.info[relasz] / sizeof(Elf32_Rela); relrel = rel == DT_RELA ? object->relacount : 0; relas = (Elf32_Rela *)(object->Dyn.info[rel]); #ifdef DL_PRINTF_DEBUG _dl_printf("object relocation size %x, numrela %x\n", object->Dyn.info[relasz], numrela); #endif if (relas == NULL) return(0); if (relrel > numrela) { _dl_printf("relacount > numrela: %ld > %ld\n", relrel, numrela); _dl_exit(20); } /* * Change protection of all write protected segments in the object * so we can do relocations such as PC32. After relocation, * restore protection. */ 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); } } } /* tight loop for leading RELATIVE relocs */ for (i = 0; i < relrel; i++, relas++) { Elf32_Addr *r_addr; #ifdef DEBUG if (ELF_R_TYPE(relas->r_info) != R_68K_RELATIVE) { _dl_printf("RELACOUNT wrong\n"); _dl_exit(20); } #endif r_addr = (Elf32_Addr *)(relas->r_offset + loff); *r_addr = relas->r_addend + loff; } for (; i < numrela; i++, relas++) { Elf32_Addr *r_addr = (Elf32_Addr *)(relas->r_offset + loff); Elf32_Addr ooff, addend, newval; const Elf32_Sym *sym, *this; const char *symn; int type; Elf32_Addr prev_value = 0, prev_ooff = 0; const Elf32_Sym *prev_sym = NULL; type = ELF32_R_TYPE(relas->r_info); if (type == R_68K_JMP_SLOT && rel != DT_JMPREL) continue; if (type == R_68K_NONE) continue; sym = object->dyn.symtab; sym += ELF32_R_SYM(relas->r_info); symn = object->dyn.strtab + sym->st_name; if (type == R_68K_COPY) { /* * we need to find a symbol, that is not in the current * object, start looking at the beginning of the list, * searching all objects but _not_ the current object, * first one found wins. */ const Elf32_Sym *cpysrc = NULL; Elf32_Addr src_loff; int size; src_loff = 0; src_loff = _dl_find_symbol(symn, &cpysrc, SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT, sym, object, NULL); if (cpysrc != NULL) { size = sym->st_size; if (sym->st_size != cpysrc->st_size) { /* _dl_find_symbol() has warned about this already */ size = sym->st_size < cpysrc->st_size ? sym->st_size : cpysrc->st_size; } _dl_bcopy((void *)(src_loff + cpysrc->st_value), r_addr, size); } else fails++; continue; } if (ELF32_R_SYM(relas->r_info) && !(ELF32_ST_BIND(sym->st_info) == STB_LOCAL && ELF32_ST_TYPE (sym->st_info) == STT_NOTYPE) && sym != prev_sym) { this = NULL; ooff = _dl_find_symbol_bysym(object, ELF32_R_SYM(relas->r_info), &this, SYM_SEARCH_ALL|SYM_WARNNOTFOUND| ((type == R_68K_JMP_SLOT) ? SYM_PLT:SYM_NOTPLT), sym, NULL); if (this == NULL) { if (ELF_ST_BIND(sym->st_info) != STB_WEAK) fails++; continue; } prev_sym = sym; prev_value = this->st_value; prev_ooff = ooff; } if (ELF32_ST_BIND(sym->st_info) == STB_LOCAL && (ELF32_ST_TYPE(sym->st_info) == STT_SECTION || ELF32_ST_TYPE(sym->st_info) == STT_NOTYPE)) addend = relas->r_addend; else addend = prev_value + relas->r_addend; switch (type) { case R_68K_PC32: newval = prev_ooff + addend; newval -= (Elf_Addr)r_addr; *r_addr = newval; break; case R_68K_32: case R_68K_GLOB_DAT: case R_68K_JMP_SLOT: newval = prev_ooff + addend; *r_addr = newval; break; case R_68K_RELATIVE: newval = loff + addend; *r_addr = newval; break; default: _dl_printf("%s:" " %s: unsupported relocation '%s' %d at %x\n", _dl_progname, object->load_name, symn, ELF32_R_TYPE(relas->r_info), r_addr); _dl_exit(1); } } /* 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); }
/* * Relocate the Global Offset Table (GOT). * This is done by calling _dl_md_reloc on DT_JMPREL for DL_BIND_NOW, * otherwise the lazy binding plt initialization is performed. */ int _dl_md_reloc_got(elf_object_t *object, int lazy) { extern void _dl_bind_start(void); /* XXX */ int fails = 0; Elf_Addr *pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT]; Elf_Addr ooff; const Elf_Sym *this; if (pltgot == NULL) return (0); pltgot[1] = (Elf_Addr)object; pltgot[2] = (Elf_Addr)_dl_bind_start; if (object->Dyn.info[DT_PLTREL] != DT_RELA) return (0); object->got_addr = 0; object->got_size = 0; this = NULL; ooff = _dl_find_symbol("__got_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_addr = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__got_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->got_size = ooff + this->st_value - object->got_addr; #if 0 plt_addr = 0; object->plt_size = 0; this = NULL; ooff = _dl_find_symbol("__plt_start", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) plt_addr = ooff + this->st_value; this = NULL; ooff = _dl_find_symbol("__plt_end", &this, SYM_SEARCH_OBJ|SYM_NOWARNNOTFOUND|SYM_PLT, NULL, object, NULL); if (this != NULL) object->plt_size = ooff + this->st_value - plt_addr; #endif if (object->got_addr == 0) object->got_start = 0; else { object->got_start = ELF_TRUNC(object->got_addr, _dl_pagesz); object->got_size += object->got_addr - object->got_start; object->got_size = ELF_ROUND(object->got_size, _dl_pagesz); } #if 0 if (plt_addr == 0) object->plt_start = 0; else { object->plt_start = ELF_TRUNC(plt_addr, _dl_pagesz); object->plt_size += plt_addr - object->plt_start; object->plt_size = ELF_ROUND(object->plt_size, _dl_pagesz); } #endif if (object->traced) lazy = 1; if (!lazy) { fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ); } else { if (object->obj_base != 0) { int i, size; Elf_Addr *addr; Elf_RelA *rela; size = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA); rela = (Elf_RelA *)object->Dyn.info[DT_JMPREL]; for (i = 0; i < size; i++) { addr = (Elf_Addr *)(object->obj_base + rela[i].r_offset); *addr += object->obj_base; } } } if (object->got_size != 0) { _dl_mprotect((void*)object->got_start, object->got_size, PROT_READ); } /* PLT is already RO, no point in mprotecting it, just do GOT */ #if 0 if (object->plt_size != 0) _dl_mprotect((void*)object->plt_start, object->plt_size, PROT_READ|PROT_EXEC); #endif return (fails); }
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); }