GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ctfdump/elf.c Lines: 63 118 53.4 %
Date: 2017-11-07 Branches: 41 102 40.2 %

Line Branch Exec Source
1
/*	$OpenBSD: elf.c,v 1.3 2017/08/29 21:10:20 deraadt 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
#include <sys/exec_elf.h>
21
22
#include <machine/reloc.h>
23
24
#include <assert.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 *, const char *, size_t, ssize_t,
30
		    char *, size_t);
31
32
int
33
iself(const char *p, size_t filesize)
34
{
35
4
	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
36
37
2
	if (filesize < (off_t)sizeof(Elf_Ehdr)) {
38
		warnx("file too small to be ELF");
39
		return 0;
40
	}
41
42


10
	if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh))
43
		return 0;
44
45
2
	if (eh->e_ident[EI_CLASS] != ELFCLASS) {
46
		warnx("unexpected word size %u", eh->e_ident[EI_CLASS]);
47
		return 0;
48
	}
49
2
	if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) {
50
		warnx("unexpected version %u", eh->e_ident[EI_VERSION]);
51
		return 0;
52
	}
53
2
	if (eh->e_ident[EI_DATA] >= ELFDATANUM) {
54
		warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
55
		return 0;
56
	}
57
2
	if (eh->e_shoff > filesize) {
58
		warnx("bogus section table offset 0x%llx", (off_t)eh->e_shoff);
59
		return 0;
60
	}
61
2
	if (eh->e_shentsize < sizeof(Elf_Shdr)) {
62
		warnx("bogus section header size %u", eh->e_shentsize);
63
		return 0;
64
	}
65
2
	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
2
	if (eh->e_shstrndx >= eh->e_shnum) {
70
		warnx("bogus string table index %u", eh->e_shstrndx);
71
		return 0;
72
	}
73
74
2
	return 1;
75
2
}
76
77
int
78
elf_getshstab(const char *p, size_t filesize, const char **shstab,
79
    size_t *shstabsize)
80
{
81
4
	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
82
	Elf_Shdr		*sh;
83
84
2
	sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize);
85
2
	if (sh->sh_type != SHT_STRTAB) {
86
		warnx("unexpected string table type");
87
		return -1;
88
	}
89
2
	if (sh->sh_offset > filesize) {
90
		warnx("bogus string table offset");
91
		return -1;
92
	}
93
2
	if (sh->sh_size > filesize - sh->sh_offset) {
94
		warnx("bogus string table size");
95
		return -1;
96
	}
97
2
	if (shstab != NULL)
98
2
		*shstab = p + sh->sh_offset;
99
2
	if (shstabsize != NULL)
100
2
		*shstabsize = sh->sh_size;
101
102
2
	return 0;
103
2
}
104
105
ssize_t
106
elf_getsymtab(const char *p, const char *shstab, size_t shstabsz,
107
    const Elf_Sym **symtab, size_t *nsymb)
108
{
109
8
	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
110
	Elf_Shdr	*sh;
111
	size_t		 snlen;
112
	ssize_t		 i;
113
114
	snlen = strlen(ELF_SYMTAB);
115
116
256
	for (i = 0; i < eh->e_shnum; i++) {
117
128
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
118
119
128
		if (sh->sh_type != SHT_SYMTAB)
120
			continue;
121
122

8
		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
123
			continue;
124
125
4
		if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
126
4
			if (symtab != NULL)
127
4
				*symtab = (Elf_Sym *)(p + sh->sh_offset);
128
4
			if (nsymb != NULL)
129
4
				*nsymb = (sh->sh_size / sh->sh_entsize);
130
131
4
			return i;
132
		}
133
	}
134
135
	return -1;
136
4
}
137
138
ssize_t
139
elf_getsection(char *p, const char *sname, const char *shstab,
140
    size_t shstabsz, const char **psdata, size_t *pssz)
141
{
142
4
	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
143
	Elf_Shdr	*sh;
144
	char		*sdata = NULL;
145
	size_t		 snlen, ssz = 0;
146
	ssize_t		 sidx, i;
147
148
2
	snlen = strlen(sname);
149
2
	if (snlen == 0)
150
		return -1;
151
152
	/* Find the given section. */
