xref: /openbsd-src/usr.bin/ctfconv/elf.c (revision c0dd97bfcad3dab6c31ec12b9de1274fd2d2f993)
1 /*	$OpenBSD: elf.c,v 1.7 2017/10/27 08:33:46 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 
84 	sh = (Elf_Shdr *)(p + eh->e_shoff + eh->e_shstrndx * eh->e_shentsize);
85 	if (sh->sh_type != SHT_STRTAB) {
86 		warnx("unexpected string table type");
87 		return -1;
88 	}
89 	if (sh->sh_offset > filesize) {
90 		warnx("bogus string table offset");
91 		return -1;
92 	}
93 	if (sh->sh_size > filesize - sh->sh_offset) {
94 		warnx("bogus string table size");
95 		return -1;
96 	}
97 	if (shstab != NULL)
98 		*shstab = p + sh->sh_offset;
99 	if (shstabsize != NULL)
100 		*shstabsize = sh->sh_size;
101 
102 	return 0;
103 }
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 	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 	for (i = 0; i < eh->e_shnum; i++) {
117 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
118 
119 		if (sh->sh_type != SHT_SYMTAB)
120 			continue;
121 
122 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
123 			continue;
124 
125 		if ((sh->sh_offset + sh->sh_size) > filesize)
126 			continue;
127 
128 		if (sh->sh_entsize == 0)
129 			continue;
130 
131 		if (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
132 			if (symtab != NULL)
133 				*symtab = (Elf_Sym *)(p + sh->sh_offset);
134 			if (nsymb != NULL)
135 				*nsymb = (sh->sh_size / sh->sh_entsize);
136 
137 			return i;
138 		}
139 	}
140 
141 	return -1;
142 }
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 	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 	snlen = strlen(sname);
155 	if (snlen == 0)
156 		return -1;
157 
158 	/* Find the given section. */
159 	for (i = 0; i < eh->e_shnum; i++) {
160 		if ((eh->e_shoff + i * eh->e_shentsize) > filesize)
161 			continue;
162 
163 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
164 
165 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
166 			continue;
167 
168 		if ((sh->sh_offset + sh->sh_size) > filesize)
169 			continue;
170 
171 		if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
172 			sidx = i;
173 			sdata = p + sh->sh_offset;
174 			ssz = sh->sh_size;
175 			elf_reloc_apply(p, filesize, shstab, shstabsz, sidx,
176 			    sdata, ssz);
177 			break;
178 		}
179 	}
180 
181 	if (sdata == NULL)
182 		return -1;
183 
184 	if (psdata != NULL)
185 		*psdata = sdata;
186 	if (pssz != NULL)
187 		*pssz = ssz;
188 
189 	return sidx;
190 }
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 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
231 	Elf_Shdr	*sh;
232 	Elf_Rel		*rel = NULL;
233 	Elf_RelA	*rela = NULL;
234 	const Elf_Sym	*symtab, *sym;
235 	ssize_t		 symtabidx;
236 	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 	symtabidx = elf_getsymtab(p, filesize, shstab, shstabsz, &symtab,
243 	    &nsymb);
244 	if (symtabidx == -1) {
245 		warnx("symbol table not found");
246 		return;
247 	}
248 
249 	/* Apply possible relocation. */
250 	for (i = 0; i < eh->e_shnum; i++) {
251 		if ((eh->e_shoff + i * eh->e_shentsize) > filesize)
252 			continue;
253 
254 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
255 
256 		if (sh->sh_size == 0)
257 			continue;
258 
259 		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 }
311