xref: /openbsd/usr.bin/ctfconv/elf.c (revision 8d2c6414)
1*8d2c6414Smillert /*	$OpenBSD: elf.c,v 1.10 2022/08/14 14:54:13 millert Exp $ */
20687c322Sjasper 
3192095f7Smpi /*
4192095f7Smpi  * Copyright (c) 2016 Martin Pieuchot <mpi@openbsd.org>
5192095f7Smpi  *
6192095f7Smpi  * Permission to use, copy, modify, and distribute this software for any
7192095f7Smpi  * purpose with or without fee is hereby granted, provided that the above
8192095f7Smpi  * copyright notice and this permission notice appear in all copies.
9192095f7Smpi  *
10192095f7Smpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11192095f7Smpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12192095f7Smpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13192095f7Smpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14192095f7Smpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15192095f7Smpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16192095f7Smpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17192095f7Smpi  */
18192095f7Smpi 
192c999465Sderaadt #include <sys/types.h>
20192095f7Smpi 
21192095f7Smpi #include <machine/reloc.h>
22192095f7Smpi 
23192095f7Smpi #include <assert.h>
24e4b342e5Smpi #include <elf.h>
25192095f7Smpi #include <err.h>
26192095f7Smpi #include <string.h>
27192095f7Smpi 
28192095f7Smpi static int	elf_reloc_size(unsigned long);
29be9aadfaSjsg static void	elf_reloc_apply(const char *, size_t, const char *, size_t,
30be9aadfaSjsg 		    ssize_t, char *, size_t);
31192095f7Smpi 
32192095f7Smpi int
iself(const char * p,size_t filesize)33192095f7Smpi iself(const char *p, size_t filesize)
34192095f7Smpi {
35192095f7Smpi 	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
36192095f7Smpi 
37*8d2c6414Smillert 	if (filesize < sizeof(Elf_Ehdr)) {
38192095f7Smpi 		warnx("file too small to be ELF");
39192095f7Smpi 		return 0;
40192095f7Smpi 	}
41192095f7Smpi 
42192095f7Smpi 	if (eh->e_ehsize < sizeof(Elf_Ehdr) || !IS_ELF(*eh))
43192095f7Smpi 		return 0;
44192095f7Smpi 
45192095f7Smpi 	if (eh->e_ident[EI_CLASS] != ELFCLASS) {
46192095f7Smpi 		warnx("unexpected word size %u", eh->e_ident[EI_CLASS]);
47192095f7Smpi 		return 0;
48192095f7Smpi 	}
49192095f7Smpi 	if (eh->e_ident[EI_VERSION] != ELF_TARG_VER) {
50192095f7Smpi 		warnx("unexpected version %u", eh->e_ident[EI_VERSION]);
51192095f7Smpi 		return 0;
52192095f7Smpi 	}
53192095f7Smpi 	if (eh->e_ident[EI_DATA] >= ELFDATANUM) {
54192095f7Smpi 		warnx("unexpected data format %u", eh->e_ident[EI_DATA]);
55192095f7Smpi 		return 0;
56192095f7Smpi 	}
57192095f7Smpi 	if (eh->e_shoff > filesize) {
58*8d2c6414Smillert 		warnx("bogus section table offset 0x%llx",
59*8d2c6414Smillert 		    (unsigned long long)eh->e_shoff);
60192095f7Smpi 		return 0;
61192095f7Smpi 	}
62192095f7Smpi 	if (eh->e_shentsize < sizeof(Elf_Shdr)) {
63192095f7Smpi 		warnx("bogus section header size %u", eh->e_shentsize);
64192095f7Smpi 		return 0;
65192095f7Smpi 	}
66192095f7Smpi 	if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) {
67192095f7Smpi 		warnx("bogus section header count %u", eh->e_shnum);
68192095f7Smpi 		return 0;
69192095f7Smpi 	}
70192095f7Smpi 	if (eh->e_shstrndx >= eh->e_shnum) {
71192095f7Smpi 		warnx("bogus string table index %u", eh->e_shstrndx);
72192095f7Smpi 		return 0;
73192095f7Smpi 	}
74192095f7Smpi 
75192095f7Smpi 	return 1;
76192095f7Smpi }
77192095f7Smpi 
78192095f7Smpi int
elf_getshstab(const char * p,size_t filesize,const char ** shstab,size_t * shstabsize)79192095f7Smpi elf_getshstab(const char *p, size_t filesize, const char **shstab,
80192095f7Smpi     size_t *shstabsize)
81192095f7Smpi {
82192095f7Smpi 	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
83192095f7Smpi 	Elf_Shdr		*sh;
8473fdf5ceSmpi 	size_t			 shoff;
85192095f7Smpi 
8673fdf5ceSmpi 	shoff = eh->e_shoff + eh->e_shstrndx * eh->e_shentsize;
8773fdf5ceSmpi 	if (shoff > (filesize - sizeof(*sh))) {
8873fdf5ceSmpi 		warnx("unexpected string table size");
8973fdf5ceSmpi 		return -1;
9073fdf5ceSmpi 	}
9173fdf5ceSmpi 
9273fdf5ceSmpi 	sh = (Elf_Shdr *)(p + shoff);
93192095f7Smpi 	if (sh->sh_type != SHT_STRTAB) {
94192095f7Smpi 		warnx("unexpected string table type");
95192095f7Smpi 		return -1;
96192095f7Smpi 	}
97192095f7Smpi 	if (sh->sh_offset > filesize) {
98192095f7Smpi 		warnx("bogus string table offset");
99192095f7Smpi 		return -1;
100192095f7Smpi 	}
101192095f7Smpi 	if (sh->sh_size > filesize - sh->sh_offset) {
102192095f7Smpi 		warnx("bogus string table size");
103192095f7Smpi 		return -1;
104192095f7Smpi 	}
105192095f7Smpi 	if (shstab != NULL)
106192095f7Smpi 		*shstab = p + sh->sh_offset;
107192095f7Smpi 	if (shstabsize != NULL)
108192095f7Smpi 		*shstabsize = sh->sh_size;
109192095f7Smpi 
110192095f7Smpi 	return 0;
111192095f7Smpi }
112192095f7Smpi 
113192095f7Smpi ssize_t
elf_getsymtab(const char * p,size_t filesize,const char * shstab,size_t shstabsz,const Elf_Sym ** symtab,size_t * nsymb,const char ** strtab,size_t * strtabsz)114be9aadfaSjsg elf_getsymtab(const char *p, size_t filesize, const char *shstab,
115622b7392Smpi     size_t shstabsz, const Elf_Sym **symtab, size_t *nsymb, const char **strtab,
116622b7392Smpi     size_t *strtabsz)
117192095f7Smpi {
118192095f7Smpi 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
119622b7392Smpi 	Elf_Shdr	*sh, *symsh;
12073fdf5ceSmpi 	size_t		 snlen, shoff;
121192095f7Smpi 	ssize_t		 i;
122192095f7Smpi 
123192095f7Smpi 	snlen = strlen(ELF_SYMTAB);
124622b7392Smpi 	symsh = NULL;
125192095f7Smpi 
126192095f7Smpi 	for (i = 0; i < eh->e_shnum; i++) {
12773fdf5ceSmpi 		shoff = eh->e_shoff + i * eh->e_shentsize;
12873fdf5ceSmpi 		if (shoff > (filesize - sizeof(*sh)))
12973fdf5ceSmpi 			continue;
130192095f7Smpi 
13173fdf5ceSmpi 		sh = (Elf_Shdr *)(p + shoff);
132192095f7Smpi 		if (sh->sh_type != SHT_SYMTAB)
133192095f7Smpi 			continue;
134192095f7Smpi 
135192095f7Smpi 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
136192095f7Smpi 			continue;
137192095f7Smpi 
13873fdf5ceSmpi 		if (sh->sh_offset > filesize)
13973fdf5ceSmpi 			continue;
14073fdf5ceSmpi 
14173fdf5ceSmpi 		if (sh->sh_size > (filesize - sh->sh_offset))
142be9aadfaSjsg 			continue;
143be9aadfaSjsg 
144f613d021Sjsg 		if (sh->sh_entsize == 0)
145f613d021Sjsg 			continue;
146f613d021Sjsg 
147192095f7Smpi 		if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
148192095f7Smpi 			if (symtab != NULL)
149192095f7Smpi 				*symtab = (Elf_Sym *)(p + sh->sh_offset);
150192095f7Smpi 			if (nsymb != NULL)
151192095f7Smpi 				*nsymb = (sh->sh_size / sh->sh_entsize);
152622b7392Smpi 			symsh = sh;
153622b7392Smpi 
154622b7392Smpi 			break;
155622b7392Smpi 		}
156622b7392Smpi 	}
157622b7392Smpi 
158622b7392Smpi 	if (symsh == NULL || (symsh->sh_link >= eh->e_shnum))
159622b7392Smpi 		return -1;
160622b7392Smpi 
16173fdf5ceSmpi 	shoff = eh->e_shoff + symsh->sh_link * eh->e_shentsize;
16273fdf5ceSmpi 	if (shoff > (filesize - sizeof(*sh)))
16373fdf5ceSmpi 		return -1;
164622b7392Smpi 
16573fdf5ceSmpi 	sh = (Elf_Shdr *)(p + shoff);
166622b7392Smpi 	if ((sh->sh_offset + sh->sh_size) > filesize)
167622b7392Smpi 		return -1;
168622b7392Smpi 
169622b7392Smpi 	if (strtab != NULL)
170622b7392Smpi 		*strtab = p + sh->sh_offset;
171622b7392Smpi 	if (strtabsz != NULL)
172622b7392Smpi 		*strtabsz = sh->sh_size;
173192095f7Smpi 
174192095f7Smpi 	return i;
175192095f7Smpi }
176192095f7Smpi 
177192095f7Smpi ssize_t
elf_getsection(char * p,size_t filesize,const char * sname,const char * shstab,size_t shstabsz,const char ** psdata,size_t * pssz)17859153d10Sjsg elf_getsection(char *p, size_t filesize, const char *sname, const char *shstab,
179192095f7Smpi     size_t shstabsz, const char **psdata, size_t *pssz)
180192095f7Smpi {
181192095f7Smpi 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
182192095f7Smpi 	Elf_Shdr	*sh;
183192095f7Smpi 	char		*sdata = NULL;
18473fdf5ceSmpi 	size_t		 snlen, shoff, ssz = 0;
185192095f7Smpi 	ssize_t		 sidx, i;
186192095f7Smpi 
187192095f7Smpi 	snlen = strlen(sname);
188192095f7Smpi 	if (snlen == 0)
189192095f7Smpi 		return -1;
190192095f7Smpi 
191192095f7Smpi 	/* Find the given section. */
192192095f7Smpi 	for (i = 0; i < eh->e_shnum; i++) {
19373fdf5ceSmpi 		shoff = eh->e_shoff + i * eh->e_shentsize;
19473fdf5ceSmpi 		if (shoff > (filesize - sizeof(*sh)))
195f613d021Sjsg 			continue;
196f613d021Sjsg 
19773fdf5ceSmpi 		sh = (Elf_Shdr *)(p + shoff);
198192095f7Smpi 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
199192095f7Smpi 			continue;
200192095f7Smpi 
20173fdf5ceSmpi 		if (sh->sh_offset > filesize)
20273fdf5ceSmpi 			continue;
20373fdf5ceSmpi 
20473fdf5ceSmpi 		if (sh->sh_size > (filesize - sh->sh_offset))
20559153d10Sjsg 			continue;
20659153d10Sjsg 
207192095f7Smpi 		if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
208192095f7Smpi 			sidx = i;
209192095f7Smpi 			sdata = p + sh->sh_offset;
210192095f7Smpi 			ssz = sh->sh_size;
211be9aadfaSjsg 			elf_reloc_apply(p, filesize, shstab, shstabsz, sidx,
212be9aadfaSjsg 			    sdata, ssz);
213192095f7Smpi 			break;
214192095f7Smpi 		}
215192095f7Smpi 	}
216192095f7Smpi 
217192095f7Smpi 	if (sdata == NULL)
218192095f7Smpi 		return -1;
219192095f7Smpi 
220192095f7Smpi 	if (psdata != NULL)
221192095f7Smpi 		*psdata = sdata;
222192095f7Smpi 	if (pssz != NULL)
223192095f7Smpi 		*pssz = ssz;
224192095f7Smpi 
225192095f7Smpi 	return sidx;
226192095f7Smpi }
227192095f7Smpi 
228192095f7Smpi static int
elf_reloc_size(unsigned long type)229192095f7Smpi elf_reloc_size(unsigned long type)
230192095f7Smpi {
231192095f7Smpi 	switch (type) {
232192095f7Smpi #ifdef R_X86_64_64
233192095f7Smpi 	case R_X86_64_64:
234192095f7Smpi 		return sizeof(uint64_t);
235192095f7Smpi #endif
236192095f7Smpi #ifdef R_X86_64_32
237192095f7Smpi 	case R_X86_64_32:
238192095f7Smpi 		return sizeof(uint32_t);
239192095f7Smpi #endif
240192095f7Smpi #ifdef RELOC_32
241192095f7Smpi 	case RELOC_32:
242192095f7Smpi 		return sizeof(uint32_t);
243192095f7Smpi #endif
244192095f7Smpi 	default:
245192095f7Smpi 		break;
246192095f7Smpi 	}
247192095f7Smpi 
248192095f7Smpi 	return -1;
249192095f7Smpi }
250192095f7Smpi 
251192095f7Smpi #define ELF_WRITE_RELOC(buf, val, rsize)				\
252192095f7Smpi do {									\
253192095f7Smpi 	if (rsize == 4) {						\
254192095f7Smpi 		uint32_t v32 = val;					\
255192095f7Smpi 		memcpy(buf, &v32, sizeof(v32));				\
256192095f7Smpi 	} else {							\
257192095f7Smpi 		uint64_t v64 = val;					\
258192095f7Smpi 		memcpy(buf, &v64, sizeof(v64));				\
259192095f7Smpi 	}								\
260192095f7Smpi } while (0)
261192095f7Smpi 
262192095f7Smpi static void
elf_reloc_apply(const char * p,size_t filesize,const char * shstab,size_t shstabsz,ssize_t sidx,char * sdata,size_t ssz)263be9aadfaSjsg elf_reloc_apply(const char *p, size_t filesize, const char *shstab,
264be9aadfaSjsg     size_t shstabsz, ssize_t sidx, char *sdata, size_t ssz)
265192095f7Smpi {
266192095f7Smpi 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
267192095f7Smpi 	Elf_Shdr	*sh;
268192095f7Smpi 	Elf_Rel		*rel = NULL;
269192095f7Smpi 	Elf_RelA	*rela = NULL;
270192095f7Smpi 	const Elf_Sym	*symtab, *sym;
271192095f7Smpi 	ssize_t		 symtabidx;
272192095f7Smpi 	size_t		 nsymb, rsym, rtyp, roff;
27373fdf5ceSmpi 	size_t		 shoff, i, j;
274192095f7Smpi 	uint64_t	 value;
275192095f7Smpi 	int		 rsize;
276192095f7Smpi 
277192095f7Smpi 	/* Find symbol table location and number of symbols. */
278be9aadfaSjsg 	symtabidx = elf_getsymtab(p, filesize, shstab, shstabsz, &symtab,
279622b7392Smpi 	    &nsymb, NULL, NULL);
280192095f7Smpi 	if (symtabidx == -1) {
281192095f7Smpi 		warnx("symbol table not found");
282192095f7Smpi 		return;
283192095f7Smpi 	}
284192095f7Smpi 
285192095f7Smpi 	/* Apply possible relocation. */
286192095f7Smpi 	for (i = 0; i < eh->e_shnum; i++) {
28773fdf5ceSmpi 		shoff = eh->e_shoff + i * eh->e_shentsize;
28873fdf5ceSmpi 		if (shoff > (filesize - sizeof(*sh)))
289f613d021Sjsg 			continue;
290f613d021Sjsg 
29173fdf5ceSmpi 		sh = (Elf_Shdr *)(p + shoff);
292192095f7Smpi 		if (sh->sh_size == 0)
293192095f7Smpi 			continue;
294192095f7Smpi 
295192095f7Smpi 		if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
296192095f7Smpi 			continue;
297192095f7Smpi 
29873fdf5ceSmpi 		if (sh->sh_offset > filesize)
29973fdf5ceSmpi 			continue;
30073fdf5ceSmpi 
30173fdf5ceSmpi 		if (sh->sh_size > (filesize - sh->sh_offset))
302f613d021Sjsg 			continue;
303f613d021Sjsg 
304192095f7Smpi 		switch (sh->sh_type) {
305192095f7Smpi 		case SHT_RELA:
306192095f7Smpi 			rela = (Elf_RelA *)(p + sh->sh_offset);
307192095f7Smpi 			for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
308192095f7Smpi 				rsym = ELF_R_SYM(rela[j].r_info);
309192095f7Smpi 				rtyp = ELF_R_TYPE(rela[j].r_info);
310192095f7Smpi 				roff = rela[j].r_offset;
311192095f7Smpi 				if (rsym >= nsymb)
312192095f7Smpi 					continue;
313f613d021Sjsg 				if (roff >= filesize)
314f613d021Sjsg 					continue;
315192095f7Smpi 				sym = &symtab[rsym];
316192095f7Smpi 				value = sym->st_value + rela[j].r_addend;
317192095f7Smpi 
318192095f7Smpi 				rsize = elf_reloc_size(rtyp);
319192095f7Smpi 				if (rsize == -1 || roff + rsize >= ssz)
320192095f7Smpi 					continue;
321192095f7Smpi 
322192095f7Smpi 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
323192095f7Smpi 			}
324192095f7Smpi 			break;
325192095f7Smpi 		case SHT_REL:
326192095f7Smpi 			rel = (Elf_Rel *)(p + sh->sh_offset);
327192095f7Smpi 			for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
328192095f7Smpi 				rsym = ELF_R_SYM(rel[j].r_info);
329192095f7Smpi 				rtyp = ELF_R_TYPE(rel[j].r_info);
330192095f7Smpi 				roff = rel[j].r_offset;
331192095f7Smpi 				if (rsym >= nsymb)
332192095f7Smpi 					continue;
333f613d021Sjsg 				if (roff >= filesize)
334f613d021Sjsg 					continue;
335192095f7Smpi 				sym = &symtab[rsym];
336192095f7Smpi 				value = sym->st_value;
337192095f7Smpi 
338192095f7Smpi 				rsize = elf_reloc_size(rtyp);
339192095f7Smpi 				if (rsize == -1 || roff + rsize >= ssz)
340192095f7Smpi 					continue;
341192095f7Smpi 
342192095f7Smpi 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
343192095f7Smpi 			}
344192095f7Smpi 			break;
345192095f7Smpi 		default:
346192095f7Smpi 			continue;
347192095f7Smpi 		}
348192095f7Smpi 	}
349192095f7Smpi }
350