xref: /freebsd/sys/kern/link_elf_obj.c (revision e9eabf5983586a1e8468d8ba2e3307240dec90d0)
1a4f67738SDoug Rabson /*-
2326e27d8SDoug Rabson  * Copyright (c) 1998-2000 Doug Rabson
3e9eabf59SPeter Wemm  * Copyright (c) 2004 Peter Wemm
4a4f67738SDoug Rabson  * All rights reserved.
5a4f67738SDoug Rabson  *
6a4f67738SDoug Rabson  * Redistribution and use in source and binary forms, with or without
7a4f67738SDoug Rabson  * modification, are permitted provided that the following conditions
8a4f67738SDoug Rabson  * are met:
9a4f67738SDoug Rabson  * 1. Redistributions of source code must retain the above copyright
10a4f67738SDoug Rabson  *    notice, this list of conditions and the following disclaimer.
11a4f67738SDoug Rabson  * 2. Redistributions in binary form must reproduce the above copyright
12a4f67738SDoug Rabson  *    notice, this list of conditions and the following disclaimer in the
13a4f67738SDoug Rabson  *    documentation and/or other materials provided with the distribution.
14a4f67738SDoug Rabson  *
15a4f67738SDoug Rabson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16a4f67738SDoug Rabson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17a4f67738SDoug Rabson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18a4f67738SDoug Rabson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19a4f67738SDoug Rabson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20a4f67738SDoug Rabson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21a4f67738SDoug Rabson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22a4f67738SDoug Rabson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23a4f67738SDoug Rabson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24a4f67738SDoug Rabson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25a4f67738SDoug Rabson  * SUCH DAMAGE.
26a4f67738SDoug Rabson  */
27a4f67738SDoug Rabson 
28677b542eSDavid E. O'Brien #include <sys/cdefs.h>
29677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
30677b542eSDavid E. O'Brien 
31326e27d8SDoug Rabson #include "opt_ddb.h"
32a3df768bSRobert Watson #include "opt_mac.h"
33326e27d8SDoug Rabson 
34a4f67738SDoug Rabson #include <sys/param.h>
35a4f67738SDoug Rabson #include <sys/systm.h>
36fb919e4dSMark Murray #include <sys/kernel.h>
37fb919e4dSMark Murray #include <sys/lock.h>
38a3df768bSRobert Watson #include <sys/mac.h>
39a4f67738SDoug Rabson #include <sys/malloc.h>
40c0b824f9SDima Dorfman #include <sys/mutex.h>
41a4f67738SDoug Rabson #include <sys/proc.h>
42a4f67738SDoug Rabson #include <sys/namei.h>
43a4f67738SDoug Rabson #include <sys/fcntl.h>
44a4f67738SDoug Rabson #include <sys/vnode.h>
45a4f67738SDoug Rabson #include <sys/linker.h>
46fb919e4dSMark Murray 
47a4f67738SDoug Rabson #include <machine/elf.h>
48a4f67738SDoug Rabson 
49fe3db7c7SDoug Rabson #include <vm/vm.h>
50fe3db7c7SDoug Rabson #include <vm/vm_param.h>
51fe3db7c7SDoug Rabson #include <vm/vm_object.h>
52fe3db7c7SDoug Rabson #include <vm/vm_kern.h>
53fe3db7c7SDoug Rabson #include <vm/vm_extern.h>
54fe3db7c7SDoug Rabson #include <vm/pmap.h>
55fe3db7c7SDoug Rabson #include <vm/vm_map.h>
56fb919e4dSMark Murray 
57f9980387SPeter Wemm #include <sys/link_elf.h>
58fe3db7c7SDoug Rabson 
59326e27d8SDoug Rabson #include "linker_if.h"
60a4f67738SDoug Rabson 
61e9eabf59SPeter Wemm typedef struct {
62e9eabf59SPeter Wemm 	void		*addr;
63e9eabf59SPeter Wemm 	Elf_Off		fileoff;
64e9eabf59SPeter Wemm 	Elf_Off		filesz;
65e9eabf59SPeter Wemm 	int		align;
66e9eabf59SPeter Wemm 	int		flags;
67e9eabf59SPeter Wemm 	int		sec;	/* Original section */
68e9eabf59SPeter Wemm 	char		*name;
69e9eabf59SPeter Wemm } Elf_progent;
70e9eabf59SPeter Wemm 
71e9eabf59SPeter Wemm typedef struct {
72e9eabf59SPeter Wemm 	void		*addr;
73e9eabf59SPeter Wemm 	Elf_Off		memsz;
74e9eabf59SPeter Wemm 	int		align;
75e9eabf59SPeter Wemm 	int		flags;
76e9eabf59SPeter Wemm 	int		sec;	/* Original section */
77e9eabf59SPeter Wemm 	char		*name;
78e9eabf59SPeter Wemm } Elf_nobitent;
79e9eabf59SPeter Wemm 
80e9eabf59SPeter Wemm typedef struct {
81e9eabf59SPeter Wemm 	Elf_Rel		*rel;
82e9eabf59SPeter Wemm 	Elf_Off		fileoff;
83e9eabf59SPeter Wemm 	Elf_Off		filesz;
84e9eabf59SPeter Wemm 	int		sec;
85e9eabf59SPeter Wemm } Elf_relent;
86e9eabf59SPeter Wemm 
87e9eabf59SPeter Wemm typedef struct {
88e9eabf59SPeter Wemm 	Elf_Rela	*rela;
89e9eabf59SPeter Wemm 	Elf_Off		fileoff;
90e9eabf59SPeter Wemm 	Elf_Off		filesz;
91e9eabf59SPeter Wemm 	int		sec;
92e9eabf59SPeter Wemm } Elf_relaent;
93e9eabf59SPeter Wemm 
94e9eabf59SPeter Wemm 
95a4f67738SDoug Rabson typedef struct elf_file {
96326e27d8SDoug Rabson 	struct linker_file lf;		/* Common fields */
97fe3db7c7SDoug Rabson 	caddr_t		address;	/* Relocation address */
98fe3db7c7SDoug Rabson 	vm_object_t	object;		/* VM object to hold file pages */
99e9eabf59SPeter Wemm 	Elf_Shdr	*e_shdr;
100e9eabf59SPeter Wemm 
101e9eabf59SPeter Wemm 	Elf_progent	*progtab;
102e9eabf59SPeter Wemm 	int		nprogtab;
103e9eabf59SPeter Wemm 
104e9eabf59SPeter Wemm 	Elf_nobitent	*nobittab;
105e9eabf59SPeter Wemm 	int		nnobittab;
106e9eabf59SPeter Wemm 
107e9eabf59SPeter Wemm 	Elf_relaent	*relatab;
108e9eabf59SPeter Wemm 	int		nrela;
109e9eabf59SPeter Wemm 
110e9eabf59SPeter Wemm 	Elf_relent	*reltab;
111e9eabf59SPeter Wemm 	int		nrel;
112e9eabf59SPeter Wemm 
113e9eabf59SPeter Wemm 	Elf_Sym		*ddbsymtab;	/* The symbol table we are using */
1142d636ab0SPeter Wemm 	long		ddbsymcnt;	/* Number of symbols */
1152d636ab0SPeter Wemm 	caddr_t		ddbstrtab;	/* String table */
1162d636ab0SPeter Wemm 	long		ddbstrcnt;	/* number of bytes in string table */
117e9eabf59SPeter Wemm 
118e9eabf59SPeter Wemm 	caddr_t		shstrtab;	/* Section name string table */
119e9eabf59SPeter Wemm 	long		shstrcnt;	/* number of bytes in string table */
120e9eabf59SPeter Wemm 
121a4f67738SDoug Rabson } *elf_file_t;
122a4f67738SDoug Rabson 
12354823af2SPeter Wemm static int	link_elf_link_preload(linker_class_t cls,
124326e27d8SDoug Rabson 		    const char *, linker_file_t *);
12554823af2SPeter Wemm static int	link_elf_link_preload_finish(linker_file_t);
12654823af2SPeter Wemm static int	link_elf_load_file(linker_class_t, const char *, linker_file_t *);
127326e27d8SDoug Rabson static int	link_elf_lookup_symbol(linker_file_t, const char *,
128326e27d8SDoug Rabson 		    c_linker_sym_t *);
129e9eabf59SPeter Wemm static int	link_elf_symbol_values(linker_file_t, c_linker_sym_t,
130e9eabf59SPeter Wemm 		    linker_symval_t *);
131326e27d8SDoug Rabson static int	link_elf_search_symbol(linker_file_t, caddr_t value,
132326e27d8SDoug Rabson 		    c_linker_sym_t *sym, long *diffp);
133326e27d8SDoug Rabson 
134326e27d8SDoug Rabson static void	link_elf_unload_file(linker_file_t);
135f41325dbSPeter Wemm static int	link_elf_lookup_set(linker_file_t, const char *,
136f41325dbSPeter Wemm 		    void ***, void ***, int *);
137bb9fe9ddSBrian Feldman static int	link_elf_each_function_name(linker_file_t,
138e9eabf59SPeter Wemm 		    int (*)(const char *, void *), void *);
1397251b4bfSJake Burkholder static void	link_elf_reloc_local(linker_file_t);
140326e27d8SDoug Rabson 
141e9eabf59SPeter Wemm static Elf_Addr elf_obj_lookup(linker_file_t lf, Elf_Word symidx, int deps);
142e9eabf59SPeter Wemm 
143326e27d8SDoug Rabson static kobj_method_t link_elf_methods[] = {
144326e27d8SDoug Rabson 	KOBJMETHOD(linker_lookup_symbol,	link_elf_lookup_symbol),
145326e27d8SDoug Rabson 	KOBJMETHOD(linker_symbol_values,	link_elf_symbol_values),
146326e27d8SDoug Rabson 	KOBJMETHOD(linker_search_symbol,	link_elf_search_symbol),
147326e27d8SDoug Rabson 	KOBJMETHOD(linker_unload,		link_elf_unload_file),
14854823af2SPeter Wemm 	KOBJMETHOD(linker_load_file,		link_elf_load_file),
14954823af2SPeter Wemm 	KOBJMETHOD(linker_link_preload,		link_elf_link_preload),
15054823af2SPeter Wemm 	KOBJMETHOD(linker_link_preload_finish,	link_elf_link_preload_finish),
151f41325dbSPeter Wemm 	KOBJMETHOD(linker_lookup_set,		link_elf_lookup_set),
152bb9fe9ddSBrian Feldman 	KOBJMETHOD(linker_each_function_name,	link_elf_each_function_name),
153326e27d8SDoug Rabson 	{ 0, 0 }
154326e27d8SDoug Rabson };
155326e27d8SDoug Rabson 
156326e27d8SDoug Rabson static struct linker_class link_elf_class = {
157326e27d8SDoug Rabson #if ELF_TARG_CLASS == ELFCLASS32
158e9eabf59SPeter Wemm 	"elf32_obj",
159326e27d8SDoug Rabson #else
160e9eabf59SPeter Wemm 	"elf64_obj",
161326e27d8SDoug Rabson #endif
162326e27d8SDoug Rabson 	link_elf_methods, sizeof(struct elf_file)
163326e27d8SDoug Rabson };
164326e27d8SDoug Rabson 
165326e27d8SDoug Rabson static int	relocate_file(elf_file_t ef);
166a4f67738SDoug Rabson 
167a4f67738SDoug Rabson static void
1681720979bSMarcel Moolenaar link_elf_error(const char *s)
1691720979bSMarcel Moolenaar {
1701720979bSMarcel Moolenaar 	printf("kldload: %s\n", s);
1711720979bSMarcel Moolenaar }
1721720979bSMarcel Moolenaar 
1731720979bSMarcel Moolenaar static void
174a4f67738SDoug Rabson link_elf_init(void *arg)
175a4f67738SDoug Rabson {
176a4f67738SDoug Rabson 
177326e27d8SDoug Rabson 	linker_add_class(&link_elf_class);
178a4f67738SDoug Rabson }
179a4f67738SDoug Rabson 
180e9eabf59SPeter Wemm SYSINIT(link_elf_obj, SI_SUB_KLD, SI_ORDER_SECOND, link_elf_init, 0);
181a4f67738SDoug Rabson 
182a4f67738SDoug Rabson static int
183e9eabf59SPeter Wemm link_elf_link_preload(linker_class_t cls, const char *filename,
184e9eabf59SPeter Wemm     linker_file_t *result)
1852d636ab0SPeter Wemm {
186e9eabf59SPeter Wemm 	/* preload not done this way */
187de78ca7eSPeter Wemm 	return (EFTYPE);
188de78ca7eSPeter Wemm }
189de78ca7eSPeter Wemm 
190de78ca7eSPeter Wemm static int
19154823af2SPeter Wemm link_elf_link_preload_finish(linker_file_t lf)
19254823af2SPeter Wemm {
193e9eabf59SPeter Wemm 	/* preload not done this way */
194e9eabf59SPeter Wemm 	return (EFTYPE);
19554823af2SPeter Wemm }
19654823af2SPeter Wemm 
19754823af2SPeter Wemm static int
198c143d6c2SMarcel Moolenaar link_elf_load_file(linker_class_t cls, const char *filename,
199c143d6c2SMarcel Moolenaar     linker_file_t *result)
200a4f67738SDoug Rabson {
201a4f67738SDoug Rabson 	struct nameidata nd;
202b40ce416SJulian Elischer 	struct thread *td = curthread;	/* XXX */
203caab6e90SPeter Wemm 	Elf_Ehdr *hdr;
204e9eabf59SPeter Wemm 	Elf_Shdr *shdr;
205fe3db7c7SDoug Rabson 	int nbytes, i;
206fe3db7c7SDoug Rabson 	caddr_t mapbase;
207fe3db7c7SDoug Rabson 	size_t mapsize;
208a4f67738SDoug Rabson 	int error = 0;
209e6796b67SKirk McKusick 	int resid, flags;
210a4f67738SDoug Rabson 	elf_file_t ef;
211a4f67738SDoug Rabson 	linker_file_t lf;
212ca65d5c7SPeter Wemm 	int symtabindex;
213ca65d5c7SPeter Wemm 	int symstrindex;
214e9eabf59SPeter Wemm 	int shstrindex;
215e9eabf59SPeter Wemm 	int nsym;
216e9eabf59SPeter Wemm 	int pb, nb, rl, ra;
217e9eabf59SPeter Wemm 	int alignmask;
218ca65d5c7SPeter Wemm 
2190cddd8f0SMatthew Dillon 	GIANT_REQUIRED;
2200cddd8f0SMatthew Dillon 
221ca65d5c7SPeter Wemm 	shdr = NULL;
222ca65d5c7SPeter Wemm 	lf = NULL;
223e9eabf59SPeter Wemm 	mapsize = 0;
224e9eabf59SPeter Wemm 	hdr = NULL;
225a4f67738SDoug Rabson 
226b40ce416SJulian Elischer 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, filename, td);
227e6796b67SKirk McKusick 	flags = FREAD;
2287c89f162SPoul-Henning Kamp 	error = vn_open(&nd, &flags, 0, -1);
229a4f67738SDoug Rabson 	if (error)
230a4f67738SDoug Rabson 		return error;
231762e6b85SEivind Eklund 	NDFREE(&nd, NDF_ONLY_PNBUF);
232a3df768bSRobert Watson #ifdef MAC
233e9eabf59SPeter Wemm 	error = mac_check_kld_load(td->td_ucred, nd.ni_vp);
234a3df768bSRobert Watson 	if (error) {
235a3df768bSRobert Watson 		goto out;
236a3df768bSRobert Watson 	}
237a3df768bSRobert Watson #endif
238a4f67738SDoug Rabson 
239e9eabf59SPeter Wemm 	/* Read the elf header from the file. */
240e9eabf59SPeter Wemm 	hdr = malloc(sizeof(*hdr), M_LINKER, M_WAITOK);
241e9eabf59SPeter Wemm 	if (hdr == NULL) {
242caab6e90SPeter Wemm 		error = ENOMEM;
243caab6e90SPeter Wemm 		goto out;
244caab6e90SPeter Wemm 	}
245e9eabf59SPeter Wemm 	error = vn_rdwr(UIO_READ, nd.ni_vp, (void *)hdr, sizeof(*hdr), 0,
2469ca43589SRobert Watson 	    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
2479ca43589SRobert Watson 	    &resid, td);
248a4f67738SDoug Rabson 	if (error)
249a4f67738SDoug Rabson 		goto out;
250e9eabf59SPeter Wemm 	if (resid != 0){
251e9eabf59SPeter Wemm 		error = ENOEXEC;
252e9eabf59SPeter Wemm 		goto out;
253e9eabf59SPeter Wemm 	}
254a4f67738SDoug Rabson 
255caab6e90SPeter Wemm 	if (!IS_ELF(*hdr)) {
256fe3db7c7SDoug Rabson 		error = ENOEXEC;
257a4f67738SDoug Rabson 		goto out;
258fe3db7c7SDoug Rabson 	}
259fe3db7c7SDoug Rabson 
260caab6e90SPeter Wemm 	if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS
261caab6e90SPeter Wemm 	    || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) {
262fe3db7c7SDoug Rabson 		link_elf_error("Unsupported file layout");
263fe3db7c7SDoug Rabson 		error = ENOEXEC;
264fe3db7c7SDoug Rabson 		goto out;
265fe3db7c7SDoug Rabson 	}
266caab6e90SPeter Wemm 	if (hdr->e_ident[EI_VERSION] != EV_CURRENT
267caab6e90SPeter Wemm 	    || hdr->e_version != EV_CURRENT) {
268fe3db7c7SDoug Rabson 		link_elf_error("Unsupported file version");
269fe3db7c7SDoug Rabson 		error = ENOEXEC;
270fe3db7c7SDoug Rabson 		goto out;
271fe3db7c7SDoug Rabson 	}
272e9eabf59SPeter Wemm 	if (hdr->e_type != ET_REL) {
273fe3db7c7SDoug Rabson 		link_elf_error("Unsupported file type");
274fe3db7c7SDoug Rabson 		error = ENOEXEC;
275fe3db7c7SDoug Rabson 		goto out;
276fe3db7c7SDoug Rabson 	}
277caab6e90SPeter Wemm 	if (hdr->e_machine != ELF_TARG_MACH) {
278fe3db7c7SDoug Rabson 		link_elf_error("Unsupported machine");
279fe3db7c7SDoug Rabson 		error = ENOEXEC;
280fe3db7c7SDoug Rabson 		goto out;
281fe3db7c7SDoug Rabson 	}
282a4f67738SDoug Rabson 
283326e27d8SDoug Rabson 	lf = linker_make_file(filename, &link_elf_class);
284326e27d8SDoug Rabson 	if (!lf) {
285326e27d8SDoug Rabson 		error = ENOMEM;
286326e27d8SDoug Rabson 		goto out;
287326e27d8SDoug Rabson 	}
288326e27d8SDoug Rabson 	ef = (elf_file_t) lf;
289e9eabf59SPeter Wemm 	ef->nprogtab = 0;
290e9eabf59SPeter Wemm 	ef->nnobittab = 0;
291e9eabf59SPeter Wemm 	ef->e_shdr = 0;
292e9eabf59SPeter Wemm 	ef->nrel = 0;
293e9eabf59SPeter Wemm 	ef->nrela = 0;
294e9eabf59SPeter Wemm 
295e9eabf59SPeter Wemm 	/* Allocate and read in the section header */
296e9eabf59SPeter Wemm 	nbytes = hdr->e_shnum * hdr->e_shentsize;
297e9eabf59SPeter Wemm 	if (nbytes == 0 || hdr->e_shoff == 0 ||
298e9eabf59SPeter Wemm 	    hdr->e_shentsize != sizeof(Elf_Shdr)) {
299e9eabf59SPeter Wemm 		error = ENOEXEC;
300e9eabf59SPeter Wemm 		goto out;
301e9eabf59SPeter Wemm 	}
302e9eabf59SPeter Wemm 	shdr = malloc(nbytes, M_LINKER, M_WAITOK | M_ZERO);
303e9eabf59SPeter Wemm 	if (shdr == NULL) {
304e9eabf59SPeter Wemm 		error = ENOMEM;
305e9eabf59SPeter Wemm 		goto out;
306e9eabf59SPeter Wemm 	}
307e9eabf59SPeter Wemm 	ef->e_shdr = shdr;
308e9eabf59SPeter Wemm 	error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)shdr, nbytes, hdr->e_shoff,
309e9eabf59SPeter Wemm 	    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED, &resid, td);
310e9eabf59SPeter Wemm 	if (error)
311e9eabf59SPeter Wemm 		goto out;
312e9eabf59SPeter Wemm 	if (resid) {
313e9eabf59SPeter Wemm 		error = ENOEXEC;
314e9eabf59SPeter Wemm 		goto out;
315e9eabf59SPeter Wemm 	}
316e9eabf59SPeter Wemm 
317e9eabf59SPeter Wemm 	/* Scan the section header for information and table sizing. */
318e9eabf59SPeter Wemm 	nsym = 0;
319e9eabf59SPeter Wemm 	symtabindex = -1;
320e9eabf59SPeter Wemm 	symstrindex = -1;
321e9eabf59SPeter Wemm 	for (i = 0; i < hdr->e_shnum; i++) {
322e9eabf59SPeter Wemm 		switch (shdr[i].sh_type) {
323e9eabf59SPeter Wemm 		case SHT_PROGBITS:
324e9eabf59SPeter Wemm 			ef->nprogtab++;
325e9eabf59SPeter Wemm 			break;
326e9eabf59SPeter Wemm 		case SHT_NOBITS:
327e9eabf59SPeter Wemm 			ef->nnobittab++;
328e9eabf59SPeter Wemm 			break;
329e9eabf59SPeter Wemm 		case SHT_SYMTAB:
330e9eabf59SPeter Wemm 			nsym++;
331e9eabf59SPeter Wemm 			symtabindex = i;
332e9eabf59SPeter Wemm 			symstrindex = shdr[i].sh_link;
333e9eabf59SPeter Wemm 			break;
334e9eabf59SPeter Wemm 		case SHT_REL:
335e9eabf59SPeter Wemm 			ef->nrel++;
336e9eabf59SPeter Wemm 			break;
337e9eabf59SPeter Wemm 		case SHT_RELA:
338e9eabf59SPeter Wemm 			ef->nrela++;
339e9eabf59SPeter Wemm 			break;
340e9eabf59SPeter Wemm 		case SHT_STRTAB:
341e9eabf59SPeter Wemm 			break;
342e9eabf59SPeter Wemm 		}
343e9eabf59SPeter Wemm 	}
344e9eabf59SPeter Wemm 	if (ef->nprogtab == 0 && ef->nnobittab == 0) {
345e9eabf59SPeter Wemm 		link_elf_error("file has no contents");
346e9eabf59SPeter Wemm 		error = ENOEXEC;
347e9eabf59SPeter Wemm 		goto out;
348e9eabf59SPeter Wemm 	}
349e9eabf59SPeter Wemm 	if (nsym != 1) {
350e9eabf59SPeter Wemm 		/* Only allow one symbol table for now */
351e9eabf59SPeter Wemm 		link_elf_error("file has no valid symbol table");
352e9eabf59SPeter Wemm 		error = ENOEXEC;
353e9eabf59SPeter Wemm 		goto out;
354e9eabf59SPeter Wemm 	}
355e9eabf59SPeter Wemm 	if (symstrindex < 0 || symstrindex > hdr->e_shnum ||
356e9eabf59SPeter Wemm 	    shdr[symstrindex].sh_type != SHT_STRTAB) {
357e9eabf59SPeter Wemm 		link_elf_error("file has invalid symbol strings");
358e9eabf59SPeter Wemm 		error = ENOEXEC;
359e9eabf59SPeter Wemm 		goto out;
360e9eabf59SPeter Wemm 	}
361e9eabf59SPeter Wemm 
362e9eabf59SPeter Wemm 	/* Allocate space for tracking the load chunks */
363e9eabf59SPeter Wemm 	/* XXX - maybe unneeded. might be able to use the shdr directly */
364e9eabf59SPeter Wemm 	if (ef->nprogtab != 0)
365e9eabf59SPeter Wemm 		ef->progtab = malloc(ef->nprogtab * sizeof(*ef->progtab), M_LINKER, M_WAITOK);
366e9eabf59SPeter Wemm 	if (ef->nnobittab != 0)
367e9eabf59SPeter Wemm 		ef->nobittab = malloc(ef->nnobittab * sizeof(*ef->nobittab), M_LINKER, M_WAITOK);
368e9eabf59SPeter Wemm 	if (ef->nrel != 0)
369e9eabf59SPeter Wemm 		ef->reltab = malloc(ef->nrel * sizeof(*ef->reltab), M_LINKER, M_WAITOK);
370e9eabf59SPeter Wemm 	if (ef->nrela != 0)
371e9eabf59SPeter Wemm 		ef->relatab = malloc(ef->nrela * sizeof(*ef->relatab), M_LINKER, M_WAITOK);
372e9eabf59SPeter Wemm 	/* XXX check for failures */
373e9eabf59SPeter Wemm 
374e9eabf59SPeter Wemm 	/* Space for symbol table */
375e9eabf59SPeter Wemm 	ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym);
376e9eabf59SPeter Wemm 	ef->ddbsymtab = malloc(shdr[symtabindex].sh_size, M_LINKER, M_WAITOK);
377e9eabf59SPeter Wemm 
378e9eabf59SPeter Wemm 	ef->ddbstrcnt = shdr[symstrindex].sh_size;
379e9eabf59SPeter Wemm 	ef->ddbstrtab = malloc(shdr[symstrindex].sh_size, M_LINKER, M_WAITOK);
380e9eabf59SPeter Wemm 
381e9eabf59SPeter Wemm 	if (ef->ddbsymtab == NULL || ef->ddbstrtab == NULL) {
382e9eabf59SPeter Wemm 		error = ENOMEM;
383e9eabf59SPeter Wemm 		goto out;
384e9eabf59SPeter Wemm 	}
385e9eabf59SPeter Wemm 
386e9eabf59SPeter Wemm 	/* Do we have a string table for the section names?  */
387e9eabf59SPeter Wemm 	shstrindex = -1;
388e9eabf59SPeter Wemm 	if (hdr->e_shstrndx != 0 && shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) {
389e9eabf59SPeter Wemm 		shstrindex = hdr->e_shstrndx;
390e9eabf59SPeter Wemm 		ef->shstrcnt = shdr[shstrindex].sh_size;
391e9eabf59SPeter Wemm 		ef->shstrtab = malloc(shdr[shstrindex].sh_size, M_LINKER, M_WAITOK);
392e9eabf59SPeter Wemm 		if (ef->shstrtab == NULL) {
393e9eabf59SPeter Wemm 			error = ENOMEM;
394e9eabf59SPeter Wemm 			goto out;
395e9eabf59SPeter Wemm 		}
396e9eabf59SPeter Wemm 		error = vn_rdwr(UIO_READ, nd.ni_vp,
397e9eabf59SPeter Wemm 		    ef->shstrtab, shdr[shstrindex].sh_size, shdr[shstrindex].sh_offset,
398e9eabf59SPeter Wemm 		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
399e9eabf59SPeter Wemm 		    &resid, td);
400e9eabf59SPeter Wemm 		if (error)
401e9eabf59SPeter Wemm 			goto out;
402e9eabf59SPeter Wemm 	}
403e9eabf59SPeter Wemm 
404e9eabf59SPeter Wemm 	/* Size code/data(progbits) and bss(nobits).  allocate space for relocs */
405e9eabf59SPeter Wemm 	pb = 0;
406e9eabf59SPeter Wemm 	nb = 0;
407e9eabf59SPeter Wemm 	rl = 0;
408e9eabf59SPeter Wemm 	ra = 0;
409e9eabf59SPeter Wemm 	alignmask = 0;
410e9eabf59SPeter Wemm 	for (i = 0; i < hdr->e_shnum; i++) {
411e9eabf59SPeter Wemm 		switch (shdr[i].sh_type) {
412e9eabf59SPeter Wemm 		case SHT_PROGBITS:
413e9eabf59SPeter Wemm 		case SHT_NOBITS:
414e9eabf59SPeter Wemm 			alignmask = shdr[i].sh_addralign - 1;
415e9eabf59SPeter Wemm 			mapsize += alignmask;
416e9eabf59SPeter Wemm 			mapsize &= ~alignmask;
417e9eabf59SPeter Wemm 			break;
418e9eabf59SPeter Wemm 		}
419e9eabf59SPeter Wemm 
420e9eabf59SPeter Wemm 		switch (shdr[i].sh_type) {
421e9eabf59SPeter Wemm 		case SHT_PROGBITS:
422e9eabf59SPeter Wemm 			ef->progtab[pb].addr = (void *)(uintptr_t)mapsize;
423e9eabf59SPeter Wemm 			ef->progtab[pb].fileoff = shdr[i].sh_offset;
424e9eabf59SPeter Wemm 			ef->progtab[pb].filesz = shdr[i].sh_size;
425e9eabf59SPeter Wemm 			ef->progtab[pb].align = shdr[i].sh_addralign;
426e9eabf59SPeter Wemm 			ef->progtab[pb].sec = i;
427e9eabf59SPeter Wemm 			if (ef->shstrtab && shdr[i].sh_name != 0)
428e9eabf59SPeter Wemm 				ef->progtab[pb].name = ef->shstrtab + shdr[i].sh_name;
429e9eabf59SPeter Wemm 			else
430e9eabf59SPeter Wemm 				ef->progtab[pb].name = "<<PROGBITS>>";
431e9eabf59SPeter Wemm 			mapsize += shdr[i].sh_size;
432e9eabf59SPeter Wemm 			pb++;
433e9eabf59SPeter Wemm 			break;
434e9eabf59SPeter Wemm 		case SHT_NOBITS:
435e9eabf59SPeter Wemm 			ef->nobittab[nb].addr = (void *)(uintptr_t)mapsize;
436e9eabf59SPeter Wemm 			ef->nobittab[nb].memsz = shdr[i].sh_size;
437e9eabf59SPeter Wemm 			ef->nobittab[nb].align = shdr[i].sh_addralign;
438e9eabf59SPeter Wemm 			ef->nobittab[nb].sec = i;
439e9eabf59SPeter Wemm 			if (ef->shstrtab && shdr[i].sh_name != 0)
440e9eabf59SPeter Wemm 				ef->nobittab[nb].name = ef->shstrtab + shdr[i].sh_name;
441e9eabf59SPeter Wemm 			else
442e9eabf59SPeter Wemm 				ef->nobittab[nb].name = "<<NOBITS>>";
443e9eabf59SPeter Wemm 			mapsize += shdr[i].sh_size;
444e9eabf59SPeter Wemm 			nb++;
445e9eabf59SPeter Wemm 			break;
446e9eabf59SPeter Wemm 		case SHT_REL:
447e9eabf59SPeter Wemm 			ef->reltab[rl].rel = malloc(shdr[i].sh_size, M_LINKER, M_WAITOK);
448e9eabf59SPeter Wemm 			ef->reltab[rl].fileoff = shdr[i].sh_offset;
449e9eabf59SPeter Wemm 			ef->reltab[rl].filesz = shdr[i].sh_size;
450e9eabf59SPeter Wemm 			ef->reltab[rl].sec = shdr[i].sh_info;
451e9eabf59SPeter Wemm 			rl++;
452e9eabf59SPeter Wemm 			break;
453e9eabf59SPeter Wemm 		case SHT_RELA:
454e9eabf59SPeter Wemm 			ef->relatab[ra].rela = malloc(shdr[i].sh_size, M_LINKER, M_WAITOK);
455e9eabf59SPeter Wemm 			ef->relatab[ra].fileoff = shdr[i].sh_offset;
456e9eabf59SPeter Wemm 			ef->relatab[ra].filesz = shdr[i].sh_size;
457e9eabf59SPeter Wemm 			ef->relatab[ra].sec = shdr[i].sh_info;
458e9eabf59SPeter Wemm 			ra++;
459e9eabf59SPeter Wemm 			break;
460e9eabf59SPeter Wemm 		}
461e9eabf59SPeter Wemm 	}
462e9eabf59SPeter Wemm 	if (pb != ef->nprogtab)
463e9eabf59SPeter Wemm 		panic("lots progbits");
464e9eabf59SPeter Wemm 	if (nb != ef->nnobittab)
465e9eabf59SPeter Wemm 		panic("lots nobits");
466e9eabf59SPeter Wemm 	if (rl != ef->nrel)
467e9eabf59SPeter Wemm 		panic("lots rel");
468e9eabf59SPeter Wemm 	if (ra != ef->nrela)
469e9eabf59SPeter Wemm 		panic("lots rela");
470e9eabf59SPeter Wemm 
471e9eabf59SPeter Wemm 	/*
472e9eabf59SPeter Wemm 	 * We know how much space we need for the text/data/bss/etc.
473e9eabf59SPeter Wemm 	 * This stuff needs to be in a single chunk so that profiling etc
474e9eabf59SPeter Wemm 	 * can get the bounds and gdb can associate offsets with modules
475e9eabf59SPeter Wemm 	 */
476e9eabf59SPeter Wemm 	ef->object = vm_object_allocate(OBJT_DEFAULT, round_page(mapsize) >> PAGE_SHIFT);
477fe3db7c7SDoug Rabson 	if (ef->object == NULL) {
478fe3db7c7SDoug Rabson 		error = ENOMEM;
479fe3db7c7SDoug Rabson 		goto out;
480fe3db7c7SDoug Rabson 	}
481fe3db7c7SDoug Rabson 	vm_object_reference(ef->object);
482fe3db7c7SDoug Rabson 	ef->address = (caddr_t) vm_map_min(kernel_map);
483fe3db7c7SDoug Rabson 	error = vm_map_find(kernel_map, ef->object, 0,
484e9eabf59SPeter Wemm 	    (vm_offset_t *) &ef->address, mapsize, TRUE, VM_PROT_ALL, VM_PROT_ALL, FALSE);
485fe3db7c7SDoug Rabson 	if (error) {
486fe3db7c7SDoug Rabson 		vm_object_deallocate(ef->object);
487326e27d8SDoug Rabson 		ef->object = 0;
488fe3db7c7SDoug Rabson 		goto out;
489fe3db7c7SDoug Rabson 	}
490fe3db7c7SDoug Rabson 	mapbase = ef->address;
491e9eabf59SPeter Wemm 	/* Wire the pages */
492e9eabf59SPeter Wemm 	vm_map_wire(kernel_map, (vm_offset_t)mapbase,
493e9eabf59SPeter Wemm 	    (vm_offset_t)mapbase + round_page(mapsize),
494e9eabf59SPeter Wemm 	    VM_MAP_WIRE_SYSTEM|VM_MAP_WIRE_NOHOLES);
495a4f67738SDoug Rabson 
496e9eabf59SPeter Wemm 	/* Add the base address to the previously calculated/aligned offsets */
497e9eabf59SPeter Wemm 	for (i = 0; i < ef->nprogtab; i++)
498e9eabf59SPeter Wemm 		ef->progtab[i].addr = mapbase + (uintptr_t)ef->progtab[i].addr;
499e9eabf59SPeter Wemm 
500e9eabf59SPeter Wemm 	for (i = 0; i < ef->nnobittab; i++)
501e9eabf59SPeter Wemm 		ef->nobittab[i].addr = mapbase + (uintptr_t)ef->nobittab[i].addr;
502e9eabf59SPeter Wemm 
503e9eabf59SPeter Wemm 
504e9eabf59SPeter Wemm 	/* Load the symbol table. */
505fe3db7c7SDoug Rabson 	error = vn_rdwr(UIO_READ, nd.ni_vp,
506e9eabf59SPeter Wemm 	    (void *)ef->ddbsymtab, shdr[symtabindex].sh_size, shdr[symtabindex].sh_offset,
5079ca43589SRobert Watson 	    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
5089ca43589SRobert Watson 	    &resid, td);
509ca65d5c7SPeter Wemm 	if (error)
510de78ca7eSPeter Wemm 		goto out;
511e9eabf59SPeter Wemm 	error = vn_rdwr(UIO_READ, nd.ni_vp,
512e9eabf59SPeter Wemm 	    ef->ddbstrtab, shdr[symstrindex].sh_size, shdr[symstrindex].sh_offset,
513e9eabf59SPeter Wemm 	    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
514e9eabf59SPeter Wemm 	    &resid, td);
515e9eabf59SPeter Wemm 	if (error)
516e9eabf59SPeter Wemm 		goto out;
517e9eabf59SPeter Wemm 
518e9eabf59SPeter Wemm 	/* Read in the text/data/set/etc sections */
519e9eabf59SPeter Wemm 	for (i = 0; i < ef->nprogtab; i++) {
520e9eabf59SPeter Wemm 		error = vn_rdwr(UIO_READ, nd.ni_vp,
521e9eabf59SPeter Wemm 		    ef->progtab[i].addr,
522e9eabf59SPeter Wemm 		    ef->progtab[i].filesz,
523e9eabf59SPeter Wemm 		    ef->progtab[i].fileoff,
524e9eabf59SPeter Wemm 		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
525e9eabf59SPeter Wemm 		    &resid, td);
526e9eabf59SPeter Wemm 		if (error)
527e9eabf59SPeter Wemm 			goto out;
528e9eabf59SPeter Wemm 	}
529e9eabf59SPeter Wemm 
530e9eabf59SPeter Wemm 	/*
531e9eabf59SPeter Wemm 	 * Read in relocation tables.  Platforms use rel or rela, but
532e9eabf59SPeter Wemm 	 * usually not both.
533e9eabf59SPeter Wemm 	 */
534e9eabf59SPeter Wemm 	for (i = 0; i < ef->nrel; i++) {
535e9eabf59SPeter Wemm 		error = vn_rdwr(UIO_READ, nd.ni_vp,
536e9eabf59SPeter Wemm 		    (void *)ef->reltab[i].rel,
537e9eabf59SPeter Wemm 		    ef->reltab[i].filesz,
538e9eabf59SPeter Wemm 		    ef->reltab[i].fileoff,
539e9eabf59SPeter Wemm 		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
540e9eabf59SPeter Wemm 		    &resid, td);
541e9eabf59SPeter Wemm 		if (error)
542e9eabf59SPeter Wemm 			goto out;
543e9eabf59SPeter Wemm 	}
544e9eabf59SPeter Wemm 	for (i = 0; i < ef->nrela; i++) {
545e9eabf59SPeter Wemm 		error = vn_rdwr(UIO_READ, nd.ni_vp,
546e9eabf59SPeter Wemm 		    (void *)ef->relatab[i].rela,
547e9eabf59SPeter Wemm 		    ef->relatab[i].filesz,
548e9eabf59SPeter Wemm 		    ef->relatab[i].fileoff,
549e9eabf59SPeter Wemm 		    UIO_SYSSPACE, IO_NODELOCKED, td->td_ucred, NOCRED,
550e9eabf59SPeter Wemm 		    &resid, td);
551e9eabf59SPeter Wemm 		if (error)
552e9eabf59SPeter Wemm 			goto out;
553e9eabf59SPeter Wemm 	}
554e9eabf59SPeter Wemm 
555e9eabf59SPeter Wemm 	/* Inform the kld system about the situation */
556e9eabf59SPeter Wemm 	lf->address = ef->address = mapbase;
557e9eabf59SPeter Wemm 	lf->size = mapsize;
558e9eabf59SPeter Wemm 
559e9eabf59SPeter Wemm 	/* Local intra-module relocations */
5607251b4bfSJake Burkholder 	link_elf_reloc_local(lf);
5617251b4bfSJake Burkholder 
562e9eabf59SPeter Wemm 	/* Pull in dependencies */
5637b9716baSIan Dowse 	error = linker_load_dependencies(lf);
564ca65d5c7SPeter Wemm 	if (error)
565de78ca7eSPeter Wemm 		goto out;
566e9eabf59SPeter Wemm 
567e9eabf59SPeter Wemm 	/* External relocations */
568326e27d8SDoug Rabson 	error = relocate_file(ef);
569ca65d5c7SPeter Wemm 	if (error)
570ca65d5c7SPeter Wemm 		goto out;
571ca65d5c7SPeter Wemm 
572e9eabf59SPeter Wemm 	/* Notify MD code that a module is being loaded. */
573e9eabf59SPeter Wemm 	error = elf_cpu_load_file(lf);
574ca65d5c7SPeter Wemm 	if (error)
575ca65d5c7SPeter Wemm 		goto out;
576a4f67738SDoug Rabson 
577a4f67738SDoug Rabson 	*result = lf;
578a4f67738SDoug Rabson 
579a4f67738SDoug Rabson out:
580ca65d5c7SPeter Wemm 	if (error && lf)
581ca65d5c7SPeter Wemm 		linker_file_unload(lf);
582e9eabf59SPeter Wemm 	if (hdr)
583e9eabf59SPeter Wemm 		free(hdr, M_LINKER);
584b40ce416SJulian Elischer 	VOP_UNLOCK(nd.ni_vp, 0, td);
585a854ed98SJohn Baldwin 	vn_close(nd.ni_vp, FREAD, td->td_ucred, td);
586a4f67738SDoug Rabson 
587a4f67738SDoug Rabson 	return error;
588a4f67738SDoug Rabson }
589a4f67738SDoug Rabson 
590a4f67738SDoug Rabson static void
591de78ca7eSPeter Wemm link_elf_unload_file(linker_file_t file)
592a4f67738SDoug Rabson {
593326e27d8SDoug Rabson 	elf_file_t ef = (elf_file_t) file;
594a4f67738SDoug Rabson 
5951aeb23cdSMarcel Moolenaar 	/* Notify MD code that a module is being unloaded. */
5961aeb23cdSMarcel Moolenaar 	elf_cpu_unload_file(file);
5971aeb23cdSMarcel Moolenaar 
598fe3db7c7SDoug Rabson 	if (ef->object) {
599fe3db7c7SDoug Rabson 		vm_map_remove(kernel_map, (vm_offset_t) ef->address,
600e9eabf59SPeter Wemm 		    (vm_offset_t) ef->address + (ef->object->size << PAGE_SHIFT));
601fe3db7c7SDoug Rabson 		vm_object_deallocate(ef->object);
602fe3db7c7SDoug Rabson 	}
603e9eabf59SPeter Wemm 	if (ef->e_shdr)
604e9eabf59SPeter Wemm 		free(ef->e_shdr, M_LINKER);
605e9eabf59SPeter Wemm 	if (ef->ddbsymtab)
606e9eabf59SPeter Wemm 		free(ef->ddbsymtab, M_LINKER);
607e9eabf59SPeter Wemm 	if (ef->ddbstrtab)
608e9eabf59SPeter Wemm 		free(ef->ddbstrtab, M_LINKER);
609de78ca7eSPeter Wemm }
610de78ca7eSPeter Wemm 
611fe3db7c7SDoug Rabson static const char *
612aa855a59SPeter Wemm symbol_name(elf_file_t ef, Elf_Word r_info)
613a4f67738SDoug Rabson {
614fe3db7c7SDoug Rabson 	const Elf_Sym *ref;
615a4f67738SDoug Rabson 
616aa855a59SPeter Wemm 	if (ELF_R_SYM(r_info)) {
617e9eabf59SPeter Wemm 		ref = ef->ddbsymtab + ELF_R_SYM(r_info);
618e9eabf59SPeter Wemm 		return ef->ddbstrtab + ref->st_name;
619fe3db7c7SDoug Rabson 	} else
620fe3db7c7SDoug Rabson 		return NULL;
621a4f67738SDoug Rabson }
622a4f67738SDoug Rabson 
623e9eabf59SPeter Wemm static Elf_Addr
624e9eabf59SPeter Wemm findbase(elf_file_t ef, int sec)
625e9eabf59SPeter Wemm {
626e9eabf59SPeter Wemm 	int i;
627e9eabf59SPeter Wemm 	Elf_Addr base = 0;
628e9eabf59SPeter Wemm 
629e9eabf59SPeter Wemm 	for (i = 0; i < ef->nprogtab; i++) {
630e9eabf59SPeter Wemm 		if (sec == ef->progtab[i].sec)
631e9eabf59SPeter Wemm 			base = (Elf_Addr)ef->progtab[i].addr;
632e9eabf59SPeter Wemm 	}
633e9eabf59SPeter Wemm 	if (base == 0) {
634e9eabf59SPeter Wemm 		for (i = 0; i < ef->nnobittab; i++) {
635e9eabf59SPeter Wemm 			if (sec == ef->nobittab[i].sec)
636e9eabf59SPeter Wemm 				base = (Elf_Addr)ef->nobittab[i].addr;
637e9eabf59SPeter Wemm 		}
638e9eabf59SPeter Wemm 	}
639e9eabf59SPeter Wemm 	if (base == 0)
640e9eabf59SPeter Wemm 		base = (Elf_Addr)ef->address;
641e9eabf59SPeter Wemm 	return base;
642e9eabf59SPeter Wemm }
643e9eabf59SPeter Wemm 
644a4f67738SDoug Rabson static int
645326e27d8SDoug Rabson relocate_file(elf_file_t ef)
646a4f67738SDoug Rabson {
647fe3db7c7SDoug Rabson 	const Elf_Rel *rellim;
648fe3db7c7SDoug Rabson 	const Elf_Rel *rel;
649fe3db7c7SDoug Rabson 	const Elf_Rela *relalim;
650fe3db7c7SDoug Rabson 	const Elf_Rela *rela;
651aa855a59SPeter Wemm 	const char *symname;
652e9eabf59SPeter Wemm 	const Elf_Sym *sym;
653e9eabf59SPeter Wemm 	int i;
654e9eabf59SPeter Wemm         Elf_Word symidx;
655e9eabf59SPeter Wemm 	Elf_Addr base;
656e9eabf59SPeter Wemm 
657a4f67738SDoug Rabson 
658fe3db7c7SDoug Rabson 	/* Perform relocations without addend if there are any: */
659e9eabf59SPeter Wemm 	for (i = 0; i < ef->nrel; i++) {
660e9eabf59SPeter Wemm 		rel = ef->reltab[i].rel;
661aa855a59SPeter Wemm 		if (rel) {
662e9eabf59SPeter Wemm 			rellim = (const Elf_Rel *)((const char *)rel + ef->reltab[i].filesz);
663e9eabf59SPeter Wemm 			base = findbase(ef, ef->reltab[i].sec);
664aa855a59SPeter Wemm 			while (rel < rellim) {
665e9eabf59SPeter Wemm 				symidx = ELF_R_SYM(rel->r_info);
666e9eabf59SPeter Wemm 				if (symidx < ef->ddbsymcnt) {
667e9eabf59SPeter Wemm 					sym = ef->ddbsymtab + symidx;
668e9eabf59SPeter Wemm 					if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) {
669e9eabf59SPeter Wemm 						if (elf_reloc(&ef->lf, base, rel, ELF_RELOC_REL, elf_obj_lookup)) {
670aa855a59SPeter Wemm 							symname = symbol_name(ef, rel->r_info);
671ef83592dSPeter Wemm 							printf("link_elf: symbol %s undefined\n", symname);
672a4f67738SDoug Rabson 							return ENOENT;
673a13ddfb6SPeter Wemm 						}
674aa855a59SPeter Wemm 					}
675a4f67738SDoug Rabson 				}
676aa855a59SPeter Wemm 				rel++;
677aa855a59SPeter Wemm 			}
678a4f67738SDoug Rabson 		}
679e9eabf59SPeter Wemm 	}
680a4f67738SDoug Rabson 
681fe3db7c7SDoug Rabson 	/* Perform relocations with addend if there are any: */
682e9eabf59SPeter Wemm 	for (i = 0; i < ef->nrela; i++) {
683e9eabf59SPeter Wemm 		rela = ef->relatab[i].rela;
684aa855a59SPeter Wemm 		if (rela) {
685e9eabf59SPeter Wemm 			relalim = (const Elf_Rela *)((const char *)rela + ef->relatab[i].filesz);
686e9eabf59SPeter Wemm 			base = findbase(ef, ef->relatab[i].sec);
687aa855a59SPeter Wemm 			while (rela < relalim) {
688e9eabf59SPeter Wemm 				symidx = ELF_R_SYM(rela->r_info);
689e9eabf59SPeter Wemm 				if (symidx < ef->ddbsymcnt) {
690e9eabf59SPeter Wemm 					sym = ef->ddbsymtab + symidx;
691e9eabf59SPeter Wemm 					if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) {
692e9eabf59SPeter Wemm 						if (elf_reloc(&ef->lf, base, rela, ELF_RELOC_RELA, elf_obj_lookup)) {
693aa855a59SPeter Wemm 							symname = symbol_name(ef, rela->r_info);
694ef83592dSPeter Wemm 							printf("link_elf: symbol %s undefined\n", symname);
695fe3db7c7SDoug Rabson 							return ENOENT;
696a13ddfb6SPeter Wemm 						}
697e9eabf59SPeter Wemm 					}
698e9eabf59SPeter Wemm 				}
699aa855a59SPeter Wemm 				rela++;
700aa855a59SPeter Wemm 			}
701a4f67738SDoug Rabson 		}
702e9eabf59SPeter Wemm 	}
703a4f67738SDoug Rabson 
704a4f67738SDoug Rabson 	return 0;
705a4f67738SDoug Rabson }
706a4f67738SDoug Rabson 
70737c84183SPoul-Henning Kamp static int
708d254af07SMatthew Dillon link_elf_lookup_symbol(linker_file_t lf, const char *name, c_linker_sym_t *sym)
709a4f67738SDoug Rabson {
710326e27d8SDoug Rabson 	elf_file_t ef = (elf_file_t) lf;
7112d636ab0SPeter Wemm 	const Elf_Sym *symp;
7122d636ab0SPeter Wemm 	const char *strp;
713a4f67738SDoug Rabson 	int i;
714a4f67738SDoug Rabson 
715e9eabf59SPeter Wemm /* XXX search for globals first */
7162d636ab0SPeter Wemm 	/* Exhaustive search */
7172d636ab0SPeter Wemm 	for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
7182d636ab0SPeter Wemm 		strp = ef->ddbstrtab + symp->st_name;
7192d636ab0SPeter Wemm 		if (strcmp(name, strp) == 0) {
7202d636ab0SPeter Wemm 			if (symp->st_shndx != SHN_UNDEF ||
7212d636ab0SPeter Wemm 			    (symp->st_value != 0 &&
7222d636ab0SPeter Wemm 				ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
723d254af07SMatthew Dillon 				*sym = (c_linker_sym_t) symp;
7242d636ab0SPeter Wemm 				return 0;
7252d636ab0SPeter Wemm 			} else
7262d636ab0SPeter Wemm 				return ENOENT;
7272d636ab0SPeter Wemm 		}
7282d636ab0SPeter Wemm 	}
7292d636ab0SPeter Wemm 
730a4f67738SDoug Rabson 	return ENOENT;
731a4f67738SDoug Rabson }
732a4f67738SDoug Rabson 
733de78ca7eSPeter Wemm static int
734fe08c21aSMatthew Dillon link_elf_symbol_values(linker_file_t lf, c_linker_sym_t sym, linker_symval_t *symval)
735a4f67738SDoug Rabson {
736326e27d8SDoug Rabson 	elf_file_t ef = (elf_file_t) lf;
7378aef1712SMatthew Dillon 	const Elf_Sym *es = (const Elf_Sym*) sym;
738a4f67738SDoug Rabson 
7395cf87418SMarcel Moolenaar 	if (es >= ef->ddbsymtab && es < (ef->ddbsymtab + ef->ddbsymcnt)) {
7402d636ab0SPeter Wemm 		symval->name = ef->ddbstrtab + es->st_name;
7412d636ab0SPeter Wemm 		symval->value = (caddr_t) ef->address + es->st_value;
7422d636ab0SPeter Wemm 		symval->size = es->st_size;
7432d636ab0SPeter Wemm 		return 0;
7442d636ab0SPeter Wemm 	}
7452d636ab0SPeter Wemm 	return ENOENT;
7462d636ab0SPeter Wemm }
747a4f67738SDoug Rabson 
748a4f67738SDoug Rabson static int
749a4f67738SDoug Rabson link_elf_search_symbol(linker_file_t lf, caddr_t value,
750d254af07SMatthew Dillon     c_linker_sym_t *sym, long *diffp)
751a4f67738SDoug Rabson {
752326e27d8SDoug Rabson 	elf_file_t ef = (elf_file_t) lf;
753586453feSBruce Evans 	u_long off = (uintptr_t) (void *) value;
754a4f67738SDoug Rabson 	u_long diff = off;
755586453feSBruce Evans 	u_long st_value;
756fe3db7c7SDoug Rabson 	const Elf_Sym *es;
757fe3db7c7SDoug Rabson 	const Elf_Sym *best = 0;
758a4f67738SDoug Rabson 	int i;
759a4f67738SDoug Rabson 
7602d636ab0SPeter Wemm 	for (i = 0, es = ef->ddbsymtab; i < ef->ddbsymcnt; i++, es++) {
761a4f67738SDoug Rabson 		if (es->st_name == 0)
762a4f67738SDoug Rabson 			continue;
763586453feSBruce Evans 		st_value = es->st_value + (uintptr_t) (void *) ef->address;
764b5abfb70SPeter Wemm 		if (off >= st_value) {
765b5abfb70SPeter Wemm 			if (off - st_value < diff) {
766b5abfb70SPeter Wemm 				diff = off - st_value;
767a4f67738SDoug Rabson 				best = es;
768a4f67738SDoug Rabson 				if (diff == 0)
769a4f67738SDoug Rabson 					break;
770b5abfb70SPeter Wemm 			} else if (off - st_value == diff) {
771a4f67738SDoug Rabson 				best = es;
772a4f67738SDoug Rabson 			}
773a4f67738SDoug Rabson 		}
774a4f67738SDoug Rabson 	}
775a4f67738SDoug Rabson 	if (best == 0)
776a4f67738SDoug Rabson 		*diffp = off;
777a4f67738SDoug Rabson 	else
778a4f67738SDoug Rabson 		*diffp = diff;
779d254af07SMatthew Dillon 	*sym = (c_linker_sym_t) best;
780a4f67738SDoug Rabson 
781a4f67738SDoug Rabson 	return 0;
782a4f67738SDoug Rabson }
783f41325dbSPeter Wemm 
784f41325dbSPeter Wemm /*
785f41325dbSPeter Wemm  * Look up a linker set on an ELF system.
786f41325dbSPeter Wemm  */
787f41325dbSPeter Wemm static int
788f41325dbSPeter Wemm link_elf_lookup_set(linker_file_t lf, const char *name,
789f41325dbSPeter Wemm     void ***startp, void ***stopp, int *countp)
790f41325dbSPeter Wemm {
791e9eabf59SPeter Wemm 	elf_file_t ef = (elf_file_t)lf;
792f41325dbSPeter Wemm 	void **start, **stop;
793e9eabf59SPeter Wemm 	int i, count;
794f41325dbSPeter Wemm 
795e9eabf59SPeter Wemm 	/* Relative to section number */
796e9eabf59SPeter Wemm 	for (i = 0; i < ef->nprogtab; i++) {
797e9eabf59SPeter Wemm 		if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) &&
798e9eabf59SPeter Wemm 		    strcmp(ef->progtab[i].name + 4, name) == 0) {
799e9eabf59SPeter Wemm 			start  = (void **)ef->progtab[i].addr;
800e9eabf59SPeter Wemm 			stop = (void **)((char *)ef->progtab[i].addr + ef->progtab[i].filesz);
801f41325dbSPeter Wemm 			count = stop - start;
802f41325dbSPeter Wemm 			if (startp)
803f41325dbSPeter Wemm 				*startp = start;
804f41325dbSPeter Wemm 			if (stopp)
805f41325dbSPeter Wemm 				*stopp = stop;
806f41325dbSPeter Wemm 			if (countp)
807f41325dbSPeter Wemm 				*countp = count;
808e9eabf59SPeter Wemm 			return (0);
809e9eabf59SPeter Wemm 		}
810e9eabf59SPeter Wemm 	}
811e9eabf59SPeter Wemm 	return (ESRCH);
812f41325dbSPeter Wemm }
813bb9fe9ddSBrian Feldman 
814bb9fe9ddSBrian Feldman static int
815bb9fe9ddSBrian Feldman link_elf_each_function_name(linker_file_t file,
816e9eabf59SPeter Wemm     int (*callback)(const char *, void *), void *opaque)
817e9eabf59SPeter Wemm {
818bb9fe9ddSBrian Feldman 	elf_file_t ef = (elf_file_t)file;
819bb9fe9ddSBrian Feldman 	const Elf_Sym *symp;
820bb9fe9ddSBrian Feldman 	int i, error;
821bb9fe9ddSBrian Feldman 
822bb9fe9ddSBrian Feldman 	/* Exhaustive search */
823bb9fe9ddSBrian Feldman 	for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
824bb9fe9ddSBrian Feldman 		if (symp->st_value != 0 &&
825bb9fe9ddSBrian Feldman 		    ELF_ST_TYPE(symp->st_info) == STT_FUNC) {
826bb9fe9ddSBrian Feldman 			error = callback(ef->ddbstrtab + symp->st_name, opaque);
827bb9fe9ddSBrian Feldman 			if (error)
828bb9fe9ddSBrian Feldman 				return (error);
829bb9fe9ddSBrian Feldman 		}
830bb9fe9ddSBrian Feldman 	}
831bb9fe9ddSBrian Feldman 	return (0);
832bb9fe9ddSBrian Feldman }
83384201059SMarcel Moolenaar 
834d297ad16SMarcel Moolenaar /*
835d297ad16SMarcel Moolenaar  * Symbol lookup function that can be used when the symbol index is known (ie
836d297ad16SMarcel Moolenaar  * in relocations). It uses the symbol index instead of doing a fully fledged
837d297ad16SMarcel Moolenaar  * hash table based lookup when such is valid. For example for local symbols.
838d297ad16SMarcel Moolenaar  * This is not only more efficient, it's also more correct. It's not always
839d297ad16SMarcel Moolenaar  * the case that the symbol can be found through the hash table.
840d297ad16SMarcel Moolenaar  */
841e9eabf59SPeter Wemm static Elf_Addr
842e9eabf59SPeter Wemm elf_obj_lookup(linker_file_t lf, Elf_Word symidx, int deps)
843d297ad16SMarcel Moolenaar {
844d297ad16SMarcel Moolenaar 	elf_file_t ef = (elf_file_t)lf;
845d297ad16SMarcel Moolenaar 	const Elf_Sym *sym;
846d297ad16SMarcel Moolenaar 	const char *symbol;
847e9eabf59SPeter Wemm 	Elf_Addr ret;
848e9eabf59SPeter Wemm 	int i;
849d297ad16SMarcel Moolenaar 
850d297ad16SMarcel Moolenaar 	/* Don't even try to lookup the symbol if the index is bogus. */
851e9eabf59SPeter Wemm 	if (symidx >= ef->ddbsymcnt)
852d297ad16SMarcel Moolenaar 		return (0);
853d297ad16SMarcel Moolenaar 
854e9eabf59SPeter Wemm 	sym = ef->ddbsymtab + symidx;
855d297ad16SMarcel Moolenaar 
856e9eabf59SPeter Wemm 	/* Theoretically we can avoid a lookup for some locals */
857e9eabf59SPeter Wemm 	switch (ELF64_ST_BIND(sym->st_info)) {
858e9eabf59SPeter Wemm 	case STB_LOCAL:
859e9eabf59SPeter Wemm 		/* Local, but undefined? huh? */
860e9eabf59SPeter Wemm 		if (sym->st_shndx == SHN_UNDEF)
861d297ad16SMarcel Moolenaar 			return (0);
862e9eabf59SPeter Wemm 		ret = 0;
863e9eabf59SPeter Wemm 		/* Relative to section number */
864e9eabf59SPeter Wemm 		for (i = 0; i < ef->nprogtab; i++) {
865e9eabf59SPeter Wemm 			if (sym->st_shndx == ef->progtab[i].sec) {
866e9eabf59SPeter Wemm 				ret = (Elf_Addr)ef->progtab[i].addr;
867e9eabf59SPeter Wemm 				break;
868d297ad16SMarcel Moolenaar 			}
869e9eabf59SPeter Wemm 		}
870e9eabf59SPeter Wemm 		if (ret == 0) {
871e9eabf59SPeter Wemm 			for (i = 0; i < ef->nnobittab; i++) {
872e9eabf59SPeter Wemm 				if (sym->st_shndx == ef->nobittab[i].sec) {
873e9eabf59SPeter Wemm 					ret = (Elf_Addr)ef->nobittab[i].addr;
874e9eabf59SPeter Wemm 					break;
875e9eabf59SPeter Wemm 				}
876e9eabf59SPeter Wemm 			}
877e9eabf59SPeter Wemm 		}
878e9eabf59SPeter Wemm 		return ret + sym->st_value;
879d297ad16SMarcel Moolenaar 
880e9eabf59SPeter Wemm 	case STB_GLOBAL:
881e9eabf59SPeter Wemm 		/* Relative to Data or Function name */
882e9eabf59SPeter Wemm 		symbol = ef->ddbstrtab + sym->st_name;
883d297ad16SMarcel Moolenaar 
884d297ad16SMarcel Moolenaar 		/* Force a lookup failure if the symbol name is bogus. */
885d297ad16SMarcel Moolenaar 		if (*symbol == 0)
886d297ad16SMarcel Moolenaar 			return (0);
887e9eabf59SPeter Wemm 		ret = ((Elf_Addr)linker_file_lookup_symbol(lf, symbol, deps));
888e9eabf59SPeter Wemm 		return ret;
889d297ad16SMarcel Moolenaar 
890e9eabf59SPeter Wemm 	case STB_WEAK:
891e9eabf59SPeter Wemm 		printf("Weak symbols not supported\n");
892e9eabf59SPeter Wemm 		return (0);
893e9eabf59SPeter Wemm 
894e9eabf59SPeter Wemm 	default:
895e9eabf59SPeter Wemm 		return (0);
896d297ad16SMarcel Moolenaar 	}
897e9eabf59SPeter Wemm }
898e9eabf59SPeter Wemm 
899e9eabf59SPeter Wemm 
9007251b4bfSJake Burkholder 
9017251b4bfSJake Burkholder static void
9027251b4bfSJake Burkholder link_elf_reloc_local(linker_file_t lf)
9037251b4bfSJake Burkholder {
904e9eabf59SPeter Wemm 	elf_file_t ef = (elf_file_t)lf;
9057251b4bfSJake Burkholder 	const Elf_Rel *rellim;
9067251b4bfSJake Burkholder 	const Elf_Rel *rel;
9077251b4bfSJake Burkholder 	const Elf_Rela *relalim;
9087251b4bfSJake Burkholder 	const Elf_Rela *rela;
909e9eabf59SPeter Wemm 	const Elf_Sym *sym;
910e9eabf59SPeter Wemm 	Elf_Addr base;
911e9eabf59SPeter Wemm 	int i;
912e9eabf59SPeter Wemm         Elf_Word symidx;
913e9eabf59SPeter Wemm 
9147251b4bfSJake Burkholder 
9157251b4bfSJake Burkholder 	/* Perform relocations without addend if there are any: */
916e9eabf59SPeter Wemm 	for (i = 0; i < ef->nrel; i++) {
917e9eabf59SPeter Wemm 		rel = ef->reltab[i].rel;
918e9eabf59SPeter Wemm 		if (rel) {
919e9eabf59SPeter Wemm 			rellim = (const Elf_Rel *)((const char *)rel + ef->reltab[i].filesz);
920e9eabf59SPeter Wemm 			base = findbase(ef, ef->reltab[i].sec);
9217251b4bfSJake Burkholder 			while (rel < rellim) {
922e9eabf59SPeter Wemm 				symidx = ELF_R_SYM(rel->r_info);
923e9eabf59SPeter Wemm 				if (symidx < ef->ddbsymcnt) {
924e9eabf59SPeter Wemm 					sym = ef->ddbsymtab + symidx;
925e9eabf59SPeter Wemm 					if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL)
926e9eabf59SPeter Wemm 						elf_reloc_local(lf, base, rel, ELF_RELOC_REL, elf_obj_lookup);
927e9eabf59SPeter Wemm 				}
9287251b4bfSJake Burkholder 				rel++;
9297251b4bfSJake Burkholder 			}
9307251b4bfSJake Burkholder 		}
931e9eabf59SPeter Wemm 	}
9327251b4bfSJake Burkholder 
9337251b4bfSJake Burkholder 	/* Perform relocations with addend if there are any: */
934e9eabf59SPeter Wemm 	for (i = 0; i < ef->nrela; i++) {
935e9eabf59SPeter Wemm 		rela = ef->relatab[i].rela;
936e9eabf59SPeter Wemm 		if (rela) {
937e9eabf59SPeter Wemm 			relalim = (const Elf_Rela *)((const char *)rela + ef->relatab[i].filesz);
938e9eabf59SPeter Wemm 			base = findbase(ef, ef->relatab[i].sec);
9397251b4bfSJake Burkholder 			while (rela < relalim) {
940e9eabf59SPeter Wemm 				symidx = ELF_R_SYM(rela->r_info);
941e9eabf59SPeter Wemm 				if (symidx < ef->ddbsymcnt) {
942e9eabf59SPeter Wemm 					sym = ef->ddbsymtab + symidx;
943e9eabf59SPeter Wemm 					if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL)
944e9eabf59SPeter Wemm 						elf_reloc_local(lf, base, rela, ELF_RELOC_RELA, elf_obj_lookup);
945e9eabf59SPeter Wemm 				}
9467251b4bfSJake Burkholder 				rela++;
9477251b4bfSJake Burkholder 			}
9487251b4bfSJake Burkholder 		}
9497251b4bfSJake Burkholder 	}
950e9eabf59SPeter Wemm }
951