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