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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Copyright 2025 Oxide Computer Company 29 */ 30 31 #include <sys/machelf.h> 32 #include <sys/modctl.h> 33 #include <sys/kobj.h> 34 35 #include <mdb/mdb_modapi.h> 36 #include <mdb/mdb_ctf.h> 37 38 static uintptr_t module_head; /* Head of kernel modctl list */ 39 40 struct krtld_ctf_module { 41 Ehdr hdr; 42 char *shdrs; 43 size_t text_size; 44 size_t data_size; 45 char *text; 46 char *ctfdata; 47 size_t ctfsize; 48 }; 49 50 struct modctl_walk_data { 51 uintptr_t mwd_head; 52 struct modctl mwd_modctl; 53 }; 54 55 static int 56 modctl_walk_init(mdb_walk_state_t *wsp) 57 { 58 struct modctl_walk_data *mwd = mdb_alloc( 59 sizeof (struct modctl_walk_data), UM_SLEEP); 60 61 mwd->mwd_head = (wsp->walk_addr == 0 ? module_head : wsp->walk_addr); 62 wsp->walk_data = mwd; 63 wsp->walk_addr = 0; 64 65 return (WALK_NEXT); 66 } 67 68 static int 69 modctl_walk_step(mdb_walk_state_t *wsp) 70 { 71 struct modctl_walk_data *mwd = wsp->walk_data; 72 int status; 73 74 if (wsp->walk_addr == mwd->mwd_head) 75 return (WALK_DONE); 76 77 if (wsp->walk_addr == 0) 78 wsp->walk_addr = mwd->mwd_head; 79 80 if (mdb_vread(&mwd->mwd_modctl, sizeof (struct modctl), 81 wsp->walk_addr) == -1) { 82 mdb_warn("failed to read modctl at %p", wsp->walk_addr); 83 return (WALK_ERR); 84 } 85 86 status = wsp->walk_callback(wsp->walk_addr, &mwd->mwd_modctl, 87 wsp->walk_cbdata); 88 89 wsp->walk_addr = (uintptr_t)mwd->mwd_modctl.mod_next; 90 91 return (status); 92 } 93 94 static void 95 modctl_walk_fini(mdb_walk_state_t *wsp) 96 { 97 mdb_free(wsp->walk_data, sizeof (struct modctl_walk_data)); 98 } 99 100 /*ARGSUSED*/ 101 static int 102 modctl_format(uintptr_t addr, const void *data, void *private) 103 { 104 const struct modctl *mcp = (const struct modctl *)data; 105 char name[MAXPATHLEN], bits[6], *bp = &bits[0]; 106 107 if (mdb_readstr(name, sizeof (name), 108 (uintptr_t)mcp->mod_filename) == -1) 109 (void) strcpy(name, "???"); 110 111 if (mcp->mod_busy) 112 *bp++ = 'b'; 113 if (mcp->mod_want) 114 *bp++ = 'w'; 115 if (mcp->mod_prim) 116 *bp++ = 'p'; 117 if (mcp->mod_loaded) 118 *bp++ = 'l'; 119 if (mcp->mod_installed) 120 *bp++ = 'i'; 121 *bp = '\0'; 122 123 mdb_printf("%?p %?p %6s 0x%02x %3d %s\n", 124 (uintptr_t)addr, (uintptr_t)mcp->mod_mp, bits, mcp->mod_loadflags, 125 mcp->mod_ref, name); 126 127 return (WALK_NEXT); 128 } 129 130 /*ARGSUSED*/ 131 static int 132 modctls(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 133 { 134 if (argc != 0) 135 return (DCMD_USAGE); 136 137 if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) { 138 mdb_printf("%<u>%?s %?s %6s %4s %3s %s%</u>\n", 139 "MODCTL", "MODULE", "BITS", "FLAGS", "REF", "FILE"); 140 } 141 142 if (flags & DCMD_ADDRSPEC) { 143 struct modctl mc; 144 145 (void) mdb_vread(&mc, sizeof (mc), addr); 146 return (modctl_format(addr, &mc, NULL)); 147 } 148 149 if (mdb_walk("modctl", modctl_format, NULL) == -1) 150 return (DCMD_ERR); 151 152 return (DCMD_OK); 153 } 154 155 static void 156 dump_ehdr(const Ehdr *ehdr) 157 { 158 mdb_printf("\nELF Header\n"); 159 160 mdb_printf(" ei_magic: { 0x%02x, %c, %c, %c }\n", 161 ehdr->e_ident[EI_MAG0], ehdr->e_ident[EI_MAG1], 162 ehdr->e_ident[EI_MAG2], ehdr->e_ident[EI_MAG3]); 163 164 mdb_printf(" ei_class: %-18u ei_data: %-16u\n", 165 ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); 166 167 mdb_printf(" e_machine: %-18hu e_version: %-16u\n", 168 ehdr->e_machine, ehdr->e_version); 169 170 mdb_printf(" e_type: %-18hu\n", ehdr->e_type); 171 mdb_printf(" e_flags: %-18u\n", ehdr->e_flags); 172 173 mdb_printf(" e_entry: 0x%16lx e_ehsize: %8hu e_shstrndx: %hu\n", 174 ehdr->e_entry, ehdr->e_ehsize, ehdr->e_shstrndx); 175 176 mdb_printf(" e_shoff: 0x%16lx e_shentsize: %8hu e_shnum: %hu\n", 177 ehdr->e_shoff, ehdr->e_shentsize, ehdr->e_shnum); 178 179 mdb_printf(" e_phoff: 0x%16lx e_phentsize: %8hu e_phnum: %hu\n", 180 ehdr->e_phoff, ehdr->e_phentsize, ehdr->e_phnum); 181 } 182 183 static void 184 dump_shdr(const Shdr *shdr, int i) 185 { 186 static const mdb_bitmask_t sh_type_masks[] = { 187 { "SHT_NULL", 0xffffffff, SHT_NULL }, 188 { "SHT_PROGBITS", 0xffffffff, SHT_PROGBITS }, 189 { "SHT_SYMTAB", 0xffffffff, SHT_SYMTAB }, 190 { "SHT_STRTAB", 0xffffffff, SHT_STRTAB }, 191 { "SHT_RELA", 0xffffffff, SHT_RELA }, 192 { "SHT_HASH", 0xffffffff, SHT_HASH }, 193 { "SHT_DYNAMIC", 0xffffffff, SHT_DYNAMIC }, 194 { "SHT_NOTE", 0xffffffff, SHT_NOTE }, 195 { "SHT_NOBITS", 0xffffffff, SHT_NOBITS }, 196 { "SHT_REL", 0xffffffff, SHT_REL }, 197 { "SHT_SHLIB", 0xffffffff, SHT_SHLIB }, 198 { "SHT_DYNSYM", 0xffffffff, SHT_DYNSYM }, 199 { "SHT_LOSUNW", 0xffffffff, SHT_LOSUNW }, 200 { "SHT_SUNW_COMDAT", 0xffffffff, SHT_SUNW_COMDAT }, 201 { "SHT_SUNW_syminfo", 0xffffffff, SHT_SUNW_syminfo }, 202 { "SHT_SUNW_verdef", 0xffffffff, SHT_SUNW_verdef }, 203 { "SHT_SUNW_verneed", 0xffffffff, SHT_SUNW_verneed }, 204 { "SHT_SUNW_versym", 0xffffffff, SHT_SUNW_versym }, 205 { "SHT_HISUNW", 0xffffffff, SHT_HISUNW }, 206 { "SHT_LOPROC", 0xffffffff, SHT_LOPROC }, 207 { "SHT_HIPROC", 0xffffffff, SHT_HIPROC }, 208 { "SHT_LOUSER", 0xffffffff, SHT_LOUSER }, 209 { "SHT_HIUSER", 0xffffffff, SHT_HIUSER }, 210 { NULL, 0, 0 } 211 }; 212 213 static const mdb_bitmask_t sh_flag_masks[] = { 214 { "SHF_WRITE", SHF_WRITE, SHF_WRITE }, 215 { "SHF_ALLOC", SHF_ALLOC, SHF_ALLOC }, 216 { "SHF_EXECINSTR", SHF_EXECINSTR, SHF_EXECINSTR }, 217 { "SHF_MASKPROC", SHF_MASKPROC, SHF_MASKPROC }, 218 { NULL, 0, 0 } 219 }; 220 221 mdb_printf("\nSection Header[%d]:\n", i); 222 223 mdb_printf(" sh_addr: 0x%-16lx sh_flags: [ %#lb ]\n", 224 shdr->sh_addr, shdr->sh_flags, sh_flag_masks); 225 226 mdb_printf(" sh_size: 0x%-16lx sh_type: [ %#lb ]\n", 227 shdr->sh_size, shdr->sh_type, sh_type_masks); 228 229 mdb_printf(" sh_offset: 0x%-16lx sh_entsize: 0x%lx\n", 230 shdr->sh_offset, shdr->sh_entsize); 231 232 mdb_printf(" sh_link: 0x%-16lx sh_info: 0x%lx\n", 233 shdr->sh_link, shdr->sh_info); 234 235 mdb_printf(" sh_addralign: 0x%-16lx\n", shdr->sh_addralign); 236 } 237 238 /*ARGSUSED*/ 239 static int 240 modhdrs(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 241 { 242 struct modctl ctl; 243 struct krtld_ctf_module mod; 244 Shdr *shdrs; 245 246 size_t nbytes; 247 int i; 248 249 if (!(flags & DCMD_ADDRSPEC)) { 250 mdb_warn("expected address of struct modctl before ::\n"); 251 return (DCMD_USAGE); 252 } 253 254 if (argc != 0) 255 return (DCMD_USAGE); 256 257 if (mdb_vread(&ctl, sizeof (struct modctl), addr) == -1) { 258 mdb_warn("failed to read modctl at %p\n", addr); 259 return (DCMD_ERR); 260 } 261 if (mdb_ctf_vread(&mod, "struct module", "struct krtld_ctf_module", 262 (uintptr_t)ctl.mod_mp, 0) == -1) { 263 mdb_warn("failed to read module at %p for modctl %p\n", 264 ctl.mod_mp, addr); 265 return (DCMD_ERR); 266 } 267 268 dump_ehdr(&mod.hdr); 269 270 nbytes = sizeof (Shdr) * mod.hdr.e_shnum; 271 shdrs = mdb_alloc(nbytes, UM_SLEEP | UM_GC); 272 mdb_vread(shdrs, nbytes, (uintptr_t)(void *)mod.shdrs); 273 274 for (i = 0; i < mod.hdr.e_shnum; i++) 275 dump_shdr(&shdrs[i], i); 276 277 return (DCMD_OK); 278 } 279 280 /*ARGSUSED*/ 281 static int 282 modinfo_format(uintptr_t addr, const void *data, void *private) 283 { 284 const struct modctl *mcp = (const struct modctl *)data; 285 286 struct modlinkage linkage; 287 struct modlmisc lmisc; 288 struct krtld_ctf_module mod; 289 290 char info[MODMAXLINKINFOLEN]; 291 char name[MODMAXNAMELEN]; 292 293 mod.text_size = 0; 294 mod.data_size = 0; 295 mod.text = NULL; 296 297 linkage.ml_rev = 0; 298 299 info[0] = '\0'; 300 301 if (mcp->mod_mp != NULL) { 302 if (mdb_ctf_vread(&mod, 303 "struct module", "struct krtld_ctf_module", 304 (uintptr_t)mcp->mod_mp, 0) == -1) { 305 mdb_warn("failed to read module at %p\n", 306 mcp->mod_mp); 307 return (WALK_NEXT); 308 } 309 } 310 311 if (mcp->mod_linkage != NULL) { 312 (void) mdb_vread(&linkage, sizeof (linkage), 313 (uintptr_t)mcp->mod_linkage); 314 315 if (linkage.ml_linkage[0] != NULL) { 316 (void) mdb_vread(&lmisc, sizeof (lmisc), 317 (uintptr_t)linkage.ml_linkage[0]); 318 mdb_readstr(info, sizeof (info), 319 (uintptr_t)lmisc.misc_linkinfo); 320 } 321 } 322 323 if (mdb_readstr(name, sizeof (name), (uintptr_t)mcp->mod_modname) == -1) 324 (void) strcpy(name, "???"); 325 326 mdb_printf("%3d %?p %8lx %3d %s (%s)\n", 327 mcp->mod_id, mod.text, mod.text_size + mod.data_size, 328 linkage.ml_rev, name, info[0] != '\0' ? info : "?"); 329 330 return (WALK_NEXT); 331 } 332 333 /*ARGSUSED*/ 334 static int 335 modinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 336 { 337 if (argc != 0) 338 return (DCMD_USAGE); 339 340 if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) { 341 mdb_printf("%<u>%3s %?s %8s %3s %s%</u>\n", 342 "ID", "LOADADDR", "SIZE", "REV", "MODULE NAME"); 343 } 344 345 if (flags & DCMD_ADDRSPEC) { 346 struct modctl mc; 347 348 (void) mdb_vread(&mc, sizeof (mc), addr); 349 return (modinfo_format(addr, &mc, NULL)); 350 } 351 352 if (mdb_walk("modctl", modinfo_format, NULL) == -1) 353 return (DCMD_ERR); 354 355 return (DCMD_OK); 356 } 357 358 /*ARGSUSED*/ 359 static int 360 ctfinfo_format(uintptr_t addr, const struct modctl *mcp, void *private) 361 { 362 char name[MODMAXNAMELEN]; 363 struct krtld_ctf_module mod; 364 365 if (mcp->mod_mp == NULL) 366 return (WALK_NEXT); /* module is not loaded */ 367 368 if (mdb_ctf_vread(&mod, "struct module", "struct krtld_ctf_module", 369 (uintptr_t)mcp->mod_mp, 0) == -1) { 370 mdb_warn("failed to read module at %p for modctl %p\n", 371 mcp->mod_mp, addr); 372 return (WALK_NEXT); 373 } 374 375 if (mdb_readstr(name, sizeof (name), (uintptr_t)mcp->mod_modname) == -1) 376 (void) mdb_snprintf(name, sizeof (name), "%a", mcp->mod_mp); 377 378 mdb_printf("%-30s %?p %lu\n", name, mod.ctfdata, (ulong_t)mod.ctfsize); 379 return (WALK_NEXT); 380 } 381 382 /*ARGSUSED*/ 383 static int 384 ctfinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 385 { 386 if ((flags & DCMD_ADDRSPEC) || argc != 0) 387 return (DCMD_USAGE); 388 389 mdb_printf("%<u>%-30s %?s %s%</u>\n", "MODULE", "CTFDATA", "CTFSIZE"); 390 if (mdb_walk("modctl", (mdb_walk_cb_t)ctfinfo_format, NULL) == -1) 391 return (DCMD_ERR); 392 393 return (DCMD_OK); 394 } 395 396 static const mdb_dcmd_t dcmds[] = { 397 { "modctl", NULL, "list modctl structures", modctls }, 398 { "modhdrs", ":", "given modctl, dump module ehdr and shdrs", modhdrs }, 399 { "modinfo", NULL, "list module information", modinfo }, 400 { "ctfinfo", NULL, "list module CTF information", ctfinfo }, 401 { NULL } 402 }; 403 404 static const mdb_walker_t walkers[] = { 405 { "modctl", "list of modctl structures", 406 modctl_walk_init, modctl_walk_step, modctl_walk_fini }, 407 { NULL } 408 }; 409 410 static const mdb_modinfo_t krtld_modinfo = { MDB_API_VERSION, dcmds, walkers }; 411 412 const mdb_modinfo_t * 413 _mdb_init(void) 414 { 415 GElf_Sym sym; 416 417 if (mdb_lookup_by_name("modules", &sym) == -1) { 418 mdb_warn("failed to lookup 'modules'"); 419 return (NULL); 420 } 421 422 module_head = (uintptr_t)sym.st_value; 423 return (&krtld_modinfo); 424 } 425