xref: /titanic_44/usr/src/cmd/dis/dis_main.c (revision dc0093f44ee4fac928e006850f8ed53f68277af5)
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