xref: /illumos-gate/usr/src/cmd/mdb/common/modules/libproc/libproc.c (revision b0fe7b8fa79924061f3bdf7f240ea116c2c0b704)
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 2007 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_pri;
50 	Elf_Data data_aux;
51 	Elf_Data *data;
52 #ifdef _LP64
53 	Elf64_Sym sym;
54 	int width = 16;
55 #else
56 	Elf32_Sym sym;
57 	int width = 8;
58 #endif
59 	int i, idx, count;
60 	char name[128];
61 	int byaddr = FALSE;
62 	int byname = FALSE;
63 	uint_t *symlist;
64 	size_t symlistsz;
65 
66 	if (mdb_getopts(argc, argv,
67 	    'a', MDB_OPT_SETBITS, TRUE, &byaddr,
68 	    'n', MDB_OPT_SETBITS, TRUE, &byname,
69 	    NULL) != argc)
70 		return (DCMD_USAGE);
71 
72 	if (byaddr && byname) {
73 		mdb_warn("only one of '-a' or '-n' can be specified\n");
74 		return (DCMD_USAGE);
75 	}
76 
77 	if (!(flags & DCMD_ADDRSPEC))
78 		return (DCMD_USAGE);
79 
80 	if (mdb_vread(&symtab, sizeof (sym_tbl_t), addr) == -1) {
81 		mdb_warn("failed to read sym_tbl_t at %p", addr);
82 		return (DCMD_ERR);
83 	}
84 
85 	if (symtab.sym_count == 0) {
86 		mdb_warn("no symbols present\n");
87 		return (DCMD_ERR);
88 	}
89 
90 	/*
91 	 * As described in the libproc header Pcontrol.h, a sym_tbl_t
92 	 * contains a primary and an optional auxiliary symbol table.
93 	 * We treat the combination as a single table, with the auxiliary
94 	 * values coming before the primary ones.
95 	 *
96 	 * Read the primary and auxiliary Elf_Data structs.
97 	 */
98 	if (mdb_vread(&data_pri, sizeof (Elf_Data),
99 	    (uintptr_t)symtab.sym_data_pri) == -1) {
100 		mdb_warn("failed to read primary Elf_Data at %p",
101 		    symtab.sym_data_pri);
102 		return (DCMD_ERR);
103 	}
104 	if ((symtab.sym_symn_aux > 0) &&
105 	    (mdb_vread(&data_aux, sizeof (Elf_Data),
106 	    (uintptr_t)symtab.sym_data_aux) == -1)) {
107 		mdb_warn("failed to read auxiliary Elf_Data at %p",
108 		    symtab.sym_data_aux);
109 		return (DCMD_ERR);
110 	}
111 
112 	symlist = NULL;
113 	if (byaddr || byname) {
114 		uintptr_t src = byaddr ? (uintptr_t)symtab.sym_byaddr :
115 		    (uintptr_t)symtab.sym_byname;
116 
117 		symlistsz = symtab.sym_count * sizeof (uint_t);
118 		symlist = mdb_alloc(symlistsz, UM_SLEEP);
119 		if (mdb_vread(symlist, symlistsz, src) == -1) {
120 			mdb_warn("failed to read sorted symbols at %p", src);
121 			return (DCMD_ERR);
122 		}
123 		count = symtab.sym_count;
124 	} else {
125 		count = symtab.sym_symn;
126 	}
127 
128 	mdb_printf("%<u>%*s  %*s  %s%</u>\n", width, "ADDRESS", width,
129 	    "SIZE", "NAME");
130 
131 	for (i = 0; i < count; i++) {
132 		if (byaddr | byname)
133 			idx = symlist[i];
134 		else
135 			idx = i;
136 
137 		/* If index is in range of primary symtab, look it up there */
138 		if (idx >= symtab.sym_symn_aux) {
139 			data = &data_pri;
140 			idx -= symtab.sym_symn_aux;
141 		} else {	/* Look it up in the auxiliary symtab */
142 			data = &data_aux;
143 		}
144 
145 		if (mdb_vread(&sym, sizeof (sym), (uintptr_t)data->d_buf +
146 		    idx * sizeof (sym)) == -1) {
147 			mdb_warn("failed to read symbol at %p",
148 			    (uintptr_t)data->d_buf + idx * sizeof (sym));
149 			if (symlist)
150 				mdb_free(symlist, symlistsz);
151 			return (DCMD_ERR);
152 		}
153 
154 		if (mdb_readstr(name, sizeof (name),
155 		    (uintptr_t)symtab.sym_strs + sym.st_name) == -1) {
156 			mdb_warn("failed to read symbol name at %p",
157 			    symtab.sym_strs + sym.st_name);
158 			name[0] = '\0';
159 		}
160 
161 		mdb_printf("%0?p  %0?p  %s\n", sym.st_value, sym.st_size,
162 		    name);
163 	}
164 
165 	if (symlist)
166 		mdb_free(symlist, symlistsz);
167 
168 	return (DCMD_OK);
169 }
170 
171 /*
172  * addr::pr_addr2map search
173  *
174  * Given a ps_prochandle_t, convert the given address to the corresponding
175  * map_info_t.  Functionally equivalent to Paddr2mptr().
176  */
177 static int
178 pr_addr2map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
179 {
180 	uintptr_t search;
181 	ps_prochandle_t psp;
182 	map_info_t *mp;
183 	int lo, hi, mid;
184 
185 	if (!(flags & DCMD_ADDRSPEC) || argc != 1)
186 		return (DCMD_USAGE);
187 
188 	if (argv[0].a_type == MDB_TYPE_IMMEDIATE)
189 		search = argv[0].a_un.a_val;
190 	else
191 		search = mdb_strtoull(argv[0].a_un.a_str);
192 
193 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), addr) == -1) {
194 		mdb_warn("failed to read ps_prochandle at %p", addr);
195 		return (DCMD_ERR);
196 	}
197 
198 	lo = 0;
199 	hi = psp.map_count;
200 	while (lo <= hi) {
201 		mid = (lo + hi) / 2;
202 		mp = &psp.mappings[mid];
203 
204 		if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size) {
205 			mdb_printf("%#lr\n", addr + offsetof(ps_prochandle_t,
206 			    mappings) + (mp - psp.mappings) *
207 			    sizeof (map_info_t));
208 			return (DCMD_OK);
209 		}
210 
211 		if (addr < mp->map_pmap.pr_vaddr)
212 			hi = mid - 1;
213 		else
214 			lo = mid + 1;
215 	}
216 
217 	mdb_warn("no corresponding map for %p\n", search);
218 	return (DCMD_ERR);
219 }
220 
221 /*
222  * ::walk pr_file_info
223  *
224  * Given a ps_prochandle_t, walk all its file_info_t structures.
225  */
226 typedef struct {
227 	uintptr_t	fiw_next;
228 	int		fiw_count;
229 } file_info_walk_t;
230 
231 static int
232 pr_file_info_walk_init(mdb_walk_state_t *wsp)
233 {
234 	ps_prochandle_t psp;
235 	file_info_walk_t *fiw;
236 
237 	if (wsp->walk_addr == NULL) {
238 		mdb_warn("pr_file_info doesn't support global walks\n");
239 		return (WALK_ERR);
240 	}
241 
242 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
243 		mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
244 		return (WALK_ERR);
245 	}
246 
247 	fiw = mdb_alloc(sizeof (file_info_walk_t), UM_SLEEP);
248 
249 	fiw->fiw_next = (uintptr_t)psp.file_head.list_forw;
250 	fiw->fiw_count = psp.num_files;
251 	wsp->walk_data = fiw;
252 
253 	return (WALK_NEXT);
254 }
255 
256 static int
257 pr_file_info_walk_step(mdb_walk_state_t *wsp)
258 {
259 	file_info_walk_t *fiw = wsp->walk_data;
260 	file_info_t f;
261 	int status;
262 
263 	if (fiw->fiw_count == 0)
264 		return (WALK_DONE);
265 
266 	if (mdb_vread(&f, sizeof (file_info_t), fiw->fiw_next) == -1) {
267 		mdb_warn("failed to read file_info_t at %p", fiw->fiw_next);
268 		return (WALK_ERR);
269 	}
270 
271 	status = wsp->walk_callback(fiw->fiw_next, &f, wsp->walk_cbdata);
272 
273 	fiw->fiw_next = (uintptr_t)f.file_list.list_forw;
274 	fiw->fiw_count--;
275 
276 	return (status);
277 }
278 
279 static void
280 pr_file_info_walk_fini(mdb_walk_state_t *wsp)
281 {
282 	file_info_walk_t *fiw = wsp->walk_data;
283 	mdb_free(fiw, sizeof (file_info_walk_t));
284 }
285 
286 /*
287  * ::walk pr_map_info
288  *
289  * Given a ps_prochandle_t, walk all its map_info_t structures.
290  */
291 typedef struct {
292 	uintptr_t	miw_next;
293 	int		miw_count;
294 	int		miw_current;
295 } map_info_walk_t;
296 
297 static int
298 pr_map_info_walk_init(mdb_walk_state_t *wsp)
299 {
300 	ps_prochandle_t psp;
301 	map_info_walk_t *miw;
302 
303 	if (wsp->walk_addr == NULL) {
304 		mdb_warn("pr_map_info doesn't support global walks\n");
305 		return (WALK_ERR);
306 	}
307 
308 	if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
309 		mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
310 		return (WALK_ERR);
311 	}
312 
313 	miw = mdb_alloc(sizeof (map_info_walk_t), UM_SLEEP);
314 
315 	miw->miw_next = (uintptr_t)psp.mappings;
316 	miw->miw_count = psp.map_count;
317 	miw->miw_current = 0;
318 	wsp->walk_data = miw;
319 
320 	return (WALK_NEXT);
321 }
322 
323 static int
324 pr_map_info_walk_step(mdb_walk_state_t *wsp)
325 {
326 	map_info_walk_t *miw = wsp->walk_data;
327 	map_info_t m;
328 	int status;
329 
330 	if (miw->miw_current == miw->miw_count)
331 		return (WALK_DONE);
332 
333 	if (mdb_vread(&m, sizeof (map_info_t), miw->miw_next) == -1) {
334 		mdb_warn("failed to read map_info_t at %p", miw->miw_next);
335 		return (WALK_DONE);
336 	}
337 
338 	status = wsp->walk_callback(miw->miw_next, &m, wsp->walk_cbdata);
339 
340 	miw->miw_current++;
341 	miw->miw_next += sizeof (map_info_t);
342 
343 	return (status);
344 }
345 
346 static void
347 pr_map_info_walk_fini(mdb_walk_state_t *wsp)
348 {
349 	map_info_walk_t *miw = wsp->walk_data;
350 	mdb_free(miw, sizeof (map_info_walk_t));
351 }
352 
353 static const mdb_dcmd_t dcmds[] = {
354 	{ "pr_addr2map",  ":addr", "convert an adress into a map_info_t",
355 	    pr_addr2map },
356 	{ "pr_symtab",	":[-a | -n]", "print the contents of a sym_tbl_t",
357 	    pr_symtab },
358 	{ NULL }
359 };
360 
361 static const mdb_walker_t walkers[] = {
362 	{ "pr_file_info", "given a ps_prochandle, walk its file_info "
363 	    "structures", pr_file_info_walk_init, pr_file_info_walk_step,
364 	    pr_file_info_walk_fini },
365 	{ "pr_map_info", "given a ps_prochandle, walk its map_info structures",
366 	    pr_map_info_walk_init, pr_map_info_walk_step,
367 	    pr_map_info_walk_fini },
368 	{ NULL }
369 };
370 
371 static const mdb_modinfo_t modinfo = {
372 	MDB_API_VERSION, dcmds, walkers
373 };
374 
375 const mdb_modinfo_t *
376 _mdb_init(void)
377 {
378 	return (&modinfo);
379 }
380