1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/*
30 * Routines for manipulating the kmdb-specific aspects of dmods.
31 */
32
33#include <sys/param.h>
34
35#include <mdb/mdb_target_impl.h>
36#include <kmdb/kmdb_module.h>
37#include <mdb/mdb_debug.h>
38#include <mdb/mdb_err.h>
39#include <mdb/mdb.h>
40
41typedef struct kmod_symarg {
42	mdb_tgt_sym_f *sym_cb;		/* Caller's callback function */
43	void *sym_data;			/* Callback function argument */
44	uint_t sym_type;		/* Symbol type/binding filter */
45	mdb_syminfo_t sym_info;		/* Symbol id and table id */
46	const char *sym_obj;		/* Containing object */
47} kmod_symarg_t;
48
49void
50kmdb_module_path_set(const char **path, size_t pathlen)
51{
52	kmdb_wr_path_t *wr;
53
54	wr = mdb_zalloc(sizeof (kmdb_wr_path_t), UM_SLEEP);
55	wr->dpth_node.wn_task = WNTASK_DMOD_PATH_CHANGE;
56	wr->dpth_path = mdb_path_dup(path, pathlen, &wr->dpth_pathlen);
57
58	kmdb_wr_driver_notify(wr);
59}
60
61void
62kmdb_module_path_ack(kmdb_wr_path_t *dpth)
63{
64	if (dpth->dpth_path != NULL)
65		mdb_path_free(dpth->dpth_path, dpth->dpth_pathlen);
66	mdb_free(dpth, sizeof (kmdb_wr_path_t));
67}
68
69static kmdb_modctl_t *
70kmdb_module_lookup_loaded(const char *name)
71{
72	kmdb_modctl_t *kmc;
73	mdb_var_t *v;
74
75	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, name)) == NULL)
76		return (NULL);
77
78	kmc = MDB_NV_COOKIE(v);
79	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
80		return (NULL);
81
82	return (kmc);
83}
84
85/*
86 * Given an address, try to match it up with a dmod symbol.
87 */
88int
89kmdb_module_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
90    size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip)
91{
92	kmdb_modctl_t *sym_kmc = NULL;
93	GElf_Sym sym;
94	uint_t symid;
95	mdb_var_t *v;
96	const char *name;
97
98	mdb_nv_rewind(&mdb.m_dmodctl);
99	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
100		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
101
102		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
103			continue;
104
105		if (mdb_gelf_symtab_lookup_by_addr(kmc->kmc_symtab, addr, flags,
106		    buf, nbytes, symp, &sip->sym_id) != 0 ||
107		    symp->st_value == 0)
108			continue;
109
110		if (flags & MDB_TGT_SYM_EXACT) {
111			sym_kmc = kmc;
112			goto found;
113		}
114
115		/*
116		 * If this is the first match we've found, or if this symbol is
117		 * closer to the specified address than the last one we found,
118		 * use it.
119		 */
120		if (sym_kmc == NULL || mdb_gelf_sym_closer(symp, &sym, addr)) {
121			sym_kmc = kmc;
122			sym = *symp;
123			symid = sip->sym_id;
124		}
125	}
126
127	if (sym_kmc == NULL)
128		return (set_errno(EMDB_NOSYMADDR));
129
130	*symp = sym;
131	sip->sym_id = symid;
132
133found:
134	/*
135	 * Once we've found something, copy the final name into the caller's
136	 * buffer, prefixed with a marker identifying this as a dmod symbol.
137	 */
138	if (buf != NULL) {
139		name = mdb_gelf_sym_name(sym_kmc->kmc_symtab, symp);
140
141		(void) mdb_snprintf(buf, nbytes, "DMOD`%s`%s",
142		    sym_kmc->kmc_modname, name);
143	}
144	sip->sym_table = MDB_TGT_SYMTAB;
145
146	return (0);
147}
148
149/*
150 * Locate a given dmod symbol
151 */
152int
153kmdb_module_lookup_by_name(const char *obj, const char *name, GElf_Sym *symp,
154    mdb_syminfo_t *sip)
155{
156	kmdb_modctl_t *kmc;
157
158	if ((kmc = kmdb_module_lookup_loaded(obj)) == NULL)
159		return (set_errno(EMDB_NOSYMADDR));
160
161	if (mdb_gelf_symtab_lookup_by_name(kmc->kmc_symtab, name,
162	    symp, &sip->sym_id) == 0) {
163		sip->sym_table = MDB_TGT_SYMTAB;
164		return (0);
165	}
166
167	return (set_errno(EMDB_NOSYM));
168}
169
170ctf_file_t *
171kmdb_module_addr_to_ctf(uintptr_t addr)
172{
173	mdb_var_t *v;
174
175	mdb_nv_rewind(&mdb.m_dmodctl);
176	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
177		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
178		struct module *mp;
179
180		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
181			continue;
182
183		mp = kmc->kmc_modctl->mod_mp;
184		if (addr - (uintptr_t)mp->text < mp->text_size ||
185		    addr - (uintptr_t)mp->data < mp->data_size ||
186		    addr - mp->bss < mp->bss_size) {
187			ctf_file_t *ctfp = kmc->kmc_mod->mod_ctfp;
188
189			if (ctfp == NULL) {
190				(void) set_errno(EMDB_NOCTF);
191				return (NULL);
192			}
193
194			return (ctfp);
195		}
196	}
197
198	(void) set_errno(EMDB_NOMAP);
199	return (NULL);
200}
201
202ctf_file_t *
203kmdb_module_name_to_ctf(const char *obj)
204{
205	kmdb_modctl_t *kmc;
206	ctf_file_t *ctfp;
207
208	if ((kmc = kmdb_module_lookup_loaded(obj)) == NULL) {
209		(void) set_errno(EMDB_NOOBJ);
210		return (NULL);
211	}
212
213	if ((ctfp = kmc->kmc_mod->mod_ctfp) == NULL) {
214		(void) set_errno(EMDB_NOCTF);
215		return (NULL);
216	}
217
218	return (ctfp);
219}
220
221static int
222kmdb_module_symtab_func(void *data, const GElf_Sym *sym, const char *name,
223    uint_t id)
224{
225	kmod_symarg_t *arg = data;
226
227	if (mdb_tgt_sym_match(sym, arg->sym_type)) {
228		arg->sym_info.sym_id = id;
229
230		return (arg->sym_cb(arg->sym_data, sym, name, &arg->sym_info,
231		    arg->sym_obj));
232	}
233
234	return (0);
235}
236
237int
238kmdb_module_symbol_iter(const char *obj, uint_t type, mdb_tgt_sym_f *cb,
239    void *p)
240{
241	kmdb_modctl_t *kmc;
242	kmod_symarg_t arg;
243	mdb_var_t *v;
244
245	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, obj)) == NULL)
246		return (set_errno(EMDB_NOMOD));
247
248	kmc = MDB_NV_COOKIE(v);
249
250	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
251		return (set_errno(EMDB_NOMOD));
252
253	arg.sym_cb = cb;
254	arg.sym_data = p;
255	arg.sym_type = type;
256	arg.sym_info.sym_table = kmc->kmc_symtab->gst_tabid;
257	arg.sym_obj = obj;
258
259	mdb_gelf_symtab_iter(kmc->kmc_symtab, kmdb_module_symtab_func, &arg);
260
261	return (0);
262}
263