elf_machdep.c revision 295207
1129198Scognet/*-
2129198Scognet * Copyright 1996-1998 John D. Polstra.
3129198Scognet * All rights reserved.
4129198Scognet *
5129198Scognet * Redistribution and use in source and binary forms, with or without
6129198Scognet * modification, are permitted provided that the following conditions
7129198Scognet * are met:
8129198Scognet * 1. Redistributions of source code must retain the above copyright
9129198Scognet *    notice, this list of conditions and the following disclaimer.
10129198Scognet * 2. Redistributions in binary form must reproduce the above copyright
11129198Scognet *    notice, this list of conditions and the following disclaimer in the
12129198Scognet *    documentation and/or other materials provided with the distribution.
13129198Scognet *
14129198Scognet * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15129198Scognet * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16129198Scognet * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17129198Scognet * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18129198Scognet * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19129198Scognet * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20129198Scognet * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21129198Scognet * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22129198Scognet * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23129198Scognet * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24129198Scognet */
25129198Scognet
26129198Scognet#include <sys/cdefs.h>
27129198Scognet__FBSDID("$FreeBSD: head/sys/arm/arm/elf_machdep.c 295207 2016-02-03 13:47:50Z mmel $");
28129198Scognet
29129198Scognet#include <sys/param.h>
30129198Scognet#include <sys/kernel.h>
31129198Scognet#include <sys/systm.h>
32129198Scognet#include <sys/exec.h>
33129198Scognet#include <sys/imgact.h>
34129198Scognet#include <sys/linker.h>
35129198Scognet#include <sys/sysent.h>
36129198Scognet#include <sys/imgact_elf.h>
37208453Skib#include <sys/proc.h>
38129198Scognet#include <sys/syscall.h>
39129198Scognet#include <sys/signalvar.h>
40129198Scognet#include <sys/vnode.h>
41129198Scognet
42129198Scognet#include <vm/vm.h>
43129198Scognet#include <vm/pmap.h>
44129198Scognet#include <vm/vm_param.h>
45129198Scognet
46291937Skib#include <machine/acle-compat.h>
47129198Scognet#include <machine/elf.h>
48129198Scognet#include <machine/md_var.h>
49129198Scognet
50270123Simpstatic boolean_t elf32_arm_abi_supported(struct image_params *);
51270123Simp
52129198Scognetstruct sysentvec elf32_freebsd_sysvec = {
53183322Skib	.sv_size	= SYS_MAXSYSCALL,
54183322Skib	.sv_table	= sysent,
55183322Skib	.sv_mask	= 0,
56183322Skib	.sv_errsize	= 0,
57183322Skib	.sv_errtbl	= NULL,
58183322Skib	.sv_transtrap	= NULL,
59183322Skib	.sv_fixup	= __elfN(freebsd_fixup),
60183322Skib	.sv_sendsig	= sendsig,
61183322Skib	.sv_sigcode	= sigcode,
62183322Skib	.sv_szsigcode	= &szsigcode,
63183322Skib	.sv_name	= "FreeBSD ELF32",
64183322Skib	.sv_coredump	= __elfN(coredump),
65183322Skib	.sv_imgact_try	= NULL,
66183322Skib	.sv_minsigstksz	= MINSIGSTKSZ,
67183322Skib	.sv_pagesize	= PAGE_SIZE,
68183322Skib	.sv_minuser	= VM_MIN_ADDRESS,
69183322Skib	.sv_maxuser	= VM_MAXUSER_ADDRESS,
70183322Skib	.sv_usrstack	= USRSTACK,
71183322Skib	.sv_psstrings	= PS_STRINGS,
72183322Skib	.sv_stackprot	= VM_PROT_ALL,
73183322Skib	.sv_copyout_strings = exec_copyout_strings,
74183322Skib	.sv_setregs	= exec_setregs,
75183322Skib	.sv_fixlimit	= NULL,
76185169Skib	.sv_maxssiz	= NULL,
77291937Skib	.sv_flags	=
78291937Skib#if __ARM_ARCH >= 6
79291937Skib			  SV_SHP | SV_TIMEKEEP |
80291937Skib#endif
81291937Skib			  SV_ABI_FREEBSD | SV_ILP32,
82208453Skib	.sv_set_syscall_retval = cpu_set_syscall_retval,
83225973Skib	.sv_fetch_syscall_args = cpu_fetch_syscall_args,
84208453Skib	.sv_syscallnames = syscallnames,
85291937Skib	.sv_shared_page_base = SHAREDPAGE,
86291937Skib	.sv_shared_page_len = PAGE_SIZE,
87219405Sdchagin	.sv_schedtail	= NULL,
88283382Sdchagin	.sv_thread_detach = NULL,
89293613Sdchagin	.sv_trap	= NULL,
90129198Scognet};
91291937SkibINIT_SYSENTVEC(elf32_sysvec, &elf32_freebsd_sysvec);
92129198Scognet
93129198Scognetstatic Elf32_Brandinfo freebsd_brand_info = {
94183322Skib	.brand		= ELFOSABI_FREEBSD,
95183322Skib	.machine	= EM_ARM,
96183322Skib	.compat_3_brand	= "FreeBSD",
97183322Skib	.emul_path	= NULL,
98183322Skib	.interp_path	= "/libexec/ld-elf.so.1",
99183322Skib	.sysvec		= &elf32_freebsd_sysvec,
100183322Skib	.interp_newpath	= NULL,
101189771Sdchagin	.brand_note	= &elf32_freebsd_brandnote,
102270123Simp	.flags		= BI_CAN_EXEC_DYN | BI_BRAND_NOTE,
103270123Simp	.header_supported= elf32_arm_abi_supported,
104183322Skib};
105129198Scognet
106197729SbzSYSINIT(elf32, SI_SUB_EXEC, SI_ORDER_FIRST,
107129198Scognet	(sysinit_cfunc_t) elf32_insert_brand_entry,
108129198Scognet	&freebsd_brand_info);
109129198Scognet
110270123Simpstatic boolean_t
111270123Simpelf32_arm_abi_supported(struct image_params *imgp)
112270123Simp{
113270123Simp	const Elf_Ehdr *hdr = (const Elf_Ehdr *)imgp->image_header;
114133464Smarcel
115270123Simp	/*
116270123Simp	 * When configured for EABI, FreeBSD supports EABI vesions 4 and 5.
117270123Simp	 */
118270123Simp	if (EF_ARM_EABI_VERSION(hdr->e_flags) < EF_ARM_EABI_FREEBSD_MIN) {
119270123Simp		if (bootverbose)
120270123Simp			uprintf("Attempting to execute non EABI binary (rev %d) image %s",
121270123Simp			    EF_ARM_EABI_VERSION(hdr->e_flags), imgp->args->fname);
122270123Simp		return (FALSE);
123270123Simp	}
124270123Simp	return (TRUE);
125270123Simp}
126270123Simp
127133464Smarcelvoid
128133464Smarcelelf32_dump_thread(struct thread *td __unused, void *dst __unused,
129133464Smarcel    size_t *off __unused)
130133464Smarcel{
131133464Smarcel}
132133464Smarcel
133269767Simp/*
134269767Simp * It is possible for the compiler to emit relocations for unaligned data.
135269767Simp * We handle this situation with these inlines.
136269767Simp */
137269767Simp#define	RELOC_ALIGNED_P(x) \
138269767Simp	(((uintptr_t)(x) & (sizeof(void *) - 1)) == 0)
139133464Smarcel
140269767Simpstatic __inline Elf_Addr
141269767Simpload_ptr(Elf_Addr *where)
142269767Simp{
143269767Simp	Elf_Addr res;
144269767Simp
145269767Simp	if (RELOC_ALIGNED_P(where))
146269767Simp		return *where;
147269767Simp	memcpy(&res, where, sizeof(res));
148269767Simp	return (res);
149269767Simp}
150269767Simp
151269767Simpstatic __inline void
152269767Simpstore_ptr(Elf_Addr *where, Elf_Addr val)
153269767Simp{
154269767Simp	if (RELOC_ALIGNED_P(where))
155269767Simp		*where = val;
156269767Simp	else
157269767Simp		memcpy(where, &val, sizeof(val));
158269767Simp}
159269767Simp#undef RELOC_ALIGNED_P
160269767Simp
161269767Simp
162129198Scognet/* Process one elf relocation with addend. */
163129198Scognetstatic int
164129282Speterelf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
165129282Speter    int type, int local, elf_lookup_fn lookup)
166129198Scognet{
167129198Scognet	Elf_Addr *where;
168129198Scognet	Elf_Addr addr;
169129198Scognet	Elf_Addr addend;
170129198Scognet	Elf_Word rtype, symidx;
171129198Scognet	const Elf_Rel *rel;
172129198Scognet	const Elf_Rela *rela;
173288000Skib	int error;
174129198Scognet
175129198Scognet	switch (type) {
176129198Scognet	case ELF_RELOC_REL:
177129198Scognet		rel = (const Elf_Rel *)data;
178129198Scognet		where = (Elf_Addr *) (relocbase + rel->r_offset);
179269767Simp		addend = load_ptr(where);
180129198Scognet		rtype = ELF_R_TYPE(rel->r_info);
181129198Scognet		symidx = ELF_R_SYM(rel->r_info);
182129198Scognet		break;
183129198Scognet	case ELF_RELOC_RELA:
184129198Scognet		rela = (const Elf_Rela *)data;
185129198Scognet		where = (Elf_Addr *) (relocbase + rela->r_offset);
186129198Scognet		addend = rela->r_addend;
187129198Scognet		rtype = ELF_R_TYPE(rela->r_info);
188129198Scognet		symidx = ELF_R_SYM(rela->r_info);
189129198Scognet		break;
190129198Scognet	default:
191129198Scognet		panic("unknown reloc type %d\n", type);
192129198Scognet	}
193129198Scognet
194129198Scognet	if (local) {
195129198Scognet		if (rtype == R_ARM_RELATIVE) {	/* A + B */
196194784Sjeff			addr = elf_relocaddr(lf, relocbase + addend);
197269767Simp			if (load_ptr(where) != addr)
198269767Simp				store_ptr(where, addr);
199129198Scognet		}
200129198Scognet		return (0);
201129198Scognet	}
202129198Scognet
203129198Scognet	switch (rtype) {
204129198Scognet
205129198Scognet		case R_ARM_NONE:	/* none */
206129198Scognet			break;
207129198Scognet
208137211Scognet		case R_ARM_ABS32:
209288000Skib			error = lookup(lf, symidx, 1, &addr);
210288000Skib			if (error != 0)
211129198Scognet				return -1;
212269767Simp			store_ptr(where, addr + load_ptr(where));
213129198Scognet			break;
214129198Scognet
215129198Scognet		case R_ARM_COPY:	/* none */
216129198Scognet			/*
217129198Scognet			 * There shouldn't be copy relocations in kernel
218129198Scognet			 * objects.
219129198Scognet			 */
220129198Scognet			printf("kldload: unexpected R_COPY relocation\n");
221129198Scognet			return -1;
222129198Scognet			break;
223129198Scognet
224137211Scognet		case R_ARM_JUMP_SLOT:
225288000Skib			error = lookup(lf, symidx, 1, &addr);
226288000Skib			if (error == 0) {
227269767Simp				store_ptr(where, addr);
228137211Scognet				return (0);
229137211Scognet			}
230137211Scognet			return (-1);
231129198Scognet		case R_ARM_RELATIVE:
232129198Scognet			break;
233129198Scognet
234129198Scognet		default:
235129198Scognet			printf("kldload: unexpected relocation type %d\n",
236129198Scognet			       rtype);
237129198Scognet			return -1;
238129198Scognet	}
239129198Scognet	return(0);
240129198Scognet}
241129198Scognet
242129198Scognetint
243129282Speterelf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
244129282Speter    elf_lookup_fn lookup)
245129198Scognet{
246129198Scognet
247129282Speter	return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup));
248129198Scognet}
249129198Scognet
250129198Scognetint
251129282Speterelf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data,
252129282Speter    int type, elf_lookup_fn lookup)
253129198Scognet{
254129198Scognet
255129282Speter	return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup));
256129198Scognet}
257129198Scognet
258129198Scognetint
259294727Sskraelf_cpu_load_file(linker_file_t lf)
260129198Scognet{
261129198Scognet
262264994Sian	/*
263264994Sian	 * The pmap code does not do an icache sync upon establishing executable
264264994Sian	 * mappings in the kernel pmap.  It's an optimization based on the fact
265264994Sian	 * that kernel memory allocations always have EXECUTABLE protection even
266264994Sian	 * when the memory isn't going to hold executable code.  The only time
267264994Sian	 * kernel memory holding instructions does need a sync is after loading
268294727Sskra	 * a kernel module, and that's when this function gets called.
269294727Sskra	 *
270294727Sskra	 * This syncs data and instruction caches after loading a module.  We
271294727Sskra	 * don't worry about the kernel itself (lf->id is 1) as locore.S did
272294727Sskra	 * that on entry.  Even if data cache maintenance was done by IO code,
273294727Sskra	 * the relocation fixup process creates dirty cache entries that we must
274294727Sskra	 * write back before doing icache sync. The instruction cache sync also
275294727Sskra	 * invalidates the branch predictor cache on platforms that have one.
276264994Sian	 */
277294727Sskra	if (lf->id == 1)
278294727Sskra		return (0);
279294727Sskra#if __ARM_ARCH >= 6
280294727Sskra	dcache_wb_pou((vm_offset_t)lf->address, (vm_size_t)lf->size);
281294727Sskra	icache_inv_all();
282294727Sskra#else
283294727Sskra	cpu_dcache_wb_range((vm_offset_t)lf->address, (vm_size_t)lf->size);
284294727Sskra	cpu_l2cache_wb_range((vm_offset_t)lf->address, (vm_size_t)lf->size);
285295207Smmel	cpu_icache_sync_range((vm_offset_t)lf->address, (vm_size_t)lf->size);
286294727Sskra#endif
287129198Scognet	return (0);
288129198Scognet}
289129198Scognet
290129198Scognetint
291129198Scognetelf_cpu_unload_file(linker_file_t lf __unused)
292129198Scognet{
293129198Scognet
294129198Scognet	return (0);
295129198Scognet}
296