1*5198Sjmcp /*
2*5198Sjmcp * CDDL HEADER START
3*5198Sjmcp *
4*5198Sjmcp * The contents of this file are subject to the terms of the
5*5198Sjmcp * Common Development and Distribution License (the "License").
6*5198Sjmcp * You may not use this file except in compliance with the License.
7*5198Sjmcp *
8*5198Sjmcp * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5198Sjmcp * or http://www.opensolaris.org/os/licensing.
10*5198Sjmcp * See the License for the specific language governing permissions
11*5198Sjmcp * and limitations under the License.
12*5198Sjmcp *
13*5198Sjmcp * When distributing Covered Code, include this CDDL HEADER in each
14*5198Sjmcp * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5198Sjmcp * If applicable, add the following below this CDDL HEADER, with the
16*5198Sjmcp * fields enclosed by brackets "[]" replaced with your own identifying
17*5198Sjmcp * information: Portions Copyright [yyyy] [name of copyright owner]
18*5198Sjmcp *
19*5198Sjmcp * CDDL HEADER END
20*5198Sjmcp */
21*5198Sjmcp
22*5198Sjmcp /*
23*5198Sjmcp * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24*5198Sjmcp * Use is subject to license terms.
25*5198Sjmcp */
26*5198Sjmcp
27*5198Sjmcp /*
28*5198Sjmcp * Copyright 2007 Jason King. All rights reserved.
29*5198Sjmcp * Use is subject to license terms.
30*5198Sjmcp */
31*5198Sjmcp
32*5198Sjmcp
33*5198Sjmcp #pragma ident "%Z%%M% %I% %E% SMI"
34*5198Sjmcp
35*5198Sjmcp /*
36*5198Sjmcp * The sparc disassembler is mostly straightforward, each instruction is
37*5198Sjmcp * represented by an inst_t structure. The inst_t definitions are organized
38*5198Sjmcp * into tables. The tables are correspond to the opcode maps documented in the
39*5198Sjmcp * various sparc architecture manuals. Each table defines the bit range of the
40*5198Sjmcp * instruction whose value act as an index into the array of instructions. A
41*5198Sjmcp * table can also refer to another table if needed. Each table also contains
42*5198Sjmcp * a function pointer of type format_fcn that knows how to output the
43*5198Sjmcp * instructions in the table, as well as handle any synthetic instructions
44*5198Sjmcp *
45*5198Sjmcp * Unfortunately, the changes from sparcv8 -> sparcv9 not only include new
46*5198Sjmcp * instructions, they sometimes renamed or just reused the same instruction to
47*5198Sjmcp * do different operations (i.e. the sparcv8 coprocessor instructions). To
48*5198Sjmcp * accommodate this, each table can define an overlay table. The overlay table
49*5198Sjmcp * is a list of (table index, architecture, new instruction definition) values.
50*5198Sjmcp *
51*5198Sjmcp *
52*5198Sjmcp * Traversal starts with the first table,
53*5198Sjmcp * get index value from the instruction
54*5198Sjmcp * if an relevant overlay entry exists for this index,
55*5198Sjmcp * grab the overlay definition
56*5198Sjmcp * else
57*5198Sjmcp * grab the definition from the array (corresponding to the index value)
58*5198Sjmcp *
59*5198Sjmcp * If the entry is an instruction,
60*5198Sjmcp * call print function of instruction.
61*5198Sjmcp * If the entry is a pointer to another table
62*5198Sjmcp * traverse the table
63*5198Sjmcp * If not valid,
64*5198Sjmcp * return an error
65*5198Sjmcp *
66*5198Sjmcp *
67*5198Sjmcp * To keep dis happy, for sparc, instead of actually returning an error, if
68*5198Sjmcp * the instruction cannot be disassembled, we instead merely place the value
69*5198Sjmcp * of the instruction into the output buffer.
70*5198Sjmcp *
71*5198Sjmcp * Adding new instructions:
72*5198Sjmcp *
73*5198Sjmcp * With the above information, it hopefully makes it clear how to add support
74*5198Sjmcp * for decoding new instructions. Presumably, with new instructions will come
75*5198Sjmcp * a new dissassembly mode (I.e. DIS_SPARC_V8, DIS_SPARC_V9, etc.).
76*5198Sjmcp *
77*5198Sjmcp * If the dissassembled format does not correspond to one of the existing
78*5198Sjmcp * formats, a new formatter will have to be written. The 'flags' value of
79*5198Sjmcp * inst_t is intended to instruct the corresponding formatter about how to
80*5198Sjmcp * output the instruction.
81*5198Sjmcp *
82*5198Sjmcp * If the corresponding entry in the correct table is currently unoccupied,
83*5198Sjmcp * simply replace the INVALID entry with the correct definition. The INST and
84*5198Sjmcp * TABLE macros are suggested to be used for this. If there is already an
85*5198Sjmcp * instruction defined, then the entry must be placed in an overlay table. If
86*5198Sjmcp * no overlay table exists for the instruction table, one will need to be
87*5198Sjmcp * created.
88*5198Sjmcp */
89*5198Sjmcp
90*5198Sjmcp #include <libdisasm.h>
91*5198Sjmcp #include <stdlib.h>
92*5198Sjmcp #include <stdio.h>
93*5198Sjmcp #include <sys/types.h>
94*5198Sjmcp #include <sys/byteorder.h>
95*5198Sjmcp #include <string.h>
96*5198Sjmcp
97*5198Sjmcp #include "libdisasm_impl.h"
98*5198Sjmcp #include "dis_sparc.h"
99*5198Sjmcp
100*5198Sjmcp static const inst_t *dis_get_overlay(dis_handle_t *, const table_t *,
101*5198Sjmcp uint32_t);
102*5198Sjmcp static uint32_t dis_get_bits(uint32_t, int, int);
103*5198Sjmcp
104*5198Sjmcp #if !defined(DIS_STANDALONE)
105*5198Sjmcp static void do_binary(uint32_t);
106*5198Sjmcp #endif /* DIS_STANDALONE */
107*5198Sjmcp
108*5198Sjmcp dis_handle_t *
dis_handle_create(int flags,void * data,dis_lookup_f lookup_func,dis_read_f read_func)109*5198Sjmcp dis_handle_create(int flags, void *data, dis_lookup_f lookup_func,
110*5198Sjmcp dis_read_f read_func)
111*5198Sjmcp {
112*5198Sjmcp
113*5198Sjmcp #if !defined(DIS_STANDALONE)
114*5198Sjmcp char *opt = NULL;
115*5198Sjmcp char *opt2, *save, *end;
116*5198Sjmcp #endif
117*5198Sjmcp dis_handle_t *dhp;
118*5198Sjmcp
119*5198Sjmcp if ((flags & (DIS_SPARC_V8|DIS_SPARC_V9|DIS_SPARC_V9_SGI)) == 0) {
120*5198Sjmcp (void) dis_seterrno(E_DIS_INVALFLAG);
121*5198Sjmcp return (NULL);
122*5198Sjmcp }
123*5198Sjmcp
124*5198Sjmcp if ((dhp = dis_zalloc(sizeof (struct dis_handle))) == NULL) {
125*5198Sjmcp (void) dis_seterrno(E_DIS_NOMEM);
126*5198Sjmcp return (NULL);
127*5198Sjmcp }
128*5198Sjmcp
129*5198Sjmcp dhp->dh_lookup = lookup_func;
130*5198Sjmcp dhp->dh_read = read_func;
131*5198Sjmcp dhp->dh_flags = flags;
132*5198Sjmcp dhp->dh_data = data;
133*5198Sjmcp dhp->dh_debug = DIS_DEBUG_COMPAT;
134*5198Sjmcp
135*5198Sjmcp #if !defined(DIS_STANDALONE)
136*5198Sjmcp
137*5198Sjmcp opt = getenv("_LIBDISASM_DEBUG");
138*5198Sjmcp if (opt == NULL)
139*5198Sjmcp return (dhp);
140*5198Sjmcp
141*5198Sjmcp opt2 = strdup(opt);
142*5198Sjmcp if (opt2 == NULL) {
143*5198Sjmcp dis_handle_destroy(dhp);
144*5198Sjmcp (void) dis_seterrno(E_DIS_NOMEM);
145*5198Sjmcp return (NULL);
146*5198Sjmcp }
147*5198Sjmcp save = opt2;
148*5198Sjmcp
149*5198Sjmcp while (opt2 != NULL) {
150*5198Sjmcp end = strchr(opt2, ',');
151*5198Sjmcp
152*5198Sjmcp if (end != 0)
153*5198Sjmcp *end++ = '\0';
154*5198Sjmcp
155*5198Sjmcp if (strcasecmp("synth-all", opt2) == 0)
156*5198Sjmcp dhp->dh_debug |= DIS_DEBUG_SYN_ALL;
157*5198Sjmcp
158*5198Sjmcp if (strcasecmp("compat", opt2) == 0)
159*5198Sjmcp dhp->dh_debug |= DIS_DEBUG_COMPAT;
160*5198Sjmcp
161*5198Sjmcp if (strcasecmp("synth-none", opt2) == 0)
162*5198Sjmcp dhp->dh_debug &= ~(DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT);
163*5198Sjmcp
164*5198Sjmcp if (strcasecmp("binary", opt2) == 0)
165*5198Sjmcp dhp->dh_debug |= DIS_DEBUG_PRTBIN;
166*5198Sjmcp
167*5198Sjmcp if (strcasecmp("format", opt2) == 0)
168*5198Sjmcp dhp->dh_debug |= DIS_DEBUG_PRTFMT;
169*5198Sjmcp
170*5198Sjmcp if (strcasecmp("all", opt2) == 0)
171*5198Sjmcp dhp->dh_debug = DIS_DEBUG_ALL;
172*5198Sjmcp
173*5198Sjmcp if (strcasecmp("none", opt2) == 0)
174*5198Sjmcp dhp->dh_debug = DIS_DEBUG_NONE;
175*5198Sjmcp
176*5198Sjmcp opt2 = end;
177*5198Sjmcp }
178*5198Sjmcp free(save);
179*5198Sjmcp #endif /* DIS_STANDALONE */
180*5198Sjmcp return (dhp);
181*5198Sjmcp }
182*5198Sjmcp
183*5198Sjmcp void
dis_handle_destroy(dis_handle_t * dhp)184*5198Sjmcp dis_handle_destroy(dis_handle_t *dhp)
185*5198Sjmcp {
186*5198Sjmcp dis_free(dhp, sizeof (dis_handle_t));
187*5198Sjmcp }
188*5198Sjmcp
189*5198Sjmcp void
dis_set_data(dis_handle_t * dhp,void * data)190*5198Sjmcp dis_set_data(dis_handle_t *dhp, void *data)
191*5198Sjmcp {
192*5198Sjmcp dhp->dh_data = data;
193*5198Sjmcp }
194*5198Sjmcp
195*5198Sjmcp void
dis_flags_set(dis_handle_t * dhp,int f)196*5198Sjmcp dis_flags_set(dis_handle_t *dhp, int f)
197*5198Sjmcp {
198*5198Sjmcp dhp->dh_flags |= f;
199*5198Sjmcp }
200*5198Sjmcp
201*5198Sjmcp void
dis_flags_clear(dis_handle_t * dhp,int f)202*5198Sjmcp dis_flags_clear(dis_handle_t *dhp, int f)
203*5198Sjmcp {
204*5198Sjmcp dhp->dh_flags &= ~f;
205*5198Sjmcp }
206*5198Sjmcp
207*5198Sjmcp /* ARGSUSED */
208*5198Sjmcp int
dis_max_instrlen(dis_handle_t * dhp)209*5198Sjmcp dis_max_instrlen(dis_handle_t *dhp)
210*5198Sjmcp {
211*5198Sjmcp return (4);
212*5198Sjmcp }
213*5198Sjmcp
214*5198Sjmcp /*
215*5198Sjmcp * The dis_i386.c comment for this says it returns the previous instruction,
216*5198Sjmcp * however, I'm fairly sure it's actually returning the _address_ of the
217*5198Sjmcp * nth previous instruction.
218*5198Sjmcp */
219*5198Sjmcp /* ARGSUSED */
220*5198Sjmcp uint64_t
dis_previnstr(dis_handle_t * dhp,uint64_t pc,int n)221*5198Sjmcp dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
222*5198Sjmcp {
223*5198Sjmcp if (n <= 0)
224*5198Sjmcp return (pc);
225*5198Sjmcp
226*5198Sjmcp if (pc < n)
227*5198Sjmcp return (pc);
228*5198Sjmcp
229*5198Sjmcp return (pc - n*4);
230*5198Sjmcp }
231*5198Sjmcp
232*5198Sjmcp int
dis_disassemble(dis_handle_t * dhp,uint64_t addr,char * buf,size_t buflen)233*5198Sjmcp dis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen)
234*5198Sjmcp {
235*5198Sjmcp const table_t *tp = &initial_table;
236*5198Sjmcp const inst_t *inp = NULL;
237*5198Sjmcp
238*5198Sjmcp uint32_t instr;
239*5198Sjmcp uint32_t idx = 0;
240*5198Sjmcp
241*5198Sjmcp if (dhp->dh_read(dhp->dh_data, addr, &instr, sizeof (instr)) !=
242*5198Sjmcp sizeof (instr))
243*5198Sjmcp return (-1);
244*5198Sjmcp
245*5198Sjmcp dhp->dh_buf = buf;
246*5198Sjmcp dhp->dh_buflen = buflen;
247*5198Sjmcp dhp->dh_addr = addr;
248*5198Sjmcp
249*5198Sjmcp buf[0] = '\0';
250*5198Sjmcp
251*5198Sjmcp /* this allows sparc code to be tested on x86 */
252*5198Sjmcp instr = BE_32(instr);
253*5198Sjmcp
254*5198Sjmcp #if !defined(DIS_STANDALONE)
255*5198Sjmcp if ((dhp->dh_debug & DIS_DEBUG_PRTBIN) != 0)
256*5198Sjmcp do_binary(instr);
257*5198Sjmcp #endif /* DIS_STANDALONE */
258*5198Sjmcp
259*5198Sjmcp /* CONSTCOND */
260*5198Sjmcp while (1) {
261*5198Sjmcp idx = dis_get_bits(instr, tp->tbl_field, tp->tbl_len);
262*5198Sjmcp inp = &tp->tbl_inp[idx];
263*5198Sjmcp
264*5198Sjmcp inp = dis_get_overlay(dhp, tp, idx);
265*5198Sjmcp
266*5198Sjmcp if ((inp->in_type == INST_NONE) ||
267*5198Sjmcp ((inp->in_arch & dhp->dh_flags) == 0))
268*5198Sjmcp goto error;
269*5198Sjmcp
270*5198Sjmcp if (inp->in_type == INST_TBL) {
271*5198Sjmcp tp = inp->in_data.in_tbl;
272*5198Sjmcp continue;
273*5198Sjmcp }
274*5198Sjmcp
275*5198Sjmcp break;
276*5198Sjmcp }
277*5198Sjmcp
278*5198Sjmcp if (tp->tbl_fmt(dhp, instr, inp, idx) == 0)
279*5198Sjmcp return (0);
280*5198Sjmcp
281*5198Sjmcp error:
282*5198Sjmcp
283*5198Sjmcp (void) snprintf(buf, buflen,
284*5198Sjmcp ((dhp->dh_flags & DIS_OCTAL) != 0) ? "0%011lo" : "0x%08lx",
285*5198Sjmcp instr);
286*5198Sjmcp
287*5198Sjmcp return (0);
288*5198Sjmcp }
289*5198Sjmcp
290*5198Sjmcp static uint32_t
dis_get_bits(uint32_t instr,int offset,int length)291*5198Sjmcp dis_get_bits(uint32_t instr, int offset, int length)
292*5198Sjmcp {
293*5198Sjmcp uint32_t mask, val;
294*5198Sjmcp int i;
295*5198Sjmcp
296*5198Sjmcp for (i = 0, mask = 0; i < length; ++i)
297*5198Sjmcp mask |= (1UL << i);
298*5198Sjmcp
299*5198Sjmcp mask = mask << (offset - length + 1);
300*5198Sjmcp
301*5198Sjmcp val = instr & mask;
302*5198Sjmcp
303*5198Sjmcp val = val >> (offset - length + 1);
304*5198Sjmcp
305*5198Sjmcp return (val);
306*5198Sjmcp }
307*5198Sjmcp
308*5198Sjmcp static const inst_t *
dis_get_overlay(dis_handle_t * dhp,const table_t * tp,uint32_t idx)309*5198Sjmcp dis_get_overlay(dis_handle_t *dhp, const table_t *tp, uint32_t idx)
310*5198Sjmcp {
311*5198Sjmcp const inst_t *ip = &tp->tbl_inp[idx];
312*5198Sjmcp int i;
313*5198Sjmcp
314*5198Sjmcp if (tp->tbl_ovp == NULL)
315*5198Sjmcp return (ip);
316*5198Sjmcp
317*5198Sjmcp for (i = 0; tp->tbl_ovp[i].ov_idx != -1; ++i) {
318*5198Sjmcp if (tp->tbl_ovp[i].ov_idx != idx)
319*5198Sjmcp continue;
320*5198Sjmcp
321*5198Sjmcp if ((tp->tbl_ovp[i].ov_inst.in_arch & dhp->dh_flags) == 0)
322*5198Sjmcp continue;
323*5198Sjmcp
324*5198Sjmcp ip = &tp->tbl_ovp[i].ov_inst;
325*5198Sjmcp break;
326*5198Sjmcp }
327*5198Sjmcp
328*5198Sjmcp return (ip);
329*5198Sjmcp }
330*5198Sjmcp
331*5198Sjmcp #if !defined(DIS_STANDALONE)
332*5198Sjmcp static void
do_binary(uint32_t instr)333*5198Sjmcp do_binary(uint32_t instr)
334*5198Sjmcp {
335*5198Sjmcp (void) fprintf(stderr, "DISASM: ");
336*5198Sjmcp prt_binary(instr, 32);
337*5198Sjmcp (void) fprintf(stderr, "\n");
338*5198Sjmcp }
339*5198Sjmcp #endif /* DIS_STANDALONE */
340