153
132
	for (i = 0; i < eh->e_shnum; i++) {
154
66
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
155
156

132
		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
157
			continue;
158
159
66
		if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
160
			sidx = i;
161
2
			sdata = p + sh->sh_offset;
162
2
			ssz = sh->sh_size;
163
2
			elf_reloc_apply(p, shstab, shstabsz, sidx, sdata, ssz);
164
2
			break;
165
		}
166
	}
167
168
2
	if (sdata == NULL)
169
		return -1;
170
171
2
	if (psdata != NULL)
172
2
		*psdata = sdata;
173
2
	if (pssz != NULL)
174
2
		*pssz = ssz;
175
176
2
	return sidx;
177
2
}
178
179
static int
180
elf_reloc_size(unsigned long type)
181
{
182
	switch (type) {
183
#ifdef R_X86_64_64
184
	case R_X86_64_64:
185
		return sizeof(uint64_t);
186
#endif
187
#ifdef R_X86_64_32
188
	case R_X86_64_32:
189
		return sizeof(uint32_t);
190
#endif
191
#ifdef RELOC_32
192
	case RELOC_32:
193
		return sizeof(uint32_t);
194
#endif
195
	default:
196
		break;
197
	}
198
199
	return -1;
200
}
201
202
#define ELF_WRITE_RELOC(buf, val, rsize)				\
203
do {									\
204
	if (rsize == 4) {						\
205
		uint32_t v32 = val;					\
206
		memcpy(buf, &v32, sizeof(v32));				\
207
	} else {							\
208
		uint64_t v64 = val;					\
209
		memcpy(buf, &v64, sizeof(v64));				\
210
	}								\
211
} while (0)
212
213
static void
214
elf_reloc_apply(const char *p, const char *shstab, size_t shstabsz,
215
    ssize_t sidx, char *sdata, size_t ssz)
216
{
217
4
	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
218
	Elf_Shdr	*sh;
219
	Elf_Rel		*rel = NULL;
220
	Elf_RelA	*rela = NULL;
221
2
	const Elf_Sym	*symtab, *sym;
222
	ssize_t		 symtabidx;
223
2
	size_t		 nsymb, rsym, rtyp, roff;
224
	size_t		 i, j;
225
	uint64_t	 value;
226
	int		 rsize;
227
228
	/* Find symbol table location and number of symbols. */
229
2
	symtabidx = elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb);
230
2
	if (symtabidx == -1) {
231
		warnx("symbol table not found");
232
		return;
233
	}
234
235
	/* Apply possible relocation. */
236
136
	for (i = 0; i < eh->e_shnum; i++) {
237
66
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
238
239
66
		if (sh->sh_size == 0)
240
			continue;
241
242

64
		if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
243
			continue;
244
245
		switch (sh->sh_type) {
246
		case SHT_RELA:
247
			rela = (Elf_RelA *)(p + sh->sh_offset);
248
			for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
249
				rsym = ELF_R_SYM(rela[j].r_info);
250
				rtyp = ELF_R_TYPE(rela[j].r_info);
251
				roff = rela[j].r_offset;
252
				if (rsym >= nsymb)
253
					continue;
254
				sym = &symtab[rsym];
255
				value = sym->st_value + rela[j].r_addend;
256
257
				rsize = elf_reloc_size(rtyp);
258
				if (rsize == -1 || roff + rsize >= ssz)
259
					continue;
260
261
				ELF_WRITE_RELOC(sdata + roff, value, rsize);
262
			}
263
			break;
264
		case SHT_REL:
265
			rel = (Elf_Rel *)(p + sh->sh_offset);
266
			for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
267
				rsym = ELF_R_SYM(rel[j].r_info);
268
				rtyp = ELF_R_TYPE(rel[j].r_info);
269
				roff = rel[j].r_offset;
270
				if (rsym >= nsymb)
271
					continue;
272
				sym = &symtab[rsym];
273
				value = sym->st_value;
274
275
				rsize = elf_reloc_size(rtyp);
276
				if (rsize == -1 || roff + rsize >= ssz)
277
					continue;
278
279
				ELF_WRITE_RELOC(sdata + roff, value, rsize);
280
			}
281
			break;
282
		default:
283
			continue;
284
		}
285
	}
286
4
}