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