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 /* 23*7a65609eSjmcp * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24dc0093f4Seschrock * Use is subject to license terms. 25dc0093f4Seschrock */ 26dc0093f4Seschrock 27dc0093f4Seschrock #pragma ident "%Z%%M% %I% %E% SMI" 28dc0093f4Seschrock 29dc0093f4Seschrock #include <assert.h> 30dc0093f4Seschrock #include <errno.h> 31dc0093f4Seschrock #include <fcntl.h> 32dc0093f4Seschrock #include <gelf.h> 33dc0093f4Seschrock #include <libelf.h> 34dc0093f4Seschrock #include <stdlib.h> 35dc0093f4Seschrock #include <string.h> 36dc0093f4Seschrock #include <unistd.h> 37dc0093f4Seschrock 38dc0093f4Seschrock #include <sys/fcntl.h> 39dc0093f4Seschrock #include <sys/stat.h> 40dc0093f4Seschrock 41dc0093f4Seschrock #include "dis_target.h" 42dc0093f4Seschrock #include "dis_util.h" 43dc0093f4Seschrock 44dc0093f4Seschrock /* 45dc0093f4Seschrock * Standard ELF disassembler target. 46dc0093f4Seschrock * 47dc0093f4Seschrock * We only support disassembly of ELF files, though this target interface could 48dc0093f4Seschrock * be extended in the future. Each basic type (target, func, section) contains 49dc0093f4Seschrock * enough information to uniquely identify the location within the file. The 50dc0093f4Seschrock * interfaces use libelf(3LIB) to do the actual processing of the file. 51dc0093f4Seschrock */ 52dc0093f4Seschrock 53dc0093f4Seschrock /* 54dc0093f4Seschrock * Symbol table entry type. We maintain our own symbol table sorted by address, 55dc0093f4Seschrock * with the symbol name already resolved against the ELF symbol table. 56dc0093f4Seschrock */ 57dc0093f4Seschrock typedef struct sym_entry { 58dc0093f4Seschrock GElf_Sym se_sym; /* value of symbol */ 59dc0093f4Seschrock char *se_name; /* name of symbol */ 60dc0093f4Seschrock int se_shndx; /* section where symbol is located */ 61dc0093f4Seschrock } sym_entry_t; 62dc0093f4Seschrock 63dc0093f4Seschrock /* 64dc0093f4Seschrock * Target data structure. This structure keeps track of the ELF file 65dc0093f4Seschrock * information, a few bits of pre-processed section index information, and 66dc0093f4Seschrock * sorted versions of the symbol table. We also keep track of the last symbol 67dc0093f4Seschrock * looked up, as the majority of lookups remain within the same symbol. 68dc0093f4Seschrock */ 69dc0093f4Seschrock struct dis_tgt { 70dc0093f4Seschrock Elf *dt_elf; /* libelf handle */ 71dc0093f4Seschrock Elf *dt_elf_root; /* main libelf handle (for archives) */ 72dc0093f4Seschrock const char *dt_filename; /* name of file */ 73dc0093f4Seschrock int dt_fd; /* underlying file descriptor */ 74dc0093f4Seschrock size_t dt_shstrndx; /* section index of .shstrtab */ 75dc0093f4Seschrock size_t dt_symidx; /* section index of symbol table */ 76dc0093f4Seschrock sym_entry_t *dt_symcache; /* last symbol looked up */ 77dc0093f4Seschrock sym_entry_t *dt_symtab; /* sorted symbol table */ 78dc0093f4Seschrock int dt_symcount; /* # of symbol table entries */ 79dc0093f4Seschrock struct dis_tgt *dt_next; /* next target (for archives) */ 80dc0093f4Seschrock Elf_Arhdr *dt_arhdr; /* archive header (for archives) */ 81dc0093f4Seschrock }; 82dc0093f4Seschrock 83dc0093f4Seschrock /* 84dc0093f4Seschrock * Function data structure. We resolve the symbol and lookup the associated ELF 85dc0093f4Seschrock * data when building this structure. The offset is calculated based on the 86dc0093f4Seschrock * section's starting address. 87dc0093f4Seschrock */ 88dc0093f4Seschrock struct dis_func { 89dc0093f4Seschrock sym_entry_t *df_sym; /* symbol table reference */ 90dc0093f4Seschrock Elf_Data *df_data; /* associated ELF data */ 91dc0093f4Seschrock size_t df_offset; /* offset within data */ 92dc0093f4Seschrock }; 93dc0093f4Seschrock 94dc0093f4Seschrock /* 95dc0093f4Seschrock * Section data structure. We store the entire section header so that we can 96dc0093f4Seschrock * determine some properties (such as whether or not it contains text) after 97dc0093f4Seschrock * building the structure. 98dc0093f4Seschrock */ 99dc0093f4Seschrock struct dis_scn { 100dc0093f4Seschrock GElf_Shdr ds_shdr; 101dc0093f4Seschrock const char *ds_name; 102dc0093f4Seschrock Elf_Data *ds_data; 103dc0093f4Seschrock }; 104dc0093f4Seschrock 105dc0093f4Seschrock /* Lifted from Psymtab.c */ 106dc0093f4Seschrock #define DATA_TYPES \ 107dc0093f4Seschrock ((1 << STT_OBJECT) | (1 << STT_FUNC) | \ 108dc0093f4Seschrock (1 << STT_COMMON) | (1 << STT_TLS)) 109dc0093f4Seschrock #define IS_DATA_TYPE(tp) (((1 << (tp)) & DATA_TYPES) != 0) 110dc0093f4Seschrock 111dc0093f4Seschrock /* 112dc0093f4Seschrock * Pick out the best symbol to used based on the sections available in the 113dc0093f4Seschrock * target. We prefer SHT_SYMTAB over SHT_DYNSYM. 114dc0093f4Seschrock */ 115dc0093f4Seschrock /* ARGSUSED */ 116dc0093f4Seschrock static void 117dc0093f4Seschrock get_symtab(dis_tgt_t *tgt, dis_scn_t *scn, void *data) 118dc0093f4Seschrock { 119dc0093f4Seschrock int *index = data; 120dc0093f4Seschrock 121dc0093f4Seschrock *index += 1; 122dc0093f4Seschrock 123dc0093f4Seschrock /* 124dc0093f4Seschrock * Prefer SHT_SYMTAB over SHT_DYNSYM 125dc0093f4Seschrock */ 126dc0093f4Seschrock if (scn->ds_shdr.sh_type == SHT_DYNSYM && tgt->dt_symidx == 0) 127dc0093f4Seschrock tgt->dt_symidx = *index; 128dc0093f4Seschrock else if (scn->ds_shdr.sh_type == SHT_SYMTAB) 129dc0093f4Seschrock tgt->dt_symidx = *index; 130dc0093f4Seschrock } 131dc0093f4Seschrock 132dc0093f4Seschrock static int 133dc0093f4Seschrock sym_compare(const void *a, const void *b) 134dc0093f4Seschrock { 135dc0093f4Seschrock const sym_entry_t *syma = a; 136dc0093f4Seschrock const sym_entry_t *symb = b; 137dc0093f4Seschrock const char *aname = syma->se_name; 138dc0093f4Seschrock const char *bname = symb->se_name; 139dc0093f4Seschrock 140dc0093f4Seschrock if (syma->se_sym.st_value < symb->se_sym.st_value) 141dc0093f4Seschrock return (-1); 142dc0093f4Seschrock 143dc0093f4Seschrock if (syma->se_sym.st_value > symb->se_sym.st_value) 144dc0093f4Seschrock return (1); 145dc0093f4Seschrock 146dc0093f4Seschrock /* 147dc0093f4Seschrock * Prefer functions over non-functions 148dc0093f4Seschrock */ 149dc0093f4Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) != 150dc0093f4Seschrock GELF_ST_TYPE(symb->se_sym.st_info)) { 151dc0093f4Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) == STT_FUNC) 152dc0093f4Seschrock return (-1); 153dc0093f4Seschrock if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC) 154dc0093f4Seschrock return (1); 155dc0093f4Seschrock } 156dc0093f4Seschrock 157dc0093f4Seschrock /* 158dc0093f4Seschrock * For symbols with the same address and type, we sort them according to 159dc0093f4Seschrock * a hierarchy: 160dc0093f4Seschrock * 161dc0093f4Seschrock * 1. weak symbols (common name) 162dc0093f4Seschrock * 2. global symbols (external name) 163dc0093f4Seschrock * 3. local symbols 164dc0093f4Seschrock */ 165dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) != 166dc0093f4Seschrock GELF_ST_BIND(symb->se_sym.st_info)) { 167dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_WEAK) 168dc0093f4Seschrock return (-1); 169dc0093f4Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK) 170dc0093f4Seschrock return (1); 171dc0093f4Seschrock 172dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL) 173dc0093f4Seschrock return (-1); 174dc0093f4Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL) 175dc0093f4Seschrock return (1); 176dc0093f4Seschrock } 177dc0093f4Seschrock 178dc0093f4Seschrock /* 179dc0093f4Seschrock * As a last resort, if we have multiple symbols of the same type at the 180dc0093f4Seschrock * same address, prefer the version with the fewest leading underscores. 181dc0093f4Seschrock */ 182dc0093f4Seschrock if (aname == NULL) 183dc0093f4Seschrock return (-1); 184dc0093f4Seschrock if (bname == NULL) 185dc0093f4Seschrock return (1); 186dc0093f4Seschrock 187dc0093f4Seschrock while (*aname == '_' && *bname == '_') { 188dc0093f4Seschrock aname++; 189dc0093f4Seschrock bname++; 190dc0093f4Seschrock } 191dc0093f4Seschrock 192dc0093f4Seschrock if (*bname == '_') 193dc0093f4Seschrock return (-1); 194dc0093f4Seschrock if (*aname == '_') 195dc0093f4Seschrock return (1); 196dc0093f4Seschrock 197dc0093f4Seschrock /* 198dc0093f4Seschrock * Prefer the symbol with the smaller size. 199dc0093f4Seschrock */ 200dc0093f4Seschrock if (syma->se_sym.st_size < symb->se_sym.st_size) 201dc0093f4Seschrock return (-1); 202dc0093f4Seschrock if (syma->se_sym.st_size > symb->se_sym.st_size) 203dc0093f4Seschrock return (1); 204dc0093f4Seschrock 205dc0093f4Seschrock /* 206dc0093f4Seschrock * We really do have two identical symbols for some reason. Just report 207dc0093f4Seschrock * them as equal, and to the lucky one go the spoils. 208dc0093f4Seschrock */ 209dc0093f4Seschrock return (0); 210dc0093f4Seschrock } 211dc0093f4Seschrock 212dc0093f4Seschrock /* 213dc0093f4Seschrock * Construct an optimized symbol table sorted by starting address. 214dc0093f4Seschrock */ 215dc0093f4Seschrock static void 216dc0093f4Seschrock construct_symtab(dis_tgt_t *tgt) 217dc0093f4Seschrock { 218dc0093f4Seschrock Elf_Scn *scn; 219dc0093f4Seschrock GElf_Shdr shdr; 220dc0093f4Seschrock Elf_Data *symdata; 221dc0093f4Seschrock int i; 222dc0093f4Seschrock GElf_Word *symshndx = NULL; 223dc0093f4Seschrock int symshndx_size; 224dc0093f4Seschrock sym_entry_t *sym; 225dc0093f4Seschrock sym_entry_t *p_symtab = NULL; 226dc0093f4Seschrock int nsym = 0; /* count of symbols we're not interested in */ 227dc0093f4Seschrock 228dc0093f4Seschrock /* 229dc0093f4Seschrock * Find the symshndx section, if any 230dc0093f4Seschrock */ 231dc0093f4Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL); scn != NULL; 232dc0093f4Seschrock scn = elf_nextscn(tgt->dt_elf, scn)) { 233dc0093f4Seschrock if (gelf_getshdr(scn, &shdr) == NULL) 234dc0093f4Seschrock break; 235dc0093f4Seschrock if (shdr.sh_type == SHT_SYMTAB_SHNDX && 236dc0093f4Seschrock shdr.sh_link == tgt->dt_symidx) { 237dc0093f4Seschrock Elf_Data *data; 238dc0093f4Seschrock 239dc0093f4Seschrock if ((data = elf_getdata(scn, NULL)) != NULL) { 240dc0093f4Seschrock symshndx = (GElf_Word *)data->d_buf; 241dc0093f4Seschrock symshndx_size = data->d_size / 242dc0093f4Seschrock sizeof (GElf_Word); 243dc0093f4Seschrock break; 244dc0093f4Seschrock } 245dc0093f4Seschrock } 246dc0093f4Seschrock } 247dc0093f4Seschrock 248dc0093f4Seschrock if ((scn = elf_getscn(tgt->dt_elf, tgt->dt_symidx)) == NULL) 249dc0093f4Seschrock die("%s: failed to get section information", tgt->dt_filename); 250dc0093f4Seschrock if (gelf_getshdr(scn, &shdr) == NULL) 251dc0093f4Seschrock die("%s: failed to get section header", tgt->dt_filename); 252dc0093f4Seschrock if (shdr.sh_entsize == 0) 253dc0093f4Seschrock die("%s: symbol table has zero size", tgt->dt_filename); 254dc0093f4Seschrock 255dc0093f4Seschrock if ((symdata = elf_getdata(scn, NULL)) == NULL) 256dc0093f4Seschrock die("%s: failed to get symbol table", tgt->dt_filename); 257dc0093f4Seschrock 258dc0093f4Seschrock tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM, 259dc0093f4Seschrock 1, EV_CURRENT); 260dc0093f4Seschrock 261dc0093f4Seschrock p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t)); 262dc0093f4Seschrock 263dc0093f4Seschrock for (i = 0, sym = p_symtab; i < tgt->dt_symcount; i++) { 264dc0093f4Seschrock if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) { 265dc0093f4Seschrock warn("%s: gelf_getsym returned NULL for %d", 266dc0093f4Seschrock tgt->dt_filename, i); 267dc0093f4Seschrock nsym++; 268dc0093f4Seschrock continue; 269dc0093f4Seschrock } 270dc0093f4Seschrock 271dc0093f4Seschrock /* 272dc0093f4Seschrock * We're only interested in data symbols. 273dc0093f4Seschrock */ 274dc0093f4Seschrock if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) { 275dc0093f4Seschrock nsym++; 276dc0093f4Seschrock continue; 277dc0093f4Seschrock } 278dc0093f4Seschrock 279dc0093f4Seschrock if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) { 280dc0093f4Seschrock if (i > symshndx_size) { 281dc0093f4Seschrock warn("%s: bad SHNX_XINDEX %d", 282dc0093f4Seschrock tgt->dt_filename, i); 283dc0093f4Seschrock sym->se_shndx = -1; 284dc0093f4Seschrock } else { 285dc0093f4Seschrock sym->se_shndx = symshndx[i]; 286dc0093f4Seschrock } 287dc0093f4Seschrock } else { 288dc0093f4Seschrock sym->se_shndx = sym->se_sym.st_shndx; 289dc0093f4Seschrock } 290dc0093f4Seschrock 291dc0093f4Seschrock if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link, 292dc0093f4Seschrock (size_t)sym->se_sym.st_name)) == NULL) { 293dc0093f4Seschrock warn("%s: failed to lookup symbol %d name", 294dc0093f4Seschrock tgt->dt_filename, i); 295dc0093f4Seschrock nsym++; 296dc0093f4Seschrock continue; 297dc0093f4Seschrock } 298dc0093f4Seschrock 299dc0093f4Seschrock sym++; 300dc0093f4Seschrock } 301dc0093f4Seschrock 302dc0093f4Seschrock tgt->dt_symcount -= nsym; 303*7a65609eSjmcp tgt->dt_symtab = realloc(p_symtab, tgt->dt_symcount * 304*7a65609eSjmcp sizeof (sym_entry_t)); 305dc0093f4Seschrock 306dc0093f4Seschrock qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t), 307dc0093f4Seschrock sym_compare); 308dc0093f4Seschrock } 309dc0093f4Seschrock 310dc0093f4Seschrock /* 311dc0093f4Seschrock * Create a target backed by an ELF file. 312dc0093f4Seschrock */ 313dc0093f4Seschrock dis_tgt_t * 314dc0093f4Seschrock dis_tgt_create(const char *file) 315dc0093f4Seschrock { 316dc0093f4Seschrock dis_tgt_t *tgt, *current; 317dc0093f4Seschrock int idx; 318dc0093f4Seschrock Elf *elf; 319dc0093f4Seschrock GElf_Ehdr ehdr; 320dc0093f4Seschrock Elf_Arhdr *arhdr = NULL; 321dc0093f4Seschrock int cmd; 322dc0093f4Seschrock 323dc0093f4Seschrock if (elf_version(EV_CURRENT) == EV_NONE) 324dc0093f4Seschrock die("libelf(3ELF) out of date"); 325dc0093f4Seschrock 326dc0093f4Seschrock tgt = safe_malloc(sizeof (dis_tgt_t)); 327dc0093f4Seschrock 328dc0093f4Seschrock if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) { 329dc0093f4Seschrock warn("%s: failed opening file, reason: %s", file, 330dc0093f4Seschrock strerror(errno)); 331dc0093f4Seschrock free(tgt); 332dc0093f4Seschrock return (NULL); 333dc0093f4Seschrock } 334dc0093f4Seschrock 335dc0093f4Seschrock if ((tgt->dt_elf_root = 336dc0093f4Seschrock elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) { 337dc0093f4Seschrock warn("%s: invalid or corrupt ELF file", file); 338dc0093f4Seschrock dis_tgt_destroy(tgt); 339dc0093f4Seschrock return (NULL); 340dc0093f4Seschrock } 341dc0093f4Seschrock 342dc0093f4Seschrock current = tgt; 343dc0093f4Seschrock cmd = ELF_C_READ; 344dc0093f4Seschrock while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) { 345dc0093f4Seschrock 346dc0093f4Seschrock if (elf_kind(tgt->dt_elf_root) == ELF_K_AR && 347dc0093f4Seschrock (arhdr = elf_getarhdr(elf)) == NULL) { 348dc0093f4Seschrock warn("%s: malformed archive", file); 349dc0093f4Seschrock dis_tgt_destroy(tgt); 350dc0093f4Seschrock return (NULL); 351dc0093f4Seschrock } 352dc0093f4Seschrock 353dc0093f4Seschrock /* 354dc0093f4Seschrock * Make sure that this Elf file is sane 355dc0093f4Seschrock */ 356dc0093f4Seschrock if (gelf_getehdr(elf, &ehdr) == NULL) { 357dc0093f4Seschrock if (arhdr != NULL) { 358dc0093f4Seschrock /* 359dc0093f4Seschrock * For archives, we drive on in the face of bad 360dc0093f4Seschrock * members. The "/" and "//" members are 361dc0093f4Seschrock * special, and should be silently ignored. 362dc0093f4Seschrock */ 363dc0093f4Seschrock if (strcmp(arhdr->ar_name, "/") != 0 && 364dc0093f4Seschrock strcmp(arhdr->ar_name, "//") != 0) 365dc0093f4Seschrock warn("%s[%s]: invalid file type", 366dc0093f4Seschrock file, arhdr->ar_name); 367dc0093f4Seschrock cmd = elf_next(elf); 368dc0093f4Seschrock (void) elf_end(elf); 369dc0093f4Seschrock continue; 370dc0093f4Seschrock } 371dc0093f4Seschrock 372dc0093f4Seschrock warn("%s: invalid file type", file); 373dc0093f4Seschrock dis_tgt_destroy(tgt); 374dc0093f4Seschrock return (NULL); 375dc0093f4Seschrock } 376dc0093f4Seschrock 377dc0093f4Seschrock /* 378dc0093f4Seschrock * If we're seeing a new Elf object, then we have an 379dc0093f4Seschrock * archive. In this case, we create a new target, and chain it 380dc0093f4Seschrock * off the master target. We can later iterate over these 381dc0093f4Seschrock * targets using dis_tgt_next(). 382dc0093f4Seschrock */ 383dc0093f4Seschrock if (current->dt_elf != NULL) { 384dc0093f4Seschrock dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t)); 385dc0093f4Seschrock next->dt_elf_root = tgt->dt_elf_root; 386dc0093f4Seschrock next->dt_fd = -1; 387dc0093f4Seschrock current->dt_next = next; 388dc0093f4Seschrock current = next; 389dc0093f4Seschrock } 390dc0093f4Seschrock current->dt_elf = elf; 391dc0093f4Seschrock current->dt_arhdr = arhdr; 392dc0093f4Seschrock 393dc0093f4Seschrock if (elf_getshstrndx(elf, ¤t->dt_shstrndx) == -1) { 394dc0093f4Seschrock warn("%s: failed to get section string table for " 395dc0093f4Seschrock "file", file); 396dc0093f4Seschrock dis_tgt_destroy(tgt); 397dc0093f4Seschrock return (NULL); 398dc0093f4Seschrock } 399dc0093f4Seschrock 400dc0093f4Seschrock idx = 0; 401dc0093f4Seschrock dis_tgt_section_iter(current, get_symtab, &idx); 402dc0093f4Seschrock 403dc0093f4Seschrock if (current->dt_symidx != 0) 404dc0093f4Seschrock construct_symtab(current); 405dc0093f4Seschrock 406dc0093f4Seschrock current->dt_filename = file; 407dc0093f4Seschrock 408dc0093f4Seschrock cmd = elf_next(elf); 409dc0093f4Seschrock } 410dc0093f4Seschrock 411dc0093f4Seschrock /* 412dc0093f4Seschrock * Final sanity check. If we had an archive with no members, then bail 413dc0093f4Seschrock * out with a nice message. 414dc0093f4Seschrock */ 415dc0093f4Seschrock if (tgt->dt_elf == NULL) { 416dc0093f4Seschrock warn("%s: empty archive\n", file); 417dc0093f4Seschrock dis_tgt_destroy(tgt); 418dc0093f4Seschrock return (NULL); 419dc0093f4Seschrock } 420dc0093f4Seschrock 421dc0093f4Seschrock return (tgt); 422dc0093f4Seschrock } 423dc0093f4Seschrock 424dc0093f4Seschrock /* 425dc0093f4Seschrock * Return the filename associated with the target. 426dc0093f4Seschrock */ 427dc0093f4Seschrock const char * 428dc0093f4Seschrock dis_tgt_name(dis_tgt_t *tgt) 429dc0093f4Seschrock { 430dc0093f4Seschrock return (tgt->dt_filename); 431dc0093f4Seschrock } 432dc0093f4Seschrock 433dc0093f4Seschrock /* 434dc0093f4Seschrock * Return the archive member name, if any. 435dc0093f4Seschrock */ 436dc0093f4Seschrock const char * 437dc0093f4Seschrock dis_tgt_member(dis_tgt_t *tgt) 438dc0093f4Seschrock { 439dc0093f4Seschrock if (tgt->dt_arhdr) 440dc0093f4Seschrock return (tgt->dt_arhdr->ar_name); 441dc0093f4Seschrock else 442dc0093f4Seschrock return (NULL); 443dc0093f4Seschrock } 444dc0093f4Seschrock 445dc0093f4Seschrock /* 446dc0093f4Seschrock * Return the Elf_Ehdr associated with this target. Needed to determine which 447dc0093f4Seschrock * disassembler to use. 448dc0093f4Seschrock */ 449dc0093f4Seschrock void 450dc0093f4Seschrock dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr) 451dc0093f4Seschrock { 452dc0093f4Seschrock (void) gelf_getehdr(tgt->dt_elf, ehdr); 453dc0093f4Seschrock } 454dc0093f4Seschrock 455dc0093f4Seschrock /* 456dc0093f4Seschrock * Return the next target in the list, if this is an archive. 457dc0093f4Seschrock */ 458dc0093f4Seschrock dis_tgt_t * 459dc0093f4Seschrock dis_tgt_next(dis_tgt_t *tgt) 460dc0093f4Seschrock { 461dc0093f4Seschrock return (tgt->dt_next); 462dc0093f4Seschrock } 463dc0093f4Seschrock 464dc0093f4Seschrock /* 465dc0093f4Seschrock * Destroy a target and free up any associated memory. 466dc0093f4Seschrock */ 467dc0093f4Seschrock void 468dc0093f4Seschrock dis_tgt_destroy(dis_tgt_t *tgt) 469dc0093f4Seschrock { 470dc0093f4Seschrock dis_tgt_t *current, *next; 471dc0093f4Seschrock 472dc0093f4Seschrock current = tgt->dt_next; 473dc0093f4Seschrock while (current != NULL) { 474dc0093f4Seschrock next = current->dt_next; 475dc0093f4Seschrock if (current->dt_elf) 476dc0093f4Seschrock (void) elf_end(current->dt_elf); 477dc0093f4Seschrock if (current->dt_symtab) 478dc0093f4Seschrock free(current->dt_symtab); 479dc0093f4Seschrock free(current); 480dc0093f4Seschrock current = next; 481dc0093f4Seschrock } 482dc0093f4Seschrock 483dc0093f4Seschrock if (tgt->dt_elf) 484dc0093f4Seschrock (void) elf_end(tgt->dt_elf); 485dc0093f4Seschrock if (tgt->dt_elf_root) 486dc0093f4Seschrock (void) elf_end(tgt->dt_elf_root); 487dc0093f4Seschrock 488dc0093f4Seschrock if (tgt->dt_symtab) 489dc0093f4Seschrock free(tgt->dt_symtab); 490dc0093f4Seschrock 491dc0093f4Seschrock free(tgt); 492dc0093f4Seschrock } 493dc0093f4Seschrock 494dc0093f4Seschrock /* 495dc0093f4Seschrock * Given an address, returns the name of the corresponding symbol, as well as 496dc0093f4Seschrock * the offset within that symbol. If no matching symbol is found, then NULL is 497dc0093f4Seschrock * returned. 498dc0093f4Seschrock * 499dc0093f4Seschrock * If 'cache_result' is specified, then we keep track of the resulting symbol. 500dc0093f4Seschrock * This cached result is consulted first on subsequent lookups in order to avoid 501dc0093f4Seschrock * unecessary lookups. This flag should be used for resolving the current PC, 502dc0093f4Seschrock * as the majority of addresses stay within the current function. 503dc0093f4Seschrock */ 504dc0093f4Seschrock const char * 505dc0093f4Seschrock dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result, 506dc0093f4Seschrock size_t *size, int *isfunc) 507dc0093f4Seschrock { 508dc0093f4Seschrock int lo, hi, mid; 509dc0093f4Seschrock sym_entry_t *sym, *osym, *match; 510dc0093f4Seschrock int found; 511dc0093f4Seschrock 512dc0093f4Seschrock if (tgt->dt_symcache != NULL && 513dc0093f4Seschrock addr >= tgt->dt_symcache->se_sym.st_value && 514dc0093f4Seschrock addr < tgt->dt_symcache->se_sym.st_value + 515dc0093f4Seschrock tgt->dt_symcache->se_sym.st_size) { 516dc0093f4Seschrock *offset = addr - tgt->dt_symcache->se_sym.st_value; 517dc0093f4Seschrock *size = tgt->dt_symcache->se_sym.st_size; 518dc0093f4Seschrock return (tgt->dt_symcache->se_name); 519dc0093f4Seschrock } 520dc0093f4Seschrock 521dc0093f4Seschrock lo = 0; 522dc0093f4Seschrock hi = (tgt->dt_symcount - 1); 523dc0093f4Seschrock found = 0; 524dc0093f4Seschrock match = osym = NULL; 525dc0093f4Seschrock while (lo <= hi) { 526dc0093f4Seschrock mid = (lo + hi) / 2; 527dc0093f4Seschrock 528dc0093f4Seschrock sym = &tgt->dt_symtab[mid]; 529dc0093f4Seschrock 530dc0093f4Seschrock if (addr >= sym->se_sym.st_value && 531dc0093f4Seschrock addr < sym->se_sym.st_value + sym->se_sym.st_size && 532dc0093f4Seschrock (!found || sym->se_sym.st_value > osym->se_sym.st_value)) { 533dc0093f4Seschrock osym = sym; 534dc0093f4Seschrock found = 1; 535dc0093f4Seschrock } else if (addr == sym->se_sym.st_value) { 536dc0093f4Seschrock /* 537dc0093f4Seschrock * Particularly for .plt objects, it's possible to have 538dc0093f4Seschrock * a zero sized object. We want to return this, but we 539dc0093f4Seschrock * want it to be a last resort. 540dc0093f4Seschrock */ 541dc0093f4Seschrock match = sym; 542dc0093f4Seschrock } 543dc0093f4Seschrock 544dc0093f4Seschrock if (addr < sym->se_sym.st_value) 545dc0093f4Seschrock hi = mid - 1; 546dc0093f4Seschrock else 547dc0093f4Seschrock lo = mid + 1; 548dc0093f4Seschrock } 549dc0093f4Seschrock 550dc0093f4Seschrock if (!found) { 551dc0093f4Seschrock if (match) 552dc0093f4Seschrock osym = match; 553dc0093f4Seschrock else 554dc0093f4Seschrock return (NULL); 555dc0093f4Seschrock } 556dc0093f4Seschrock 557dc0093f4Seschrock /* 558dc0093f4Seschrock * Walk backwards to find the best match. 559dc0093f4Seschrock */ 560dc0093f4Seschrock do { 561dc0093f4Seschrock sym = osym; 562dc0093f4Seschrock 563dc0093f4Seschrock if (osym == tgt->dt_symtab) 564dc0093f4Seschrock break; 565dc0093f4Seschrock 566dc0093f4Seschrock osym = osym - 1; 567dc0093f4Seschrock } while ((sym->se_sym.st_value == osym->se_sym.st_value) && 568dc0093f4Seschrock (addr >= osym->se_sym.st_value) && 569dc0093f4Seschrock (addr < osym->se_sym.st_value + osym->se_sym.st_size)); 570dc0093f4Seschrock 571dc0093f4Seschrock if (cache_result) 572dc0093f4Seschrock tgt->dt_symcache = sym; 573dc0093f4Seschrock 574dc0093f4Seschrock *offset = addr - sym->se_sym.st_value; 575dc0093f4Seschrock *size = sym->se_sym.st_size; 576dc0093f4Seschrock if (isfunc) 577dc0093f4Seschrock *isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC); 578dc0093f4Seschrock 579dc0093f4Seschrock return (sym->se_name); 580dc0093f4Seschrock } 581dc0093f4Seschrock 582dc0093f4Seschrock /* 583dc0093f4Seschrock * Given an address, return the starting offset of the next symbol in the file. 584dc0093f4Seschrock * Relies on the fact that this is only used when we encounter a bad instruction 585dc0093f4Seschrock * in the input stream, so we know that the last symbol looked up will be in the 586dc0093f4Seschrock * cache. 587dc0093f4Seschrock */ 588dc0093f4Seschrock off_t 589dc0093f4Seschrock dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr) 590dc0093f4Seschrock { 591dc0093f4Seschrock sym_entry_t *sym = tgt->dt_symcache; 592dc0093f4Seschrock uint64_t start; 593dc0093f4Seschrock 594dc0093f4Seschrock /* make sure the cached symbol and address are valid */ 595dc0093f4Seschrock if (sym == NULL || addr < sym->se_sym.st_value || 596dc0093f4Seschrock addr >= sym->se_sym.st_value + sym->se_sym.st_size) 597dc0093f4Seschrock return (0); 598dc0093f4Seschrock 599dc0093f4Seschrock start = sym->se_sym.st_value; 600dc0093f4Seschrock 601dc0093f4Seschrock /* find the next symbol */ 602dc0093f4Seschrock while (sym != tgt->dt_symtab + tgt->dt_symcount && 603dc0093f4Seschrock sym->se_sym.st_value == start) 604dc0093f4Seschrock sym++; 605dc0093f4Seschrock 606dc0093f4Seschrock return (sym->se_sym.st_value - addr); 607dc0093f4Seschrock } 608dc0093f4Seschrock 609dc0093f4Seschrock /* 610dc0093f4Seschrock * Iterate over all sections in the target, executing the given callback for 611dc0093f4Seschrock * each. 612dc0093f4Seschrock */ 613dc0093f4Seschrock void 614dc0093f4Seschrock dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data) 615dc0093f4Seschrock { 616dc0093f4Seschrock dis_scn_t sdata; 617dc0093f4Seschrock Elf_Scn *scn; 618dc0093f4Seschrock int idx; 619dc0093f4Seschrock 620dc0093f4Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL; 621dc0093f4Seschrock scn = elf_nextscn(tgt->dt_elf, scn), idx++) { 622dc0093f4Seschrock 623dc0093f4Seschrock if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) { 624dc0093f4Seschrock warn("%s: failed to get section %d header", 625dc0093f4Seschrock tgt->dt_filename, idx); 626dc0093f4Seschrock continue; 627dc0093f4Seschrock } 628dc0093f4Seschrock 629dc0093f4Seschrock if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx, 630dc0093f4Seschrock sdata.ds_shdr.sh_name)) == NULL) { 631dc0093f4Seschrock warn("%s: failed to get section %d name", 632dc0093f4Seschrock tgt->dt_filename, idx); 633dc0093f4Seschrock continue; 634dc0093f4Seschrock } 635dc0093f4Seschrock 636dc0093f4Seschrock if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) { 637dc0093f4Seschrock warn("%s: failed to get data for section '%s'", 638dc0093f4Seschrock tgt->dt_filename, sdata.ds_name); 639dc0093f4Seschrock continue; 640dc0093f4Seschrock } 641dc0093f4Seschrock 642dc0093f4Seschrock func(tgt, &sdata, data); 643dc0093f4Seschrock } 644dc0093f4Seschrock } 645dc0093f4Seschrock 646dc0093f4Seschrock /* 647dc0093f4Seschrock * Return 1 if the given section contains text, 0 otherwise. 648dc0093f4Seschrock */ 649dc0093f4Seschrock int 650dc0093f4Seschrock dis_section_istext(dis_scn_t *scn) 651dc0093f4Seschrock { 652dc0093f4Seschrock return ((scn->ds_shdr.sh_type == SHT_PROGBITS) && 653dc0093f4Seschrock (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR))); 654dc0093f4Seschrock } 655dc0093f4Seschrock 656dc0093f4Seschrock /* 657dc0093f4Seschrock * Return a pointer to the section data. 658dc0093f4Seschrock */ 659dc0093f4Seschrock void * 660dc0093f4Seschrock dis_section_data(dis_scn_t *scn) 661dc0093f4Seschrock { 662dc0093f4Seschrock return (scn->ds_data->d_buf); 663dc0093f4Seschrock } 664dc0093f4Seschrock 665dc0093f4Seschrock /* 666dc0093f4Seschrock * Return the size of the section data. 667dc0093f4Seschrock */ 668dc0093f4Seschrock size_t 669dc0093f4Seschrock dis_section_size(dis_scn_t *scn) 670dc0093f4Seschrock { 671dc0093f4Seschrock return (scn->ds_data->d_size); 672dc0093f4Seschrock } 673dc0093f4Seschrock 674dc0093f4Seschrock /* 675dc0093f4Seschrock * Return the address for the given section. 676dc0093f4Seschrock */ 677dc0093f4Seschrock uint64_t 678dc0093f4Seschrock dis_section_addr(dis_scn_t *scn) 679dc0093f4Seschrock { 680dc0093f4Seschrock return (scn->ds_shdr.sh_addr); 681dc0093f4Seschrock } 682dc0093f4Seschrock 683dc0093f4Seschrock /* 684dc0093f4Seschrock * Return the name of the current section. 685dc0093f4Seschrock */ 686dc0093f4Seschrock const char * 687dc0093f4Seschrock dis_section_name(dis_scn_t *scn) 688dc0093f4Seschrock { 689dc0093f4Seschrock return (scn->ds_name); 690dc0093f4Seschrock } 691dc0093f4Seschrock 692dc0093f4Seschrock /* 693dc0093f4Seschrock * Create an allocated copy of the given section 694dc0093f4Seschrock */ 695dc0093f4Seschrock dis_scn_t * 696dc0093f4Seschrock dis_section_copy(dis_scn_t *scn) 697dc0093f4Seschrock { 698dc0093f4Seschrock dis_scn_t *new; 699dc0093f4Seschrock 700dc0093f4Seschrock new = safe_malloc(sizeof (dis_scn_t)); 701dc0093f4Seschrock (void) memcpy(new, scn, sizeof (dis_scn_t)); 702dc0093f4Seschrock 703dc0093f4Seschrock return (new); 704dc0093f4Seschrock } 705dc0093f4Seschrock 706dc0093f4Seschrock /* 707dc0093f4Seschrock * Free section memory 708dc0093f4Seschrock */ 709dc0093f4Seschrock void 710dc0093f4Seschrock dis_section_free(dis_scn_t *scn) 711dc0093f4Seschrock { 712dc0093f4Seschrock free(scn); 713dc0093f4Seschrock } 714dc0093f4Seschrock 715dc0093f4Seschrock /* 716dc0093f4Seschrock * Iterate over all functions in the target, executing the given callback for 717dc0093f4Seschrock * each one. 718dc0093f4Seschrock */ 719dc0093f4Seschrock void 720dc0093f4Seschrock dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data) 721dc0093f4Seschrock { 722dc0093f4Seschrock int i; 723dc0093f4Seschrock sym_entry_t *sym; 724dc0093f4Seschrock dis_func_t df; 725dc0093f4Seschrock Elf_Scn *scn; 726dc0093f4Seschrock GElf_Shdr shdr; 727dc0093f4Seschrock 728dc0093f4Seschrock for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) { 729dc0093f4Seschrock 730dc0093f4Seschrock /* ignore non-functions */ 731dc0093f4Seschrock if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) || 732161d9479Srie (sym->se_name == NULL) || 733161d9479Srie (sym->se_sym.st_size == 0) || 734161d9479Srie (sym->se_shndx >= SHN_LORESERVE)) 735dc0093f4Seschrock continue; 736dc0093f4Seschrock 737dc0093f4Seschrock /* get the ELF data associated with this function */ 738dc0093f4Seschrock if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL || 739dc0093f4Seschrock gelf_getshdr(scn, &shdr) == NULL || 740dc0093f4Seschrock (df.df_data = elf_getdata(scn, NULL)) == NULL || 741dc0093f4Seschrock df.df_data->d_size == 0) { 742dc0093f4Seschrock warn("%s: failed to read section %d", 743dc0093f4Seschrock tgt->dt_filename, sym->se_shndx); 744dc0093f4Seschrock continue; 745dc0093f4Seschrock } 746dc0093f4Seschrock 747dc0093f4Seschrock /* 748dc0093f4Seschrock * Verify that the address lies within the section that we think 749dc0093f4Seschrock * it does. 750dc0093f4Seschrock */ 751dc0093f4Seschrock if (sym->se_sym.st_value < shdr.sh_addr || 752dc0093f4Seschrock (sym->se_sym.st_value + sym->se_sym.st_size) > 753dc0093f4Seschrock (shdr.sh_addr + shdr.sh_size)) { 754dc0093f4Seschrock warn("%s: bad section %d for address %p", 755dc0093f4Seschrock tgt->dt_filename, sym->se_sym.st_shndx, 756dc0093f4Seschrock sym->se_sym.st_value); 757dc0093f4Seschrock continue; 758dc0093f4Seschrock } 759dc0093f4Seschrock 760dc0093f4Seschrock df.df_sym = sym; 761dc0093f4Seschrock df.df_offset = sym->se_sym.st_value - shdr.sh_addr; 762dc0093f4Seschrock 763dc0093f4Seschrock func(tgt, &df, data); 764dc0093f4Seschrock } 765dc0093f4Seschrock } 766dc0093f4Seschrock 767dc0093f4Seschrock /* 768dc0093f4Seschrock * Return the data associated with a given function. 769dc0093f4Seschrock */ 770dc0093f4Seschrock void * 771dc0093f4Seschrock dis_function_data(dis_func_t *func) 772dc0093f4Seschrock { 773dc0093f4Seschrock return ((char *)func->df_data->d_buf + func->df_offset); 774dc0093f4Seschrock } 775dc0093f4Seschrock 776dc0093f4Seschrock /* 777dc0093f4Seschrock * Return the size of a function. 778dc0093f4Seschrock */ 779dc0093f4Seschrock size_t 780dc0093f4Seschrock dis_function_size(dis_func_t *func) 781dc0093f4Seschrock { 782dc0093f4Seschrock return (func->df_sym->se_sym.st_size); 783dc0093f4Seschrock } 784dc0093f4Seschrock 785dc0093f4Seschrock /* 786dc0093f4Seschrock * Return the address of a function. 787dc0093f4Seschrock */ 788dc0093f4Seschrock uint64_t 789dc0093f4Seschrock dis_function_addr(dis_func_t *func) 790dc0093f4Seschrock { 791dc0093f4Seschrock return (func->df_sym->se_sym.st_value); 792dc0093f4Seschrock } 793dc0093f4Seschrock 794dc0093f4Seschrock /* 795dc0093f4Seschrock * Return the name of the function 796dc0093f4Seschrock */ 797dc0093f4Seschrock const char * 798dc0093f4Seschrock dis_function_name(dis_func_t *func) 799dc0093f4Seschrock { 800dc0093f4Seschrock return (func->df_sym->se_name); 801dc0093f4Seschrock } 802dc0093f4Seschrock 803dc0093f4Seschrock /* 804dc0093f4Seschrock * Return a copy of a function. 805dc0093f4Seschrock */ 806dc0093f4Seschrock dis_func_t * 807dc0093f4Seschrock dis_function_copy(dis_func_t *func) 808dc0093f4Seschrock { 809dc0093f4Seschrock dis_func_t *new; 810dc0093f4Seschrock 811dc0093f4Seschrock new = safe_malloc(sizeof (dis_func_t)); 812dc0093f4Seschrock (void) memcpy(new, func, sizeof (dis_func_t)); 813dc0093f4Seschrock 814dc0093f4Seschrock return (new); 815dc0093f4Seschrock } 816dc0093f4Seschrock 817dc0093f4Seschrock /* 818dc0093f4Seschrock * Free function memory 819dc0093f4Seschrock */ 820dc0093f4Seschrock void 821dc0093f4Seschrock dis_function_free(dis_func_t *func) 822dc0093f4Seschrock { 823dc0093f4Seschrock free(func); 824dc0093f4Seschrock } 825