diff --git a/usr.sbin/kldxref/ef.h b/usr.sbin/kldxref/ef.h index a96bd72d6931..2909704bf2d1 100644 --- a/usr.sbin/kldxref/ef.h +++ b/usr.sbin/kldxref/ef.h @@ -1,310 +1,310 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000, Boris Popov * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _EF_H_ #define _EF_H_ #include #include #define EF_CLOSE(ef) \ (ef)->ef_ops->close((ef)->ef_ef) #define EF_SEG_READ_REL(ef, address, len, dest) \ (ef)->ef_ops->seg_read_rel((ef)->ef_ef, address, len, dest) #define EF_SEG_READ_STRING(ef, address, len, dest) \ (ef)->ef_ops->seg_read_string((ef)->ef_ef, address, len, dest) #define EF_SYMADDR(ef, symidx) \ (ef)->ef_ops->symaddr((ef)->ef_ef, symidx) #define EF_LOOKUP_SET(ef, name, startp, stopp, countp) \ (ef)->ef_ops->lookup_set((ef)->ef_ef, name, startp, stopp, countp) /* XXX, should have a different name. */ typedef struct ef_file *elf_file_t; /* FreeBSD's headers define additional typedef's for ELF structures. */ typedef Elf64_Size GElf_Size; typedef Elf64_Hashelt GElf_Hashelt; struct elf_file; struct elf_file_ops { void (*close)(elf_file_t ef); int (*seg_read_rel)(elf_file_t ef, GElf_Addr address, size_t len, void *dest); int (*seg_read_string)(elf_file_t ef, GElf_Addr address, size_t len, char *dest); GElf_Addr (*symaddr)(elf_file_t ef, GElf_Size symidx); int (*lookup_set)(elf_file_t ef, const char *name, GElf_Addr *startp, GElf_Addr *stopp, long *countp); }; typedef int (elf_reloc_t)(struct elf_file *ef, const void *reldata, Elf_Type reltype, GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest); struct elf_reloc_data { unsigned char class; unsigned char data; GElf_Half machine; elf_reloc_t *reloc; }; #define ELF_RELOC(_class, _data, _machine, _reloc) \ static struct elf_reloc_data __CONCAT(elf_reloc_data_, __LINE__) = { \ .class = (_class), \ .data = (_data), \ .machine = (_machine), \ .reloc = (_reloc) \ }; \ DATA_SET(elf_reloc, __CONCAT(elf_reloc_data_, __LINE__)) struct elf_file { elf_file_t ef_ef; struct elf_file_ops *ef_ops; const char *ef_filename; Elf *ef_elf; elf_reloc_t *ef_reloc; GElf_Ehdr ef_hdr; size_t ef_pointer_size; int ef_fd; }; #define elf_class(ef) ((ef)->ef_hdr.e_ident[EI_CLASS]) #define elf_encoding(ef) ((ef)->ef_hdr.e_ident[EI_DATA]) /* * "Generic" versions of module metadata structures. */ struct Gmod_depend { int md_ver_minimum; int md_ver_preferred; int md_ver_maximum; }; struct Gmod_version { int mv_version; }; struct Gmod_metadata { int md_version; /* structure version MDTV_* */ int md_type; /* type of entry MDT_* */ GElf_Addr md_data; /* specific data */ GElf_Addr md_cval; /* common string label */ }; struct Gmod_pnp_match_info { GElf_Addr descr; /* Description of the table */ GElf_Addr bus; /* Name of the bus for this table */ GElf_Addr table; /* Pointer to pnp table */ int entry_len; /* Length of each entry in the table (may be */ /* longer than descr describes). */ int num_entry; /* Number of entries in the table */ }; __BEGIN_DECLS /* * Attempt to parse an open ELF file as either an executable or DSO * (ef_open) or an object file (ef_obj_open). On success, these * routines initialize the 'ef_ef' and 'ef_ops' members of 'ef'. */ int ef_open(struct elf_file *ef, int verbose); int ef_obj_open(struct elf_file *ef, int verbose); /* * Direct operations on an ELF file regardless of type. Many of these * use libelf. */ /* * Open an ELF file with libelf. Populates fields other than ef_ef * and ef_ops in '*efile'. */ int elf_open_file(struct elf_file *efile, const char *filename, int verbose); /* Close an ELF file. */ void elf_close_file(struct elf_file *efile); /* Is an ELF file the same architecture as hdr? */ bool elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr); /* The size of a single object of 'type'. */ size_t elf_object_size(struct elf_file *efile, Elf_Type type); /* The size of a pointer in architecture of 'efile'. */ size_t elf_pointer_size(struct elf_file *efile); /* * Read and convert an array of a data type from an ELF file. This is * a wrapper around gelf_xlatetom() which reads an array of raw ELF * objects from the file and converts them into host structures using * native endianness. The data is returned in a dynamically-allocated * buffer. */ int elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset, size_t len, void **out); /* Reads "raw" data from an ELF file without any translation. */ int elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst, size_t len); /* * A wrapper around elf_read_raw_data which returns the data in a * dynamically-allocated buffer. */ int elf_read_raw_data_alloc(struct elf_file *efile, off_t offset, size_t len, void **out); /* * Read relocated data from an ELF file and return it in a * dynamically-allocated buffer. Note that no translation * (byte-swapping for endianness, 32-vs-64) is performed on the * returned data, but any ELF relocations which affect the contents * are applied to the returned data. The address parameter gives the * address of the data buffer if the ELF file were loaded into memory * rather than a direct file offset. */ int elf_read_relocated_data(struct elf_file *efile, GElf_Addr address, size_t len, void **buf); /* * Read the program headers from an ELF file and return them in a * dynamically-allocated array of GElf_Phdr objects. */ int elf_read_phdrs(struct elf_file *efile, size_t *nphdrp, GElf_Phdr **phdrp); /* * Read the section headers from an ELF file and return them in a * dynamically-allocated array of GElf_Shdr objects. */ int elf_read_shdrs(struct elf_file *efile, size_t *nshdrp, GElf_Shdr **shdrp); /* * Read the dynamic table from a section of an ELF file into a * dynamically-allocated array of GElf_Dyn objects. */ -int elf_read_dynamic(struct elf_file *efile, int section_index, long *ndynp, +int elf_read_dynamic(struct elf_file *efile, int section_index, size_t *ndynp, GElf_Dyn **dynp); /* * Read a symbol table from a section of an ELF file into a * dynamically-allocated array of GElf_Sym objects. */ int elf_read_symbols(struct elf_file *efile, int section_index, - long *nsymp, GElf_Sym **symp); + size_t *nsymp, GElf_Sym **symp); /* * Read a string table described by a section header of an ELF file * into a dynamically-allocated buffer. */ int elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr, long *strcnt, char **strtab); /* * Read a table of relocation objects from a section of an ELF file * into a dynamically-allocated array of GElf_Rel objects. */ int elf_read_rel(struct elf_file *efile, int section_index, long *nrelp, GElf_Rel **relp); /* * Read a table of relocation-with-addend objects from a section of an * ELF file into a dynamically-allocated array of GElf_Rela objects. */ int elf_read_rela(struct elf_file *efile, int section_index, long *nrelap, GElf_Rela **relap); /* * Read a string from an ELF file and return it in the provided * buffer. If the string is longer than the buffer, this fails with * EFAULT. The address parameter gives the address of the data buffer * if the ELF file were loaded into memory rather than a direct file * offset. */ int elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst, size_t len); /* Return the address extracted from a target pointer stored at 'p'. */ GElf_Addr elf_address_from_pointer(struct elf_file *efile, const void *p); /* * Read a linker set and return an array of addresses extracted from the * relocated pointers in the linker set. */ int elf_read_linker_set(struct elf_file *efile, const char *name, GElf_Addr **buf, long *countp); /* * Read and convert a target 'struct mod_depend' into a host * 'struct Gmod_depend'. */ int elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr, struct Gmod_depend *mdp); /* * Read and convert a target 'struct mod_version' into a host * 'struct Gmod_version'. */ int elf_read_mod_version(struct elf_file *efile, GElf_Addr addr, struct Gmod_version *mdv); /* * Read and convert a target 'struct mod_metadata' into a host * 'struct Gmod_metadata'. */ int elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr, struct Gmod_metadata *md); /* * Read and convert a target 'struct mod_pnp_match_info' into a host * 'struct Gmod_pnp_match_info'. */ int elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr, struct Gmod_pnp_match_info *pnp); /* * Apply relocations to the values obtained from the file. `relbase' is the * target relocation address of the section, and `dataoff/len' is the region * that is to be relocated, and has been copied to *dest */ int elf_reloc(struct elf_file *ef, const void *reldata, Elf_Type reltype, GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest); __END_DECLS #endif /* _EF_H_*/ diff --git a/usr.sbin/kldxref/ef_obj.c b/usr.sbin/kldxref/ef_obj.c index a1d46241b803..36c0dce432b6 100644 --- a/usr.sbin/kldxref/ef_obj.c +++ b/usr.sbin/kldxref/ef_obj.c @@ -1,492 +1,492 @@ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (c) 2000, Boris Popov * Copyright (c) 1998-2000 Doug Rabson * Copyright (c) 2004 Peter Wemm * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Boris Popov. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "ef.h" typedef struct { char *addr; GElf_Off size; int flags; int sec; /* Original section */ char *name; } Elf_progent; typedef struct { GElf_Rel *rel; long nrel; int sec; } Elf_relent; typedef struct { GElf_Rela *rela; long nrela; int sec; } Elf_relaent; struct ef_file { char *ef_name; struct elf_file *ef_efile; char *address; GElf_Off size; Elf_progent *progtab; int nprogtab; Elf_relaent *relatab; int nrela; Elf_relent *reltab; int nrel; GElf_Sym *ddbsymtab; /* The symbol table we are using */ - long ddbsymcnt; /* Number of symbols */ + size_t ddbsymcnt; /* Number of symbols */ caddr_t ddbstrtab; /* String table */ long ddbstrcnt; /* number of bytes in string table */ caddr_t shstrtab; /* Section name string table */ long shstrcnt; /* number of bytes in string table */ int ef_verbose; }; static void ef_obj_close(elf_file_t ef); static int ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest); static int ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest); static GElf_Addr ef_obj_symaddr(elf_file_t ef, GElf_Size symidx); static int ef_obj_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp, GElf_Addr *stopp, long *countp); static int ef_obj_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym); static struct elf_file_ops ef_obj_file_ops = { .close = ef_obj_close, .seg_read_rel = ef_obj_seg_read_rel, .seg_read_string = ef_obj_seg_read_string, .symaddr = ef_obj_symaddr, .lookup_set = ef_obj_lookup_set, }; static int ef_obj_lookup_symbol(elf_file_t ef, const char *name, GElf_Sym **sym) { GElf_Sym *symp; const char *strp; int i; for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) { strp = ef->ddbstrtab + symp->st_name; if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) { *sym = symp; return (0); } } return (ENOENT); } static int ef_obj_lookup_set(elf_file_t ef, const char *name, GElf_Addr *startp, GElf_Addr *stopp, long *countp) { int i; for (i = 0; i < ef->nprogtab; i++) { if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) && strcmp(ef->progtab[i].name + 4, name) == 0) { *startp = ef->progtab[i].addr - ef->address; *stopp = ef->progtab[i].addr + ef->progtab[i].size - ef->address; *countp = (*stopp - *startp) / elf_pointer_size(ef->ef_efile); return (0); } } return (ESRCH); } static GElf_Addr ef_obj_symaddr(elf_file_t ef, GElf_Size symidx) { const GElf_Sym *sym; - if (symidx >= (size_t)ef->ddbsymcnt) + if (symidx >= ef->ddbsymcnt) return (0); sym = ef->ddbsymtab + symidx; if (sym->st_shndx != SHN_UNDEF) return (sym->st_value - (GElf_Addr)ef->address); return (0); } static int ef_obj_seg_read_rel(elf_file_t ef, GElf_Addr address, size_t len, void *dest) { char *memaddr; GElf_Rel *r; GElf_Rela *a; GElf_Addr secbase, dataoff; int error, i, sec; if (address + len > ef->size) { if (ef->ef_verbose) warnx("ef_obj_seg_read_rel(%s): bad offset/len (%lx:%ld)", ef->ef_name, (long)address, (long)len); return (EFAULT); } bcopy(ef->address + address, dest, len); /* Find out which section contains the data. */ memaddr = ef->address + address; sec = -1; secbase = dataoff = 0; for (i = 0; i < ef->nprogtab; i++) { if (ef->progtab[i].addr == NULL) continue; if (memaddr < (char *)ef->progtab[i].addr || memaddr + len > (char *)ef->progtab[i].addr + ef->progtab[i].size) continue; sec = ef->progtab[i].sec; /* We relocate to address 0. */ secbase = (char *)ef->progtab[i].addr - ef->address; dataoff = memaddr - ef->address; break; } if (sec == -1) return (EFAULT); /* Now do the relocations. */ for (i = 0; i < ef->nrel; i++) { if (ef->reltab[i].sec != sec) continue; for (r = ef->reltab[i].rel; r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) { error = elf_reloc(ef->ef_efile, r, ELF_T_REL, secbase, dataoff, len, dest); if (error != 0) return (error); } } for (i = 0; i < ef->nrela; i++) { if (ef->relatab[i].sec != sec) continue; for (a = ef->relatab[i].rela; a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) { error = elf_reloc(ef->ef_efile, a, ELF_T_RELA, secbase, dataoff, len, dest); if (error != 0) return (error); } } return (0); } static int ef_obj_seg_read_string(elf_file_t ef, GElf_Addr address, size_t len, char *dest) { if (address >= ef->size) { if (ef->ef_verbose) warnx("ef_obj_seg_read_string(%s): bad address (%lx)", ef->ef_name, (long)address); return (EFAULT); } if (ef->size - address < len) len = ef->size - address; if (strnlen(ef->address + address, len) == len) return (EFAULT); memcpy(dest, ef->address + address, len); return (0); } int ef_obj_open(struct elf_file *efile, int verbose) { elf_file_t ef; GElf_Ehdr *hdr; GElf_Shdr *shdr; GElf_Sym *es; char *mapbase; size_t i, mapsize, alignmask, max_addralign, nshdr; int error, pb, ra, rl; int j, nsym, symstrindex, symtabindex; hdr = &efile->ef_hdr; if (hdr->e_type != ET_REL || hdr->e_shnum == 0 || hdr->e_shoff == 0 || hdr->e_shentsize != elf_object_size(efile, ELF_T_SHDR)) return (EFTYPE); ef = calloc(1, sizeof(*ef)); if (ef == NULL) return (errno); efile->ef_ef = ef; efile->ef_ops = &ef_obj_file_ops; ef->ef_verbose = verbose; ef->ef_name = strdup(efile->ef_filename); ef->ef_efile = efile; error = elf_read_shdrs(efile, &nshdr, &shdr); if (error != 0) { shdr = NULL; goto out; } /* Scan the section headers for information and table sizing. */ nsym = 0; symtabindex = -1; symstrindex = -1; for (i = 0; i < nshdr; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: ef->nprogtab++; break; case SHT_SYMTAB: nsym++; symtabindex = i; symstrindex = shdr[i].sh_link; break; case SHT_REL: ef->nrel++; break; case SHT_RELA: ef->nrela++; break; case SHT_STRTAB: break; } } if (ef->nprogtab == 0) { warnx("%s: file has no contents", ef->ef_name); goto out; } if (nsym != 1) { warnx("%s: file has no valid symbol table", ef->ef_name); goto out; } if (symstrindex < 0 || symstrindex > nshdr || shdr[symstrindex].sh_type != SHT_STRTAB) { warnx("%s: file has invalid symbol strings", ef->ef_name); goto out; } /* Allocate space for tracking the load chunks */ if (ef->nprogtab != 0) ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab)); if (ef->nrel != 0) ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab)); if (ef->nrela != 0) ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab)); if ((ef->nprogtab != 0 && ef->progtab == NULL) || (ef->nrel != 0 && ef->reltab == NULL) || (ef->nrela != 0 && ef->relatab == NULL)) { printf("malloc failed\n"); error = ENOMEM; goto out; } if (elf_read_symbols(efile, symtabindex, &ef->ddbsymcnt, &ef->ddbsymtab) != 0) { printf("elf_read_symbols failed\n"); goto out; } if (elf_read_string_table(efile, &shdr[symstrindex], &ef->ddbstrcnt, &ef->ddbstrtab) != 0) { printf("elf_read_string_table failed\n"); goto out; } /* Do we have a string table for the section names? */ if (hdr->e_shstrndx != 0 && shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) { if (elf_read_string_table(efile, &shdr[hdr->e_shstrndx], &ef->shstrcnt, &ef->shstrtab) != 0) { printf("elf_read_string_table failed\n"); goto out; } } /* Size up code/data(progbits) and bss(nobits). */ alignmask = 0; max_addralign = 0; mapsize = 0; for (i = 0; i < nshdr; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: alignmask = shdr[i].sh_addralign - 1; if (shdr[i].sh_addralign > max_addralign) max_addralign = shdr[i].sh_addralign; mapsize += alignmask; mapsize &= ~alignmask; mapsize += shdr[i].sh_size; break; } } /* We know how much space we need for the text/data/bss/etc. */ ef->size = mapsize; if (posix_memalign((void **)&ef->address, max_addralign, mapsize)) { printf("posix_memalign failed\n"); goto out; } mapbase = ef->address; /* * Now load code/data(progbits), zero bss(nobits), allocate * space for and load relocs */ pb = 0; rl = 0; ra = 0; alignmask = 0; for (i = 0; i < nshdr; i++) { switch (shdr[i].sh_type) { case SHT_PROGBITS: case SHT_NOBITS: alignmask = shdr[i].sh_addralign - 1; mapbase += alignmask; mapbase = (char *)((uintptr_t)mapbase & ~alignmask); ef->progtab[pb].addr = (void *)(uintptr_t)mapbase; if (shdr[i].sh_type == SHT_PROGBITS) { ef->progtab[pb].name = "<>"; if (elf_read_raw_data(efile, shdr[i].sh_offset, ef->progtab[pb].addr, shdr[i].sh_size) != 0) { printf("failed to read progbits\n"); goto out; } } else { ef->progtab[pb].name = "<>"; bzero(ef->progtab[pb].addr, shdr[i].sh_size); } ef->progtab[pb].size = shdr[i].sh_size; ef->progtab[pb].sec = i; if (ef->shstrtab && shdr[i].sh_name != 0) ef->progtab[pb].name = ef->shstrtab + shdr[i].sh_name; /* Update all symbol values with the offset. */ for (j = 0; j < ef->ddbsymcnt; j++) { es = &ef->ddbsymtab[j]; if (es->st_shndx != i) continue; es->st_value += (GElf_Addr)ef->progtab[pb].addr; } mapbase += shdr[i].sh_size; pb++; break; case SHT_REL: ef->reltab[rl].sec = shdr[i].sh_info; if (elf_read_rel(efile, i, &ef->reltab[rl].nrel, &ef->reltab[rl].rel) != 0) { printf("elf_read_rel failed\n"); goto out; } rl++; break; case SHT_RELA: ef->relatab[ra].sec = shdr[i].sh_info; if (elf_read_rela(efile, i, &ef->relatab[ra].nrela, &ef->relatab[ra].rela) != 0) { printf("elf_read_rela failed\n"); goto out; } ra++; break; } } error = 0; out: free(shdr); if (error != 0) ef_obj_close(ef); return (error); } static void ef_obj_close(elf_file_t ef) { int i; if (ef->ef_name) free(ef->ef_name); if (ef->size != 0) free(ef->address); if (ef->nprogtab != 0) free(ef->progtab); if (ef->nrel != 0) { for (i = 0; i < ef->nrel; i++) if (ef->reltab[i].rel != NULL) free(ef->reltab[i].rel); free(ef->reltab); } if (ef->nrela != 0) { for (i = 0; i < ef->nrela; i++) if (ef->relatab[i].rela != NULL) free(ef->relatab[i].rela); free(ef->relatab); } if (ef->ddbsymtab != NULL) free(ef->ddbsymtab); if (ef->ddbstrtab != NULL) free(ef->ddbstrtab); if (ef->shstrtab != NULL) free(ef->shstrtab); ef->ef_efile->ef_ops = NULL; ef->ef_efile->ef_ef = NULL; free(ef); } diff --git a/usr.sbin/kldxref/elf.c b/usr.sbin/kldxref/elf.c index a30eb4456a76..f8a6510ed352 100644 --- a/usr.sbin/kldxref/elf.c +++ b/usr.sbin/kldxref/elf.c @@ -1,674 +1,674 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2021-2023 John Baldwin * * This software was developed by SRI International and the University * of Cambridge Computer Laboratory (Department of Computer Science * and Technology) under Defense Advanced Research Projects Agency * (DARPA) contract HR0011-18-C-0016 ("ECATS"), as part of the DARPA * SSITH research programme and under DARPA Contract No. HR001122S0003 * ("MTSS"). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include "ef.h" SET_DECLARE(elf_reloc, struct elf_reloc_data); static elf_reloc_t * elf_find_reloc(const GElf_Ehdr *hdr) { struct elf_reloc_data **erd; SET_FOREACH(erd, elf_reloc) { if (hdr->e_ident[EI_CLASS] == (*erd)->class && hdr->e_ident[EI_DATA] == (*erd)->data && hdr->e_machine == (*erd)->machine) return ((*erd)->reloc); } return (NULL); } int elf_open_file(struct elf_file *efile, const char *filename, int verbose) { int error; memset(efile, 0, sizeof(*efile)); efile->ef_filename = filename; efile->ef_fd = open(filename, O_RDONLY); if (efile->ef_fd == -1) { if (verbose) warn("open(%s)", filename); return (errno); } efile->ef_elf = elf_begin(efile->ef_fd, ELF_C_READ, NULL); if (efile->ef_elf == NULL) { if (verbose) warnx("elf_begin(%s): %s", filename, elf_errmsg(0)); elf_close_file(efile); return (EINVAL); } if (elf_kind(efile->ef_elf) != ELF_K_ELF) { if (verbose) warnx("%s: not an ELF file", filename); elf_close_file(efile); return (EINVAL); } if (gelf_getehdr(efile->ef_elf, &efile->ef_hdr) == NULL) { if (verbose) warnx("gelf_getehdr(%s): %s", filename, elf_errmsg(0)); elf_close_file(efile); return (EINVAL); } efile->ef_reloc = elf_find_reloc(&efile->ef_hdr); if (efile->ef_reloc == NULL) { if (verbose) warnx("%s: unsupported architecture", filename); elf_close_file(efile); return (EFTYPE); } error = ef_open(efile, verbose); if (error != 0) { error = ef_obj_open(efile, verbose); if (error != 0) { if (verbose) warnc(error, "%s: not a valid DSO or object file", filename); elf_close_file(efile); return (error); } } efile->ef_pointer_size = elf_object_size(efile, ELF_T_ADDR); return (0); } void elf_close_file(struct elf_file *efile) { if (efile->ef_ops != NULL) { EF_CLOSE(efile); } if (efile->ef_elf != NULL) { elf_end(efile->ef_elf); efile->ef_elf = NULL; } if (efile->ef_fd > 0) { close(efile->ef_fd); efile->ef_fd = -1; } } bool elf_compatible(struct elf_file *efile, const GElf_Ehdr *hdr) { if (efile->ef_hdr.e_ident[EI_CLASS] != hdr->e_ident[EI_CLASS] || efile->ef_hdr.e_ident[EI_DATA] != hdr->e_ident[EI_DATA] || efile->ef_hdr.e_machine != hdr->e_machine) return (false); return (true); } size_t elf_object_size(struct elf_file *efile, Elf_Type type) { return (gelf_fsize(efile->ef_elf, type, 1, efile->ef_hdr.e_version)); } /* * The number of objects of 'type' in region of the file of size * 'file_size'. */ static size_t elf_object_count(struct elf_file *efile, Elf_Type type, size_t file_size) { return (file_size / elf_object_size(efile, type)); } int elf_read_raw_data(struct elf_file *efile, off_t offset, void *dst, size_t len) { ssize_t nread; if (offset != (off_t)-1) { if (lseek(efile->ef_fd, offset, SEEK_SET) == -1) return (EIO); } nread = read(efile->ef_fd, dst, len); if (nread == -1) return (errno); if (nread != len) return (EIO); return (0); } int elf_read_raw_data_alloc(struct elf_file *efile, off_t offset, size_t len, void **out) { void *buf; int error; buf = malloc(len); if (buf == NULL) return (ENOMEM); error = elf_read_raw_data(efile, offset, buf, len); if (error != 0) { free(buf); return (error); } *out = buf; return (0); } int elf_read_data(struct elf_file *efile, Elf_Type type, off_t offset, size_t len, void **out) { Elf_Data dst, src; void *buf; int error; buf = malloc(len); if (buf == NULL) return (ENOMEM); error = elf_read_raw_data(efile, offset, buf, len); if (error != 0) { free(buf); return (error); } memset(&dst, 0, sizeof(dst)); memset(&src, 0, sizeof(src)); src.d_buf = buf; src.d_size = len; src.d_type = type; src.d_version = efile->ef_hdr.e_version; dst.d_buf = buf; dst.d_size = len; dst.d_version = EV_CURRENT; if (gelf_xlatetom(efile->ef_elf, &dst, &src, elf_encoding(efile)) == NULL) { free(buf); return (ENXIO); } if (dst.d_size != len) warnx("elf_read_data: translation of type %u size mismatch", type); *out = buf; return (0); } int elf_read_relocated_data(struct elf_file *efile, GElf_Addr address, size_t len, void **buf) { int error; void *p; p = malloc(len); if (p == NULL) return (ENOMEM); error = EF_SEG_READ_REL(efile, address, len, p); if (error != 0) { free(p); return (error); } *buf = p; return (0); } int elf_read_phdrs(struct elf_file *efile, size_t *nphdrp, GElf_Phdr **phdrp) { GElf_Phdr *phdr; size_t nphdr, i; int error; if (elf_getphdrnum(efile->ef_elf, &nphdr) == -1) return (EFTYPE); phdr = calloc(nphdr, sizeof(*phdr)); if (phdr == NULL) return (ENOMEM); for (i = 0; i < nphdr; i++) { if (gelf_getphdr(efile->ef_elf, i, &phdr[i]) == NULL) { error = EFTYPE; goto out; } } *nphdrp = nphdr; *phdrp = phdr; return (0); out: free(phdr); return (error); } int elf_read_shdrs(struct elf_file *efile, size_t *nshdrp, GElf_Shdr **shdrp) { GElf_Shdr *shdr; Elf_Scn *scn; size_t nshdr, i; int error; if (elf_getshdrnum(efile->ef_elf, &nshdr) == -1) return (EFTYPE); shdr = calloc(nshdr, sizeof(*shdr)); if (shdr == NULL) return (ENOMEM); for (i = 0; i < nshdr; i++) { scn = elf_getscn(efile->ef_elf, i); if (scn == NULL) { error = EFTYPE; goto out; } if (gelf_getshdr(scn, &shdr[i]) == NULL) { error = EFTYPE; goto out; } } *nshdrp = nshdr; *shdrp = shdr; return (0); out: free(shdr); return (error); } int -elf_read_dynamic(struct elf_file *efile, int section_index, long *ndynp, +elf_read_dynamic(struct elf_file *efile, int section_index, size_t *ndynp, GElf_Dyn **dynp) { GElf_Shdr shdr; Elf_Scn *scn; Elf_Data *data; GElf_Dyn *dyn; long i, ndyn; scn = elf_getscn(efile->ef_elf, section_index); if (scn == NULL) return (EINVAL); if (gelf_getshdr(scn, &shdr) == NULL) return (EINVAL); data = elf_getdata(scn, NULL); if (data == NULL) return (EINVAL); ndyn = elf_object_count(efile, ELF_T_DYN, shdr.sh_size); dyn = calloc(ndyn, sizeof(*dyn)); if (dyn == NULL) return (ENOMEM); for (i = 0; i < ndyn; i++) { if (gelf_getdyn(data, i, &dyn[i]) == NULL) { free(dyn); return (EINVAL); } } *ndynp = ndyn; *dynp = dyn; return (0); } int -elf_read_symbols(struct elf_file *efile, int section_index, long *nsymp, +elf_read_symbols(struct elf_file *efile, int section_index, size_t *nsymp, GElf_Sym **symp) { GElf_Shdr shdr; Elf_Scn *scn; Elf_Data *data; GElf_Sym *sym; - long i, nsym; + size_t i, nsym; scn = elf_getscn(efile->ef_elf, section_index); if (scn == NULL) return (EINVAL); if (gelf_getshdr(scn, &shdr) == NULL) return (EINVAL); data = elf_getdata(scn, NULL); if (data == NULL) return (EINVAL); nsym = elf_object_count(efile, ELF_T_SYM, shdr.sh_size); sym = calloc(nsym, sizeof(*sym)); if (sym == NULL) return (ENOMEM); for (i = 0; i < nsym; i++) { if (gelf_getsym(data, i, &sym[i]) == NULL) { free(sym); return (EINVAL); } } *nsymp = nsym; *symp = sym; return (0); } int elf_read_string_table(struct elf_file *efile, const GElf_Shdr *shdr, long *strcnt, char **strtab) { int error; if (shdr->sh_type != SHT_STRTAB) return (EINVAL); error = elf_read_raw_data_alloc(efile, shdr->sh_offset, shdr->sh_size, (void **)strtab); if (error != 0) return (error); *strcnt = shdr->sh_size; return (0); } int elf_read_rel(struct elf_file *efile, int section_index, long *nrelp, GElf_Rel **relp) { GElf_Shdr shdr; Elf_Scn *scn; Elf_Data *data; GElf_Rel *rel; long i, nrel; scn = elf_getscn(efile->ef_elf, section_index); if (scn == NULL) return (EINVAL); if (gelf_getshdr(scn, &shdr) == NULL) return (EINVAL); data = elf_getdata(scn, NULL); if (data == NULL) return (EINVAL); nrel = elf_object_count(efile, ELF_T_REL, shdr.sh_size); rel = calloc(nrel, sizeof(*rel)); if (rel == NULL) return (ENOMEM); for (i = 0; i < nrel; i++) { if (gelf_getrel(data, i, &rel[i]) == NULL) { free(rel); return (EINVAL); } } *nrelp = nrel; *relp = rel; return (0); } int elf_read_rela(struct elf_file *efile, int section_index, long *nrelap, GElf_Rela **relap) { GElf_Shdr shdr; Elf_Scn *scn; Elf_Data *data; GElf_Rela *rela; long i, nrela; scn = elf_getscn(efile->ef_elf, section_index); if (scn == NULL) return (EINVAL); if (gelf_getshdr(scn, &shdr) == NULL) return (EINVAL); data = elf_getdata(scn, NULL); if (data == NULL) return (EINVAL); nrela = elf_object_count(efile, ELF_T_RELA, shdr.sh_size); rela = calloc(nrela, sizeof(*rela)); if (rela == NULL) return (ENOMEM); for (i = 0; i < nrela; i++) { if (gelf_getrela(data, i, &rela[i]) == NULL) { free(rela); return (EINVAL); } } *nrelap = nrela; *relap = rela; return (0); } size_t elf_pointer_size(struct elf_file *efile) { return (efile->ef_pointer_size); } int elf_int(struct elf_file *efile, const void *p) { if (elf_encoding(efile) == ELFDATA2LSB) return (le32dec(p)); else return (be32dec(p)); } GElf_Addr elf_address_from_pointer(struct elf_file *efile, const void *p) { switch (elf_class(efile)) { case ELFCLASS32: if (elf_encoding(efile) == ELFDATA2LSB) return (le32dec(p)); else return (be32dec(p)); case ELFCLASS64: if (elf_encoding(efile) == ELFDATA2LSB) return (le64dec(p)); else return (be64dec(p)); default: __builtin_unreachable(); } } int elf_read_string(struct elf_file *efile, GElf_Addr address, void *dst, size_t len) { return (EF_SEG_READ_STRING(efile, address, len, dst)); } int elf_read_linker_set(struct elf_file *efile, const char *name, GElf_Addr **bufp, long *countp) { GElf_Addr *buf, start, stop; char *p; void *raw; long i, count; int error; error = EF_LOOKUP_SET(efile, name, &start, &stop, &count); if (error != 0) return (error); error = elf_read_relocated_data(efile, start, count * elf_pointer_size(efile), &raw); if (error != 0) return (error); buf = calloc(count, sizeof(*buf)); if (buf == NULL) { free(raw); return (ENOMEM); } p = raw; for (i = 0; i < count; i++) { buf[i] = elf_address_from_pointer(efile, p); p += elf_pointer_size(efile); } free(raw); *bufp = buf; *countp = count; return (0); } int elf_read_mod_depend(struct elf_file *efile, GElf_Addr addr, struct Gmod_depend *mdp) { int *p; int error; error = elf_read_relocated_data(efile, addr, sizeof(int) * 3, (void **)&p); if (error != 0) return (error); memset(mdp, 0, sizeof(*mdp)); mdp->md_ver_minimum = elf_int(efile, p); mdp->md_ver_preferred = elf_int(efile, p + 1); mdp->md_ver_maximum = elf_int(efile, p + 2); free(p); return (0); } int elf_read_mod_version(struct elf_file *efile, GElf_Addr addr, struct Gmod_version *mdv) { int error, value; error = EF_SEG_READ_REL(efile, addr, sizeof(int), &value); if (error != 0) return (error); memset(mdv, 0, sizeof(*mdv)); mdv->mv_version = elf_int(efile, &value); return (0); } int elf_read_mod_metadata(struct elf_file *efile, GElf_Addr addr, struct Gmod_metadata *md) { char *p; size_t len, offset, pointer_size; int error; pointer_size = elf_pointer_size(efile); len = 2 * sizeof(int); len = roundup(len, pointer_size); len += 2 * pointer_size; error = elf_read_relocated_data(efile, addr, len, (void **)&p); if (error != 0) return (error); memset(md, 0, sizeof(*md)); offset = 0; md->md_version = elf_int(efile, p + offset); offset += sizeof(int); md->md_type = elf_int(efile, p + offset); offset += sizeof(int); offset = roundup(offset, pointer_size); md->md_data = elf_address_from_pointer(efile, p + offset); offset += pointer_size; md->md_cval = elf_address_from_pointer(efile, p + offset); free(p); return (0); } int elf_read_mod_pnp_match_info(struct elf_file *efile, GElf_Addr addr, struct Gmod_pnp_match_info *pnp) { char *p; size_t len, offset, pointer_size; int error; pointer_size = elf_pointer_size(efile); len = 3 * pointer_size; len = roundup(len, pointer_size); len += 2 * sizeof(int); error = elf_read_relocated_data(efile, addr, len, (void **)&p); if (error != 0) return (error); memset(pnp, 0, sizeof(*pnp)); offset = 0; pnp->descr = elf_address_from_pointer(efile, p + offset); offset += pointer_size; pnp->bus = elf_address_from_pointer(efile, p + offset); offset += pointer_size; pnp->table = elf_address_from_pointer(efile, p + offset); offset += pointer_size; offset = roundup(offset, pointer_size); pnp->entry_len = elf_int(efile, p + offset); offset += sizeof(int); pnp->num_entry = elf_int(efile, p + offset); free(p); return (0); } int elf_reloc(struct elf_file *efile, const void *reldata, Elf_Type reltype, GElf_Addr relbase, GElf_Addr dataoff, size_t len, void *dest) { return (efile->ef_reloc(efile, reldata, reltype, relbase, dataoff, len, dest)); }