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