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