xref: /titanic_44/usr/src/cmd/mdb/common/mdb/mdb_disasm.c (revision 7aa76ffc594f84c1c092911a84f85a79ddb44c73)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * 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.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
2280148899SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
25*7aa76ffcSBryan Cantrill /*
26*7aa76ffcSBryan Cantrill  * Copyright 2011 Joyent, Inc.  All rights reserved.
27*7aa76ffcSBryan Cantrill  */
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate #include <mdb/mdb_disasm_impl.h>
307c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
317c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h>
327c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
337c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
347c478bd9Sstevel@tonic-gate #include <mdb/mdb_nv.h>
357c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
367c478bd9Sstevel@tonic-gate 
37dc0093f4Seschrock #include <libdisasm.h>
38dc0093f4Seschrock 
397c478bd9Sstevel@tonic-gate int
mdb_dis_select(const char * name)407c478bd9Sstevel@tonic-gate mdb_dis_select(const char *name)
417c478bd9Sstevel@tonic-gate {
427c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_lookup(&mdb.m_disasms, name);
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate 	if (v != NULL) {
457c478bd9Sstevel@tonic-gate 		mdb.m_disasm = mdb_nv_get_cookie(v);
467c478bd9Sstevel@tonic-gate 		return (0);
477c478bd9Sstevel@tonic-gate 	}
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate 	if (mdb.m_target == NULL) {
507c478bd9Sstevel@tonic-gate 		if (mdb.m_defdisasm != NULL)
517c478bd9Sstevel@tonic-gate 			strfree(mdb.m_defdisasm);
527c478bd9Sstevel@tonic-gate 		mdb.m_defdisasm = strdup(name);
537c478bd9Sstevel@tonic-gate 		return (0);
547c478bd9Sstevel@tonic-gate 	}
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate 	return (set_errno(EMDB_NODIS));
577c478bd9Sstevel@tonic-gate }
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate mdb_disasm_t *
mdb_dis_create(mdb_dis_ctor_f * ctor)607c478bd9Sstevel@tonic-gate mdb_dis_create(mdb_dis_ctor_f *ctor)
617c478bd9Sstevel@tonic-gate {
627c478bd9Sstevel@tonic-gate 	mdb_disasm_t *dp = mdb_zalloc(sizeof (mdb_disasm_t), UM_SLEEP);
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	if ((dp->dis_module = mdb.m_lmod) == NULL)
657c478bd9Sstevel@tonic-gate 		dp->dis_module = &mdb.m_rmod;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate 	if (ctor(dp) == 0) {
687c478bd9Sstevel@tonic-gate 		mdb_var_t *v = mdb_nv_lookup(&mdb.m_disasms, dp->dis_name);
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 		if (v != NULL) {
717c478bd9Sstevel@tonic-gate 			dp->dis_ops->dis_destroy(dp);
727c478bd9Sstevel@tonic-gate 			mdb_free(dp, sizeof (mdb_disasm_t));
737c478bd9Sstevel@tonic-gate 			(void) set_errno(EMDB_DISEXISTS);
747c478bd9Sstevel@tonic-gate 			return (NULL);
757c478bd9Sstevel@tonic-gate 		}
767c478bd9Sstevel@tonic-gate 
777c478bd9Sstevel@tonic-gate 		(void) mdb_nv_insert(&mdb.m_disasms, dp->dis_name, NULL,
787c478bd9Sstevel@tonic-gate 		    (uintptr_t)dp, MDB_NV_RDONLY | MDB_NV_SILENT);
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 		if (mdb.m_disasm == NULL) {
817c478bd9Sstevel@tonic-gate 			mdb.m_disasm = dp;
827c478bd9Sstevel@tonic-gate 		} else if (mdb.m_defdisasm != NULL &&
837c478bd9Sstevel@tonic-gate 		    strcmp(mdb.m_defdisasm, dp->dis_name) == 0) {
847c478bd9Sstevel@tonic-gate 			mdb.m_disasm = dp;
857c478bd9Sstevel@tonic-gate 			strfree(mdb.m_defdisasm);
867c478bd9Sstevel@tonic-gate 			mdb.m_defdisasm = NULL;
877c478bd9Sstevel@tonic-gate 		}
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 		return (dp);
907c478bd9Sstevel@tonic-gate 	}
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	mdb_free(dp, sizeof (mdb_disasm_t));
937c478bd9Sstevel@tonic-gate 	return (NULL);
947c478bd9Sstevel@tonic-gate }
957c478bd9Sstevel@tonic-gate 
967c478bd9Sstevel@tonic-gate void
mdb_dis_destroy(mdb_disasm_t * dp)977c478bd9Sstevel@tonic-gate mdb_dis_destroy(mdb_disasm_t *dp)
987c478bd9Sstevel@tonic-gate {
997c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_lookup(&mdb.m_disasms, dp->dis_name);
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate 	ASSERT(v != NULL);
1027c478bd9Sstevel@tonic-gate 	mdb_nv_remove(&mdb.m_disasms, v);
1037c478bd9Sstevel@tonic-gate 	dp->dis_ops->dis_destroy(dp);
1047c478bd9Sstevel@tonic-gate 	mdb_free(dp, sizeof (mdb_disasm_t));
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	if (mdb.m_disasm == dp)
1077c478bd9Sstevel@tonic-gate 		(void) mdb_dis_select("default");
1087c478bd9Sstevel@tonic-gate }
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate mdb_tgt_addr_t
mdb_dis_ins2str(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,char * buf,size_t len,mdb_tgt_addr_t addr)1117c478bd9Sstevel@tonic-gate mdb_dis_ins2str(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
1127c478bd9Sstevel@tonic-gate     char *buf, size_t len, mdb_tgt_addr_t addr)
1137c478bd9Sstevel@tonic-gate {
1147c478bd9Sstevel@tonic-gate 	return (dp->dis_ops->dis_ins2str(dp, t, as, buf, len, addr));
1157c478bd9Sstevel@tonic-gate }
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate mdb_tgt_addr_t
mdb_dis_previns(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint_t n)1187c478bd9Sstevel@tonic-gate mdb_dis_previns(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
1197c478bd9Sstevel@tonic-gate     mdb_tgt_addr_t addr, uint_t n)
1207c478bd9Sstevel@tonic-gate {
1217c478bd9Sstevel@tonic-gate 	return (dp->dis_ops->dis_previns(dp, t, as, addr, n));
1227c478bd9Sstevel@tonic-gate }
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate mdb_tgt_addr_t
mdb_dis_nextins(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,mdb_tgt_addr_t addr)1257c478bd9Sstevel@tonic-gate mdb_dis_nextins(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
1267c478bd9Sstevel@tonic-gate     mdb_tgt_addr_t addr)
1277c478bd9Sstevel@tonic-gate {
1287c478bd9Sstevel@tonic-gate 	return (dp->dis_ops->dis_nextins(dp, t, as, addr));
1297c478bd9Sstevel@tonic-gate }
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1327c478bd9Sstevel@tonic-gate int
cmd_dismode(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1337c478bd9Sstevel@tonic-gate cmd_dismode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1347c478bd9Sstevel@tonic-gate {
1357c478bd9Sstevel@tonic-gate 	if ((flags & DCMD_ADDRSPEC) || argc > 1)
1367c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	if (argc != 0) {
1397c478bd9Sstevel@tonic-gate 		const char *name;
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 		if (argv->a_type == MDB_TYPE_STRING)
1427c478bd9Sstevel@tonic-gate 			name = argv->a_un.a_str;
1437c478bd9Sstevel@tonic-gate 		else
1447c478bd9Sstevel@tonic-gate 			name = numtostr(argv->a_un.a_val, 10, NTOS_UNSIGNED);
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 		if (mdb_dis_select(name) == -1) {
1477c478bd9Sstevel@tonic-gate 			warn("failed to set disassembly mode");
1487c478bd9Sstevel@tonic-gate 			return (DCMD_ERR);
1497c478bd9Sstevel@tonic-gate 		}
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	mdb_printf("disassembly mode is %s (%s)\n",
1537c478bd9Sstevel@tonic-gate 	    mdb.m_disasm->dis_name, mdb.m_disasm->dis_desc);
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
1567c478bd9Sstevel@tonic-gate }
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1597c478bd9Sstevel@tonic-gate static int
print_dis(mdb_var_t * v,void * ignore)1607c478bd9Sstevel@tonic-gate print_dis(mdb_var_t *v, void *ignore)
1617c478bd9Sstevel@tonic-gate {
1627c478bd9Sstevel@tonic-gate 	mdb_disasm_t *dp = mdb_nv_get_cookie(v);
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	mdb_printf("%-24s - %s\n", dp->dis_name, dp->dis_desc);
1657c478bd9Sstevel@tonic-gate 	return (0);
1667c478bd9Sstevel@tonic-gate }
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1697c478bd9Sstevel@tonic-gate int
cmd_disasms(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1707c478bd9Sstevel@tonic-gate cmd_disasms(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1717c478bd9Sstevel@tonic-gate {
1727c478bd9Sstevel@tonic-gate 	if ((flags & DCMD_ADDRSPEC) || argc != 0)
1737c478bd9Sstevel@tonic-gate 		return (DCMD_USAGE);
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	mdb_nv_sort_iter(&mdb.m_disasms, print_dis, NULL, UM_SLEEP | UM_GC);
1767c478bd9Sstevel@tonic-gate 	return (DCMD_OK);
1777c478bd9Sstevel@tonic-gate }
1787c478bd9Sstevel@tonic-gate 
179dc0093f4Seschrock /*
180dc0093f4Seschrock  * Generic libdisasm disassembler interfaces.
181dc0093f4Seschrock  */
182dc0093f4Seschrock 
183dc0093f4Seschrock #define	DISBUFSZ	64
184dc0093f4Seschrock 
185dc0093f4Seschrock /*
186dc0093f4Seschrock  * Internal structure used by the read and lookup routines.
187dc0093f4Seschrock  */
188dc0093f4Seschrock typedef struct dis_buf {
189dc0093f4Seschrock 	mdb_tgt_t	*db_tgt;
190dc0093f4Seschrock 	mdb_tgt_as_t	db_as;
191dc0093f4Seschrock 	mdb_tgt_addr_t	db_addr;
192dc0093f4Seschrock 	mdb_tgt_addr_t	db_nextaddr;
193dc0093f4Seschrock 	uchar_t		db_buf[DISBUFSZ];
194dc0093f4Seschrock 	ssize_t		db_bufsize;
195108ba071Seschrock 	boolean_t	db_readerr;
196dc0093f4Seschrock } dis_buf_t;
197dc0093f4Seschrock 
198dc0093f4Seschrock /*
199dc0093f4Seschrock  * Disassembler support routine for lookup up an address.  Rely on mdb's "%a"
200dc0093f4Seschrock  * qualifier to convert the address to a symbol.
201dc0093f4Seschrock  */
202dc0093f4Seschrock /*ARGSUSED*/
203dc0093f4Seschrock static int
libdisasm_lookup(void * data,uint64_t addr,char * buf,size_t buflen,uint64_t * start,size_t * len)204dc0093f4Seschrock libdisasm_lookup(void *data, uint64_t addr, char *buf, size_t buflen,
205dc0093f4Seschrock     uint64_t *start, size_t *len)
206dc0093f4Seschrock {
207d79705c6Seschrock 	char c;
208dc0093f4Seschrock 	GElf_Sym sym;
209dc0093f4Seschrock 
210d79705c6Seschrock 	if (buf != NULL) {
211dc0093f4Seschrock #ifdef __sparc
212dc0093f4Seschrock 		uint32_t instr[3];
213dc0093f4Seschrock 		uint32_t dtrace_id;
214dc0093f4Seschrock 
215dc0093f4Seschrock 		/*
216dc0093f4Seschrock 		 * On SPARC, DTrace FBT trampoline entries have a sethi/or pair
217dc0093f4Seschrock 		 * that indicates the dtrace probe id; this may appear as the
218dc0093f4Seschrock 		 * first two instructions or one instruction into the
219dc0093f4Seschrock 		 * trampoline.
220dc0093f4Seschrock 		 */
221dc0093f4Seschrock 		if (mdb_vread(instr, sizeof (instr), (uintptr_t)addr) ==
222dc0093f4Seschrock 		    sizeof (instr)) {
223dc0093f4Seschrock 			if ((instr[0] & 0xfffc0000) == 0x11000000 &&
224dc0093f4Seschrock 			    (instr[1] & 0xffffe000) == 0x90122000) {
225dc0093f4Seschrock 				dtrace_id = (instr[0] << 10) |
226dc0093f4Seschrock 				    (instr[1] & 0x1fff);
227dc0093f4Seschrock 				(void) mdb_snprintf(buf, sizeof (buf), "dt=%#x",
228dc0093f4Seschrock 				    dtrace_id);
229dc0093f4Seschrock 				goto out;
230dc0093f4Seschrock 			} else if ((instr[1] & 0xfffc0000) == 0x11000000 &&
231dc0093f4Seschrock 			    (instr[2] & 0xffffe000) == 0x90122000) {
232dc0093f4Seschrock 				dtrace_id = (instr[1] << 10) |
233dc0093f4Seschrock 				    (instr[2] & 0x1fff);
234dc0093f4Seschrock 				(void) mdb_snprintf(buf, sizeof (buf), "dt=%#x",
235dc0093f4Seschrock 				    dtrace_id);
236dc0093f4Seschrock 				goto out;
237dc0093f4Seschrock 			}
238dc0093f4Seschrock 		}
239dc0093f4Seschrock #endif
240d79705c6Seschrock 		(void) mdb_snprintf(buf, buflen, "%a", (uintptr_t)addr);
241dc0093f4Seschrock 	}
242dc0093f4Seschrock 
243dc0093f4Seschrock #ifdef __sparc
244dc0093f4Seschrock out:
245dc0093f4Seschrock #endif
246dc0093f4Seschrock 	if (mdb_lookup_by_addr(addr, MDB_SYM_FUZZY, &c, 1, &sym) < 0)
247dc0093f4Seschrock 		return (-1);
248dc0093f4Seschrock 	if (start != NULL)
249dc0093f4Seschrock 		*start = sym.st_value;
250dc0093f4Seschrock 	if (len != NULL)
251dc0093f4Seschrock 		*len = sym.st_size;
252dc0093f4Seschrock 
253dc0093f4Seschrock 	return (0);
254dc0093f4Seschrock }
255dc0093f4Seschrock 
256dc0093f4Seschrock /*
257dc0093f4Seschrock  * Disassembler support routine for reading from the target.  Rather than having
258dc0093f4Seschrock  * to read one byte at a time, we read from the address space in chunks.  If the
259dc0093f4Seschrock  * current address doesn't lie within our buffer range, we read in the chunk
260dc0093f4Seschrock  * starting from the given address.
261dc0093f4Seschrock  */
262dc0093f4Seschrock static int
libdisasm_read(void * data,uint64_t pc,void * buf,size_t buflen)263dc0093f4Seschrock libdisasm_read(void *data, uint64_t pc, void *buf, size_t buflen)
264dc0093f4Seschrock {
265dc0093f4Seschrock 	dis_buf_t *db = data;
266dc0093f4Seschrock 	size_t offset;
267dc0093f4Seschrock 	size_t len;
268dc0093f4Seschrock 
269dc0093f4Seschrock 	if (pc - db->db_addr >= db->db_bufsize) {
270108ba071Seschrock 		if (mdb_tgt_aread(db->db_tgt, db->db_as, db->db_buf,
271108ba071Seschrock 		    sizeof (db->db_buf), pc) != -1) {
272108ba071Seschrock 			db->db_bufsize = sizeof (db->db_buf);
273108ba071Seschrock 		} else if (mdb_tgt_aread(db->db_tgt, db->db_as, db->db_buf,
274108ba071Seschrock 		    buflen, pc) != -1) {
275108ba071Seschrock 			db->db_bufsize = buflen;
276108ba071Seschrock 		} else {
277108ba071Seschrock 			if (!db->db_readerr)
278108ba071Seschrock 				mdb_warn("failed to read instruction at %#lr",
279108ba071Seschrock 				    (uintptr_t)pc);
280108ba071Seschrock 			db->db_readerr = B_TRUE;
281dc0093f4Seschrock 			return (-1);
282108ba071Seschrock 		}
283dc0093f4Seschrock 		db->db_addr = pc;
284dc0093f4Seschrock 	}
285dc0093f4Seschrock 
286dc0093f4Seschrock 	offset = pc - db->db_addr;
287dc0093f4Seschrock 
288dc0093f4Seschrock 	len = MIN(buflen, db->db_bufsize - offset);
289dc0093f4Seschrock 
29080148899SSurya Prakki 	(void) memcpy(buf, (char *)db->db_buf + offset, len);
291dc0093f4Seschrock 	db->db_nextaddr = pc + len;
292dc0093f4Seschrock 
293dc0093f4Seschrock 	return (len);
294dc0093f4Seschrock }
295dc0093f4Seschrock 
296dc0093f4Seschrock static mdb_tgt_addr_t
libdisasm_ins2str(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,char * buf,size_t len,mdb_tgt_addr_t pc)297dc0093f4Seschrock libdisasm_ins2str(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
298dc0093f4Seschrock     char *buf, size_t len, mdb_tgt_addr_t pc)
299dc0093f4Seschrock {
300dc0093f4Seschrock 	dis_handle_t *dhp = dp->dis_data;
301dc0093f4Seschrock 	dis_buf_t db = { 0 };
302f576acbeSdmick 	const char *p;
303dc0093f4Seschrock 
304dc0093f4Seschrock 	/*
305dc0093f4Seschrock 	 * Set the libdisasm data to point to our buffer.  This will be
306dc0093f4Seschrock 	 * passed as the first argument to the lookup and read functions.
307dc0093f4Seschrock 	 */
308dc0093f4Seschrock 	db.db_tgt = t;
309dc0093f4Seschrock 	db.db_as = as;
310dc0093f4Seschrock 
311dc0093f4Seschrock 	dis_set_data(dhp, &db);
312dc0093f4Seschrock 
313f576acbeSdmick 	if ((p = mdb_tgt_name(t)) != NULL && strcmp(p, "proc") == 0) {
314e0070315Sdmick 		/* check for ELF ET_REL type; turn on NOIMMSYM if so */
315e0070315Sdmick 
316e0070315Sdmick 		GElf_Ehdr 	leh;
317e0070315Sdmick 
318e0070315Sdmick 		if (mdb_tgt_getxdata(t, "ehdr", &leh, sizeof (leh)) != -1 &&
319e0070315Sdmick 		    leh.e_type == ET_REL)  {
320e0070315Sdmick 			dis_flags_set(dhp, DIS_NOIMMSYM);
321e0070315Sdmick 		} else {
322e0070315Sdmick 			dis_flags_clear(dhp, DIS_NOIMMSYM);
323e0070315Sdmick 		}
324e0070315Sdmick 	}
325e0070315Sdmick 
326dc0093f4Seschrock 	/*
327108ba071Seschrock 	 * Attempt to disassemble the instruction.  If this fails because of an
328108ba071Seschrock 	 * unknown opcode, drive on anyway.  If it fails because we couldn't
329108ba071Seschrock 	 * read from the target, bail out immediately.
330dc0093f4Seschrock 	 */
331dc0093f4Seschrock 	if (dis_disassemble(dhp, pc, buf, len) != 0)
332dc0093f4Seschrock 		(void) mdb_snprintf(buf, len,
333dc0093f4Seschrock 		    "***ERROR--unknown op code***");
334dc0093f4Seschrock 
335108ba071Seschrock 	if (db.db_readerr)
336108ba071Seschrock 		return (pc);
337108ba071Seschrock 
338dc0093f4Seschrock 	/*
339dc0093f4Seschrock 	 * Return the updated location
340dc0093f4Seschrock 	 */
341dc0093f4Seschrock 	return (db.db_nextaddr);
342dc0093f4Seschrock }
343dc0093f4Seschrock 
344dc0093f4Seschrock static mdb_tgt_addr_t
libdisasm_previns(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,mdb_tgt_addr_t pc,uint_t n)345dc0093f4Seschrock libdisasm_previns(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
346dc0093f4Seschrock     mdb_tgt_addr_t pc, uint_t n)
347dc0093f4Seschrock {
348dc0093f4Seschrock 	dis_handle_t *dhp = dp->dis_data;
349dc0093f4Seschrock 	dis_buf_t db = { 0 };
350dc0093f4Seschrock 
351dc0093f4Seschrock 	/*
352dc0093f4Seschrock 	 * Set the libdisasm data to point to our buffer.  This will be
353dc0093f4Seschrock 	 * passed as the first argument to the lookup and read functions.
354108ba071Seschrock 	 * We set 'readerr' to B_TRUE to turn off the mdb_warn() in
355108ba071Seschrock 	 * libdisasm_read, because the code works by probing backwards until a
356108ba071Seschrock 	 * valid address is found.
357dc0093f4Seschrock 	 */
358dc0093f4Seschrock 	db.db_tgt = t;
359dc0093f4Seschrock 	db.db_as = as;
360108ba071Seschrock 	db.db_readerr = B_TRUE;
361dc0093f4Seschrock 
362dc0093f4Seschrock 	dis_set_data(dhp, &db);
363dc0093f4Seschrock 
364dc0093f4Seschrock 	return (dis_previnstr(dhp, pc, n));
365dc0093f4Seschrock }
366dc0093f4Seschrock 
367dc0093f4Seschrock /*ARGSUSED*/
368dc0093f4Seschrock static mdb_tgt_addr_t
libdisasm_nextins(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,mdb_tgt_addr_t pc)369dc0093f4Seschrock libdisasm_nextins(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
370dc0093f4Seschrock     mdb_tgt_addr_t pc)
371dc0093f4Seschrock {
372dc0093f4Seschrock 	mdb_tgt_addr_t npc;
373dc0093f4Seschrock 	char c;
374dc0093f4Seschrock 
375dc0093f4Seschrock 	if ((npc = libdisasm_ins2str(dp, t, as, &c, 1, pc)) == pc)
376dc0093f4Seschrock 		return (pc);
377dc0093f4Seschrock 
378dc0093f4Seschrock 	/*
379dc0093f4Seschrock 	 * Probe the address to make sure we can read something from it - we
380dc0093f4Seschrock 	 * want the address we return to actually contain something.
381dc0093f4Seschrock 	 */
382dc0093f4Seschrock 	if (mdb_tgt_aread(t, as, &c, 1, npc) != 1)
383dc0093f4Seschrock 		return (pc);
384dc0093f4Seschrock 
385dc0093f4Seschrock 	return (npc);
386dc0093f4Seschrock }
387dc0093f4Seschrock 
388dc0093f4Seschrock static void
libdisasm_destroy(mdb_disasm_t * dp)389dc0093f4Seschrock libdisasm_destroy(mdb_disasm_t *dp)
390dc0093f4Seschrock {
391dc0093f4Seschrock 	dis_handle_t *dhp = dp->dis_data;
392dc0093f4Seschrock 
393dc0093f4Seschrock 	dis_handle_destroy(dhp);
394dc0093f4Seschrock }
395dc0093f4Seschrock 
396dc0093f4Seschrock static const mdb_dis_ops_t libdisasm_ops = {
397dc0093f4Seschrock 	libdisasm_destroy,
398dc0093f4Seschrock 	libdisasm_ins2str,
399dc0093f4Seschrock 	libdisasm_previns,
400dc0093f4Seschrock 	libdisasm_nextins
401dc0093f4Seschrock };
402dc0093f4Seschrock 
403dc0093f4Seschrock /*
404dc0093f4Seschrock  * Generic function for creating a libdisasm-backed disassembler.  Creates an
405dc0093f4Seschrock  * MDB disassembler with the given name backed by libdis with the given flags.
406dc0093f4Seschrock  */
407dc0093f4Seschrock static int
libdisasm_create(mdb_disasm_t * dp,const char * name,const char * desc,int flags)408dc0093f4Seschrock libdisasm_create(mdb_disasm_t *dp, const char *name,
409dc0093f4Seschrock 		const char *desc, int flags)
410dc0093f4Seschrock {
411dc0093f4Seschrock 	if ((dp->dis_data = dis_handle_create(flags, NULL, libdisasm_lookup,
412dc0093f4Seschrock 	    libdisasm_read)) == NULL)
413dc0093f4Seschrock 		return (-1);
414dc0093f4Seschrock 
415dc0093f4Seschrock 	dp->dis_name = name;
416dc0093f4Seschrock 	dp->dis_ops = &libdisasm_ops;
417dc0093f4Seschrock 	dp->dis_desc = desc;
418dc0093f4Seschrock 
419dc0093f4Seschrock 	return (0);
420dc0093f4Seschrock }
421dc0093f4Seschrock 
422dc0093f4Seschrock #if defined(__i386) || defined(__amd64)
423dc0093f4Seschrock static int
ia16_create(mdb_disasm_t * dp)424*7aa76ffcSBryan Cantrill ia16_create(mdb_disasm_t *dp)
425*7aa76ffcSBryan Cantrill {
426*7aa76ffcSBryan Cantrill 	return (libdisasm_create(dp,
427*7aa76ffcSBryan Cantrill 	    "ia16",
428*7aa76ffcSBryan Cantrill 	    "Intel 16-bit disassembler",
429*7aa76ffcSBryan Cantrill 	    DIS_X86_SIZE16));
430*7aa76ffcSBryan Cantrill }
431*7aa76ffcSBryan Cantrill 
432*7aa76ffcSBryan Cantrill static int
ia32_create(mdb_disasm_t * dp)433dc0093f4Seschrock ia32_create(mdb_disasm_t *dp)
434dc0093f4Seschrock {
435dc0093f4Seschrock 	return (libdisasm_create(dp,
436dc0093f4Seschrock 	    "ia32",
437dc0093f4Seschrock 	    "Intel 32-bit disassembler",
438dc0093f4Seschrock 	    DIS_X86_SIZE32));
439dc0093f4Seschrock }
440dc0093f4Seschrock #endif
441dc0093f4Seschrock 
442dc0093f4Seschrock #if defined(__amd64)
443dc0093f4Seschrock static int
amd64_create(mdb_disasm_t * dp)444dc0093f4Seschrock amd64_create(mdb_disasm_t *dp)
445dc0093f4Seschrock {
446dc0093f4Seschrock 	return (libdisasm_create(dp,
447dc0093f4Seschrock 	    "amd64",
448dc0093f4Seschrock 	    "AMD64 and IA32e 64-bit disassembler",
449dc0093f4Seschrock 	    DIS_X86_SIZE64));
450dc0093f4Seschrock }
451dc0093f4Seschrock #endif
452dc0093f4Seschrock 
453dc0093f4Seschrock #if defined(__sparc)
454dc0093f4Seschrock static int
sparc1_create(mdb_disasm_t * dp)455dc0093f4Seschrock sparc1_create(mdb_disasm_t *dp)
456dc0093f4Seschrock {
457dc0093f4Seschrock 	return (libdisasm_create(dp,
458dc0093f4Seschrock 	    "1",
459dc0093f4Seschrock 	    "SPARC-v8 disassembler",
460dc0093f4Seschrock 	    DIS_SPARC_V8));
461dc0093f4Seschrock }
462dc0093f4Seschrock 
463dc0093f4Seschrock static int
sparc2_create(mdb_disasm_t * dp)464dc0093f4Seschrock sparc2_create(mdb_disasm_t *dp)
465dc0093f4Seschrock {
466dc0093f4Seschrock 	return (libdisasm_create(dp,
467dc0093f4Seschrock 	    "2",
468dc0093f4Seschrock 	    "SPARC-v9 disassembler",
469dc0093f4Seschrock 	    DIS_SPARC_V9));
470dc0093f4Seschrock }
471dc0093f4Seschrock 
472dc0093f4Seschrock static int
sparc4_create(mdb_disasm_t * dp)473dc0093f4Seschrock sparc4_create(mdb_disasm_t *dp)
474dc0093f4Seschrock {
475dc0093f4Seschrock 	return (libdisasm_create(dp,
476dc0093f4Seschrock 	    "4",
477dc0093f4Seschrock 	    "UltraSPARC1-v9 disassembler",
478dc0093f4Seschrock 	    DIS_SPARC_V9 | DIS_SPARC_V9_SGI));
479dc0093f4Seschrock }
480dc0093f4Seschrock 
481dc0093f4Seschrock static int
sparcv8_create(mdb_disasm_t * dp)482dc0093f4Seschrock sparcv8_create(mdb_disasm_t *dp)
483dc0093f4Seschrock {
484dc0093f4Seschrock 	return (libdisasm_create(dp,
485dc0093f4Seschrock 	    "v8",
486dc0093f4Seschrock 	    "SPARC-v8 disassembler",
487dc0093f4Seschrock 	    DIS_SPARC_V8));
488dc0093f4Seschrock }
489dc0093f4Seschrock 
490dc0093f4Seschrock static int
sparcv9_create(mdb_disasm_t * dp)491dc0093f4Seschrock sparcv9_create(mdb_disasm_t *dp)
492dc0093f4Seschrock {
493dc0093f4Seschrock 	return (libdisasm_create(dp,
494dc0093f4Seschrock 	    "v9",
495dc0093f4Seschrock 	    "SPARC-v9 disassembler",
496dc0093f4Seschrock 	    DIS_SPARC_V9));
497dc0093f4Seschrock }
498dc0093f4Seschrock 
499dc0093f4Seschrock static int
sparcv9plus_create(mdb_disasm_t * dp)500dc0093f4Seschrock sparcv9plus_create(mdb_disasm_t *dp)
501dc0093f4Seschrock {
502dc0093f4Seschrock 	return (libdisasm_create(dp,
503dc0093f4Seschrock 	    "v9plus",
504dc0093f4Seschrock 	    "UltraSPARC1-v9 disassembler",
505dc0093f4Seschrock 	    DIS_SPARC_V9 | DIS_SPARC_V9_SGI));
506dc0093f4Seschrock }
507dc0093f4Seschrock #endif
508dc0093f4Seschrock 
5097c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5107c478bd9Sstevel@tonic-gate static void
defdis_destroy(mdb_disasm_t * dp)5117c478bd9Sstevel@tonic-gate defdis_destroy(mdb_disasm_t *dp)
5127c478bd9Sstevel@tonic-gate {
5137c478bd9Sstevel@tonic-gate 	/* Nothing to do here */
5147c478bd9Sstevel@tonic-gate }
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5177c478bd9Sstevel@tonic-gate static mdb_tgt_addr_t
defdis_ins2str(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,char * buf,size_t len,mdb_tgt_addr_t addr)5187c478bd9Sstevel@tonic-gate defdis_ins2str(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
5197c478bd9Sstevel@tonic-gate     char *buf, size_t len, mdb_tgt_addr_t addr)
5207c478bd9Sstevel@tonic-gate {
5217c478bd9Sstevel@tonic-gate 	return (addr);
5227c478bd9Sstevel@tonic-gate }
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5257c478bd9Sstevel@tonic-gate static mdb_tgt_addr_t
defdis_previns(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint_t n)5267c478bd9Sstevel@tonic-gate defdis_previns(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
5277c478bd9Sstevel@tonic-gate     mdb_tgt_addr_t addr, uint_t n)
5287c478bd9Sstevel@tonic-gate {
5297c478bd9Sstevel@tonic-gate 	return (addr);
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5337c478bd9Sstevel@tonic-gate static mdb_tgt_addr_t
defdis_nextins(mdb_disasm_t * dp,mdb_tgt_t * t,mdb_tgt_as_t as,mdb_tgt_addr_t addr)5347c478bd9Sstevel@tonic-gate defdis_nextins(mdb_disasm_t *dp, mdb_tgt_t *t, mdb_tgt_as_t as,
5357c478bd9Sstevel@tonic-gate     mdb_tgt_addr_t addr)
5367c478bd9Sstevel@tonic-gate {
5377c478bd9Sstevel@tonic-gate 	return (addr);
5387c478bd9Sstevel@tonic-gate }
5397c478bd9Sstevel@tonic-gate 
5407c478bd9Sstevel@tonic-gate static const mdb_dis_ops_t defdis_ops = {
5417c478bd9Sstevel@tonic-gate 	defdis_destroy,
5427c478bd9Sstevel@tonic-gate 	defdis_ins2str,
5437c478bd9Sstevel@tonic-gate 	defdis_previns,
5447c478bd9Sstevel@tonic-gate 	defdis_nextins
5457c478bd9Sstevel@tonic-gate };
5467c478bd9Sstevel@tonic-gate 
5477c478bd9Sstevel@tonic-gate static int
defdis_create(mdb_disasm_t * dp)5487c478bd9Sstevel@tonic-gate defdis_create(mdb_disasm_t *dp)
5497c478bd9Sstevel@tonic-gate {
5507c478bd9Sstevel@tonic-gate 	dp->dis_name = "default";
5517c478bd9Sstevel@tonic-gate 	dp->dis_desc = "default no-op disassembler";
5527c478bd9Sstevel@tonic-gate 	dp->dis_ops = &defdis_ops;
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	return (0);
5557c478bd9Sstevel@tonic-gate }
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate mdb_dis_ctor_f *const mdb_dis_builtins[] = {
558dc0093f4Seschrock 	defdis_create,
5597c478bd9Sstevel@tonic-gate #if defined(__amd64)
560*7aa76ffcSBryan Cantrill 	ia16_create,
5617c478bd9Sstevel@tonic-gate 	ia32_create,
5627c478bd9Sstevel@tonic-gate 	amd64_create,
5637c478bd9Sstevel@tonic-gate #elif defined(__i386)
564*7aa76ffcSBryan Cantrill 	ia16_create,
5657c478bd9Sstevel@tonic-gate 	ia32_create,
566dc0093f4Seschrock #elif defined(__sparc)
567dc0093f4Seschrock 	sparc1_create,
568dc0093f4Seschrock 	sparc2_create,
569dc0093f4Seschrock 	sparc4_create,
570dc0093f4Seschrock 	sparcv8_create,
571dc0093f4Seschrock 	sparcv9_create,
572dc0093f4Seschrock 	sparcv9plus_create,
5737c478bd9Sstevel@tonic-gate #endif
5747c478bd9Sstevel@tonic-gate 	NULL
5757c478bd9Sstevel@tonic-gate };
576