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