2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A/*
2N/A * Copyright 2007 Jason King. All rights reserved.
2N/A * Use is subject to license terms.
2N/A */
2N/A
2N/A
2N/A#pragma ident "%Z%%M% %I% %E% SMI"
2N/A
2N/A/*
2N/A * The sparc disassembler is mostly straightforward, each instruction is
2N/A * represented by an inst_t structure. The inst_t definitions are organized
2N/A * into tables. The tables are correspond to the opcode maps documented in the
2N/A * various sparc architecture manuals. Each table defines the bit range of the
2N/A * instruction whose value act as an index into the array of instructions. A
2N/A * table can also refer to another table if needed. Each table also contains
2N/A * a function pointer of type format_fcn that knows how to output the
2N/A * instructions in the table, as well as handle any synthetic instructions
2N/A *
2N/A * Unfortunately, the changes from sparcv8 -> sparcv9 not only include new
2N/A * instructions, they sometimes renamed or just reused the same instruction to
2N/A * do different operations (i.e. the sparcv8 coprocessor instructions). To
2N/A * accommodate this, each table can define an overlay table. The overlay table
2N/A * is a list of (table index, architecture, new instruction definition) values.
2N/A *
2N/A *
2N/A * Traversal starts with the first table,
2N/A * get index value from the instruction
2N/A * if an relevant overlay entry exists for this index,
2N/A * grab the overlay definition
2N/A * else
2N/A * grab the definition from the array (corresponding to the index value)
2N/A *
2N/A * If the entry is an instruction,
2N/A * call print function of instruction.
2N/A * If the entry is a pointer to another table
2N/A * traverse the table
2N/A * If not valid,
2N/A * return an error
2N/A *
2N/A *
2N/A * To keep dis happy, for sparc, instead of actually returning an error, if
2N/A * the instruction cannot be disassembled, we instead merely place the value
2N/A * of the instruction into the output buffer.
2N/A *
2N/A * Adding new instructions:
2N/A *
2N/A * With the above information, it hopefully makes it clear how to add support
2N/A * for decoding new instructions. Presumably, with new instructions will come
2N/A * a new dissassembly mode (I.e. DIS_SPARC_V8, DIS_SPARC_V9, etc.).
2N/A *
2N/A * If the dissassembled format does not correspond to one of the existing
2N/A * formats, a new formatter will have to be written. The 'flags' value of
2N/A * inst_t is intended to instruct the corresponding formatter about how to
2N/A * output the instruction.
2N/A *
2N/A * If the corresponding entry in the correct table is currently unoccupied,
2N/A * simply replace the INVALID entry with the correct definition. The INST and
2N/A * TABLE macros are suggested to be used for this. If there is already an
2N/A * instruction defined, then the entry must be placed in an overlay table. If
2N/A * no overlay table exists for the instruction table, one will need to be
2N/A * created.
2N/A */
2N/A
2N/A#include <libdisasm.h>
2N/A#include <stdlib.h>
2N/A#include <stdio.h>
2N/A#include <sys/types.h>
2N/A#include <sys/byteorder.h>
2N/A#include <string.h>
2N/A
2N/A#include "libdisasm_impl.h"
2N/A#include "dis_sparc.h"
2N/A
2N/Astatic const inst_t *dis_get_overlay(dis_handle_t *, const table_t *,
2N/A uint32_t);
2N/Astatic uint32_t dis_get_bits(uint32_t, int, int);
2N/A
2N/A#if !defined(DIS_STANDALONE)
2N/Astatic void do_binary(uint32_t);
2N/A#endif /* DIS_STANDALONE */
2N/A
2N/Adis_handle_t *
2N/Adis_handle_create(int flags, void *data, dis_lookup_f lookup_func,
2N/A dis_read_f read_func)
2N/A{
2N/A
2N/A#if !defined(DIS_STANDALONE)
2N/A char *opt = NULL;
2N/A char *opt2, *save, *end;
2N/A#endif
2N/A dis_handle_t *dhp;
2N/A
2N/A if ((flags & (DIS_SPARC_V8|DIS_SPARC_V9|DIS_SPARC_V9_SGI)) == 0) {
2N/A (void) dis_seterrno(E_DIS_INVALFLAG);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((dhp = dis_zalloc(sizeof (struct dis_handle))) == NULL) {
2N/A (void) dis_seterrno(E_DIS_NOMEM);
2N/A return (NULL);
2N/A }
2N/A
2N/A dhp->dh_lookup = lookup_func;
2N/A dhp->dh_read = read_func;
2N/A dhp->dh_flags = flags;
2N/A dhp->dh_data = data;
2N/A dhp->dh_debug = DIS_DEBUG_COMPAT;
2N/A
2N/A#if !defined(DIS_STANDALONE)
2N/A
2N/A opt = getenv("_LIBDISASM_DEBUG");
2N/A if (opt == NULL)
2N/A return (dhp);
2N/A
2N/A opt2 = strdup(opt);
2N/A if (opt2 == NULL) {
2N/A dis_handle_destroy(dhp);
2N/A (void) dis_seterrno(E_DIS_NOMEM);
2N/A return (NULL);
2N/A }
2N/A save = opt2;
2N/A
2N/A while (opt2 != NULL) {
2N/A end = strchr(opt2, ',');
2N/A
2N/A if (end != 0)
2N/A *end++ = '\0';
2N/A
2N/A if (strcasecmp("synth-all", opt2) == 0)
2N/A dhp->dh_debug |= DIS_DEBUG_SYN_ALL;
2N/A
2N/A if (strcasecmp("compat", opt2) == 0)
2N/A dhp->dh_debug |= DIS_DEBUG_COMPAT;
2N/A
2N/A if (strcasecmp("synth-none", opt2) == 0)
2N/A dhp->dh_debug &= ~(DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT);
2N/A
2N/A if (strcasecmp("binary", opt2) == 0)
2N/A dhp->dh_debug |= DIS_DEBUG_PRTBIN;
2N/A
2N/A if (strcasecmp("format", opt2) == 0)
2N/A dhp->dh_debug |= DIS_DEBUG_PRTFMT;
2N/A
2N/A if (strcasecmp("all", opt2) == 0)
2N/A dhp->dh_debug = DIS_DEBUG_ALL;
2N/A
2N/A if (strcasecmp("none", opt2) == 0)
2N/A dhp->dh_debug = DIS_DEBUG_NONE;
2N/A
2N/A opt2 = end;
2N/A }
2N/A free(save);
2N/A#endif /* DIS_STANDALONE */
2N/A return (dhp);
2N/A}
2N/A
2N/Avoid
2N/Adis_handle_destroy(dis_handle_t *dhp)
2N/A{
2N/A dis_free(dhp, sizeof (dis_handle_t));
2N/A}
2N/A
2N/Avoid
2N/Adis_set_data(dis_handle_t *dhp, void *data)
2N/A{
2N/A dhp->dh_data = data;
2N/A}
2N/A
2N/Avoid
2N/Adis_flags_set(dis_handle_t *dhp, int f)
2N/A{
2N/A dhp->dh_flags |= f;
2N/A}
2N/A
2N/Avoid
2N/Adis_flags_clear(dis_handle_t *dhp, int f)
2N/A{
2N/A dhp->dh_flags &= ~f;
2N/A}
2N/A
2N/A/* ARGSUSED */
2N/Aint
2N/Adis_max_instrlen(dis_handle_t *dhp)
2N/A{
2N/A return (4);
2N/A}
2N/A
2N/A/*
2N/A * The dis_i386.c comment for this says it returns the previous instruction,
2N/A * however, I'm fairly sure it's actually returning the _address_ of the
2N/A * nth previous instruction.
2N/A */
2N/A/* ARGSUSED */
2N/Auint64_t
2N/Adis_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
2N/A{
2N/A if (n <= 0)
2N/A return (pc);
2N/A
2N/A if (pc < n)
2N/A return (pc);
2N/A
2N/A return (pc - n*4);
2N/A}
2N/A
2N/Aint
2N/Adis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen)
2N/A{
2N/A const table_t *tp = &initial_table;
2N/A const inst_t *inp = NULL;
2N/A
2N/A uint32_t instr;
2N/A uint32_t idx = 0;
2N/A
2N/A if (dhp->dh_read(dhp->dh_data, addr, &instr, sizeof (instr)) !=
2N/A sizeof (instr))
2N/A return (-1);
2N/A
2N/A dhp->dh_buf = buf;
2N/A dhp->dh_buflen = buflen;
2N/A dhp->dh_addr = addr;
2N/A
2N/A buf[0] = '\0';
2N/A
2N/A /* this allows sparc code to be tested on x86 */
2N/A instr = BE_32(instr);
2N/A
2N/A#if !defined(DIS_STANDALONE)
2N/A if ((dhp->dh_debug & DIS_DEBUG_PRTBIN) != 0)
2N/A do_binary(instr);
2N/A#endif /* DIS_STANDALONE */
2N/A
2N/A /* CONSTCOND */
2N/A while (1) {
2N/A idx = dis_get_bits(instr, tp->tbl_field, tp->tbl_len);
2N/A inp = &tp->tbl_inp[idx];
2N/A
2N/A inp = dis_get_overlay(dhp, tp, idx);
2N/A
2N/A if ((inp->in_type == INST_NONE) ||
2N/A ((inp->in_arch & dhp->dh_flags) == 0))
2N/A goto error;
2N/A
2N/A if (inp->in_type == INST_TBL) {
2N/A tp = inp->in_data.in_tbl;
2N/A continue;
2N/A }
2N/A
2N/A break;
2N/A }
2N/A
2N/A if (tp->tbl_fmt(dhp, instr, inp, idx) == 0)
2N/A return (0);
2N/A
2N/Aerror:
2N/A
2N/A (void) snprintf(buf, buflen,
2N/A ((dhp->dh_flags & DIS_OCTAL) != 0) ? "0%011lo" : "0x%08lx",
2N/A instr);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic uint32_t
2N/Adis_get_bits(uint32_t instr, int offset, int length)
2N/A{
2N/A uint32_t mask, val;
2N/A int i;
2N/A
2N/A for (i = 0, mask = 0; i < length; ++i)
2N/A mask |= (1UL << i);
2N/A
2N/A mask = mask << (offset - length + 1);
2N/A
2N/A val = instr & mask;
2N/A
2N/A val = val >> (offset - length + 1);
2N/A
2N/A return (val);
2N/A}
2N/A
2N/Astatic const inst_t *
2N/Adis_get_overlay(dis_handle_t *dhp, const table_t *tp, uint32_t idx)
2N/A{
2N/A const inst_t *ip = &tp->tbl_inp[idx];
2N/A int i;
2N/A
2N/A if (tp->tbl_ovp == NULL)
2N/A return (ip);
2N/A
2N/A for (i = 0; tp->tbl_ovp[i].ov_idx != -1; ++i) {
2N/A if (tp->tbl_ovp[i].ov_idx != idx)
2N/A continue;
2N/A
2N/A if ((tp->tbl_ovp[i].ov_inst.in_arch & dhp->dh_flags) == 0)
2N/A continue;
2N/A
2N/A ip = &tp->tbl_ovp[i].ov_inst;
2N/A break;
2N/A }
2N/A
2N/A return (ip);
2N/A}
2N/A
2N/A#if !defined(DIS_STANDALONE)
2N/Astatic void
2N/Ado_binary(uint32_t instr)
2N/A{
2N/A (void) fprintf(stderr, "DISASM: ");
2N/A prt_binary(instr, 32);
2N/A (void) fprintf(stderr, "\n");
2N/A}
2N/A#endif /* DIS_STANDALONE */