2 * SPDX-License-Identifier: BSD-4-Clause
4 * Copyright (c) 2000, Boris Popov
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Boris Popov.
18 * 4. Neither the name of the author nor the names of any co-contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #include <sys/param.h>
38 #include <sys/linker.h>
47 #include <machine/elf.h>
55 struct elf_file *ef_efile;
60 void *ef_fpage; /* First block of the file */
61 int ef_fplen; /* length of first block */
62 Elf_Dyn *ef_dyn; /* Symbol table etc. */
63 Elf_Hashelt ef_nbuckets;
64 Elf_Hashelt ef_nchains;
65 Elf_Hashelt *ef_buckets;
66 Elf_Hashelt *ef_chains;
67 Elf_Hashelt *ef_hashtab;
74 Elf_Phdr *ef_segs[MAXSEGS];
76 Elf_Rel *ef_rel; /* relocation table */
77 int ef_relsz; /* number of entries */
78 Elf_Rela *ef_rela; /* relocation table */
79 int ef_relasz; /* number of entries */
82 static void ef_print_phdr(Elf_Phdr *);
83 static u_long ef_get_offset(elf_file_t, Elf_Off);
84 static int ef_parse_dynamic(elf_file_t);
86 static int ef_get_type(elf_file_t ef);
87 static int ef_close(elf_file_t ef);
88 static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
89 static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
92 static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len,
94 static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len,
96 static int ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len,
98 static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
100 static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
103 static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx);
104 static int ef_lookup_set(elf_file_t ef, const char *name, long *startp,
105 long *stopp, long *countp);
106 static int ef_lookup_symbol(elf_file_t ef, const char *name,
109 static struct elf_file_ops ef_file_ops = {
110 .get_type = ef_get_type,
113 .read_entry = ef_read_entry,
114 .seg_read = ef_seg_read,
115 .seg_read_rel = ef_seg_read_rel,
116 .seg_read_string = ef_seg_read_string,
117 .seg_read_entry = ef_seg_read_entry,
118 .seg_read_entry_rel = ef_seg_read_entry_rel,
119 .symaddr = ef_symaddr,
120 .lookup_set = ef_lookup_set,
121 .lookup_symbol = ef_lookup_symbol
125 ef_print_phdr(Elf_Phdr *phdr)
128 if ((phdr->p_flags & PF_W) == 0) {
129 printf("text=0x%lx ", (long)phdr->p_filesz);
131 printf("data=0x%lx", (long)phdr->p_filesz);
132 if (phdr->p_filesz < phdr->p_memsz)
133 printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
139 ef_get_offset(elf_file_t ef, Elf_Off off)
144 for (i = 0; i < ef->ef_nsegs; i++) {
146 if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
147 return (ph->p_offset + (off - ph->p_vaddr));
154 ef_get_type(elf_file_t ef)
157 return (ef->ef_type);
161 * next three functions copied from link_elf.c
164 elf_hash(const char *name)
167 const unsigned char *p;
170 p = (const unsigned char *)name;
173 if ((g = h & 0xf0000000) != 0)
181 ef_lookup_symbol(elf_file_t ef, const char *name, Elf_Sym **sym)
183 unsigned long hash, symnum;
187 /* First, search hashed global symbols */
188 hash = elf_hash(name);
189 symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
191 while (symnum != STN_UNDEF) {
192 if (symnum >= ef->ef_nchains) {
193 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
198 symp = ef->ef_symtab + symnum;
199 if (symp->st_name == 0) {
200 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
205 strp = ef->ef_strtab + symp->st_name;
207 if (strcmp(name, strp) == 0) {
208 if (symp->st_shndx != SHN_UNDEF ||
209 (symp->st_value != 0 &&
210 ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
217 symnum = ef->ef_chains[symnum];
224 ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp,
231 len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
232 setsym = malloc(len);
236 /* get address of first entry */
237 snprintf(setsym, len, "%s%s", "__start_set_", name);
238 error = ef_lookup_symbol(ef, setsym, &sym);
241 *startp = sym->st_value;
243 /* get address of last entry */
244 snprintf(setsym, len, "%s%s", "__stop_set_", name);
245 error = ef_lookup_symbol(ef, setsym, &sym);
248 *stopp = sym->st_value;
250 /* and the number of entries */
251 *countp = (*stopp - *startp) / sizeof(void *);
259 ef_symaddr(elf_file_t ef, Elf_Size symidx)
263 if (symidx >= ef->ef_nchains)
265 sym = ef->ef_symtab + symidx;
267 if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
268 sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
269 return (sym->st_value);
274 ef_parse_dynamic(elf_file_t ef)
277 Elf_Hashelt hashhdr[2];
286 rel_off = rela_off = 0;
287 rel_sz = rela_sz = 0;
288 rel_entry = rela_entry = 0;
289 for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
292 error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
293 sizeof(hashhdr), hashhdr);
295 warnx("can't read hash header (%lx)",
296 ef_get_offset(ef, dp->d_un.d_ptr));
299 ef->ef_nbuckets = hashhdr[0];
300 ef->ef_nchains = hashhdr[1];
301 error = ef_read_entry(ef, -1,
302 (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt),
303 (void **)&ef->ef_hashtab);
305 warnx("can't read hash table");
308 ef->ef_buckets = ef->ef_hashtab;
309 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
312 ef->ef_stroff = dp->d_un.d_ptr;
315 ef->ef_strsz = dp->d_un.d_val;
318 ef->ef_symoff = dp->d_un.d_ptr;
321 if (dp->d_un.d_val != sizeof(Elf_Sym))
326 warnx("second DT_REL entry ignored");
327 rel_off = dp->d_un.d_ptr;
331 warnx("second DT_RELSZ entry ignored");
332 rel_sz = dp->d_un.d_val;
336 warnx("second DT_RELENT entry ignored");
337 rel_entry = dp->d_un.d_val;
341 warnx("second DT_RELA entry ignored");
342 rela_off = dp->d_un.d_ptr;
346 warnx("second DT_RELASZ entry ignored");
347 rela_sz = dp->d_un.d_val;
351 warnx("second DT_RELAENT entry ignored");
352 rela_entry = dp->d_un.d_val;
356 if (ef->ef_symoff == 0) {
357 warnx("%s: no .dynsym section found\n", ef->ef_name);
360 if (ef->ef_stroff == 0) {
361 warnx("%s: no .dynstr section found\n", ef->ef_name);
364 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
365 ef->ef_nchains * sizeof(Elf_Sym),
366 (void **)&ef->ef_symtab) != 0) {
368 warnx("%s: can't load .dynsym section (0x%lx)",
369 ef->ef_name, (long)ef->ef_symoff);
372 if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
373 (void **)&ef->ef_strtab) != 0) {
374 warnx("can't load .dynstr section");
378 if (rel_entry == 0) {
379 warnx("%s: no DT_RELENT for DT_REL", ef->ef_name);
382 if (rel_entry != sizeof(Elf_Rel)) {
383 warnx("%s: inconsistent DT_RELENT value",
387 if (rel_sz % rel_entry != 0) {
388 warnx("%s: inconsistent values for DT_RELSZ and "
389 "DT_RELENT", ef->ef_name);
392 if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz,
393 (void **)&ef->ef_rel) != 0) {
394 warnx("%s: cannot load DT_REL section", ef->ef_name);
397 ef->ef_relsz = rel_sz / rel_entry;
399 warnx("%s: %d REL entries", ef->ef_name,
403 if (rela_entry == 0) {
404 warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name);
407 if (rela_entry != sizeof(Elf_Rela)) {
408 warnx("%s: inconsistent DT_RELAENT value",
412 if (rela_sz % rela_entry != 0) {
413 warnx("%s: inconsistent values for DT_RELASZ and "
414 "DT_RELAENT", ef->ef_name);
417 if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz,
418 (void **)&ef->ef_rela) != 0) {
419 warnx("%s: cannot load DT_RELA section", ef->ef_name);
422 ef->ef_relasz = rela_sz / rela_entry;
424 warnx("%s: %d RELA entries", ef->ef_name,
431 ef_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
435 if (offset != (Elf_Off)-1) {
436 if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
440 r = read(ef->ef_fd, dest, len);
441 if (r != -1 && (size_t)r == len)
448 ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
455 error = ef_read(ef, offset, len, *ptr);
462 ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
466 ofs = ef_get_offset(ef, offset);
469 warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
470 ef->ef_name, (long)offset, ofs);
473 return (ef_read(ef, ofs, len, dest));
477 ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
484 ofs = ef_get_offset(ef, offset);
487 warnx("ef_seg_read_rel(%s): zero offset (%lx:%ld)",
488 ef->ef_name, (long)offset, ofs);
491 if ((error = ef_read(ef, ofs, len, dest)) != 0)
494 for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
495 error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len,
500 for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
501 error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len,
510 ef_seg_read_string(elf_file_t ef, Elf_Off offset, size_t len, char *dest)
515 ofs = ef_get_offset(ef, offset);
516 if (ofs == 0 || ofs == (Elf_Off)-1) {
518 warnx("ef_seg_read_string(%s): bad offset (%lx:%ld)",
519 ef->ef_name, (long)offset, ofs);
523 r = pread(ef->ef_fd, dest, len, ofs);
526 if (strnlen(dest, len) == len)
533 ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
540 error = ef_seg_read(ef, offset, len, *ptr);
547 ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
554 error = ef_seg_read_rel(ef, offset, len, *ptr);
561 ef_open(const char *filename, struct elf_file *efile, int verbose)
569 Elf_Phdr *phdr, *phdyn, *phlimit;
571 if (filename == NULL)
573 if ((fd = open(filename, O_RDONLY)) == -1)
576 ef = malloc(sizeof(*ef));
583 efile->ef_ops = &ef_file_ops;
585 bzero(ef, sizeof(*ef));
586 ef->ef_verbose = verbose;
588 ef->ef_name = strdup(filename);
589 ef->ef_efile = efile;
590 hdr = (Elf_Ehdr *)&ef->ef_hdr;
592 res = read(fd, hdr, sizeof(*hdr));
594 if (res != sizeof(*hdr))
598 if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
599 hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
600 hdr->e_ident[EI_VERSION] != EV_CURRENT ||
601 hdr->e_version != EV_CURRENT ||
602 hdr->e_machine != ELF_TARG_MACH ||
603 hdr->e_phentsize != sizeof(Elf_Phdr))
605 phlen = hdr->e_phnum * sizeof(Elf_Phdr);
606 if (ef_read_entry(ef, hdr->e_phoff, phlen,
607 (void **)&ef->ef_ph) != 0)
610 phlimit = phdr + hdr->e_phnum;
613 while (phdr < phlimit) {
616 switch (phdr->p_type) {
619 ef->ef_segs[nsegs] = phdr;
633 warnx("Skipping %s: not dynamically-linked",
636 } else if (nsegs > MAXSEGS) {
637 warnx("%s: too many segments", filename);
640 ef->ef_nsegs = nsegs;
641 if (ef_read_entry(ef, phdyn->p_offset,
642 phdyn->p_filesz, (void **)&ef->ef_dyn) != 0) {
643 printf("ef_read_entry failed\n");
646 error = ef_parse_dynamic(ef);
649 if (hdr->e_type == ET_DYN) {
650 ef->ef_type = EFT_KLD;
652 } else if (hdr->e_type == ET_EXEC) {
653 ef->ef_type = EFT_KERNEL;
664 ef_close(elf_file_t ef)
670 ef->ef_efile->ef_ops = NULL;
671 ef->ef_efile->ef_ef = NULL;