xref: /freebsd/lib/libkldelf/elf.c (revision 72e15f76a1b3e7bddb5fa1b0429e41d07950af65)
1968bcca2SKa Ho Ng /*-
2968bcca2SKa Ho Ng  * SPDX-License-Identifier: BSD-2-Clause
3968bcca2SKa Ho Ng  *
4968bcca2SKa Ho Ng  * Copyright (c) 2021-2023 John Baldwin <jhb@FreeBSD.org>
5968bcca2SKa Ho Ng  *
6968bcca2SKa Ho Ng  * This software was developed by SRI International and the University
7968bcca2SKa Ho Ng  * of Cambridge Computer Laboratory (Department of Computer Science
8968bcca2SKa Ho Ng  * and Technology) under Defense Advanced Research Projects Agency
9968bcca2SKa Ho Ng  * (DARPA) contract HR0011-18-C-0016 ("ECATS"), as part of the DARPA
10968bcca2SKa Ho Ng  * SSITH research programme and under DARPA Contract No. HR001123C0031
11968bcca2SKa Ho Ng  * ("MTSS").
12968bcca2SKa Ho Ng  *
13968bcca2SKa Ho Ng  * Redistribution and use in source and binary forms, with or without
14968bcca2SKa Ho Ng  * modification, are permitted provided that the following conditions
15968bcca2SKa Ho Ng  * are met:
16968bcca2SKa Ho Ng  * 1. Redistributions of source code must retain the above copyright
17968bcca2SKa Ho Ng  *    notice, this list of conditions and the following disclaimer.
18968bcca2SKa Ho Ng  * 2. Redistributions in binary form must reproduce the above copyright
19968bcca2SKa Ho Ng  *    notice, this list of conditions and the following disclaimer in the
20968bcca2SKa Ho Ng  *    documentation and/or other materials provided with the distribution.
21968bcca2SKa Ho Ng  *
22968bcca2SKa Ho Ng  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23968bcca2SKa Ho Ng  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24968bcca2SKa Ho Ng  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25968bcca2SKa Ho Ng  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26968bcca2SKa Ho Ng  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27968bcca2SKa Ho Ng  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28968bcca2SKa Ho Ng  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29968bcca2SKa Ho Ng  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30968bcca2SKa Ho Ng  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31968bcca2SKa Ho Ng  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32968bcca2SKa Ho Ng  * SUCH DAMAGE.
33968bcca2SKa Ho Ng  */
34968bcca2SKa Ho Ng 
35968bcca2SKa Ho Ng #include <sys/param.h>
36968bcca2SKa Ho Ng #include <sys/endian.h>
37968bcca2SKa Ho Ng 
38968bcca2SKa Ho Ng #include <err.h>
39968bcca2SKa Ho Ng #include <errno.h>
40968bcca2SKa Ho Ng #include <fcntl.h>
41968bcca2SKa Ho Ng #include <gelf.h>
42968bcca2SKa Ho Ng #include <libelf.h>
43968bcca2SKa Ho Ng #include <stdlib.h>
44968bcca2SKa Ho Ng #include <string.h>
45968bcca2SKa Ho Ng #include <unistd.h>
46968bcca2SKa Ho Ng 
47968bcca2SKa Ho Ng #include "kldelf.h"
48968bcca2SKa Ho Ng 
49968bcca2SKa Ho Ng SET_DECLARE(elf_reloc, struct elf_reloc_data);
50968bcca2SKa Ho Ng 
51968bcca2SKa Ho Ng static elf_reloc_t *
elf_find_reloc(const GElf_Ehdr * hdr)52968bcca2SKa Ho Ng elf_find_reloc(const GElf_Ehdr *hdr)
53968bcca2SKa Ho Ng {
54968bcca2SKa Ho Ng 	struct elf_reloc_data **erd;
55968bcca2SKa Ho Ng 
56968bcca2SKa Ho Ng 	SET_FOREACH(erd, elf_reloc) {
57968bcca2SKa Ho Ng 		if (hdr->e_ident[EI_CLASS] == (*erd)->class &&
58968bcca2SKa Ho Ng 		    hdr->e_ident[EI_DATA] == (*erd)->data &&
59968bcca2SKa Ho Ng 		    hdr->e_machine == (*erd)->machine)
60968bcca2SKa Ho Ng 			return ((*erd)->reloc);
61968bcca2SKa Ho Ng 	}
62968bcca2SKa Ho Ng 	return (NULL);
63968bcca2SKa Ho Ng }
64968bcca2SKa Ho Ng 
65968bcca2SKa Ho Ng int
elf_open_file(struct elf_file * efile,const char * filename,int verbose)66968bcca2SKa Ho Ng elf_open_file(struct elf_file *efile, const char *filename, int verbose)
67968bcca2SKa Ho Ng {
68968bcca2SKa Ho Ng 	int error;
69968bcca2SKa Ho Ng 
70968bcca2SKa Ho Ng 	memset(efile, 0, sizeof(*efile));
71968bcca2SKa Ho Ng 	efile->ef_filename = filename;
72968bcca2SKa Ho Ng 	efile->ef_fd = open(filename, O_RDONLY);
73968bcca2SKa Ho Ng 	if (efile->ef_fd == -1) {
74968bcca2SKa Ho Ng 		if (verbose)
75968bcca2SKa Ho Ng 			warn("open(%s)", filename);
76968bcca2SKa Ho Ng 		return (errno);
77968bcca2SKa Ho Ng 	}
78968bcca2SKa Ho Ng 
79968bcca2SKa Ho Ng 	efile->ef_elf = elf_begin(efile->ef_fd, ELF_C_READ, NULL);
80968bcca2SKa Ho Ng 	if (efile->ef_elf == NULL) {
81968bcca2SKa Ho Ng 		if (verbose)
82968bcca2SKa Ho Ng 			warnx("elf_begin(%s): %s", filename, elf_errmsg(0));
83968bcca2SKa Ho Ng 		elf_close_file(efile);
84968bcca2SKa Ho Ng 		return (EINVAL);
85968bcca2SKa Ho Ng 	}
86968bcca2SKa Ho Ng 
87968bcca2SKa Ho Ng 	if (elf_kind(efile->ef_elf) != ELF_K_ELF) {
88968bcca2SKa Ho Ng 		if (verbose)
89968bcca2SKa Ho Ng 			warnx("%s: not an ELF file", filename);
90968bcca2SKa Ho Ng 		elf_close_file(efile);
91968bcca2SKa Ho Ng 		return (EINVAL);
92968bcca2SKa Ho Ng 	}
93968bcca2SKa Ho Ng 
94968bcca2SKa Ho Ng 	if (gelf_getehdr(efile->ef_elf, &efile->ef_hdr) == NULL) {
95968bcca2SKa Ho Ng 		if (verbose)
96968bcca2SKa Ho Ng 			warnx("gelf_getehdr(%s): %s", filename, elf_errmsg(0));
97968bcca2SKa Ho Ng 		elf_close_file(efile);
98968bcca2SKa Ho Ng 		return (EINVAL);
99968bcca2SKa Ho Ng 	}
100968bcca2SKa Ho Ng 
101968bcca2SKa Ho Ng 	efile->ef_reloc = elf_find_reloc(&efile->ef_hdr);
102968bcca2SKa Ho Ng 	if (efile->ef_reloc == NULL) {
103968bcca2SKa Ho Ng 		if (verbose)
104968bcca2SKa Ho Ng 			warnx("%s: unsupported architecture", filename);
105968bcca2SKa Ho Ng 		elf_close_file(efile);
106968bcca2SKa Ho Ng 		return (EFTYPE);
107968bcca2SKa Ho Ng 	}
108968bcca2SKa Ho Ng 
109968bcca2SKa Ho Ng 	error = ef_open(efile, verbose);
110968bcca2SKa Ho Ng 	if (error != 0) {
111968bcca2SKa Ho Ng 		error = ef_obj_open(efile, verbose);
112968bcca2SKa Ho Ng 		if (error != 0) {
113968bcca2SKa Ho Ng 			if (verbose)
114968bcca2SKa Ho Ng 				warnc(error, "%s: not a valid DSO or object file",
115968bcca2SKa Ho Ng 				    filename);
116968bcca2SKa Ho Ng 			elf_close_file(efile);
117968bcca2SKa Ho Ng 			return (error);
118968bcca2SKa Ho Ng 		}
119968bcca2SKa Ho Ng 	}
120968bcca2SKa Ho Ng 
121968bcca2SKa Ho Ng 	efile->ef_pointer_size = elf_object_size(efile, ELF_T_ADDR);
122968bcca2SKa Ho Ng 
123968bcca2SKa Ho Ng 	return (0);
124968bcca2SKa Ho Ng }
125968bcca2SKa Ho Ng 
126968bcca2SKa Ho Ng void
elf_close_file(struct elf_file * efile)127968bcca2SKa Ho Ng elf_close_file(struct elf_file *efile)
128968bcca2SKa Ho Ng {
129968bcca2SKa Ho Ng 	if (efile->ef_ops != NULL) {
130968bcca2SKa Ho Ng 		EF_CLOSE(efile);
131968bcca2SKa Ho Ng 	}
132968bcca2SKa Ho Ng 	if (efile->ef_elf != NULL) {
133968bcca2SKa Ho Ng 		elf_end(efile->ef_elf);
134968bcca2SKa Ho Ng 		efile->ef_elf = NULL;
135968bcca2SKa Ho Ng 	}
136968bcca2SKa Ho Ng 	if (efile->ef_fd > 0) {
137968bcca2SKa Ho Ng 		close(efile->ef_fd);
138968bcca2SKa Ho Ng 		efile->ef_fd = -1;
139968bcca2SKa Ho Ng 	}
140968bcca2SKa Ho Ng }
141968bcca2SKa Ho Ng 
142968bcca2SKa Ho Ng bool
elf_compatible(struct elf_file * efile,const GElf_Ehdr * hdr)143968bcca2SKa Ho Ng elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr)
144968bcca2SKa Ho Ng {
145968bcca2SKa Ho Ng 	if (efile->ef_hdr.e_ident[EI_CLASS] != hdr->e_ident[EI_CLASS] ||
146968bcca2SKa Ho Ng 	    efile->ef_hdr.e_ident[EI_DATA] != hdr->e_ident[EI_DATA] ||
147968bcca2SKa Ho Ng 	    efile->ef_hdr.e_machine != hdr->e_machine)
148968bcca2SKa Ho Ng 		return (false);
149968bcca2SKa Ho Ng 	return (true);
150968bcca2SKa Ho Ng }
151968bcca2SKa Ho Ng 
152968bcca2SKa Ho Ng size_t
elf_object_size(struct elf_file * efile,Elf_Type type)153968bcca2SKa Ho Ng elf_object_size(struct elf_file *efile, Elf_Type type)
154968bcca2SKa Ho Ng {
155968bcca2SKa Ho Ng 	return (gelf_fsize(efile->ef_elf, type, 1, efile->ef_hdr.e_version));
156968bcca2SKa Ho Ng }
157968bcca2SKa Ho Ng 
158968bcca2SKa Ho Ng /*
159968bcca2SKa Ho Ng  * The number of objects of 'type' in region of the file of size
160968bcca2SKa Ho Ng  * 'file_size'.
161968bcca2SKa Ho Ng  */
162968bcca2SKa Ho Ng static size_t
elf_object_count(struct elf_file * efile,Elf_Type type,size_t file_size)163968bcca2SKa Ho Ng elf_object_count(struct elf_file *efile, Elf_Type type, size_t file_size)
164968bcca2SKa Ho Ng {
165968bcca2SKa Ho Ng 	return (file_size / elf_object_size(efile, type));
166968bcca2SKa Ho Ng }
167968bcca2SKa Ho Ng 
168968bcca2SKa Ho Ng int
elf_read_raw_data(struct elf_file * efile,off_t offset,void * dst,size_t len)169968bcca2SKa Ho Ng elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst, size_t len)
170968bcca2SKa Ho Ng {
171968bcca2SKa Ho Ng 	ssize_t nread;
172968bcca2SKa Ho Ng 
173968bcca2SKa Ho Ng 	nread = pread(efile->ef_fd, dst, len, offset);
174968bcca2SKa Ho Ng 	if (nread == -1)
175968bcca2SKa Ho Ng 		return (errno);
176968bcca2SKa Ho Ng 	if (nread != len)
177968bcca2SKa Ho Ng 		return (EIO);
178968bcca2SKa Ho Ng 	return (0);
179968bcca2SKa Ho Ng }
180968bcca2SKa Ho Ng 
181968bcca2SKa Ho Ng int
elf_read_raw_data_alloc(struct elf_file * efile,off_t offset,size_t len,void ** out)182968bcca2SKa Ho Ng elf_read_raw_data_alloc(struct elf_file *efile, off_t offset, size_t len,
183968bcca2SKa Ho Ng     void **out)
184968bcca2SKa Ho Ng {
185968bcca2SKa Ho Ng 	void *buf;
186968bcca2SKa Ho Ng 	int error;
187968bcca2SKa Ho Ng 
188968bcca2SKa Ho Ng 	buf = malloc(len);
189968bcca2SKa Ho Ng 	if (buf == NULL)
190968bcca2SKa Ho Ng 		return (ENOMEM);
191968bcca2SKa Ho Ng 	error = elf_read_raw_data(efile, offset, buf, len);
192968bcca2SKa Ho Ng 	if (error != 0) {
193968bcca2SKa Ho Ng 		free(buf);
194968bcca2SKa Ho Ng 		return (error);
195968bcca2SKa Ho Ng 	}
196968bcca2SKa Ho Ng 	*out = buf;
197968bcca2SKa Ho Ng 	return (0);
198968bcca2SKa Ho Ng }
199968bcca2SKa Ho Ng 
200968bcca2SKa Ho Ng int
elf_read_raw_string(struct elf_file * efile,off_t offset,char * dst,size_t len)201968bcca2SKa Ho Ng elf_read_raw_string(struct elf_file *efile, off_t offset, char *dst, size_t len)
202968bcca2SKa Ho Ng {
203968bcca2SKa Ho Ng 	ssize_t nread;
204968bcca2SKa Ho Ng 
205968bcca2SKa Ho Ng 	nread = pread(efile->ef_fd, dst, len, offset);
206968bcca2SKa Ho Ng 	if (nread == -1)
207968bcca2SKa Ho Ng 		return (errno);
208968bcca2SKa Ho Ng 	if (nread == 0)
209968bcca2SKa Ho Ng 		return (EIO);
210968bcca2SKa Ho Ng 
211968bcca2SKa Ho Ng 	/* A short read is ok so long as the data contains a terminator. */
212968bcca2SKa Ho Ng 	if (strnlen(dst, nread) == nread)
213968bcca2SKa Ho Ng 		return (EFAULT);
214968bcca2SKa Ho Ng 
215968bcca2SKa Ho Ng 	return (0);
216968bcca2SKa Ho Ng }
217968bcca2SKa Ho Ng 
218968bcca2SKa Ho Ng int
elf_read_data(struct elf_file * efile,Elf_Type type,off_t offset,size_t len,void ** out)219968bcca2SKa Ho Ng elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset, size_t len,
220968bcca2SKa Ho Ng     void **out)
221968bcca2SKa Ho Ng {
222968bcca2SKa Ho Ng 	Elf_Data dst, src;
223968bcca2SKa Ho Ng 	void *buf;
224968bcca2SKa Ho Ng 	int error;
225968bcca2SKa Ho Ng 
226968bcca2SKa Ho Ng 	buf = malloc(len);
227968bcca2SKa Ho Ng 	if (buf == NULL)
228968bcca2SKa Ho Ng 		return (ENOMEM);
229968bcca2SKa Ho Ng 
230968bcca2SKa Ho Ng 	error = elf_read_raw_data(efile, offset, buf, len);
231968bcca2SKa Ho Ng 	if (error != 0) {
232968bcca2SKa Ho Ng 		free(buf);
233968bcca2SKa Ho Ng 		return (error);
234968bcca2SKa Ho Ng 	}
235968bcca2SKa Ho Ng 
236968bcca2SKa Ho Ng 	memset(&dst, 0, sizeof(dst));
237968bcca2SKa Ho Ng 	memset(&src, 0, sizeof(src));
238968bcca2SKa Ho Ng 
239968bcca2SKa Ho Ng 	src.d_buf = buf;
240968bcca2SKa Ho Ng 	src.d_size = len;
241968bcca2SKa Ho Ng 	src.d_type = type;
242968bcca2SKa Ho Ng 	src.d_version = efile->ef_hdr.e_version;
243968bcca2SKa Ho Ng 
244968bcca2SKa Ho Ng 	dst.d_buf = buf;
245968bcca2SKa Ho Ng 	dst.d_size = len;
246968bcca2SKa Ho Ng 	dst.d_version = EV_CURRENT;
247968bcca2SKa Ho Ng 
248968bcca2SKa Ho Ng 	if (gelf_xlatetom(efile->ef_elf, &dst, &src, elf_encoding(efile)) ==
249968bcca2SKa Ho Ng 	    NULL) {
250968bcca2SKa Ho Ng 		free(buf);
251968bcca2SKa Ho Ng 		return (ENXIO);
252968bcca2SKa Ho Ng 	}
253968bcca2SKa Ho Ng 
254968bcca2SKa Ho Ng 	if (dst.d_size != len)
255968bcca2SKa Ho Ng 		warnx("elf_read_data: translation of type %u size mismatch",
256968bcca2SKa Ho Ng 		    type);
257968bcca2SKa Ho Ng 
258968bcca2SKa Ho Ng 	*out = buf;
259968bcca2SKa Ho Ng 	return (0);
260968bcca2SKa Ho Ng }
261968bcca2SKa Ho Ng 
262968bcca2SKa Ho Ng int
elf_read_relocated_data(struct elf_file * efile,GElf_Addr address,size_t len,void ** buf)263968bcca2SKa Ho Ng elf_read_relocated_data(struct elf_file *efile, GElf_Addr address, size_t len,
264968bcca2SKa Ho Ng     void **buf)
265968bcca2SKa Ho Ng {
266968bcca2SKa Ho Ng 	int error;
267968bcca2SKa Ho Ng 	void *p;
268968bcca2SKa Ho Ng 
269968bcca2SKa Ho Ng 	p = malloc(len);
270968bcca2SKa Ho Ng 	if (p == NULL)
271968bcca2SKa Ho Ng 		return (ENOMEM);
272968bcca2SKa Ho Ng 	error = EF_SEG_READ_REL(efile, address, len, p);
273968bcca2SKa Ho Ng 	if (error != 0) {
274968bcca2SKa Ho Ng 		free(p);
275968bcca2SKa Ho Ng 		return (error);
276968bcca2SKa Ho Ng 	}
277968bcca2SKa Ho Ng 	*buf = p;
278968bcca2SKa Ho Ng 	return (0);
279968bcca2SKa Ho Ng }
280968bcca2SKa Ho Ng 
281968bcca2SKa Ho Ng int
elf_read_phdrs(struct elf_file * efile,size_t * nphdrp,GElf_Phdr ** phdrp)282968bcca2SKa Ho Ng elf_read_phdrs(struct elf_file *efile, size_t *nphdrp, GElf_Phdr **phdrp)
283968bcca2SKa Ho Ng {
284968bcca2SKa Ho Ng 	GElf_Phdr *phdr;
285968bcca2SKa Ho Ng 	size_t nphdr, i;
286968bcca2SKa Ho Ng 	int error;
287968bcca2SKa Ho Ng 
288968bcca2SKa Ho Ng 	if (elf_getphdrnum(efile->ef_elf, &nphdr) == -1)
289968bcca2SKa Ho Ng 		return (EFTYPE);
290968bcca2SKa Ho Ng 
291968bcca2SKa Ho Ng 	phdr = calloc(nphdr, sizeof(*phdr));
292968bcca2SKa Ho Ng 	if (phdr == NULL)
293968bcca2SKa Ho Ng 		return (ENOMEM);
294968bcca2SKa Ho Ng 
295968bcca2SKa Ho Ng 	for (i = 0; i < nphdr; i++) {
296968bcca2SKa Ho Ng 		if (gelf_getphdr(efile->ef_elf, i, &phdr[i]) == NULL) {
297968bcca2SKa Ho Ng 			error = EFTYPE;
298968bcca2SKa Ho Ng 			goto out;
299968bcca2SKa Ho Ng 		}
300968bcca2SKa Ho Ng 	}
301968bcca2SKa Ho Ng 
302968bcca2SKa Ho Ng 	*nphdrp = nphdr;
303968bcca2SKa Ho Ng 	*phdrp = phdr;
304968bcca2SKa Ho Ng 	return (0);
305968bcca2SKa Ho Ng out:
306968bcca2SKa Ho Ng 	free(phdr);
307968bcca2SKa Ho Ng 	return (error);
308968bcca2SKa Ho Ng }
309968bcca2SKa Ho Ng 
310968bcca2SKa Ho Ng int
elf_read_shdrs(struct elf_file * efile,size_t * nshdrp,GElf_Shdr ** shdrp)311968bcca2SKa Ho Ng elf_read_shdrs(struct elf_file *efile, size_t *nshdrp, GElf_Shdr **shdrp)
312968bcca2SKa Ho Ng {
313968bcca2SKa Ho Ng 	GElf_Shdr *shdr;
314968bcca2SKa Ho Ng 	Elf_Scn *scn;
315968bcca2SKa Ho Ng 	size_t nshdr, i;
316968bcca2SKa Ho Ng 	int error;
317968bcca2SKa Ho Ng 
318968bcca2SKa Ho Ng 	if (elf_getshdrnum(efile->ef_elf, &nshdr) == -1)
319968bcca2SKa Ho Ng 		return (EFTYPE);
320968bcca2SKa Ho Ng 
321968bcca2SKa Ho Ng 	shdr = calloc(nshdr, sizeof(*shdr));
322968bcca2SKa Ho Ng 	if (shdr == NULL)
323968bcca2SKa Ho Ng 		return (ENOMEM);
324968bcca2SKa Ho Ng 
325968bcca2SKa Ho Ng 	for (i = 0; i < nshdr; i++) {
326968bcca2SKa Ho Ng 		scn = elf_getscn(efile->ef_elf, i);
327968bcca2SKa Ho Ng 		if (scn == NULL) {
328968bcca2SKa Ho Ng 			error = EFTYPE;
329968bcca2SKa Ho Ng 			goto out;
330968bcca2SKa Ho Ng 		}
331968bcca2SKa Ho Ng 		if (gelf_getshdr(scn, &shdr[i]) == NULL) {
332968bcca2SKa Ho Ng 			error = EFTYPE;
333968bcca2SKa Ho Ng 			goto out;
334968bcca2SKa Ho Ng 		}
335968bcca2SKa Ho Ng 	}
336968bcca2SKa Ho Ng 
337968bcca2SKa Ho Ng 	*nshdrp = nshdr;
338968bcca2SKa Ho Ng 	*shdrp = shdr;
339968bcca2SKa Ho Ng 	return (0);
340968bcca2SKa Ho Ng out:
341968bcca2SKa Ho Ng 	free(shdr);
342968bcca2SKa Ho Ng 	return (error);
343968bcca2SKa Ho Ng }
344968bcca2SKa Ho Ng 
345968bcca2SKa Ho Ng int
elf_read_dynamic(struct elf_file * efile,int section_index,size_t * ndynp,GElf_Dyn ** dynp)346968bcca2SKa Ho Ng elf_read_dynamic(struct elf_file *efile, int section_index, size_t *ndynp,
347968bcca2SKa Ho Ng     GElf_Dyn **dynp)
348968bcca2SKa Ho Ng {
349968bcca2SKa Ho Ng 	GElf_Shdr shdr;
350968bcca2SKa Ho Ng 	Elf_Scn *scn;
351968bcca2SKa Ho Ng 	Elf_Data *data;
352968bcca2SKa Ho Ng 	GElf_Dyn *dyn;
353968bcca2SKa Ho Ng 	long i, ndyn;
354968bcca2SKa Ho Ng 
355968bcca2SKa Ho Ng 	scn = elf_getscn(efile->ef_elf, section_index);
356968bcca2SKa Ho Ng 	if (scn == NULL)
357968bcca2SKa Ho Ng 		return (EINVAL);
358968bcca2SKa Ho Ng 	if (gelf_getshdr(scn, &shdr) == NULL)
359968bcca2SKa Ho Ng 		return (EINVAL);
360968bcca2SKa Ho Ng 	data = elf_getdata(scn, NULL);
361968bcca2SKa Ho Ng 	if (data == NULL)
362968bcca2SKa Ho Ng 		return (EINVAL);
363968bcca2SKa Ho Ng 
364968bcca2SKa Ho Ng 	ndyn = elf_object_count(efile, ELF_T_DYN, shdr.sh_size);
365968bcca2SKa Ho Ng 	dyn = calloc(ndyn, sizeof(*dyn));
366968bcca2SKa Ho Ng 	if (dyn == NULL)
367968bcca2SKa Ho Ng 		return (ENOMEM);
368968bcca2SKa Ho Ng 
369968bcca2SKa Ho Ng 	for (i = 0; i < ndyn; i++) {
370968bcca2SKa Ho Ng 		if (gelf_getdyn(data, i, &dyn[i]) == NULL) {
371968bcca2SKa Ho Ng 			free(dyn);
372968bcca2SKa Ho Ng 			return (EINVAL);
373968bcca2SKa Ho Ng 		}
374968bcca2SKa Ho Ng 	}
375968bcca2SKa Ho Ng 
376968bcca2SKa Ho Ng 	*ndynp = ndyn;
377968bcca2SKa Ho Ng 	*dynp = dyn;
378968bcca2SKa Ho Ng 	return (0);
379968bcca2SKa Ho Ng }
380968bcca2SKa Ho Ng 
381968bcca2SKa Ho Ng int
elf_read_symbols(struct elf_file * efile,int section_index,size_t * nsymp,GElf_Sym ** symp)382968bcca2SKa Ho Ng elf_read_symbols(struct elf_file *efile, int section_index, size_t *nsymp,
383968bcca2SKa Ho Ng     GElf_Sym **symp)
384968bcca2SKa Ho Ng {
385968bcca2SKa Ho Ng 	GElf_Shdr shdr;
386968bcca2SKa Ho Ng 	Elf_Scn *scn;
387968bcca2SKa Ho Ng 	Elf_Data *data;
388968bcca2SKa Ho Ng 	GElf_Sym *sym;
389968bcca2SKa Ho Ng 	size_t i, nsym;
390968bcca2SKa Ho Ng 
391968bcca2SKa Ho Ng 	scn = elf_getscn(efile->ef_elf, section_index);
392968bcca2SKa Ho Ng 	if (scn == NULL)
393968bcca2SKa Ho Ng 		return (EINVAL);
394968bcca2SKa Ho Ng 	if (gelf_getshdr(scn, &shdr) == NULL)
395968bcca2SKa Ho Ng 		return (EINVAL);
396968bcca2SKa Ho Ng 	data = elf_getdata(scn, NULL);
397968bcca2SKa Ho Ng 	if (data == NULL)
398968bcca2SKa Ho Ng 		return (EINVAL);
399968bcca2SKa Ho Ng 
400968bcca2SKa Ho Ng 	nsym = elf_object_count(efile, ELF_T_SYM, shdr.sh_size);
401968bcca2SKa Ho Ng 	sym = calloc(nsym, sizeof(*sym));
402968bcca2SKa Ho Ng 	if (sym == NULL)
403968bcca2SKa Ho Ng 		return (ENOMEM);
404968bcca2SKa Ho Ng 
405968bcca2SKa Ho Ng 	for (i = 0; i < nsym; i++) {
406968bcca2SKa Ho Ng 		if (gelf_getsym(data, i, &sym[i]) == NULL) {
407968bcca2SKa Ho Ng 			free(sym);
408968bcca2SKa Ho Ng 			return (EINVAL);
409968bcca2SKa Ho Ng 		}
410968bcca2SKa Ho Ng 	}
411968bcca2SKa Ho Ng 
412968bcca2SKa Ho Ng 	*nsymp = nsym;
413968bcca2SKa Ho Ng 	*symp = sym;
414968bcca2SKa Ho Ng 	return (0);
415968bcca2SKa Ho Ng }
416968bcca2SKa Ho Ng 
417968bcca2SKa Ho Ng int
elf_read_string_table(struct elf_file * efile,const GElf_Shdr * shdr,long * strcnt,char ** strtab)418968bcca2SKa Ho Ng elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr,
419968bcca2SKa Ho Ng     long *strcnt, char **strtab)
420968bcca2SKa Ho Ng {
421968bcca2SKa Ho Ng 	int error;
422968bcca2SKa Ho Ng 
423968bcca2SKa Ho Ng 	if (shdr->sh_type != SHT_STRTAB)
424968bcca2SKa Ho Ng 		return (EINVAL);
425968bcca2SKa Ho Ng 	error = elf_read_raw_data_alloc(efile, shdr->sh_offset, shdr->sh_size,
426968bcca2SKa Ho Ng 	    (void **)strtab);
427968bcca2SKa Ho Ng 	if (error != 0)
428968bcca2SKa Ho Ng 		return (error);
429968bcca2SKa Ho Ng 	*strcnt = shdr->sh_size;
430968bcca2SKa Ho Ng 	return (0);
431968bcca2SKa Ho Ng }
432968bcca2SKa Ho Ng 
433968bcca2SKa Ho Ng int
elf_read_rel(struct elf_file * efile,int section_index,long * nrelp,GElf_Rel ** relp)434968bcca2SKa Ho Ng elf_read_rel(struct elf_file *efile, int section_index, long *nrelp,
435968bcca2SKa Ho Ng     GElf_Rel **relp)
436968bcca2SKa Ho Ng {
437968bcca2SKa Ho Ng 	GElf_Shdr shdr;
438968bcca2SKa Ho Ng 	Elf_Scn *scn;
439968bcca2SKa Ho Ng 	Elf_Data *data;
440968bcca2SKa Ho Ng 	GElf_Rel *rel;
441968bcca2SKa Ho Ng 	long i, nrel;
442968bcca2SKa Ho Ng 
443968bcca2SKa Ho Ng 	scn = elf_getscn(efile->ef_elf, section_index);
444968bcca2SKa Ho Ng 	if (scn == NULL)
445968bcca2SKa Ho Ng 		return (EINVAL);
446968bcca2SKa Ho Ng 	if (gelf_getshdr(scn, &shdr) == NULL)
447968bcca2SKa Ho Ng 		return (EINVAL);
448968bcca2SKa Ho Ng 	data = elf_getdata(scn, NULL);
449968bcca2SKa Ho Ng 	if (data == NULL)
450968bcca2SKa Ho Ng 		return (EINVAL);
451968bcca2SKa Ho Ng 
452968bcca2SKa Ho Ng 	nrel = elf_object_count(efile, ELF_T_REL, shdr.sh_size);
453968bcca2SKa Ho Ng 	rel = calloc(nrel, sizeof(*rel));
454968bcca2SKa Ho Ng 	if (rel == NULL)
455968bcca2SKa Ho Ng 		return (ENOMEM);
456968bcca2SKa Ho Ng 
457968bcca2SKa Ho Ng 	for (i = 0; i < nrel; i++) {
458968bcca2SKa Ho Ng 		if (gelf_getrel(data, i, &rel[i]) == NULL) {
459968bcca2SKa Ho Ng 			free(rel);
460968bcca2SKa Ho Ng 			return (EINVAL);
461968bcca2SKa Ho Ng 		}
462968bcca2SKa Ho Ng 	}
463968bcca2SKa Ho Ng 
464968bcca2SKa Ho Ng 	*nrelp = nrel;
465968bcca2SKa Ho Ng 	*relp = rel;
466968bcca2SKa Ho Ng 	return (0);
467968bcca2SKa Ho Ng }
468968bcca2SKa Ho Ng 
469968bcca2SKa Ho Ng int
elf_read_rela(struct elf_file * efile,int section_index,long * nrelap,GElf_Rela ** relap)470968bcca2SKa Ho Ng elf_read_rela(struct elf_file *efile, int section_index, long *nrelap,
471968bcca2SKa Ho Ng     GElf_Rela **relap)
472968bcca2SKa Ho Ng {
473968bcca2SKa Ho Ng 	GElf_Shdr shdr;
474968bcca2SKa Ho Ng 	Elf_Scn *scn;
475968bcca2SKa Ho Ng 	Elf_Data *data;
476968bcca2SKa Ho Ng 	GElf_Rela *rela;
477968bcca2SKa Ho Ng 	long i, nrela;
478968bcca2SKa Ho Ng 
479968bcca2SKa Ho Ng 	scn = elf_getscn(efile->ef_elf, section_index);
480968bcca2SKa Ho Ng 	if (scn == NULL)
481968bcca2SKa Ho Ng 		return (EINVAL);
482968bcca2SKa Ho Ng 	if (gelf_getshdr(scn, &shdr) == NULL)
483968bcca2SKa Ho Ng 		return (EINVAL);
484968bcca2SKa Ho Ng 	data = elf_getdata(scn, NULL);
485968bcca2SKa Ho Ng 	if (data == NULL)
486968bcca2SKa Ho Ng 		return (EINVAL);
487968bcca2SKa Ho Ng 
488968bcca2SKa Ho Ng 	nrela = elf_object_count(efile, ELF_T_RELA, shdr.sh_size);
489968bcca2SKa Ho Ng 	rela = calloc(nrela, sizeof(*rela));
490968bcca2SKa Ho Ng 	if (rela == NULL)
491968bcca2SKa Ho Ng 		return (ENOMEM);
492968bcca2SKa Ho Ng 
493968bcca2SKa Ho Ng 	for (i = 0; i < nrela; i++) {
494968bcca2SKa Ho Ng 		if (gelf_getrela(data, i, &rela[i]) == NULL) {
495968bcca2SKa Ho Ng 			free(rela);
496968bcca2SKa Ho Ng 			return (EINVAL);
497968bcca2SKa Ho Ng 		}
498968bcca2SKa Ho Ng 	}
499968bcca2SKa Ho Ng 
500968bcca2SKa Ho Ng 	*nrelap = nrela;
501968bcca2SKa Ho Ng 	*relap = rela;
502968bcca2SKa Ho Ng 	return (0);
503968bcca2SKa Ho Ng }
504968bcca2SKa Ho Ng 
505968bcca2SKa Ho Ng size_t
elf_pointer_size(struct elf_file * efile)506968bcca2SKa Ho Ng elf_pointer_size(struct elf_file *efile)
507968bcca2SKa Ho Ng {
508968bcca2SKa Ho Ng 	return (efile->ef_pointer_size);
509968bcca2SKa Ho Ng }
510968bcca2SKa Ho Ng 
511968bcca2SKa Ho Ng int
elf_int(struct elf_file * efile,const void * p)512968bcca2SKa Ho Ng elf_int(struct elf_file *efile, const void *p)
513968bcca2SKa Ho Ng {
514968bcca2SKa Ho Ng 	if (elf_encoding(efile) == ELFDATA2LSB)
515968bcca2SKa Ho Ng 		return (le32dec(p));
516968bcca2SKa Ho Ng 	else
517968bcca2SKa Ho Ng 		return (be32dec(p));
518968bcca2SKa Ho Ng }
519968bcca2SKa Ho Ng 
520968bcca2SKa Ho Ng GElf_Addr
elf_address_from_pointer(struct elf_file * efile,const void * p)521968bcca2SKa Ho Ng elf_address_from_pointer(struct elf_file *efile, const void *p)
522968bcca2SKa Ho Ng {
523968bcca2SKa Ho Ng 	switch (elf_class(efile)) {
524968bcca2SKa Ho Ng 	case ELFCLASS32:
525968bcca2SKa Ho Ng 		if (elf_encoding(efile) == ELFDATA2LSB)
526968bcca2SKa Ho Ng 			return (le32dec(p));
527968bcca2SKa Ho Ng 		else
528968bcca2SKa Ho Ng 			return (be32dec(p));
529968bcca2SKa Ho Ng 	case ELFCLASS64:
530968bcca2SKa Ho Ng 		if (elf_encoding(efile) == ELFDATA2LSB)
531968bcca2SKa Ho Ng 			return (le64dec(p));
532968bcca2SKa Ho Ng 		else
533968bcca2SKa Ho Ng 			return (be64dec(p));
534968bcca2SKa Ho Ng 	default:
535968bcca2SKa Ho Ng 		__unreachable();
536968bcca2SKa Ho Ng 	}
537968bcca2SKa Ho Ng }
538968bcca2SKa Ho Ng 
539968bcca2SKa Ho Ng int
elf_read_string(struct elf_file * efile,GElf_Addr address,void * dst,size_t len)540968bcca2SKa Ho Ng elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst,
541968bcca2SKa Ho Ng     size_t len)
542968bcca2SKa Ho Ng {
543968bcca2SKa Ho Ng 	return (EF_SEG_READ_STRING(efile, address, len, dst));
544968bcca2SKa Ho Ng }
545968bcca2SKa Ho Ng 
546968bcca2SKa Ho Ng int
elf_read_linker_set(struct elf_file * efile,const char * name,GElf_Addr ** bufp,long * countp)547968bcca2SKa Ho Ng elf_read_linker_set(struct elf_file *efile, const char *name, GElf_Addr **bufp,
548968bcca2SKa Ho Ng     long *countp)
549968bcca2SKa Ho Ng {
550968bcca2SKa Ho Ng 	GElf_Addr *buf, start, stop;
551968bcca2SKa Ho Ng 	char *p;
552968bcca2SKa Ho Ng 	void *raw;
553968bcca2SKa Ho Ng 	long i, count;
554968bcca2SKa Ho Ng 	int error;
555968bcca2SKa Ho Ng 
556968bcca2SKa Ho Ng 	error = EF_LOOKUP_SET(efile, name, &start, &stop, &count);
557968bcca2SKa Ho Ng 	if (error != 0)
558968bcca2SKa Ho Ng 		return (error);
559968bcca2SKa Ho Ng 
560968bcca2SKa Ho Ng 	error = elf_read_relocated_data(efile, start,
561968bcca2SKa Ho Ng 	    count * elf_pointer_size(efile), &raw);
562968bcca2SKa Ho Ng 	if (error != 0)
563968bcca2SKa Ho Ng 		return (error);
564968bcca2SKa Ho Ng 
565968bcca2SKa Ho Ng 	buf = calloc(count, sizeof(*buf));
566968bcca2SKa Ho Ng 	if (buf == NULL) {
567968bcca2SKa Ho Ng 		free(raw);
568968bcca2SKa Ho Ng 		return (ENOMEM);
569968bcca2SKa Ho Ng 	}
570968bcca2SKa Ho Ng 
571968bcca2SKa Ho Ng 	p = raw;
572968bcca2SKa Ho Ng 	for (i = 0; i < count; i++) {
573968bcca2SKa Ho Ng 		buf[i] = elf_address_from_pointer(efile, p);
574968bcca2SKa Ho Ng 		p += elf_pointer_size(efile);
575968bcca2SKa Ho Ng 	}
576968bcca2SKa Ho Ng 	free(raw);
577968bcca2SKa Ho Ng 
578968bcca2SKa Ho Ng 	*bufp = buf;
579968bcca2SKa Ho Ng 	*countp = count;
580968bcca2SKa Ho Ng 	return (0);
581968bcca2SKa Ho Ng }
582968bcca2SKa Ho Ng 
583968bcca2SKa Ho Ng int
elf_read_mod_depend(struct elf_file * efile,GElf_Addr addr,struct Gmod_depend * mdp)584968bcca2SKa Ho Ng elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr,
585968bcca2SKa Ho Ng     struct Gmod_depend *mdp)
586968bcca2SKa Ho Ng {
587968bcca2SKa Ho Ng 	int *p;
588968bcca2SKa Ho Ng 	int error;
589968bcca2SKa Ho Ng 
590968bcca2SKa Ho Ng 	error = elf_read_relocated_data(efile, addr, sizeof(int) * 3,
591968bcca2SKa Ho Ng 	    (void **)&p);
592968bcca2SKa Ho Ng 	if (error != 0)
593968bcca2SKa Ho Ng 		return (error);
594968bcca2SKa Ho Ng 
595968bcca2SKa Ho Ng 	memset(mdp, 0, sizeof(*mdp));
596968bcca2SKa Ho Ng 	mdp->md_ver_minimum = elf_int(efile, p);
597968bcca2SKa Ho Ng 	mdp->md_ver_preferred = elf_int(efile, p + 1);
598968bcca2SKa Ho Ng 	mdp->md_ver_maximum = elf_int(efile, p + 2);
599968bcca2SKa Ho Ng 	free(p);
600968bcca2SKa Ho Ng 	return (0);
601968bcca2SKa Ho Ng }
602968bcca2SKa Ho Ng 
603968bcca2SKa Ho Ng int
elf_read_mod_version(struct elf_file * efile,GElf_Addr addr,struct Gmod_version * mdv)604968bcca2SKa Ho Ng elf_read_mod_version(struct elf_file *efile, GElf_Addr addr,
605968bcca2SKa Ho Ng     struct Gmod_version *mdv)
606968bcca2SKa Ho Ng {
607968bcca2SKa Ho Ng 	int error, value;
608968bcca2SKa Ho Ng 
609968bcca2SKa Ho Ng 	error = EF_SEG_READ_REL(efile, addr, sizeof(int), &value);
610968bcca2SKa Ho Ng 	if (error != 0)
611968bcca2SKa Ho Ng 		return (error);
612968bcca2SKa Ho Ng 
613968bcca2SKa Ho Ng 	memset(mdv, 0, sizeof(*mdv));
614968bcca2SKa Ho Ng 	mdv->mv_version = elf_int(efile, &value);
615968bcca2SKa Ho Ng 	return (0);
616968bcca2SKa Ho Ng }
617968bcca2SKa Ho Ng 
618968bcca2SKa Ho Ng int
elf_read_mod_metadata(struct elf_file * efile,GElf_Addr addr,struct Gmod_metadata * md)619968bcca2SKa Ho Ng elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr,
620968bcca2SKa Ho Ng     struct Gmod_metadata *md)
621968bcca2SKa Ho Ng {
622968bcca2SKa Ho Ng 	char *p;
623968bcca2SKa Ho Ng 	size_t len, offset, pointer_size;
624968bcca2SKa Ho Ng 	int error;
625968bcca2SKa Ho Ng 
626968bcca2SKa Ho Ng 	pointer_size = elf_pointer_size(efile);
627968bcca2SKa Ho Ng 	len = 2 * sizeof(int);
628968bcca2SKa Ho Ng 	len = roundup(len, pointer_size);
629968bcca2SKa Ho Ng 	len += 2 * pointer_size;
630968bcca2SKa Ho Ng 
631968bcca2SKa Ho Ng 	error = elf_read_relocated_data(efile, addr, len, (void **)&p);
632968bcca2SKa Ho Ng 	if (error != 0)
633968bcca2SKa Ho Ng 		return (error);
634968bcca2SKa Ho Ng 
635968bcca2SKa Ho Ng 	memset(md, 0, sizeof(*md));
636968bcca2SKa Ho Ng 	offset = 0;
637968bcca2SKa Ho Ng 	md->md_version = elf_int(efile, p + offset);
638968bcca2SKa Ho Ng 	offset += sizeof(int);
639968bcca2SKa Ho Ng 	md->md_type = elf_int(efile, p + offset);
640968bcca2SKa Ho Ng 	offset += sizeof(int);
641968bcca2SKa Ho Ng 	offset = roundup(offset, pointer_size);
642968bcca2SKa Ho Ng 	md->md_data = elf_address_from_pointer(efile, p + offset);
643968bcca2SKa Ho Ng 	offset += pointer_size;
644968bcca2SKa Ho Ng  	md->md_cval = elf_address_from_pointer(efile, p + offset);
645968bcca2SKa Ho Ng 	free(p);
646968bcca2SKa Ho Ng 	return (0);
647968bcca2SKa Ho Ng }
648968bcca2SKa Ho Ng 
649968bcca2SKa Ho Ng int
elf_read_mod_pnp_match_info(struct elf_file * efile,GElf_Addr addr,struct Gmod_pnp_match_info * pnp)650968bcca2SKa Ho Ng elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr,
651968bcca2SKa Ho Ng     struct Gmod_pnp_match_info *pnp)
652968bcca2SKa Ho Ng {
653968bcca2SKa Ho Ng 	char *p;
654968bcca2SKa Ho Ng 	size_t len, offset, pointer_size;
655968bcca2SKa Ho Ng 	int error;
656968bcca2SKa Ho Ng 
657968bcca2SKa Ho Ng 	pointer_size = elf_pointer_size(efile);
658968bcca2SKa Ho Ng 	len = 3 * pointer_size;
659968bcca2SKa Ho Ng 	len = roundup(len, pointer_size);
660968bcca2SKa Ho Ng 	len += 2 * sizeof(int);
661968bcca2SKa Ho Ng 
662968bcca2SKa Ho Ng 	error = elf_read_relocated_data(efile, addr, len, (void **)&p);
663968bcca2SKa Ho Ng 	if (error != 0)
664968bcca2SKa Ho Ng 		return (error);
665968bcca2SKa Ho Ng 
666968bcca2SKa Ho Ng 	memset(pnp, 0, sizeof(*pnp));
667968bcca2SKa Ho Ng 	offset = 0;
668968bcca2SKa Ho Ng 	pnp->descr = elf_address_from_pointer(efile, p + offset);
669968bcca2SKa Ho Ng 	offset += pointer_size;
670968bcca2SKa Ho Ng 	pnp->bus = elf_address_from_pointer(efile, p + offset);
671968bcca2SKa Ho Ng 	offset += pointer_size;
672968bcca2SKa Ho Ng 	pnp->table = elf_address_from_pointer(efile, p + offset);
673968bcca2SKa Ho Ng 	offset += pointer_size;
674968bcca2SKa Ho Ng 	offset = roundup(offset, pointer_size);
675968bcca2SKa Ho Ng 	pnp->entry_len = elf_int(efile, p + offset);
676968bcca2SKa Ho Ng 	offset += sizeof(int);
677968bcca2SKa Ho Ng 	pnp->num_entry = elf_int(efile, p + offset);
678968bcca2SKa Ho Ng 	free(p);
679968bcca2SKa Ho Ng 	return (0);
680968bcca2SKa Ho Ng }
681968bcca2SKa Ho Ng 
682968bcca2SKa Ho Ng int
elf_reloc(struct elf_file * efile,const void * reldata,Elf_Type reltype,GElf_Addr relbase,GElf_Addr dataoff,size_t len,void * dest)683968bcca2SKa Ho Ng elf_reloc(struct elf_file *efile, const void *reldata, Elf_Type reltype,
684968bcca2SKa Ho Ng     GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
685968bcca2SKa Ho Ng {
686968bcca2SKa Ho Ng 	return (efile->ef_reloc(efile, reldata, reltype, relbase, dataoff, len,
687968bcca2SKa Ho Ng 	    dest));
688968bcca2SKa Ho Ng }
68943628a31SKa Ho Ng 
69043628a31SKa Ho Ng int
elf_lookup_symbol(struct elf_file * efile,const char * name,GElf_Sym ** sym,bool see_local)691*72e15f76SKa Ho Ng elf_lookup_symbol(struct elf_file *efile, const char *name, GElf_Sym **sym,
692*72e15f76SKa Ho Ng     bool see_local)
69343628a31SKa Ho Ng {
694*72e15f76SKa Ho Ng 	return (EF_LOOKUP_SYMBOL(efile, name, sym, see_local));
69543628a31SKa Ho Ng }
696