Path: blob/main/libexec/rtld-elf/powerpc64/reloc.c
34923 views
/* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */12/*-3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (C) 1998 Tsubai Masanari6* All rights reserved.7*8* Redistribution and use in source and binary forms, with or without9* modification, are permitted provided that the following conditions10* are met:11* 1. Redistributions of source code must retain the above copyright12* notice, this list of conditions and the following disclaimer.13* 2. Redistributions in binary form must reproduce the above copyright14* notice, this list of conditions and the following disclaimer in the15* documentation and/or other materials provided with the distribution.16* 3. The name of the author may not be used to endorse or promote products17* derived from this software without specific prior written permission.18*19* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR20* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES21* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.22* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,23* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT24* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,25* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY26* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT27* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE28* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.29*/3031#include <sys/param.h>32#include <sys/mman.h>33#include <sys/sysctl.h>3435#include <errno.h>36#include <stdio.h>37#include <stdlib.h>38#include <string.h>39#include <unistd.h>40#include <machine/cpu.h>41#include <machine/md_var.h>4243#include "debug.h"44#include "rtld.h"4546#if !defined(_CALL_ELF) || _CALL_ELF == 147struct funcdesc {48Elf_Addr addr;49Elf_Addr toc;50Elf_Addr env;51};52#endif5354bool55arch_digest_dynamic(struct Struct_Obj_Entry *obj, const Elf_Dyn *dynp)56{57if (dynp->d_tag == DT_PPC64_GLINK) {58obj->glink = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr);59return (true);60}6162return (false);63}6465/*66* Process the R_PPC_COPY relocations67*/68int69do_copy_relocations(Obj_Entry *dstobj)70{71const Elf_Rela *relalim;72const Elf_Rela *rela;7374/*75* COPY relocs are invalid outside of the main program76*/77assert(dstobj->mainprog);7879relalim = (const Elf_Rela *)((const char *) dstobj->rela +80dstobj->relasize);81for (rela = dstobj->rela; rela < relalim; rela++) {82void *dstaddr;83const Elf_Sym *dstsym;84const char *name;85size_t size;86const void *srcaddr;87const Elf_Sym *srcsym = NULL;88const Obj_Entry *srcobj, *defobj;89SymLook req;90int res;9192if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) {93continue;94}9596dstaddr = (void *)(dstobj->relocbase + rela->r_offset);97dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info);98name = dstobj->strtab + dstsym->st_name;99size = dstsym->st_size;100symlook_init(&req, name);101req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info));102req.flags = SYMLOOK_EARLY;103104for (srcobj = globallist_next(dstobj); srcobj != NULL;105srcobj = globallist_next(srcobj)) {106res = symlook_obj(&req, srcobj);107if (res == 0) {108srcsym = req.sym_out;109defobj = req.defobj_out;110break;111}112}113114if (srcobj == NULL) {115_rtld_error("Undefined symbol \"%s\" "116" referenced from COPY"117" relocation in %s", name, dstobj->path);118return (-1);119}120121srcaddr = (const void *)(defobj->relocbase+srcsym->st_value);122memcpy(dstaddr, srcaddr, size);123dbg("copy_reloc: src=%p,dst=%p,size=%zd\n",srcaddr,dstaddr,size);124}125126return (0);127}128129130/*131* Perform early relocation of the run-time linker image132*/133void134reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase)135{136const Elf_Rela *rela = NULL, *relalim;137Elf_Addr relasz = 0;138Elf_Addr *where;139140/*141* Extract the rela/relasz values from the dynamic section142*/143for (; dynp->d_tag != DT_NULL; dynp++) {144switch (dynp->d_tag) {145case DT_RELA:146rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr);147break;148case DT_RELASZ:149relasz = dynp->d_un.d_val;150break;151}152}153154/*155* Relocate these values156*/157relalim = (const Elf_Rela *)((const char *)rela + relasz);158for (; rela < relalim; rela++) {159where = (Elf_Addr *)(relocbase + rela->r_offset);160*where = (Elf_Addr)(relocbase + rela->r_addend);161}162}163164165/*166* Relocate a non-PLT object with addend.167*/168static int169reloc_nonplt_object(Obj_Entry *obj_rtld __unused, Obj_Entry *obj,170const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate)171{172const Elf_Sym *def = NULL;173const Obj_Entry *defobj;174Elf_Addr *where, symval = 0;175176/*177* First, resolve symbol for relocations which178* reference symbols.179*/180switch (ELF_R_TYPE(rela->r_info)) {181182case R_PPC64_UADDR64: /* doubleword64 S + A */183case R_PPC64_ADDR64:184case R_PPC_GLOB_DAT:185case R_PPC64_DTPMOD64:186case R_PPC64_TPREL64:187case R_PPC64_DTPREL64:188def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,189flags, cache, lockstate);190if (def == NULL) {191return (-1);192}193/*194* If symbol is IFUNC, only perform relocation195* when caller allowed it by passing196* SYMLOOK_IFUNC flag. Skip the relocations197* otherwise.198*199* Also error out in case IFUNC relocations200* are specified for TLS, which cannot be201* usefully interpreted.202*/203if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {204switch (ELF_R_TYPE(rela->r_info)) {205case R_PPC64_UADDR64:206case R_PPC64_ADDR64:207case R_PPC_GLOB_DAT:208if ((flags & SYMLOOK_IFUNC) == 0) {209dbg("Non-PLT reference to IFUNC found!");210obj->non_plt_gnu_ifunc = true;211return (0);212}213symval = (Elf_Addr)rtld_resolve_ifunc(214defobj, def);215break;216default:217_rtld_error("%s: IFUNC for TLS reloc",218obj->path);219return (-1);220}221} else {222if ((flags & SYMLOOK_IFUNC) != 0)223return (0);224symval = (Elf_Addr)defobj->relocbase +225def->st_value;226}227break;228default:229if ((flags & SYMLOOK_IFUNC) != 0)230return (0);231}232233where = (Elf_Addr *)(obj->relocbase + rela->r_offset);234235switch (ELF_R_TYPE(rela->r_info)) {236case R_PPC_NONE:237break;238case R_PPC64_UADDR64:239case R_PPC64_ADDR64:240case R_PPC_GLOB_DAT:241/* Don't issue write if unnecessary; avoid COW page fault */242if (*where != symval + rela->r_addend) {243*where = symval + rela->r_addend;244}245break;246case R_PPC64_DTPMOD64:247*where = (Elf_Addr) defobj->tlsindex;248break;249case R_PPC64_TPREL64:250/*251* We lazily allocate offsets for static TLS as we252* see the first relocation that references the253* TLS block. This allows us to support (small254* amounts of) static TLS in dynamically loaded255* modules. If we run out of space, we generate an256* error.257*/258if (!defobj->tls_static) {259if (!allocate_tls_offset(260__DECONST(Obj_Entry *, defobj))) {261_rtld_error("%s: No space available for static "262"Thread Local Storage", obj->path);263return (-1);264}265}266267*(Elf_Addr **)where = *where * sizeof(Elf_Addr)268+ (Elf_Addr *)(def->st_value + rela->r_addend269+ defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE);270break;271case R_PPC64_DTPREL64:272*where += (Elf_Addr)(def->st_value + rela->r_addend273- TLS_DTV_OFFSET);274break;275case R_PPC_RELATIVE: /* doubleword64 B + A */276symval = (Elf_Addr)(obj->relocbase + rela->r_addend);277278/* As above, don't issue write unnecessarily */279if (*where != symval) {280*where = symval;281}282break;283case R_PPC_COPY:284/*285* These are deferred until all other relocations286* have been done. All we do here is make sure287* that the COPY relocation is not in a shared288* library. They are allowed only in executable289* files.290*/291if (!obj->mainprog) {292_rtld_error("%s: Unexpected R_COPY "293" relocation in shared library",294obj->path);295return (-1);296}297break;298case R_PPC_IRELATIVE:299/*300* These will be handled by reloc_iresolve().301*/302obj->irelative = true;303break;304case R_PPC_JMP_SLOT:305/*306* These will be handled by the plt/jmpslot routines307*/308break;309310default:311_rtld_error("%s: Unsupported relocation type %ld"312" in non-PLT relocations\n", obj->path,313ELF_R_TYPE(rela->r_info));314return (-1);315}316return (0);317}318319320/*321* Process non-PLT relocations322*/323int324reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags,325RtldLockState *lockstate)326{327const Elf_Rela *relalim;328const Elf_Rela *rela;329const Elf_Phdr *phdr;330SymCache *cache;331int bytes = obj->dynsymcount * sizeof(SymCache);332int r = -1;333334/*335* The dynamic loader may be called from a thread, we have336* limited amounts of stack available so we cannot use alloca().337*/338if (obj != obj_rtld) {339cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON,340-1, 0);341if (cache == MAP_FAILED)342cache = NULL;343} else344cache = NULL;345346/*347* From the SVR4 PPC ABI:348* "The PowerPC family uses only the Elf32_Rela relocation349* entries with explicit addends."350*/351relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);352for (rela = obj->rela; rela < relalim; rela++) {353if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags,354lockstate) < 0)355goto done;356}357r = 0;358done:359if (cache)360munmap(cache, bytes);361362/*363* Synchronize icache for executable segments in case we made364* any changes.365*/366for (phdr = obj->phdr;367(const char *)phdr < (const char *)obj->phdr + obj->phsize;368phdr++) {369if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X) != 0) {370__syncicache(obj->relocbase + phdr->p_vaddr,371phdr->p_memsz);372}373}374375return (r);376}377378379/*380* Initialise a PLT slot to the resolving trampoline381*/382static int383reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)384{385Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);386long reloff;387388reloff = rela - obj->pltrela;389390dbg(" reloc_plt_object: where=%p,reloff=%lx,glink=%#lx", (void *)where,391reloff, obj->glink);392393#if !defined(_CALL_ELF) || _CALL_ELF == 1394/* Glink code is 3 instructions after the first 32k, 2 before */395*where = (Elf_Addr)obj->glink + 32 +3968*((reloff < 0x8000) ? reloff : 0x8000) +39712*((reloff < 0x8000) ? 0 : (reloff - 0x8000));398#else399/* 64-Bit ELF V2 ABI Specification, sec. 4.2.5.3. */400*where = (Elf_Addr)obj->glink + 4*reloff + 32;401#endif402403return (0);404}405406/*407* Process the PLT relocations.408*/409int410reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)411{412const Elf_Rela *relalim;413const Elf_Rela *rela;414415if (obj->pltrelasize != 0) {416relalim = (const Elf_Rela *)((const char *)obj->pltrela +417obj->pltrelasize);418for (rela = obj->pltrela; rela < relalim; rela++) {419420#if defined(_CALL_ELF) && _CALL_ELF == 2421if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {422dbg("ABI violation - found IRELATIVE in the PLT.");423obj->irelative = true;424continue;425}426#endif427/*428* PowerPC(64) .rela.plt is composed of an array of429* R_PPC_JMP_SLOT relocations. Unlike other platforms,430* this is the ONLY relocation type that is valid here.431*/432assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);433434if (reloc_plt_object(obj, rela) < 0) {435return (-1);436}437}438}439440return (0);441}442443/*444* LD_BIND_NOW was set - force relocation for all jump slots445*/446int447reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)448{449const Obj_Entry *defobj;450const Elf_Rela *relalim;451const Elf_Rela *rela;452const Elf_Sym *def;453Elf_Addr *where;454Elf_Addr target;455456relalim = (const Elf_Rela *)((const char *)obj->pltrela +457obj->pltrelasize);458for (rela = obj->pltrela; rela < relalim; rela++) {459/* This isn't actually a jump slot, ignore it. */460if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE)461continue;462assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);463where = (Elf_Addr *)(obj->relocbase + rela->r_offset);464def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,465SYMLOOK_IN_PLT | flags, NULL, lockstate);466if (def == NULL) {467dbg("reloc_jmpslots: sym not found");468return (-1);469}470471target = (Elf_Addr)(defobj->relocbase + def->st_value);472473if (def == &sym_zero) {474/* Zero undefined weak symbols */475#if !defined(_CALL_ELF) || _CALL_ELF == 1476bzero(where, sizeof(struct funcdesc));477#else478*where = 0;479#endif480} else {481if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {482/* LD_BIND_NOW, ifunc in shared lib.*/483obj->gnu_ifunc = true;484continue;485}486reloc_jmpslot(where, target, defobj, obj,487(const Elf_Rel *) rela);488}489}490491obj->jmpslots_done = true;492493return (0);494}495496497/*498* Update the value of a PLT jump slot.499*/500Elf_Addr501reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj __unused,502const Obj_Entry *obj __unused, const Elf_Rel *rel __unused)503{504505/*506* At the PLT entry pointed at by `wherep', construct507* a direct transfer to the now fully resolved function508* address.509*/510511#if !defined(_CALL_ELF) || _CALL_ELF == 1512dbg(" reloc_jmpslot: where=%p, target=%p (%#lx + %#lx)",513(void *)wherep, (void *)target, *(Elf_Addr *)target,514(Elf_Addr)defobj->relocbase);515516if (ld_bind_not)517goto out;518519/*520* For the trampoline, the second two elements of the function521* descriptor are unused, so we are fine replacing those at any time522* with the real ones with no thread safety implications. However, we523* need to make sure the main entry point pointer ([0]) is seen to be524* modified *after* the second two elements. This can't be done in525* general, since there are no barriers in the reading code, but put in526* some isyncs to at least make it a little better.527*/528memcpy(wherep, (void *)target, sizeof(struct funcdesc));529wherep[2] = ((Elf_Addr *)target)[2];530wherep[1] = ((Elf_Addr *)target)[1];531__asm __volatile ("isync" : : : "memory");532wherep[0] = ((Elf_Addr *)target)[0];533__asm __volatile ("isync" : : : "memory");534535if (((struct funcdesc *)(wherep))->addr < (Elf_Addr)defobj->relocbase) {536/*537* It is possible (LD_BIND_NOW) that the function538* descriptor we are copying has not yet been relocated.539* If this happens, fix it. Don't worry about threading in540* this case since LD_BIND_NOW makes it irrelevant.541*/542543((struct funcdesc *)(wherep))->addr +=544(Elf_Addr)defobj->relocbase;545((struct funcdesc *)(wherep))->toc +=546(Elf_Addr)defobj->relocbase;547}548#else549dbg(" reloc_jmpslot: where=%p, target=%p", (void *)wherep,550(void *)target);551552assert(target >= (Elf_Addr)defobj->relocbase);553554if (ld_bind_not)555goto out;556557if (*wherep != target)558*wherep = target;559560#endif561out:562563return (target);564}565566int567reloc_iresolve(Obj_Entry *obj,568struct Struct_RtldLockState *lockstate)569{570/*571* Since PLT slots on PowerPC64 are always R_PPC_JMP_SLOT,572* R_PPC_IRELATIVE is in RELA.573*/574#if !defined(_CALL_ELF) || _CALL_ELF == 1575(void)(obj);576(void)(lockstate);577/* XXX not implemented */578return (0);579#else580const Elf_Rela *relalim;581const Elf_Rela *rela;582Elf_Addr *where, target, *ptr;583584if (!obj->irelative)585return (0);586587relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);588for (rela = obj->rela; rela < relalim; rela++) {589if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {590ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);591where = (Elf_Addr *)(obj->relocbase + rela->r_offset);592593lock_release(rtld_bind_lock, lockstate);594target = call_ifunc_resolver(ptr);595wlock_acquire(rtld_bind_lock, lockstate);596597*where = target;598}599}600/*601* XXX Remove me when lld is fixed!602* LLD currently makes illegal relocations in the PLT.603*/604relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);605for (rela = obj->pltrela; rela < relalim; rela++) {606if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {607ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);608where = (Elf_Addr *)(obj->relocbase + rela->r_offset);609610lock_release(rtld_bind_lock, lockstate);611target = call_ifunc_resolver(ptr);612wlock_acquire(rtld_bind_lock, lockstate);613614*where = target;615}616}617618obj->irelative = false;619return (0);620#endif621}622623int624reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused,625struct Struct_RtldLockState *lockstate __unused)626{627#if !defined(_CALL_ELF) || _CALL_ELF == 1628_rtld_error("reloc_gnu_ifunc(): Not implemented!");629/* XXX not implemented */630return (-1);631#else632633const Elf_Rela *relalim;634const Elf_Rela *rela;635Elf_Addr *where, target;636const Elf_Sym *def;637const Obj_Entry *defobj;638639if (!obj->gnu_ifunc)640return (0);641relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);642for (rela = obj->pltrela; rela < relalim; rela++) {643if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) {644where = (Elf_Addr *)(obj->relocbase + rela->r_offset);645def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,646SYMLOOK_IN_PLT | flags, NULL, lockstate);647if (def == NULL)648return (-1);649if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)650continue;651lock_release(rtld_bind_lock, lockstate);652target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);653wlock_acquire(rtld_bind_lock, lockstate);654reloc_jmpslot(where, target, defobj, obj,655(const Elf_Rel *)rela);656}657}658obj->gnu_ifunc = false;659return (0);660#endif661}662663int664reloc_iresolve_nonplt(Obj_Entry *obj __unused,665struct Struct_RtldLockState *lockstate __unused)666{667return (0);668}669670void671init_pltgot(Obj_Entry *obj)672{673Elf_Addr *pltcall;674675pltcall = obj->pltgot;676677if (pltcall == NULL) {678return;679}680681#if defined(_CALL_ELF) && _CALL_ELF == 2682pltcall[0] = (Elf_Addr)&_rtld_bind_start;683pltcall[1] = (Elf_Addr)obj;684#else685memcpy(pltcall, _rtld_bind_start, sizeof(struct funcdesc));686pltcall[2] = (Elf_Addr)obj;687#endif688}689690/*691* Actual values are 32 bit.692*/693u_long cpu_features;694u_long cpu_features2;695696void697powerpc64_abi_variant_hook(Elf_Auxinfo** aux_info)698{699/*700* Since aux_info[] is easier to work with than aux, go ahead and701* initialize cpu_features / cpu_features2.702*/703cpu_features = -1UL;704cpu_features2 = -1UL;705if (aux_info[AT_HWCAP] != NULL)706cpu_features = (uint32_t)aux_info[AT_HWCAP]->a_un.a_val;707if (aux_info[AT_HWCAP2] != NULL)708cpu_features2 = (uint32_t)aux_info[AT_HWCAP2]->a_un.a_val;709}710711void712ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)713{714715}716717void718allocate_initial_tls(Obj_Entry *list)719{720721/*722* Fix the size of the static TLS block by using the maximum723* offset allocated so far and adding a bit for dynamic modules to724* use.725*/726727tls_static_space = tls_last_offset + tls_last_size +728ld_static_tls_extra;729730_tcb_set(allocate_tls(list, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));731}732733void*734__tls_get_addr(tls_index* ti)735{736return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset +737TLS_DTV_OFFSET));738}739740741