diff --git a/libexec/rtld-elf/powerpc64/reloc.c b/libexec/rtld-elf/powerpc64/reloc.c --- a/libexec/rtld-elf/powerpc64/reloc.c +++ b/libexec/rtld-elf/powerpc64/reloc.c @@ -168,11 +168,13 @@ */ static int reloc_nonplt_object(Obj_Entry *obj_rtld __unused, Obj_Entry *obj, - const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate) + const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate, + Elf_Addr **wherep) { const Elf_Sym *def = NULL; const Obj_Entry *defobj; - Elf_Addr *where, symval = 0; + Elf_Addr symval = 0; + Elf_Addr *where; /* * First, resolve symbol for relocations which @@ -235,6 +237,7 @@ switch (ELF_R_TYPE(rela->r_info)) { case R_PPC_NONE: + *wherep = NULL; break; case R_PPC64_UADDR64: case R_PPC64_ADDR64: @@ -243,9 +246,11 @@ if (*where != symval + rela->r_addend) { *where = symval + rela->r_addend; } + *wherep = where; break; case R_PPC64_DTPMOD64: *where = (Elf_Addr) defobj->tlsindex; + *wherep = where; break; case R_PPC64_TPREL64: /* @@ -268,10 +273,12 @@ *(Elf_Addr **)where = *where * sizeof(Elf_Addr) + (Elf_Addr *)(def->st_value + rela->r_addend + defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE); + *wherep = where; break; case R_PPC64_DTPREL64: *where += (Elf_Addr)(def->st_value + rela->r_addend - TLS_DTV_OFFSET); + *wherep = where; break; case R_PPC_RELATIVE: /* doubleword64 B + A */ symval = (Elf_Addr)(obj->relocbase + rela->r_addend); @@ -280,6 +287,7 @@ if (*where != symval) { *where = symval; } + *wherep = where; break; case R_PPC_COPY: /* @@ -295,17 +303,20 @@ obj->path); return (-1); } + *wherep = NULL; break; case R_PPC_IRELATIVE: /* * These will be handled by reloc_iresolve(). */ obj->irelative = true; + *wherep = NULL; break; case R_PPC_JMP_SLOT: /* * These will be handled by the plt/jmpslot routines */ + *wherep = NULL; break; default: @@ -314,9 +325,38 @@ ELF_R_TYPE(rela->r_info)); return (-1); } + *wherep = where; return (0); } +static void +phdr_syncicache(Obj_Entry *obj, const Elf_Phdr *phdr) +{ + if ((phdr->p_flags & PF_X) != 0) + __syncicache(obj->relocbase + phdr->p_vaddr, phdr->p_memsz); +} + +static bool +is_addr_in_phdr(Obj_Entry *obj, const Elf_Phdr *phdr, ElF_Addr addr) +{ + return (obj->relocbase + phdrl->p_vaddr >= addr && + addr < obj->relocbase + phdrl->p_vaddr + phdrl->p_memsz); +} + +static const Elf_Phdr * +find_phdr_from_addr(Obj_Entry *obj, ElF_Addr addr) +{ + const Elf_Phdr *phdr = NULL; + size_t c; + + for (c = obj->phsize / sizeof(*phdr), phdrl = obj->phdr; + c > 0; c--, phdrl++) { + if (phdrl->p_type == PT_LOAD && + is_where_in_phdr(obj, phdrl, addr)) + return (phdrl); + } + return (NULL); +} /* * Process non-PLT relocations @@ -327,7 +367,8 @@ { const Elf_Rela *relalim; const Elf_Rela *rela; - const Elf_Phdr *phdr; + const Elf_Phdr *phdr = NULL; + Elf_Addr *where; SymCache *cache; int bytes = obj->dynsymcount * sizeof(SymCache); int r = -1; @@ -352,11 +393,24 @@ relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags, - lockstate) < 0) + lockstate, &where) < 0) goto done; + if (where != NULL) { + if (phdr != NULL && + !is_addr_in_phdr(obj, phdr, (Elf_Addr)where)) { + phdr_syncicache(obj, phdr); + phdr = NULL; + } + if (phdr == NULL) { + phdr = find_phdr_from_addr(obj, + (Elf_Addr)where); + } + } } r = 0; done: + if (phdr != NULL) + phdr_syncicache(obj, phdr); if (cache) munmap(cache, bytes); @@ -368,8 +422,6 @@ (const char *)phdr < (const char *)obj->phdr + obj->phsize; phdr++) { if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X) != 0) { - __syncicache(obj->relocbase + phdr->p_vaddr, - phdr->p_memsz); } }