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
pr_symtab(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pr_addr2map(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)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
pr_file_info_walk_init(mdb_walk_state_t * wsp)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
pr_file_info_walk_step(mdb_walk_state_t * wsp)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
pr_file_info_walk_fini(mdb_walk_state_t * wsp)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
pr_map_info_walk_init(mdb_walk_state_t * wsp)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
pr_map_info_walk_step(mdb_walk_state_t * wsp)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
pr_map_info_walk_fini(mdb_walk_state_t * wsp)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 *
_mdb_init(void)376 _mdb_init(void)
377 {
378 return (&modinfo);
379 }
380