166458Sdfr/*-
266458Sdfr * Copyright 1996-1998 John D. Polstra.
366458Sdfr * All rights reserved.
466458Sdfr *
566458Sdfr * Redistribution and use in source and binary forms, with or without
666458Sdfr * modification, are permitted provided that the following conditions
766458Sdfr * are met:
866458Sdfr * 1. Redistributions of source code must retain the above copyright
966458Sdfr *    notice, this list of conditions and the following disclaimer.
1066458Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1166458Sdfr *    notice, this list of conditions and the following disclaimer in the
1266458Sdfr *    documentation and/or other materials provided with the distribution.
1366458Sdfr *
1466458Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1566458Sdfr * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1666458Sdfr * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1766458Sdfr * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1866458Sdfr * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1966458Sdfr * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2066458Sdfr * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2166458Sdfr * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2266458Sdfr * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2366458Sdfr * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2466458Sdfr *
2566458Sdfr * $FreeBSD$
2666458Sdfr */
2766458Sdfr
2866458Sdfr#include <sys/param.h>
2966458Sdfr#include <sys/kernel.h>
3066458Sdfr#include <sys/systm.h>
31102808Sjake#include <sys/exec.h>
32102808Sjake#include <sys/imgact.h>
3366458Sdfr#include <sys/malloc.h>
3466458Sdfr#include <sys/proc.h>
3566458Sdfr#include <sys/namei.h>
3666458Sdfr#include <sys/fcntl.h>
3766458Sdfr#include <sys/vnode.h>
3866458Sdfr#include <sys/linker.h>
39100384Speter#include <sys/sysent.h>
40100384Speter#include <sys/imgact_elf.h>
41100384Speter#include <sys/syscall.h>
42100384Speter#include <sys/signalvar.h>
43102808Sjake
44102808Sjake#include <vm/vm.h>
45102808Sjake#include <vm/vm_param.h>
46102808Sjake
4766458Sdfr#include <machine/elf.h>
48118935Smarcel#include <machine/frame.h>
49100384Speter#include <machine/md_var.h>
50105469Smarcel#include <machine/unwind.h>
5166458Sdfr
52133464SmarcelElf_Addr link_elf_get_gp(linker_file_t);
53118935Smarcel
54133464Smarcelextern Elf_Addr fptr_storage[];
55133464Smarcel
56100384Speterstruct sysentvec elf64_freebsd_sysvec = {
57183322Skib	.sv_size	= SYS_MAXSYSCALL,
58183322Skib	.sv_table	= sysent,
59183322Skib	.sv_mask	= 0,
60183322Skib	.sv_sigsize	= 0,
61183322Skib	.sv_sigtbl	= NULL,
62183322Skib	.sv_errsize	= 0,
63183322Skib	.sv_errtbl	= NULL,
64183322Skib	.sv_transtrap	= NULL,
65183322Skib	.sv_fixup	= __elfN(freebsd_fixup),
66183322Skib	.sv_sendsig	= sendsig,
67183322Skib	.sv_sigcode	= NULL,
68183322Skib	.sv_szsigcode	= NULL,
69183322Skib	.sv_prepsyscall	= NULL,
70183322Skib	.sv_name	= "FreeBSD ELF64",
71183322Skib	.sv_coredump	= __elfN(coredump),
72183322Skib	.sv_imgact_try	= NULL,
73183322Skib	.sv_minsigstksz	= MINSIGSTKSZ,
74183322Skib	.sv_pagesize	= PAGE_SIZE,
75183322Skib	.sv_minuser	= VM_MIN_ADDRESS,
76183322Skib	.sv_maxuser	= VM_MAXUSER_ADDRESS,
77183322Skib	.sv_usrstack	= USRSTACK,
78183322Skib	.sv_psstrings	= PS_STRINGS,
79183322Skib	.sv_stackprot	= VM_PROT_READ|VM_PROT_WRITE,
80183322Skib	.sv_copyout_strings = exec_copyout_strings,
81183322Skib	.sv_setregs	= exec_setregs,
82183322Skib	.sv_fixlimit	= NULL,
83185169Skib	.sv_maxssiz	= NULL,
84208453Skib	.sv_flags	= SV_ABI_FREEBSD | SV_LP64,
85208453Skib	.sv_set_syscall_retval = cpu_set_syscall_retval,
86208453Skib	.sv_fetch_syscall_args = cpu_fetch_syscall_args,
87208453Skib	.sv_syscallnames = syscallnames,
88219405Sdchagin	.sv_schedtail	= NULL,
89100384Speter};
90100384Speter
91100384Speterstatic Elf64_Brandinfo freebsd_brand_info = {
92183322Skib	.brand		= ELFOSABI_FREEBSD,
93183322Skib	.machine	= EM_IA_64,
94183322Skib	.compat_3_brand	= "FreeBSD",
95183322Skib	.emul_path	= NULL,
96183322Skib	.interp_path	= "/libexec/ld-elf.so.1",
97183322Skib	.sysvec		= &elf64_freebsd_sysvec,
98183322Skib	.interp_newpath	= NULL,
99189771Sdchagin	.brand_note	= &elf64_freebsd_brandnote,
100190708Sdchagin	.flags		= BI_CAN_EXEC_DYN | BI_BRAND_NOTE
101133464Smarcel};
102197729SbzSYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST,
103133464Smarcel    (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_info);
104100384Speter
105123742Speterstatic Elf64_Brandinfo freebsd_brand_oinfo = {
106183322Skib	.brand		= ELFOSABI_FREEBSD,
107183322Skib	.machine	= EM_IA_64,
108183322Skib	.compat_3_brand	= "FreeBSD",
109183322Skib	.emul_path	= NULL,
110183322Skib	.interp_path	= "/usr/libexec/ld-elf.so.1",
111183322Skib	.sysvec		= &elf64_freebsd_sysvec,
112183322Skib	.interp_newpath	= NULL,
113189771Sdchagin	.brand_note	= &elf64_freebsd_brandnote,
114190708Sdchagin	.flags		= BI_CAN_EXEC_DYN | BI_BRAND_NOTE
115133464Smarcel};
116123742SpeterSYSINIT(oelf64, SI_SUB_EXEC, SI_ORDER_ANY,
117133464Smarcel    (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_oinfo);
118123742Speter
11995229Smarcel
120133464Smarcelvoid
121133464Smarcelelf64_dump_thread(struct thread *td, void *dst, size_t *off __unused)
122118935Smarcel{
123118935Smarcel
124133464Smarcel	/* Flush the dirty registers onto the backingstore. */
125133464Smarcel	if (dst == NULL)
126133464Smarcel		ia64_flush_dirty(td, &td->td_frame->tf_special);
127118935Smarcel}
128118935Smarcel
129133464Smarcel
13095229Smarcelstatic Elf_Addr
131153504Smarcellookup_fdesc(linker_file_t lf, Elf_Size symidx, elf_lookup_fn lookup)
13295229Smarcel{
133105147Smarcel	linker_file_t top;
13495229Smarcel	Elf_Addr addr;
135105147Smarcel	const char *symname;
13695229Smarcel	int i;
13795229Smarcel	static int eot = 0;
13895229Smarcel
139129282Speter	addr = lookup(lf, symidx, 0);
14095410Smarcel	if (addr == 0) {
141105147Smarcel		top = lf;
142105147Smarcel		symname = elf_get_symname(top, symidx);
143105147Smarcel		for (i = 0; i < top->ndeps; i++) {
144105147Smarcel			lf = top->deps[i];
145105147Smarcel			addr = (Elf_Addr)linker_file_lookup_symbol(lf,
146105147Smarcel			    symname, 0);
14795410Smarcel			if (addr != 0)
148105147Smarcel				break;
14995229Smarcel		}
150105147Smarcel		if (addr == 0)
151105147Smarcel			return (0);
15295229Smarcel	}
15395229Smarcel
15495229Smarcel	if (eot)
15595410Smarcel		return (0);
15695229Smarcel
15795229Smarcel	/*
15895229Smarcel	 * Lookup and/or construct OPD
15995229Smarcel	 */
16095229Smarcel	for (i = 0; i < 8192; i += 2) {
16195229Smarcel		if (fptr_storage[i] == addr)
16295229Smarcel			return (Elf_Addr)(fptr_storage + i);
16395229Smarcel
16495229Smarcel		if (fptr_storage[i] == 0) {
16595229Smarcel			fptr_storage[i] = addr;
16695229Smarcel			fptr_storage[i+1] = link_elf_get_gp(lf);
16795229Smarcel			return (Elf_Addr)(fptr_storage + i);
16895229Smarcel		}
16995229Smarcel	}
17095229Smarcel
17195229Smarcel	printf("%s: fptr table full\n", __func__);
17295229Smarcel	eot = 1;
17395229Smarcel
17495410Smarcel	return (0);
17595229Smarcel}
17695229Smarcel
17766458Sdfr/* Process one elf relocation with addend. */
178109605Sjakestatic int
179129282Speterelf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data,
180129282Speter    int type, int local, elf_lookup_fn lookup)
18166458Sdfr{
18266458Sdfr	Elf_Addr *where;
18395229Smarcel	Elf_Addr addend, addr;
184153504Smarcel	Elf_Size rtype, symidx;
18566458Sdfr	const Elf_Rel *rel;
18666458Sdfr	const Elf_Rela *rela;
18766458Sdfr
18866458Sdfr	switch (type) {
18966458Sdfr	case ELF_RELOC_REL:
19066458Sdfr		rel = (const Elf_Rel *)data;
19195229Smarcel		where = (Elf_Addr *)(relocbase + rel->r_offset);
19266458Sdfr		rtype = ELF_R_TYPE(rel->r_info);
19395410Smarcel		symidx = ELF_R_SYM(rel->r_info);
19495229Smarcel		switch (rtype) {
195154491Smarcel		case R_IA_64_DIR64LSB:
196154491Smarcel		case R_IA_64_FPTR64LSB:
197154491Smarcel		case R_IA_64_REL64LSB:
19895229Smarcel			addend = *where;
19995229Smarcel			break;
20095229Smarcel		default:
20195229Smarcel			addend = 0;
20295229Smarcel			break;
20395229Smarcel		}
20466458Sdfr		break;
20566458Sdfr	case ELF_RELOC_RELA:
20666458Sdfr		rela = (const Elf_Rela *)data;
20795229Smarcel		where = (Elf_Addr *)(relocbase + rela->r_offset);
20895229Smarcel		rtype = ELF_R_TYPE(rela->r_info);
20995410Smarcel		symidx = ELF_R_SYM(rela->r_info);
21066458Sdfr		addend = rela->r_addend;
21166458Sdfr		break;
21266458Sdfr	default:
21395229Smarcel		panic("%s: invalid ELF relocation (0x%x)\n", __func__, type);
21466458Sdfr	}
21566458Sdfr
216109605Sjake	if (local) {
217154491Smarcel		if (rtype == R_IA_64_REL64LSB)
218194784Sjeff			*where = elf_relocaddr(lf, relocbase + addend);
219109605Sjake		return (0);
220109605Sjake	}
221109605Sjake
22266458Sdfr	switch (rtype) {
223154491Smarcel	case R_IA_64_NONE:
22495229Smarcel		break;
225154491Smarcel	case R_IA_64_DIR64LSB:	/* word64 LSB	S + A */
226129282Speter		addr = lookup(lf, symidx, 1);
22795229Smarcel		if (addr == 0)
22895410Smarcel			return (-1);
22995229Smarcel		*where = addr + addend;
23095229Smarcel		break;
231154491Smarcel	case R_IA_64_FPTR64LSB:	/* word64 LSB	@fptr(S + A) */
23295229Smarcel		if (addend != 0) {
23395229Smarcel			printf("%s: addend ignored for OPD relocation\n",
23495229Smarcel			    __func__);
23595229Smarcel		}
236129282Speter		addr = lookup_fdesc(lf, symidx, lookup);
23795229Smarcel		if (addr == 0)
23895410Smarcel			return (-1);
23995229Smarcel		*where = addr;
24095229Smarcel		break;
241154491Smarcel	case R_IA_64_REL64LSB:	/* word64 LSB	BD + A */
24295229Smarcel		break;
243154491Smarcel	case R_IA_64_IPLTLSB:
244129282Speter		addr = lookup_fdesc(lf, symidx, lookup);
24595229Smarcel		if (addr == 0)
24695410Smarcel			return (-1);
24795229Smarcel		where[0] = *((Elf_Addr*)addr) + addend;
24895229Smarcel		where[1] = *((Elf_Addr*)addr + 1);
24995229Smarcel		break;
25095229Smarcel	default:
25195229Smarcel		printf("%s: unknown relocation (0x%x)\n", __func__,
25295229Smarcel		    (int)rtype);
25395229Smarcel		return -1;
25466458Sdfr	}
25595229Smarcel
25695410Smarcel	return (0);
25766458Sdfr}
258105469Smarcel
259105469Smarcelint
260129282Speterelf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type,
261129282Speter    elf_lookup_fn lookup)
262109605Sjake{
263109605Sjake
264129282Speter	return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup));
265109605Sjake}
266109605Sjake
267109605Sjakeint
268129282Speterelf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data,
269129282Speter    int type, elf_lookup_fn lookup)
270109605Sjake{
271109605Sjake
272129282Speter	return (elf_reloc_internal(lf, relocbase, data, type, 1, lookup));
273109605Sjake}
274109605Sjake
275109605Sjakeint
276105470Smarcelelf_cpu_load_file(linker_file_t lf)
277105469Smarcel{
278105470Smarcel	Elf_Ehdr *hdr;
279105470Smarcel	Elf_Phdr *ph, *phlim;
280105470Smarcel	Elf_Addr reloc, vaddr;
281105469Smarcel
282105470Smarcel	hdr = (Elf_Ehdr *)(lf->address);
283105470Smarcel	if (!IS_ELF(*hdr)) {
284105470Smarcel		printf("Missing or corrupted ELF header at %p\n", hdr);
285105470Smarcel		return (EFTYPE);
286105470Smarcel	}
287105470Smarcel
288105470Smarcel	/*
289105470Smarcel	 * Iterate over the segments and register the unwind table if
290105470Smarcel	 * we come across it.
291105470Smarcel	 */
292105470Smarcel	ph = (Elf_Phdr *)(lf->address + hdr->e_phoff);
293105470Smarcel	phlim = ph + hdr->e_phnum;
294105470Smarcel	reloc = ~0ULL;
295105470Smarcel	while (ph < phlim) {
296105470Smarcel		if (ph->p_type == PT_LOAD && reloc == ~0ULL)
297105470Smarcel			reloc = (Elf_Addr)lf->address - ph->p_vaddr;
298105470Smarcel
299105470Smarcel		if (ph->p_type == PT_IA_64_UNWIND) {
300105470Smarcel			vaddr = ph->p_vaddr + reloc;
301115084Smarcel			unw_table_add((vm_offset_t)lf->address, vaddr,
302105470Smarcel			    vaddr + ph->p_memsz);
303105470Smarcel		}
304105470Smarcel		++ph;
305105470Smarcel	}
306105470Smarcel
307192324Smarcel	/*
308192324Smarcel	 * Make the I-cache coherent, but don't worry obout the kernel
309192324Smarcel	 * itself because the loader needs to do that.
310192324Smarcel	 */
311177769Smarcel	if (lf->id != 1)
312192324Smarcel		ia64_sync_icache((uintptr_t)lf->address, lf->size);
313177769Smarcel
314105469Smarcel	return (0);
315105469Smarcel}
316105469Smarcel
317105469Smarcelint
318105470Smarcelelf_cpu_unload_file(linker_file_t lf)
319105469Smarcel{
320105469Smarcel
321115084Smarcel	unw_table_remove((vm_offset_t)lf->address);
322105469Smarcel	return (0);
323105469Smarcel}
324