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