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