19b718f1andrew/*-
2c71beb9kib * Copyright (c) 2014, 2015 The FreeBSD Foundation.
39b718f1andrew * Copyright (c) 2014 Andrew Turner.
49b718f1andrew * All rights reserved.
59b718f1andrew *
69b718f1andrew * This software was developed by Andrew Turner under
79b718f1andrew * sponsorship from the FreeBSD Foundation.
89b718f1andrew *
9c71beb9kib * Portions of this software were developed by Konstantin Belousov
10c71beb9kib * under sponsorship from the FreeBSD Foundation.
11c71beb9kib *
129b718f1andrew * Redistribution and use in source and binary forms, with or without
139b718f1andrew * modification, are permitted provided that the following conditions
149b718f1andrew * are met:
159b718f1andrew * 1. Redistributions of source code must retain the above copyright
169b718f1andrew *    notice, this list of conditions and the following disclaimer.
179b718f1andrew * 2. Redistributions in binary form must reproduce the above copyright
189b718f1andrew *    notice, this list of conditions and the following disclaimer in the
199b718f1andrew *    documentation and/or other materials provided with the distribution.
209b718f1andrew *
217370293kib * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
227370293kib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
237370293kib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
247370293kib * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
257370293kib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
267370293kib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
277370293kib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
287370293kib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
297370293kib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
307370293kib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
317370293kib * SUCH DAMAGE.
329b718f1andrew */
339b718f1andrew
349b718f1andrew#include <sys/cdefs.h>
359b718f1andrew__FBSDID("$FreeBSD$");
369b718f1andrew
379b718f1andrew#include <sys/param.h>
389b718f1andrew#include <sys/kernel.h>
399b718f1andrew#include <sys/systm.h>
409b718f1andrew#include <sys/exec.h>
419b718f1andrew#include <sys/imgact.h>
429b718f1andrew#include <sys/linker.h>
439b718f1andrew#include <sys/proc.h>
449b718f1andrew#include <sys/sysent.h>
459b718f1andrew#include <sys/imgact_elf.h>
469b718f1andrew#include <sys/syscall.h>
479b718f1andrew#include <sys/signalvar.h>
489b718f1andrew#include <sys/vnode.h>
499b718f1andrew
509b718f1andrew#include <vm/vm.h>
519b718f1andrew#include <vm/vm_param.h>
529b718f1andrew
539b718f1andrew#include <machine/elf.h>
549b718f1andrew#include <machine/md_var.h>
559b718f1andrew
569b718f1andrew#include "linker_if.h"
579b718f1andrew
588d6dc91manuu_long elf_hwcap;
598d6dc91manu
609b718f1andrewstatic struct sysentvec elf64_freebsd_sysvec = {
619b718f1andrew	.sv_size	= SYS_MAXSYSCALL,
629b718f1andrew	.sv_table	= sysent,
639b718f1andrew	.sv_errsize	= 0,
649b718f1andrew	.sv_errtbl	= NULL,
659b718f1andrew	.sv_transtrap	= NULL,
669b718f1andrew	.sv_fixup	= __elfN(freebsd_fixup),
679b718f1andrew	.sv_sendsig	= sendsig,
689b718f1andrew	.sv_sigcode	= sigcode,
699b718f1andrew	.sv_szsigcode	= &szsigcode,
709b718f1andrew	.sv_name	= "FreeBSD ELF64",
719b718f1andrew	.sv_coredump	= __elfN(coredump),
729b718f1andrew	.sv_imgact_try	= NULL,
739b718f1andrew	.sv_minsigstksz	= MINSIGSTKSZ,
749b718f1andrew	.sv_minuser	= VM_MIN_ADDRESS,
759b718f1andrew	.sv_maxuser	= VM_MAXUSER_ADDRESS,
769b718f1andrew	.sv_usrstack	= USRSTACK,
779b718f1andrew	.sv_psstrings	= PS_STRINGS,
789b718f1andrew	.sv_stackprot	= VM_PROT_READ | VM_PROT_WRITE,
793f50cb7jhb	.sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs),
809b718f1andrew	.sv_copyout_strings = exec_copyout_strings,
819b718f1andrew	.sv_setregs	= exec_setregs,
829b718f1andrew	.sv_fixlimit	= NULL,
839b718f1andrew	.sv_maxssiz	= NULL,
840af6ee1kib	.sv_flags	= SV_SHP | SV_TIMEKEEP | SV_ABI_FREEBSD | SV_LP64 |
850af6ee1kib	    SV_ASLR,
869b718f1andrew	.sv_set_syscall_retval = cpu_set_syscall_retval,
879b718f1andrew	.sv_fetch_syscall_args = cpu_fetch_syscall_args,
889b718f1andrew	.sv_syscallnames = syscallnames,
8980e8626kib	.sv_shared_page_base = SHAREDPAGE,
9080e8626kib	.sv_shared_page_len = PAGE_SIZE,
919b718f1andrew	.sv_schedtail	= NULL,
92e706df7dchagin	.sv_thread_detach = NULL,
93e706df7dchagin	.sv_trap	= NULL,
948d6dc91manu	.sv_hwcap	= &elf_hwcap,
959b718f1andrew};
9680e8626kibINIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec);
979b718f1andrew
989b718f1andrewstatic Elf64_Brandinfo freebsd_brand_info = {
999b718f1andrew	.brand		= ELFOSABI_FREEBSD,
1009b718f1andrew	.machine	= EM_AARCH64,
1019b718f1andrew	.compat_3_brand	= "FreeBSD",
1029b718f1andrew	.emul_path	= NULL,
1039b718f1andrew	.interp_path	= "/libexec/ld-elf.so.1",
1049b718f1andrew	.sysvec		= &elf64_freebsd_sysvec,
1059b718f1andrew	.interp_newpath	= NULL,
1069b718f1andrew	.brand_note	= &elf64_freebsd_brandnote,
1079b718f1andrew	.flags		= BI_CAN_EXEC_DYN | BI_BRAND_NOTE
1089b718f1andrew};
1099b718f1andrew
1109b718f1andrewSYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST,
1119b718f1andrew    (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_info);
1129b718f1andrew
1139b718f1andrewvoid
1149b718f1andrewelf64_dump_thread(struct thread *td __unused, void *dst __unused,
1159b718f1andrew    size_t *off __unused)
1169b718f1andrew{
1179b718f1andrew
1189b718f1andrew}
1199b718f1andrew
1201d715a5markjbool
1211d715a5markjelf_is_ifunc_reloc(Elf_Size r_info __unused)
1221d715a5markj{
1231d715a5markj
1242a6fe8candrew	return (ELF_R_TYPE(r_info) == R_AARCH64_IRELATIVE);
1251d715a5markj}
1261d715a5markj
127c71beb9kibstatic int
128ad161bbmarkjreloc_instr_imm(Elf32_Addr *where, Elf_Addr val, u_int msb, u_int lsb)
129ad161bbmarkj{
130ad161bbmarkj
131ad161bbmarkj	/* Check bounds: upper bits must be all ones or all zeros. */
132ad161bbmarkj	if ((uint64_t)((int64_t)val >> (msb + 1)) + 1 > 1)
133ad161bbmarkj		return (-1);
134ad161bbmarkj	val >>= lsb;
135ad161bbmarkj	val &= (1 << (msb - lsb + 1)) - 1;
136ad161bbmarkj	*where |= (Elf32_Addr)val;
137ad161bbmarkj	return (0);
138ad161bbmarkj}
139ad161bbmarkj
140ad161bbmarkj/*
141ad161bbmarkj * Process a relocation.  Support for some static relocations is required
142ad161bbmarkj * in order for the -zifunc-noplt optimization to work.
143ad161bbmarkj */
144ad161bbmarkjstatic int
145c71beb9kibelf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
146c71beb9kib    int type, int local, elf_lookup_fn lookup)
147c71beb9kib{
1482a6fe8candrew	Elf_Addr *where, addr, addend, val;
149c71beb9kib	Elf_Word rtype, symidx;
150c71beb9kib	const Elf_Rel *rel;
151c71beb9kib	const Elf_Rela *rela;
152c71beb9kib	int error;
153c71beb9kib
154c71beb9kib	switch (type) {
155c71beb9kib	case ELF_RELOC_REL:
156c71beb9kib		rel = (const Elf_Rel *)data;
157c71beb9kib		where = (Elf_Addr *) (relocbase + rel->r_offset);
158c71beb9kib		addend = *where;
159c71beb9kib		rtype = ELF_R_TYPE(rel->r_info);
160c71beb9kib		symidx = ELF_R_SYM(rel->r_info);
161c71beb9kib		break;
162c71beb9kib	case ELF_RELOC_RELA:
163c71beb9kib		rela = (const Elf_Rela *)data;
164c71beb9kib		where = (Elf_Addr *) (relocbase + rela->r_offset);
165c71beb9kib		addend = rela->r_addend;
166c71beb9kib		rtype = ELF_R_TYPE(rela->r_info);
167c71beb9kib		symidx = ELF_R_SYM(rela->r_info);
168c71beb9kib		break;
169c71beb9kib	default:
170c71beb9kib		panic("unknown reloc type %d\n", type);
171c71beb9kib	}
172c71beb9kib
173c71beb9kib	if (local) {
174c71beb9kib		if (rtype == R_AARCH64_RELATIVE)
175c71beb9kib			*where = elf_relocaddr(lf, relocbase + addend);
176c71beb9kib		return (0);
177c71beb9kib	}
178c71beb9kib
179ad161bbmarkj	error = 0;
180c71beb9kib	switch (rtype) {
181c71beb9kib	case R_AARCH64_NONE:
182c71beb9kib	case R_AARCH64_RELATIVE:
183c71beb9kib		break;
184ad161bbmarkj	case R_AARCH64_TSTBR14:
185ad161bbmarkj		error = lookup(lf, symidx, 1, &addr);
186ad161bbmarkj		if (error != 0)
187ad161bbmarkj			return (-1);
188ad161bbmarkj		error = reloc_instr_imm((Elf32_Addr *)where,
189ad161bbmarkj		    addr + addend - (Elf_Addr)where, 15, 2);
190ad161bbmarkj		break;
191ad161bbmarkj	case R_AARCH64_CONDBR19:
192ad161bbmarkj		error = lookup(lf, symidx, 1, &addr);
193ad161bbmarkj		if (error != 0)
194ad161bbmarkj			return (-1);
195ad161bbmarkj		error = reloc_instr_imm((Elf32_Addr *)where,
196ad161bbmarkj		    addr + addend - (Elf_Addr)where, 20, 2);
197ad161bbmarkj		break;
198ad161bbmarkj	case R_AARCH64_JUMP26:
199ad161bbmarkj	case R_AARCH64_CALL26:
200ad161bbmarkj		error = lookup(lf, symidx, 1, &addr);
201ad161bbmarkj		if (error != 0)
202ad161bbmarkj			return (-1);
203ad161bbmarkj		error = reloc_instr_imm((Elf32_Addr *)where,
204ad161bbmarkj		    addr + addend - (Elf_Addr)where, 27, 2);
205ad161bbmarkj		break;
206c71beb9kib	case R_AARCH64_ABS64:
207c71beb9kib	case R_AARCH64_GLOB_DAT:
208c71beb9kib	case R_AARCH64_JUMP_SLOT:
209c71beb9kib		error = lookup(lf, symidx, 1, &addr);
210c71beb9kib		if (error != 0)
211c71beb9kib			return (-1);
212c71beb9kib		*where = addr + addend;
213c71beb9kib		break;
2142a6fe8candrew	case R_AARCH64_IRELATIVE:
2152a6fe8candrew		addr = relocbase + addend;
2162a6fe8candrew		val = ((Elf64_Addr (*)(void))addr)();
2172a6fe8candrew		if (*where != val)
2182a6fe8candrew			*where = val;
2192a6fe8candrew		break;
220c71beb9kib	default:
221c71beb9kib		printf("kldload: unexpected relocation type %d\n", rtype);
222c71beb9kib		return (-1);
223c71beb9kib	}
224ad161bbmarkj	return (error);
225c71beb9kib}
226c71beb9kib
2279b718f1andrewint
2289b718f1andrewelf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data,
229c71beb9kib    int type, elf_lookup_fn lookup)
2309b718f1andrew{
2319b718f1andrew
232c71beb9kib	return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup));
2339b718f1andrew}
2349b718f1andrew
2359b718f1andrew/* Process one elf relocation with addend. */
2369b718f1andrewint
2379b718f1andrewelf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
2389b718f1andrew    elf_lookup_fn lookup)
2399b718f1andrew{
2409b718f1andrew
241c71beb9kib	return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup));
2429b718f1andrew}
2439b718f1andrew
2449b718f1andrewint
245c71beb9kibelf_cpu_load_file(linker_file_t lf)
2469b718f1andrew{
2479b718f1andrew
248c71beb9kib	if (lf->id != 1)
249c71beb9kib		cpu_icache_sync_range((vm_offset_t)lf->address, lf->size);
2509b718f1andrew	return (0);
2519b718f1andrew}
2529b718f1andrew
2539b718f1andrewint
2549b718f1andrewelf_cpu_unload_file(linker_file_t lf __unused)
2559b718f1andrew{
2569b718f1andrew
2579b718f1andrew	return (0);
2589b718f1andrew}
2598191fa8bdragon
2608191fa8bdragonint
2618191fa8bdragonelf_cpu_parse_dynamic(linker_file_t lf __unused, Elf_Dyn *dynamic __unused)
2628191fa8bdragon{
2638191fa8bdragon
2648191fa8bdragon	return (0);
2658191fa8bdragon}
266