ef_obj.c revision 330897
1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 2000, Boris Popov
5 * Copyright (c) 1998-2000 Doug Rabson
6 * Copyright (c) 2004 Peter Wemm
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *    This product includes software developed by Boris Popov.
20 * 4. Neither the name of the author nor the names of any co-contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 * $FreeBSD: stable/11/usr.sbin/kldxref/ef_obj.c 330897 2018-03-14 03:19:51Z eadler $
37 */
38
39#include <sys/param.h>
40#include <sys/linker.h>
41#include <string.h>
42#include <stdio.h>
43#include <stdlib.h>
44#include <unistd.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <machine/elf.h>
48#define FREEBSD_ELF
49
50#include <err.h>
51
52#include "ef.h"
53
54typedef struct {
55	void		*addr;
56	Elf_Off		size;
57	int		flags;
58	int		sec;	/* Original section */
59	char		*name;
60} Elf_progent;
61
62typedef struct {
63	Elf_Rel		*rel;
64	int		nrel;
65	int		sec;
66} Elf_relent;
67
68typedef struct {
69	Elf_Rela	*rela;
70	int		nrela;
71	int		sec;
72} Elf_relaent;
73
74struct ef_file {
75	char		*ef_name;
76	int		ef_fd;
77	Elf_Ehdr	ef_hdr;
78	struct elf_file *ef_efile;
79
80	caddr_t		address;
81	Elf_Off		size;
82	Elf_Shdr	*e_shdr;
83
84	Elf_progent	*progtab;
85	int		nprogtab;
86
87	Elf_relaent	*relatab;
88	int		nrela;
89
90	Elf_relent	*reltab;
91	int		nrel;
92
93	Elf_Sym		*ddbsymtab;	/* The symbol table we are using */
94	long		ddbsymcnt;	/* Number of symbols */
95	caddr_t		ddbstrtab;	/* String table */
96	long		ddbstrcnt;	/* number of bytes in string table */
97
98	caddr_t		shstrtab;	/* Section name string table */
99	long		shstrcnt;	/* number of bytes in string table */
100
101	int		ef_verbose;
102};
103
104static int ef_obj_get_type(elf_file_t ef);
105static int ef_obj_close(elf_file_t ef);
106static int ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
107static int ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
108    void **ptr);
109static int ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len,
110    void *dest);
111static int ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len,
112    void *dest);
113static int ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len,
114    char *dest);
115static int ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
116    void **ptr);
117static int ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
118    void **ptr);
119static Elf_Addr ef_obj_symaddr(elf_file_t ef, Elf_Size symidx);
120static int ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp,
121    long *stopp, long *countp);
122static int ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
123
124static struct elf_file_ops ef_obj_file_ops = {
125	ef_obj_get_type,
126	ef_obj_close,
127	ef_obj_read,
128	ef_obj_read_entry,
129	ef_obj_seg_read,
130	ef_obj_seg_read_rel,
131	ef_obj_seg_read_string,
132	ef_obj_seg_read_entry,
133	ef_obj_seg_read_entry_rel,
134	ef_obj_symaddr,
135	ef_obj_lookup_set,
136	ef_obj_lookup_symbol
137};
138
139static int
140ef_obj_get_type(elf_file_t __unused ef)
141{
142
143	return (EFT_KLD);
144}
145
146static int
147ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
148{
149	Elf_Sym *symp;
150	const char *strp;
151	int i;
152
153	for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
154		strp = ef->ddbstrtab + symp->st_name;
155		if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) {
156			*sym = symp;
157			return 0;
158		}
159	}
160	return ENOENT;
161}
162
163static int
164ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp,
165    long *countp)
166{
167	int i;
168
169	for (i = 0; i < ef->nprogtab; i++) {
170		if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) &&
171		    strcmp(ef->progtab[i].name + 4, name) == 0) {
172			*startp = (char *)ef->progtab[i].addr - ef->address;
173			*stopp = (char *)ef->progtab[i].addr +
174			    ef->progtab[i].size - ef->address;
175			*countp = (*stopp - *startp) / sizeof(void *);
176			return (0);
177		}
178	}
179	return (ESRCH);
180}
181
182static Elf_Addr
183ef_obj_symaddr(elf_file_t ef, Elf_Size symidx)
184{
185	const Elf_Sym *sym;
186
187	if (symidx >= (size_t) ef->ddbsymcnt)
188		return (0);
189	sym = ef->ddbsymtab + symidx;
190
191	if (sym->st_shndx != SHN_UNDEF)
192		return (sym->st_value - (Elf_Addr)ef->address);
193	return (0);
194}
195
196static int
197ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
198{
199	ssize_t r;
200
201	if (offset != (Elf_Off)-1) {
202		if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
203			return EIO;
204	}
205
206	r = read(ef->ef_fd, dest, len);
207	if (r != -1 && (size_t)r == len)
208		return 0;
209	else
210		return EIO;
211}
212
213static int
214ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
215{
216	int error;
217
218	*ptr = malloc(len);
219	if (*ptr == NULL)
220		return ENOMEM;
221	error = ef_obj_read(ef, offset, len, *ptr);
222	if (error)
223		free(*ptr);
224	return error;
225}
226
227static int
228ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
229{
230
231	if (offset + len > ef->size) {
232		if (ef->ef_verbose)
233			warnx("ef_obj_seg_read(%s): bad offset/len (%lx:%ld)",
234			    ef->ef_name, (long)offset, (long)len);
235		return (EFAULT);
236	}
237	bcopy(ef->address + offset, dest, len);
238	return (0);
239}
240
241static int
242ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
243{
244	char *memaddr;
245	Elf_Rel *r;
246	Elf_Rela *a;
247	Elf_Off secbase, dataoff;
248	int error, i, sec;
249
250	if (offset + len > ef->size) {
251		if (ef->ef_verbose)
252			warnx("ef_obj_seg_read_rel(%s): bad offset/len (%lx:%ld)",
253			    ef->ef_name, (long)offset, (long)len);
254		return (EFAULT);
255	}
256	bcopy(ef->address + offset, dest, len);
257
258	/* Find out which section contains the data. */
259	memaddr = ef->address + offset;
260	sec = -1;
261	secbase = dataoff = 0;
262	for (i = 0; i < ef->nprogtab; i++) {
263		if (ef->progtab[i].addr == NULL)
264			continue;
265		if (memaddr < (char *)ef->progtab[i].addr || memaddr + len >
266		     (char *)ef->progtab[i].addr + ef->progtab[i].size)
267			continue;
268		sec = ef->progtab[i].sec;
269		/* We relocate to address 0. */
270		secbase = (char *)ef->progtab[i].addr - ef->address;
271		dataoff = memaddr - ef->address;
272		break;
273	}
274
275	if (sec == -1)
276		return (EFAULT);
277
278	/* Now do the relocations. */
279	for (i = 0; i < ef->nrel; i++) {
280		if (ef->reltab[i].sec != sec)
281			continue;
282		for (r = ef->reltab[i].rel;
283		     r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) {
284			error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, secbase,
285			    dataoff, len, dest);
286			if (error != 0)
287				return (error);
288		}
289	}
290	for (i = 0; i < ef->nrela; i++) {
291		if (ef->relatab[i].sec != sec)
292			continue;
293		for (a = ef->relatab[i].rela;
294		     a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) {
295			error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA,
296			    secbase, dataoff, len, dest);
297			if (error != 0)
298				return (error);
299		}
300	}
301	return (0);
302}
303
304static int
305ef_obj_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest)
306{
307
308	if (offset >= ef->size) {
309		if (ef->ef_verbose)
310			warnx("ef_obj_seg_read_string(%s): bad offset (%lx)",
311			    ef->ef_name, (long)offset);
312		return (EFAULT);
313	}
314
315	if (ef->size - offset < len)
316		len = ef->size - offset;
317
318	if (strnlen(ef->address + offset, len) == len)
319		return (EFAULT);
320
321	memcpy(dest, ef->address + offset, len);
322	return (0);
323}
324
325static int
326ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
327{
328	int error;
329
330	*ptr = malloc(len);
331	if (*ptr == NULL)
332		return ENOMEM;
333	error = ef_obj_seg_read(ef, offset, len, *ptr);
334	if (error)
335		free(*ptr);
336	return error;
337}
338
339static int
340ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
341    void **ptr)
342{
343	int error;
344
345	*ptr = malloc(len);
346	if (*ptr == NULL)
347		return ENOMEM;
348	error = ef_obj_seg_read_rel(ef, offset, len, *ptr);
349	if (error)
350		free(*ptr);
351	return error;
352}
353
354int
355ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
356{
357	elf_file_t ef;
358	Elf_Ehdr *hdr;
359	Elf_Shdr *shdr;
360	Elf_Sym *es;
361	char *mapbase;
362	void *vtmp;
363	size_t mapsize, alignmask, max_addralign;
364	int error, fd, pb, ra, res, rl;
365	int i, j, nbytes, nsym, shstrindex, symstrindex, symtabindex;
366
367	if (filename == NULL)
368		return EFTYPE;
369	if ((fd = open(filename, O_RDONLY)) == -1)
370		return errno;
371
372	ef = calloc(1, sizeof(*ef));
373	if (ef == NULL) {
374		close(fd);
375		return (ENOMEM);
376	}
377
378	efile->ef_ef = ef;
379	efile->ef_ops = &ef_obj_file_ops;
380
381	ef->ef_verbose = verbose;
382	ef->ef_fd = fd;
383	ef->ef_name = strdup(filename);
384	ef->ef_efile = efile;
385	hdr = (Elf_Ehdr *)&ef->ef_hdr;
386
387	res = read(fd, hdr, sizeof(*hdr));
388	error = EFTYPE;
389	if (res != sizeof(*hdr))
390		goto out;
391	if (!IS_ELF(*hdr))
392		goto out;
393	if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
394	    hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
395	    hdr->e_ident[EI_VERSION] != EV_CURRENT ||
396	    hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH ||
397	    hdr->e_type != ET_REL)
398		goto out;
399
400	nbytes = hdr->e_shnum * hdr->e_shentsize;
401	if (nbytes == 0 || hdr->e_shoff == 0 ||
402	    hdr->e_shentsize != sizeof(Elf_Shdr))
403		goto out;
404
405	if (ef_obj_read_entry(ef, hdr->e_shoff, nbytes, &vtmp) != 0) {
406		printf("ef_read_entry failed\n");
407		goto out;
408	}
409	ef->e_shdr = shdr = vtmp;
410
411	/* Scan the section header for information and table sizing. */
412	nsym = 0;
413	symtabindex = -1;
414	symstrindex = -1;
415	for (i = 0; i < hdr->e_shnum; i++) {
416		switch (shdr[i].sh_type) {
417		case SHT_PROGBITS:
418		case SHT_NOBITS:
419			ef->nprogtab++;
420			break;
421		case SHT_SYMTAB:
422			nsym++;
423			symtabindex = i;
424			symstrindex = shdr[i].sh_link;
425			break;
426		case SHT_REL:
427			ef->nrel++;
428			break;
429		case SHT_RELA:
430			ef->nrela++;
431			break;
432		case SHT_STRTAB:
433			break;
434		}
435	}
436
437	if (ef->nprogtab == 0) {
438		warnx("%s: file has no contents", filename);
439		goto out;
440	}
441	if (nsym != 1) {
442		warnx("%s: file has no valid symbol table", filename);
443		goto out;
444	}
445	if (symstrindex < 0 || symstrindex > hdr->e_shnum ||
446	    shdr[symstrindex].sh_type != SHT_STRTAB) {
447		warnx("%s: file has invalid symbol strings", filename);
448		goto out;
449	}
450
451	/* Allocate space for tracking the load chunks */
452	if (ef->nprogtab != 0)
453		ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab));
454	if (ef->nrel != 0)
455		ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab));
456	if (ef->nrela != 0)
457		ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab));
458	if ((ef->nprogtab != 0 && ef->progtab == NULL) ||
459	    (ef->nrel != 0 && ef->reltab == NULL) ||
460	    (ef->nrela != 0 && ef->relatab == NULL)) {
461		printf("malloc failed\n");
462		error = ENOMEM;
463		goto out;
464	}
465
466	ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym);
467	if (ef_obj_read_entry(ef, shdr[symtabindex].sh_offset,
468	    shdr[symtabindex].sh_size, (void**)&ef->ddbsymtab) != 0) {
469		printf("ef_read_entry failed\n");
470		goto out;
471	}
472
473	ef->ddbstrcnt = shdr[symstrindex].sh_size;
474	if (ef_obj_read_entry(ef, shdr[symstrindex].sh_offset,
475	    shdr[symstrindex].sh_size, (void**)&ef->ddbstrtab) != 0) {
476		printf("ef_read_entry failed\n");
477		goto out;
478	}
479
480	/* Do we have a string table for the section names?  */
481	shstrindex = -1;
482	if (hdr->e_shstrndx != 0 &&
483	    shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) {
484		shstrindex = hdr->e_shstrndx;
485		ef->shstrcnt = shdr[shstrindex].sh_size;
486		if (ef_obj_read_entry(ef, shdr[shstrindex].sh_offset,
487		    shdr[shstrindex].sh_size, (void**)&ef->shstrtab) != 0) {
488			printf("ef_read_entry failed\n");
489			goto out;
490		}
491	}
492
493	/* Size up code/data(progbits) and bss(nobits). */
494	alignmask = 0;
495	max_addralign = 0;
496	mapsize = 0;
497	for (i = 0; i < hdr->e_shnum; i++) {
498		switch (shdr[i].sh_type) {
499		case SHT_PROGBITS:
500		case SHT_NOBITS:
501			alignmask = shdr[i].sh_addralign - 1;
502			if (shdr[i].sh_addralign > max_addralign)
503				max_addralign = shdr[i].sh_addralign;
504			mapsize += alignmask;
505			mapsize &= ~alignmask;
506			mapsize += shdr[i].sh_size;
507			break;
508		}
509	}
510
511	/* We know how much space we need for the text/data/bss/etc. */
512	ef->size = mapsize;
513	if (posix_memalign((void **)&ef->address, max_addralign, mapsize)) {
514		printf("posix_memalign failed\n");
515		goto out;
516	}
517	mapbase = ef->address;
518
519	/*
520	 * Now load code/data(progbits), zero bss(nobits), allocate
521	 * space for and load relocs
522	 */
523	pb = 0;
524	rl = 0;
525	ra = 0;
526	alignmask = 0;
527	for (i = 0; i < hdr->e_shnum; i++) {
528		switch (shdr[i].sh_type) {
529		case SHT_PROGBITS:
530		case SHT_NOBITS:
531			alignmask = shdr[i].sh_addralign - 1;
532			mapbase += alignmask;
533			mapbase  = (char *)((uintptr_t)mapbase & ~alignmask);
534			ef->progtab[pb].addr = (void *)(uintptr_t)mapbase;
535			if (shdr[i].sh_type == SHT_PROGBITS) {
536				ef->progtab[pb].name = "<<PROGBITS>>";
537				if (ef_obj_read(ef, shdr[i].sh_offset,
538				    shdr[i].sh_size,
539				    ef->progtab[pb].addr) != 0) {
540					printf("failed to read progbits\n");
541					goto out;
542				}
543			} else {
544				ef->progtab[pb].name = "<<NOBITS>>";
545				bzero(ef->progtab[pb].addr, shdr[i].sh_size);
546			}
547			ef->progtab[pb].size = shdr[i].sh_size;
548			ef->progtab[pb].sec = i;
549			if (ef->shstrtab && shdr[i].sh_name != 0)
550				ef->progtab[pb].name =
551				    ef->shstrtab + shdr[i].sh_name;
552
553			/* Update all symbol values with the offset. */
554			for (j = 0; j < ef->ddbsymcnt; j++) {
555				es = &ef->ddbsymtab[j];
556				if (es->st_shndx != i)
557					continue;
558				es->st_value += (Elf_Addr)ef->progtab[pb].addr;
559			}
560			mapbase += shdr[i].sh_size;
561			pb++;
562			break;
563		case SHT_REL:
564			ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
565			ef->reltab[rl].sec = shdr[i].sh_info;
566			if (ef_obj_read_entry(ef, shdr[i].sh_offset,
567			    shdr[i].sh_size, (void**)&ef->reltab[rl].rel) !=
568			    0) {
569				printf("ef_read_entry failed\n");
570				goto out;
571			}
572			rl++;
573			break;
574		case SHT_RELA:
575			ef->relatab[ra].nrela =
576			    shdr[i].sh_size / sizeof(Elf_Rela);
577			ef->relatab[ra].sec = shdr[i].sh_info;
578			if (ef_obj_read_entry(ef, shdr[i].sh_offset,
579			    shdr[i].sh_size, (void**)&ef->relatab[ra].rela) !=
580			    0) {
581				printf("ef_read_entry failed\n");
582				goto out;
583			}
584			ra++;
585			break;
586		}
587	}
588	error = 0;
589out:
590	if (error)
591		ef_obj_close(ef);
592	return error;
593}
594
595static int
596ef_obj_close(elf_file_t ef)
597{
598	int i;
599
600	close(ef->ef_fd);
601	if (ef->ef_name)
602		free(ef->ef_name);
603	if (ef->e_shdr != NULL)
604		free(ef->e_shdr);
605	if (ef->size != 0)
606		free(ef->address);
607	if (ef->nprogtab != 0)
608		free(ef->progtab);
609	if (ef->nrel != 0) {
610		for (i = 0; i < ef->nrel; i++)
611			if (ef->reltab[i].rel != NULL)
612				free(ef->reltab[i].rel);
613		free(ef->reltab);
614	}
615	if (ef->nrela != 0) {
616		for (i = 0; i < ef->nrela; i++)
617			if (ef->relatab[i].rela != NULL)
618				free(ef->relatab[i].rela);
619		free(ef->relatab);
620	}
621	if (ef->ddbsymtab != NULL)
622		free(ef->ddbsymtab);
623	if (ef->ddbstrtab != NULL)
624		free(ef->ddbstrtab);
625	if (ef->shstrtab != NULL)
626		free(ef->shstrtab);
627	ef->ef_efile->ef_ops = NULL;
628	ef->ef_efile->ef_ef = NULL;
629	free(ef);
630
631	return 0;
632}
633