xref: /openbsd/usr.bin/ctfconv/elf.c (revision 771fbea0)
1 /*	$OpenBSD: elf.c,v 1.9 2017/11/14 09:14:50 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 	size_t			 shoff;
84 
85 	shoff = eh->e_shoff + eh->e_shstrndx * eh->e_shentsize;
86 	if (shoff > (filesize - sizeof(*sh))) {
87 		warnx("unexpected string table size");
88 		return -1;
89 	}
90 
91 	sh = (Elf_Shdr *)(p + shoff);
92 	if (sh->sh_type != SHT_STRTAB) {
93 		warnx("unexpected string table type");
94 		return -1;
95 	}
96 	if (sh->sh_offset > filesize) {
97 		warnx("bogus string table offset");
98 		return -1;
99 	}
100 	if (sh->sh_size > filesize - sh->sh_offset) {
101 		warnx("bogus string table size");
102 		return -1;
103 	}
104 	if (shstab != NULL)
105 		*shstab = p + sh->sh_offset;
106 	if (shstabsize != NULL)
107 		*shstabsize = sh->sh_size;
108 
109 	return 0;
110 }
111 
112 ssize_t
113 elf_getsymtab(const char *p, size_t filesize, const char *shstab,
114     size_t shstabsz, const Elf_Sym **symtab, size_t *nsymb, const char **strtab,
115     size_t *strtabsz)
116 {
117 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
118 	Elf_Shdr	*sh, *symsh;
119 	size_t		 snlen, shoff;
120 	ssize_t		 i;
121 
122 	snlen = strlen(ELF_SYMTAB);
123 	symsh = NULL;
124 
125 	for (i = 0; i < eh->e_shnum; i++) {
126 		shoff = eh->e_shoff + i * eh->e_shentsize;
127 		if (shoff > (filesize - sizeof(*sh)))
128 			continue;
129 
130 		sh = (Elf_Shdr *)(p + shoff);
131 		if (sh->sh_type != SHT_SYMTAB)
132 			continue;
133 
134 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
135 			continue;
136 
137 		if (sh->sh_offset > filesize)
138 			continue;
139 
140 		if (sh->sh_size > (filesize - sh->sh_offset))
141 			continue;
142 
143 		if (sh->sh_entsize == 0)
144 			continue;
145 
146 		if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
147 			if (symtab != NULL)
148 				*symtab = (Elf_Sym *)(p + sh->sh_offset);
149 			if (nsymb != NULL)
150 				*nsymb = (sh->sh_size / sh->sh_entsize);
151 			symsh = sh;
152 
153 			break;
154 		}
155 	}
156 
157 	if (symsh == NULL || (symsh->sh_link >= eh->e_shnum))
158 		return -1;
159 
160 	shoff = eh->e_shoff + symsh->sh_link * eh->e_shentsize;
161 	if (shoff > (filesize - sizeof(*sh)))
162 		return -1;
163 
164 	sh = (Elf_Shdr *)(p + shoff);
165 	if ((sh->sh_offset + sh->sh_size) > filesize)
166 		return -1;
167 
168 	if (strtab != NULL)
169 		*strtab = p + sh->sh_offset;
170 	if (strtabsz != NULL)
171 		*strtabsz = sh->sh_size;
172 
173 	return i;
174 }
175 
176 ssize_t
177 elf_getsection(char *p, size_t filesize, const char *sname, const char *shstab,
178     size_t shstabsz, const char **psdata, size_t *pssz)
179 {
180 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
181 	Elf_Shdr	*sh;
182 	char		*sdata = NULL;
183 	size_t		 snlen, shoff, ssz = 0;
184 	ssize_t		 sidx, i;
185 
186 	snlen = strlen(sname);
187 	if (snlen == 0)
188 		return -1;
189 
190 	/* Find the given section. */
191 	for (i = 0; i < eh->e_shnum; i++) {
192 		shoff = eh->e_shoff + i * eh->e_shentsize;
193 		if (shoff > (filesize - sizeof(*sh)))
194 			continue;
195 
196 		sh = (Elf_Shdr *)(p + shoff);
197 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
198 			continue;
199 
200 		if (sh->sh_offset > filesize)
201 			continue;
202 
203 		if (sh->sh_size > (filesize - sh->sh_offset))
204 			continue;
205 
206 		if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
207 			sidx = i;
208 			sdata = p + sh->sh_offset;
209 			ssz = sh->sh_size;
210 			elf_reloc_apply(p, filesize, shstab, shstabsz, sidx,
211 			    sdata, ssz);
212 			break;
213 		}
214 	}
215 
216 	if (sdata == NULL)
217 		return -1;
218 
219 	if (psdata != NULL)
220 		*psdata = sdata;
221 	if (pssz != NULL)
222 		*pssz = ssz;
223 
224 	return sidx;
225 }
226 
227 static int
228 elf_reloc_size(unsigned long type)
229 {
230 	switch (type) {
231 #ifdef R_X86_64_64
232 	case R_X86_64_64:
233 		return sizeof(uint64_t);
234 #endif
235 #ifdef R_X86_64_32
236 	case R_X86_64_32:
237 		return sizeof(uint32_t);
238 #endif
239 #ifdef RELOC_32
240 	case RELOC_32:
241 		return sizeof(uint32_t);
242 #endif
243 	default:
244 		break;
245 	}
246 
247 	return -1;
248 }
249 
250 #define ELF_WRITE_RELOC(buf, val, rsize)				\
251 do {									\
252 	if (rsize == 4) {						\
253 		uint32_t v32 = val;					\
254 		memcpy(buf, &v32, sizeof(v32));				\
255 	} else {							\
256 		uint64_t v64 = val;					\
257 		memcpy(buf, &v64, sizeof(v64));				\
258 	}								\
259 } while (0)
260 
261 static void
262 elf_reloc_apply(const char *p, size_t filesize, const char *shstab,
263     size_t shstabsz, ssize_t sidx, char *sdata, size_t ssz)
264 {
265 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
266 	Elf_Shdr	*sh;
267 	Elf_Rel		*rel = NULL;
268 	Elf_RelA	*rela = NULL;
269 	const Elf_Sym	*symtab, *sym;
270 	ssize_t		 symtabidx;
271 	size_t		 nsymb, rsym, rtyp, roff;
272 	size_t		 shoff, i, j;
273 	uint64_t	 value;
274 	int		 rsize;
275 
276 	/* Find symbol table location and number of symbols. */
277 	symtabidx = elf_getsymtab(p, filesize, shstab, shstabsz, &symtab,
278 	    &nsymb, NULL, NULL);
279 	if (symtabidx == -1) {
280 		warnx("symbol table not found");
281 		return;
282 	}
283 
284 	/* Apply possible relocation. */
285 	for (i = 0; i < eh->e_shnum; i++) {
286 		shoff = eh->e_shoff + i * eh->e_shentsize;
287 		if (shoff > (filesize - sizeof(*sh)))
288 			continue;
289 
290 		sh = (Elf_Shdr *)(p + shoff);
291 		if (sh->sh_size == 0)
292 			continue;
293 
294 		if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
295 			continue;
296 
297 		if (sh->sh_offset > filesize)
298 			continue;
299 
300 		if (sh->sh_size > (filesize - sh->sh_offset))
301 			continue;
302 
303 		switch (sh->sh_type) {
304 		case SHT_RELA:
305 			rela = (Elf_RelA *)(p + sh->sh_offset);
306 			for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
307 				rsym = ELF_R_SYM(rela[j].r_info);
308 				rtyp = ELF_R_TYPE(rela[j].r_info);
309 				roff = rela[j].r_offset;
310 				if (rsym >= nsymb)
311 					continue;
312 				if (roff >= filesize)
313 					continue;
314 				sym = &symtab[rsym];
315 				value = sym->st_value + rela[j].r_addend;
316 
317 				rsize = elf_reloc_size(rtyp);
318 				if (rsize == -1 || roff + rsize >= ssz)
319 					continue;
320 
321 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
322 			}
323 			break;
324 		case SHT_REL:
325 			rel = (Elf_Rel *)(p + sh->sh_offset);
326 			for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
327 				rsym = ELF_R_SYM(rel[j].r_info);
328 				rtyp = ELF_R_TYPE(rel[j].r_info);
329 				roff = rel[j].r_offset;
330 				if (rsym >= nsymb)
331 					continue;
332 				if (roff >= filesize)
333 					continue;
334 				sym = &symtab[rsym];
335 				value = sym->st_value;
336 
337 				rsize = elf_reloc_size(rtyp);
338 				if (rsize == -1 || roff + rsize >= ssz)
339 					continue;
340 
341 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
342 			}
343 			break;
344 		default:
345 			continue;
346 		}
347 	}
348 }
349