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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <libproc.h>
29 #include <Pcontrol.h>
30 #include <stddef.h>
31 
32 #include <mdb/mdb_modapi.h>
33 
34 typedef struct ps_prochandle ps_prochandle_t;
35 
36 /*
37  * addr::pr_symtab [-a | n]
38  *
39  * 	-a	Sort symbols by address
40  * 	-n	Sort symbols by name
41  *
42  * Given a sym_tbl_t, dump its contents in tabular form.  When given '-a' or
43  * '-n', we use the sorted tables 'sym_byaddr' or 'sym_byname', respectively.
44  */
45 static int
46 pr_symtab(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
47 {
48 	sym_tbl_t symtab;
49 	Elf_Data data;
50 #ifdef _LP64
51 	Elf64_Sym sym;
52 	int width = 16;
53 #else
54 	Elf32_Sym sym;
55 	int width = 8;
56 #endif
57 	int i, idx, count;
58 	char name[128];
59 	int byaddr = FALSE;
60 	int byname = FALSE;
61 	uint_t *symlist;
62 	size_t symlistsz;
63 
64 	if (mdb_getopts(argc, argv,
65 	    'a', MDB_OPT_SETBITS, TRUE, &byaddr,
66 	    'n', MDB_OPT_SETBITS, TRUE, &byname,
67 	    NULL) != argc)
68 		return (DCMD_USAGE);
69 
70 	if (byaddr && byname) {
71 		mdb_warn("only one of '-a' or '-n' can be specified\n");
72 		return (DCMD_USAGE);
73 	}
74 
75 	if (!(flags & DCMD_ADDRSPEC))
76 		return (DCMD_USAGE);
77 
78 	if (mdb_vread(&symtab, sizeof (sym_tbl_t), addr) == -1) {
79 		mdb_warn("failed to read sym_tbl_t at %p", addr);
80 		return (DCMD_ERR);
81 	}
82 
83 	if (symtab.sym_count == 0) {
84 		mdb_warn("no symbols present\n");
85 		return (DCMD_ERR);
86 	}
87 
88 	if (mdb_vread(&data, sizeof (Elf_Data),
89 	    (uintptr_t)symtab.sym_data) == -1) {
90 		mdb_warn("failed to read Elf_Data at %p", symtab.sym_data);
91 		return (DCMD_ERR);
92 	}
93 
94 	symlist = NULL;
95 	if (byaddr || byname) {
96 		uintptr_t src = byaddr ? (uintptr_t)symtab.sym_byaddr :
97 		    (uintptr_t)symtab.sym_byname;
98 
99 		symlistsz = symtab.sym_count * sizeof (uint_t);
100 		symlist = mdb_alloc(symlistsz, UM_SLEEP);
101 		if (mdb_vread(symlist, symlistsz, src) == -1) {
102 			mdb_warn("failed to read sorted symbols at %p", src);
103 			return (DCMD_ERR);
104 		}
105 		count = symtab.sym_count;
106 	} else {
107 		count = symtab.sym_symn;
108 	}
109 
110 	mdb_printf("%<u>%*s  %*s  %s%</u>\n", width, "ADDRESS", width,
111 	    "SIZE", "NAME");
112 
113 	for (i = 0; i < count; i++) {
114 		if (byaddr | byname)
115 			idx = symlist[i];
116 		else
117 			idx = i;
118 
119 		if (mdb_vread(&sym, sizeof (sym), (uintptr_t)data.d_buf +
120 		    idx * sizeof (sym)) == -1) {
121 			mdb_warn("failed to read symbol at %p",
122 			    (uintptr_t)data.d_buf + idx * sizeof (sym));
123 			if (symlist)
124 				mdb_free(symlist, symlistsz);
125 			return (DCMD_ERR);
126 		}
127 
128 		if (mdb_readstr(name, sizeof (name),
129 		    (uintptr_t)symtab.sym_strs + sym.st_name) == -1) {
130 			mdb_warn("failed to read symbol name at %p",
131 			    symtab.sym_strs + sym.st_name);
132 			name[0] = '\0';
133 		}
134 
135 		mdb_printf("%0?p  %0?p  %s\n", sym.st_value, sym.st_size,
136 		    name);
137 	}
138 
139 	if (symlist)
140 		mdb_free(symlist, symlistsz);
141 
142 	return (DCMD_OK);
143 }
144 
145 /*
146  * addr::pr_addr2map search
147  *
148  * Given a ps_prochandle_t, convert the given address to the corresponding
149  * map_info_t.  Functionally equivalent to Paddr2mptr().
150  */
151 static int
152 pr_addr2map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
153 {
154 	uintptr_t search;
155 	ps_prochandle_t psp;
156 	map_info_t *mp;
157 	int lo, hi, mid;
158 
159 	if (!(flags & DCMD_ADDRSPEC) || argc != 1)
160 		return (DCMD_USAGE);
161 
162 	if (argv[0].a_type == MDB_TYPE_IMMEDIATE)
163 		search = argv[0].a_un.a_val;
164 	else
165 		search = mdb_strtoull(argv[0].a_un.a_str);
166 
167 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), addr) == -1) {
168 		mdb_warn("failed to read ps_prochandle at %p", addr);
169 		return (DCMD_ERR);
170 	}
171 
172 	lo = 0;
173 	hi = psp.map_count;
174 	while (lo <= hi) {
175 		mid = (lo + hi) / 2;
176 		mp = &psp.mappings[mid];
177 
178 		if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size) {
179 			mdb_printf("%#lr\n", addr + offsetof(ps_prochandle_t,
180 			    mappings) + (mp - psp.mappings) *
181 			    sizeof (map_info_t));
182 			return (DCMD_OK);
183 		}
184 
185 		if (addr < mp->map_pmap.pr_vaddr)
186 			hi = mid - 1;
187 		else
188 			lo = mid + 1;
189 	}
190 
191 	mdb_warn("no corresponding map for %p\n", search);
192 	return (DCMD_ERR);
193 }
194 
195 /*
196  * ::walk pr_file_info
197  *
198  * Given a ps_prochandle_t, walk all its file_info_t structures.
199  */
200 typedef struct {
201 	uintptr_t	fiw_next;
202 	int		fiw_count;
203 } file_info_walk_t;
204 
205 static int
206 pr_file_info_walk_init(mdb_walk_state_t *wsp)
207 {
208 	ps_prochandle_t psp;
209 	file_info_walk_t *fiw;
210 
211 	if (wsp->walk_addr == NULL) {
212 		mdb_warn("pr_file_info doesn't support global walks\n");
213 		return (WALK_ERR);
214 	}
215 
216 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
217 		mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
218 		return (WALK_ERR);
219 	}
220 
221 	fiw = mdb_alloc(sizeof (file_info_walk_t), UM_SLEEP);
222 
223 	fiw->fiw_next = (uintptr_t)psp.file_head.list_forw;
224 	fiw->fiw_count = psp.num_files;
225 	wsp->walk_data = fiw;
226 
227 	return (WALK_NEXT);
228 }
229 
230 static int
231 pr_file_info_walk_step(mdb_walk_state_t *wsp)
232 {
233 	file_info_walk_t *fiw = wsp->walk_data;
234 	file_info_t f;
235 	int status;
236 
237 	if (fiw->fiw_count == 0)
238 		return (WALK_DONE);
239 
240 	if (mdb_vread(&f, sizeof (file_info_t), fiw->fiw_next) == -1) {
241 		mdb_warn("failed to read file_info_t at %p", fiw->fiw_next);
242 		return (WALK_ERR);
243 	}
244 
245 	status = wsp->walk_callback(fiw->fiw_next, &f, wsp->walk_cbdata);
246 
247 	fiw->fiw_next = (uintptr_t)f.file_list.list_forw;
248 	fiw->fiw_count--;
249 
250 	return (status);
251 }
252 
253 static void
254 pr_file_info_walk_fini(mdb_walk_state_t *wsp)
255 {
256 	file_info_walk_t *fiw = wsp->walk_data;
257 	mdb_free(fiw, sizeof (file_info_walk_t));
258 }
259 
260 /*
261  * ::walk pr_map_info
262  *
263  * Given a ps_prochandle_t, walk all its map_info_t structures.
264  */
265 typedef struct {
266 	uintptr_t	miw_next;
267 	int		miw_count;
268 	int		miw_current;
269 } map_info_walk_t;
270 
271 static int
272 pr_map_info_walk_init(mdb_walk_state_t *wsp)
273 {
274 	ps_prochandle_t psp;
275 	map_info_walk_t *miw;
276 
277 	if (wsp->walk_addr == NULL) {
278 		mdb_warn("pr_map_info doesn't support global walks\n");
279 		return (WALK_ERR);
280 	}
281 
282 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
283 		mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
284 		return (WALK_ERR);
285 	}
286 
287 	miw = mdb_alloc(sizeof (map_info_walk_t), UM_SLEEP);
288 
289 	miw->miw_next = (uintptr_t)psp.mappings;
290 	miw->miw_count = psp.map_count;
291 	miw->miw_current = 0;
292 	wsp->walk_data = miw;
293 
294 	return (WALK_NEXT);
295 }
296 
297 static int
298 pr_map_info_walk_step(mdb_walk_state_t *wsp)
299 {
300 	map_info_walk_t *miw = wsp->walk_data;
301 	map_info_t m;
302 	int status;
303 
304 	if (miw->miw_current == miw->miw_count)
305 		return (WALK_DONE);
306 
307 	if (mdb_vread(&m, sizeof (map_info_t), miw->miw_next) == -1) {
308 		mdb_warn("failed to read map_info_t at %p", miw->miw_next);
309 		return (WALK_DONE);
310 	}
311 
312 	status = wsp->walk_callback(miw->miw_next, &m, wsp->walk_cbdata);
313 
314 	miw->miw_current++;
315 	miw->miw_next += sizeof (map_info_t);
316 
317 	return (status);
318 }
319 
320 static void
321 pr_map_info_walk_fini(mdb_walk_state_t *wsp)
322 {
323 	map_info_walk_t *miw = wsp->walk_data;
324 	mdb_free(miw, sizeof (map_info_walk_t));
325 }
326 
327 static const mdb_dcmd_t dcmds[] = {
328 	{ "pr_addr2map",  ":addr", "convert an adress into a map_info_t",
329 	    pr_addr2map },
330 	{ "pr_symtab",	":[-a | -n]", "print the contents of a sym_tbl_t",
331 	    pr_symtab },
332 	{ NULL }
333 };
334 
335 static const mdb_walker_t walkers[] = {
336 	{ "pr_file_info", "given a ps_prochandle, walk its file_info "
337 	    "structures", pr_file_info_walk_init, pr_file_info_walk_step,
338 	    pr_file_info_walk_fini },
339 	{ "pr_map_info", "given a ps_prochandle, walk its map_info structures",
340 	    pr_map_info_walk_init, pr_map_info_walk_step,
341 	    pr_map_info_walk_fini },
342 	{ NULL }
343 };
344 
345 static const mdb_modinfo_t modinfo = {
346 	MDB_API_VERSION, dcmds, walkers
347 };
348 
349 const mdb_modinfo_t *
350 _mdb_init(void)
351 {
352 	return (&modinfo);
353 }
354