Edit

IABSD.fr/src/libexec/ld.so/powerpc64/rtld_machine.c

Branch :

  • Show log

    Commit

  • Author : kettenis
    Date : 2020-07-18 16:41:43
    Hash : 92e19e77
    Message : Use the same names as the 64-bit PowerPC ELF ABI for the relocations.

  • libexec/ld.so/powerpc64/rtld_machine.c
  • /*	$OpenBSD: rtld_machine.c,v 1.5 2020/07/18 16:41:43 kettenis Exp $ */
    
    /*
     * Copyright (c) 1999 Dale Rahn
     *
     * 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/mman.h>
    #include <sys/syscall.h>
    #include <sys/unistd.h>
    
    #include <nlist.h>
    #include <link.h>
    
    #include "syscall.h"
    #include "archdep.h"
    #include "resolve.h"
    
    #define	DT_PROC(n)	((n) - DT_LOPROC + DT_NUM)
    
    int64_t pcookie __attribute__((section(".openbsd.randomdata"))) __dso_hidden;
    
    /* relocation bits */
    #define B24_VALID_RANGE(x) \
        ((((x) & 0xfe000000) == 0x00000000) || (((x) &  0xfe000000) == 0xfe000000))
    
    void _dl_bind_start(void); /* XXX */
    Elf_Addr _dl_bind(elf_object_t *object, int reloff);
    
    int
    _dl_md_reloc(elf_object_t *object, int rel, int relasz)
    {
    	int	i;
    	int	numrela;
    	long	relrel;
    	int	fails = 0;
    	Elf_Addr loff;
    	Elf_RelA  *relas;
    	/* for jmp table relocations */
    	Elf_Addr prev_value = 0, prev_ooff = 0;
    	const Elf_Sym *prev_sym = NULL;
    
    	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("relcount > numrel: %ld > %d", relrel, numrela);
    
    	/* 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);
    		*r_addr = loff + relas->r_addend;
    	}
    	for (; i < numrela; i++, relas++) {
    		Elf_Addr *r_addr = (Elf_Addr *)(relas->r_offset + loff);
    		const Elf_Sym *sym;
    		const char *symn;
    		int type;
    
    		if (ELF_R_SYM(relas->r_info) == 0xffffff)
    			continue;
    
    		type = ELF_R_TYPE(relas->r_info);
    
    		if (type == R_PPC64_JMP_SLOT && rel != DT_JMPREL)
    			continue;
    
    		sym = object->dyn.symtab;
    		sym += ELF_R_SYM(relas->r_info);
    		symn = object->dyn.strtab + sym->st_name;
    
    		if (ELF_R_SYM(relas->r_info) &&
    		    !(ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
    		    ELF_ST_TYPE (sym->st_info) == STT_NOTYPE) &&
    		    sym != prev_sym) {
    			struct sym_res sr;
    
    			sr = _dl_find_symbol(symn,
    			    SYM_SEARCH_ALL|SYM_WARNNOTFOUND|
    			    ((type == R_PPC64_JMP_SLOT) ?
    			    SYM_PLT:SYM_NOTPLT), sym, object);
    
    			if (sr.sym == NULL) {
    				if (ELF_ST_BIND(sym->st_info) != STB_WEAK)
    					fails++;
    				continue;
    			}
    			prev_sym = sym;
    			prev_value = sr.sym->st_value;
    			prev_ooff = sr.obj->obj_base;
    		}
    
    		switch (type) {
    		case R_PPC64_ADDR64:
    			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
    			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
    			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
    				*r_addr = prev_ooff + relas->r_addend;
    			} else {
    				*r_addr = prev_ooff + prev_value +
    				    relas->r_addend;
    			}
    			break;
    		case R_PPC64_RELATIVE:
    			if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
    			    (ELF_ST_TYPE(sym->st_info) == STT_SECTION ||
    			    ELF_ST_TYPE(sym->st_info) == STT_NOTYPE) ) {
    				*r_addr = loff + relas->r_addend;
    			} else {
    				*r_addr = loff + prev_value +
    				    relas->r_addend;
    			}
    			break;
    		/*
    		 * For Secure-PLT, RELOC_JMP_SLOT simply sets PLT
    		 * slots similarly to how RELOC_GLOB_DAT updates GOT
    		 * slots.
    		 */
    		case R_PPC64_JMP_SLOT:
    		case R_PPC64_GLOB_DAT:
    			*r_addr = prev_ooff + prev_value + relas->r_addend;
    			break;
    #if 0
    		/* should not be supported ??? */
    		case RELOC_REL24:
    		    {
    			Elf_Addr val = prev_ooff + prev_value +
    			    relas->r_addend - (Elf_Addr)r_addr;
    			if (!B24_VALID_RANGE(val)) {
    				/* invalid offset */
    				_dl_die("%s: invalid %s offset %llx at %p",
    				    object->load_name, "REL24", val,
    				    (void *)r_addr);
    			}
    			val &= ~0xfc000003;
    			val |= (*r_addr & 0xfc000003);
    			*r_addr = val;
    
    			_dl_dcbf(r_addr);
    		    }
    		break;
    #endif
    #if 0
    		case RELOC_16_LO:
    		    {
    			Elf_Addr val;
    
    			val = loff + relas->r_addend;
    			*(Elf_Half *)r_addr = val;
    
    			_dl_dcbf(r_addr);
    		    }
    		break;
    #endif
    #if 0
    		case RELOC_16_HI:
    		    {
    			Elf_Addr val;
    
    			val = loff + relas->r_addend;
    			*(Elf_Half *)r_addr = (val >> 16);
    
    			_dl_dcbf(r_addr);
    		    }
    		break;
    #endif
    #if 0
    		case RELOC_16_HA:
    		    {
    			Elf_Addr val;
    
    			val = loff + relas->r_addend;
    			*(Elf_Half *)r_addr = ((val + 0x8000) >> 16);
    
    			_dl_dcbf(r_addr);
    		    }
    		break;
    #endif
    #if 0
    		case RELOC_REL14_TAKEN:
    			/* val |= 1 << (31-10) XXX? */
    		case RELOC_REL14:
    		case RELOC_REL14_NTAKEN:
    		    {
    			Elf_Addr val = prev_ooff + prev_value +
    			    relas->r_addend - (Elf_Addr)r_addr;
    			if (((val & 0xffff8000) != 0) &&
    			    ((val & 0xffff8000) != 0xffff8000)) {
    				/* invalid offset */
    				_dl_die("%s: invalid %s offset %llx at %p",
    				    object->load_name, "REL14", val,
    				    (void *)r_addr);
    			}
    			val &= ~0xffff0003;
    			val |= (*r_addr & 0xffff0003);
    			*r_addr = val;
    			_dl_dcbf(r_addr);
    		    }
    			break;
    #endif
    		case R_PPC64_COPY:
    		{
    			struct sym_res sr;
    			/*
    			 * 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.
    			 */
    			sr = _dl_find_symbol(symn,
    			    SYM_SEARCH_OTHER|SYM_WARNNOTFOUND| SYM_NOTPLT,
    			    sym, object);
    			if (sr.sym != NULL) {
    				_dl_bcopy((void *)(sr.obj->obj_base + sr.sym->st_value),
    				    r_addr, sym->st_size);
    			} else
    				fails++;
    		}
    			break;
    		case R_PPC64_NONE:
    			break;
    
    		default:
    			_dl_die("%s: unsupported relocation '%s' %lld at %p\n",
    			    object->load_name, symn,
    			    ELF_R_TYPE(relas->r_info), (void *)r_addr );
    		}
    	}
    
    	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)
    {
    	int fails = 0;
    
    	if (object->Dyn.info[DT_PLTREL] != DT_RELA)
    		return 0;
    
    	if (!lazy) {
    		fails = _dl_md_reloc(object, DT_JMPREL, DT_PLTRELSZ);
    	} else {
    		Elf_Addr *plt;
    		int numplt, n;
    
    		/* Relocate processor-specific tags. */
    		object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] += object->obj_base;
    
    		if (object->Dyn.info[DT_PLTREL] != DT_RELA)
    			_dl_die(" bad relocation type PLTREL not RELA");
    
    		plt = (Elf_Addr *)
    		   (Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
    		numplt = object->Dyn.info[DT_PLTRELSZ] / sizeof(Elf_RelA);
    		plt[0] = (uint64_t)_dl_bind_start;
    		plt[1] = (uint64_t)object;
    		for (n = 0; n < numplt; n++) {
    			plt[n + 2] = object->Dyn.info[DT_PROC(DT_PPC64_GLINK)] +
    			    n * 4 + 32;
    		}
    	}
    
    	return fails;
    }
    
    Elf_Addr
    _dl_bind(elf_object_t *object, int relidx)
    {
    	const Elf_Sym *sym;
    	struct sym_res sr;
    	const char *symn;
    	Elf_RelA *relas;
    	Elf_Addr *plttable;
    	int64_t cookie = pcookie;
    	struct {
    		struct __kbind param;
    		Elf_Addr newval;
    	} buf;
    
    	relas = ((Elf_RelA *)object->Dyn.info[DT_JMPREL]) + relidx;
    
    	sym = object->dyn.symtab;
    	sym += ELF_R_SYM(relas->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;
    
    	if (__predict_false(sr.obj->traced) && _dl_trace_plt(sr.obj, symn))
    		return buf.newval;
    
    	plttable = (Elf_Addr *)(Elf_RelA *)(object->Dyn.info[DT_PLTGOT]);
    	buf.param.kb_addr = &plttable[relidx + 2];
    	buf.param.kb_size = sizeof(Elf_Addr);
    
    	{
    		register long syscall_num __asm("r0") = SYS_kbind;
    		register void *arg1 __asm("r3") = &buf.param;
    		register long  arg2 __asm("r4") = sizeof(struct __kbind) +
    		    sizeof(Elf_Addr);
    		register long  arg3 __asm("r5") = cookie;
    
    		__asm volatile("sc" : "+r" (syscall_num), "+r" (arg1),
    		    "+r" (arg2) : "r" (arg3) : "cc", "memory");
    	}
    
    	return buf.newval;
    }