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
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*5ae68c69Sjohnlev  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * Routines for manipulating the kmdb-specific aspects of dmods.
317c478bd9Sstevel@tonic-gate  */
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <sys/param.h>
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #include <mdb/mdb_target_impl.h>
367c478bd9Sstevel@tonic-gate #include <kmdb/kmdb_module.h>
377c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
387c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
397c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate typedef struct kmod_symarg {
427c478bd9Sstevel@tonic-gate 	mdb_tgt_sym_f *sym_cb;		/* Caller's callback function */
437c478bd9Sstevel@tonic-gate 	void *sym_data;			/* Callback function argument */
447c478bd9Sstevel@tonic-gate 	uint_t sym_type;		/* Symbol type/binding filter */
457c478bd9Sstevel@tonic-gate 	mdb_syminfo_t sym_info;		/* Symbol id and table id */
467c478bd9Sstevel@tonic-gate 	const char *sym_obj;		/* Containing object */
477c478bd9Sstevel@tonic-gate } kmod_symarg_t;
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate void
507c478bd9Sstevel@tonic-gate kmdb_module_path_set(const char **path, size_t pathlen)
517c478bd9Sstevel@tonic-gate {
527c478bd9Sstevel@tonic-gate 	kmdb_wr_path_t *wr;
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate 	wr = mdb_zalloc(sizeof (kmdb_wr_path_t), UM_SLEEP);
557c478bd9Sstevel@tonic-gate 	wr->dpth_node.wn_task = WNTASK_DMOD_PATH_CHANGE;
567c478bd9Sstevel@tonic-gate 	wr->dpth_path = mdb_path_dup(path, pathlen, &wr->dpth_pathlen);
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate 	kmdb_wr_driver_notify(wr);
597c478bd9Sstevel@tonic-gate }
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate void
627c478bd9Sstevel@tonic-gate kmdb_module_path_ack(kmdb_wr_path_t *dpth)
637c478bd9Sstevel@tonic-gate {
647c478bd9Sstevel@tonic-gate 	if (dpth->dpth_path != NULL)
657c478bd9Sstevel@tonic-gate 		mdb_path_free(dpth->dpth_path, dpth->dpth_pathlen);
667c478bd9Sstevel@tonic-gate 	mdb_free(dpth, sizeof (kmdb_wr_path_t));
677c478bd9Sstevel@tonic-gate }
687c478bd9Sstevel@tonic-gate 
69*5ae68c69Sjohnlev static kmdb_modctl_t *
70*5ae68c69Sjohnlev kmdb_module_lookup_loaded(const char *name)
71*5ae68c69Sjohnlev {
72*5ae68c69Sjohnlev 	kmdb_modctl_t *kmc;
73*5ae68c69Sjohnlev 	mdb_var_t *v;
74*5ae68c69Sjohnlev 
75*5ae68c69Sjohnlev 	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, name)) == NULL)
76*5ae68c69Sjohnlev 		return (NULL);
77*5ae68c69Sjohnlev 
78*5ae68c69Sjohnlev 	kmc = MDB_NV_COOKIE(v);
79*5ae68c69Sjohnlev 	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
80*5ae68c69Sjohnlev 		return (NULL);
81*5ae68c69Sjohnlev 
82*5ae68c69Sjohnlev 	return (kmc);
83*5ae68c69Sjohnlev }
84*5ae68c69Sjohnlev 
857c478bd9Sstevel@tonic-gate /*
867c478bd9Sstevel@tonic-gate  * Given an address, try to match it up with a dmod symbol.
877c478bd9Sstevel@tonic-gate  */
887c478bd9Sstevel@tonic-gate int
897c478bd9Sstevel@tonic-gate kmdb_module_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
907c478bd9Sstevel@tonic-gate     size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip)
917c478bd9Sstevel@tonic-gate {
927c478bd9Sstevel@tonic-gate 	kmdb_modctl_t *sym_kmc = NULL;
937c478bd9Sstevel@tonic-gate 	GElf_Sym sym;
947c478bd9Sstevel@tonic-gate 	uint_t symid;
957c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
967c478bd9Sstevel@tonic-gate 	const char *name;
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	mdb_nv_rewind(&mdb.m_dmodctl);
997c478bd9Sstevel@tonic-gate 	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
1007c478bd9Sstevel@tonic-gate 		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate 		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
1037c478bd9Sstevel@tonic-gate 			continue;
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 		if (mdb_gelf_symtab_lookup_by_addr(kmc->kmc_symtab, addr, flags,
1067c478bd9Sstevel@tonic-gate 		    buf, nbytes, symp, &sip->sym_id) != 0 ||
1077c478bd9Sstevel@tonic-gate 		    symp->st_value == 0)
1087c478bd9Sstevel@tonic-gate 			continue;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 		if (flags & MDB_TGT_SYM_EXACT) {
1117c478bd9Sstevel@tonic-gate 			sym_kmc = kmc;
1127c478bd9Sstevel@tonic-gate 			goto found;
1137c478bd9Sstevel@tonic-gate 		}
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 		/*
1167c478bd9Sstevel@tonic-gate 		 * If this is the first match we've found, or if this symbol is
1177c478bd9Sstevel@tonic-gate 		 * closer to the specified address than the last one we found,
1187c478bd9Sstevel@tonic-gate 		 * use it.
1197c478bd9Sstevel@tonic-gate 		 */
1207c478bd9Sstevel@tonic-gate 		if (sym_kmc == NULL || mdb_gelf_sym_closer(symp, &sym, addr)) {
1217c478bd9Sstevel@tonic-gate 			sym_kmc = kmc;
1227c478bd9Sstevel@tonic-gate 			sym = *symp;
1237c478bd9Sstevel@tonic-gate 			symid = sip->sym_id;
1247c478bd9Sstevel@tonic-gate 		}
1257c478bd9Sstevel@tonic-gate 	}
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate 	if (sym_kmc == NULL)
1287c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_NOSYMADDR));
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	*symp = sym;
1317c478bd9Sstevel@tonic-gate 	sip->sym_id = symid;
1327c478bd9Sstevel@tonic-gate 
1337c478bd9Sstevel@tonic-gate found:
1347c478bd9Sstevel@tonic-gate 	/*
1357c478bd9Sstevel@tonic-gate 	 * Once we've found something, copy the final name into the caller's
1367c478bd9Sstevel@tonic-gate 	 * buffer, prefixed with a marker identifying this as a dmod symbol.
1377c478bd9Sstevel@tonic-gate 	 */
1387c478bd9Sstevel@tonic-gate 	if (buf != NULL) {
1397c478bd9Sstevel@tonic-gate 		name = mdb_gelf_sym_name(sym_kmc->kmc_symtab, symp);
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 		(void) mdb_snprintf(buf, nbytes, "DMOD`%s`%s",
1427c478bd9Sstevel@tonic-gate 		    sym_kmc->kmc_modname, name);
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 	sip->sym_table = MDB_TGT_SYMTAB;
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	return (0);
1477c478bd9Sstevel@tonic-gate }
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate /*
1507c478bd9Sstevel@tonic-gate  * Locate a given dmod symbol
1517c478bd9Sstevel@tonic-gate  */
1527c478bd9Sstevel@tonic-gate int
1537c478bd9Sstevel@tonic-gate kmdb_module_lookup_by_name(const char *obj, const char *name, GElf_Sym *symp,
1547c478bd9Sstevel@tonic-gate     mdb_syminfo_t *sip)
1557c478bd9Sstevel@tonic-gate {
1567c478bd9Sstevel@tonic-gate 	kmdb_modctl_t *kmc;
1577c478bd9Sstevel@tonic-gate 
158*5ae68c69Sjohnlev 	if ((kmc = kmdb_module_lookup_loaded(obj)) == NULL)
1597c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_NOSYMADDR));
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	if (mdb_gelf_symtab_lookup_by_name(kmc->kmc_symtab, name,
1627c478bd9Sstevel@tonic-gate 	    symp, &sip->sym_id) == 0) {
1637c478bd9Sstevel@tonic-gate 		sip->sym_table = MDB_TGT_SYMTAB;
1647c478bd9Sstevel@tonic-gate 		return (0);
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	return (set_errno(EMDB_NOSYM));
1687c478bd9Sstevel@tonic-gate }
1697c478bd9Sstevel@tonic-gate 
170*5ae68c69Sjohnlev ctf_file_t *
171*5ae68c69Sjohnlev kmdb_module_addr_to_ctf(uintptr_t addr)
172*5ae68c69Sjohnlev {
173*5ae68c69Sjohnlev 	mdb_var_t *v;
174*5ae68c69Sjohnlev 
175*5ae68c69Sjohnlev 	mdb_nv_rewind(&mdb.m_dmodctl);
176*5ae68c69Sjohnlev 	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
177*5ae68c69Sjohnlev 		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
178*5ae68c69Sjohnlev 		struct module *mp;
179*5ae68c69Sjohnlev 
180*5ae68c69Sjohnlev 		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
181*5ae68c69Sjohnlev 			continue;
182*5ae68c69Sjohnlev 
183*5ae68c69Sjohnlev 		mp = kmc->kmc_modctl->mod_mp;
184*5ae68c69Sjohnlev 		if (addr - (uintptr_t)mp->text < mp->text_size ||
185*5ae68c69Sjohnlev 		    addr - (uintptr_t)mp->data < mp->data_size ||
186*5ae68c69Sjohnlev 		    addr - mp->bss < mp->bss_size) {
187*5ae68c69Sjohnlev 			ctf_file_t *ctfp = kmc->kmc_mod->mod_ctfp;
188*5ae68c69Sjohnlev 
189*5ae68c69Sjohnlev 			if (ctfp == NULL) {
190*5ae68c69Sjohnlev 				(void) set_errno(EMDB_NOCTF);
191*5ae68c69Sjohnlev 				return (NULL);
192*5ae68c69Sjohnlev 			}
193*5ae68c69Sjohnlev 
194*5ae68c69Sjohnlev 			return (ctfp);
195*5ae68c69Sjohnlev 		}
196*5ae68c69Sjohnlev 	}
197*5ae68c69Sjohnlev 
198*5ae68c69Sjohnlev 	(void) set_errno(EMDB_NOMAP);
199*5ae68c69Sjohnlev 	return (NULL);
200*5ae68c69Sjohnlev }
201*5ae68c69Sjohnlev 
202*5ae68c69Sjohnlev ctf_file_t *
203*5ae68c69Sjohnlev kmdb_module_name_to_ctf(const char *obj)
204*5ae68c69Sjohnlev {
205*5ae68c69Sjohnlev 	kmdb_modctl_t *kmc;
206*5ae68c69Sjohnlev 	ctf_file_t *ctfp;
207*5ae68c69Sjohnlev 
208*5ae68c69Sjohnlev 	if ((kmc = kmdb_module_lookup_loaded(obj)) == NULL) {
209*5ae68c69Sjohnlev 		(void) set_errno(EMDB_NOOBJ);
210*5ae68c69Sjohnlev 		return (NULL);
211*5ae68c69Sjohnlev 	}
212*5ae68c69Sjohnlev 
213*5ae68c69Sjohnlev 	if ((ctfp = kmc->kmc_mod->mod_ctfp) == NULL) {
214*5ae68c69Sjohnlev 		(void) set_errno(EMDB_NOCTF);
215*5ae68c69Sjohnlev 		return (NULL);
216*5ae68c69Sjohnlev 	}
217*5ae68c69Sjohnlev 
218*5ae68c69Sjohnlev 	return (ctfp);
219*5ae68c69Sjohnlev }
220*5ae68c69Sjohnlev 
2217c478bd9Sstevel@tonic-gate static int
2227c478bd9Sstevel@tonic-gate kmdb_module_symtab_func(void *data, const GElf_Sym *sym, const char *name,
2237c478bd9Sstevel@tonic-gate     uint_t id)
2247c478bd9Sstevel@tonic-gate {
2257c478bd9Sstevel@tonic-gate 	kmod_symarg_t *arg = data;
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	if (mdb_tgt_sym_match(sym, arg->sym_type)) {
2287c478bd9Sstevel@tonic-gate 		arg->sym_info.sym_id = id;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 		return (arg->sym_cb(arg->sym_data, sym, name, &arg->sym_info,
2317c478bd9Sstevel@tonic-gate 		    arg->sym_obj));
2327c478bd9Sstevel@tonic-gate 	}
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	return (0);
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate int
2387c478bd9Sstevel@tonic-gate kmdb_module_symbol_iter(const char *obj, uint_t type, mdb_tgt_sym_f *cb,
2397c478bd9Sstevel@tonic-gate     void *p)
2407c478bd9Sstevel@tonic-gate {
2417c478bd9Sstevel@tonic-gate 	kmdb_modctl_t *kmc;
2427c478bd9Sstevel@tonic-gate 	kmod_symarg_t arg;
2437c478bd9Sstevel@tonic-gate 	mdb_var_t *v;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, obj)) == NULL)
2467c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_NOMOD));
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	kmc = MDB_NV_COOKIE(v);
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
2517c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_NOMOD));
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 	arg.sym_cb = cb;
2547c478bd9Sstevel@tonic-gate 	arg.sym_data = p;
2557c478bd9Sstevel@tonic-gate 	arg.sym_type = type;
2567c478bd9Sstevel@tonic-gate 	arg.sym_info.sym_table = kmc->kmc_symtab->gst_tabid;
2577c478bd9Sstevel@tonic-gate 	arg.sym_obj = obj;
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate 	mdb_gelf_symtab_iter(kmc->kmc_symtab, kmdb_module_symtab_func, &arg);
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	return (0);
2627c478bd9Sstevel@tonic-gate }
263