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