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