xref: /onnv-gate/usr/src/cmd/dis/dis_main.c (revision 3892:55e05ad4374a)
11545Seschrock /*
21545Seschrock  * CDDL HEADER START
31545Seschrock  *
41545Seschrock  * The contents of this file are subject to the terms of the
51545Seschrock  * Common Development and Distribution License (the "License").
61545Seschrock  * You may not use this file except in compliance with the License.
71545Seschrock  *
81545Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91545Seschrock  * or http://www.opensolaris.org/os/licensing.
101545Seschrock  * See the License for the specific language governing permissions
111545Seschrock  * and limitations under the License.
121545Seschrock  *
131545Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
141545Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151545Seschrock  * If applicable, add the following below this CDDL HEADER, with the
161545Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
171545Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
181545Seschrock  *
191545Seschrock  * CDDL HEADER END
201545Seschrock  */
211545Seschrock 
221545Seschrock /*
23*3892Sdmick  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
241545Seschrock  * Use is subject to license terms.
251545Seschrock  */
261545Seschrock 
271545Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
281545Seschrock 
291545Seschrock #include <ctype.h>
301545Seschrock #include <getopt.h>
311545Seschrock #include <stdio.h>
321545Seschrock #include <stdlib.h>
331545Seschrock #include <string.h>
341545Seschrock #include <sys/sysmacros.h>
351545Seschrock #include <sys/elf_SPARC.h>
361545Seschrock 
371545Seschrock #include <libdisasm.h>
381545Seschrock 
391545Seschrock #include "dis_target.h"
401545Seschrock #include "dis_util.h"
411545Seschrock #include "dis_list.h"
421545Seschrock 
431545Seschrock int g_demangle;		/* Demangle C++ names */
441545Seschrock int g_quiet;		/* Quiet mode */
451545Seschrock int g_numeric;		/* Numeric mode */
461545Seschrock int g_flags;		/* libdisasm language flags */
471545Seschrock int g_doall;		/* true if no functions or sections were given */
481545Seschrock 
491545Seschrock dis_namelist_t *g_funclist;	/* list of functions to disassemble, if any */
501545Seschrock dis_namelist_t *g_seclist;	/* list of sections to disassemble, if any */
511545Seschrock 
521545Seschrock /*
531545Seschrock  * Section options for -d, -D, and -s
541545Seschrock  */
551545Seschrock #define	DIS_DATA_RELATIVE	1
561545Seschrock #define	DIS_DATA_ABSOLUTE	2
571545Seschrock #define	DIS_TEXT		3
581545Seschrock 
591545Seschrock /*
601545Seschrock  * libdisasm callback data.  Keeps track of current data (function or section)
611545Seschrock  * and offset within that data.
621545Seschrock  */
631545Seschrock typedef struct dis_buffer {
641545Seschrock 	dis_tgt_t	*db_tgt;	/* current dis target */
651545Seschrock 	void		*db_data;	/* function or section data */
661545Seschrock 	uint64_t	db_addr;	/* address of function start */
671545Seschrock 	size_t		db_size;	/* size of data */
681545Seschrock 	uint64_t	db_nextaddr;	/* next address to be read */
691545Seschrock } dis_buffer_t;
701545Seschrock 
711545Seschrock #define	MINSYMWIDTH	22	/* Minimum width of symbol portion of line */
721545Seschrock 
731545Seschrock /*
741545Seschrock  * Given a symbol+offset as returned by dis_tgt_lookup(), print an appropriately
751545Seschrock  * formatted symbol, based on the offset and current setttings.
761545Seschrock  */
771545Seschrock void
getsymname(uint64_t addr,const char * symbol,off_t offset,char * buf,size_t buflen)781545Seschrock getsymname(uint64_t addr, const char *symbol, off_t offset, char *buf,
791545Seschrock     size_t buflen)
801545Seschrock {
812089Sdmick 	if (symbol == NULL || g_numeric) {
822089Sdmick 		if (g_flags & DIS_OCTAL)
832089Sdmick 			(void) snprintf(buf, buflen, "0%llo", addr);
842089Sdmick 		else
852089Sdmick 			(void) snprintf(buf, buflen, "0x%llx", addr);
862089Sdmick 	} else {
871545Seschrock 		if (g_demangle)
881545Seschrock 			symbol = dis_demangle(symbol);
891545Seschrock 
901545Seschrock 		if (offset == 0)
911545Seschrock 			(void) snprintf(buf, buflen, "%s", symbol);
921545Seschrock 		else if (g_flags & DIS_OCTAL)
931545Seschrock 			(void) snprintf(buf, buflen, "%s+0%o", symbol, offset);
941545Seschrock 		else
951545Seschrock 			(void) snprintf(buf, buflen, "%s+0x%x", symbol, offset);
961545Seschrock 	}
971545Seschrock }
981545Seschrock 
991545Seschrock /*
1001545Seschrock  * The main disassembly routine.  Given a fixed-sized buffer and starting
1011545Seschrock  * address, disassemble the data using the supplied target and libdisasm handle.
1021545Seschrock  */
1031545Seschrock void
dis_data(dis_tgt_t * tgt,dis_handle_t * dhp,uint64_t addr,void * data,size_t datalen)1041545Seschrock dis_data(dis_tgt_t *tgt, dis_handle_t *dhp, uint64_t addr, void *data,
1051545Seschrock     size_t datalen)
1061545Seschrock {
1071545Seschrock 	dis_buffer_t db = { 0 };
1081545Seschrock 	char buf[BUFSIZE];
1091545Seschrock 	char symbuf[BUFSIZE];
1101545Seschrock 	const char *symbol;
1111545Seschrock 	off_t symoffset;
1121545Seschrock 	int i;
1131545Seschrock 	int bytesperline;
1141545Seschrock 	size_t symsize;
1151545Seschrock 	int isfunc;
1161545Seschrock 	size_t symwidth = 0;
1171545Seschrock 
1181545Seschrock 	db.db_tgt = tgt;
1191545Seschrock 	db.db_data = data;
1201545Seschrock 	db.db_addr = addr;
1211545Seschrock 	db.db_size = datalen;
1221545Seschrock 
1231545Seschrock 	dis_set_data(dhp, &db);
1241545Seschrock 
1251545Seschrock 	if ((bytesperline = dis_max_instrlen(dhp)) > 6)
1261545Seschrock 		bytesperline = 6;
1271545Seschrock 
1281545Seschrock 	while (addr < db.db_addr + db.db_size) {
1291545Seschrock 
1301545Seschrock 		if (dis_disassemble(dhp, addr, buf, BUFSIZE) != 0) {
1311545Seschrock 			/*
1321545Seschrock 			 * If we encounter an invalid opcode, we just
1331545Seschrock 			 * print "*** invalid opcode ***" at that first bad
1341545Seschrock 			 * instruction and continue with printing the rest
1351545Seschrock 			 * of the instruction stream as hex data,
1361545Seschrock 			 * We then find the next valid symbol in the section,
1371545Seschrock 			 * and disassemble from there.
1381545Seschrock 			 */
1391545Seschrock 			off_t next;
1401545Seschrock 
1411545Seschrock 			(void) snprintf(buf, sizeof (buf),
1421545Seschrock 			    "*** invalid opcode ***");
1431545Seschrock 
1441545Seschrock 			if ((next = dis_tgt_next_symbol(tgt, addr)) == 0) {
1451545Seschrock 				db.db_nextaddr = db.db_addr + db.db_size;
1461545Seschrock 			} else {
1471545Seschrock 				if (next > db.db_size)
1481545Seschrock 					db.db_nextaddr = db.db_addr +
1491545Seschrock 					    db.db_size;
1501545Seschrock 				else
1511545Seschrock 					db.db_nextaddr = addr + next;
1521545Seschrock 			}
1531545Seschrock 		}
1541545Seschrock 
1551545Seschrock 		/*
1561545Seschrock 		 * Print out the line as:
1571545Seschrock 		 *
1581545Seschrock 		 * 	address:	bytes	text
1591545Seschrock 		 *
1601545Seschrock 		 * If there are more than 6 bytes in any given instruction,
1611545Seschrock 		 * spread the bytes across two lines.  We try to get symbolic
1621545Seschrock 		 * information for the address, but if that fails we print out
1631545Seschrock 		 * the numeric address instead.
1641545Seschrock 		 *
1651545Seschrock 		 * We try to keep the address portion of the text aligned at
1661545Seschrock 		 * MINSYMWIDTH characters.  If we are disassembling a function
1671545Seschrock 		 * with a long name, this can be annoying.  So we pick a width
1681545Seschrock 		 * based on the maximum width that the current symbol can be.
1691545Seschrock 		 * This at least produces text aligned within each function.
1701545Seschrock 		 */
1711545Seschrock 		symbol = dis_tgt_lookup(tgt, addr, &symoffset, 1, &symsize,
1721545Seschrock 		    &isfunc);
1731545Seschrock 		/* Get the maximum length for this symbol */
1741545Seschrock 		getsymname(addr, symbol, symsize, symbuf, sizeof (symbuf));
1751545Seschrock 		symwidth = MAX(strlen(symbuf), MINSYMWIDTH);
1761545Seschrock 
1771545Seschrock 		getsymname(addr, symbol, symoffset, symbuf, sizeof (symbuf));
1781545Seschrock 
1791545Seschrock 		/*
1801545Seschrock 		 * If we've crossed a new function boundary, print out the
1811545Seschrock 		 * function name on a blank line.
1821545Seschrock 		 */
1831545Seschrock 		if (!g_quiet && symoffset == 0 && symbol != NULL && isfunc)
1841545Seschrock 			(void) printf("%s()\n", symbol);
1851545Seschrock 
1861545Seschrock 		(void) printf("    %s:%*s ", symbuf,
1871545Seschrock 		    symwidth - strlen(symbuf), "");
1881545Seschrock 
1891545Seschrock 		/* print bytes */
1901545Seschrock 		for (i = 0; i < MIN(bytesperline, (db.db_nextaddr - addr));
1911545Seschrock 		    i++) {
1921545Seschrock 			int byte = *((uchar_t *)data + (addr - db.db_addr) + i);
1931545Seschrock 			if (g_flags & DIS_OCTAL)
1941545Seschrock 				(void) printf("%03o ", byte);
1951545Seschrock 			else
1961545Seschrock 				(void) printf("%02x ", byte);
1971545Seschrock 		}
1981545Seschrock 
1991545Seschrock 		/* trailing spaces for missing bytes */
2001545Seschrock 		for (; i < bytesperline; i++) {
2011545Seschrock 			if (g_flags & DIS_OCTAL)
2021545Seschrock 				(void) printf("    ");
2031545Seschrock 			else
2041545Seschrock 				(void) printf("   ");
2051545Seschrock 		}
2061545Seschrock 
2071545Seschrock 		/* contents of disassembly */
2081545Seschrock 		(void) printf(" %s", buf);
2091545Seschrock 
2101545Seschrock 		/* excess bytes that spill over onto subsequent lines */
2111545Seschrock 		for (; i < db.db_nextaddr - addr; i++) {
2121545Seschrock 			int byte = *((uchar_t *)data + (addr - db.db_addr) + i);
2131545Seschrock 			if (i % bytesperline == 0)
2141545Seschrock 				(void) printf("\n    %*s  ", symwidth, "");
2151545Seschrock 			if (g_flags & DIS_OCTAL)
2161545Seschrock 				(void) printf("%03o ", byte);
2171545Seschrock 			else
2181545Seschrock 				(void) printf("%02x ", byte);
2191545Seschrock 		}
2201545Seschrock 
2211545Seschrock 		(void) printf("\n");
2221545Seschrock 
2231545Seschrock 		addr = db.db_nextaddr;
2241545Seschrock 	}
2251545Seschrock }
2261545Seschrock 
2271545Seschrock /*
2281545Seschrock  * libdisasm wrapper around symbol lookup.  Invoke the target-specific lookup
2291545Seschrock  * function, and convert the result using getsymname().
2301545Seschrock  */
2311545Seschrock int
do_lookup(void * data,uint64_t addr,char * buf,size_t buflen,uint64_t * start,size_t * symlen)2321545Seschrock do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start,
2331545Seschrock     size_t *symlen)
2341545Seschrock {
2351545Seschrock 	dis_buffer_t *db = data;
2361545Seschrock 	const char *symbol;
2371545Seschrock 	off_t offset;
2381545Seschrock 	size_t size;
2391545Seschrock 
2401545Seschrock 	/*
2411545Seschrock 	 * If NULL symbol is returned, getsymname takes care of
2421545Seschrock 	 * printing appropriate address in buf instead of symbol.
2431545Seschrock 	 */
2441545Seschrock 	symbol = dis_tgt_lookup(db->db_tgt, addr, &offset, 0, &size, NULL);
2451545Seschrock 
2461545Seschrock 	if (buf != NULL)
2471545Seschrock 		getsymname(addr, symbol, offset, buf, buflen);
2481545Seschrock 
2491545Seschrock 	if (start != NULL)
2501545Seschrock 		*start = addr - offset;
2511545Seschrock 	if (symlen != NULL)
2521545Seschrock 		*symlen = size;
2531545Seschrock 
2542089Sdmick 	if (symbol == NULL)
2552089Sdmick 		return (-1);
2562089Sdmick 
2571545Seschrock 	return (0);
2581545Seschrock }
2591545Seschrock 
2601545Seschrock /*
2611545Seschrock  * libdisasm wrapper around target reading.  libdisasm will always read data
2621545Seschrock  * in order, so update our current offset within the buffer appropriately.
2631545Seschrock  * We only support reading from within the current object; libdisasm should
2641545Seschrock  * never ask us to do otherwise.
2651545Seschrock  */
2661545Seschrock int
do_read(void * data,uint64_t addr,void * buf,size_t len)2671545Seschrock do_read(void *data, uint64_t addr, void *buf, size_t len)
2681545Seschrock {
2691545Seschrock 	dis_buffer_t *db = data;
2701545Seschrock 	size_t offset;
2711545Seschrock 
2721545Seschrock 	if (addr < db->db_addr || addr >= db->db_addr + db->db_size)
2731545Seschrock 		return (-1);
2741545Seschrock 
2751545Seschrock 	offset = addr - db->db_addr;
2761545Seschrock 	len = MIN(len, db->db_size - offset);
2771545Seschrock 
2781545Seschrock 	(void) memcpy(buf, (char *)db->db_data + offset, len);
2791545Seschrock 
2801545Seschrock 	db->db_nextaddr = addr + len;
2811545Seschrock 
2821545Seschrock 	return (len);
2831545Seschrock }
2841545Seschrock 
2851545Seschrock /*
2861545Seschrock  * Routine to dump raw data in a human-readable format.  Used by the -d and -D
2871545Seschrock  * options.  We model our output after the xxd(1) program, which gives nicely
2881545Seschrock  * formatted output, along with an ASCII translation of the result.
2891545Seschrock  */
2901545Seschrock void
dump_data(uint64_t addr,void * data,size_t datalen)2911545Seschrock dump_data(uint64_t addr, void *data, size_t datalen)
2921545Seschrock {
2931545Seschrock 	uintptr_t curaddr = addr & (~0xf);
2941545Seschrock 	uint8_t *bytes = data;
2951545Seschrock 	int i;
2961545Seschrock 	int width;
2971545Seschrock 
2981545Seschrock 	/*
2991545Seschrock 	 * Determine if the address given to us fits in 32-bit range, in which
3001545Seschrock 	 * case use a 4-byte width.
3011545Seschrock 	 */
3021545Seschrock 	if (((addr + datalen) & 0xffffffff00000000ULL) == 0ULL)
3031545Seschrock 		width = 8;
3041545Seschrock 	else
3051545Seschrock 		width = 16;
3061545Seschrock 
3071545Seschrock 	while (curaddr < addr + datalen) {
3081545Seschrock 		/*
3091545Seschrock 		 * Display leading address
3101545Seschrock 		 */
3111545Seschrock 		(void) printf("%0*x: ", width, curaddr);
3121545Seschrock 
3131545Seschrock 		/*
3141545Seschrock 		 * Print out data in two-byte chunks.  If the current address
3151545Seschrock 		 * is before the starting address or after the end of the
3161545Seschrock 		 * section, print spaces.
3171545Seschrock 		 */
3181545Seschrock 		for (i = 0; i < 16; i++) {
3191545Seschrock 			if (curaddr + i < addr ||curaddr + i >= addr + datalen)
3201545Seschrock 				(void) printf("  ");
3211545Seschrock 			else
3221545Seschrock 				(void) printf("%02x",
3231545Seschrock 				    bytes[curaddr + i - addr]);
3241545Seschrock 
3251545Seschrock 			if (i & 1)
3261545Seschrock 				(void) printf(" ");
3271545Seschrock 		}
3281545Seschrock 
3291545Seschrock 		(void) printf(" ");
3301545Seschrock 
3311545Seschrock 		/*
3321545Seschrock 		 * Print out the ASCII representation
3331545Seschrock 		 */
3341545Seschrock 		for (i = 0; i < 16; i++) {
3351545Seschrock 			if (curaddr + i < addr ||
3361545Seschrock 			    curaddr + i >= addr + datalen) {
3371545Seschrock 				(void) printf(" ");
3381545Seschrock 			} else {
3391545Seschrock 				uint8_t byte = bytes[curaddr + i - addr];
3401545Seschrock 				if (isprint(byte))
3411545Seschrock 					(void) printf("%c", byte);
3421545Seschrock 				else
3431545Seschrock 					(void) printf(".");
3441545Seschrock 			}
3451545Seschrock 		}
3461545Seschrock 
3471545Seschrock 		(void) printf("\n");
3481545Seschrock 
3491545Seschrock 		curaddr += 16;
3501545Seschrock 	}
3511545Seschrock }
3521545Seschrock 
3531545Seschrock /*
3541545Seschrock  * Disassemble a section implicitly specified as part of a file.  This function
3551545Seschrock  * is called for all sections when no other flags are specified.  We ignore any
3561545Seschrock  * data sections, and print out only those sections containing text.
3571545Seschrock  */
3581545Seschrock void
dis_text_section(dis_tgt_t * tgt,dis_scn_t * scn,void * data)3591545Seschrock dis_text_section(dis_tgt_t *tgt, dis_scn_t *scn, void *data)
3601545Seschrock {
3611545Seschrock 	dis_handle_t *dhp = data;
3621545Seschrock 
3631545Seschrock 	/* ignore data sections */
3641545Seschrock 	if (!dis_section_istext(scn))
3651545Seschrock 		return;
3661545Seschrock 
3671545Seschrock 	if (!g_quiet)
3681545Seschrock 		(void) printf("\nsection %s\n", dis_section_name(scn));
3691545Seschrock 
3701545Seschrock 	dis_data(tgt, dhp, dis_section_addr(scn), dis_section_data(scn),
3711545Seschrock 	    dis_section_size(scn));
3721545Seschrock }
3731545Seschrock 
3741545Seschrock /*
3751545Seschrock  * Structure passed to dis_named_{section,function} which keeps track of both
3761545Seschrock  * the target and the libdisasm handle.
3771545Seschrock  */
3781545Seschrock typedef struct callback_arg {
3791545Seschrock 	dis_tgt_t	*ca_tgt;
3801545Seschrock 	dis_handle_t	*ca_handle;
3811545Seschrock } callback_arg_t;
3821545Seschrock 
3831545Seschrock /*
3841545Seschrock  * Disassemble a section explicitly named with -s, -d, or -D.  The 'type'
3851545Seschrock  * argument contains the type of argument given.  Pass the data onto the
3861545Seschrock  * appropriate helper routine.
3871545Seschrock  */
3881545Seschrock void
dis_named_section(dis_scn_t * scn,int type,void * data)3891545Seschrock dis_named_section(dis_scn_t *scn, int type, void *data)
3901545Seschrock {
3911545Seschrock 	callback_arg_t *ca = data;
3921545Seschrock 
3931545Seschrock 	if (!g_quiet)
3941545Seschrock 		(void) printf("\nsection %s\n", dis_section_name(scn));
3951545Seschrock 
3961545Seschrock 	switch (type) {
3971545Seschrock 	case DIS_DATA_RELATIVE:
3981545Seschrock 		dump_data(0, dis_section_data(scn), dis_section_size(scn));
3991545Seschrock 		break;
4001545Seschrock 	case DIS_DATA_ABSOLUTE:
4011545Seschrock 		dump_data(dis_section_addr(scn), dis_section_data(scn),
4021545Seschrock 		    dis_section_size(scn));
4031545Seschrock 		break;
4041545Seschrock 	case DIS_TEXT:
4051545Seschrock 		dis_data(ca->ca_tgt, ca->ca_handle, dis_section_addr(scn),
4061545Seschrock 		    dis_section_data(scn), dis_section_size(scn));
4071545Seschrock 		break;
4081545Seschrock 	}
4091545Seschrock }
4101545Seschrock 
4111545Seschrock /*
4121545Seschrock  * Disassemble a function explicitly specified with '-F'.  The 'type' argument
4131545Seschrock  * is unused.
4141545Seschrock  */
4151545Seschrock /* ARGSUSED */
4161545Seschrock void
dis_named_function(dis_func_t * func,int type,void * data)4171545Seschrock dis_named_function(dis_func_t *func, int type, void *data)
4181545Seschrock {
4191545Seschrock 	callback_arg_t *ca = data;
4201545Seschrock 
4211545Seschrock 	dis_data(ca->ca_tgt, ca->ca_handle, dis_function_addr(func),
4221545Seschrock 	    dis_function_data(func), dis_function_size(func));
4231545Seschrock }
4241545Seschrock 
4251545Seschrock /*
4261545Seschrock  * Disassemble a complete file.  First, we determine the type of the file based
4271545Seschrock  * on the ELF machine type, and instantiate a version of the disassembler
4281545Seschrock  * appropriate for the file.  We then resolve any named sections or functions
4291545Seschrock  * against the file, and iterate over the results (or all sections if no flags
4301545Seschrock  * were specified).
4311545Seschrock  */
4321545Seschrock void
dis_file(const char * filename)4331545Seschrock dis_file(const char *filename)
4341545Seschrock {
4351545Seschrock 	dis_tgt_t *tgt, *current;
4361545Seschrock 	dis_scnlist_t *sections;
4371545Seschrock 	dis_funclist_t *functions;
4381545Seschrock 	dis_handle_t *dhp;
4391545Seschrock 	GElf_Ehdr ehdr;
4401545Seschrock 
4411545Seschrock 	/*
4421545Seschrock 	 * First, initialize the target
4431545Seschrock 	 */
4441545Seschrock 	if ((tgt = dis_tgt_create(filename)) == NULL)
4451545Seschrock 		return;
4461545Seschrock 
4471545Seschrock 	if (!g_quiet)
4481545Seschrock 		(void) printf("disassembly for %s\n\n",  filename);
4491545Seschrock 
4501545Seschrock 	/*
4511545Seschrock 	 * A given file may contain multiple targets (if it is an archive, for
4521545Seschrock 	 * example).  We iterate over all possible targets if this is the case.
4531545Seschrock 	 */
4541545Seschrock 	for (current = tgt; current != NULL; current = dis_tgt_next(current)) {
4551545Seschrock 		dis_tgt_ehdr(current, &ehdr);
4561545Seschrock 
4571545Seschrock 		/*
4581545Seschrock 		 * Eventually, this should probably live within libdisasm, and
4591545Seschrock 		 * we should be able to disassemble targets from different
4601545Seschrock 		 * architectures.  For now, we only support objects as the
4611545Seschrock 		 * native machine type.
4621545Seschrock 		 */
4631545Seschrock 		switch (ehdr.e_machine) {
4641545Seschrock #ifdef __sparc
4651545Seschrock 		case EM_SPARC:
4661545Seschrock 			if (ehdr.e_ident[EI_CLASS] != ELFCLASS32 ||
4671545Seschrock 			    ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
4681545Seschrock 				warn("invalid E_IDENT field for SPARC object");
4691545Seschrock 				return;
4701545Seschrock 			}
4711545Seschrock 			g_flags |= DIS_SPARC_V8;
4721545Seschrock 			break;
4731545Seschrock 
4741545Seschrock 		case EM_SPARC32PLUS:
4751545Seschrock 			if (ehdr.e_ident[EI_CLASS] != ELFCLASS32 ||
4761545Seschrock 			    ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
4771545Seschrock 				warn("invalid E_IDENT field for SPARC object");
4781545Seschrock 				return;
4791545Seschrock 			}
4801545Seschrock 
4811545Seschrock 			switch (ehdr.e_flags & EF_SPARC_32PLUS_MASK) {
4821545Seschrock 			case (EF_SPARC_32PLUS | EF_SPARC_SUN_US1 |
4831545Seschrock 			    EF_SPARC_SUN_US3):
4841545Seschrock 			case (EF_SPARC_32PLUS | EF_SPARC_SUN_US1):
4851545Seschrock 				g_flags |= DIS_SPARC_V9 | DIS_SPARC_V9_SGI;
4861545Seschrock 			default:
4871545Seschrock 				g_flags |= DIS_SPARC_V9;
4881545Seschrock 			}
4891545Seschrock 			break;
4901545Seschrock 
4911545Seschrock 		case EM_SPARCV9:
4921545Seschrock 			if (ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
4931545Seschrock 			    ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
4941545Seschrock 				warn("invalid E_IDENT field for SPARC object");
4951545Seschrock 				return;
4961545Seschrock 			}
4971545Seschrock 
4981545Seschrock 			g_flags |= DIS_SPARC_V9 | DIS_SPARC_V9_SGI;
4991545Seschrock 			break;
5001545Seschrock #endif /* __sparc */
5011545Seschrock 
5021545Seschrock #if defined(__i386) || defined(__amd64)
5031545Seschrock 		case EM_386:
5041545Seschrock 			g_flags |= DIS_X86_SIZE32;
5051545Seschrock 			break;
5061545Seschrock 
5071545Seschrock 		case EM_AMD64:
5081545Seschrock 			g_flags |= DIS_X86_SIZE64;
5091545Seschrock 			break;
5101545Seschrock #endif /* __i386 || __amd64 */
5111545Seschrock 
5121545Seschrock 		default:
5131545Seschrock 			die("%s: unsupported ELF machine 0x%x", filename,
5141545Seschrock 			    ehdr.e_machine);
5151545Seschrock 		}
5161545Seschrock 
517*3892Sdmick 		/*
518*3892Sdmick 		 * If ET_REL (.o), printing immediate symbols is likely to
519*3892Sdmick 		 * result in garbage, as symbol lookups on unrelocated
520*3892Sdmick 		 * immediates find false and useless matches.
521*3892Sdmick 		 */
522*3892Sdmick 
523*3892Sdmick 		if (ehdr.e_type == ET_REL)
524*3892Sdmick 			g_flags |= DIS_NOIMMSYM;
525*3892Sdmick 
5261545Seschrock 		if (!g_quiet && dis_tgt_member(current) != NULL)
5271545Seschrock 			(void) printf("\narchive member %s\n",
5281545Seschrock 			    dis_tgt_member(current));
5291545Seschrock 
5301545Seschrock 		/*
5311545Seschrock 		 * Instantiate a libdisasm handle based on the file type.
5321545Seschrock 		 */
5331545Seschrock 		if ((dhp = dis_handle_create(g_flags, current, do_lookup,
5341545Seschrock 		    do_read)) == NULL)
5351545Seschrock 			die("%s: failed to initialize disassembler: %s",
5361545Seschrock 			    filename, dis_strerror(dis_errno()));
5371545Seschrock 
5381545Seschrock 		if (g_doall) {
5391545Seschrock 			/*
5401545Seschrock 			 * With no arguments, iterate over all sections and
5411545Seschrock 			 * disassemble only those that contain text.
5421545Seschrock 			 */
5431545Seschrock 			dis_tgt_section_iter(current, dis_text_section, dhp);
5441545Seschrock 		} else {
5451545Seschrock 			callback_arg_t ca;
5461545Seschrock 
5471545Seschrock 			ca.ca_tgt = current;
5481545Seschrock 			ca.ca_handle = dhp;
5491545Seschrock 
5501545Seschrock 			/*
5511545Seschrock 			 * If sections or functions were explicitly specified,
5521545Seschrock 			 * resolve those names against the object, and iterate
5531545Seschrock 			 * over just the resulting data.
5541545Seschrock 			 */
5551545Seschrock 			sections = dis_namelist_resolve_sections(g_seclist,
5561545Seschrock 			    current);
5571545Seschrock 			functions = dis_namelist_resolve_functions(g_funclist,
5581545Seschrock 			    current);
5591545Seschrock 
5601545Seschrock 			dis_scnlist_iter(sections, dis_named_section, &ca);
5611545Seschrock 			dis_funclist_iter(functions, dis_named_function, &ca);
5621545Seschrock 
5631545Seschrock 			dis_scnlist_destroy(sections);
5641545Seschrock 			dis_funclist_destroy(functions);
5651545Seschrock 		}
5661545Seschrock 
5671545Seschrock 		dis_handle_destroy(dhp);
5681545Seschrock 	}
5691545Seschrock 
5701545Seschrock 	dis_tgt_destroy(tgt);
5711545Seschrock }
5721545Seschrock 
5731545Seschrock void
usage(void)5741545Seschrock usage(void)
5751545Seschrock {
5761545Seschrock 	(void) fprintf(stderr, "usage: dis [-CVoqn] [-d sec] \n");
5771545Seschrock 	(void) fprintf(stderr, "\t[-D sec] [-F function] [-t sec] file ..\n");
5781545Seschrock 	exit(2);
5791545Seschrock }
5801545Seschrock 
5811545Seschrock typedef struct lib_node {
5821545Seschrock 	char *path;
5831545Seschrock 	struct lib_node *next;
5841545Seschrock } lib_node_t;
5851545Seschrock 
5861545Seschrock int
main(int argc,char ** argv)5871545Seschrock main(int argc, char **argv)
5881545Seschrock {
5891545Seschrock 	int optchar;
5901545Seschrock 	int i;
5911545Seschrock 	lib_node_t *libs = NULL;
5921545Seschrock 
5931545Seschrock 	g_funclist = dis_namelist_create();
5941545Seschrock 	g_seclist = dis_namelist_create();
5951545Seschrock 
5961545Seschrock 	while ((optchar = getopt(argc, argv, "Cd:D:F:l:Lot:Vqn")) != -1) {
5971545Seschrock 		switch (optchar) {
5981545Seschrock 		case 'C':
5991545Seschrock 			g_demangle = 1;
6001545Seschrock 			break;
6011545Seschrock 		case 'd':
6021545Seschrock 			dis_namelist_add(g_seclist, optarg, DIS_DATA_RELATIVE);
6031545Seschrock 			break;
6041545Seschrock 		case 'D':
6051545Seschrock 			dis_namelist_add(g_seclist, optarg, DIS_DATA_ABSOLUTE);
6061545Seschrock 			break;
6071545Seschrock 		case 'F':
6081545Seschrock 			dis_namelist_add(g_funclist, optarg, 0);
6091545Seschrock 			break;
6101545Seschrock 		case 'l': {
6111545Seschrock 			/*
6121545Seschrock 			 * The '-l foo' option historically would attempt to
6131545Seschrock 			 * disassemble '$LIBDIR/libfoo.a'.  The $LIBDIR
6141545Seschrock 			 * environment variable has never been supported or
6151545Seschrock 			 * documented for our linker.  However, until this
6161545Seschrock 			 * option is formally EOLed, we have to support it.
6171545Seschrock 			 */
6181545Seschrock 			char *dir;
6191545Seschrock 			lib_node_t *node;
6201545Seschrock 			size_t len;
6211545Seschrock 
6221545Seschrock 			if ((dir = getenv("LIBDIR")) == NULL ||
6231545Seschrock 			    dir[0] == '\0')
6241545Seschrock 				dir = "/usr/lib";
6251545Seschrock 			node = safe_malloc(sizeof (lib_node_t));
6261545Seschrock 			len = strlen(optarg) + strlen(dir) + sizeof ("/lib.a");
6271545Seschrock 			node->path = safe_malloc(len);
6281545Seschrock 
6291545Seschrock 			(void) snprintf(node->path, len, "%s/lib%s.a", dir,
6301545Seschrock 			    optarg);
6311545Seschrock 			node->next = libs;
6321545Seschrock 			libs = node;
6331545Seschrock 			break;
6341545Seschrock 		}
6351545Seschrock 		case 'L':
6361545Seschrock 			/*
6371545Seschrock 			 * The '-L' option historically would attempt to read
6381545Seschrock 			 * the .debug section of the target to determine source
6391545Seschrock 			 * line information in order to annotate the output.
6401545Seschrock 			 * No compiler has emitted these sections in many years,
6411545Seschrock 			 * and the option has never done what it purported to
6421545Seschrock 			 * do.  We silently consume the option for
6431545Seschrock 			 * compatibility.
6441545Seschrock 			 */
6451545Seschrock 			break;
6461545Seschrock 		case 'n':
6471545Seschrock 			g_numeric = 1;
6481545Seschrock 			break;
6491545Seschrock 		case 'o':
6501545Seschrock 			g_flags |= DIS_OCTAL;
6511545Seschrock 			break;
6521545Seschrock 		case 'q':
6531545Seschrock 			g_quiet = 1;
6541545Seschrock 			break;
6551545Seschrock 		case 't':
6561545Seschrock 			dis_namelist_add(g_seclist, optarg, DIS_TEXT);
6571545Seschrock 			break;
6581545Seschrock 		case 'V':
6591545Seschrock 			(void) printf("Solaris disassembler version 1.0\n");
6601545Seschrock 			return (0);
6611545Seschrock 		default:
6621545Seschrock 			usage();
6631545Seschrock 			break;
6641545Seschrock 		}
6651545Seschrock 	}
6661545Seschrock 
6671545Seschrock 	argc -= optind;
6681545Seschrock 	argv += optind;
6691545Seschrock 
6701545Seschrock 	if (argc == 0 && libs == NULL) {
6711545Seschrock 		warn("no objects specified");
6721545Seschrock 		usage();
6731545Seschrock 	}
6741545Seschrock 
6751545Seschrock 	if (dis_namelist_empty(g_funclist) && dis_namelist_empty(g_seclist))
6761545Seschrock 		g_doall = 1;
6771545Seschrock 
6781545Seschrock 	/*
6791545Seschrock 	 * See comment for 'l' option, above.
6801545Seschrock 	 */
6811545Seschrock 	while (libs != NULL) {
6821545Seschrock 		lib_node_t *node = libs->next;
6831545Seschrock 
6841545Seschrock 		dis_file(libs->path);
6851545Seschrock 		free(libs->path);
6861545Seschrock 		free(libs);
6871545Seschrock 		libs = node;
6881545Seschrock 	}
6891545Seschrock 
6901545Seschrock 	for (i = 0; i < argc; i++)
6911545Seschrock 		dis_file(argv[i]);
6921545Seschrock 
6931545Seschrock 	dis_namelist_destroy(g_funclist);
6941545Seschrock 	dis_namelist_destroy(g_seclist);
6951545Seschrock 
6961545Seschrock 	return (g_error);
6971545Seschrock }
698