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