GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/ctfconv/elf.c Lines: 68 126 54.0 %
Date: 2017-11-07 Branches: 46 118 39.0 %

Line Branch Exec Source
1
/*	$OpenBSD: elf.c,v 1.6 2017/09/30 10:15:59 jsg 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 *, 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
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, size_t filesize, const char *shstab,
107
    size_t shstabsz, const Elf_Sym **symtab, size_t *nsymb)
108
{
109
20
	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
620
	for (i = 0; i < eh->e_shnum; i++) {
117
310
		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
118
119
310
		if (sh->sh_type != SHT_SYMTAB)
120
			continue;
121
122

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

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

248
		if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
260
			continue;
261
262
		if ((sh->sh_offset + sh->sh_size) > filesize)
263
			continue;
264
265
		switch (sh->sh_type) {
266
		case SHT_RELA:
267
			rela = (Elf_RelA *)(p + sh->sh_offset);
268
			for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
269
				rsym = ELF_R_SYM(rela[j].r_info);
270
				rtyp = ELF_R_TYPE(rela[j].r_info);
271
				roff = rela[j].r_offset;
272
				if (rsym >= nsymb)
273
					continue;
274
				if (roff >= filesize)
275
					continue;
276
				sym = &symtab[rsym];
277
				value = sym->st_value + rela[j].r_addend;
278
279
				rsize = elf_reloc_size(rtyp);
280
				if (rsize == -1 || roff + rsize >= ssz)
281
					continue;
282
283
				ELF_WRITE_RELOC(sdata + roff, value, rsize);
284
			}
285
			break;
286
		case SHT_REL:
287
			rel = (Elf_Rel *)(p + sh->sh_offset);
288
			for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
289
				rsym = ELF_R_SYM(rel[j].r_info);
290
				rtyp = ELF_R_TYPE(rel[j].r_info);
291
				roff = rel[j].r_offset;
292
				if (rsym >= nsymb)
293
					continue;
294
				if (roff >= filesize)
295
					continue;
296
				sym = &symtab[rsym];
297
				value = sym->st_value;
298
299
				rsize = elf_reloc_size(rtyp);
300
				if (rsize == -1 || roff + rsize >= ssz)
301
					continue;
302
303
				ELF_WRITE_RELOC(sdata + roff, value, rsize);
304
			}
305
			break;
306
		default:
307
			continue;
308
		}
309
	}
310
16
}