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 2021 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 if (argv[0].a_type == MDB_TYPE_IMMEDIATE)
190 search = argv[0].a_un.a_val;
191 else
192 search = mdb_strtoull(argv[0].a_un.a_str);
193
194 if (mdb_vread(&psp, sizeof (ps_prochandle_t), addr) == -1) {
195 mdb_warn("failed to read ps_prochandle at %p", addr);
196 return (DCMD_ERR);
197 }
198
199 lo = 0;
200 hi = psp.map_count;
201 while (lo <= hi) {
202 mid = (lo + hi) / 2;
203 mp = &psp.mappings[mid];
204
205 if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size) {
206 mdb_printf("%#lr\n", addr + offsetof(ps_prochandle_t,
207 mappings) + (mp - psp.mappings) *
208 sizeof (map_info_t));
209 return (DCMD_OK);
210 }
211
212 if (addr < mp->map_pmap.pr_vaddr)
213 hi = mid - 1;
214 else
215 lo = mid + 1;
216 }
217
218 mdb_warn("no corresponding map for %p\n", search);
219 return (DCMD_ERR);
220 }
221
222 /*
223 * ::walk pr_file_info
224 *
225 * Given a ps_prochandle_t, walk all its file_info_t structures.
226 */
227 static int
pr_file_info_walk_init(mdb_walk_state_t * wsp)228 pr_file_info_walk_init(mdb_walk_state_t *wsp)
229 {
230 if (wsp->walk_addr == 0) {
231 mdb_warn("pr_file_info doesn't support global walks\n");
232 return (WALK_ERR);
233 }
234
235 wsp->walk_addr += offsetof(ps_prochandle_t, file_head);
236 if (mdb_layered_walk("list", wsp) == -1) {
237 mdb_warn("failed to walk layered 'list'");
238 return (WALK_ERR);
239 }
240
241 return (WALK_NEXT);
242 }
243
244 static int
pr_file_info_walk_step(mdb_walk_state_t * wsp)245 pr_file_info_walk_step(mdb_walk_state_t *wsp)
246 {
247 return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
248 wsp->walk_cbdata));
249 }
250
251 /*
252 * ::walk pr_map_info
253 *
254 * Given a ps_prochandle_t, walk all its map_info_t structures.
255 */
256 typedef struct {
257 uintptr_t miw_next;
258 int miw_count;
259 int miw_current;
260 } map_info_walk_t;
261
262 static int
pr_map_info_walk_init(mdb_walk_state_t * wsp)263 pr_map_info_walk_init(mdb_walk_state_t *wsp)
264 {
265 ps_prochandle_t psp;
266 map_info_walk_t *miw;
267
268 if (wsp->walk_addr == 0) {
269 mdb_warn("pr_map_info doesn't support global walks\n");
270 return (WALK_ERR);
271 }
272
273 if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
274 mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
275 return (WALK_ERR);
276 }
277
278 miw = mdb_alloc(sizeof (map_info_walk_t), UM_SLEEP);
279
280 miw->miw_next = (uintptr_t)psp.mappings;
281 miw->miw_count = psp.map_count;
282 miw->miw_current = 0;
283 wsp->walk_data = miw;
284
285 return (WALK_NEXT);
286 }
287
288 static int
pr_map_info_walk_step(mdb_walk_state_t * wsp)289 pr_map_info_walk_step(mdb_walk_state_t *wsp)
290 {
291 map_info_walk_t *miw = wsp->walk_data;
292 map_info_t m;
293 int status;
294
295 if (miw->miw_current == miw->miw_count)
296 return (WALK_DONE);
297
298 if (mdb_vread(&m, sizeof (map_info_t), miw->miw_next) == -1) {
299 mdb_warn("failed to read map_info_t at %p", miw->miw_next);
300 return (WALK_DONE);
301 }
302
303 status = wsp->walk_callback(miw->miw_next, &m, wsp->walk_cbdata);
304
305 miw->miw_current++;
306 miw->miw_next += sizeof (map_info_t);
307
308 return (status);
309 }
310
311 static void
pr_map_info_walk_fini(mdb_walk_state_t * wsp)312 pr_map_info_walk_fini(mdb_walk_state_t *wsp)
313 {
314 map_info_walk_t *miw = wsp->walk_data;
315 mdb_free(miw, sizeof (map_info_walk_t));
316 }
317
318 static const mdb_dcmd_t dcmds[] = {
319 { "pr_addr2map", ":addr", "convert an adress into a map_info_t",
320 pr_addr2map },
321 { "pr_symtab", ":[-a | -n]", "print the contents of a sym_tbl_t",
322 pr_symtab },
323 { NULL }
324 };
325
326 static const mdb_walker_t walkers[] = {
327 { "pr_file_info", "given a ps_prochandle, walk its file_info "
328 "structures", pr_file_info_walk_init, pr_file_info_walk_step },
329 { "pr_map_info", "given a ps_prochandle, walk its map_info structures",
330 pr_map_info_walk_init, pr_map_info_walk_step,
331 pr_map_info_walk_fini },
332 { NULL }
333 };
334
335 static const mdb_modinfo_t modinfo = {
336 MDB_API_VERSION, dcmds, walkers
337 };
338
339 const mdb_modinfo_t *
_mdb_init(void)340 _mdb_init(void)
341 {
342 return (&modinfo);
343 }
344