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