1dc0093f4Seschrock /* 2dc0093f4Seschrock * CDDL HEADER START 3dc0093f4Seschrock * 4dc0093f4Seschrock * The contents of this file are subject to the terms of the 5dc0093f4Seschrock * Common Development and Distribution License (the "License"). 6dc0093f4Seschrock * You may not use this file except in compliance with the License. 7dc0093f4Seschrock * 8dc0093f4Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9dc0093f4Seschrock * or http://www.opensolaris.org/os/licensing. 10dc0093f4Seschrock * See the License for the specific language governing permissions 11dc0093f4Seschrock * and limitations under the License. 12dc0093f4Seschrock * 13dc0093f4Seschrock * When distributing Covered Code, include this CDDL HEADER in each 14dc0093f4Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15dc0093f4Seschrock * If applicable, add the following below this CDDL HEADER, with the 16dc0093f4Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 17dc0093f4Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 18dc0093f4Seschrock * 19dc0093f4Seschrock * CDDL HEADER END 20dc0093f4Seschrock */ 21dc0093f4Seschrock 22dc0093f4Seschrock /* 2323a1cceaSRoger A. Faulkner * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24b5f3c6ffSJason King * 25b5f3c6ffSJason King * Copyright 2011 Jason King. All rights reserved. 26dc0093f4Seschrock */ 27dc0093f4Seschrock 28dc0093f4Seschrock #include <assert.h> 29dc0093f4Seschrock #include <errno.h> 30dc0093f4Seschrock #include <fcntl.h> 31dc0093f4Seschrock #include <gelf.h> 32dc0093f4Seschrock #include <libelf.h> 33dc0093f4Seschrock #include <stdlib.h> 34dc0093f4Seschrock #include <string.h> 35dc0093f4Seschrock #include <unistd.h> 36dc0093f4Seschrock 37dc0093f4Seschrock #include <sys/fcntl.h> 38dc0093f4Seschrock #include <sys/stat.h> 39b5f3c6ffSJason King #include <sys/sysmacros.h> 40b5f3c6ffSJason King #include <sys/types.h> 41dc0093f4Seschrock 42dc0093f4Seschrock #include "dis_target.h" 43dc0093f4Seschrock #include "dis_util.h" 44dc0093f4Seschrock 45dc0093f4Seschrock /* 46dc0093f4Seschrock * Standard ELF disassembler target. 47dc0093f4Seschrock * 48dc0093f4Seschrock * We only support disassembly of ELF files, though this target interface could 49dc0093f4Seschrock * be extended in the future. Each basic type (target, func, section) contains 50dc0093f4Seschrock * enough information to uniquely identify the location within the file. The 51dc0093f4Seschrock * interfaces use libelf(3LIB) to do the actual processing of the file. 52dc0093f4Seschrock */ 53dc0093f4Seschrock 54dc0093f4Seschrock /* 55dc0093f4Seschrock * Symbol table entry type. We maintain our own symbol table sorted by address, 56dc0093f4Seschrock * with the symbol name already resolved against the ELF symbol table. 57dc0093f4Seschrock */ 58dc0093f4Seschrock typedef struct sym_entry { 59dc0093f4Seschrock GElf_Sym se_sym; /* value of symbol */ 60dc0093f4Seschrock char *se_name; /* name of symbol */ 61dc0093f4Seschrock int se_shndx; /* section where symbol is located */ 62dc0093f4Seschrock } sym_entry_t; 63dc0093f4Seschrock 64dc0093f4Seschrock /* 65b5f3c6ffSJason King * Create a map of the virtual address ranges of every section. This will 66b5f3c6ffSJason King * allow us to create dummpy mappings for unassigned addresses. Otherwise 67b5f3c6ffSJason King * multiple sections with unassigned addresses will appear to overlap and 68b5f3c6ffSJason King * mess up symbol resolution (which uses the virtual address). 69b5f3c6ffSJason King */ 70b5f3c6ffSJason King typedef struct dis_shnmap { 71b5f3c6ffSJason King const char *dm_name; /* name of section */ 72b5f3c6ffSJason King uint64_t dm_start; /* virtual address of section */ 73b5f3c6ffSJason King size_t dm_length; /* address length */ 74b5f3c6ffSJason King boolean_t dm_mapped; /* did we assign the mapping */ 75b5f3c6ffSJason King } dis_shnmap_t; 76b5f3c6ffSJason King 77b5f3c6ffSJason King /* 78dc0093f4Seschrock * Target data structure. This structure keeps track of the ELF file 79dc0093f4Seschrock * information, a few bits of pre-processed section index information, and 80dc0093f4Seschrock * sorted versions of the symbol table. We also keep track of the last symbol 81dc0093f4Seschrock * looked up, as the majority of lookups remain within the same symbol. 82dc0093f4Seschrock */ 83dc0093f4Seschrock struct dis_tgt { 84dc0093f4Seschrock Elf *dt_elf; /* libelf handle */ 85dc0093f4Seschrock Elf *dt_elf_root; /* main libelf handle (for archives) */ 86dc0093f4Seschrock const char *dt_filename; /* name of file */ 87dc0093f4Seschrock int dt_fd; /* underlying file descriptor */ 88dc0093f4Seschrock size_t dt_shstrndx; /* section index of .shstrtab */ 89dc0093f4Seschrock size_t dt_symidx; /* section index of symbol table */ 90dc0093f4Seschrock sym_entry_t *dt_symcache; /* last symbol looked up */ 91dc0093f4Seschrock sym_entry_t *dt_symtab; /* sorted symbol table */ 92dc0093f4Seschrock int dt_symcount; /* # of symbol table entries */ 93dc0093f4Seschrock struct dis_tgt *dt_next; /* next target (for archives) */ 94dc0093f4Seschrock Elf_Arhdr *dt_arhdr; /* archive header (for archives) */ 95b5f3c6ffSJason King dis_shnmap_t *dt_shnmap; /* section address map */ 96b5f3c6ffSJason King size_t dt_shncount; /* # of sections in target */ 97dc0093f4Seschrock }; 98dc0093f4Seschrock 99dc0093f4Seschrock /* 100dc0093f4Seschrock * Function data structure. We resolve the symbol and lookup the associated ELF 101dc0093f4Seschrock * data when building this structure. The offset is calculated based on the 102dc0093f4Seschrock * section's starting address. 103dc0093f4Seschrock */ 104dc0093f4Seschrock struct dis_func { 105dc0093f4Seschrock sym_entry_t *df_sym; /* symbol table reference */ 106dc0093f4Seschrock Elf_Data *df_data; /* associated ELF data */ 107dc0093f4Seschrock size_t df_offset; /* offset within data */ 108dc0093f4Seschrock }; 109dc0093f4Seschrock 110dc0093f4Seschrock /* 111dc0093f4Seschrock * Section data structure. We store the entire section header so that we can 112dc0093f4Seschrock * determine some properties (such as whether or not it contains text) after 113dc0093f4Seschrock * building the structure. 114dc0093f4Seschrock */ 115dc0093f4Seschrock struct dis_scn { 116dc0093f4Seschrock GElf_Shdr ds_shdr; 117dc0093f4Seschrock const char *ds_name; 118dc0093f4Seschrock Elf_Data *ds_data; 119dc0093f4Seschrock }; 120dc0093f4Seschrock 12123a1cceaSRoger A. Faulkner /* Lifted from Psymtab.c, omitting STT_TLS */ 122dc0093f4Seschrock #define DATA_TYPES \ 12323a1cceaSRoger A. Faulkner ((1 << STT_OBJECT) | (1 << STT_FUNC) | (1 << STT_COMMON)) 124dc0093f4Seschrock #define IS_DATA_TYPE(tp) (((1 << (tp)) & DATA_TYPES) != 0) 125dc0093f4Seschrock 126dc0093f4Seschrock /* 127b5f3c6ffSJason King * Save the virtual address range for this section and select the 128b5f3c6ffSJason King * best section to use as the symbol table. We prefer SHT_SYMTAB 129b5f3c6ffSJason King * over SHT_DYNSYM. 130dc0093f4Seschrock */ 131dc0093f4Seschrock /* ARGSUSED */ 132dc0093f4Seschrock static void 133b5f3c6ffSJason King tgt_scn_init(dis_tgt_t *tgt, dis_scn_t *scn, void *data) 134dc0093f4Seschrock { 135dc0093f4Seschrock int *index = data; 136dc0093f4Seschrock 137dc0093f4Seschrock *index += 1; 138dc0093f4Seschrock 139b5f3c6ffSJason King tgt->dt_shnmap[*index].dm_name = scn->ds_name; 140b5f3c6ffSJason King tgt->dt_shnmap[*index].dm_start = scn->ds_shdr.sh_addr; 141b5f3c6ffSJason King tgt->dt_shnmap[*index].dm_length = scn->ds_shdr.sh_size; 142b5f3c6ffSJason King tgt->dt_shnmap[*index].dm_mapped = B_FALSE; 143b5f3c6ffSJason King 144dc0093f4Seschrock /* 145dc0093f4Seschrock * Prefer SHT_SYMTAB over SHT_DYNSYM 146dc0093f4Seschrock */ 147dc0093f4Seschrock if (scn->ds_shdr.sh_type == SHT_DYNSYM && tgt->dt_symidx == 0) 148dc0093f4Seschrock tgt->dt_symidx = *index; 149dc0093f4Seschrock else if (scn->ds_shdr.sh_type == SHT_SYMTAB) 150dc0093f4Seschrock tgt->dt_symidx = *index; 151dc0093f4Seschrock } 152dc0093f4Seschrock 153dc0093f4Seschrock static int 154dc0093f4Seschrock sym_compare(const void *a, const void *b) 155dc0093f4Seschrock { 156dc0093f4Seschrock const sym_entry_t *syma = a; 157dc0093f4Seschrock const sym_entry_t *symb = b; 158dc0093f4Seschrock const char *aname = syma->se_name; 159dc0093f4Seschrock const char *bname = symb->se_name; 160dc0093f4Seschrock 161dc0093f4Seschrock if (syma->se_sym.st_value < symb->se_sym.st_value) 162dc0093f4Seschrock return (-1); 163dc0093f4Seschrock 164dc0093f4Seschrock if (syma->se_sym.st_value > symb->se_sym.st_value) 165dc0093f4Seschrock return (1); 166dc0093f4Seschrock 167dc0093f4Seschrock /* 168dc0093f4Seschrock * Prefer functions over non-functions 169dc0093f4Seschrock */ 170dc0093f4Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) != 171dc0093f4Seschrock GELF_ST_TYPE(symb->se_sym.st_info)) { 172dc0093f4Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) == STT_FUNC) 173dc0093f4Seschrock return (-1); 174dc0093f4Seschrock if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC) 175dc0093f4Seschrock return (1); 176dc0093f4Seschrock } 177dc0093f4Seschrock 178dc0093f4Seschrock /* 179dc0093f4Seschrock * For symbols with the same address and type, we sort them according to 180dc0093f4Seschrock * a hierarchy: 181dc0093f4Seschrock * 182dc0093f4Seschrock * 1. weak symbols (common name) 183dc0093f4Seschrock * 2. global symbols (external name) 184dc0093f4Seschrock * 3. local symbols 185dc0093f4Seschrock */ 186dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) != 187dc0093f4Seschrock GELF_ST_BIND(symb->se_sym.st_info)) { 188dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_WEAK) 189dc0093f4Seschrock return (-1); 190dc0093f4Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK) 191dc0093f4Seschrock return (1); 192dc0093f4Seschrock 193dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL) 194dc0093f4Seschrock return (-1); 195dc0093f4Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL) 196dc0093f4Seschrock return (1); 197dc0093f4Seschrock } 198dc0093f4Seschrock 199dc0093f4Seschrock /* 200dc0093f4Seschrock * As a last resort, if we have multiple symbols of the same type at the 201dc0093f4Seschrock * same address, prefer the version with the fewest leading underscores. 202dc0093f4Seschrock */ 203dc0093f4Seschrock if (aname == NULL) 204dc0093f4Seschrock return (-1); 205dc0093f4Seschrock if (bname == NULL) 206dc0093f4Seschrock return (1); 207dc0093f4Seschrock 208dc0093f4Seschrock while (*aname == '_' && *bname == '_') { 209dc0093f4Seschrock aname++; 210dc0093f4Seschrock bname++; 211dc0093f4Seschrock } 212dc0093f4Seschrock 213dc0093f4Seschrock if (*bname == '_') 214dc0093f4Seschrock return (-1); 215dc0093f4Seschrock if (*aname == '_') 216dc0093f4Seschrock return (1); 217dc0093f4Seschrock 218dc0093f4Seschrock /* 219dc0093f4Seschrock * Prefer the symbol with the smaller size. 220dc0093f4Seschrock */ 221dc0093f4Seschrock if (syma->se_sym.st_size < symb->se_sym.st_size) 222dc0093f4Seschrock return (-1); 223dc0093f4Seschrock if (syma->se_sym.st_size > symb->se_sym.st_size) 224dc0093f4Seschrock return (1); 225dc0093f4Seschrock 226dc0093f4Seschrock /* 227dc0093f4Seschrock * We really do have two identical symbols for some reason. Just report 228dc0093f4Seschrock * them as equal, and to the lucky one go the spoils. 229dc0093f4Seschrock */ 230dc0093f4Seschrock return (0); 231dc0093f4Seschrock } 232dc0093f4Seschrock 233dc0093f4Seschrock /* 234dc0093f4Seschrock * Construct an optimized symbol table sorted by starting address. 235dc0093f4Seschrock */ 236dc0093f4Seschrock static void 237dc0093f4Seschrock construct_symtab(dis_tgt_t *tgt) 238dc0093f4Seschrock { 239dc0093f4Seschrock Elf_Scn *scn; 240dc0093f4Seschrock GElf_Shdr shdr; 241dc0093f4Seschrock Elf_Data *symdata; 242dc0093f4Seschrock int i; 243dc0093f4Seschrock GElf_Word *symshndx = NULL; 244dc0093f4Seschrock int symshndx_size; 245dc0093f4Seschrock sym_entry_t *sym; 246dc0093f4Seschrock sym_entry_t *p_symtab = NULL; 247dc0093f4Seschrock int nsym = 0; /* count of symbols we're not interested in */ 248dc0093f4Seschrock 249dc0093f4Seschrock /* 250dc0093f4Seschrock * Find the symshndx section, if any 251dc0093f4Seschrock */ 252dc0093f4Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL); scn != NULL; 253dc0093f4Seschrock scn = elf_nextscn(tgt->dt_elf, scn)) { 254dc0093f4Seschrock if (gelf_getshdr(scn, &shdr) == NULL) 255dc0093f4Seschrock break; 256dc0093f4Seschrock if (shdr.sh_type == SHT_SYMTAB_SHNDX && 257dc0093f4Seschrock shdr.sh_link == tgt->dt_symidx) { 258dc0093f4Seschrock Elf_Data *data; 259dc0093f4Seschrock 260dc0093f4Seschrock if ((data = elf_getdata(scn, NULL)) != NULL) { 261dc0093f4Seschrock symshndx = (GElf_Word *)data->d_buf; 262dc0093f4Seschrock symshndx_size = data->d_size / 263dc0093f4Seschrock sizeof (GElf_Word); 264dc0093f4Seschrock break; 265dc0093f4Seschrock } 266dc0093f4Seschrock } 267dc0093f4Seschrock } 268dc0093f4Seschrock 269dc0093f4Seschrock if ((scn = elf_getscn(tgt->dt_elf, tgt->dt_symidx)) == NULL) 270dc0093f4Seschrock die("%s: failed to get section information", tgt->dt_filename); 271dc0093f4Seschrock if (gelf_getshdr(scn, &shdr) == NULL) 272dc0093f4Seschrock die("%s: failed to get section header", tgt->dt_filename); 273dc0093f4Seschrock if (shdr.sh_entsize == 0) 274dc0093f4Seschrock die("%s: symbol table has zero size", tgt->dt_filename); 275dc0093f4Seschrock 276dc0093f4Seschrock if ((symdata = elf_getdata(scn, NULL)) == NULL) 277dc0093f4Seschrock die("%s: failed to get symbol table", tgt->dt_filename); 278dc0093f4Seschrock 279dc0093f4Seschrock tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM, 280dc0093f4Seschrock 1, EV_CURRENT); 281dc0093f4Seschrock 282dc0093f4Seschrock p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t)); 283dc0093f4Seschrock 284dc0093f4Seschrock for (i = 0, sym = p_symtab; i < tgt->dt_symcount; i++) { 285dc0093f4Seschrock if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) { 286dc0093f4Seschrock warn("%s: gelf_getsym returned NULL for %d", 287dc0093f4Seschrock tgt->dt_filename, i); 288dc0093f4Seschrock nsym++; 289dc0093f4Seschrock continue; 290dc0093f4Seschrock } 291dc0093f4Seschrock 292dc0093f4Seschrock /* 293dc0093f4Seschrock * We're only interested in data symbols. 294dc0093f4Seschrock */ 295dc0093f4Seschrock if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) { 296dc0093f4Seschrock nsym++; 297dc0093f4Seschrock continue; 298dc0093f4Seschrock } 299dc0093f4Seschrock 300dc0093f4Seschrock if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) { 301dc0093f4Seschrock if (i > symshndx_size) { 302dc0093f4Seschrock warn("%s: bad SHNX_XINDEX %d", 303dc0093f4Seschrock tgt->dt_filename, i); 304dc0093f4Seschrock sym->se_shndx = -1; 305dc0093f4Seschrock } else { 306dc0093f4Seschrock sym->se_shndx = symshndx[i]; 307dc0093f4Seschrock } 308dc0093f4Seschrock } else { 309dc0093f4Seschrock sym->se_shndx = sym->se_sym.st_shndx; 310dc0093f4Seschrock } 311dc0093f4Seschrock 3126e6df3cfSJason King /* Deal with symbols with special section indicies */ 3136e6df3cfSJason King if (sym->se_shndx == SHN_ABS) { 3146e6df3cfSJason King /* 3156e6df3cfSJason King * If st_value == 0, references to these 3166e6df3cfSJason King * symbols in code are modified in situ 3176e6df3cfSJason King * thus we will never attempt to look 3186e6df3cfSJason King * them up. 3196e6df3cfSJason King */ 3206e6df3cfSJason King if (sym->se_sym.st_value == 0) { 3216e6df3cfSJason King /* 3226e6df3cfSJason King * References to these symbols in code 3236e6df3cfSJason King * are modified in situ by the runtime 3246e6df3cfSJason King * linker and no code on disk will ever 3256e6df3cfSJason King * attempt to look them up. 3266e6df3cfSJason King */ 3276e6df3cfSJason King nsym++; 3286e6df3cfSJason King continue; 3296e6df3cfSJason King } else { 3306e6df3cfSJason King /* 3316e6df3cfSJason King * If st_value != 0, (such as examining 3326e6df3cfSJason King * something in /system/object/.../object) 3336e6df3cfSJason King * the values should resolve to a value 3346e6df3cfSJason King * within an existing section (such as 3356e6df3cfSJason King * .data). This also means it never needs 3366e6df3cfSJason King * to have st_value mapped. 3376e6df3cfSJason King */ 3386e6df3cfSJason King sym++; 3396e6df3cfSJason King continue; 3406e6df3cfSJason King } 3416e6df3cfSJason King } 3426e6df3cfSJason King 3436e6df3cfSJason King /* 3446e6df3cfSJason King * Ignore the symbol if it has some other special 3456e6df3cfSJason King * section index 3466e6df3cfSJason King */ 3476e6df3cfSJason King if (sym->se_shndx == SHN_UNDEF || 3486e6df3cfSJason King sym->se_shndx >= SHN_LORESERVE) { 3496e6df3cfSJason King nsym++; 3506e6df3cfSJason King continue; 3516e6df3cfSJason King } 3526e6df3cfSJason King 353dc0093f4Seschrock if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link, 354dc0093f4Seschrock (size_t)sym->se_sym.st_name)) == NULL) { 355dc0093f4Seschrock warn("%s: failed to lookup symbol %d name", 356dc0093f4Seschrock tgt->dt_filename, i); 357dc0093f4Seschrock nsym++; 358dc0093f4Seschrock continue; 359dc0093f4Seschrock } 360dc0093f4Seschrock 361b5f3c6ffSJason King /* 362b5f3c6ffSJason King * If we had to map this section, its symbol value 363b5f3c6ffSJason King * also needs to be mapped. 364b5f3c6ffSJason King */ 365b5f3c6ffSJason King if (tgt->dt_shnmap[sym->se_shndx].dm_mapped) 366b5f3c6ffSJason King sym->se_sym.st_value += 367b5f3c6ffSJason King tgt->dt_shnmap[sym->se_shndx].dm_start; 368b5f3c6ffSJason King 369dc0093f4Seschrock sym++; 370dc0093f4Seschrock } 371dc0093f4Seschrock 372dc0093f4Seschrock tgt->dt_symcount -= nsym; 3737a65609eSjmcp tgt->dt_symtab = realloc(p_symtab, tgt->dt_symcount * 3747a65609eSjmcp sizeof (sym_entry_t)); 375dc0093f4Seschrock 376dc0093f4Seschrock qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t), 377dc0093f4Seschrock sym_compare); 378dc0093f4Seschrock } 379dc0093f4Seschrock 380dc0093f4Seschrock /* 381b5f3c6ffSJason King * Assign virtual address ranges for sections that need it 382b5f3c6ffSJason King */ 383b5f3c6ffSJason King static void 384b5f3c6ffSJason King create_addrmap(dis_tgt_t *tgt) 385b5f3c6ffSJason King { 386b5f3c6ffSJason King uint64_t addr; 387b5f3c6ffSJason King int i; 388b5f3c6ffSJason King 389b5f3c6ffSJason King if (tgt->dt_shnmap == NULL) 390b5f3c6ffSJason King return; 391b5f3c6ffSJason King 392b5f3c6ffSJason King /* find the greatest used address */ 393b5f3c6ffSJason King for (addr = 0, i = 1; i < tgt->dt_shncount; i++) 394b5f3c6ffSJason King if (tgt->dt_shnmap[i].dm_start > addr) 395b5f3c6ffSJason King addr = tgt->dt_shnmap[i].dm_start + 396b5f3c6ffSJason King tgt->dt_shnmap[i].dm_length; 397b5f3c6ffSJason King 398b5f3c6ffSJason King addr = P2ROUNDUP(addr, 0x1000); 399b5f3c6ffSJason King 400b5f3c6ffSJason King /* 401b5f3c6ffSJason King * Assign section a starting address beyond the largest mapped section 402b5f3c6ffSJason King * if no address was given. 403b5f3c6ffSJason King */ 404b5f3c6ffSJason King for (i = 1; i < tgt->dt_shncount; i++) { 405b5f3c6ffSJason King if (tgt->dt_shnmap[i].dm_start != 0) 406b5f3c6ffSJason King continue; 407b5f3c6ffSJason King 408b5f3c6ffSJason King tgt->dt_shnmap[i].dm_start = addr; 409b5f3c6ffSJason King tgt->dt_shnmap[i].dm_mapped = B_TRUE; 410b5f3c6ffSJason King addr = P2ROUNDUP(addr + tgt->dt_shnmap[i].dm_length, 0x1000); 411b5f3c6ffSJason King } 412b5f3c6ffSJason King } 413b5f3c6ffSJason King 414b5f3c6ffSJason King /* 415dc0093f4Seschrock * Create a target backed by an ELF file. 416dc0093f4Seschrock */ 417dc0093f4Seschrock dis_tgt_t * 418dc0093f4Seschrock dis_tgt_create(const char *file) 419dc0093f4Seschrock { 420dc0093f4Seschrock dis_tgt_t *tgt, *current; 421dc0093f4Seschrock int idx; 422dc0093f4Seschrock Elf *elf; 423dc0093f4Seschrock GElf_Ehdr ehdr; 424dc0093f4Seschrock Elf_Arhdr *arhdr = NULL; 425dc0093f4Seschrock int cmd; 426dc0093f4Seschrock 427dc0093f4Seschrock if (elf_version(EV_CURRENT) == EV_NONE) 428dc0093f4Seschrock die("libelf(3ELF) out of date"); 429dc0093f4Seschrock 430dc0093f4Seschrock tgt = safe_malloc(sizeof (dis_tgt_t)); 431dc0093f4Seschrock 432dc0093f4Seschrock if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) { 433dc0093f4Seschrock warn("%s: failed opening file, reason: %s", file, 434dc0093f4Seschrock strerror(errno)); 435dc0093f4Seschrock free(tgt); 436dc0093f4Seschrock return (NULL); 437dc0093f4Seschrock } 438dc0093f4Seschrock 439dc0093f4Seschrock if ((tgt->dt_elf_root = 440dc0093f4Seschrock elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) { 441dc0093f4Seschrock warn("%s: invalid or corrupt ELF file", file); 442dc0093f4Seschrock dis_tgt_destroy(tgt); 443dc0093f4Seschrock return (NULL); 444dc0093f4Seschrock } 445dc0093f4Seschrock 446dc0093f4Seschrock current = tgt; 447dc0093f4Seschrock cmd = ELF_C_READ; 448dc0093f4Seschrock while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) { 44927553b5cSRichard Lowe size_t shnum = 0; 450dc0093f4Seschrock 451dc0093f4Seschrock if (elf_kind(tgt->dt_elf_root) == ELF_K_AR && 452dc0093f4Seschrock (arhdr = elf_getarhdr(elf)) == NULL) { 453dc0093f4Seschrock warn("%s: malformed archive", file); 454dc0093f4Seschrock dis_tgt_destroy(tgt); 455dc0093f4Seschrock return (NULL); 456dc0093f4Seschrock } 457dc0093f4Seschrock 458dc0093f4Seschrock /* 459dc0093f4Seschrock * Make sure that this Elf file is sane 460dc0093f4Seschrock */ 461dc0093f4Seschrock if (gelf_getehdr(elf, &ehdr) == NULL) { 462dc0093f4Seschrock if (arhdr != NULL) { 463dc0093f4Seschrock /* 464dc0093f4Seschrock * For archives, we drive on in the face of bad 465dc0093f4Seschrock * members. The "/" and "//" members are 466dc0093f4Seschrock * special, and should be silently ignored. 467dc0093f4Seschrock */ 468dc0093f4Seschrock if (strcmp(arhdr->ar_name, "/") != 0 && 469dc0093f4Seschrock strcmp(arhdr->ar_name, "//") != 0) 470dc0093f4Seschrock warn("%s[%s]: invalid file type", 471dc0093f4Seschrock file, arhdr->ar_name); 472dc0093f4Seschrock cmd = elf_next(elf); 473dc0093f4Seschrock (void) elf_end(elf); 474dc0093f4Seschrock continue; 475dc0093f4Seschrock } 476dc0093f4Seschrock 477dc0093f4Seschrock warn("%s: invalid file type", file); 478dc0093f4Seschrock dis_tgt_destroy(tgt); 479dc0093f4Seschrock return (NULL); 480dc0093f4Seschrock } 481dc0093f4Seschrock 482dc0093f4Seschrock /* 483dc0093f4Seschrock * If we're seeing a new Elf object, then we have an 484dc0093f4Seschrock * archive. In this case, we create a new target, and chain it 485dc0093f4Seschrock * off the master target. We can later iterate over these 486dc0093f4Seschrock * targets using dis_tgt_next(). 487dc0093f4Seschrock */ 488dc0093f4Seschrock if (current->dt_elf != NULL) { 489dc0093f4Seschrock dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t)); 490dc0093f4Seschrock next->dt_elf_root = tgt->dt_elf_root; 491dc0093f4Seschrock next->dt_fd = -1; 492dc0093f4Seschrock current->dt_next = next; 493dc0093f4Seschrock current = next; 494dc0093f4Seschrock } 495dc0093f4Seschrock current->dt_elf = elf; 496dc0093f4Seschrock current->dt_arhdr = arhdr; 497dc0093f4Seschrock 49862b628a6SAli Bahrami if (elf_getshdrstrndx(elf, ¤t->dt_shstrndx) == -1) { 499dc0093f4Seschrock warn("%s: failed to get section string table for " 500dc0093f4Seschrock "file", file); 501dc0093f4Seschrock dis_tgt_destroy(tgt); 502dc0093f4Seschrock return (NULL); 503dc0093f4Seschrock } 504dc0093f4Seschrock 50527553b5cSRichard Lowe if (elf_getshdrnum(elf, &shnum) == -1) { 50627553b5cSRichard Lowe warn("%s: failed to get number of sections in file", 50727553b5cSRichard Lowe file); 50827553b5cSRichard Lowe dis_tgt_destroy(tgt); 50927553b5cSRichard Lowe return (NULL); 51027553b5cSRichard Lowe } 51127553b5cSRichard Lowe 512b5f3c6ffSJason King current->dt_shnmap = safe_malloc(sizeof (dis_shnmap_t) * 51327553b5cSRichard Lowe shnum); 51427553b5cSRichard Lowe current->dt_shncount = shnum; 515dc0093f4Seschrock 516b5f3c6ffSJason King idx = 0; 517b5f3c6ffSJason King dis_tgt_section_iter(current, tgt_scn_init, &idx); 5186e6df3cfSJason King current->dt_filename = file; 519b5f3c6ffSJason King 520b5f3c6ffSJason King create_addrmap(current); 521dc0093f4Seschrock if (current->dt_symidx != 0) 522dc0093f4Seschrock construct_symtab(current); 523dc0093f4Seschrock 524dc0093f4Seschrock cmd = elf_next(elf); 525dc0093f4Seschrock } 526dc0093f4Seschrock 527dc0093f4Seschrock /* 528dc0093f4Seschrock * Final sanity check. If we had an archive with no members, then bail 529dc0093f4Seschrock * out with a nice message. 530dc0093f4Seschrock */ 531dc0093f4Seschrock if (tgt->dt_elf == NULL) { 532dc0093f4Seschrock warn("%s: empty archive\n", file); 533dc0093f4Seschrock dis_tgt_destroy(tgt); 534dc0093f4Seschrock return (NULL); 535dc0093f4Seschrock } 536dc0093f4Seschrock 537dc0093f4Seschrock return (tgt); 538dc0093f4Seschrock } 539dc0093f4Seschrock 540dc0093f4Seschrock /* 541dc0093f4Seschrock * Return the filename associated with the target. 542dc0093f4Seschrock */ 543dc0093f4Seschrock const char * 544dc0093f4Seschrock dis_tgt_name(dis_tgt_t *tgt) 545dc0093f4Seschrock { 546dc0093f4Seschrock return (tgt->dt_filename); 547dc0093f4Seschrock } 548dc0093f4Seschrock 549dc0093f4Seschrock /* 550dc0093f4Seschrock * Return the archive member name, if any. 551dc0093f4Seschrock */ 552dc0093f4Seschrock const char * 553dc0093f4Seschrock dis_tgt_member(dis_tgt_t *tgt) 554dc0093f4Seschrock { 555dc0093f4Seschrock if (tgt->dt_arhdr) 556dc0093f4Seschrock return (tgt->dt_arhdr->ar_name); 557dc0093f4Seschrock else 558dc0093f4Seschrock return (NULL); 559dc0093f4Seschrock } 560dc0093f4Seschrock 561dc0093f4Seschrock /* 562dc0093f4Seschrock * Return the Elf_Ehdr associated with this target. Needed to determine which 563dc0093f4Seschrock * disassembler to use. 564dc0093f4Seschrock */ 565dc0093f4Seschrock void 566dc0093f4Seschrock dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr) 567dc0093f4Seschrock { 568dc0093f4Seschrock (void) gelf_getehdr(tgt->dt_elf, ehdr); 569dc0093f4Seschrock } 570dc0093f4Seschrock 571dc0093f4Seschrock /* 572dc0093f4Seschrock * Return the next target in the list, if this is an archive. 573dc0093f4Seschrock */ 574dc0093f4Seschrock dis_tgt_t * 575dc0093f4Seschrock dis_tgt_next(dis_tgt_t *tgt) 576dc0093f4Seschrock { 577dc0093f4Seschrock return (tgt->dt_next); 578dc0093f4Seschrock } 579dc0093f4Seschrock 580dc0093f4Seschrock /* 581dc0093f4Seschrock * Destroy a target and free up any associated memory. 582dc0093f4Seschrock */ 583dc0093f4Seschrock void 584dc0093f4Seschrock dis_tgt_destroy(dis_tgt_t *tgt) 585dc0093f4Seschrock { 586dc0093f4Seschrock dis_tgt_t *current, *next; 587dc0093f4Seschrock 588dc0093f4Seschrock current = tgt->dt_next; 589dc0093f4Seschrock while (current != NULL) { 590dc0093f4Seschrock next = current->dt_next; 591dc0093f4Seschrock if (current->dt_elf) 592dc0093f4Seschrock (void) elf_end(current->dt_elf); 593dc0093f4Seschrock if (current->dt_symtab) 594dc0093f4Seschrock free(current->dt_symtab); 595dc0093f4Seschrock free(current); 596dc0093f4Seschrock current = next; 597dc0093f4Seschrock } 598dc0093f4Seschrock 599dc0093f4Seschrock if (tgt->dt_elf) 600dc0093f4Seschrock (void) elf_end(tgt->dt_elf); 601dc0093f4Seschrock if (tgt->dt_elf_root) 602dc0093f4Seschrock (void) elf_end(tgt->dt_elf_root); 603dc0093f4Seschrock 604dc0093f4Seschrock if (tgt->dt_symtab) 605dc0093f4Seschrock free(tgt->dt_symtab); 606dc0093f4Seschrock 607dc0093f4Seschrock free(tgt); 608dc0093f4Seschrock } 609dc0093f4Seschrock 610dc0093f4Seschrock /* 611b5f3c6ffSJason King * Given an address, return the section it is in and set the offset within 612b5f3c6ffSJason King * the section. 613b5f3c6ffSJason King */ 614b5f3c6ffSJason King const char * 615b5f3c6ffSJason King dis_find_section(dis_tgt_t *tgt, uint64_t addr, off_t *offset) 616b5f3c6ffSJason King { 617b5f3c6ffSJason King int i; 618b5f3c6ffSJason King 619b5f3c6ffSJason King for (i = 1; i < tgt->dt_shncount; i++) { 620b5f3c6ffSJason King if ((addr >= tgt->dt_shnmap[i].dm_start) && 621b5f3c6ffSJason King (addr < tgt->dt_shnmap[i].dm_start + 622b5f3c6ffSJason King tgt->dt_shnmap[i].dm_length)) { 623b5f3c6ffSJason King *offset = addr - tgt->dt_shnmap[i].dm_start; 624b5f3c6ffSJason King return (tgt->dt_shnmap[i].dm_name); 625b5f3c6ffSJason King } 626b5f3c6ffSJason King } 627b5f3c6ffSJason King 628b5f3c6ffSJason King *offset = 0; 629b5f3c6ffSJason King return (NULL); 630b5f3c6ffSJason King } 631b5f3c6ffSJason King 632b5f3c6ffSJason King /* 633dc0093f4Seschrock * Given an address, returns the name of the corresponding symbol, as well as 634dc0093f4Seschrock * the offset within that symbol. If no matching symbol is found, then NULL is 635dc0093f4Seschrock * returned. 636dc0093f4Seschrock * 637dc0093f4Seschrock * If 'cache_result' is specified, then we keep track of the resulting symbol. 638dc0093f4Seschrock * This cached result is consulted first on subsequent lookups in order to avoid 639dc0093f4Seschrock * unecessary lookups. This flag should be used for resolving the current PC, 640dc0093f4Seschrock * as the majority of addresses stay within the current function. 641dc0093f4Seschrock */ 642dc0093f4Seschrock const char * 643dc0093f4Seschrock dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result, 644dc0093f4Seschrock size_t *size, int *isfunc) 645dc0093f4Seschrock { 646dc0093f4Seschrock int lo, hi, mid; 647dc0093f4Seschrock sym_entry_t *sym, *osym, *match; 648dc0093f4Seschrock int found; 649dc0093f4Seschrock 650*dc986d9fSRichard Lowe *offset = 0; 651*dc986d9fSRichard Lowe *size = 0; 652*dc986d9fSRichard Lowe if (isfunc != NULL) 653*dc986d9fSRichard Lowe *isfunc = 0; 654*dc986d9fSRichard Lowe 655dc0093f4Seschrock if (tgt->dt_symcache != NULL && 656dc0093f4Seschrock addr >= tgt->dt_symcache->se_sym.st_value && 657dc0093f4Seschrock addr < tgt->dt_symcache->se_sym.st_value + 658dc0093f4Seschrock tgt->dt_symcache->se_sym.st_size) { 659*dc986d9fSRichard Lowe sym = tgt->dt_symcache; 660*dc986d9fSRichard Lowe *offset = addr - sym->se_sym.st_value; 661*dc986d9fSRichard Lowe *size = sym->se_sym.st_size; 662*dc986d9fSRichard Lowe if (isfunc != NULL) 663*dc986d9fSRichard Lowe *isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == 664*dc986d9fSRichard Lowe STT_FUNC); 665*dc986d9fSRichard Lowe return (sym->se_name); 666dc0093f4Seschrock } 667dc0093f4Seschrock 668dc0093f4Seschrock lo = 0; 669dc0093f4Seschrock hi = (tgt->dt_symcount - 1); 670dc0093f4Seschrock found = 0; 671dc0093f4Seschrock match = osym = NULL; 672dc0093f4Seschrock while (lo <= hi) { 673dc0093f4Seschrock mid = (lo + hi) / 2; 674dc0093f4Seschrock 675dc0093f4Seschrock sym = &tgt->dt_symtab[mid]; 676dc0093f4Seschrock 677dc0093f4Seschrock if (addr >= sym->se_sym.st_value && 678dc0093f4Seschrock addr < sym->se_sym.st_value + sym->se_sym.st_size && 679dc0093f4Seschrock (!found || sym->se_sym.st_value > osym->se_sym.st_value)) { 680dc0093f4Seschrock osym = sym; 681dc0093f4Seschrock found = 1; 682dc0093f4Seschrock } else if (addr == sym->se_sym.st_value) { 683dc0093f4Seschrock /* 684dc0093f4Seschrock * Particularly for .plt objects, it's possible to have 685dc0093f4Seschrock * a zero sized object. We want to return this, but we 686dc0093f4Seschrock * want it to be a last resort. 687dc0093f4Seschrock */ 688dc0093f4Seschrock match = sym; 689dc0093f4Seschrock } 690dc0093f4Seschrock 691dc0093f4Seschrock if (addr < sym->se_sym.st_value) 692dc0093f4Seschrock hi = mid - 1; 693dc0093f4Seschrock else 694dc0093f4Seschrock lo = mid + 1; 695dc0093f4Seschrock } 696dc0093f4Seschrock 697dc0093f4Seschrock if (!found) { 698dc0093f4Seschrock if (match) 699dc0093f4Seschrock osym = match; 700dc0093f4Seschrock else 701dc0093f4Seschrock return (NULL); 702dc0093f4Seschrock } 703dc0093f4Seschrock 704dc0093f4Seschrock /* 705dc0093f4Seschrock * Walk backwards to find the best match. 706dc0093f4Seschrock */ 707dc0093f4Seschrock do { 708dc0093f4Seschrock sym = osym; 709dc0093f4Seschrock 710dc0093f4Seschrock if (osym == tgt->dt_symtab) 711dc0093f4Seschrock break; 712dc0093f4Seschrock 713dc0093f4Seschrock osym = osym - 1; 714dc0093f4Seschrock } while ((sym->se_sym.st_value == osym->se_sym.st_value) && 715dc0093f4Seschrock (addr >= osym->se_sym.st_value) && 716dc0093f4Seschrock (addr < osym->se_sym.st_value + osym->se_sym.st_size)); 717dc0093f4Seschrock 718dc0093f4Seschrock if (cache_result) 719dc0093f4Seschrock tgt->dt_symcache = sym; 720dc0093f4Seschrock 721dc0093f4Seschrock *offset = addr - sym->se_sym.st_value; 722dc0093f4Seschrock *size = sym->se_sym.st_size; 723dc0093f4Seschrock if (isfunc) 724dc0093f4Seschrock *isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC); 725dc0093f4Seschrock 726dc0093f4Seschrock return (sym->se_name); 727dc0093f4Seschrock } 728dc0093f4Seschrock 729dc0093f4Seschrock /* 730dc0093f4Seschrock * Given an address, return the starting offset of the next symbol in the file. 7316e6df3cfSJason King * Only needed on variable length instruction architectures. 732dc0093f4Seschrock */ 733dc0093f4Seschrock off_t 734dc0093f4Seschrock dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr) 735dc0093f4Seschrock { 736b5f3c6ffSJason King sym_entry_t *sym; 737dc0093f4Seschrock 738edd4ab01SRichard Lowe sym = (tgt->dt_symcache != NULL) ? tgt->dt_symcache : tgt->dt_symtab; 739edd4ab01SRichard Lowe 740edd4ab01SRichard Lowe while (sym != (tgt->dt_symtab + tgt->dt_symcount)) { 741b5f3c6ffSJason King if (sym->se_sym.st_value >= addr) 742dc0093f4Seschrock return (sym->se_sym.st_value - addr); 743edd4ab01SRichard Lowe sym++; 744dc0093f4Seschrock } 745dc0093f4Seschrock 746b5f3c6ffSJason King return (0); 747b5f3c6ffSJason King } 748b5f3c6ffSJason King 749dc0093f4Seschrock /* 750dc0093f4Seschrock * Iterate over all sections in the target, executing the given callback for 751dc0093f4Seschrock * each. 752dc0093f4Seschrock */ 753dc0093f4Seschrock void 754dc0093f4Seschrock dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data) 755dc0093f4Seschrock { 756dc0093f4Seschrock dis_scn_t sdata; 757dc0093f4Seschrock Elf_Scn *scn; 758dc0093f4Seschrock int idx; 759dc0093f4Seschrock 760dc0093f4Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL; 761dc0093f4Seschrock scn = elf_nextscn(tgt->dt_elf, scn), idx++) { 762dc0093f4Seschrock 763dc0093f4Seschrock if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) { 764dc0093f4Seschrock warn("%s: failed to get section %d header", 765dc0093f4Seschrock tgt->dt_filename, idx); 766dc0093f4Seschrock continue; 767dc0093f4Seschrock } 768dc0093f4Seschrock 769dc0093f4Seschrock if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx, 770dc0093f4Seschrock sdata.ds_shdr.sh_name)) == NULL) { 771dc0093f4Seschrock warn("%s: failed to get section %d name", 772dc0093f4Seschrock tgt->dt_filename, idx); 773dc0093f4Seschrock continue; 774dc0093f4Seschrock } 775dc0093f4Seschrock 776dc0093f4Seschrock if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) { 777dc0093f4Seschrock warn("%s: failed to get data for section '%s'", 778dc0093f4Seschrock tgt->dt_filename, sdata.ds_name); 779dc0093f4Seschrock continue; 780dc0093f4Seschrock } 781dc0093f4Seschrock 782b5f3c6ffSJason King /* 783b5f3c6ffSJason King * dis_tgt_section_iter is also used before the section map 784b5f3c6ffSJason King * is initialized, so only check when we need to. If the 785b5f3c6ffSJason King * section map is uninitialized, it will return 0 and have 786b5f3c6ffSJason King * no net effect. 787b5f3c6ffSJason King */ 788b5f3c6ffSJason King if (sdata.ds_shdr.sh_addr == 0) 789b5f3c6ffSJason King sdata.ds_shdr.sh_addr = tgt->dt_shnmap[idx].dm_start; 790b5f3c6ffSJason King 791dc0093f4Seschrock func(tgt, &sdata, data); 792dc0093f4Seschrock } 793dc0093f4Seschrock } 794dc0093f4Seschrock 795dc0093f4Seschrock /* 796dc0093f4Seschrock * Return 1 if the given section contains text, 0 otherwise. 797dc0093f4Seschrock */ 798dc0093f4Seschrock int 799dc0093f4Seschrock dis_section_istext(dis_scn_t *scn) 800dc0093f4Seschrock { 801dc0093f4Seschrock return ((scn->ds_shdr.sh_type == SHT_PROGBITS) && 802dc0093f4Seschrock (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR))); 803dc0093f4Seschrock } 804dc0093f4Seschrock 805dc0093f4Seschrock /* 806dc0093f4Seschrock * Return a pointer to the section data. 807dc0093f4Seschrock */ 808dc0093f4Seschrock void * 809dc0093f4Seschrock dis_section_data(dis_scn_t *scn) 810dc0093f4Seschrock { 811dc0093f4Seschrock return (scn->ds_data->d_buf); 812dc0093f4Seschrock } 813dc0093f4Seschrock 814dc0093f4Seschrock /* 815dc0093f4Seschrock * Return the size of the section data. 816dc0093f4Seschrock */ 817dc0093f4Seschrock size_t 818dc0093f4Seschrock dis_section_size(dis_scn_t *scn) 819dc0093f4Seschrock { 820dc0093f4Seschrock return (scn->ds_data->d_size); 821dc0093f4Seschrock } 822dc0093f4Seschrock 823dc0093f4Seschrock /* 824dc0093f4Seschrock * Return the address for the given section. 825dc0093f4Seschrock */ 826dc0093f4Seschrock uint64_t 827dc0093f4Seschrock dis_section_addr(dis_scn_t *scn) 828dc0093f4Seschrock { 829dc0093f4Seschrock return (scn->ds_shdr.sh_addr); 830dc0093f4Seschrock } 831dc0093f4Seschrock 832dc0093f4Seschrock /* 833dc0093f4Seschrock * Return the name of the current section. 834dc0093f4Seschrock */ 835dc0093f4Seschrock const char * 836dc0093f4Seschrock dis_section_name(dis_scn_t *scn) 837dc0093f4Seschrock { 838dc0093f4Seschrock return (scn->ds_name); 839dc0093f4Seschrock } 840dc0093f4Seschrock 841dc0093f4Seschrock /* 842dc0093f4Seschrock * Create an allocated copy of the given section 843dc0093f4Seschrock */ 844dc0093f4Seschrock dis_scn_t * 845dc0093f4Seschrock dis_section_copy(dis_scn_t *scn) 846dc0093f4Seschrock { 847dc0093f4Seschrock dis_scn_t *new; 848dc0093f4Seschrock 849dc0093f4Seschrock new = safe_malloc(sizeof (dis_scn_t)); 850dc0093f4Seschrock (void) memcpy(new, scn, sizeof (dis_scn_t)); 851dc0093f4Seschrock 852dc0093f4Seschrock return (new); 853dc0093f4Seschrock } 854dc0093f4Seschrock 855dc0093f4Seschrock /* 856dc0093f4Seschrock * Free section memory 857dc0093f4Seschrock */ 858dc0093f4Seschrock void 859dc0093f4Seschrock dis_section_free(dis_scn_t *scn) 860dc0093f4Seschrock { 861dc0093f4Seschrock free(scn); 862dc0093f4Seschrock } 863dc0093f4Seschrock 864dc0093f4Seschrock /* 865dc0093f4Seschrock * Iterate over all functions in the target, executing the given callback for 866dc0093f4Seschrock * each one. 867dc0093f4Seschrock */ 868dc0093f4Seschrock void 869dc0093f4Seschrock dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data) 870dc0093f4Seschrock { 871dc0093f4Seschrock int i; 872dc0093f4Seschrock sym_entry_t *sym; 873dc0093f4Seschrock dis_func_t df; 874dc0093f4Seschrock Elf_Scn *scn; 875dc0093f4Seschrock GElf_Shdr shdr; 876dc0093f4Seschrock 877dc0093f4Seschrock for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) { 878dc0093f4Seschrock 879dc0093f4Seschrock /* ignore non-functions */ 880dc0093f4Seschrock if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) || 881161d9479Srie (sym->se_name == NULL) || 882161d9479Srie (sym->se_sym.st_size == 0) || 883161d9479Srie (sym->se_shndx >= SHN_LORESERVE)) 884dc0093f4Seschrock continue; 885dc0093f4Seschrock 886dc0093f4Seschrock /* get the ELF data associated with this function */ 887dc0093f4Seschrock if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL || 888dc0093f4Seschrock gelf_getshdr(scn, &shdr) == NULL || 889dc0093f4Seschrock (df.df_data = elf_getdata(scn, NULL)) == NULL || 890dc0093f4Seschrock df.df_data->d_size == 0) { 891dc0093f4Seschrock warn("%s: failed to read section %d", 892dc0093f4Seschrock tgt->dt_filename, sym->se_shndx); 893dc0093f4Seschrock continue; 894dc0093f4Seschrock } 895dc0093f4Seschrock 896b5f3c6ffSJason King if (tgt->dt_shnmap[sym->se_shndx].dm_mapped) 897b5f3c6ffSJason King shdr.sh_addr = tgt->dt_shnmap[sym->se_shndx].dm_start; 898b5f3c6ffSJason King 899dc0093f4Seschrock /* 900dc0093f4Seschrock * Verify that the address lies within the section that we think 901dc0093f4Seschrock * it does. 902dc0093f4Seschrock */ 903dc0093f4Seschrock if (sym->se_sym.st_value < shdr.sh_addr || 904dc0093f4Seschrock (sym->se_sym.st_value + sym->se_sym.st_size) > 905dc0093f4Seschrock (shdr.sh_addr + shdr.sh_size)) { 906dc0093f4Seschrock warn("%s: bad section %d for address %p", 907dc0093f4Seschrock tgt->dt_filename, sym->se_sym.st_shndx, 908dc0093f4Seschrock sym->se_sym.st_value); 909dc0093f4Seschrock continue; 910dc0093f4Seschrock } 911dc0093f4Seschrock 912dc0093f4Seschrock df.df_sym = sym; 913dc0093f4Seschrock df.df_offset = sym->se_sym.st_value - shdr.sh_addr; 914dc0093f4Seschrock 915dc0093f4Seschrock func(tgt, &df, data); 916dc0093f4Seschrock } 917dc0093f4Seschrock } 918dc0093f4Seschrock 919dc0093f4Seschrock /* 920dc0093f4Seschrock * Return the data associated with a given function. 921dc0093f4Seschrock */ 922dc0093f4Seschrock void * 923dc0093f4Seschrock dis_function_data(dis_func_t *func) 924dc0093f4Seschrock { 925dc0093f4Seschrock return ((char *)func->df_data->d_buf + func->df_offset); 926dc0093f4Seschrock } 927dc0093f4Seschrock 928dc0093f4Seschrock /* 929dc0093f4Seschrock * Return the size of a function. 930dc0093f4Seschrock */ 931dc0093f4Seschrock size_t 932dc0093f4Seschrock dis_function_size(dis_func_t *func) 933dc0093f4Seschrock { 934dc0093f4Seschrock return (func->df_sym->se_sym.st_size); 935dc0093f4Seschrock } 936dc0093f4Seschrock 937dc0093f4Seschrock /* 938dc0093f4Seschrock * Return the address of a function. 939dc0093f4Seschrock */ 940dc0093f4Seschrock uint64_t 941dc0093f4Seschrock dis_function_addr(dis_func_t *func) 942dc0093f4Seschrock { 943dc0093f4Seschrock return (func->df_sym->se_sym.st_value); 944dc0093f4Seschrock } 945dc0093f4Seschrock 946dc0093f4Seschrock /* 947dc0093f4Seschrock * Return the name of the function 948dc0093f4Seschrock */ 949dc0093f4Seschrock const char * 950dc0093f4Seschrock dis_function_name(dis_func_t *func) 951dc0093f4Seschrock { 952dc0093f4Seschrock return (func->df_sym->se_name); 953dc0093f4Seschrock } 954dc0093f4Seschrock 955dc0093f4Seschrock /* 956dc0093f4Seschrock * Return a copy of a function. 957dc0093f4Seschrock */ 958dc0093f4Seschrock dis_func_t * 959dc0093f4Seschrock dis_function_copy(dis_func_t *func) 960dc0093f4Seschrock { 961dc0093f4Seschrock dis_func_t *new; 962dc0093f4Seschrock 963dc0093f4Seschrock new = safe_malloc(sizeof (dis_func_t)); 964dc0093f4Seschrock (void) memcpy(new, func, sizeof (dis_func_t)); 965dc0093f4Seschrock 966dc0093f4Seschrock return (new); 967dc0093f4Seschrock } 968dc0093f4Seschrock 969dc0093f4Seschrock /* 970dc0093f4Seschrock * Free function memory 971dc0093f4Seschrock */ 972dc0093f4Seschrock void 973dc0093f4Seschrock dis_function_free(dis_func_t *func) 974dc0093f4Seschrock { 975dc0093f4Seschrock free(func); 976dc0093f4Seschrock } 977