1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2021-2023 John Baldwin <jhb@FreeBSD.org>
5 *
6 * This software was developed by SRI International and the University
7 * of Cambridge Computer Laboratory (Department of Computer Science
8 * and Technology) under Defense Advanced Research Projects Agency
9 * (DARPA) contract HR0011-18-C-0016 ("ECATS"), as part of the DARPA
10 * SSITH research programme and under DARPA Contract No. HR001123C0031
11 * ("MTSS").
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/param.h>
36#include <sys/endian.h>
37
38#include <err.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <gelf.h>
42#include <libelf.h>
43#include <stdlib.h>
44#include <string.h>
45#include <unistd.h>
46
47#include "ef.h"
48
49SET_DECLARE(elf_reloc, struct elf_reloc_data);
50
51static elf_reloc_t *
52elf_find_reloc(const GElf_Ehdr *hdr)
53{
54	struct elf_reloc_data **erd;
55
56	SET_FOREACH(erd, elf_reloc) {
57		if (hdr->e_ident[EI_CLASS] == (*erd)->class &&
58		    hdr->e_ident[EI_DATA] == (*erd)->data &&
59		    hdr->e_machine == (*erd)->machine)
60			return ((*erd)->reloc);
61	}
62	return (NULL);
63}
64
65int
66elf_open_file(struct elf_file *efile, const char *filename, int verbose)
67{
68	int error;
69
70	memset(efile, 0, sizeof(*efile));
71	efile->ef_filename = filename;
72	efile->ef_fd = open(filename, O_RDONLY);
73	if (efile->ef_fd == -1) {
74		if (verbose)
75			warn("open(%s)", filename);
76		return (errno);
77	}
78
79	efile->ef_elf = elf_begin(efile->ef_fd, ELF_C_READ, NULL);
80	if (efile->ef_elf == NULL) {
81		if (verbose)
82			warnx("elf_begin(%s): %s", filename, elf_errmsg(0));
83		elf_close_file(efile);
84		return (EINVAL);
85	}
86
87	if (elf_kind(efile->ef_elf) != ELF_K_ELF) {
88		if (verbose)
89			warnx("%s: not an ELF file", filename);
90		elf_close_file(efile);
91		return (EINVAL);
92	}
93
94	if (gelf_getehdr(efile->ef_elf, &efile->ef_hdr) == NULL) {
95		if (verbose)
96			warnx("gelf_getehdr(%s): %s", filename, elf_errmsg(0));
97		elf_close_file(efile);
98		return (EINVAL);
99	}
100
101	efile->ef_reloc = elf_find_reloc(&efile->ef_hdr);
102	if (efile->ef_reloc == NULL) {
103		if (verbose)
104			warnx("%s: unsupported architecture", filename);
105		elf_close_file(efile);
106		return (EFTYPE);
107	}
108
109	error = ef_open(efile, verbose);
110	if (error != 0) {
111		error = ef_obj_open(efile, verbose);
112		if (error != 0) {
113			if (verbose)
114				warnc(error, "%s: not a valid DSO or object file",
115				    filename);
116			elf_close_file(efile);
117			return (error);
118		}
119	}
120
121	efile->ef_pointer_size = elf_object_size(efile, ELF_T_ADDR);
122
123	return (0);
124}
125
126void
127elf_close_file(struct elf_file *efile)
128{
129	if (efile->ef_ops != NULL) {
130		EF_CLOSE(efile);
131	}
132	if (efile->ef_elf != NULL) {
133		elf_end(efile->ef_elf);
134		efile->ef_elf = NULL;
135	}
136	if (efile->ef_fd > 0) {
137		close(efile->ef_fd);
138		efile->ef_fd = -1;
139	}
140}
141
142bool
143elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr)
144{
145	if (efile->ef_hdr.e_ident[EI_CLASS] != hdr->e_ident[EI_CLASS] ||
146	    efile->ef_hdr.e_ident[EI_DATA] != hdr->e_ident[EI_DATA] ||
147	    efile->ef_hdr.e_machine != hdr->e_machine)
148		return (false);
149	return (true);
150}
151
152size_t
153elf_object_size(struct elf_file *efile, Elf_Type type)
154{
155	return (gelf_fsize(efile->ef_elf, type, 1, efile->ef_hdr.e_version));
156}
157
158/*
159 * The number of objects of 'type' in region of the file of size
160 * 'file_size'.
161 */
162static size_t
163elf_object_count(struct elf_file *efile, Elf_Type type, size_t file_size)
164{
165	return (file_size / elf_object_size(efile, type));
166}
167
168int
169elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst, size_t len)
170{
171	ssize_t nread;
172
173	nread = pread(efile->ef_fd, dst, len, offset);
174	if (nread == -1)
175		return (errno);
176	if (nread != len)
177		return (EIO);
178	return (0);
179}
180
181int
182elf_read_raw_data_alloc(struct elf_file *efile, off_t offset, size_t len,
183    void **out)
184{
185	void *buf;
186	int error;
187
188	buf = malloc(len);
189	if (buf == NULL)
190		return (ENOMEM);
191	error = elf_read_raw_data(efile, offset, buf, len);
192	if (error != 0) {
193		free(buf);
194		return (error);
195	}
196	*out = buf;
197	return (0);
198}
199
200int
201elf_read_raw_string(struct elf_file *efile, off_t offset, char *dst, size_t len)
202{
203	ssize_t nread;
204
205	nread = pread(efile->ef_fd, dst, len, offset);
206	if (nread == -1)
207		return (errno);
208	if (nread == 0)
209		return (EIO);
210
211	/* A short read is ok so long as the data contains a terminator. */
212	if (strnlen(dst, nread) == nread)
213		return (EFAULT);
214
215	return (0);
216}
217
218int
219elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset, size_t len,
220    void **out)
221{
222	Elf_Data dst, src;
223	void *buf;
224	int error;
225
226	buf = malloc(len);
227	if (buf == NULL)
228		return (ENOMEM);
229
230	error = elf_read_raw_data(efile, offset, buf, len);
231	if (error != 0) {
232		free(buf);
233		return (error);
234	}
235
236	memset(&dst, 0, sizeof(dst));
237	memset(&src, 0, sizeof(src));
238
239	src.d_buf = buf;
240	src.d_size = len;
241	src.d_type = type;
242	src.d_version = efile->ef_hdr.e_version;
243
244	dst.d_buf = buf;
245	dst.d_size = len;
246	dst.d_version = EV_CURRENT;
247
248	if (gelf_xlatetom(efile->ef_elf, &dst, &src, elf_encoding(efile)) ==
249	    NULL) {
250		free(buf);
251		return (ENXIO);
252	}
253
254	if (dst.d_size != len)
255		warnx("elf_read_data: translation of type %u size mismatch",
256		    type);
257
258	*out = buf;
259	return (0);
260}
261
262int
263elf_read_relocated_data(struct elf_file *efile, GElf_Addr address, size_t len,
264    void **buf)
265{
266	int error;
267	void *p;
268
269	p = malloc(len);
270	if (p == NULL)
271		return (ENOMEM);
272	error = EF_SEG_READ_REL(efile, address, len, p);
273	if (error != 0) {
274		free(p);
275		return (error);
276	}
277	*buf = p;
278	return (0);
279}
280
281int
282elf_read_phdrs(struct elf_file *efile, size_t *nphdrp, GElf_Phdr **phdrp)
283{
284	GElf_Phdr *phdr;
285	size_t nphdr, i;
286	int error;
287
288	if (elf_getphdrnum(efile->ef_elf, &nphdr) == -1)
289		return (EFTYPE);
290
291	phdr = calloc(nphdr, sizeof(*phdr));
292	if (phdr == NULL)
293		return (ENOMEM);
294
295	for (i = 0; i < nphdr; i++) {
296		if (gelf_getphdr(efile->ef_elf, i, &phdr[i]) == NULL) {
297			error = EFTYPE;
298			goto out;
299		}
300	}
301
302	*nphdrp = nphdr;
303	*phdrp = phdr;
304	return (0);
305out:
306	free(phdr);
307	return (error);
308}
309
310int
311elf_read_shdrs(struct elf_file *efile, size_t *nshdrp, GElf_Shdr **shdrp)
312{
313	GElf_Shdr *shdr;
314	Elf_Scn *scn;
315	size_t nshdr, i;
316	int error;
317
318	if (elf_getshdrnum(efile->ef_elf, &nshdr) == -1)
319		return (EFTYPE);
320
321	shdr = calloc(nshdr, sizeof(*shdr));
322	if (shdr == NULL)
323		return (ENOMEM);
324
325	for (i = 0; i < nshdr; i++) {
326		scn = elf_getscn(efile->ef_elf, i);
327		if (scn == NULL) {
328			error = EFTYPE;
329			goto out;
330		}
331		if (gelf_getshdr(scn, &shdr[i]) == NULL) {
332			error = EFTYPE;
333			goto out;
334		}
335	}
336
337	*nshdrp = nshdr;
338	*shdrp = shdr;
339	return (0);
340out:
341	free(shdr);
342	return (error);
343}
344
345int
346elf_read_dynamic(struct elf_file *efile, int section_index, size_t *ndynp,
347    GElf_Dyn **dynp)
348{
349	GElf_Shdr shdr;
350	Elf_Scn *scn;
351	Elf_Data *data;
352	GElf_Dyn *dyn;
353	long i, ndyn;
354
355	scn = elf_getscn(efile->ef_elf, section_index);
356	if (scn == NULL)
357		return (EINVAL);
358	if (gelf_getshdr(scn, &shdr) == NULL)
359		return (EINVAL);
360	data = elf_getdata(scn, NULL);
361	if (data == NULL)
362		return (EINVAL);
363
364	ndyn = elf_object_count(efile, ELF_T_DYN, shdr.sh_size);
365	dyn = calloc(ndyn, sizeof(*dyn));
366	if (dyn == NULL)
367		return (ENOMEM);
368
369	for (i = 0; i < ndyn; i++) {
370		if (gelf_getdyn(data, i, &dyn[i]) == NULL) {
371			free(dyn);
372			return (EINVAL);
373		}
374	}
375
376	*ndynp = ndyn;
377	*dynp = dyn;
378	return (0);
379}
380
381int
382elf_read_symbols(struct elf_file *efile, int section_index, size_t *nsymp,
383    GElf_Sym **symp)
384{
385	GElf_Shdr shdr;
386	Elf_Scn *scn;
387	Elf_Data *data;
388	GElf_Sym *sym;
389	size_t i, nsym;
390
391	scn = elf_getscn(efile->ef_elf, section_index);
392	if (scn == NULL)
393		return (EINVAL);
394	if (gelf_getshdr(scn, &shdr) == NULL)
395		return (EINVAL);
396	data = elf_getdata(scn, NULL);
397	if (data == NULL)
398		return (EINVAL);
399
400	nsym = elf_object_count(efile, ELF_T_SYM, shdr.sh_size);
401	sym = calloc(nsym, sizeof(*sym));
402	if (sym == NULL)
403		return (ENOMEM);
404
405	for (i = 0; i < nsym; i++) {
406		if (gelf_getsym(data, i, &sym[i]) == NULL) {
407			free(sym);
408			return (EINVAL);
409		}
410	}
411
412	*nsymp = nsym;
413	*symp = sym;
414	return (0);
415}
416
417int
418elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr,
419    long *strcnt, char **strtab)
420{
421	int error;
422
423	if (shdr->sh_type != SHT_STRTAB)
424		return (EINVAL);
425	error = elf_read_raw_data_alloc(efile, shdr->sh_offset, shdr->sh_size,
426	    (void **)strtab);
427	if (error != 0)
428		return (error);
429	*strcnt = shdr->sh_size;
430	return (0);
431}
432
433int
434elf_read_rel(struct elf_file *efile, int section_index, long *nrelp,
435    GElf_Rel **relp)
436{
437	GElf_Shdr shdr;
438	Elf_Scn *scn;
439	Elf_Data *data;
440	GElf_Rel *rel;
441	long i, nrel;
442
443	scn = elf_getscn(efile->ef_elf, section_index);
444	if (scn == NULL)
445		return (EINVAL);
446	if (gelf_getshdr(scn, &shdr) == NULL)
447		return (EINVAL);
448	data = elf_getdata(scn, NULL);
449	if (data == NULL)
450		return (EINVAL);
451
452	nrel = elf_object_count(efile, ELF_T_REL, shdr.sh_size);
453	rel = calloc(nrel, sizeof(*rel));
454	if (rel == NULL)
455		return (ENOMEM);
456
457	for (i = 0; i < nrel; i++) {
458		if (gelf_getrel(data, i, &rel[i]) == NULL) {
459			free(rel);
460			return (EINVAL);
461		}
462	}
463
464	*nrelp = nrel;
465	*relp = rel;
466	return (0);
467}
468
469int
470elf_read_rela(struct elf_file *efile, int section_index, long *nrelap,
471    GElf_Rela **relap)
472{
473	GElf_Shdr shdr;
474	Elf_Scn *scn;
475	Elf_Data *data;
476	GElf_Rela *rela;
477	long i, nrela;
478
479	scn = elf_getscn(efile->ef_elf, section_index);
480	if (scn == NULL)
481		return (EINVAL);
482	if (gelf_getshdr(scn, &shdr) == NULL)
483		return (EINVAL);
484	data = elf_getdata(scn, NULL);
485	if (data == NULL)
486		return (EINVAL);
487
488	nrela = elf_object_count(efile, ELF_T_RELA, shdr.sh_size);
489	rela = calloc(nrela, sizeof(*rela));
490	if (rela == NULL)
491		return (ENOMEM);
492
493	for (i = 0; i < nrela; i++) {
494		if (gelf_getrela(data, i, &rela[i]) == NULL) {
495			free(rela);
496			return (EINVAL);
497		}
498	}
499
500	*nrelap = nrela;
501	*relap = rela;
502	return (0);
503}
504
505size_t
506elf_pointer_size(struct elf_file *efile)
507{
508	return (efile->ef_pointer_size);
509}
510
511int
512elf_int(struct elf_file *efile, const void *p)
513{
514	if (elf_encoding(efile) == ELFDATA2LSB)
515		return (le32dec(p));
516	else
517		return (be32dec(p));
518}
519
520GElf_Addr
521elf_address_from_pointer(struct elf_file *efile, const void *p)
522{
523	switch (elf_class(efile)) {
524	case ELFCLASS32:
525		if (elf_encoding(efile) == ELFDATA2LSB)
526			return (le32dec(p));
527		else
528			return (be32dec(p));
529	case ELFCLASS64:
530		if (elf_encoding(efile) == ELFDATA2LSB)
531			return (le64dec(p));
532		else
533			return (be64dec(p));
534	default:
535		__unreachable();
536	}
537}
538
539int
540elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst,
541    size_t len)
542{
543	return (EF_SEG_READ_STRING(efile, address, len, dst));
544}
545
546int
547elf_read_linker_set(struct elf_file *efile, const char *name, GElf_Addr **bufp,
548    long *countp)
549{
550	GElf_Addr *buf, start, stop;
551	char *p;
552	void *raw;
553	long i, count;
554	int error;
555
556	error = EF_LOOKUP_SET(efile, name, &start, &stop, &count);
557	if (error != 0)
558		return (error);
559
560	error = elf_read_relocated_data(efile, start,
561	    count * elf_pointer_size(efile), &raw);
562	if (error != 0)
563		return (error);
564
565	buf = calloc(count, sizeof(*buf));
566	if (buf == NULL) {
567		free(raw);
568		return (ENOMEM);
569	}
570
571	p = raw;
572	for (i = 0; i < count; i++) {
573		buf[i] = elf_address_from_pointer(efile, p);
574		p += elf_pointer_size(efile);
575	}
576	free(raw);
577
578	*bufp = buf;
579	*countp = count;
580	return (0);
581}
582
583int
584elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr,
585    struct Gmod_depend *mdp)
586{
587	int *p;
588	int error;
589
590	error = elf_read_relocated_data(efile, addr, sizeof(int) * 3,
591	    (void **)&p);
592	if (error != 0)
593		return (error);
594
595	memset(mdp, 0, sizeof(*mdp));
596	mdp->md_ver_minimum = elf_int(efile, p);
597	mdp->md_ver_preferred = elf_int(efile, p + 1);
598	mdp->md_ver_maximum = elf_int(efile, p + 2);
599	free(p);
600	return (0);
601}
602
603int
604elf_read_mod_version(struct elf_file *efile, GElf_Addr addr,
605    struct Gmod_version *mdv)
606{
607	int error, value;
608
609	error = EF_SEG_READ_REL(efile, addr, sizeof(int), &value);
610	if (error != 0)
611		return (error);
612
613	memset(mdv, 0, sizeof(*mdv));
614	mdv->mv_version = elf_int(efile, &value);
615	return (0);
616}
617
618int
619elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr,
620    struct Gmod_metadata *md)
621{
622	char *p;
623	size_t len, offset, pointer_size;
624	int error;
625
626	pointer_size = elf_pointer_size(efile);
627	len = 2 * sizeof(int);
628	len = roundup(len, pointer_size);
629	len += 2 * pointer_size;
630
631	error = elf_read_relocated_data(efile, addr, len, (void **)&p);
632	if (error != 0)
633		return (error);
634
635	memset(md, 0, sizeof(*md));
636	offset = 0;
637	md->md_version = elf_int(efile, p + offset);
638	offset += sizeof(int);
639	md->md_type = elf_int(efile, p + offset);
640	offset += sizeof(int);
641	offset = roundup(offset, pointer_size);
642	md->md_data = elf_address_from_pointer(efile, p + offset);
643	offset += pointer_size;
644 	md->md_cval = elf_address_from_pointer(efile, p + offset);
645	free(p);
646	return (0);
647}
648
649int
650elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr,
651    struct Gmod_pnp_match_info *pnp)
652{
653	char *p;
654	size_t len, offset, pointer_size;
655	int error;
656
657	pointer_size = elf_pointer_size(efile);
658	len = 3 * pointer_size;
659	len = roundup(len, pointer_size);
660	len += 2 * sizeof(int);
661
662	error = elf_read_relocated_data(efile, addr, len, (void **)&p);
663	if (error != 0)
664		return (error);
665
666	memset(pnp, 0, sizeof(*pnp));
667	offset = 0;
668	pnp->descr = elf_address_from_pointer(efile, p + offset);
669	offset += pointer_size;
670	pnp->bus = elf_address_from_pointer(efile, p + offset);
671	offset += pointer_size;
672	pnp->table = elf_address_from_pointer(efile, p + offset);
673	offset += pointer_size;
674	offset = roundup(offset, pointer_size);
675	pnp->entry_len = elf_int(efile, p + offset);
676	offset += sizeof(int);
677	pnp->num_entry = elf_int(efile, p + offset);
678	free(p);
679	return (0);
680}
681
682int
683elf_reloc(struct elf_file *efile, const void *reldata, Elf_Type reltype,
684    GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest)
685{
686	return (efile->ef_reloc(efile, reldata, reltype, relbase, dataoff, len,
687	    dest));
688}
689