19acbbeafSnn35248 /*
29acbbeafSnn35248 * CDDL HEADER START
39acbbeafSnn35248 *
49acbbeafSnn35248 * The contents of this file are subject to the terms of the
59acbbeafSnn35248 * Common Development and Distribution License (the "License").
69acbbeafSnn35248 * You may not use this file except in compliance with the License.
79acbbeafSnn35248 *
89acbbeafSnn35248 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99acbbeafSnn35248 * or http://www.opensolaris.org/os/licensing.
109acbbeafSnn35248 * See the License for the specific language governing permissions
119acbbeafSnn35248 * and limitations under the License.
129acbbeafSnn35248 *
139acbbeafSnn35248 * When distributing Covered Code, include this CDDL HEADER in each
149acbbeafSnn35248 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159acbbeafSnn35248 * If applicable, add the following below this CDDL HEADER, with the
169acbbeafSnn35248 * fields enclosed by brackets "[]" replaced with your own identifying
179acbbeafSnn35248 * information: Portions Copyright [yyyy] [name of copyright owner]
189acbbeafSnn35248 *
199acbbeafSnn35248 * CDDL HEADER END
209acbbeafSnn35248 */
219acbbeafSnn35248 /*
22d51e9074Sab196087 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
239acbbeafSnn35248 * Use is subject to license terms.
249acbbeafSnn35248 */
25*50d4d24eSRobert Mustacchi /*
26*50d4d24eSRobert Mustacchi * Copyright 2021 Oxide Computer Company
27*50d4d24eSRobert Mustacchi */
289acbbeafSnn35248
299acbbeafSnn35248 #include <libproc.h>
309acbbeafSnn35248 #include <Pcontrol.h>
319acbbeafSnn35248 #include <stddef.h>
329acbbeafSnn35248
339acbbeafSnn35248 #include <mdb/mdb_modapi.h>
349acbbeafSnn35248
359acbbeafSnn35248 typedef struct ps_prochandle ps_prochandle_t;
369acbbeafSnn35248
379acbbeafSnn35248 /*
389acbbeafSnn35248 * addr::pr_symtab [-a | n]
399acbbeafSnn35248 *
409acbbeafSnn35248 * -a Sort symbols by address
419acbbeafSnn35248 * -n Sort symbols by name
429acbbeafSnn35248 *
439acbbeafSnn35248 * Given a sym_tbl_t, dump its contents in tabular form. When given '-a' or
449acbbeafSnn35248 * '-n', we use the sorted tables 'sym_byaddr' or 'sym_byname', respectively.
459acbbeafSnn35248 */
469acbbeafSnn35248 static int
pr_symtab(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)479acbbeafSnn35248 pr_symtab(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
489acbbeafSnn35248 {
499acbbeafSnn35248 sym_tbl_t symtab;
50d51e9074Sab196087 Elf_Data data_pri;
51d51e9074Sab196087 Elf_Data data_aux;
52d51e9074Sab196087 Elf_Data *data;
539acbbeafSnn35248 #ifdef _LP64
549acbbeafSnn35248 Elf64_Sym sym;
559acbbeafSnn35248 int width = 16;
569acbbeafSnn35248 #else
579acbbeafSnn35248 Elf32_Sym sym;
589acbbeafSnn35248 int width = 8;
599acbbeafSnn35248 #endif
609acbbeafSnn35248 int i, idx, count;
619acbbeafSnn35248 char name[128];
629acbbeafSnn35248 int byaddr = FALSE;
639acbbeafSnn35248 int byname = FALSE;
649acbbeafSnn35248 uint_t *symlist;
659acbbeafSnn35248 size_t symlistsz;
669acbbeafSnn35248
679acbbeafSnn35248 if (mdb_getopts(argc, argv,
689acbbeafSnn35248 'a', MDB_OPT_SETBITS, TRUE, &byaddr,
699acbbeafSnn35248 'n', MDB_OPT_SETBITS, TRUE, &byname,
709acbbeafSnn35248 NULL) != argc)
719acbbeafSnn35248 return (DCMD_USAGE);
729acbbeafSnn35248
739acbbeafSnn35248 if (byaddr && byname) {
749acbbeafSnn35248 mdb_warn("only one of '-a' or '-n' can be specified\n");
759acbbeafSnn35248 return (DCMD_USAGE);
769acbbeafSnn35248 }
779acbbeafSnn35248
789acbbeafSnn35248 if (!(flags & DCMD_ADDRSPEC))
799acbbeafSnn35248 return (DCMD_USAGE);
809acbbeafSnn35248
819acbbeafSnn35248 if (mdb_vread(&symtab, sizeof (sym_tbl_t), addr) == -1) {
829acbbeafSnn35248 mdb_warn("failed to read sym_tbl_t at %p", addr);
839acbbeafSnn35248 return (DCMD_ERR);
849acbbeafSnn35248 }
859acbbeafSnn35248
869acbbeafSnn35248 if (symtab.sym_count == 0) {
879acbbeafSnn35248 mdb_warn("no symbols present\n");
889acbbeafSnn35248 return (DCMD_ERR);
899acbbeafSnn35248 }
909acbbeafSnn35248
91d51e9074Sab196087 /*
92d51e9074Sab196087 * As described in the libproc header Pcontrol.h, a sym_tbl_t
93d51e9074Sab196087 * contains a primary and an optional auxiliary symbol table.
94d51e9074Sab196087 * We treat the combination as a single table, with the auxiliary
95d51e9074Sab196087 * values coming before the primary ones.
96d51e9074Sab196087 *
97d51e9074Sab196087 * Read the primary and auxiliary Elf_Data structs.
98d51e9074Sab196087 */
99d51e9074Sab196087 if (mdb_vread(&data_pri, sizeof (Elf_Data),
100d51e9074Sab196087 (uintptr_t)symtab.sym_data_pri) == -1) {
101d51e9074Sab196087 mdb_warn("failed to read primary Elf_Data at %p",
102d51e9074Sab196087 symtab.sym_data_pri);
103d51e9074Sab196087 return (DCMD_ERR);
104d51e9074Sab196087 }
105d51e9074Sab196087 if ((symtab.sym_symn_aux > 0) &&
106d51e9074Sab196087 (mdb_vread(&data_aux, sizeof (Elf_Data),
107d51e9074Sab196087 (uintptr_t)symtab.sym_data_aux) == -1)) {
108d51e9074Sab196087 mdb_warn("failed to read auxiliary Elf_Data at %p",
109d51e9074Sab196087 symtab.sym_data_aux);
1109acbbeafSnn35248 return (DCMD_ERR);
1119acbbeafSnn35248 }
1129acbbeafSnn35248
1139acbbeafSnn35248 symlist = NULL;
1149acbbeafSnn35248 if (byaddr || byname) {
1159acbbeafSnn35248 uintptr_t src = byaddr ? (uintptr_t)symtab.sym_byaddr :
1169acbbeafSnn35248 (uintptr_t)symtab.sym_byname;
1179acbbeafSnn35248
1189acbbeafSnn35248 symlistsz = symtab.sym_count * sizeof (uint_t);
1199acbbeafSnn35248 symlist = mdb_alloc(symlistsz, UM_SLEEP);
1209acbbeafSnn35248 if (mdb_vread(symlist, symlistsz, src) == -1) {
1219acbbeafSnn35248 mdb_warn("failed to read sorted symbols at %p", src);
1229acbbeafSnn35248 return (DCMD_ERR);
1239acbbeafSnn35248 }
1249acbbeafSnn35248 count = symtab.sym_count;
1259acbbeafSnn35248 } else {
1269acbbeafSnn35248 count = symtab.sym_symn;
1279acbbeafSnn35248 }
1289acbbeafSnn35248
1299acbbeafSnn35248 mdb_printf("%<u>%*s %*s %s%</u>\n", width, "ADDRESS", width,
1309acbbeafSnn35248 "SIZE", "NAME");
1319acbbeafSnn35248
1329acbbeafSnn35248 for (i = 0; i < count; i++) {
1339acbbeafSnn35248 if (byaddr | byname)
1349acbbeafSnn35248 idx = symlist[i];
1359acbbeafSnn35248 else
1369acbbeafSnn35248 idx = i;
1379acbbeafSnn35248
138d51e9074Sab196087 /* If index is in range of primary symtab, look it up there */
139d51e9074Sab196087 if (idx >= symtab.sym_symn_aux) {
140d51e9074Sab196087 data = &data_pri;
141d51e9074Sab196087 idx -= symtab.sym_symn_aux;
142d51e9074Sab196087 } else { /* Look it up in the auxiliary symtab */
143d51e9074Sab196087 data = &data_aux;
144d51e9074Sab196087 }
145d51e9074Sab196087
146d51e9074Sab196087 if (mdb_vread(&sym, sizeof (sym), (uintptr_t)data->d_buf +
1479acbbeafSnn35248 idx * sizeof (sym)) == -1) {
1489acbbeafSnn35248 mdb_warn("failed to read symbol at %p",
149d51e9074Sab196087 (uintptr_t)data->d_buf + idx * sizeof (sym));
1509acbbeafSnn35248 if (symlist)
1519acbbeafSnn35248 mdb_free(symlist, symlistsz);
1529acbbeafSnn35248 return (DCMD_ERR);
1539acbbeafSnn35248 }
1549acbbeafSnn35248
1559acbbeafSnn35248 if (mdb_readstr(name, sizeof (name),
1569acbbeafSnn35248 (uintptr_t)symtab.sym_strs + sym.st_name) == -1) {
1579acbbeafSnn35248 mdb_warn("failed to read symbol name at %p",
1589acbbeafSnn35248 symtab.sym_strs + sym.st_name);
1599acbbeafSnn35248 name[0] = '\0';
1609acbbeafSnn35248 }
1619acbbeafSnn35248
1629acbbeafSnn35248 mdb_printf("%0?p %0?p %s\n", sym.st_value, sym.st_size,
1639acbbeafSnn35248 name);
1649acbbeafSnn35248 }
1659acbbeafSnn35248
1669acbbeafSnn35248 if (symlist)
1679acbbeafSnn35248 mdb_free(symlist, symlistsz);
1689acbbeafSnn35248
1699acbbeafSnn35248 return (DCMD_OK);
1709acbbeafSnn35248 }
1719acbbeafSnn35248
1729acbbeafSnn35248 /*
1739acbbeafSnn35248 * addr::pr_addr2map search
1749acbbeafSnn35248 *
1759acbbeafSnn35248 * Given a ps_prochandle_t, convert the given address to the corresponding
1769acbbeafSnn35248 * map_info_t. Functionally equivalent to Paddr2mptr().
1779acbbeafSnn35248 */
1789acbbeafSnn35248 static int
pr_addr2map(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1799acbbeafSnn35248 pr_addr2map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1809acbbeafSnn35248 {
1819acbbeafSnn35248 uintptr_t search;
1829acbbeafSnn35248 ps_prochandle_t psp;
1839acbbeafSnn35248 map_info_t *mp;
1849acbbeafSnn35248 int lo, hi, mid;
1859acbbeafSnn35248
1869acbbeafSnn35248 if (!(flags & DCMD_ADDRSPEC) || argc != 1)
1879acbbeafSnn35248 return (DCMD_USAGE);
1889acbbeafSnn35248
1899acbbeafSnn35248 if (argv[0].a_type == MDB_TYPE_IMMEDIATE)
1909acbbeafSnn35248 search = argv[0].a_un.a_val;
1919acbbeafSnn35248 else
1929acbbeafSnn35248 search = mdb_strtoull(argv[0].a_un.a_str);
1939acbbeafSnn35248
1949acbbeafSnn35248 if (mdb_vread(&psp, sizeof (ps_prochandle_t), addr) == -1) {
1959acbbeafSnn35248 mdb_warn("failed to read ps_prochandle at %p", addr);
1969acbbeafSnn35248 return (DCMD_ERR);
1979acbbeafSnn35248 }
1989acbbeafSnn35248
1999acbbeafSnn35248 lo = 0;
2009acbbeafSnn35248 hi = psp.map_count;
2019acbbeafSnn35248 while (lo <= hi) {
2029acbbeafSnn35248 mid = (lo + hi) / 2;
2039acbbeafSnn35248 mp = &psp.mappings[mid];
2049acbbeafSnn35248
2059acbbeafSnn35248 if ((addr - mp->map_pmap.pr_vaddr) < mp->map_pmap.pr_size) {
2069acbbeafSnn35248 mdb_printf("%#lr\n", addr + offsetof(ps_prochandle_t,
2079acbbeafSnn35248 mappings) + (mp - psp.mappings) *
2089acbbeafSnn35248 sizeof (map_info_t));
2099acbbeafSnn35248 return (DCMD_OK);
2109acbbeafSnn35248 }
2119acbbeafSnn35248
2129acbbeafSnn35248 if (addr < mp->map_pmap.pr_vaddr)
2139acbbeafSnn35248 hi = mid - 1;
2149acbbeafSnn35248 else
2159acbbeafSnn35248 lo = mid + 1;
2169acbbeafSnn35248 }
2179acbbeafSnn35248
2189acbbeafSnn35248 mdb_warn("no corresponding map for %p\n", search);
2199acbbeafSnn35248 return (DCMD_ERR);
2209acbbeafSnn35248 }
2219acbbeafSnn35248
2229acbbeafSnn35248 /*
2239acbbeafSnn35248 * ::walk pr_file_info
2249acbbeafSnn35248 *
2259acbbeafSnn35248 * Given a ps_prochandle_t, walk all its file_info_t structures.
2269acbbeafSnn35248 */
2279acbbeafSnn35248 static int
pr_file_info_walk_init(mdb_walk_state_t * wsp)2289acbbeafSnn35248 pr_file_info_walk_init(mdb_walk_state_t *wsp)
2299acbbeafSnn35248 {
230892ad162SToomas Soome if (wsp->walk_addr == 0) {
2319acbbeafSnn35248 mdb_warn("pr_file_info doesn't support global walks\n");
2329acbbeafSnn35248 return (WALK_ERR);
2339acbbeafSnn35248 }
2349acbbeafSnn35248
235*50d4d24eSRobert Mustacchi wsp->walk_addr += offsetof(ps_prochandle_t, file_head);
236*50d4d24eSRobert Mustacchi if (mdb_layered_walk("list", wsp) == -1) {
237*50d4d24eSRobert Mustacchi mdb_warn("failed to walk layered 'list'");
2389acbbeafSnn35248 return (WALK_ERR);
2399acbbeafSnn35248 }
2409acbbeafSnn35248
2419acbbeafSnn35248 return (WALK_NEXT);
2429acbbeafSnn35248 }
2439acbbeafSnn35248
2449acbbeafSnn35248 static int
pr_file_info_walk_step(mdb_walk_state_t * wsp)2459acbbeafSnn35248 pr_file_info_walk_step(mdb_walk_state_t *wsp)
2469acbbeafSnn35248 {
247*50d4d24eSRobert Mustacchi return (wsp->walk_callback(wsp->walk_addr, wsp->walk_layer,
248*50d4d24eSRobert Mustacchi wsp->walk_cbdata));
2499acbbeafSnn35248 }
2509acbbeafSnn35248
2519acbbeafSnn35248 /*
2529acbbeafSnn35248 * ::walk pr_map_info
2539acbbeafSnn35248 *
2549acbbeafSnn35248 * Given a ps_prochandle_t, walk all its map_info_t structures.
2559acbbeafSnn35248 */
2569acbbeafSnn35248 typedef struct {
2579acbbeafSnn35248 uintptr_t miw_next;
2589acbbeafSnn35248 int miw_count;
2599acbbeafSnn35248 int miw_current;
2609acbbeafSnn35248 } map_info_walk_t;
2619acbbeafSnn35248
2629acbbeafSnn35248 static int
pr_map_info_walk_init(mdb_walk_state_t * wsp)2639acbbeafSnn35248 pr_map_info_walk_init(mdb_walk_state_t *wsp)
2649acbbeafSnn35248 {
2659acbbeafSnn35248 ps_prochandle_t psp;
2669acbbeafSnn35248 map_info_walk_t *miw;
2679acbbeafSnn35248
268892ad162SToomas Soome if (wsp->walk_addr == 0) {
2699acbbeafSnn35248 mdb_warn("pr_map_info doesn't support global walks\n");
2709acbbeafSnn35248 return (WALK_ERR);
2719acbbeafSnn35248 }
2729acbbeafSnn35248
2739acbbeafSnn35248 if (mdb_vread(&psp, sizeof (ps_prochandle_t), wsp->walk_addr) == -1) {
2749acbbeafSnn35248 mdb_warn("failed to read ps_prochandle at %p", wsp->walk_addr);
2759acbbeafSnn35248 return (WALK_ERR);
2769acbbeafSnn35248 }
2779acbbeafSnn35248
2789acbbeafSnn35248 miw = mdb_alloc(sizeof (map_info_walk_t), UM_SLEEP);
2799acbbeafSnn35248
2809acbbeafSnn35248 miw->miw_next = (uintptr_t)psp.mappings;
2819acbbeafSnn35248 miw->miw_count = psp.map_count;
2829acbbeafSnn35248 miw->miw_current = 0;
2839acbbeafSnn35248 wsp->walk_data = miw;
2849acbbeafSnn35248
2859acbbeafSnn35248 return (WALK_NEXT);
2869acbbeafSnn35248 }
2879acbbeafSnn35248
2889acbbeafSnn35248 static int
pr_map_info_walk_step(mdb_walk_state_t * wsp)2899acbbeafSnn35248 pr_map_info_walk_step(mdb_walk_state_t *wsp)
2909acbbeafSnn35248 {
2919acbbeafSnn35248 map_info_walk_t *miw = wsp->walk_data;
2929acbbeafSnn35248 map_info_t m;
2939acbbeafSnn35248 int status;
2949acbbeafSnn35248
2959acbbeafSnn35248 if (miw->miw_current == miw->miw_count)
2969acbbeafSnn35248 return (WALK_DONE);
2979acbbeafSnn35248
2989acbbeafSnn35248 if (mdb_vread(&m, sizeof (map_info_t), miw->miw_next) == -1) {
2999acbbeafSnn35248 mdb_warn("failed to read map_info_t at %p", miw->miw_next);
3009acbbeafSnn35248 return (WALK_DONE);
3019acbbeafSnn35248 }
3029acbbeafSnn35248
3039acbbeafSnn35248 status = wsp->walk_callback(miw->miw_next, &m, wsp->walk_cbdata);
3049acbbeafSnn35248
3059acbbeafSnn35248 miw->miw_current++;
3069acbbeafSnn35248 miw->miw_next += sizeof (map_info_t);
3079acbbeafSnn35248
3089acbbeafSnn35248 return (status);
3099acbbeafSnn35248 }
3109acbbeafSnn35248
3119acbbeafSnn35248 static void
pr_map_info_walk_fini(mdb_walk_state_t * wsp)3129acbbeafSnn35248 pr_map_info_walk_fini(mdb_walk_state_t *wsp)
3139acbbeafSnn35248 {
3149acbbeafSnn35248 map_info_walk_t *miw = wsp->walk_data;
3159acbbeafSnn35248 mdb_free(miw, sizeof (map_info_walk_t));
3169acbbeafSnn35248 }
3179acbbeafSnn35248
3189acbbeafSnn35248 static const mdb_dcmd_t dcmds[] = {
3199acbbeafSnn35248 { "pr_addr2map", ":addr", "convert an adress into a map_info_t",
3209acbbeafSnn35248 pr_addr2map },
3219acbbeafSnn35248 { "pr_symtab", ":[-a | -n]", "print the contents of a sym_tbl_t",
3229acbbeafSnn35248 pr_symtab },
3239acbbeafSnn35248 { NULL }
3249acbbeafSnn35248 };
3259acbbeafSnn35248
3269acbbeafSnn35248 static const mdb_walker_t walkers[] = {
3279acbbeafSnn35248 { "pr_file_info", "given a ps_prochandle, walk its file_info "
328*50d4d24eSRobert Mustacchi "structures", pr_file_info_walk_init, pr_file_info_walk_step },
3299acbbeafSnn35248 { "pr_map_info", "given a ps_prochandle, walk its map_info structures",
3309acbbeafSnn35248 pr_map_info_walk_init, pr_map_info_walk_step,
3319acbbeafSnn35248 pr_map_info_walk_fini },
3329acbbeafSnn35248 { NULL }
3339acbbeafSnn35248 };
3349acbbeafSnn35248
3359acbbeafSnn35248 static const mdb_modinfo_t modinfo = {
3369acbbeafSnn35248 MDB_API_VERSION, dcmds, walkers
3379acbbeafSnn35248 };
3389acbbeafSnn35248
3399acbbeafSnn35248 const mdb_modinfo_t *
_mdb_init(void)3409acbbeafSnn35248 _mdb_init(void)
3419acbbeafSnn35248 {
3429acbbeafSnn35248 return (&modinfo);
3439acbbeafSnn35248 }
344