GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ctfdump/elf.c Lines: 0 130 0.0 %
Date: 2017-11-13 Branches: 0 128 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: elf.c,v 1.7 2017/11/06 14:59:27 mpi Exp $ */
2
3
/*
4
 * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <machine/reloc.h>
22
23
#include <assert.h>
24
#include <elf.h>
25
#include <err.h>
26
#include <string.h>
27
28
static int	elf_reloc_size(unsigned long);
29
static void	elf_reloc_apply(const char *, size_t, const char *, size_t,
30
		    ssize_t, char *, size_t);
31
32
int
33
iself(const char *p, size_t filesize)
34
{
35
	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
36
37
	if (filesize < (off_t)sizeof(Elf_Ehdr)) {
38
		warnx("file too small to be ELF");
39
		return 0;
40
	}
41
42
	if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh))
43
		return 0;
44
45
	if (eh->e_ident[EI_CLASS] != ELFCLASS) {
46
		warnx("unexpected word size %u", eh->e_ident[EI_CLASS]);
47
		return 0;
48
	}
49
	if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) {
50
		warnx("unexpected version %u", eh->e_ident[EI_VERSION]);
51
		return 0;
52
	}
53
	if (eh->e_ident[EI_DATA] >= ELFDATANUM) {
54
		warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
55
		return 0;
56
	}
57
	if (eh->e_shoff > filesize) {
58
		warnx("bogus section table offset 0x%llx", (off_t)eh->e_shoff);
59
		return 0;
60
	}
61
	if (eh->e_shentsize < sizeof(Elf_Shdr)) {
62
		warnx("bogus section header size %u", eh->e_shentsize);
63
		return 0;
64
	}
65
	if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) {
66
		warnx("bogus section header count %u", eh->e_shnum);
67
		return 0;
68
	}
69
	if (eh->e_shstrndx >= eh->e_shnum) {
70
		warnx("bogus string table index %u", eh->e_shstrndx);
71
		return 0;
72
	}
73
74
	return 1;
75
}
76
77
int
78
elf_getshstab(const char *p, size_t filesize, const char **shstab,
79
    size_t *shstabsize)
80
{
81
	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
82
	Elf_Shdr		*sh;
83
84
	sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize);
85
	if (sh->sh_type != SHT_STRTAB) {
86
		warnx("unexpected string table type");
87
		return -1;
88
	}
89
	if (sh->sh_offset > filesize) {
90
		warnx("bogus string table offset");
91
		return -1;
92
	}
93
	if (sh->sh_size > filesize - sh->sh_offset) {
94
		warnx("bogus string table size");
95
		return -1;
96
	}
97
	if (shstab != NULL)
98
		*shstab = p + sh->sh_offset;
99
	if (shstabsize != NULL)
100
		*shstabsize = sh->sh_size;
101
102
	return 0;
103
}
104
105
ssize_t
106
elf_getsymtab(const char *p, size_t filesize, const char *shstab,
107
    size_t shstabsz, const Elf_Sym **symtab, size_t *nsymb, const char **strtab,
108
    size_t *strtabsz)
109
{
110
	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
111
	Elf_Shdr	*sh, *symsh;
112
	size_t		 snlen;
113
	ssize_t		 i;
114
115
	snlen = strlen(ELF_SYMTAB);
116
	symsh = NULL;
117
118
	for (i = 0; i < eh->e_shnum; i++) {
119
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
120
121
		if (sh->sh_type != SHT_SYMTAB)
122
			continue;
123
124
		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
125
			continue;
126
127
		if ((sh->sh_offset + sh->sh_size) > filesize)
128
			continue;
129
130
		if (sh->sh_entsize == 0)
131
			continue;
132
133
		if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
134
			if (symtab != NULL)
135
				*symtab = (Elf_Sym *)(p + sh->sh_offset);
136
			if (nsymb != NULL)
137
				*nsymb = (sh->sh_size / sh->sh_entsize);
138
			symsh = sh;
139
140
			break;
141
		}
142
	}
143
144
	if (symsh == NULL || (symsh->sh_link >= eh->e_shnum))
145
		return -1;
146
147
	sh = (Elf_Shdr *)(p + eh->e_shoff + symsh->sh_link * eh->e_shentsize);
148
149
	if ((sh->sh_offset + sh->sh_size) > filesize)
150
		return -1;
151
152
	if (strtab != NULL)
153
		*strtab = p + sh->sh_offset;
154
	if (strtabsz != NULL)
155
		*strtabsz = sh->sh_size;
156
157
	return i;
158
}
159
160
ssize_t
161
elf_getsection(char *p, size_t filesize, const char *sname, const char *shstab,
162
    size_t shstabsz, const char **psdata, size_t *pssz)
163
{
164
	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
165
	Elf_Shdr	*sh;
166
	char		*sdata = NULL;
167
	size_t		 snlen, ssz = 0;
168
	ssize_t		 sidx, i;
169
170
	snlen = strlen(sname);
171
	if (snlen == 0)
172
		return -1;
173
174
	/* Find the given section. */
