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