Path: blob/main/libexec/rtld-elf/powerpc64/reloc.c
103970 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;329SymCache *cache;330int bytes = obj->dynsymcount * sizeof(SymCache);331int r = -1;332333/*334* The dynamic loader may be called from a thread, we have335* limited amounts of stack available so we cannot use alloca().336*/337if (obj != obj_rtld) {338cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON,339-1, 0);340if (cache == MAP_FAILED)341cache = NULL;342} else343cache = NULL;344345/*346* From the SVR4 PPC ABI:347* "The PowerPC family uses only the Elf32_Rela relocation348* entries with explicit addends."349*/350relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);351for (rela = obj->rela; rela < relalim; rela++) {352if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags,353lockstate) < 0)354goto done;355}356r = 0;357done:358if (cache)359munmap(cache, bytes);360return (r);361}362363364/*365* Initialise a PLT slot to the resolving trampoline366*/367static int368reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela)369{370Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset);371long reloff;372373reloff = rela - obj->pltrela;374375dbg(" reloc_plt_object: where=%p,reloff=%lx,glink=%#lx", (void *)where,376reloff, obj->glink);377378#if !defined(_CALL_ELF) || _CALL_ELF == 1379/* Glink code is 3 instructions after the first 32k, 2 before */380*where = (Elf_Addr)obj->glink + 32 +3818*((reloff < 0x8000) ? reloff : 0x8000) +38212*((reloff < 0x8000) ? 0 : (reloff - 0x8000));383#else384/* 64-Bit ELF V2 ABI Specification, sec. 4.2.5.3. */385*where = (Elf_Addr)obj->glink + 4*reloff + 32;386#endif387388return (0);389}390391/*392* Process the PLT relocations.393*/394int395reloc_plt(Obj_Entry *obj, int flags __unused, RtldLockState *lockstate __unused)396{397const Elf_Rela *relalim;398const Elf_Rela *rela;399400if (obj->pltrelasize != 0) {401relalim = (const Elf_Rela *)((const char *)obj->pltrela +402obj->pltrelasize);403for (rela = obj->pltrela; rela < relalim; rela++) {404405#if defined(_CALL_ELF) && _CALL_ELF == 2406if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {407dbg("ABI violation - found IRELATIVE in the PLT.");408obj->irelative = true;409continue;410}411#endif412/*413* PowerPC(64) .rela.plt is composed of an array of414* R_PPC_JMP_SLOT relocations. Unlike other platforms,415* this is the ONLY relocation type that is valid here.416*/417assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);418419if (reloc_plt_object(obj, rela) < 0) {420return (-1);421}422}423}424425return (0);426}427428/*429* LD_BIND_NOW was set - force relocation for all jump slots430*/431int432reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate)433{434const Obj_Entry *defobj;435const Elf_Rela *relalim;436const Elf_Rela *rela;437const Elf_Sym *def;438Elf_Addr *where;439Elf_Addr target;440441relalim = (const Elf_Rela *)((const char *)obj->pltrela +442obj->pltrelasize);443for (rela = obj->pltrela; rela < relalim; rela++) {444/* This isn't actually a jump slot, ignore it. */445if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE)446continue;447assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT);448where = (Elf_Addr *)(obj->relocbase + rela->r_offset);449def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,450SYMLOOK_IN_PLT | flags, NULL, lockstate);451if (def == NULL) {452dbg("reloc_jmpslots: sym not found");453return (-1);454}455456target = (Elf_Addr)(defobj->relocbase + def->st_value);457458if (def == &sym_zero) {459/* Zero undefined weak symbols */460#if !defined(_CALL_ELF) || _CALL_ELF == 1461bzero(where, sizeof(struct funcdesc));462#else463*where = 0;464#endif465} else {466if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) {467/* LD_BIND_NOW, ifunc in shared lib.*/468obj->gnu_ifunc = true;469continue;470}471reloc_jmpslot(where, target, defobj, obj,472(const Elf_Rel *) rela);473}474}475476obj->jmpslots_done = true;477478return (0);479}480481482/*483* Update the value of a PLT jump slot.484*/485Elf_Addr486reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj __unused,487const Obj_Entry *obj __unused, const Elf_Rel *rel __unused)488{489490/*491* At the PLT entry pointed at by `wherep', construct492* a direct transfer to the now fully resolved function493* address.494*/495496#if !defined(_CALL_ELF) || _CALL_ELF == 1497dbg(" reloc_jmpslot: where=%p, target=%p (%#lx + %#lx)",498(void *)wherep, (void *)target, *(Elf_Addr *)target,499(Elf_Addr)defobj->relocbase);500501if (ld_bind_not)502goto out;503504/*505* For the trampoline, the second two elements of the function506* descriptor are unused, so we are fine replacing those at any time507* with the real ones with no thread safety implications. However, we508* need to make sure the main entry point pointer ([0]) is seen to be509* modified *after* the second two elements. This can't be done in510* general, since there are no barriers in the reading code, but put in511* some isyncs to at least make it a little better.512*/513memcpy(wherep, (void *)target, sizeof(struct funcdesc));514wherep[2] = ((Elf_Addr *)target)[2];515wherep[1] = ((Elf_Addr *)target)[1];516__asm __volatile ("isync" : : : "memory");517wherep[0] = ((Elf_Addr *)target)[0];518__asm __volatile ("isync" : : : "memory");519520if (((struct funcdesc *)(wherep))->addr < (Elf_Addr)defobj->relocbase) {521/*522* It is possible (LD_BIND_NOW) that the function523* descriptor we are copying has not yet been relocated.524* If this happens, fix it. Don't worry about threading in525* this case since LD_BIND_NOW makes it irrelevant.526*/527528((struct funcdesc *)(wherep))->addr +=529(Elf_Addr)defobj->relocbase;530((struct funcdesc *)(wherep))->toc +=531(Elf_Addr)defobj->relocbase;532}533#else534dbg(" reloc_jmpslot: where=%p, target=%p", (void *)wherep,535(void *)target);536537assert(target >= (Elf_Addr)defobj->relocbase);538539if (ld_bind_not)540goto out;541542if (*wherep != target)543*wherep = target;544545#endif546out:547548return (target);549}550551int552reloc_iresolve(Obj_Entry *obj,553struct Struct_RtldLockState *lockstate)554{555/*556* Since PLT slots on PowerPC64 are always R_PPC_JMP_SLOT,557* R_PPC_IRELATIVE is in RELA.558*/559#if !defined(_CALL_ELF) || _CALL_ELF == 1560(void)(obj);561(void)(lockstate);562/* XXX not implemented */563return (0);564#else565const Elf_Rela *relalim;566const Elf_Rela *rela;567Elf_Addr *where, target, *ptr;568569if (!obj->irelative)570return (0);571572relalim = (const Elf_Rela *)((const char *)obj->rela + obj->relasize);573for (rela = obj->rela; rela < relalim; rela++) {574if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {575ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);576where = (Elf_Addr *)(obj->relocbase + rela->r_offset);577578lock_release(rtld_bind_lock, lockstate);579target = call_ifunc_resolver(ptr);580wlock_acquire(rtld_bind_lock, lockstate);581582*where = target;583}584}585/*586* XXX Remove me when lld is fixed!587* LLD currently makes illegal relocations in the PLT.588*/589relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);590for (rela = obj->pltrela; rela < relalim; rela++) {591if (ELF_R_TYPE(rela->r_info) == R_PPC_IRELATIVE) {592ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend);593where = (Elf_Addr *)(obj->relocbase + rela->r_offset);594595lock_release(rtld_bind_lock, lockstate);596target = call_ifunc_resolver(ptr);597wlock_acquire(rtld_bind_lock, lockstate);598599*where = target;600}601}602603obj->irelative = false;604return (0);605#endif606}607608int609reloc_gnu_ifunc(Obj_Entry *obj __unused, int flags __unused,610struct Struct_RtldLockState *lockstate __unused)611{612#if !defined(_CALL_ELF) || _CALL_ELF == 1613_rtld_error("reloc_gnu_ifunc(): Not implemented!");614/* XXX not implemented */615return (-1);616#else617618const Elf_Rela *relalim;619const Elf_Rela *rela;620Elf_Addr *where, target;621const Elf_Sym *def;622const Obj_Entry *defobj;623624if (!obj->gnu_ifunc)625return (0);626relalim = (const Elf_Rela *)((const char *)obj->pltrela + obj->pltrelasize);627for (rela = obj->pltrela; rela < relalim; rela++) {628if (ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT) {629where = (Elf_Addr *)(obj->relocbase + rela->r_offset);630def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj,631SYMLOOK_IN_PLT | flags, NULL, lockstate);632if (def == NULL)633return (-1);634if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC)635continue;636lock_release(rtld_bind_lock, lockstate);637target = (Elf_Addr)rtld_resolve_ifunc(defobj, def);638wlock_acquire(rtld_bind_lock, lockstate);639reloc_jmpslot(where, target, defobj, obj,640(const Elf_Rel *)rela);641}642}643obj->gnu_ifunc = false;644return (0);645#endif646}647648int649reloc_iresolve_nonplt(Obj_Entry *obj __unused,650struct Struct_RtldLockState *lockstate __unused)651{652return (0);653}654655void656init_pltgot(Obj_Entry *obj)657{658Elf_Addr *pltcall;659660pltcall = obj->pltgot;661662if (pltcall == NULL) {663return;664}665666#if defined(_CALL_ELF) && _CALL_ELF == 2667pltcall[0] = (Elf_Addr)&_rtld_bind_start;668pltcall[1] = (Elf_Addr)obj;669#else670memcpy(pltcall, _rtld_bind_start, sizeof(struct funcdesc));671pltcall[2] = (Elf_Addr)obj;672#endif673}674675/*676* Actual values are 32 bit.677*/678u_long cpu_features;679u_long cpu_features2;680681void682powerpc64_abi_variant_hook(Elf_Auxinfo** aux_info)683{684/*685* Since aux_info[] is easier to work with than aux, go ahead and686* initialize cpu_features / cpu_features2.687*/688cpu_features = -1UL;689cpu_features2 = -1UL;690if (aux_info[AT_HWCAP] != NULL)691cpu_features = (uint32_t)aux_info[AT_HWCAP]->a_un.a_val;692if (aux_info[AT_HWCAP2] != NULL)693cpu_features2 = (uint32_t)aux_info[AT_HWCAP2]->a_un.a_val;694}695696void697ifunc_init(Elf_Auxinfo *aux_info[__min_size(AT_COUNT)] __unused)698{699700}701702void703allocate_initial_tls(Obj_Entry *list)704{705706/*707* Fix the size of the static TLS block by using the maximum708* offset allocated so far and adding a bit for dynamic modules to709* use.710*/711712tls_static_space = tls_last_offset + tls_last_size +713ld_static_tls_extra;714715_tcb_set(allocate_tls(list, NULL, TLS_TCB_SIZE, TLS_TCB_ALIGN));716}717718void*719__tls_get_addr(tls_index* ti)720{721return (tls_get_addr_common(_tcb_get(), ti->ti_module, ti->ti_offset +722TLS_DTV_OFFSET));723}724725void726arch_fix_auxv(Elf_Auxinfo *aux, Elf_Auxinfo *aux_info[])727{728Elf_Auxinfo *auxp;729730for (auxp = aux; auxp->a_type != AT_NULL; auxp++) {731if (auxp->a_type == 23) /* AT_STACKPROT */732return;733}734735/* Remap from old-style auxv numbers. */736aux_info[23] = aux_info[21]; /* AT_STACKPROT */737aux_info[21] = aux_info[19]; /* AT_PAGESIZESLEN */738aux_info[19] = aux_info[17]; /* AT_NCPUS */739aux_info[17] = aux_info[15]; /* AT_CANARYLEN */740aux_info[15] = aux_info[13]; /* AT_EXECPATH */741aux_info[13] = NULL; /* AT_GID */742743aux_info[20] = aux_info[18]; /* AT_PAGESIZES */744aux_info[18] = aux_info[16]; /* AT_OSRELDATE */745aux_info[16] = aux_info[14]; /* AT_CANARY */746aux_info[14] = NULL; /* AT_EGID */747}748749750