175
	for (i = 0; i < eh->e_shnum; i++) {
176
		if ((eh->e_shoff + i * eh->e_shentsize) > filesize)
177
			continue;
178
179
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
180
181
		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
182
			continue;
183
184
		if ((sh->sh_offset + sh->sh_size) > filesize)
185
			continue;
186
187
		if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
188
			sidx = i;
189
			sdata = p + sh->sh_offset;
190
			ssz = sh->sh_size;
191
			elf_reloc_apply(p, filesize, shstab, shstabsz, sidx,
192
			    sdata, ssz);
193
			break;
194
		}
195
	}
196
197
	if (sdata == NULL)
198
		return -1;
199
200
	if (psdata != NULL)
201
		*psdata = sdata;
202
	if (pssz != NULL)
203
		*pssz = ssz;
204
205
	return sidx;
206
}
207
208
static int
209
elf_reloc_size(unsigned long type)
210
{
211
	switch (type) {
212
#ifdef R_X86_64_64
213
	case R_X86_64_64:
214
		return sizeof(uint64_t);
215
#endif
216
#ifdef R_X86_64_32
217
	case R_X86_64_32:
218
		return sizeof(uint32_t);
219
#endif
220
#ifdef RELOC_32
221
	case RELOC_32:
222
		return sizeof(uint32_t);
223
#endif
224
	default:
225
		break;
226
	}
227
228
	return -1;
229
}
230
231
#define ELF_WRITE_RELOC(buf, val, rsize)				\
232
do {									\
233
	if (rsize == 4) {						\
234
		uint32_t v32 = val;					\
235
		memcpy(buf, &v32, sizeof(v32));				\
236
	} else {							\
237
		uint64_t v64 = val;					\
238
		memcpy(buf, &v64, sizeof(v64));				\
239
	}								\
240
} while (0)
241
242
static void
243
elf_reloc_apply(const char *p, size_t filesize, const char *shstab,
244
    size_t shstabsz, ssize_t sidx, char *sdata, size_t ssz)
245
{
246
	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
247
	Elf_Shdr	*sh;
248
	Elf_Rel		*rel = NULL;
249
	Elf_RelA	*rela = NULL;
250
	const Elf_Sym	*symtab, *sym;
251
	ssize_t		 symtabidx;
252
	size_t		 nsymb, rsym, rtyp, roff;
253
	size_t		 i, j;
254
	uint64_t	 value;
255
	int		 rsize;
256
257
	/* Find symbol table location and number of symbols. */
258
	symtabidx = elf_getsymtab(p, filesize, shstab, shstabsz, &symtab,
259
	    &nsymb, NULL, NULL);
260
	if (symtabidx == -1) {
261
		warnx("symbol table not found");
262
		return;
263
	}
264
265
	/* Apply possible relocation. */
266
	for (i = 0; i < eh->e_shnum; i++) {
267
		if ((eh->e_shoff + i * eh->e_shentsize) > filesize)
268
			continue;
269
270
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
271
272
		if (sh->sh_size == 0)
273
			continue;
274
275
		if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
276
			continue;
277
278
		if ((sh->sh_offset + sh->sh_size) > filesize)
279
			continue;
280
281
		switch (sh->sh_type) {
282
		case SHT_RELA:
283
			rela = (Elf_RelA *)(p + sh->sh_offset);
284
			for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
285
				rsym = ELF_R_SYM(rela[j].r_info);
286
				rtyp = ELF_R_TYPE(rela[j].r_info);
287
				roff = rela[j].r_offset;
288
				if (rsym >= nsymb)
289
					continue;
290
				if (roff >= filesize)
291
					continue;
292
				sym = &symtab[rsym];
293
				value = sym->st_value + rela[j].r_addend;
294
295
				rsize = elf_reloc_size(rtyp);
296
				if (rsize == -1 || roff + rsize >= ssz)
297
					continue;
298
299
				ELF_WRITE_RELOC(sdata + roff, value, rsize);
300
			}
301
			break;
302
		case SHT_REL:
303
			rel = (Elf_Rel *)(p + sh->sh_offset);
304
			for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
305
				rsym = ELF_R_SYM(rel[j].r_info);
306
				rtyp = ELF_R_TYPE(rel[j].r_info);
307
				roff = rel[j].r_offset;
308
				if (rsym >= nsymb)
309
					continue;
310
				if (roff >= filesize)
311
					continue;
312
				sym = &symtab[rsym];
313
				value = sym->st_value;
314
315
				rsize = elf_reloc_size(rtyp);
316
				if (rsize == -1 || roff + rsize >= ssz)
317
					continue;
318
319
				ELF_WRITE_RELOC(sdata + roff, value, rsize);
320
			}
321
			break;
322
		default:
323
			continue;
324
		}
325
	}
326
}