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 /*
28  * Routines for manipulating the kmdb-specific aspects of dmods.
29  */
30 
31 #include <sys/param.h>
32 
33 #include <mdb/mdb_target_impl.h>
34 #include <kmdb/kmdb_module.h>
35 #include <mdb/mdb_debug.h>
36 #include <mdb/mdb_err.h>
37 #include <mdb/mdb.h>
38 
39 typedef struct kmod_symarg {
40 	mdb_tgt_sym_f *sym_cb;		/* Caller's callback function */
41 	void *sym_data;			/* Callback function argument */
42 	uint_t sym_type;		/* Symbol type/binding filter */
43 	mdb_syminfo_t sym_info;		/* Symbol id and table id */
44 	const char *sym_obj;		/* Containing object */
45 } kmod_symarg_t;
46 
47 void
kmdb_module_path_set(const char ** path,size_t pathlen)48 kmdb_module_path_set(const char **path, size_t pathlen)
49 {
50 	kmdb_wr_path_t *wr;
51 
52 	wr = mdb_zalloc(sizeof (kmdb_wr_path_t), UM_SLEEP);
53 	wr->dpth_node.wn_task = WNTASK_DMOD_PATH_CHANGE;
54 	wr->dpth_path = mdb_path_dup(path, pathlen, &wr->dpth_pathlen);
55 
56 	kmdb_wr_driver_notify(wr);
57 }
58 
59 void
kmdb_module_path_ack(kmdb_wr_path_t * dpth)60 kmdb_module_path_ack(kmdb_wr_path_t *dpth)
61 {
62 	if (dpth->dpth_path != NULL)
63 		mdb_path_free(dpth->dpth_path, dpth->dpth_pathlen);
64 	mdb_free(dpth, sizeof (kmdb_wr_path_t));
65 }
66 
67 static kmdb_modctl_t *
kmdb_module_lookup_loaded(const char * name)68 kmdb_module_lookup_loaded(const char *name)
69 {
70 	kmdb_modctl_t *kmc;
71 	mdb_var_t *v;
72 
73 	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, name)) == NULL)
74 		return (NULL);
75 
76 	kmc = MDB_NV_COOKIE(v);
77 	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
78 		return (NULL);
79 
80 	return (kmc);
81 }
82 
83 /*
84  * Given an address, try to match it up with a dmod symbol.
85  */
86 int
kmdb_module_lookup_by_addr(uintptr_t addr,uint_t flags,char * buf,size_t nbytes,GElf_Sym * symp,mdb_syminfo_t * sip)87 kmdb_module_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
88     size_t nbytes, GElf_Sym *symp, mdb_syminfo_t *sip)
89 {
90 	kmdb_modctl_t *sym_kmc = NULL;
91 	GElf_Sym sym;
92 	uint_t symid;
93 	mdb_var_t *v;
94 	const char *name;
95 
96 	mdb_nv_rewind(&mdb.m_dmodctl);
97 	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
98 		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
99 
100 		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
101 			continue;
102 
103 		if (mdb_gelf_symtab_lookup_by_addr(kmc->kmc_symtab, addr, flags,
104 		    buf, nbytes, symp, &sip->sym_id) != 0 ||
105 		    symp->st_value == 0)
106 			continue;
107 
108 		if (flags & MDB_TGT_SYM_EXACT) {
109 			sym_kmc = kmc;
110 			goto found;
111 		}
112 
113 		/*
114 		 * If this is the first match we've found, or if this symbol is
115 		 * closer to the specified address than the last one we found,
116 		 * use it.
117 		 */
118 		if (sym_kmc == NULL || mdb_gelf_sym_closer(symp, &sym, addr)) {
119 			sym_kmc = kmc;
120 			sym = *symp;
121 			symid = sip->sym_id;
122 		}
123 	}
124 
125 	if (sym_kmc == NULL)
126 		return (set_errno(EMDB_NOSYMADDR));
127 
128 	*symp = sym;
129 	sip->sym_id = symid;
130 
131 found:
132 	/*
133 	 * Once we've found something, copy the final name into the caller's
134 	 * buffer, prefixed with a marker identifying this as a dmod symbol.
135 	 */
136 	if (buf != NULL) {
137 		name = mdb_gelf_sym_name(sym_kmc->kmc_symtab, symp);
138 
139 		(void) mdb_snprintf(buf, nbytes, "DMOD`%s`%s",
140 		    sym_kmc->kmc_modname, name);
141 	}
142 	sip->sym_table = MDB_TGT_SYMTAB;
143 
144 	return (0);
145 }
146 
147 /*
148  * Locate a given dmod symbol
149  */
150 int
kmdb_module_lookup_by_name(const char * obj,const char * name,GElf_Sym * symp,mdb_syminfo_t * sip)151 kmdb_module_lookup_by_name(const char *obj, const char *name, GElf_Sym *symp,
152     mdb_syminfo_t *sip)
153 {
154 	kmdb_modctl_t *kmc;
155 
156 	if ((kmc = kmdb_module_lookup_loaded(obj)) == NULL)
157 		return (set_errno(EMDB_NOSYMADDR));
158 
159 	if (mdb_gelf_symtab_lookup_by_name(kmc->kmc_symtab, name,
160 	    symp, &sip->sym_id) == 0) {
161 		sip->sym_table = MDB_TGT_SYMTAB;
162 		return (0);
163 	}
164 
165 	return (set_errno(EMDB_NOSYM));
166 }
167 
168 ctf_file_t *
kmdb_module_addr_to_ctf(uintptr_t addr)169 kmdb_module_addr_to_ctf(uintptr_t addr)
170 {
171 	mdb_var_t *v;
172 
173 	mdb_nv_rewind(&mdb.m_dmodctl);
174 	while ((v = mdb_nv_advance(&mdb.m_dmodctl)) != NULL) {
175 		kmdb_modctl_t *kmc = MDB_NV_COOKIE(v);
176 		struct module *mp;
177 
178 		if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
179 			continue;
180 
181 		mp = kmc->kmc_modctl->mod_mp;
182 		if (addr - (uintptr_t)mp->text < mp->text_size ||
183 		    addr - (uintptr_t)mp->data < mp->data_size ||
184 		    addr - mp->bss < mp->bss_size) {
185 			ctf_file_t *ctfp = kmc->kmc_mod->mod_ctfp;
186 
187 			if (ctfp == NULL) {
188 				(void) set_errno(EMDB_NOCTF);
189 				return (NULL);
190 			}
191 
192 			return (ctfp);
193 		}
194 	}
195 
196 	(void) set_errno(EMDB_NOMAP);
197 	return (NULL);
198 }
199 
200 ctf_file_t *
kmdb_module_name_to_ctf(const char * obj)201 kmdb_module_name_to_ctf(const char *obj)
202 {
203 	kmdb_modctl_t *kmc;
204 	ctf_file_t *ctfp;
205 
206 	if ((kmc = kmdb_module_lookup_loaded(obj)) == NULL) {
207 		(void) set_errno(EMDB_NOOBJ);
208 		return (NULL);
209 	}
210 
211 	if ((ctfp = kmc->kmc_mod->mod_ctfp) == NULL) {
212 		(void) set_errno(EMDB_NOCTF);
213 		return (NULL);
214 	}
215 
216 	return (ctfp);
217 }
218 
219 static int
kmdb_module_symtab_func(void * data,const GElf_Sym * sym,const char * name,uint_t id)220 kmdb_module_symtab_func(void *data, const GElf_Sym *sym, const char *name,
221     uint_t id)
222 {
223 	kmod_symarg_t *arg = data;
224 
225 	if (mdb_tgt_sym_match(sym, arg->sym_type)) {
226 		arg->sym_info.sym_id = id;
227 
228 		return (arg->sym_cb(arg->sym_data, sym, name, &arg->sym_info,
229 		    arg->sym_obj));
230 	}
231 
232 	return (0);
233 }
234 
235 int
kmdb_module_symbol_iter(const char * obj,uint_t type,mdb_tgt_sym_f * cb,void * p)236 kmdb_module_symbol_iter(const char *obj, uint_t type, mdb_tgt_sym_f *cb,
237     void *p)
238 {
239 	kmdb_modctl_t *kmc;
240 	kmod_symarg_t arg;
241 	mdb_var_t *v;
242 
243 	if ((v = mdb_nv_lookup(&mdb.m_dmodctl, obj)) == NULL)
244 		return (set_errno(EMDB_NOMOD));
245 
246 	kmc = MDB_NV_COOKIE(v);
247 
248 	if (kmc->kmc_state != KMDB_MC_STATE_LOADED)
249 		return (set_errno(EMDB_NOMOD));
250 
251 	arg.sym_cb = cb;
252 	arg.sym_data = p;
253 	arg.sym_type = type;
254 	arg.sym_info.sym_table = kmc->kmc_symtab->gst_tabid;
255 	arg.sym_obj = obj;
256 
257 	mdb_gelf_symtab_iter(kmc->kmc_symtab, kmdb_module_symtab_func, &arg);
258 
259 	return (0);
260 }
261