root/libexec/ld.so/alpha/rtld_machine.c
/*      $OpenBSD: rtld_machine.c,v 1.71 2022/01/08 06:49:41 guenther Exp $ */

/*
 * Copyright (c) 1999 Dale Rahn
 * Copyright (c) 2001 Niklas Hallqvist
 * Copyright (c) 2001 Artur Grabowski
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#define _DYN_LOADER

#include <sys/types.h>
#include <sys/exec_elf.h>
#include <sys/syscall.h>
#include <sys/unistd.h>

#include <machine/pal.h>
#include <machine/reloc.h>

#include "util.h"
#include "resolve.h"

#define DT_PROC(n)      ((n) - DT_LOPROC + DT_NUM)

int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;

int
_dl_md_reloc(elf_object_t *object, int rel, int relasz)
{
        long    i;
        long    numrela;
        long    relrel;
        int     fails = 0;
        Elf_Addr loff;
        Elf_Addr prev_value = 0;
        const Elf_Sym *prev_sym = NULL;
        Elf_RelA  *relas;

        loff = object->obj_base;
        numrela = object->Dyn.info[relasz] / sizeof(Elf_RelA);
        relrel = rel == DT_RELA ? object->relacount : 0;
        relas = (Elf_RelA *)(object->Dyn.info[rel]);

        if (relas == NULL)
                return 0;

        if (relrel > numrela)
                _dl_die("relacount > numrel: %ld > %ld", relrel, numrela);

        if (! object->Dyn.info[DT_PROC(DT_ALPHA_PLTRO)])
                _dl_die("unsupported insecure PLT object");

        /* tight loop for leading RELATIVE relocs */
        for (i = 0; i < relrel; i++, relas++) {
                Elf_Addr *r_addr;

                r_addr = (Elf_Addr *)(relas->r_offset + loff);

                /* Handle unaligned RELATIVE relocs */
                if ((((Elf_Addr)r_addr) & 0x7) != 0) {
                        Elf_Addr tmp;
                        _dl_bcopy(r_addr, &tmp, sizeof(Elf_Addr));
                        tmp += loff;
                        _dl_bcopy(&tmp, r_addr, sizeof(Elf_Addr));
                } else
                        *r_addr += loff;
        }
        for (; i < numrela; i++, relas++) {
                Elf_Addr *r_addr;
                struct sym_res sr;
                const Elf_Sym *sym;
                const char *symn;

                r_addr = (Elf_Addr *)(relas->r_offset + loff);

                if (ELF_R_SYM(relas->r_info) == 0xffffffff)
                        continue;


                sym = object->dyn.symtab;
                sym += ELF_R_SYM(relas->r_info);
                symn = object->dyn.strtab + sym->st_name;

                switch (ELF_R_TYPE(relas->r_info)) {
                case R_TYPE(REFQUAD):
                        sr = _dl_find_symbol(symn,
                            SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
                            sym, object);
                        if (sr.sym == NULL)
                                goto resolve_failed;
                        *r_addr += sr.obj->obj_base + sr.sym->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):
                        sr = _dl_find_symbol(symn,
                            SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
                            sym, object);
                        if (sr.sym == NULL)
                                goto resolve_failed;
                        *r_addr = sr.obj->obj_base + sr.sym->st_value +
                            relas->r_addend;
                        break;
                case R_TYPE(GLOB_DAT):
                        if (sym == prev_sym) {
                                *r_addr = prev_value + relas->r_addend;
                                break;
                        }
                        sr = _dl_find_symbol(symn,
                            SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_NOTPLT,
                            sym, object);
                        if (sr.sym == NULL)
                                goto resolve_failed;
                        prev_sym = sym;
                        prev_value = sr.obj->obj_base + sr.sym->st_value;
                        *r_addr = prev_value + relas->r_addend;
                        break;
                case R_TYPE(NONE):
                        break;
                default:
                        _dl_die("%s: unsupported relocation '%s' %lld at %p",
                            object->load_name, symn,
                            ELF_R_TYPE(relas->r_info), (void *)r_addr);
                }
                continue;
resolve_failed:
                if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
                        fails++;
        }
        __asm volatile("imb" : : : "memory");

        return fails;
}

/*
 * Resolve a symbol at run-time.
 */
Elf_Addr
_dl_bind(elf_object_t *object, int reloff)
{
        Elf_RelA *rela;
        struct sym_res sr;
        const Elf_Sym *sym;
        const char *symn;
        uint64_t cookie = pcookie;
        struct {
                struct __kbind param;
                Elf_Addr newval;
        } buf;

        rela = (Elf_RelA *)(object->Dyn.info[DT_JMPREL] + reloff);

        sym = object->dyn.symtab;
        sym += ELF_R_SYM(rela->r_info);
        symn = object->dyn.strtab + sym->st_name;

        sr = _dl_find_symbol(symn, SYM_SEARCH_ALL|SYM_WARNNOTFOUND|SYM_PLT,
            sym, object);
        if (sr.sym == NULL)
                _dl_die("lazy binding failed!");

        buf.newval = sr.obj->obj_base + sr.sym->st_value + rela->r_addend;

        if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
                return buf.newval;

        buf.param.kb_addr = (Elf_Addr *)(object->obj_base + rela->r_offset);
        buf.param.kb_size = sizeof(Elf_Addr);

        /* directly code the syscall, so that it's actually inline here */
        {
                register long syscall_num __asm("$0") /* v0 */ = SYS_kbind;
                register void *arg1 __asm("$16") /* a0 */ = &buf;
                register long  arg2 __asm("$17") /* a1 */ = sizeof(buf);
                register long  arg3 __asm("$18") /* a2 */ = cookie;

                __asm volatile( "call_pal %1" : "+r" (syscall_num)
                    : "i" (PAL_OSF1_callsys), "r" (arg1), "r" (arg2),
                    "r" (arg3) : "$19", "$20", "memory");
        }

        return buf.newval;
}

void _dl_bind_start(void) __dso_hidden; /* XXX */

/*
 *      Relocate the Global Offset Table (GOT).
 */
int
_dl_md_reloc_got(elf_object_t *object, int lazy)
{
        int     fails = 0;
        Elf_Addr *pltgot;

        if (object->Dyn.info[DT_PLTREL] != DT_RELA)
                return 0;

        pltgot = (Elf_Addr *)object->Dyn.info[DT_PLTGOT];

        if (!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;
                        }
                }
                pltgot[0] = (Elf_Addr)_dl_bind_start;
                pltgot[1] = (Elf_Addr)object;
        }

        return fails;
}