xref: /openbsd/usr.bin/ctfconv/elf.c (revision 0687c322)
1*0687c322Sjasper /*	$OpenBSD: elf.c,v 1.2 2017/08/11 14:58:56 jasper Exp $ */
2*0687c322Sjasper 
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 
19192095f7Smpi #include <sys/param.h>
20192095f7Smpi #include <sys/exec_elf.h>
21192095f7Smpi 
22192095f7Smpi #include <machine/reloc.h>
23192095f7Smpi 
24192095f7Smpi #include <assert.h>
25192095f7Smpi #include <err.h>
26192095f7Smpi #include <string.h>
27192095f7Smpi 
28192095f7Smpi static int	elf_reloc_size(unsigned long);
29192095f7Smpi static void	elf_reloc_apply(const char *, const char *, size_t, ssize_t,
30192095f7Smpi 		    char *, size_t);
31192095f7Smpi 
32192095f7Smpi int
33192095f7Smpi iself(const char *p, size_t filesize)
34192095f7Smpi {
35192095f7Smpi 	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
36192095f7Smpi 
37192095f7Smpi 	if (filesize < (off_t)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) {
58192095f7Smpi 		warnx("bogus section table offset 0x%llx", (off_t)eh->e_shoff);
59192095f7Smpi 		return 0;
60192095f7Smpi 	}
61192095f7Smpi 	if (eh->e_shentsize < sizeof(Elf_Shdr)) {
62192095f7Smpi 		warnx("bogus section header size %u", eh->e_shentsize);
63192095f7Smpi 		return 0;
64192095f7Smpi 	}
65192095f7Smpi 	if (eh->e_shnum > (filesize - eh->e_shoff) / eh->e_shentsize) {
66192095f7Smpi 		warnx("bogus section header count %u", eh->e_shnum);
67192095f7Smpi 		return 0;
68192095f7Smpi 	}
69192095f7Smpi 	if (eh->e_shstrndx >= eh->e_shnum) {
70192095f7Smpi 		warnx("bogus string table index %u", eh->e_shstrndx);
71192095f7Smpi 		return 0;
72192095f7Smpi 	}
73192095f7Smpi 
74192095f7Smpi 	return 1;
75192095f7Smpi }
76192095f7Smpi 
77192095f7Smpi int
78192095f7Smpi elf_getshstab(const char *p, size_t filesize, const char **shstab,
79192095f7Smpi     size_t *shstabsize)
80192095f7Smpi {
81192095f7Smpi 	Elf_Ehdr		*eh = (Elf_Ehdr *)p;
82192095f7Smpi 	Elf_Shdr		*sh;
83192095f7Smpi 
84192095f7Smpi 	sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize);
85192095f7Smpi 	if (sh->sh_type != SHT_STRTAB) {
86192095f7Smpi 		warnx("unexpected string table type");
87192095f7Smpi 		return -1;
88192095f7Smpi 	}
89192095f7Smpi 	if (sh->sh_offset > filesize) {
90192095f7Smpi 		warnx("bogus string table offset");
91192095f7Smpi 		return -1;
92192095f7Smpi 	}
93192095f7Smpi 	if (sh->sh_size > filesize - sh->sh_offset) {
94192095f7Smpi 		warnx("bogus string table size");
95192095f7Smpi 		return -1;
96192095f7Smpi 	}
97192095f7Smpi 	if (shstab != NULL)
98192095f7Smpi 		*shstab = p + sh->sh_offset;
99192095f7Smpi 	if (shstabsize != NULL)
100192095f7Smpi 		*shstabsize = sh->sh_size;
101192095f7Smpi 
102192095f7Smpi 	return 0;
103192095f7Smpi }
104192095f7Smpi 
105192095f7Smpi ssize_t
106192095f7Smpi elf_getsymtab(const char *p, const char *shstab, size_t shstabsz,
107192095f7Smpi     const Elf_Sym **symtab, size_t *nsymb)
108192095f7Smpi {
109192095f7Smpi 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
110192095f7Smpi 	Elf_Shdr	*sh;
111192095f7Smpi 	size_t		 snlen;
112192095f7Smpi 	ssize_t		 i;
113192095f7Smpi 
114192095f7Smpi 	snlen = strlen(ELF_SYMTAB);
115192095f7Smpi 
116192095f7Smpi 	for (i = 0; i < eh->e_shnum; i++) {
117192095f7Smpi 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
118192095f7Smpi 
119192095f7Smpi 		if (sh->sh_type != SHT_SYMTAB)
120192095f7Smpi 			continue;
121192095f7Smpi 
122192095f7Smpi 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
123192095f7Smpi 			continue;
124192095f7Smpi 
125192095f7Smpi 		if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
126192095f7Smpi 			if (symtab != NULL)
127192095f7Smpi 				*symtab = (Elf_Sym *)(p + sh->sh_offset);
128192095f7Smpi 			if (nsymb != NULL)
129192095f7Smpi 				*nsymb = (sh->sh_size / sh->sh_entsize);
130192095f7Smpi 
131192095f7Smpi 			return i;
132192095f7Smpi 		}
133192095f7Smpi 	}
134192095f7Smpi 
135192095f7Smpi 	return -1;
136192095f7Smpi }
137192095f7Smpi 
138192095f7Smpi ssize_t
139192095f7Smpi elf_getsection(char *p, const char *sname, const char *shstab,
140192095f7Smpi     size_t shstabsz, const char **psdata, size_t *pssz)
141192095f7Smpi {
142192095f7Smpi 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
143192095f7Smpi 	Elf_Shdr	*sh;
144192095f7Smpi 	char		*sdata = NULL;
145192095f7Smpi 	size_t		 snlen, ssz = 0;
146192095f7Smpi 	ssize_t		 sidx, i;
147192095f7Smpi 
148192095f7Smpi 	snlen = strlen(sname);
149192095f7Smpi 	if (snlen == 0)
150192095f7Smpi 		return -1;
151192095f7Smpi 
152192095f7Smpi 	/* Find the given section. */
153192095f7Smpi 	for (i = 0; i < eh->e_shnum; i++) {
154192095f7Smpi 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
155192095f7Smpi 
156192095f7Smpi 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
157192095f7Smpi 			continue;
158192095f7Smpi 
159192095f7Smpi 		if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
160192095f7Smpi 			sidx = i;
161192095f7Smpi 			sdata = p + sh->sh_offset;
162192095f7Smpi 			ssz = sh->sh_size;
163192095f7Smpi 			elf_reloc_apply(p, shstab, shstabsz, sidx, sdata, ssz);
164192095f7Smpi 			break;
165192095f7Smpi 		}
166192095f7Smpi 	}
167192095f7Smpi 
168192095f7Smpi 	if (sdata == NULL)
169192095f7Smpi 		return -1;
170192095f7Smpi 
171192095f7Smpi 	if (psdata != NULL)
172192095f7Smpi 		*psdata = sdata;
173192095f7Smpi 	if (pssz != NULL)
174192095f7Smpi 		*pssz = ssz;
175192095f7Smpi 
176192095f7Smpi 	return sidx;
177192095f7Smpi }
178192095f7Smpi 
179192095f7Smpi static int
180192095f7Smpi elf_reloc_size(unsigned long type)
181192095f7Smpi {
182192095f7Smpi 	switch (type) {
183192095f7Smpi #ifdef R_X86_64_64
184192095f7Smpi 	case R_X86_64_64:
185192095f7Smpi 		return sizeof(uint64_t);
186192095f7Smpi #endif
187192095f7Smpi #ifdef R_X86_64_32
188192095f7Smpi 	case R_X86_64_32:
189192095f7Smpi 		return sizeof(uint32_t);
190192095f7Smpi #endif
191192095f7Smpi #ifdef RELOC_32
192192095f7Smpi 	case RELOC_32:
193192095f7Smpi 		return sizeof(uint32_t);
194192095f7Smpi #endif
195192095f7Smpi 	default:
196192095f7Smpi 		break;
197192095f7Smpi 	}
198192095f7Smpi 
199192095f7Smpi 	return -1;
200192095f7Smpi }
201192095f7Smpi 
202192095f7Smpi #define ELF_WRITE_RELOC(buf, val, rsize)				\
203192095f7Smpi do {									\
204192095f7Smpi 	if (rsize == 4) {						\
205192095f7Smpi 		uint32_t v32 = val;					\
206192095f7Smpi 		memcpy(buf, &v32, sizeof(v32));				\
207192095f7Smpi 	} else {							\
208192095f7Smpi 		uint64_t v64 = val;					\
209192095f7Smpi 		memcpy(buf, &v64, sizeof(v64));				\
210192095f7Smpi 	}								\
211192095f7Smpi } while (0)
212192095f7Smpi 
213192095f7Smpi static void
214192095f7Smpi elf_reloc_apply(const char *p, const char *shstab, size_t shstabsz,
215192095f7Smpi     ssize_t sidx, char *sdata, size_t ssz)
216192095f7Smpi {
217192095f7Smpi 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
218192095f7Smpi 	Elf_Shdr	*sh;
219192095f7Smpi 	Elf_Rel		*rel = NULL;
220192095f7Smpi 	Elf_RelA	*rela = NULL;
221192095f7Smpi 	const Elf_Sym	*symtab, *sym;
222192095f7Smpi 	ssize_t		 symtabidx;
223192095f7Smpi 	size_t		 nsymb, rsym, rtyp, roff;
224192095f7Smpi 	size_t		 i, j;
225192095f7Smpi 	uint64_t	 value;
226192095f7Smpi 	int		 rsize;
227192095f7Smpi 
228192095f7Smpi 	/* Find symbol table location and number of symbols. */
229192095f7Smpi 	symtabidx = elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb);
230192095f7Smpi 	if (symtabidx == -1) {
231192095f7Smpi 		warnx("symbol table not found");
232192095f7Smpi 		return;
233192095f7Smpi 	}
234192095f7Smpi 
235192095f7Smpi 	/* Apply possible relocation. */
236192095f7Smpi 	for (i = 0; i < eh->e_shnum; i++) {
237192095f7Smpi 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
238192095f7Smpi 
239192095f7Smpi 		if (sh->sh_size == 0)
240192095f7Smpi 			continue;
241192095f7Smpi 
242192095f7Smpi 		if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
243192095f7Smpi 			continue;
244192095f7Smpi 
245192095f7Smpi 		switch (sh->sh_type) {
246192095f7Smpi 		case SHT_RELA:
247192095f7Smpi 			rela = (Elf_RelA *)(p + sh->sh_offset);
248192095f7Smpi 			for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
249192095f7Smpi 				rsym = ELF_R_SYM(rela[j].r_info);
250192095f7Smpi 				rtyp = ELF_R_TYPE(rela[j].r_info);
251192095f7Smpi 				roff = rela[j].r_offset;
252192095f7Smpi 				if (rsym >= nsymb)
253192095f7Smpi 					continue;
254192095f7Smpi 				sym = &symtab[rsym];
255192095f7Smpi 				value = sym->st_value + rela[j].r_addend;
256192095f7Smpi 
257192095f7Smpi 				rsize = elf_reloc_size(rtyp);
258192095f7Smpi 				if (rsize == -1 || roff + rsize >= ssz)
259192095f7Smpi 					continue;
260192095f7Smpi 
261192095f7Smpi 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
262192095f7Smpi 			}
263192095f7Smpi 			break;
264192095f7Smpi 		case SHT_REL:
265192095f7Smpi 			rel = (Elf_Rel *)(p + sh->sh_offset);
266192095f7Smpi 			for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
267192095f7Smpi 				rsym = ELF_R_SYM(rel[j].r_info);
268192095f7Smpi 				rtyp = ELF_R_TYPE(rel[j].r_info);
269192095f7Smpi 				roff = rel[j].r_offset;
270192095f7Smpi 				if (rsym >= nsymb)
271192095f7Smpi 					continue;
272192095f7Smpi 				sym = &symtab[rsym];
273192095f7Smpi 				value = sym->st_value;
274192095f7Smpi 
275192095f7Smpi 				rsize = elf_reloc_size(rtyp);
276192095f7Smpi 				if (rsize == -1 || roff + rsize >= ssz)
277192095f7Smpi 					continue;
278192095f7Smpi 
279192095f7Smpi 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
280192095f7Smpi 			}
281192095f7Smpi 			break;
282192095f7Smpi 		default:
283192095f7Smpi 			continue;
284192095f7Smpi 		}
285192095f7Smpi 	}
286192095f7Smpi }
287