xref: /openbsd-src/usr.bin/ctfconv/elf.c (revision c7e8ea31cd41a963f06f0a8ba93948b06aa6b4a4)
1 /*	$OpenBSD: elf.c,v 1.2 2017/08/11 14:58:56 jasper 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/param.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 *, const char *, size_t, ssize_t,
30 		    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, const char *shstab, size_t shstabsz,
107     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 (strncmp(shstab + sh->sh_name, ELF_SYMTAB, snlen) == 0) {
126 			if (symtab != NULL)
127 				*symtab = (Elf_Sym *)(p + sh->sh_offset);
128 			if (nsymb != NULL)
129 				*nsymb = (sh->sh_size / sh->sh_entsize);
130 
131 			return i;
132 		}
133 	}
134 
135 	return -1;
136 }
137 
138 ssize_t
139 elf_getsection(char *p, const char *sname, const char *shstab,
140     size_t shstabsz, const char **psdata, size_t *pssz)
141 {
142 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
143 	Elf_Shdr	*sh;
144 	char		*sdata = NULL;
145 	size_t		 snlen, ssz = 0;
146 	ssize_t		 sidx, i;
147 
148 	snlen = strlen(sname);
149 	if (snlen == 0)
150 		return -1;
151 
152 	/* Find the given section. */
153 	for (i = 0; i < eh->e_shnum; i++) {
154 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
155 
156 		if ((sh->sh_link >= eh->e_shnum) || (sh->sh_name >= shstabsz))
157 			continue;
158 
159 		if (strncmp(shstab + sh->sh_name, sname, snlen) == 0) {
160 			sidx = i;
161 			sdata = p + sh->sh_offset;
162 			ssz = sh->sh_size;
163 			elf_reloc_apply(p, shstab, shstabsz, sidx, sdata, ssz);
164 			break;
165 		}
166 	}
167 
168 	if (sdata == NULL)
169 		return -1;
170 
171 	if (psdata != NULL)
172 		*psdata = sdata;
173 	if (pssz != NULL)
174 		*pssz = ssz;
175 
176 	return sidx;
177 }
178 
179 static int
180 elf_reloc_size(unsigned long type)
181 {
182 	switch (type) {
183 #ifdef R_X86_64_64
184 	case R_X86_64_64:
185 		return sizeof(uint64_t);
186 #endif
187 #ifdef R_X86_64_32
188 	case R_X86_64_32:
189 		return sizeof(uint32_t);
190 #endif
191 #ifdef RELOC_32
192 	case RELOC_32:
193 		return sizeof(uint32_t);
194 #endif
195 	default:
196 		break;
197 	}
198 
199 	return -1;
200 }
201 
202 #define ELF_WRITE_RELOC(buf, val, rsize)				\
203 do {									\
204 	if (rsize == 4) {						\
205 		uint32_t v32 = val;					\
206 		memcpy(buf, &v32, sizeof(v32));				\
207 	} else {							\
208 		uint64_t v64 = val;					\
209 		memcpy(buf, &v64, sizeof(v64));				\
210 	}								\
211 } while (0)
212 
213 static void
214 elf_reloc_apply(const char *p, const char *shstab, size_t shstabsz,
215     ssize_t sidx, char *sdata, size_t ssz)
216 {
217 	Elf_Ehdr	*eh = (Elf_Ehdr *)p;
218 	Elf_Shdr	*sh;
219 	Elf_Rel		*rel = NULL;
220 	Elf_RelA	*rela = NULL;
221 	const Elf_Sym	*symtab, *sym;
222 	ssize_t		 symtabidx;
223 	size_t		 nsymb, rsym, rtyp, roff;
224 	size_t		 i, j;
225 	uint64_t	 value;
226 	int		 rsize;
227 
228 	/* Find symbol table location and number of symbols. */
229 	symtabidx = elf_getsymtab(p, shstab, shstabsz, &symtab, &nsymb);
230 	if (symtabidx == -1) {
231 		warnx("symbol table not found");
232 		return;
233 	}
234 
235 	/* Apply possible relocation. */
236 	for (i = 0; i < eh->e_shnum; i++) {
237 		sh = (Elf_Shdr *)(p + eh->e_shoff + i * eh->e_shentsize);
238 
239 		if (sh->sh_size == 0)
240 			continue;
241 
242 		if ((sh->sh_info != sidx) || (sh->sh_link != symtabidx))
243 			continue;
244 
245 		switch (sh->sh_type) {
246 		case SHT_RELA:
247 			rela = (Elf_RelA *)(p + sh->sh_offset);
248 			for (j = 0; j < (sh->sh_size / sizeof(Elf_RelA)); j++) {
249 				rsym = ELF_R_SYM(rela[j].r_info);
250 				rtyp = ELF_R_TYPE(rela[j].r_info);
251 				roff = rela[j].r_offset;
252 				if (rsym >= nsymb)
253 					continue;
254 				sym = &symtab[rsym];
255 				value = sym->st_value + rela[j].r_addend;
256 
257 				rsize = elf_reloc_size(rtyp);
258 				if (rsize == -1 || roff + rsize >= ssz)
259 					continue;
260 
261 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
262 			}
263 			break;
264 		case SHT_REL:
265 			rel = (Elf_Rel *)(p + sh->sh_offset);
266 			for (j = 0; j < (sh->sh_size / sizeof(Elf_Rel)); j++) {
267 				rsym = ELF_R_SYM(rel[j].r_info);
268 				rtyp = ELF_R_TYPE(rel[j].r_info);
269 				roff = rel[j].r_offset;
270 				if (rsym >= nsymb)
271 					continue;
272 				sym = &symtab[rsym];
273 				value = sym->st_value;
274 
275 				rsize = elf_reloc_size(rtyp);
276 				if (rsize == -1 || roff + rsize >= ssz)
277 					continue;
278 
279 				ELF_WRITE_RELOC(sdata + roff, value, rsize);
280 			}
281 			break;
282 		default:
283 			continue;
284 		}
285 	}
286 }
287