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 /* 23e0070315Sdmick * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24dc0093f4Seschrock * Use is subject to license terms. 25b5f3c6ffSJason King * 26b5f3c6ffSJason King * Copyright 2011 Jason King. All rights reserved. 27*f7184619SJoshua M. Clulow * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org> 28dc0093f4Seschrock */ 29dc0093f4Seschrock 30dc0093f4Seschrock #include <ctype.h> 31dc0093f4Seschrock #include <getopt.h> 32dc0093f4Seschrock #include <stdio.h> 33dc0093f4Seschrock #include <stdlib.h> 34dc0093f4Seschrock #include <string.h> 35dc0093f4Seschrock #include <sys/sysmacros.h> 36dc0093f4Seschrock #include <sys/elf_SPARC.h> 37dc0093f4Seschrock 38dc0093f4Seschrock #include <libdisasm.h> 39dc0093f4Seschrock 40dc0093f4Seschrock #include "dis_target.h" 41dc0093f4Seschrock #include "dis_util.h" 42dc0093f4Seschrock #include "dis_list.h" 43dc0093f4Seschrock 44dc0093f4Seschrock int g_demangle; /* Demangle C++ names */ 45dc0093f4Seschrock int g_quiet; /* Quiet mode */ 46dc0093f4Seschrock int g_numeric; /* Numeric mode */ 47dc0093f4Seschrock int g_flags; /* libdisasm language flags */ 48dc0093f4Seschrock int g_doall; /* true if no functions or sections were given */ 49dc0093f4Seschrock 50dc0093f4Seschrock dis_namelist_t *g_funclist; /* list of functions to disassemble, if any */ 51dc0093f4Seschrock dis_namelist_t *g_seclist; /* list of sections to disassemble, if any */ 52dc0093f4Seschrock 53dc0093f4Seschrock /* 54dc0093f4Seschrock * Section options for -d, -D, and -s 55dc0093f4Seschrock */ 56dc0093f4Seschrock #define DIS_DATA_RELATIVE 1 57dc0093f4Seschrock #define DIS_DATA_ABSOLUTE 2 58dc0093f4Seschrock #define DIS_TEXT 3 59dc0093f4Seschrock 60dc0093f4Seschrock /* 61dc0093f4Seschrock * libdisasm callback data. Keeps track of current data (function or section) 62dc0093f4Seschrock * and offset within that data. 63dc0093f4Seschrock */ 64dc0093f4Seschrock typedef struct dis_buffer { 65dc0093f4Seschrock dis_tgt_t *db_tgt; /* current dis target */ 66dc0093f4Seschrock void *db_data; /* function or section data */ 67dc0093f4Seschrock uint64_t db_addr; /* address of function start */ 68dc0093f4Seschrock size_t db_size; /* size of data */ 69dc0093f4Seschrock uint64_t db_nextaddr; /* next address to be read */ 70dc0093f4Seschrock } dis_buffer_t; 71dc0093f4Seschrock 72dc0093f4Seschrock #define MINSYMWIDTH 22 /* Minimum width of symbol portion of line */ 73dc0093f4Seschrock 74dc0093f4Seschrock /* 75dc0093f4Seschrock * Given a symbol+offset as returned by dis_tgt_lookup(), print an appropriately 76dc0093f4Seschrock * formatted symbol, based on the offset and current setttings. 77dc0093f4Seschrock */ 78dc0093f4Seschrock void 79dc0093f4Seschrock getsymname(uint64_t addr, const char *symbol, off_t offset, char *buf, 80dc0093f4Seschrock size_t buflen) 81dc0093f4Seschrock { 82d267098bSdmick if (symbol == NULL || g_numeric) { 83d267098bSdmick if (g_flags & DIS_OCTAL) 84d267098bSdmick (void) snprintf(buf, buflen, "0%llo", addr); 85d267098bSdmick else 86d267098bSdmick (void) snprintf(buf, buflen, "0x%llx", addr); 87d267098bSdmick } else { 88dc0093f4Seschrock if (g_demangle) 89dc0093f4Seschrock symbol = dis_demangle(symbol); 90dc0093f4Seschrock 91dc0093f4Seschrock if (offset == 0) 92dc0093f4Seschrock (void) snprintf(buf, buflen, "%s", symbol); 93dc0093f4Seschrock else if (g_flags & DIS_OCTAL) 94dc0093f4Seschrock (void) snprintf(buf, buflen, "%s+0%o", symbol, offset); 95dc0093f4Seschrock else 96dc0093f4Seschrock (void) snprintf(buf, buflen, "%s+0x%x", symbol, offset); 97dc0093f4Seschrock } 98dc0093f4Seschrock } 99dc0093f4Seschrock 100dc0093f4Seschrock /* 101*f7184619SJoshua M. Clulow * Determine if we are on an architecture with fixed-size instructions, 102*f7184619SJoshua M. Clulow * and if so, what size they are. 103*f7184619SJoshua M. Clulow */ 104*f7184619SJoshua M. Clulow static int 105*f7184619SJoshua M. Clulow insn_size(dis_handle_t *dhp) 106*f7184619SJoshua M. Clulow { 107*f7184619SJoshua M. Clulow int min = dis_min_instrlen(dhp); 108*f7184619SJoshua M. Clulow int max = dis_max_instrlen(dhp); 109*f7184619SJoshua M. Clulow 110*f7184619SJoshua M. Clulow if (min == max) 111*f7184619SJoshua M. Clulow return (min); 112*f7184619SJoshua M. Clulow 113*f7184619SJoshua M. Clulow return (0); 114*f7184619SJoshua M. Clulow } 115*f7184619SJoshua M. Clulow 116*f7184619SJoshua M. Clulow /* 117dc0093f4Seschrock * The main disassembly routine. Given a fixed-sized buffer and starting 118dc0093f4Seschrock * address, disassemble the data using the supplied target and libdisasm handle. 119dc0093f4Seschrock */ 120dc0093f4Seschrock void 121dc0093f4Seschrock dis_data(dis_tgt_t *tgt, dis_handle_t *dhp, uint64_t addr, void *data, 122dc0093f4Seschrock size_t datalen) 123dc0093f4Seschrock { 124dc0093f4Seschrock dis_buffer_t db = { 0 }; 125dc0093f4Seschrock char buf[BUFSIZE]; 126dc0093f4Seschrock char symbuf[BUFSIZE]; 127dc0093f4Seschrock const char *symbol; 128b5f3c6ffSJason King const char *last_symbol; 129dc0093f4Seschrock off_t symoffset; 130dc0093f4Seschrock int i; 131dc0093f4Seschrock int bytesperline; 132dc0093f4Seschrock size_t symsize; 133dc0093f4Seschrock int isfunc; 134dc0093f4Seschrock size_t symwidth = 0; 135*f7184619SJoshua M. Clulow int ret; 136*f7184619SJoshua M. Clulow int insz = insn_size(dhp); 137dc0093f4Seschrock 138dc0093f4Seschrock db.db_tgt = tgt; 139dc0093f4Seschrock db.db_data = data; 140dc0093f4Seschrock db.db_addr = addr; 141dc0093f4Seschrock db.db_size = datalen; 142dc0093f4Seschrock 143dc0093f4Seschrock dis_set_data(dhp, &db); 144dc0093f4Seschrock 145dc0093f4Seschrock if ((bytesperline = dis_max_instrlen(dhp)) > 6) 146dc0093f4Seschrock bytesperline = 6; 147dc0093f4Seschrock 148b5f3c6ffSJason King symbol = NULL; 149b5f3c6ffSJason King 150dc0093f4Seschrock while (addr < db.db_addr + db.db_size) { 151dc0093f4Seschrock 152*f7184619SJoshua M. Clulow ret = dis_disassemble(dhp, addr, buf, BUFSIZE); 153*f7184619SJoshua M. Clulow if (ret != 0 && insz > 0) { 154dc0093f4Seschrock /* 155*f7184619SJoshua M. Clulow * Since we know instructions are fixed size, we 156b5f3c6ffSJason King * always know the address of the next instruction 157dc0093f4Seschrock */ 158b5f3c6ffSJason King (void) snprintf(buf, sizeof (buf), 159b5f3c6ffSJason King "*** invalid opcode ***"); 160*f7184619SJoshua M. Clulow db.db_nextaddr = addr + insz; 161b5f3c6ffSJason King 162*f7184619SJoshua M. Clulow } else if (ret != 0) { 163dc0093f4Seschrock off_t next; 164dc0093f4Seschrock 165dc0093f4Seschrock (void) snprintf(buf, sizeof (buf), 166dc0093f4Seschrock "*** invalid opcode ***"); 167dc0093f4Seschrock 168b5f3c6ffSJason King /* 169b5f3c6ffSJason King * On architectures with variable sized instructions 170b5f3c6ffSJason King * we have no way to figure out where the next 171b5f3c6ffSJason King * instruction starts if we encounter an invalid 172b5f3c6ffSJason King * instruction. Instead we print the rest of the 173b5f3c6ffSJason King * instruction stream as hex until we reach the 174b5f3c6ffSJason King * next valid symbol in the section. 175b5f3c6ffSJason King */ 176dc0093f4Seschrock if ((next = dis_tgt_next_symbol(tgt, addr)) == 0) { 177dc0093f4Seschrock db.db_nextaddr = db.db_addr + db.db_size; 178dc0093f4Seschrock } else { 179dc0093f4Seschrock if (next > db.db_size) 180dc0093f4Seschrock db.db_nextaddr = db.db_addr + 181dc0093f4Seschrock db.db_size; 182dc0093f4Seschrock else 183dc0093f4Seschrock db.db_nextaddr = addr + next; 184dc0093f4Seschrock } 185dc0093f4Seschrock } 186dc0093f4Seschrock 187dc0093f4Seschrock /* 188dc0093f4Seschrock * Print out the line as: 189dc0093f4Seschrock * 190dc0093f4Seschrock * address: bytes text 191dc0093f4Seschrock * 192dc0093f4Seschrock * If there are more than 6 bytes in any given instruction, 193dc0093f4Seschrock * spread the bytes across two lines. We try to get symbolic 194dc0093f4Seschrock * information for the address, but if that fails we print out 195dc0093f4Seschrock * the numeric address instead. 196dc0093f4Seschrock * 197dc0093f4Seschrock * We try to keep the address portion of the text aligned at 198dc0093f4Seschrock * MINSYMWIDTH characters. If we are disassembling a function 199dc0093f4Seschrock * with a long name, this can be annoying. So we pick a width 200dc0093f4Seschrock * based on the maximum width that the current symbol can be. 201dc0093f4Seschrock * This at least produces text aligned within each function. 202dc0093f4Seschrock */ 203b5f3c6ffSJason King last_symbol = symbol; 204dc0093f4Seschrock symbol = dis_tgt_lookup(tgt, addr, &symoffset, 1, &symsize, 205dc0093f4Seschrock &isfunc); 206b5f3c6ffSJason King if (symbol == NULL) { 207b5f3c6ffSJason King symbol = dis_find_section(tgt, addr, &symoffset); 208b5f3c6ffSJason King symsize = symoffset; 209b5f3c6ffSJason King } 210dc0093f4Seschrock 211b5f3c6ffSJason King if (symbol != last_symbol) 212b5f3c6ffSJason King getsymname(addr, symbol, symsize, symbuf, 213b5f3c6ffSJason King sizeof (symbuf)); 214b5f3c6ffSJason King 215b5f3c6ffSJason King symwidth = MAX(symwidth, strlen(symbuf)); 216dc0093f4Seschrock getsymname(addr, symbol, symoffset, symbuf, sizeof (symbuf)); 217dc0093f4Seschrock 218dc0093f4Seschrock /* 219dc0093f4Seschrock * If we've crossed a new function boundary, print out the 220dc0093f4Seschrock * function name on a blank line. 221dc0093f4Seschrock */ 222dc0093f4Seschrock if (!g_quiet && symoffset == 0 && symbol != NULL && isfunc) 223dc0093f4Seschrock (void) printf("%s()\n", symbol); 224dc0093f4Seschrock 225dc0093f4Seschrock (void) printf(" %s:%*s ", symbuf, 226dc0093f4Seschrock symwidth - strlen(symbuf), ""); 227dc0093f4Seschrock 228dc0093f4Seschrock /* print bytes */ 229dc0093f4Seschrock for (i = 0; i < MIN(bytesperline, (db.db_nextaddr - addr)); 230dc0093f4Seschrock i++) { 231dc0093f4Seschrock int byte = *((uchar_t *)data + (addr - db.db_addr) + i); 232dc0093f4Seschrock if (g_flags & DIS_OCTAL) 233dc0093f4Seschrock (void) printf("%03o ", byte); 234dc0093f4Seschrock else 235dc0093f4Seschrock (void) printf("%02x ", byte); 236dc0093f4Seschrock } 237dc0093f4Seschrock 238dc0093f4Seschrock /* trailing spaces for missing bytes */ 239dc0093f4Seschrock for (; i < bytesperline; i++) { 240dc0093f4Seschrock if (g_flags & DIS_OCTAL) 241dc0093f4Seschrock (void) printf(" "); 242dc0093f4Seschrock else 243dc0093f4Seschrock (void) printf(" "); 244dc0093f4Seschrock } 245dc0093f4Seschrock 246dc0093f4Seschrock /* contents of disassembly */ 247dc0093f4Seschrock (void) printf(" %s", buf); 248dc0093f4Seschrock 249dc0093f4Seschrock /* excess bytes that spill over onto subsequent lines */ 250dc0093f4Seschrock for (; i < db.db_nextaddr - addr; i++) { 251dc0093f4Seschrock int byte = *((uchar_t *)data + (addr - db.db_addr) + i); 252dc0093f4Seschrock if (i % bytesperline == 0) 253dc0093f4Seschrock (void) printf("\n %*s ", symwidth, ""); 254dc0093f4Seschrock if (g_flags & DIS_OCTAL) 255dc0093f4Seschrock (void) printf("%03o ", byte); 256dc0093f4Seschrock else 257dc0093f4Seschrock (void) printf("%02x ", byte); 258dc0093f4Seschrock } 259dc0093f4Seschrock 260dc0093f4Seschrock (void) printf("\n"); 261dc0093f4Seschrock 262dc0093f4Seschrock addr = db.db_nextaddr; 263dc0093f4Seschrock } 264dc0093f4Seschrock } 265dc0093f4Seschrock 266dc0093f4Seschrock /* 267dc0093f4Seschrock * libdisasm wrapper around symbol lookup. Invoke the target-specific lookup 268dc0093f4Seschrock * function, and convert the result using getsymname(). 269dc0093f4Seschrock */ 270dc0093f4Seschrock int 271dc0093f4Seschrock do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start, 272dc0093f4Seschrock size_t *symlen) 273dc0093f4Seschrock { 274dc0093f4Seschrock dis_buffer_t *db = data; 275dc0093f4Seschrock const char *symbol; 276dc0093f4Seschrock off_t offset; 277dc0093f4Seschrock size_t size; 278dc0093f4Seschrock 279dc0093f4Seschrock /* 280dc0093f4Seschrock * If NULL symbol is returned, getsymname takes care of 281dc0093f4Seschrock * printing appropriate address in buf instead of symbol. 282dc0093f4Seschrock */ 283dc0093f4Seschrock symbol = dis_tgt_lookup(db->db_tgt, addr, &offset, 0, &size, NULL); 284dc0093f4Seschrock 285dc0093f4Seschrock if (buf != NULL) 286dc0093f4Seschrock getsymname(addr, symbol, offset, buf, buflen); 287dc0093f4Seschrock 288dc0093f4Seschrock if (start != NULL) 289dc0093f4Seschrock *start = addr - offset; 290dc0093f4Seschrock if (symlen != NULL) 291dc0093f4Seschrock *symlen = size; 292dc0093f4Seschrock 293d267098bSdmick if (symbol == NULL) 294d267098bSdmick return (-1); 295d267098bSdmick 296dc0093f4Seschrock return (0); 297dc0093f4Seschrock } 298dc0093f4Seschrock 299dc0093f4Seschrock /* 300dc0093f4Seschrock * libdisasm wrapper around target reading. libdisasm will always read data 301dc0093f4Seschrock * in order, so update our current offset within the buffer appropriately. 302dc0093f4Seschrock * We only support reading from within the current object; libdisasm should 303dc0093f4Seschrock * never ask us to do otherwise. 304dc0093f4Seschrock */ 305dc0093f4Seschrock int 306dc0093f4Seschrock do_read(void *data, uint64_t addr, void *buf, size_t len) 307dc0093f4Seschrock { 308dc0093f4Seschrock dis_buffer_t *db = data; 309dc0093f4Seschrock size_t offset; 310dc0093f4Seschrock 311dc0093f4Seschrock if (addr < db->db_addr || addr >= db->db_addr + db->db_size) 312dc0093f4Seschrock return (-1); 313dc0093f4Seschrock 314dc0093f4Seschrock offset = addr - db->db_addr; 315dc0093f4Seschrock len = MIN(len, db->db_size - offset); 316dc0093f4Seschrock 317dc0093f4Seschrock (void) memcpy(buf, (char *)db->db_data + offset, len); 318dc0093f4Seschrock 319dc0093f4Seschrock db->db_nextaddr = addr + len; 320dc0093f4Seschrock 321dc0093f4Seschrock return (len); 322dc0093f4Seschrock } 323dc0093f4Seschrock 324dc0093f4Seschrock /* 325dc0093f4Seschrock * Routine to dump raw data in a human-readable format. Used by the -d and -D 326dc0093f4Seschrock * options. We model our output after the xxd(1) program, which gives nicely 327dc0093f4Seschrock * formatted output, along with an ASCII translation of the result. 328dc0093f4Seschrock */ 329dc0093f4Seschrock void 330dc0093f4Seschrock dump_data(uint64_t addr, void *data, size_t datalen) 331dc0093f4Seschrock { 332dc0093f4Seschrock uintptr_t curaddr = addr & (~0xf); 333dc0093f4Seschrock uint8_t *bytes = data; 334dc0093f4Seschrock int i; 335dc0093f4Seschrock int width; 336dc0093f4Seschrock 337dc0093f4Seschrock /* 338dc0093f4Seschrock * Determine if the address given to us fits in 32-bit range, in which 339dc0093f4Seschrock * case use a 4-byte width. 340dc0093f4Seschrock */ 341dc0093f4Seschrock if (((addr + datalen) & 0xffffffff00000000ULL) == 0ULL) 342dc0093f4Seschrock width = 8; 343dc0093f4Seschrock else 344dc0093f4Seschrock width = 16; 345dc0093f4Seschrock 346dc0093f4Seschrock while (curaddr < addr + datalen) { 347dc0093f4Seschrock /* 348dc0093f4Seschrock * Display leading address 349dc0093f4Seschrock */ 350dc0093f4Seschrock (void) printf("%0*x: ", width, curaddr); 351dc0093f4Seschrock 352dc0093f4Seschrock /* 353dc0093f4Seschrock * Print out data in two-byte chunks. If the current address 354dc0093f4Seschrock * is before the starting address or after the end of the 355dc0093f4Seschrock * section, print spaces. 356dc0093f4Seschrock */ 357dc0093f4Seschrock for (i = 0; i < 16; i++) { 358dc0093f4Seschrock if (curaddr + i < addr ||curaddr + i >= addr + datalen) 359dc0093f4Seschrock (void) printf(" "); 360dc0093f4Seschrock else 361dc0093f4Seschrock (void) printf("%02x", 362dc0093f4Seschrock bytes[curaddr + i - addr]); 363dc0093f4Seschrock 364dc0093f4Seschrock if (i & 1) 365dc0093f4Seschrock (void) printf(" "); 366dc0093f4Seschrock } 367dc0093f4Seschrock 368dc0093f4Seschrock (void) printf(" "); 369dc0093f4Seschrock 370dc0093f4Seschrock /* 371dc0093f4Seschrock * Print out the ASCII representation 372dc0093f4Seschrock */ 373dc0093f4Seschrock for (i = 0; i < 16; i++) { 374dc0093f4Seschrock if (curaddr + i < addr || 375dc0093f4Seschrock curaddr + i >= addr + datalen) { 376dc0093f4Seschrock (void) printf(" "); 377dc0093f4Seschrock } else { 378dc0093f4Seschrock uint8_t byte = bytes[curaddr + i - addr]; 379dc0093f4Seschrock if (isprint(byte)) 380dc0093f4Seschrock (void) printf("%c", byte); 381dc0093f4Seschrock else 382dc0093f4Seschrock (void) printf("."); 383dc0093f4Seschrock } 384dc0093f4Seschrock } 385dc0093f4Seschrock 386dc0093f4Seschrock (void) printf("\n"); 387dc0093f4Seschrock 388dc0093f4Seschrock curaddr += 16; 389dc0093f4Seschrock } 390dc0093f4Seschrock } 391dc0093f4Seschrock 392dc0093f4Seschrock /* 393dc0093f4Seschrock * Disassemble a section implicitly specified as part of a file. This function 394dc0093f4Seschrock * is called for all sections when no other flags are specified. We ignore any 395dc0093f4Seschrock * data sections, and print out only those sections containing text. 396dc0093f4Seschrock */ 397dc0093f4Seschrock void 398dc0093f4Seschrock dis_text_section(dis_tgt_t *tgt, dis_scn_t *scn, void *data) 399dc0093f4Seschrock { 400dc0093f4Seschrock dis_handle_t *dhp = data; 401dc0093f4Seschrock 402dc0093f4Seschrock /* ignore data sections */ 403dc0093f4Seschrock if (!dis_section_istext(scn)) 404dc0093f4Seschrock return; 405dc0093f4Seschrock 406dc0093f4Seschrock if (!g_quiet) 407dc0093f4Seschrock (void) printf("\nsection %s\n", dis_section_name(scn)); 408dc0093f4Seschrock 409dc0093f4Seschrock dis_data(tgt, dhp, dis_section_addr(scn), dis_section_data(scn), 410dc0093f4Seschrock dis_section_size(scn)); 411dc0093f4Seschrock } 412dc0093f4Seschrock 413dc0093f4Seschrock /* 414dc0093f4Seschrock * Structure passed to dis_named_{section,function} which keeps track of both 415dc0093f4Seschrock * the target and the libdisasm handle. 416dc0093f4Seschrock */ 417dc0093f4Seschrock typedef struct callback_arg { 418dc0093f4Seschrock dis_tgt_t *ca_tgt; 419dc0093f4Seschrock dis_handle_t *ca_handle; 420dc0093f4Seschrock } callback_arg_t; 421dc0093f4Seschrock 422dc0093f4Seschrock /* 423dc0093f4Seschrock * Disassemble a section explicitly named with -s, -d, or -D. The 'type' 424dc0093f4Seschrock * argument contains the type of argument given. Pass the data onto the 425dc0093f4Seschrock * appropriate helper routine. 426dc0093f4Seschrock */ 427dc0093f4Seschrock void 428dc0093f4Seschrock dis_named_section(dis_scn_t *scn, int type, void *data) 429dc0093f4Seschrock { 430dc0093f4Seschrock callback_arg_t *ca = data; 431dc0093f4Seschrock 432dc0093f4Seschrock if (!g_quiet) 433dc0093f4Seschrock (void) printf("\nsection %s\n", dis_section_name(scn)); 434dc0093f4Seschrock 435dc0093f4Seschrock switch (type) { 436dc0093f4Seschrock case DIS_DATA_RELATIVE: 437dc0093f4Seschrock dump_data(0, dis_section_data(scn), dis_section_size(scn)); 438dc0093f4Seschrock break; 439dc0093f4Seschrock case DIS_DATA_ABSOLUTE: 440dc0093f4Seschrock dump_data(dis_section_addr(scn), dis_section_data(scn), 441dc0093f4Seschrock dis_section_size(scn)); 442dc0093f4Seschrock break; 443dc0093f4Seschrock case DIS_TEXT: 444dc0093f4Seschrock dis_data(ca->ca_tgt, ca->ca_handle, dis_section_addr(scn), 445dc0093f4Seschrock dis_section_data(scn), dis_section_size(scn)); 446dc0093f4Seschrock break; 447dc0093f4Seschrock } 448dc0093f4Seschrock } 449dc0093f4Seschrock 450dc0093f4Seschrock /* 451dc0093f4Seschrock * Disassemble a function explicitly specified with '-F'. The 'type' argument 452dc0093f4Seschrock * is unused. 453dc0093f4Seschrock */ 454dc0093f4Seschrock /* ARGSUSED */ 455dc0093f4Seschrock void 456dc0093f4Seschrock dis_named_function(dis_func_t *func, int type, void *data) 457dc0093f4Seschrock { 458dc0093f4Seschrock callback_arg_t *ca = data; 459dc0093f4Seschrock 460dc0093f4Seschrock dis_data(ca->ca_tgt, ca->ca_handle, dis_function_addr(func), 461dc0093f4Seschrock dis_function_data(func), dis_function_size(func)); 462dc0093f4Seschrock } 463dc0093f4Seschrock 464dc0093f4Seschrock /* 465dc0093f4Seschrock * Disassemble a complete file. First, we determine the type of the file based 466dc0093f4Seschrock * on the ELF machine type, and instantiate a version of the disassembler 467dc0093f4Seschrock * appropriate for the file. We then resolve any named sections or functions 468dc0093f4Seschrock * against the file, and iterate over the results (or all sections if no flags 469dc0093f4Seschrock * were specified). 470dc0093f4Seschrock */ 471dc0093f4Seschrock void 472dc0093f4Seschrock dis_file(const char *filename) 473dc0093f4Seschrock { 474dc0093f4Seschrock dis_tgt_t *tgt, *current; 475dc0093f4Seschrock dis_scnlist_t *sections; 476dc0093f4Seschrock dis_funclist_t *functions; 477dc0093f4Seschrock dis_handle_t *dhp; 478dc0093f4Seschrock GElf_Ehdr ehdr; 479dc0093f4Seschrock 480dc0093f4Seschrock /* 481dc0093f4Seschrock * First, initialize the target 482dc0093f4Seschrock */ 483dc0093f4Seschrock if ((tgt = dis_tgt_create(filename)) == NULL) 484dc0093f4Seschrock return; 485dc0093f4Seschrock 486dc0093f4Seschrock if (!g_quiet) 487dc0093f4Seschrock (void) printf("disassembly for %s\n\n", filename); 488dc0093f4Seschrock 489dc0093f4Seschrock /* 490dc0093f4Seschrock * A given file may contain multiple targets (if it is an archive, for 491dc0093f4Seschrock * example). We iterate over all possible targets if this is the case. 492dc0093f4Seschrock */ 493dc0093f4Seschrock for (current = tgt; current != NULL; current = dis_tgt_next(current)) { 494dc0093f4Seschrock dis_tgt_ehdr(current, &ehdr); 495dc0093f4Seschrock 496dc0093f4Seschrock /* 497dc0093f4Seschrock * Eventually, this should probably live within libdisasm, and 498dc0093f4Seschrock * we should be able to disassemble targets from different 499dc0093f4Seschrock * architectures. For now, we only support objects as the 500dc0093f4Seschrock * native machine type. 501dc0093f4Seschrock */ 502dc0093f4Seschrock switch (ehdr.e_machine) { 503dc0093f4Seschrock case EM_SPARC: 504dc0093f4Seschrock if (ehdr.e_ident[EI_CLASS] != ELFCLASS32 || 505dc0093f4Seschrock ehdr.e_ident[EI_DATA] != ELFDATA2MSB) { 506dc0093f4Seschrock warn("invalid E_IDENT field for SPARC object"); 507dc0093f4Seschrock return; 508dc0093f4Seschrock } 509dc0093f4Seschrock g_flags |= DIS_SPARC_V8; 510dc0093f4Seschrock break; 511dc0093f4Seschrock 512dc0093f4Seschrock case EM_SPARC32PLUS: 513b5f3c6ffSJason King { 514b5f3c6ffSJason King uint64_t flags = ehdr.e_flags & EF_SPARC_32PLUS_MASK; 515b5f3c6ffSJason King 516dc0093f4Seschrock if (ehdr.e_ident[EI_CLASS] != ELFCLASS32 || 517dc0093f4Seschrock ehdr.e_ident[EI_DATA] != ELFDATA2MSB) { 518dc0093f4Seschrock warn("invalid E_IDENT field for SPARC object"); 519dc0093f4Seschrock return; 520dc0093f4Seschrock } 521dc0093f4Seschrock 522b5f3c6ffSJason King if (flags != 0 && 523b5f3c6ffSJason King (flags & (EF_SPARC_32PLUS | EF_SPARC_SUN_US1 | 524b5f3c6ffSJason King EF_SPARC_SUN_US3)) != EF_SPARC_32PLUS) 525dc0093f4Seschrock g_flags |= DIS_SPARC_V9 | DIS_SPARC_V9_SGI; 526b5f3c6ffSJason King else 527dc0093f4Seschrock g_flags |= DIS_SPARC_V9; 528dc0093f4Seschrock break; 529b5f3c6ffSJason King } 530dc0093f4Seschrock 531dc0093f4Seschrock case EM_SPARCV9: 532dc0093f4Seschrock if (ehdr.e_ident[EI_CLASS] != ELFCLASS64 || 533dc0093f4Seschrock ehdr.e_ident[EI_DATA] != ELFDATA2MSB) { 534dc0093f4Seschrock warn("invalid E_IDENT field for SPARC object"); 535dc0093f4Seschrock return; 536dc0093f4Seschrock } 537dc0093f4Seschrock 538dc0093f4Seschrock g_flags |= DIS_SPARC_V9 | DIS_SPARC_V9_SGI; 539dc0093f4Seschrock break; 540dc0093f4Seschrock 541dc0093f4Seschrock case EM_386: 542dc0093f4Seschrock g_flags |= DIS_X86_SIZE32; 543dc0093f4Seschrock break; 544dc0093f4Seschrock 545dc0093f4Seschrock case EM_AMD64: 546dc0093f4Seschrock g_flags |= DIS_X86_SIZE64; 547dc0093f4Seschrock break; 548dc0093f4Seschrock 549dc0093f4Seschrock default: 550dc0093f4Seschrock die("%s: unsupported ELF machine 0x%x", filename, 551dc0093f4Seschrock ehdr.e_machine); 552dc0093f4Seschrock } 553dc0093f4Seschrock 554e0070315Sdmick /* 555e0070315Sdmick * If ET_REL (.o), printing immediate symbols is likely to 556e0070315Sdmick * result in garbage, as symbol lookups on unrelocated 557e0070315Sdmick * immediates find false and useless matches. 558e0070315Sdmick */ 559e0070315Sdmick 560e0070315Sdmick if (ehdr.e_type == ET_REL) 561e0070315Sdmick g_flags |= DIS_NOIMMSYM; 562e0070315Sdmick 563dc0093f4Seschrock if (!g_quiet && dis_tgt_member(current) != NULL) 564dc0093f4Seschrock (void) printf("\narchive member %s\n", 565dc0093f4Seschrock dis_tgt_member(current)); 566dc0093f4Seschrock 567dc0093f4Seschrock /* 568dc0093f4Seschrock * Instantiate a libdisasm handle based on the file type. 569dc0093f4Seschrock */ 570dc0093f4Seschrock if ((dhp = dis_handle_create(g_flags, current, do_lookup, 571dc0093f4Seschrock do_read)) == NULL) 572dc0093f4Seschrock die("%s: failed to initialize disassembler: %s", 573dc0093f4Seschrock filename, dis_strerror(dis_errno())); 574dc0093f4Seschrock 575dc0093f4Seschrock if (g_doall) { 576dc0093f4Seschrock /* 577dc0093f4Seschrock * With no arguments, iterate over all sections and 578dc0093f4Seschrock * disassemble only those that contain text. 579dc0093f4Seschrock */ 580dc0093f4Seschrock dis_tgt_section_iter(current, dis_text_section, dhp); 581dc0093f4Seschrock } else { 582dc0093f4Seschrock callback_arg_t ca; 583dc0093f4Seschrock 584dc0093f4Seschrock ca.ca_tgt = current; 585dc0093f4Seschrock ca.ca_handle = dhp; 586dc0093f4Seschrock 587dc0093f4Seschrock /* 588dc0093f4Seschrock * If sections or functions were explicitly specified, 589dc0093f4Seschrock * resolve those names against the object, and iterate 590dc0093f4Seschrock * over just the resulting data. 591dc0093f4Seschrock */ 592dc0093f4Seschrock sections = dis_namelist_resolve_sections(g_seclist, 593dc0093f4Seschrock current); 594dc0093f4Seschrock functions = dis_namelist_resolve_functions(g_funclist, 595dc0093f4Seschrock current); 596dc0093f4Seschrock 597dc0093f4Seschrock dis_scnlist_iter(sections, dis_named_section, &ca); 598dc0093f4Seschrock dis_funclist_iter(functions, dis_named_function, &ca); 599dc0093f4Seschrock 600dc0093f4Seschrock dis_scnlist_destroy(sections); 601dc0093f4Seschrock dis_funclist_destroy(functions); 602dc0093f4Seschrock } 603dc0093f4Seschrock 604dc0093f4Seschrock dis_handle_destroy(dhp); 605dc0093f4Seschrock } 606dc0093f4Seschrock 607dc0093f4Seschrock dis_tgt_destroy(tgt); 608dc0093f4Seschrock } 609dc0093f4Seschrock 610dc0093f4Seschrock void 611dc0093f4Seschrock usage(void) 612dc0093f4Seschrock { 613dc0093f4Seschrock (void) fprintf(stderr, "usage: dis [-CVoqn] [-d sec] \n"); 614dc0093f4Seschrock (void) fprintf(stderr, "\t[-D sec] [-F function] [-t sec] file ..\n"); 615dc0093f4Seschrock exit(2); 616dc0093f4Seschrock } 617dc0093f4Seschrock 618dc0093f4Seschrock typedef struct lib_node { 619dc0093f4Seschrock char *path; 620dc0093f4Seschrock struct lib_node *next; 621dc0093f4Seschrock } lib_node_t; 622dc0093f4Seschrock 623dc0093f4Seschrock int 624dc0093f4Seschrock main(int argc, char **argv) 625dc0093f4Seschrock { 626dc0093f4Seschrock int optchar; 627dc0093f4Seschrock int i; 628dc0093f4Seschrock lib_node_t *libs = NULL; 629dc0093f4Seschrock 630dc0093f4Seschrock g_funclist = dis_namelist_create(); 631dc0093f4Seschrock g_seclist = dis_namelist_create(); 632dc0093f4Seschrock 633dc0093f4Seschrock while ((optchar = getopt(argc, argv, "Cd:D:F:l:Lot:Vqn")) != -1) { 634dc0093f4Seschrock switch (optchar) { 635dc0093f4Seschrock case 'C': 636dc0093f4Seschrock g_demangle = 1; 637dc0093f4Seschrock break; 638dc0093f4Seschrock case 'd': 639dc0093f4Seschrock dis_namelist_add(g_seclist, optarg, DIS_DATA_RELATIVE); 640dc0093f4Seschrock break; 641dc0093f4Seschrock case 'D': 642dc0093f4Seschrock dis_namelist_add(g_seclist, optarg, DIS_DATA_ABSOLUTE); 643dc0093f4Seschrock break; 644dc0093f4Seschrock case 'F': 645dc0093f4Seschrock dis_namelist_add(g_funclist, optarg, 0); 646dc0093f4Seschrock break; 647dc0093f4Seschrock case 'l': { 648dc0093f4Seschrock /* 649dc0093f4Seschrock * The '-l foo' option historically would attempt to 650dc0093f4Seschrock * disassemble '$LIBDIR/libfoo.a'. The $LIBDIR 651dc0093f4Seschrock * environment variable has never been supported or 652dc0093f4Seschrock * documented for our linker. However, until this 653dc0093f4Seschrock * option is formally EOLed, we have to support it. 654dc0093f4Seschrock */ 655dc0093f4Seschrock char *dir; 656dc0093f4Seschrock lib_node_t *node; 657dc0093f4Seschrock size_t len; 658dc0093f4Seschrock 659dc0093f4Seschrock if ((dir = getenv("LIBDIR")) == NULL || 660dc0093f4Seschrock dir[0] == '\0') 661dc0093f4Seschrock dir = "/usr/lib"; 662dc0093f4Seschrock node = safe_malloc(sizeof (lib_node_t)); 663dc0093f4Seschrock len = strlen(optarg) + strlen(dir) + sizeof ("/lib.a"); 664dc0093f4Seschrock node->path = safe_malloc(len); 665dc0093f4Seschrock 666dc0093f4Seschrock (void) snprintf(node->path, len, "%s/lib%s.a", dir, 667dc0093f4Seschrock optarg); 668dc0093f4Seschrock node->next = libs; 669dc0093f4Seschrock libs = node; 670dc0093f4Seschrock break; 671dc0093f4Seschrock } 672dc0093f4Seschrock case 'L': 673dc0093f4Seschrock /* 674dc0093f4Seschrock * The '-L' option historically would attempt to read 675dc0093f4Seschrock * the .debug section of the target to determine source 676dc0093f4Seschrock * line information in order to annotate the output. 677dc0093f4Seschrock * No compiler has emitted these sections in many years, 678dc0093f4Seschrock * and the option has never done what it purported to 679dc0093f4Seschrock * do. We silently consume the option for 680dc0093f4Seschrock * compatibility. 681dc0093f4Seschrock */ 682dc0093f4Seschrock break; 683dc0093f4Seschrock case 'n': 684dc0093f4Seschrock g_numeric = 1; 685dc0093f4Seschrock break; 686dc0093f4Seschrock case 'o': 687dc0093f4Seschrock g_flags |= DIS_OCTAL; 688dc0093f4Seschrock break; 689dc0093f4Seschrock case 'q': 690dc0093f4Seschrock g_quiet = 1; 691dc0093f4Seschrock break; 692dc0093f4Seschrock case 't': 693dc0093f4Seschrock dis_namelist_add(g_seclist, optarg, DIS_TEXT); 694dc0093f4Seschrock break; 695dc0093f4Seschrock case 'V': 696dc0093f4Seschrock (void) printf("Solaris disassembler version 1.0\n"); 697dc0093f4Seschrock return (0); 698dc0093f4Seschrock default: 699dc0093f4Seschrock usage(); 700dc0093f4Seschrock break; 701dc0093f4Seschrock } 702dc0093f4Seschrock } 703dc0093f4Seschrock 704dc0093f4Seschrock argc -= optind; 705dc0093f4Seschrock argv += optind; 706dc0093f4Seschrock 707dc0093f4Seschrock if (argc == 0 && libs == NULL) { 708dc0093f4Seschrock warn("no objects specified"); 709dc0093f4Seschrock usage(); 710dc0093f4Seschrock } 711dc0093f4Seschrock 712dc0093f4Seschrock if (dis_namelist_empty(g_funclist) && dis_namelist_empty(g_seclist)) 713dc0093f4Seschrock g_doall = 1; 714dc0093f4Seschrock 715dc0093f4Seschrock /* 716dc0093f4Seschrock * See comment for 'l' option, above. 717dc0093f4Seschrock */ 718dc0093f4Seschrock while (libs != NULL) { 719dc0093f4Seschrock lib_node_t *node = libs->next; 720dc0093f4Seschrock 721dc0093f4Seschrock dis_file(libs->path); 722dc0093f4Seschrock free(libs->path); 723dc0093f4Seschrock free(libs); 724dc0093f4Seschrock libs = node; 725dc0093f4Seschrock } 726dc0093f4Seschrock 727dc0093f4Seschrock for (i = 0; i < argc; i++) 728dc0093f4Seschrock dis_file(argv[i]); 729dc0093f4Seschrock 730dc0093f4Seschrock dis_namelist_destroy(g_funclist); 731dc0093f4Seschrock dis_namelist_destroy(g_seclist); 732dc0093f4Seschrock 733dc0093f4Seschrock return (g_error); 734dc0093f4Seschrock } 735