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 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 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 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 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 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 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 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 * 340 _mdb_init(void) 341 { 342 return (&modinfo); 343 } 344