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