dis_main.c revision b5f3c6ffe7f93e6132a702a851a69b5ecd78c066
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright 2011 Jason King. All rights reserved.
*/
#include <ctype.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sysmacros.h>
#include <sys/elf_SPARC.h>
#include <libdisasm.h>
#include "dis_target.h"
#include "dis_util.h"
#include "dis_list.h"
int g_demangle; /* Demangle C++ names */
int g_quiet; /* Quiet mode */
int g_numeric; /* Numeric mode */
int g_flags; /* libdisasm language flags */
int g_doall; /* true if no functions or sections were given */
/*
* Section options for -d, -D, and -s
*/
#define DIS_DATA_RELATIVE 1
#define DIS_DATA_ABSOLUTE 2
#define DIS_TEXT 3
/*
* libdisasm callback data. Keeps track of current data (function or section)
* and offset within that data.
*/
typedef struct dis_buffer {
void *db_data; /* function or section data */
} dis_buffer_t;
/*
* Given a symbol+offset as returned by dis_tgt_lookup(), print an appropriately
* formatted symbol, based on the offset and current setttings.
*/
void
{
else
} else {
if (g_demangle)
if (offset == 0)
else
}
}
/*
* The main disassembly routine. Given a fixed-sized buffer and starting
* address, disassemble the data using the supplied target and libdisasm handle.
*/
void
{
dis_buffer_t db = { 0 };
const char *symbol;
const char *last_symbol;
int i;
int bytesperline;
int isfunc;
bytesperline = 6;
#if defined(__sparc)
/*
* Since sparc instructions are fixed size, we
* always know the address of the next instruction
*/
"*** invalid opcode ***");
#else
"*** invalid opcode ***");
/*
* On architectures with variable sized instructions
* we have no way to figure out where the next
* instruction starts if we encounter an invalid
* instruction. Instead we print the rest of the
* instruction stream as hex until we reach the
* next valid symbol in the section.
*/
} else {
else
}
#endif
}
/*
* Print out the line as:
*
* address: bytes text
*
* If there are more than 6 bytes in any given instruction,
* spread the bytes across two lines. We try to get symbolic
* information for the address, but if that fails we print out
* the numeric address instead.
*
* We try to keep the address portion of the text aligned at
* MINSYMWIDTH characters. If we are disassembling a function
* with a long name, this can be annoying. So we pick a width
* based on the maximum width that the current symbol can be.
* This at least produces text aligned within each function.
*/
&isfunc);
}
if (symbol != last_symbol)
sizeof (symbuf));
/*
* If we've crossed a new function boundary, print out the
* function name on a blank line.
*/
/* print bytes */
i++) {
else
}
/* trailing spaces for missing bytes */
for (; i < bytesperline; i++) {
(void) printf(" ");
else
(void) printf(" ");
}
/* contents of disassembly */
/* excess bytes that spill over onto subsequent lines */
if (i % bytesperline == 0)
else
}
(void) printf("\n");
}
}
/*
* libdisasm wrapper around symbol lookup. Invoke the target-specific lookup
* function, and convert the result using getsymname().
*/
int
{
const char *symbol;
/*
* If NULL symbol is returned, getsymname takes care of
* printing appropriate address in buf instead of symbol.
*/
return (-1);
return (0);
}
/*
* libdisasm wrapper around target reading. libdisasm will always read data
* in order, so update our current offset within the buffer appropriately.
* We only support reading from within the current object; libdisasm should
* never ask us to do otherwise.
*/
int
{
return (-1);
return (len);
}
/*
* Routine to dump raw data in a human-readable format. Used by the -d and -D
* options. We model our output after the xxd(1) program, which gives nicely
* formatted output, along with an ASCII translation of the result.
*/
void
{
int i;
int width;
/*
* Determine if the address given to us fits in 32-bit range, in which
* case use a 4-byte width.
*/
width = 8;
else
width = 16;
/*
* Display leading address
*/
/*
* Print out data in two-byte chunks. If the current address
* is before the starting address or after the end of the
* section, print spaces.
*/
for (i = 0; i < 16; i++) {
(void) printf(" ");
else
(void) printf("%02x",
if (i & 1)
(void) printf(" ");
}
(void) printf(" ");
/*
* Print out the ASCII representation
*/
for (i = 0; i < 16; i++) {
(void) printf(" ");
} else {
else
(void) printf(".");
}
}
(void) printf("\n");
curaddr += 16;
}
}
/*
* Disassemble a section implicitly specified as part of a file. This function
* is called for all sections when no other flags are specified. We ignore any
* data sections, and print out only those sections containing text.
*/
void
{
/* ignore data sections */
if (!dis_section_istext(scn))
return;
if (!g_quiet)
}
/*
* Structure passed to dis_named_{section,function} which keeps track of both
* the target and the libdisasm handle.
*/
typedef struct callback_arg {
/*
* Disassemble a section explicitly named with -s, -d, or -D. The 'type'
* argument contains the type of argument given. Pass the data onto the
* appropriate helper routine.
*/
void
{
if (!g_quiet)
switch (type) {
case DIS_DATA_RELATIVE:
break;
case DIS_DATA_ABSOLUTE:
break;
case DIS_TEXT:
break;
}
}
/*
* Disassemble a function explicitly specified with '-F'. The 'type' argument
* is unused.
*/
/* ARGSUSED */
void
{
}
/*
* Disassemble a complete file. First, we determine the type of the file based
* on the ELF machine type, and instantiate a version of the disassembler
* appropriate for the file. We then resolve any named sections or functions
* against the file, and iterate over the results (or all sections if no flags
* were specified).
*/
void
{
/*
* First, initialize the target
*/
return;
if (!g_quiet)
/*
* A given file may contain multiple targets (if it is an archive, for
* example). We iterate over all possible targets if this is the case.
*/
/*
* Eventually, this should probably live within libdisasm, and
* we should be able to disassemble targets from different
* architectures. For now, we only support objects as the
* native machine type.
*/
#ifdef __sparc
case EM_SPARC:
warn("invalid E_IDENT field for SPARC object");
return;
}
g_flags |= DIS_SPARC_V8;
break;
case EM_SPARC32PLUS:
{
warn("invalid E_IDENT field for SPARC object");
return;
}
if (flags != 0 &&
else
g_flags |= DIS_SPARC_V9;
break;
}
case EM_SPARCV9:
warn("invalid E_IDENT field for SPARC object");
return;
}
break;
#endif /* __sparc */
case EM_386:
break;
case EM_AMD64:
break;
#endif /* __i386 || __amd64 */
default:
}
/*
* If ET_REL (.o), printing immediate symbols is likely to
* result in garbage, as symbol lookups on unrelocated
* immediates find false and useless matches.
*/
g_flags |= DIS_NOIMMSYM;
(void) printf("\narchive member %s\n",
/*
* Instantiate a libdisasm handle based on the file type.
*/
die("%s: failed to initialize disassembler: %s",
if (g_doall) {
/*
* With no arguments, iterate over all sections and
* disassemble only those that contain text.
*/
} else {
/*
* If sections or functions were explicitly specified,
* resolve those names against the object, and iterate
* over just the resulting data.
*/
current);
current);
}
}
}
void
usage(void)
{
exit(2);
}
typedef struct lib_node {
char *path;
} lib_node_t;
int
{
int optchar;
int i;
switch (optchar) {
case 'C':
g_demangle = 1;
break;
case 'd':
break;
case 'D':
break;
case 'F':
break;
case 'l': {
/*
* The '-l foo' option historically would attempt to
* environment variable has never been supported or
* documented for our linker. However, until this
* option is formally EOLed, we have to support it.
*/
char *dir;
dir[0] == '\0')
optarg);
break;
}
case 'L':
/*
* The '-L' option historically would attempt to read
* the .debug section of the target to determine source
* line information in order to annotate the output.
* No compiler has emitted these sections in many years,
* and the option has never done what it purported to
* do. We silently consume the option for
* compatibility.
*/
break;
case 'n':
g_numeric = 1;
break;
case 'o':
break;
case 'q':
g_quiet = 1;
break;
case 't':
break;
case 'V':
(void) printf("Solaris disassembler version 1.0\n");
return (0);
default:
usage();
break;
}
}
warn("no objects specified");
usage();
}
g_doall = 1;
/*
* See comment for 'l' option, above.
*/
}
for (i = 0; i < argc; i++)
return (g_error);
}