elf32_machdep.c revision 338867
1214571Sdim/*- 2214571Sdim * Copyright 1996-1998 John D. Polstra. 3214571Sdim * All rights reserved. 4214571Sdim * 5214571Sdim * Redistribution and use in source and binary forms, with or without 6214571Sdim * modification, are permitted provided that the following conditions 7214571Sdim * are met: 8214571Sdim * 1. Redistributions of source code must retain the above copyright 9214571Sdim * notice, this list of conditions and the following disclaimer. 10214571Sdim * 2. Redistributions in binary form must reproduce the above copyright 11214571Sdim * notice, this list of conditions and the following disclaimer in the 12214571Sdim * documentation and/or other materials provided with the distribution. 13214571Sdim * 14214571Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15214571Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16214571Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17214571Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18214571Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19214571Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20214571Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21214571Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22214571Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23214571Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24214571Sdim * 25214571Sdim * $FreeBSD: stable/11/sys/powerpc/powerpc/elf32_machdep.c 338867 2018-09-21 20:40:37Z markj $ 26214571Sdim */ 27214571Sdim 28214571Sdim#include <sys/param.h> 29214571Sdim#include <sys/kernel.h> 30214571Sdim#include <sys/systm.h> 31214571Sdim 32214571Sdim#define __ELF_WORD_SIZE 32 33214571Sdim 34214571Sdim#include <sys/exec.h> 35214571Sdim#include <sys/imgact.h> 36214571Sdim#include <sys/malloc.h> 37214571Sdim#include <sys/proc.h> 38214571Sdim#include <sys/namei.h> 39214571Sdim#include <sys/fcntl.h> 40214571Sdim#include <sys/sysent.h> 41214571Sdim#include <sys/imgact_elf.h> 42214571Sdim#include <sys/syscall.h> 43214571Sdim#include <sys/signalvar.h> 44214571Sdim#include <sys/vnode.h> 45214571Sdim#include <sys/linker.h> 46214571Sdim 47214571Sdim#include <vm/vm.h> 48214571Sdim#include <vm/vm_param.h> 49214571Sdim 50214571Sdim#include <machine/altivec.h> 51214571Sdim#include <machine/cpu.h> 52214571Sdim#include <machine/elf.h> 53214571Sdim#include <machine/reg.h> 54214571Sdim#include <machine/md_var.h> 55214571Sdim 56214571Sdim#ifdef __powerpc64__ 57214571Sdim#include <compat/freebsd32/freebsd32_proto.h> 58214571Sdim#include <compat/freebsd32/freebsd32_util.h> 59214571Sdim 60214571Sdimextern const char *freebsd32_syscallnames[]; 61214571Sdim#endif 62214571Sdim 63214571Sdimstruct sysentvec elf32_freebsd_sysvec = { 64214571Sdim .sv_size = SYS_MAXSYSCALL, 65214571Sdim#ifdef __powerpc64__ 66214571Sdim .sv_table = freebsd32_sysent, 67214571Sdim#else 68214571Sdim .sv_table = sysent, 69214571Sdim#endif 70214571Sdim .sv_mask = 0, 71214571Sdim .sv_errsize = 0, 72214571Sdim .sv_errtbl = NULL, 73214571Sdim .sv_transtrap = NULL, 74214571Sdim .sv_fixup = __elfN(freebsd_fixup), 75214571Sdim .sv_sendsig = sendsig, 76214571Sdim .sv_sigcode = sigcode32, 77214571Sdim .sv_szsigcode = &szsigcode32, 78214571Sdim .sv_name = "FreeBSD ELF32", 79214571Sdim .sv_coredump = __elfN(coredump), 80214571Sdim .sv_imgact_try = NULL, 81214571Sdim .sv_minsigstksz = MINSIGSTKSZ, 82214571Sdim .sv_pagesize = PAGE_SIZE, 83214571Sdim .sv_minuser = VM_MIN_ADDRESS, 84214571Sdim .sv_stackprot = VM_PROT_ALL, 85214571Sdim#ifdef __powerpc64__ 86214571Sdim .sv_maxuser = VM_MAXUSER_ADDRESS, 87214571Sdim .sv_usrstack = FREEBSD32_USRSTACK, 88214571Sdim .sv_psstrings = FREEBSD32_PS_STRINGS, 89214571Sdim .sv_copyout_strings = freebsd32_copyout_strings, 90214571Sdim .sv_setregs = ppc32_setregs, 91214571Sdim .sv_syscallnames = freebsd32_syscallnames, 92214571Sdim#else 93214571Sdim .sv_maxuser = VM_MAXUSER_ADDRESS, 94214571Sdim .sv_usrstack = USRSTACK, 95214571Sdim .sv_psstrings = PS_STRINGS, 96214571Sdim .sv_copyout_strings = exec_copyout_strings, 97214571Sdim .sv_setregs = exec_setregs, 98214571Sdim .sv_syscallnames = syscallnames, 99214571Sdim#endif 100214571Sdim .sv_fixlimit = NULL, 101214571Sdim .sv_maxssiz = NULL, 102214571Sdim .sv_flags = SV_ABI_FREEBSD | SV_ILP32 | SV_SHP, 103214571Sdim .sv_set_syscall_retval = cpu_set_syscall_retval, 104214571Sdim .sv_fetch_syscall_args = cpu_fetch_syscall_args, 105214571Sdim .sv_shared_page_base = FREEBSD32_SHAREDPAGE, 106214571Sdim .sv_shared_page_len = PAGE_SIZE, 107214571Sdim .sv_schedtail = NULL, 108214571Sdim .sv_thread_detach = NULL, 109214571Sdim .sv_trap = NULL, 110214571Sdim}; 111214571SdimINIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); 112214571Sdim 113214571Sdimstatic Elf32_Brandinfo freebsd_brand_info = { 114214571Sdim .brand = ELFOSABI_FREEBSD, 115214571Sdim .machine = EM_PPC, 116214571Sdim .compat_3_brand = "FreeBSD", 117214571Sdim .emul_path = NULL, 118214571Sdim .interp_path = "/libexec/ld-elf.so.1", 119214571Sdim .sysvec = &elf32_freebsd_sysvec, 120214571Sdim#ifdef __powerpc64__ 121214571Sdim .interp_newpath = "/libexec/ld-elf32.so.1", 122214571Sdim#else 123214571Sdim .interp_newpath = NULL, 124214571Sdim#endif 125214571Sdim .brand_note = &elf32_freebsd_brandnote, 126214571Sdim .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE 127214571Sdim}; 128214571Sdim 129214571SdimSYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, 130214571Sdim (sysinit_cfunc_t) elf32_insert_brand_entry, 131214571Sdim &freebsd_brand_info); 132214571Sdim 133214571Sdimstatic Elf32_Brandinfo freebsd_brand_oinfo = { 134214571Sdim .brand = ELFOSABI_FREEBSD, 135214571Sdim .machine = EM_PPC, 136214571Sdim .compat_3_brand = "FreeBSD", 137214571Sdim .emul_path = NULL, 138214571Sdim .interp_path = "/usr/libexec/ld-elf.so.1", 139214571Sdim .sysvec = &elf32_freebsd_sysvec, 140214571Sdim .interp_newpath = NULL, 141214571Sdim .brand_note = &elf32_freebsd_brandnote, 142214571Sdim .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE 143214571Sdim}; 144214571Sdim 145214571SdimSYSINIT(oelf32, SI_SUB_EXEC, SI_ORDER_ANY, 146214571Sdim (sysinit_cfunc_t) elf32_insert_brand_entry, 147214571Sdim &freebsd_brand_oinfo); 148214571Sdim 149214571Sdimvoid elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase); 150214571Sdim 151214571Sdimvoid 152214571Sdimelf32_dump_thread(struct thread *td, void *dst, size_t *off) 153214571Sdim{ 154214571Sdim size_t len; 155214571Sdim struct pcb *pcb; 156214571Sdim 157214571Sdim len = 0; 158214571Sdim pcb = td->td_pcb; 159214571Sdim if (pcb->pcb_flags & PCB_VEC) { 160214571Sdim save_vec_nodrop(td); 161214571Sdim if (dst != NULL) { 162214571Sdim len += elf32_populate_note(NT_PPC_VMX, 163214571Sdim &pcb->pcb_vec, dst, 164214571Sdim sizeof(pcb->pcb_vec), NULL); 165214571Sdim } else 166214571Sdim len += elf32_populate_note(NT_PPC_VMX, NULL, NULL, 167214571Sdim sizeof(pcb->pcb_vec), NULL); 168214571Sdim } 169214571Sdim *off = len; 170214571Sdim} 171214571Sdim 172214571Sdim#ifndef __powerpc64__ 173214571Sdimbool 174214571Sdimelf_is_ifunc_reloc(Elf_Size r_info __unused) 175214571Sdim{ 176214571Sdim 177214571Sdim return (false); 178214571Sdim} 179214571Sdim 180214571Sdim/* Process one elf relocation with addend. */ 181214571Sdimstatic int 182214571Sdimelf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, 183214571Sdim int type, int local, elf_lookup_fn lookup) 184214571Sdim{ 185214571Sdim Elf_Addr *where; 186214571Sdim Elf_Half *hwhere; 187214571Sdim Elf_Addr addr; 188214571Sdim Elf_Addr addend; 189214571Sdim Elf_Word rtype, symidx; 190214571Sdim const Elf_Rela *rela; 191214571Sdim int error; 192214571Sdim 193214571Sdim switch (type) { 194214571Sdim case ELF_RELOC_REL: 195214571Sdim panic("PPC only supports RELA relocations"); 196214571Sdim break; 197214571Sdim case ELF_RELOC_RELA: 198214571Sdim rela = (const Elf_Rela *)data; 199214571Sdim where = (Elf_Addr *) ((uintptr_t)relocbase + rela->r_offset); 200214571Sdim hwhere = (Elf_Half *) ((uintptr_t)relocbase + rela->r_offset); 201214571Sdim addend = rela->r_addend; 202214571Sdim rtype = ELF_R_TYPE(rela->r_info); 203214571Sdim symidx = ELF_R_SYM(rela->r_info); 204214571Sdim break; 205214571Sdim default: 206214571Sdim panic("elf_reloc: unknown relocation mode %d\n", type); 207214571Sdim } 208214571Sdim 209214571Sdim switch (rtype) { 210214571Sdim 211214571Sdim case R_PPC_NONE: 212214571Sdim break; 213214571Sdim 214214571Sdim case R_PPC_ADDR32: /* word32 S + A */ 215214571Sdim error = lookup(lf, symidx, 1, &addr); 216214571Sdim if (error != 0) 217214571Sdim return -1; 218214571Sdim *where = elf_relocaddr(lf, addr + addend); 219214571Sdim break; 220214571Sdim 221214571Sdim case R_PPC_ADDR16_LO: /* #lo(S) */ 222214571Sdim error = lookup(lf, symidx, 1, &addr); 223214571Sdim if (error != 0) 224214571Sdim return -1; 225214571Sdim /* 226214571Sdim * addend values are sometimes relative to sections 227214571Sdim * (i.e. .rodata) in rela, where in reality they 228214571Sdim * are relative to relocbase. Detect this condition. 229214571Sdim */ 230214571Sdim if (addr > relocbase && addr <= (relocbase + addend)) 231214571Sdim addr = relocbase; 232214571Sdim addr = elf_relocaddr(lf, addr + addend); 233214571Sdim *hwhere = addr & 0xffff; 234214571Sdim break; 235214571Sdim 236214571Sdim case R_PPC_ADDR16_HA: /* #ha(S) */ 237214571Sdim error = lookup(lf, symidx, 1, &addr); 238214571Sdim if (error != 0) 239214571Sdim return -1; 240214571Sdim /* 241214571Sdim * addend values are sometimes relative to sections 242214571Sdim * (i.e. .rodata) in rela, where in reality they 243214571Sdim * are relative to relocbase. Detect this condition. 244214571Sdim */ 245214571Sdim if (addr > relocbase && addr <= (relocbase + addend)) 246214571Sdim addr = relocbase; 247214571Sdim addr = elf_relocaddr(lf, addr + addend); 248214571Sdim *hwhere = ((addr >> 16) + ((addr & 0x8000) ? 1 : 0)) 249214571Sdim & 0xffff; 250214571Sdim break; 251214571Sdim 252214571Sdim case R_PPC_RELATIVE: /* word32 B + A */ 253214571Sdim *where = elf_relocaddr(lf, relocbase + addend); 254214571Sdim break; 255214571Sdim 256214571Sdim default: 257214571Sdim printf("kldload: unexpected relocation type %d\n", 258214571Sdim (int) rtype); 259214571Sdim return -1; 260214571Sdim } 261214571Sdim return(0); 262214571Sdim} 263214571Sdim 264214571Sdimvoid 265214571Sdimelf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase) 266214571Sdim{ 267214571Sdim Elf_Rela *rela = NULL, *relalim; 268214571Sdim Elf_Addr relasz = 0; 269214571Sdim Elf_Addr *where; 270214571Sdim 271214571Sdim /* 272214571Sdim * Extract the rela/relasz values from the dynamic section 273214571Sdim */ 274214571Sdim for (; dynp->d_tag != DT_NULL; dynp++) { 275214571Sdim switch (dynp->d_tag) { 276214571Sdim case DT_RELA: 277214571Sdim rela = (Elf_Rela *)(relocbase+dynp->d_un.d_ptr); 278214571Sdim break; 279214571Sdim case DT_RELASZ: 280214571Sdim relasz = dynp->d_un.d_val; 281214571Sdim break; 282214571Sdim } 283214571Sdim } 284214571Sdim 285214571Sdim /* 286214571Sdim * Relocate these values 287214571Sdim */ 288214571Sdim relalim = (Elf_Rela *)((caddr_t)rela + relasz); 289214571Sdim for (; rela < relalim; rela++) { 290214571Sdim if (ELF_R_TYPE(rela->r_info) != R_PPC_RELATIVE) 291214571Sdim continue; 292214571Sdim where = (Elf_Addr *)(relocbase + rela->r_offset); 293214571Sdim *where = (Elf_Addr)(relocbase + rela->r_addend); 294214571Sdim } 295214571Sdim} 296214571Sdim 297214571Sdimint 298214571Sdimelf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, 299214571Sdim elf_lookup_fn lookup) 300214571Sdim{ 301214571Sdim 302214571Sdim return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); 303214571Sdim} 304214571Sdim 305214571Sdimint 306214571Sdimelf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, 307214571Sdim int type, elf_lookup_fn lookup) 308214571Sdim{ 309214571Sdim 310214571Sdim return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); 311214571Sdim} 312214571Sdim 313214571Sdimint 314214571Sdimelf_cpu_load_file(linker_file_t lf) 315214571Sdim{ 316214571Sdim /* Only sync the cache for non-kernel modules */ 317214571Sdim if (lf->id != 1) 318214571Sdim __syncicache(lf->address, lf->size); 319214571Sdim return (0); 320214571Sdim} 321214571Sdim 322214571Sdimint 323214571Sdimelf_cpu_unload_file(linker_file_t lf __unused) 324214571Sdim{ 325214571Sdim 326214571Sdim return (0); 327214571Sdim} 328214571Sdim#endif 329214571Sdim