elf32_machdep.c revision 288000
190075Sobrien/*- 290075Sobrien * Copyright 1996-1998 John D. Polstra. 390075Sobrien * All rights reserved. 490075Sobrien * 590075Sobrien * Redistribution and use in source and binary forms, with or without 690075Sobrien * modification, are permitted provided that the following conditions 790075Sobrien * are met: 890075Sobrien * 1. Redistributions of source code must retain the above copyright 990075Sobrien * notice, this list of conditions and the following disclaimer. 1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1190075Sobrien * notice, this list of conditions and the following disclaimer in the 1290075Sobrien * documentation and/or other materials provided with the distribution. 1390075Sobrien * 1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1590075Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1690075Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1790075Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1890075Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1990075Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2090075Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2190075Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2290075Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2390075Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2490075Sobrien * 2590075Sobrien * $FreeBSD: head/sys/powerpc/powerpc/elf32_machdep.c 288000 2015-09-20 01:27:59Z kib $ 2690075Sobrien */ 2790075Sobrien 2890075Sobrien#include <sys/param.h> 2990075Sobrien#include <sys/kernel.h> 3090075Sobrien#include <sys/systm.h> 3190075Sobrien 3290075Sobrien#define __ELF_WORD_SIZE 32 3390075Sobrien 3490075Sobrien#include <sys/exec.h> 3590075Sobrien#include <sys/imgact.h> 3690075Sobrien#include <sys/malloc.h> 3790075Sobrien#include <sys/proc.h> 3890075Sobrien#include <sys/namei.h> 3990075Sobrien#include <sys/fcntl.h> 4090075Sobrien#include <sys/sysent.h> 4190075Sobrien#include <sys/imgact_elf.h> 4290075Sobrien#include <sys/syscall.h> 4390075Sobrien#include <sys/signalvar.h> 4490075Sobrien#include <sys/vnode.h> 4590075Sobrien#include <sys/linker.h> 4690075Sobrien 4790075Sobrien#include <vm/vm.h> 4890075Sobrien#include <vm/vm_param.h> 4990075Sobrien 5090075Sobrien#include <machine/altivec.h> 5190075Sobrien#include <machine/cpu.h> 5290075Sobrien#include <machine/elf.h> 5390075Sobrien#include <machine/reg.h> 5490075Sobrien#include <machine/md_var.h> 5590075Sobrien 5690075Sobrien#ifdef __powerpc64__ 5790075Sobrien#include <compat/freebsd32/freebsd32_proto.h> 5890075Sobrien#include <compat/freebsd32/freebsd32_util.h> 5990075Sobrien 6090075Sobrienextern const char *freebsd32_syscallnames[]; 6190075Sobrien#endif 6290075Sobrien 6390075Sobrienstruct sysentvec elf32_freebsd_sysvec = { 6490075Sobrien .sv_size = SYS_MAXSYSCALL, 6590075Sobrien#ifdef __powerpc64__ 6690075Sobrien .sv_table = freebsd32_sysent, 6790075Sobrien#else 6890075Sobrien .sv_table = sysent, 6990075Sobrien#endif 7090075Sobrien .sv_mask = 0, 7190075Sobrien .sv_sigsize = 0, 7290075Sobrien .sv_sigtbl = NULL, 7390075Sobrien .sv_errsize = 0, 7490075Sobrien .sv_errtbl = NULL, 7590075Sobrien .sv_transtrap = NULL, 7690075Sobrien .sv_fixup = __elfN(freebsd_fixup), 7790075Sobrien .sv_sendsig = sendsig, 7890075Sobrien .sv_sigcode = sigcode32, 7990075Sobrien .sv_szsigcode = &szsigcode32, 8090075Sobrien .sv_prepsyscall = NULL, 8190075Sobrien .sv_name = "FreeBSD ELF32", 8290075Sobrien .sv_coredump = __elfN(coredump), 8390075Sobrien .sv_imgact_try = NULL, 8490075Sobrien .sv_minsigstksz = MINSIGSTKSZ, 8590075Sobrien .sv_pagesize = PAGE_SIZE, 8690075Sobrien .sv_minuser = VM_MIN_ADDRESS, 8790075Sobrien .sv_stackprot = VM_PROT_ALL, 8890075Sobrien#ifdef __powerpc64__ 8990075Sobrien .sv_maxuser = VM_MAXUSER_ADDRESS, 9090075Sobrien .sv_usrstack = FREEBSD32_USRSTACK, 9190075Sobrien .sv_psstrings = FREEBSD32_PS_STRINGS, 9290075Sobrien .sv_copyout_strings = freebsd32_copyout_strings, 9390075Sobrien .sv_setregs = ppc32_setregs, 9490075Sobrien .sv_syscallnames = freebsd32_syscallnames, 9590075Sobrien#else 9690075Sobrien .sv_maxuser = VM_MAXUSER_ADDRESS, 9790075Sobrien .sv_usrstack = USRSTACK, 9890075Sobrien .sv_psstrings = PS_STRINGS, 9990075Sobrien .sv_copyout_strings = exec_copyout_strings, 10090075Sobrien .sv_setregs = exec_setregs, 10190075Sobrien .sv_syscallnames = syscallnames, 10290075Sobrien#endif 10390075Sobrien .sv_fixlimit = NULL, 10490075Sobrien .sv_maxssiz = NULL, 10590075Sobrien .sv_flags = SV_ABI_FREEBSD | SV_ILP32 | SV_SHP, 10690075Sobrien .sv_set_syscall_retval = cpu_set_syscall_retval, 10790075Sobrien .sv_fetch_syscall_args = cpu_fetch_syscall_args, 10890075Sobrien .sv_shared_page_base = FREEBSD32_SHAREDPAGE, 10990075Sobrien .sv_shared_page_len = PAGE_SIZE, 11090075Sobrien .sv_schedtail = NULL, 11190075Sobrien .sv_thread_detach = NULL, 11290075Sobrien}; 11390075SobrienINIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec); 11490075Sobrien 11590075Sobrienstatic Elf32_Brandinfo freebsd_brand_info = { 11690075Sobrien .brand = ELFOSABI_FREEBSD, 11790075Sobrien .machine = EM_PPC, 11890075Sobrien .compat_3_brand = "FreeBSD", 11990075Sobrien .emul_path = NULL, 12090075Sobrien .interp_path = "/libexec/ld-elf.so.1", 12190075Sobrien .sysvec = &elf32_freebsd_sysvec, 12290075Sobrien#ifdef __powerpc64__ 12390075Sobrien .interp_newpath = "/libexec/ld-elf32.so.1", 12490075Sobrien#else 12590075Sobrien .interp_newpath = NULL, 12690075Sobrien#endif 12790075Sobrien .brand_note = &elf32_freebsd_brandnote, 12890075Sobrien .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE 12990075Sobrien}; 13090075Sobrien 13190075SobrienSYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST, 13290075Sobrien (sysinit_cfunc_t) elf32_insert_brand_entry, 13390075Sobrien &freebsd_brand_info); 13490075Sobrien 13590075Sobrienstatic Elf32_Brandinfo freebsd_brand_oinfo = { 13690075Sobrien .brand = ELFOSABI_FREEBSD, 13790075Sobrien .machine = EM_PPC, 13890075Sobrien .compat_3_brand = "FreeBSD", 13990075Sobrien .emul_path = NULL, 14090075Sobrien .interp_path = "/usr/libexec/ld-elf.so.1", 14190075Sobrien .sysvec = &elf32_freebsd_sysvec, 14290075Sobrien .interp_newpath = NULL, 14390075Sobrien .brand_note = &elf32_freebsd_brandnote, 14490075Sobrien .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE 14590075Sobrien}; 14690075Sobrien 14790075SobrienSYSINIT(oelf32, SI_SUB_EXEC, SI_ORDER_ANY, 14890075Sobrien (sysinit_cfunc_t) elf32_insert_brand_entry, 14990075Sobrien &freebsd_brand_oinfo); 15090075Sobrien 15190075Sobrienvoid elf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase); 15290075Sobrien 15390075Sobrienvoid 15490075Sobrienelf32_dump_thread(struct thread *td, void *dst, size_t *off) 15590075Sobrien{ 15690075Sobrien size_t len; 15790075Sobrien struct pcb *pcb; 15890075Sobrien 15990075Sobrien len = 0; 16090075Sobrien pcb = td->td_pcb; 16190075Sobrien if (pcb->pcb_flags & PCB_VEC) { 16290075Sobrien save_vec_nodrop(td); 16390075Sobrien if (dst != NULL) { 16490075Sobrien len += elf32_populate_note(NT_PPC_VMX, 16590075Sobrien &pcb->pcb_vec, dst, 16690075Sobrien sizeof(pcb->pcb_vec), NULL); 16790075Sobrien } else 16890075Sobrien len += elf32_populate_note(NT_PPC_VMX, NULL, NULL, 16990075Sobrien sizeof(pcb->pcb_vec), NULL); 17090075Sobrien } 17190075Sobrien *off = len; 17290075Sobrien} 17390075Sobrien 17490075Sobrien#ifndef __powerpc64__ 17590075Sobrien/* Process one elf relocation with addend. */ 17690075Sobrienstatic int 17790075Sobrienelf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, 17890075Sobrien int type, int local, elf_lookup_fn lookup) 17990075Sobrien{ 18090075Sobrien Elf_Addr *where; 18190075Sobrien Elf_Half *hwhere; 18290075Sobrien Elf_Addr addr; 18390075Sobrien Elf_Addr addend; 18490075Sobrien Elf_Word rtype, symidx; 18590075Sobrien const Elf_Rela *rela; 18690075Sobrien int error; 18790075Sobrien 18890075Sobrien switch (type) { 18990075Sobrien case ELF_RELOC_REL: 19090075Sobrien panic("PPC only supports RELA relocations"); 19190075Sobrien break; 19290075Sobrien case ELF_RELOC_RELA: 19390075Sobrien rela = (const Elf_Rela *)data; 19490075Sobrien where = (Elf_Addr *) ((uintptr_t)relocbase + rela->r_offset); 19590075Sobrien hwhere = (Elf_Half *) ((uintptr_t)relocbase + rela->r_offset); 19690075Sobrien addend = rela->r_addend; 19790075Sobrien rtype = ELF_R_TYPE(rela->r_info); 19890075Sobrien symidx = ELF_R_SYM(rela->r_info); 19990075Sobrien break; 20090075Sobrien default: 20190075Sobrien panic("elf_reloc: unknown relocation mode %d\n", type); 20290075Sobrien } 20390075Sobrien 20490075Sobrien switch (rtype) { 20590075Sobrien 20690075Sobrien case R_PPC_NONE: 20790075Sobrien break; 20890075Sobrien 20990075Sobrien case R_PPC_ADDR32: /* word32 S + A */ 21090075Sobrien error = lookup(lf, symidx, 1, &addr); 21190075Sobrien if (error != 0) 21290075Sobrien return -1; 21390075Sobrien *where = elf_relocaddr(lf, addr + addend); 21490075Sobrien break; 21590075Sobrien 21690075Sobrien case R_PPC_ADDR16_LO: /* #lo(S) */ 21790075Sobrien error = lookup(lf, symidx, 1, &addr); 21890075Sobrien if (error != 0) 21990075Sobrien return -1; 22090075Sobrien /* 22190075Sobrien * addend values are sometimes relative to sections 22290075Sobrien * (i.e. .rodata) in rela, where in reality they 22390075Sobrien * are relative to relocbase. Detect this condition. 22490075Sobrien */ 22590075Sobrien if (addr > relocbase && addr <= (relocbase + addend)) 22690075Sobrien addr = relocbase; 22790075Sobrien addr = elf_relocaddr(lf, addr + addend); 22890075Sobrien *hwhere = addr & 0xffff; 22990075Sobrien break; 23090075Sobrien 23190075Sobrien case R_PPC_ADDR16_HA: /* #ha(S) */ 23290075Sobrien error = lookup(lf, symidx, 1, &addr); 23390075Sobrien if (error != 0) 23490075Sobrien return -1; 23590075Sobrien /* 23690075Sobrien * addend values are sometimes relative to sections 23790075Sobrien * (i.e. .rodata) in rela, where in reality they 23890075Sobrien * are relative to relocbase. Detect this condition. 23990075Sobrien */ 24090075Sobrien if (addr > relocbase && addr <= (relocbase + addend)) 24190075Sobrien addr = relocbase; 24290075Sobrien addr = elf_relocaddr(lf, addr + addend); 24390075Sobrien *hwhere = ((addr >> 16) + ((addr & 0x8000) ? 1 : 0)) 24490075Sobrien & 0xffff; 24590075Sobrien break; 24690075Sobrien 24790075Sobrien case R_PPC_RELATIVE: /* word32 B + A */ 24890075Sobrien *where = elf_relocaddr(lf, relocbase + addend); 24990075Sobrien break; 25090075Sobrien 25190075Sobrien default: 25290075Sobrien printf("kldload: unexpected relocation type %d\n", 25390075Sobrien (int) rtype); 25490075Sobrien return -1; 25590075Sobrien } 25690075Sobrien return(0); 25790075Sobrien} 25890075Sobrien 25990075Sobrienvoid 26090075Sobrienelf_reloc_self(Elf_Dyn *dynp, Elf_Addr relocbase) 26190075Sobrien{ 26290075Sobrien Elf_Rela *rela = 0, *relalim; 26390075Sobrien Elf_Addr relasz = 0; 26490075Sobrien Elf_Addr *where; 26590075Sobrien 26690075Sobrien /* 26790075Sobrien * Extract the rela/relasz values from the dynamic section 26890075Sobrien */ 26990075Sobrien for (; dynp->d_tag != DT_NULL; dynp++) { 27090075Sobrien switch (dynp->d_tag) { 27190075Sobrien case DT_RELA: 27290075Sobrien rela = (Elf_Rela *)(relocbase+dynp->d_un.d_ptr); 27390075Sobrien break; 27490075Sobrien case DT_RELASZ: 27590075Sobrien relasz = dynp->d_un.d_val; 27690075Sobrien break; 27790075Sobrien } 27890075Sobrien } 27990075Sobrien 28090075Sobrien /* 28190075Sobrien * Relocate these values 28290075Sobrien */ 28390075Sobrien relalim = (Elf_Rela *)((caddr_t)rela + relasz); 28490075Sobrien for (; rela < relalim; rela++) { 28590075Sobrien if (ELF_R_TYPE(rela->r_info) != R_PPC_RELATIVE) 28690075Sobrien continue; 28790075Sobrien where = (Elf_Addr *)(relocbase + rela->r_offset); 28890075Sobrien *where = (Elf_Addr)(relocbase + rela->r_addend); 28990075Sobrien } 29090075Sobrien} 29190075Sobrien 29290075Sobrienint 29390075Sobrienelf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, 29490075Sobrien elf_lookup_fn lookup) 29590075Sobrien{ 29690075Sobrien 29790075Sobrien return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); 29890075Sobrien} 29990075Sobrien 30090075Sobrienint 30190075Sobrienelf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, 30290075Sobrien int type, elf_lookup_fn lookup) 30390075Sobrien{ 30490075Sobrien 30590075Sobrien return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup)); 30690075Sobrien} 30790075Sobrien 30890075Sobrienint 30990075Sobrienelf_cpu_load_file(linker_file_t lf) 31090075Sobrien{ 31190075Sobrien /* Only sync the cache for non-kernel modules */ 31290075Sobrien if (lf->id != 1) 31390075Sobrien __syncicache(lf->address, lf->size); 31490075Sobrien return (0); 31590075Sobrien} 31690075Sobrien 31790075Sobrienint 31890075Sobrienelf_cpu_unload_file(linker_file_t lf __unused) 31990075Sobrien{ 32090075Sobrien 32190075Sobrien return (0); 32290075Sobrien} 32390075Sobrien#endif 32490075Sobrien