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
35 #include <sys/param.h>
49 struct elf_file *ef_efile;
51 void *ef_fpage; /* First block of the file */
52 int ef_fplen; /* length of first block */
53 GElf_Hashelt ef_nbuckets;
54 GElf_Hashelt ef_nchains;
55 GElf_Hashelt *ef_buckets;
56 GElf_Hashelt *ef_chains;
57 GElf_Hashelt *ef_hashtab;
62 GElf_Phdr *ef_segs[MAXSEGS];
64 GElf_Rel *ef_rel; /* relocation table */
65 long ef_relsz; /* number of entries */
66 GElf_Rela *ef_rela; /* relocation table */
67 long ef_relasz; /* number of entries */
70 static void ef_print_phdr(GElf_Phdr *);
71 static GElf_Off ef_get_offset(elf_file_t, GElf_Addr);
73 static void ef_close(elf_file_t ef);
75 static int ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len,
77 static int ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len,
80 static GElf_Addr ef_symaddr(elf_file_t ef, GElf_Size symidx);
81 static int ef_lookup_set(elf_file_t ef, const char *name,
82 GElf_Addr *startp, GElf_Addr *stopp, long *countp);
83 static int ef_lookup_symbol(elf_file_t ef, const char *name,
86 static struct elf_file_ops ef_file_ops = {
88 .seg_read_rel = ef_seg_read_rel,
89 .seg_read_string = ef_seg_read_string,
90 .symaddr = ef_symaddr,
91 .lookup_set = ef_lookup_set,
95 ef_print_phdr(GElf_Phdr *phdr)
98 if ((phdr->p_flags & PF_W) == 0) {
99 printf("text=0x%jx ", (uintmax_t)phdr->p_filesz);
101 printf("data=0x%jx", (uintmax_t)phdr->p_filesz);
102 if (phdr->p_filesz < phdr->p_memsz)
104 (uintmax_t)(phdr->p_memsz - phdr->p_filesz));
110 ef_get_offset(elf_file_t ef, GElf_Addr addr)
115 for (i = 0; i < ef->ef_nsegs; i++) {
117 if (addr >= ph->p_vaddr && addr < ph->p_vaddr + ph->p_memsz) {
118 return (ph->p_offset + (addr - ph->p_vaddr));
125 * next two functions copied from link_elf.c
128 ef_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym)
130 unsigned long hash, symnum;
134 /* First, search hashed global symbols */
135 hash = elf_hash(name);
136 symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
138 while (symnum != STN_UNDEF) {
139 if (symnum >= ef->ef_nchains) {
140 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
145 symp = ef->ef_symtab + symnum;
146 if (symp->st_name == 0) {
147 warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
152 strp = ef->ef_strtab + symp->st_name;
154 if (strcmp(name, strp) == 0) {
155 if (symp->st_shndx != SHN_UNDEF ||
156 (symp->st_value != 0 &&
157 GELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
164 symnum = ef->ef_chains[symnum];
171 ef_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp,
172 GElf_Addr *stopp, long *countp)
178 len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
179 setsym = malloc(len);
183 /* get address of first entry */
184 snprintf(setsym, len, "%s%s", "__start_set_", name);
185 error = ef_lookup_symbol(ef, setsym, &sym);
188 *startp = sym->st_value;
190 /* get address of last entry */
191 snprintf(setsym, len, "%s%s", "__stop_set_", name);
192 error = ef_lookup_symbol(ef, setsym, &sym);
195 *stopp = sym->st_value;
197 /* and the number of entries */
198 *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile);
206 ef_symaddr(elf_file_t ef, GElf_Size symidx)
210 if (symidx >= ef->ef_nchains)
212 sym = ef->ef_symtab + symidx;
214 if (GELF_ST_BIND(sym->st_info) == STB_LOCAL &&
215 sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
216 return (sym->st_value);
221 ef_parse_dynamic(elf_file_t ef, const GElf_Phdr *phdyn)
225 size_t i, ndyn, nshdr, nsym;
227 GElf_Off hash_off, sym_off, str_off;
235 * The kernel linker parses the PT_DYNAMIC segment to find
236 * various important tables. The gelf API of libelf is
237 * section-oriented and requires extracting data from sections
238 * instead of segments (program headers). As a result,
239 * iterate over section headers to read various tables after
240 * parsing values from PT_DYNAMIC.
242 error = elf_read_shdrs(ef->ef_efile, &nshdr, &shdr);
247 /* Find section for .dynamic. */
249 for (i = 0; i < nshdr; i++) {
250 if (shdr[i].sh_type == SHT_DYNAMIC) {
251 if (shdr[i].sh_offset != phdyn->p_offset ||
252 shdr[i].sh_size != phdyn->p_filesz) {
253 warnx(".dynamic section doesn't match phdr");
257 if (dynamic_idx != -1) {
258 warnx("multiple SHT_DYNAMIC sections");
266 error = elf_read_dynamic(ef->ef_efile, dynamic_idx, &ndyn, &dyn);
270 hash_off = rel_off = rela_off = sym_off = str_off = 0;
271 rel_sz = rela_sz = 0;
272 for (i = 0; i < ndyn; i++) {
274 if (dp->d_tag == DT_NULL)
280 warnx("second DT_HASH entry ignored");
282 hash_off = ef_get_offset(ef, dp->d_un.d_ptr);
286 warnx("second DT_STRTAB entry ignored");
288 str_off = ef_get_offset(ef, dp->d_un.d_ptr);
292 warnx("second DT_SYMTAB entry ignored");
294 sym_off = ef_get_offset(ef, dp->d_un.d_ptr);
297 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
305 warnx("second DT_REL entry ignored");
307 rel_off = ef_get_offset(ef, dp->d_un.d_ptr);
311 warnx("second DT_RELSZ entry ignored");
313 rel_sz = dp->d_un.d_val;
316 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
324 warnx("second DT_RELA entry ignored");
326 rela_off = ef_get_offset(ef, dp->d_un.d_ptr);
330 warnx("second DT_RELSZ entry ignored");
332 rela_sz = dp->d_un.d_val;
335 if (dp->d_un.d_val != elf_object_size(ef->ef_efile,
344 warnx("%s: no .hash section found\n", ef->ef_name);
349 warnx("%s: no .dynsym section found\n", ef->ef_name);
354 warnx("%s: no .dynstr section found\n", ef->ef_name);
358 if (rel_off == 0 && rela_off == 0) {
359 warnx("%s: no ELF relocation table found\n", ef->ef_name);
365 for (i = 0; i < nshdr; i++) {
366 switch (shdr[i].sh_type) {
368 if (shdr[i].sh_offset != hash_off) {
369 warnx("%s: ignoring SHT_HASH at different offset from DT_HASH",
375 * libelf(3) mentions ELF_T_HASH, but it is
378 if (shdr[i].sh_size < sizeof(*ef->ef_hashtab) * 2) {
379 warnx("hash section too small");
383 error = elf_read_data(ef->ef_efile, ELF_T_WORD,
384 shdr[i].sh_offset, shdr[i].sh_size,
385 (void **)&ef->ef_hashtab);
387 warnc(error, "can't read hash table");
390 ef->ef_nbuckets = ef->ef_hashtab[0];
391 ef->ef_nchains = ef->ef_hashtab[1];
392 if ((2 + ef->ef_nbuckets + ef->ef_nchains) *
393 sizeof(*ef->ef_hashtab) != shdr[i].sh_size) {
394 warnx("inconsistent hash section size");
399 ef->ef_buckets = ef->ef_hashtab + 2;
400 ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
403 if (shdr[i].sh_offset != sym_off) {
404 warnx("%s: ignoring SHT_DYNSYM at different offset from DT_SYMTAB",
408 error = elf_read_symbols(ef->ef_efile, i, &nsym,
412 warnx("%s: can't load .dynsym section (0x%jx)",
413 ef->ef_name, (uintmax_t)sym_off);
418 if (shdr[i].sh_offset != str_off)
420 error = elf_read_string_table(ef->ef_efile,
421 &shdr[i], &ef->ef_strsz, &ef->ef_strtab);
423 warnx("can't load .dynstr section");
429 if (shdr[i].sh_offset != rel_off)
431 if (shdr[i].sh_size != rel_sz) {
432 warnx("%s: size mismatch for DT_REL section",
437 error = elf_read_rel(ef->ef_efile, i, &ef->ef_relsz,
440 warnx("%s: cannot load DT_REL section",
446 if (shdr[i].sh_offset != rela_off)
448 if (shdr[i].sh_size != rela_sz) {
449 warnx("%s: size mismatch for DT_RELA section",
454 error = elf_read_rela(ef->ef_efile, i, &ef->ef_relasz,
457 warnx("%s: cannot load DT_RELA section",
465 if (ef->ef_hashtab == NULL) {
466 warnx("%s: did not find a symbol hash table", ef->ef_name);
470 if (ef->ef_symtab == NULL) {
471 warnx("%s: did not find a dynamic symbol table", ef->ef_name);
475 if (nsym != ef->ef_nchains) {
476 warnx("%s: symbol count mismatch", ef->ef_name);
480 if (ef->ef_strtab == NULL) {
481 warnx("%s: did not find a dynamic string table", ef->ef_name);
485 if (rel_off != 0 && ef->ef_rel == NULL) {
486 warnx("%s: did not find a DT_REL relocation table",
491 if (rela_off != 0 && ef->ef_rela == NULL) {
492 warnx("%s: did not find a DT_RELA relocation table",
506 ef_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest)
513 ofs = ef_get_offset(ef, address);
516 warnx("ef_seg_read_rel(%s): bad address (%jx)",
517 ef->ef_name, (uintmax_t)address);
520 error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
524 for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
525 error = elf_reloc(ef->ef_efile, r, ELF_T_REL, 0, address,
530 for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
531 error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, 0, address,
540 ef_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest)
545 ofs = ef_get_offset(ef, address);
548 warnx("ef_seg_read_string(%s): bad offset (%jx:%ju)",
549 ef->ef_name, (uintmax_t)address, (uintmax_t)ofs);
553 error = elf_read_raw_data(ef->ef_efile, ofs, dest, len);
556 if (strnlen(dest, len) == len)
563 ef_open(struct elf_file *efile, int verbose)
567 size_t i, nphdr, nsegs;
569 GElf_Phdr *phdr, *phdyn;
571 hdr = &efile->ef_hdr;
572 if (hdr->e_phnum == 0 ||
573 hdr->e_phentsize != elf_object_size(efile, ELF_T_PHDR) ||
574 hdr->e_shnum == 0 || hdr->e_shoff == 0 ||
575 hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR))
578 ef = malloc(sizeof(*ef));
583 efile->ef_ops = &ef_file_ops;
585 bzero(ef, sizeof(*ef));
586 ef->ef_verbose = verbose;
587 ef->ef_name = strdup(efile->ef_filename);
588 ef->ef_efile = efile;
590 error = elf_read_phdrs(efile, &nphdr, &ef->ef_ph);
600 for (i = 0; i < nphdr; i++, phdr++) {
603 switch (phdr->p_type) {
606 ef->ef_segs[nsegs] = phdr;
619 warnx("Skipping %s: not dynamically-linked",
624 if (nsegs > MAXSEGS) {
625 warnx("%s: too many segments", ef->ef_name);
628 ef->ef_nsegs = nsegs;
630 error = ef_parse_dynamic(ef, phdyn);
638 ef_close(elf_file_t ef)
644 free(ef->ef_hashtab);
648 ef->ef_efile->ef_ops = NULL;
649 ef->ef_efile->ef_ef = NULL;