11545Seschrock /* 21545Seschrock * CDDL HEADER START 31545Seschrock * 41545Seschrock * The contents of this file are subject to the terms of the 51545Seschrock * Common Development and Distribution License (the "License"). 61545Seschrock * You may not use this file except in compliance with the License. 71545Seschrock * 81545Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91545Seschrock * or http://www.opensolaris.org/os/licensing. 101545Seschrock * See the License for the specific language governing permissions 111545Seschrock * and limitations under the License. 121545Seschrock * 131545Seschrock * When distributing Covered Code, include this CDDL HEADER in each 141545Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151545Seschrock * If applicable, add the following below this CDDL HEADER, with the 161545Seschrock * fields enclosed by brackets "[]" replaced with your own identifying 171545Seschrock * information: Portions Copyright [yyyy] [name of copyright owner] 181545Seschrock * 191545Seschrock * CDDL HEADER END 201545Seschrock */ 211545Seschrock 221545Seschrock /* 231545Seschrock * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 241545Seschrock * Use is subject to license terms. 251545Seschrock */ 261545Seschrock 271545Seschrock #pragma ident "%Z%%M% %I% %E% SMI" 281545Seschrock 291545Seschrock #include <assert.h> 301545Seschrock #include <errno.h> 311545Seschrock #include <fcntl.h> 321545Seschrock #include <gelf.h> 331545Seschrock #include <libelf.h> 341545Seschrock #include <stdlib.h> 351545Seschrock #include <string.h> 361545Seschrock #include <unistd.h> 371545Seschrock 381545Seschrock #include <sys/fcntl.h> 391545Seschrock #include <sys/stat.h> 401545Seschrock 411545Seschrock #include "dis_target.h" 421545Seschrock #include "dis_util.h" 431545Seschrock 441545Seschrock /* 451545Seschrock * Standard ELF disassembler target. 461545Seschrock * 471545Seschrock * We only support disassembly of ELF files, though this target interface could 481545Seschrock * be extended in the future. Each basic type (target, func, section) contains 491545Seschrock * enough information to uniquely identify the location within the file. The 501545Seschrock * interfaces use libelf(3LIB) to do the actual processing of the file. 511545Seschrock */ 521545Seschrock 531545Seschrock /* 541545Seschrock * Symbol table entry type. We maintain our own symbol table sorted by address, 551545Seschrock * with the symbol name already resolved against the ELF symbol table. 561545Seschrock */ 571545Seschrock typedef struct sym_entry { 581545Seschrock GElf_Sym se_sym; /* value of symbol */ 591545Seschrock char *se_name; /* name of symbol */ 601545Seschrock int se_shndx; /* section where symbol is located */ 611545Seschrock } sym_entry_t; 621545Seschrock 631545Seschrock /* 641545Seschrock * Target data structure. This structure keeps track of the ELF file 651545Seschrock * information, a few bits of pre-processed section index information, and 661545Seschrock * sorted versions of the symbol table. We also keep track of the last symbol 671545Seschrock * looked up, as the majority of lookups remain within the same symbol. 681545Seschrock */ 691545Seschrock struct dis_tgt { 701545Seschrock Elf *dt_elf; /* libelf handle */ 711545Seschrock Elf *dt_elf_root; /* main libelf handle (for archives) */ 721545Seschrock const char *dt_filename; /* name of file */ 731545Seschrock int dt_fd; /* underlying file descriptor */ 741545Seschrock size_t dt_shstrndx; /* section index of .shstrtab */ 751545Seschrock size_t dt_symidx; /* section index of symbol table */ 761545Seschrock sym_entry_t *dt_symcache; /* last symbol looked up */ 771545Seschrock sym_entry_t *dt_symtab; /* sorted symbol table */ 781545Seschrock int dt_symcount; /* # of symbol table entries */ 791545Seschrock struct dis_tgt *dt_next; /* next target (for archives) */ 801545Seschrock Elf_Arhdr *dt_arhdr; /* archive header (for archives) */ 811545Seschrock }; 821545Seschrock 831545Seschrock /* 841545Seschrock * Function data structure. We resolve the symbol and lookup the associated ELF 851545Seschrock * data when building this structure. The offset is calculated based on the 861545Seschrock * section's starting address. 871545Seschrock */ 881545Seschrock struct dis_func { 891545Seschrock sym_entry_t *df_sym; /* symbol table reference */ 901545Seschrock Elf_Data *df_data; /* associated ELF data */ 911545Seschrock size_t df_offset; /* offset within data */ 921545Seschrock }; 931545Seschrock 941545Seschrock /* 951545Seschrock * Section data structure. We store the entire section header so that we can 961545Seschrock * determine some properties (such as whether or not it contains text) after 971545Seschrock * building the structure. 981545Seschrock */ 991545Seschrock struct dis_scn { 1001545Seschrock GElf_Shdr ds_shdr; 1011545Seschrock const char *ds_name; 1021545Seschrock Elf_Data *ds_data; 1031545Seschrock }; 1041545Seschrock 1051545Seschrock /* Lifted from Psymtab.c */ 1061545Seschrock #define DATA_TYPES \ 1071545Seschrock ((1 << STT_OBJECT) | (1 << STT_FUNC) | \ 1081545Seschrock (1 << STT_COMMON) | (1 << STT_TLS)) 1091545Seschrock #define IS_DATA_TYPE(tp) (((1 << (tp)) & DATA_TYPES) != 0) 1101545Seschrock 1111545Seschrock /* 1121545Seschrock * Pick out the best symbol to used based on the sections available in the 1131545Seschrock * target. We prefer SHT_SYMTAB over SHT_DYNSYM. 1141545Seschrock */ 1151545Seschrock /* ARGSUSED */ 1161545Seschrock static void 1171545Seschrock get_symtab(dis_tgt_t *tgt, dis_scn_t *scn, void *data) 1181545Seschrock { 1191545Seschrock int *index = data; 1201545Seschrock 1211545Seschrock *index += 1; 1221545Seschrock 1231545Seschrock /* 1241545Seschrock * Prefer SHT_SYMTAB over SHT_DYNSYM 1251545Seschrock */ 1261545Seschrock if (scn->ds_shdr.sh_type == SHT_DYNSYM && tgt->dt_symidx == 0) 1271545Seschrock tgt->dt_symidx = *index; 1281545Seschrock else if (scn->ds_shdr.sh_type == SHT_SYMTAB) 1291545Seschrock tgt->dt_symidx = *index; 1301545Seschrock } 1311545Seschrock 1321545Seschrock static int 1331545Seschrock sym_compare(const void *a, const void *b) 1341545Seschrock { 1351545Seschrock const sym_entry_t *syma = a; 1361545Seschrock const sym_entry_t *symb = b; 1371545Seschrock const char *aname = syma->se_name; 1381545Seschrock const char *bname = symb->se_name; 1391545Seschrock 1401545Seschrock if (syma->se_sym.st_value < symb->se_sym.st_value) 1411545Seschrock return (-1); 1421545Seschrock 1431545Seschrock if (syma->se_sym.st_value > symb->se_sym.st_value) 1441545Seschrock return (1); 1451545Seschrock 1461545Seschrock /* 1471545Seschrock * Prefer functions over non-functions 1481545Seschrock */ 1491545Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) != 1501545Seschrock GELF_ST_TYPE(symb->se_sym.st_info)) { 1511545Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) == STT_FUNC) 1521545Seschrock return (-1); 1531545Seschrock if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC) 1541545Seschrock return (1); 1551545Seschrock } 1561545Seschrock 1571545Seschrock /* 1581545Seschrock * For symbols with the same address and type, we sort them according to 1591545Seschrock * a hierarchy: 1601545Seschrock * 1611545Seschrock * 1. weak symbols (common name) 1621545Seschrock * 2. global symbols (external name) 1631545Seschrock * 3. local symbols 1641545Seschrock */ 1651545Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) != 1661545Seschrock GELF_ST_BIND(symb->se_sym.st_info)) { 1671545Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_WEAK) 1681545Seschrock return (-1); 1691545Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK) 1701545Seschrock return (1); 1711545Seschrock 1721545Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL) 1731545Seschrock return (-1); 1741545Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL) 1751545Seschrock return (1); 1761545Seschrock } 1771545Seschrock 1781545Seschrock /* 1791545Seschrock * As a last resort, if we have multiple symbols of the same type at the 1801545Seschrock * same address, prefer the version with the fewest leading underscores. 1811545Seschrock */ 1821545Seschrock if (aname == NULL) 1831545Seschrock return (-1); 1841545Seschrock if (bname == NULL) 1851545Seschrock return (1); 1861545Seschrock 1871545Seschrock while (*aname == '_' && *bname == '_') { 1881545Seschrock aname++; 1891545Seschrock bname++; 1901545Seschrock } 1911545Seschrock 1921545Seschrock if (*bname == '_') 1931545Seschrock return (-1); 1941545Seschrock if (*aname == '_') 1951545Seschrock return (1); 1961545Seschrock 1971545Seschrock /* 1981545Seschrock * Prefer the symbol with the smaller size. 1991545Seschrock */ 2001545Seschrock if (syma->se_sym.st_size < symb->se_sym.st_size) 2011545Seschrock return (-1); 2021545Seschrock if (syma->se_sym.st_size > symb->se_sym.st_size) 2031545Seschrock return (1); 2041545Seschrock 2051545Seschrock /* 2061545Seschrock * We really do have two identical symbols for some reason. Just report 2071545Seschrock * them as equal, and to the lucky one go the spoils. 2081545Seschrock */ 2091545Seschrock return (0); 2101545Seschrock } 2111545Seschrock 2121545Seschrock /* 2131545Seschrock * Construct an optimized symbol table sorted by starting address. 2141545Seschrock */ 2151545Seschrock static void 2161545Seschrock construct_symtab(dis_tgt_t *tgt) 2171545Seschrock { 2181545Seschrock Elf_Scn *scn; 2191545Seschrock GElf_Shdr shdr; 2201545Seschrock Elf_Data *symdata; 2211545Seschrock int i; 2221545Seschrock GElf_Word *symshndx = NULL; 2231545Seschrock int symshndx_size; 2241545Seschrock sym_entry_t *sym; 2251545Seschrock sym_entry_t *p_symtab = NULL; 2261545Seschrock int nsym = 0; /* count of symbols we're not interested in */ 2271545Seschrock 2281545Seschrock /* 2291545Seschrock * Find the symshndx section, if any 2301545Seschrock */ 2311545Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL); scn != NULL; 2321545Seschrock scn = elf_nextscn(tgt->dt_elf, scn)) { 2331545Seschrock if (gelf_getshdr(scn, &shdr) == NULL) 2341545Seschrock break; 2351545Seschrock if (shdr.sh_type == SHT_SYMTAB_SHNDX && 2361545Seschrock shdr.sh_link == tgt->dt_symidx) { 2371545Seschrock Elf_Data *data; 2381545Seschrock 2391545Seschrock if ((data = elf_getdata(scn, NULL)) != NULL) { 2401545Seschrock symshndx = (GElf_Word *)data->d_buf; 2411545Seschrock symshndx_size = data->d_size / 2421545Seschrock sizeof (GElf_Word); 2431545Seschrock break; 2441545Seschrock } 2451545Seschrock } 2461545Seschrock } 2471545Seschrock 2481545Seschrock if ((scn = elf_getscn(tgt->dt_elf, tgt->dt_symidx)) == NULL) 2491545Seschrock die("%s: failed to get section information", tgt->dt_filename); 2501545Seschrock if (gelf_getshdr(scn, &shdr) == NULL) 2511545Seschrock die("%s: failed to get section header", tgt->dt_filename); 2521545Seschrock if (shdr.sh_entsize == 0) 2531545Seschrock die("%s: symbol table has zero size", tgt->dt_filename); 2541545Seschrock 2551545Seschrock if ((symdata = elf_getdata(scn, NULL)) == NULL) 2561545Seschrock die("%s: failed to get symbol table", tgt->dt_filename); 2571545Seschrock 2581545Seschrock tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM, 2591545Seschrock 1, EV_CURRENT); 2601545Seschrock 2611545Seschrock p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t)); 2621545Seschrock 2631545Seschrock for (i = 0, sym = p_symtab; i < tgt->dt_symcount; i++) { 2641545Seschrock (void) memset(sym, sizeof (sym_entry_t), 0); 2651545Seschrock if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) { 2661545Seschrock warn("%s: gelf_getsym returned NULL for %d", 2671545Seschrock tgt->dt_filename, i); 2681545Seschrock nsym++; 2691545Seschrock continue; 2701545Seschrock } 2711545Seschrock 2721545Seschrock /* 2731545Seschrock * We're only interested in data symbols. 2741545Seschrock */ 2751545Seschrock if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) { 2761545Seschrock nsym++; 2771545Seschrock continue; 2781545Seschrock } 2791545Seschrock 2801545Seschrock if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) { 2811545Seschrock if (i > symshndx_size) { 2821545Seschrock warn("%s: bad SHNX_XINDEX %d", 2831545Seschrock tgt->dt_filename, i); 2841545Seschrock sym->se_shndx = -1; 2851545Seschrock } else { 2861545Seschrock sym->se_shndx = symshndx[i]; 2871545Seschrock } 2881545Seschrock } else { 2891545Seschrock sym->se_shndx = sym->se_sym.st_shndx; 2901545Seschrock } 2911545Seschrock 2921545Seschrock if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link, 2931545Seschrock (size_t)sym->se_sym.st_name)) == NULL) { 2941545Seschrock warn("%s: failed to lookup symbol %d name", 2951545Seschrock tgt->dt_filename, i); 2961545Seschrock nsym++; 2971545Seschrock continue; 2981545Seschrock } 2991545Seschrock 3001545Seschrock sym++; 3011545Seschrock } 3021545Seschrock 3031545Seschrock tgt->dt_symcount -= nsym; 3041545Seschrock tgt->dt_symtab = realloc(p_symtab, 3051545Seschrock tgt->dt_symcount * sizeof (sym_entry_t)); 3061545Seschrock 3071545Seschrock qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t), 3081545Seschrock sym_compare); 3091545Seschrock } 3101545Seschrock 3111545Seschrock /* 3121545Seschrock * Create a target backed by an ELF file. 3131545Seschrock */ 3141545Seschrock dis_tgt_t * 3151545Seschrock dis_tgt_create(const char *file) 3161545Seschrock { 3171545Seschrock dis_tgt_t *tgt, *current; 3181545Seschrock int idx; 3191545Seschrock Elf *elf; 3201545Seschrock GElf_Ehdr ehdr; 3211545Seschrock Elf_Arhdr *arhdr = NULL; 3221545Seschrock int cmd; 3231545Seschrock 3241545Seschrock if (elf_version(EV_CURRENT) == EV_NONE) 3251545Seschrock die("libelf(3ELF) out of date"); 3261545Seschrock 3271545Seschrock tgt = safe_malloc(sizeof (dis_tgt_t)); 3281545Seschrock 3291545Seschrock if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) { 3301545Seschrock warn("%s: failed opening file, reason: %s", file, 3311545Seschrock strerror(errno)); 3321545Seschrock free(tgt); 3331545Seschrock return (NULL); 3341545Seschrock } 3351545Seschrock 3361545Seschrock if ((tgt->dt_elf_root = 3371545Seschrock elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) { 3381545Seschrock warn("%s: invalid or corrupt ELF file", file); 3391545Seschrock dis_tgt_destroy(tgt); 3401545Seschrock return (NULL); 3411545Seschrock } 3421545Seschrock 3431545Seschrock current = tgt; 3441545Seschrock cmd = ELF_C_READ; 3451545Seschrock while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) { 3461545Seschrock 3471545Seschrock if (elf_kind(tgt->dt_elf_root) == ELF_K_AR && 3481545Seschrock (arhdr = elf_getarhdr(elf)) == NULL) { 3491545Seschrock warn("%s: malformed archive", file); 3501545Seschrock dis_tgt_destroy(tgt); 3511545Seschrock return (NULL); 3521545Seschrock } 3531545Seschrock 3541545Seschrock /* 3551545Seschrock * Make sure that this Elf file is sane 3561545Seschrock */ 3571545Seschrock if (gelf_getehdr(elf, &ehdr) == NULL) { 3581545Seschrock if (arhdr != NULL) { 3591545Seschrock /* 3601545Seschrock * For archives, we drive on in the face of bad 3611545Seschrock * members. The "/" and "//" members are 3621545Seschrock * special, and should be silently ignored. 3631545Seschrock */ 3641545Seschrock if (strcmp(arhdr->ar_name, "/") != 0 && 3651545Seschrock strcmp(arhdr->ar_name, "//") != 0) 3661545Seschrock warn("%s[%s]: invalid file type", 3671545Seschrock file, arhdr->ar_name); 3681545Seschrock cmd = elf_next(elf); 3691545Seschrock (void) elf_end(elf); 3701545Seschrock continue; 3711545Seschrock } 3721545Seschrock 3731545Seschrock warn("%s: invalid file type", file); 3741545Seschrock dis_tgt_destroy(tgt); 3751545Seschrock return (NULL); 3761545Seschrock } 3771545Seschrock 3781545Seschrock /* 3791545Seschrock * If we're seeing a new Elf object, then we have an 3801545Seschrock * archive. In this case, we create a new target, and chain it 3811545Seschrock * off the master target. We can later iterate over these 3821545Seschrock * targets using dis_tgt_next(). 3831545Seschrock */ 3841545Seschrock if (current->dt_elf != NULL) { 3851545Seschrock dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t)); 3861545Seschrock next->dt_elf_root = tgt->dt_elf_root; 3871545Seschrock next->dt_fd = -1; 3881545Seschrock current->dt_next = next; 3891545Seschrock current = next; 3901545Seschrock } 3911545Seschrock current->dt_elf = elf; 3921545Seschrock current->dt_arhdr = arhdr; 3931545Seschrock 3941545Seschrock if (elf_getshstrndx(elf, ¤t->dt_shstrndx) == -1) { 3951545Seschrock warn("%s: failed to get section string table for " 3961545Seschrock "file", file); 3971545Seschrock dis_tgt_destroy(tgt); 3981545Seschrock return (NULL); 3991545Seschrock } 4001545Seschrock 4011545Seschrock idx = 0; 4021545Seschrock dis_tgt_section_iter(current, get_symtab, &idx); 4031545Seschrock 4041545Seschrock if (current->dt_symidx != 0) 4051545Seschrock construct_symtab(current); 4061545Seschrock 4071545Seschrock current->dt_filename = file; 4081545Seschrock 4091545Seschrock cmd = elf_next(elf); 4101545Seschrock } 4111545Seschrock 4121545Seschrock /* 4131545Seschrock * Final sanity check. If we had an archive with no members, then bail 4141545Seschrock * out with a nice message. 4151545Seschrock */ 4161545Seschrock if (tgt->dt_elf == NULL) { 4171545Seschrock warn("%s: empty archive\n", file); 4181545Seschrock dis_tgt_destroy(tgt); 4191545Seschrock return (NULL); 4201545Seschrock } 4211545Seschrock 4221545Seschrock return (tgt); 4231545Seschrock } 4241545Seschrock 4251545Seschrock /* 4261545Seschrock * Return the filename associated with the target. 4271545Seschrock */ 4281545Seschrock const char * 4291545Seschrock dis_tgt_name(dis_tgt_t *tgt) 4301545Seschrock { 4311545Seschrock return (tgt->dt_filename); 4321545Seschrock } 4331545Seschrock 4341545Seschrock /* 4351545Seschrock * Return the archive member name, if any. 4361545Seschrock */ 4371545Seschrock const char * 4381545Seschrock dis_tgt_member(dis_tgt_t *tgt) 4391545Seschrock { 4401545Seschrock if (tgt->dt_arhdr) 4411545Seschrock return (tgt->dt_arhdr->ar_name); 4421545Seschrock else 4431545Seschrock return (NULL); 4441545Seschrock } 4451545Seschrock 4461545Seschrock /* 4471545Seschrock * Return the Elf_Ehdr associated with this target. Needed to determine which 4481545Seschrock * disassembler to use. 4491545Seschrock */ 4501545Seschrock void 4511545Seschrock dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr) 4521545Seschrock { 4531545Seschrock (void) gelf_getehdr(tgt->dt_elf, ehdr); 4541545Seschrock } 4551545Seschrock 4561545Seschrock /* 4571545Seschrock * Return the next target in the list, if this is an archive. 4581545Seschrock */ 4591545Seschrock dis_tgt_t * 4601545Seschrock dis_tgt_next(dis_tgt_t *tgt) 4611545Seschrock { 4621545Seschrock return (tgt->dt_next); 4631545Seschrock } 4641545Seschrock 4651545Seschrock /* 4661545Seschrock * Destroy a target and free up any associated memory. 4671545Seschrock */ 4681545Seschrock void 4691545Seschrock dis_tgt_destroy(dis_tgt_t *tgt) 4701545Seschrock { 4711545Seschrock dis_tgt_t *current, *next; 4721545Seschrock 4731545Seschrock current = tgt->dt_next; 4741545Seschrock while (current != NULL) { 4751545Seschrock next = current->dt_next; 4761545Seschrock if (current->dt_elf) 4771545Seschrock (void) elf_end(current->dt_elf); 4781545Seschrock if (current->dt_symtab) 4791545Seschrock free(current->dt_symtab); 4801545Seschrock free(current); 4811545Seschrock current = next; 4821545Seschrock } 4831545Seschrock 4841545Seschrock if (tgt->dt_elf) 4851545Seschrock (void) elf_end(tgt->dt_elf); 4861545Seschrock if (tgt->dt_elf_root) 4871545Seschrock (void) elf_end(tgt->dt_elf_root); 4881545Seschrock 4891545Seschrock if (tgt->dt_symtab) 4901545Seschrock free(tgt->dt_symtab); 4911545Seschrock 4921545Seschrock free(tgt); 4931545Seschrock } 4941545Seschrock 4951545Seschrock /* 4961545Seschrock * Given an address, returns the name of the corresponding symbol, as well as 4971545Seschrock * the offset within that symbol. If no matching symbol is found, then NULL is 4981545Seschrock * returned. 4991545Seschrock * 5001545Seschrock * If 'cache_result' is specified, then we keep track of the resulting symbol. 5011545Seschrock * This cached result is consulted first on subsequent lookups in order to avoid 5021545Seschrock * unecessary lookups. This flag should be used for resolving the current PC, 5031545Seschrock * as the majority of addresses stay within the current function. 5041545Seschrock */ 5051545Seschrock const char * 5061545Seschrock dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result, 5071545Seschrock size_t *size, int *isfunc) 5081545Seschrock { 5091545Seschrock int lo, hi, mid; 5101545Seschrock sym_entry_t *sym, *osym, *match; 5111545Seschrock int found; 5121545Seschrock 5131545Seschrock if (tgt->dt_symcache != NULL && 5141545Seschrock addr >= tgt->dt_symcache->se_sym.st_value && 5151545Seschrock addr < tgt->dt_symcache->se_sym.st_value + 5161545Seschrock tgt->dt_symcache->se_sym.st_size) { 5171545Seschrock *offset = addr - tgt->dt_symcache->se_sym.st_value; 5181545Seschrock *size = tgt->dt_symcache->se_sym.st_size; 5191545Seschrock return (tgt->dt_symcache->se_name); 5201545Seschrock } 5211545Seschrock 5221545Seschrock lo = 0; 5231545Seschrock hi = (tgt->dt_symcount - 1); 5241545Seschrock found = 0; 5251545Seschrock match = osym = NULL; 5261545Seschrock while (lo <= hi) { 5271545Seschrock mid = (lo + hi) / 2; 5281545Seschrock 5291545Seschrock sym = &tgt->dt_symtab[mid]; 5301545Seschrock 5311545Seschrock if (addr >= sym->se_sym.st_value && 5321545Seschrock addr < sym->se_sym.st_value + sym->se_sym.st_size && 5331545Seschrock (!found || sym->se_sym.st_value > osym->se_sym.st_value)) { 5341545Seschrock osym = sym; 5351545Seschrock found = 1; 5361545Seschrock } else if (addr == sym->se_sym.st_value) { 5371545Seschrock /* 5381545Seschrock * Particularly for .plt objects, it's possible to have 5391545Seschrock * a zero sized object. We want to return this, but we 5401545Seschrock * want it to be a last resort. 5411545Seschrock */ 5421545Seschrock match = sym; 5431545Seschrock } 5441545Seschrock 5451545Seschrock if (addr < sym->se_sym.st_value) 5461545Seschrock hi = mid - 1; 5471545Seschrock else 5481545Seschrock lo = mid + 1; 5491545Seschrock } 5501545Seschrock 5511545Seschrock if (!found) { 5521545Seschrock if (match) 5531545Seschrock osym = match; 5541545Seschrock else 5551545Seschrock return (NULL); 5561545Seschrock } 5571545Seschrock 5581545Seschrock /* 5591545Seschrock * Walk backwards to find the best match. 5601545Seschrock */ 5611545Seschrock do { 5621545Seschrock sym = osym; 5631545Seschrock 5641545Seschrock if (osym == tgt->dt_symtab) 5651545Seschrock break; 5661545Seschrock 5671545Seschrock osym = osym - 1; 5681545Seschrock } while ((sym->se_sym.st_value == osym->se_sym.st_value) && 5691545Seschrock (addr >= osym->se_sym.st_value) && 5701545Seschrock (addr < osym->se_sym.st_value + osym->se_sym.st_size)); 5711545Seschrock 5721545Seschrock if (cache_result) 5731545Seschrock tgt->dt_symcache = sym; 5741545Seschrock 5751545Seschrock *offset = addr - sym->se_sym.st_value; 5761545Seschrock *size = sym->se_sym.st_size; 5771545Seschrock if (isfunc) 5781545Seschrock *isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC); 5791545Seschrock 5801545Seschrock return (sym->se_name); 5811545Seschrock } 5821545Seschrock 5831545Seschrock /* 5841545Seschrock * Given an address, return the starting offset of the next symbol in the file. 5851545Seschrock * Relies on the fact that this is only used when we encounter a bad instruction 5861545Seschrock * in the input stream, so we know that the last symbol looked up will be in the 5871545Seschrock * cache. 5881545Seschrock */ 5891545Seschrock off_t 5901545Seschrock dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr) 5911545Seschrock { 5921545Seschrock sym_entry_t *sym = tgt->dt_symcache; 5931545Seschrock uint64_t start; 5941545Seschrock 5951545Seschrock /* make sure the cached symbol and address are valid */ 5961545Seschrock if (sym == NULL || addr < sym->se_sym.st_value || 5971545Seschrock addr >= sym->se_sym.st_value + sym->se_sym.st_size) 5981545Seschrock return (0); 5991545Seschrock 6001545Seschrock start = sym->se_sym.st_value; 6011545Seschrock 6021545Seschrock /* find the next symbol */ 6031545Seschrock while (sym != tgt->dt_symtab + tgt->dt_symcount && 6041545Seschrock sym->se_sym.st_value == start) 6051545Seschrock sym++; 6061545Seschrock 6071545Seschrock return (sym->se_sym.st_value - addr); 6081545Seschrock } 6091545Seschrock 6101545Seschrock /* 6111545Seschrock * Iterate over all sections in the target, executing the given callback for 6121545Seschrock * each. 6131545Seschrock */ 6141545Seschrock void 6151545Seschrock dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data) 6161545Seschrock { 6171545Seschrock dis_scn_t sdata; 6181545Seschrock Elf_Scn *scn; 6191545Seschrock int idx; 6201545Seschrock 6211545Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL; 6221545Seschrock scn = elf_nextscn(tgt->dt_elf, scn), idx++) { 6231545Seschrock 6241545Seschrock if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) { 6251545Seschrock warn("%s: failed to get section %d header", 6261545Seschrock tgt->dt_filename, idx); 6271545Seschrock continue; 6281545Seschrock } 6291545Seschrock 6301545Seschrock if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx, 6311545Seschrock sdata.ds_shdr.sh_name)) == NULL) { 6321545Seschrock warn("%s: failed to get section %d name", 6331545Seschrock tgt->dt_filename, idx); 6341545Seschrock continue; 6351545Seschrock } 6361545Seschrock 6371545Seschrock if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) { 6381545Seschrock warn("%s: failed to get data for section '%s'", 6391545Seschrock tgt->dt_filename, sdata.ds_name); 6401545Seschrock continue; 6411545Seschrock } 6421545Seschrock 6431545Seschrock func(tgt, &sdata, data); 6441545Seschrock } 6451545Seschrock } 6461545Seschrock 6471545Seschrock /* 6481545Seschrock * Return 1 if the given section contains text, 0 otherwise. 6491545Seschrock */ 6501545Seschrock int 6511545Seschrock dis_section_istext(dis_scn_t *scn) 6521545Seschrock { 6531545Seschrock return ((scn->ds_shdr.sh_type == SHT_PROGBITS) && 6541545Seschrock (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR))); 6551545Seschrock } 6561545Seschrock 6571545Seschrock /* 6581545Seschrock * Return a pointer to the section data. 6591545Seschrock */ 6601545Seschrock void * 6611545Seschrock dis_section_data(dis_scn_t *scn) 6621545Seschrock { 6631545Seschrock return (scn->ds_data->d_buf); 6641545Seschrock } 6651545Seschrock 6661545Seschrock /* 6671545Seschrock * Return the size of the section data. 6681545Seschrock */ 6691545Seschrock size_t 6701545Seschrock dis_section_size(dis_scn_t *scn) 6711545Seschrock { 6721545Seschrock return (scn->ds_data->d_size); 6731545Seschrock } 6741545Seschrock 6751545Seschrock /* 6761545Seschrock * Return the address for the given section. 6771545Seschrock */ 6781545Seschrock uint64_t 6791545Seschrock dis_section_addr(dis_scn_t *scn) 6801545Seschrock { 6811545Seschrock return (scn->ds_shdr.sh_addr); 6821545Seschrock } 6831545Seschrock 6841545Seschrock /* 6851545Seschrock * Return the name of the current section. 6861545Seschrock */ 6871545Seschrock const char * 6881545Seschrock dis_section_name(dis_scn_t *scn) 6891545Seschrock { 6901545Seschrock return (scn->ds_name); 6911545Seschrock } 6921545Seschrock 6931545Seschrock /* 6941545Seschrock * Create an allocated copy of the given section 6951545Seschrock */ 6961545Seschrock dis_scn_t * 6971545Seschrock dis_section_copy(dis_scn_t *scn) 6981545Seschrock { 6991545Seschrock dis_scn_t *new; 7001545Seschrock 7011545Seschrock new = safe_malloc(sizeof (dis_scn_t)); 7021545Seschrock (void) memcpy(new, scn, sizeof (dis_scn_t)); 7031545Seschrock 7041545Seschrock return (new); 7051545Seschrock } 7061545Seschrock 7071545Seschrock /* 7081545Seschrock * Free section memory 7091545Seschrock */ 7101545Seschrock void 7111545Seschrock dis_section_free(dis_scn_t *scn) 7121545Seschrock { 7131545Seschrock free(scn); 7141545Seschrock } 7151545Seschrock 7161545Seschrock /* 7171545Seschrock * Iterate over all functions in the target, executing the given callback for 7181545Seschrock * each one. 7191545Seschrock */ 7201545Seschrock void 7211545Seschrock dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data) 7221545Seschrock { 7231545Seschrock int i; 7241545Seschrock sym_entry_t *sym; 7251545Seschrock dis_func_t df; 7261545Seschrock Elf_Scn *scn; 7271545Seschrock GElf_Shdr shdr; 7281545Seschrock 7291545Seschrock for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) { 7301545Seschrock 7311545Seschrock /* ignore non-functions */ 7321545Seschrock if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) || 733*2355Srie (sym->se_name == NULL) || 734*2355Srie (sym->se_sym.st_size == 0) || 735*2355Srie (sym->se_shndx >= SHN_LORESERVE)) 7361545Seschrock continue; 7371545Seschrock 7381545Seschrock /* get the ELF data associated with this function */ 7391545Seschrock if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL || 7401545Seschrock gelf_getshdr(scn, &shdr) == NULL || 7411545Seschrock (df.df_data = elf_getdata(scn, NULL)) == NULL || 7421545Seschrock df.df_data->d_size == 0) { 7431545Seschrock warn("%s: failed to read section %d", 7441545Seschrock tgt->dt_filename, sym->se_shndx); 7451545Seschrock continue; 7461545Seschrock } 7471545Seschrock 7481545Seschrock /* 7491545Seschrock * Verify that the address lies within the section that we think 7501545Seschrock * it does. 7511545Seschrock */ 7521545Seschrock if (sym->se_sym.st_value < shdr.sh_addr || 7531545Seschrock (sym->se_sym.st_value + sym->se_sym.st_size) > 7541545Seschrock (shdr.sh_addr + shdr.sh_size)) { 7551545Seschrock warn("%s: bad section %d for address %p", 7561545Seschrock tgt->dt_filename, sym->se_sym.st_shndx, 7571545Seschrock sym->se_sym.st_value); 7581545Seschrock continue; 7591545Seschrock } 7601545Seschrock 7611545Seschrock df.df_sym = sym; 7621545Seschrock df.df_offset = sym->se_sym.st_value - shdr.sh_addr; 7631545Seschrock 7641545Seschrock func(tgt, &df, data); 7651545Seschrock } 7661545Seschrock } 7671545Seschrock 7681545Seschrock /* 7691545Seschrock * Return the data associated with a given function. 7701545Seschrock */ 7711545Seschrock void * 7721545Seschrock dis_function_data(dis_func_t *func) 7731545Seschrock { 7741545Seschrock return ((char *)func->df_data->d_buf + func->df_offset); 7751545Seschrock } 7761545Seschrock 7771545Seschrock /* 7781545Seschrock * Return the size of a function. 7791545Seschrock */ 7801545Seschrock size_t 7811545Seschrock dis_function_size(dis_func_t *func) 7821545Seschrock { 7831545Seschrock return (func->df_sym->se_sym.st_size); 7841545Seschrock } 7851545Seschrock 7861545Seschrock /* 7871545Seschrock * Return the address of a function. 7881545Seschrock */ 7891545Seschrock uint64_t 7901545Seschrock dis_function_addr(dis_func_t *func) 7911545Seschrock { 7921545Seschrock return (func->df_sym->se_sym.st_value); 7931545Seschrock } 7941545Seschrock 7951545Seschrock /* 7961545Seschrock * Return the name of the function 7971545Seschrock */ 7981545Seschrock const char * 7991545Seschrock dis_function_name(dis_func_t *func) 8001545Seschrock { 8011545Seschrock return (func->df_sym->se_name); 8021545Seschrock } 8031545Seschrock 8041545Seschrock /* 8051545Seschrock * Return a copy of a function. 8061545Seschrock */ 8071545Seschrock dis_func_t * 8081545Seschrock dis_function_copy(dis_func_t *func) 8091545Seschrock { 8101545Seschrock dis_func_t *new; 8111545Seschrock 8121545Seschrock new = safe_malloc(sizeof (dis_func_t)); 8131545Seschrock (void) memcpy(new, func, sizeof (dis_func_t)); 8141545Seschrock 8151545Seschrock return (new); 8161545Seschrock } 8171545Seschrock 8181545Seschrock /* 8191545Seschrock * Free function memory 8201545Seschrock */ 8211545Seschrock void 8221545Seschrock dis_function_free(dis_func_t *func) 8231545Seschrock { 8241545Seschrock free(func); 8251545Seschrock } 826