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