1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2015 Joyent, Inc. All rights reserved.
14 * Copyright 2022 Oxide Computer Company
15 */
16
17 /*
18 * ::typedef exists to allow a user to create and import auxiliary CTF
19 * information for the currently running target. ::typedef is similar to the C
20 * typedef keyword. However, ::typedef has no illusions of grandeur. It is not a
21 * standards complaint version of C's typedef. For specifics on what it does and
22 * does not support, please see the help message for ::typedef later on in this
23 * file.
24 *
25 * In addition to allowing the user to create types, it has a notion of a
26 * built-in set of types that a compiler might provide. Currently ::typedef
27 * supports both the standard illumos 32-bit and 64-bit environments, mainly
28 * LP32 and LP64. These are not present by default; it is up to the user to
29 * request that they be inserted.
30 *
31 * To facilitate this, ::typedef adds all of its type information to an
32 * auxiliary CTF container that is a part of the global mdb state. This is
33 * abstracted away from ::typedef by the mdb_ctf_* apis. This container is
34 * referred to as the synthetic container, as it holds these synthetic types.
35 * The synthetic container does not have a parent CTF container. This is rather
36 * important to its operation, as a user can end up referencing types that come
37 * from many different such containers (eg. different kernel modules). As such,
38 * whenever a type is referenced that we do not know about, we search all of the
39 * CTF containers that mdb knows about it. If we find it, then that type is
40 * imported (along with all of its dependent types) into the synthetic
41 * container.
42 *
43 * Finally, ::typedef can source CTF information from external files with the -r
44 * option. This will copy in every type from their container into the synthetic
45 * container, because of this the parent and child relationship between
46 * containers with parents cannot be maintained.
47 */
48
49 #include <mdb/mdb_modapi.h>
50 #include <mdb/mdb_string.h>
51 #include <mdb/mdb_ctf.h>
52 #include <mdb/mdb_list.h>
53 #include <mdb/mdb_nv.h>
54
55 struct parse_node;
56
57 #define PN_F_POINTER 0x01
58 #define PN_F_ARRAY 0x02
59
60 typedef struct parse_node {
61 mdb_list_t pn_list; /* list entry, must be first */
62 char *pn_type; /* name of base type */
63 char *pn_name; /* name of the member */
64 int pn_flags; /* flags */
65 int pn_nptrs; /* number of pointers */
66 int pn_asub; /* value of array subscript */
67 } parse_node_t;
68
69 typedef struct parse_root {
70 mdb_list_t pr_nodes; /* list of members */
71 int pr_kind; /* CTF_K_* */
72 const char *pr_name; /* entity name */
73 const char *pr_tname; /* entity typedef */
74 } parse_root_t;
75
76 static int
typedef_valid_identifier(const char * str)77 typedef_valid_identifier(const char *str)
78 {
79 /*
80 * We can't use the standard ctype.h functions because those aren't
81 * necessairly available in kmdb. On the flip side, we only care about
82 * ascii characters here so that isn't too bad.
83 *
84 * C Identifiers have to start with a letter or a _. Afterwards they can
85 * be alphanumeric or an _.
86 */
87
88 if (*str == '\0')
89 return (1);
90
91 if (*str != '_' &&
92 (*str < 'A' || *str > 'Z') &&
93 (*str < 'a' || *str > 'z'))
94 return (1);
95 str++;
96
97 while (*str != '\0') {
98 if (*str != '_' &&
99 (*str < '0' || *str > '9') &&
100 (*str < 'A' || *str > 'Z') &&
101 (*str < 'a' || *str > 'z'))
102 return (1);
103 str++;
104 }
105
106 return (0);
107 }
108
109 /*ARGSUSED*/
110 static int
typedef_list_cb(mdb_ctf_id_t id,void * arg)111 typedef_list_cb(mdb_ctf_id_t id, void *arg)
112 {
113 char buf[MDB_SYM_NAMLEN];
114
115 /*
116 * The user may have created an anonymous structure or union as part of
117 * running ::typedef. If this is the case, we passed a NULL pointer for
118 * the name into the ctf routines. When we go back and ask for the name
119 * of that, ctf goes through and loops through all the declarations.
120 * This, however correctly, gives us back something undesirable to the
121 * user, eg. the name is simply 'struct' and 'union'. Because a typedef
122 * will always have a non-anonymous name for that, we instead opt to
123 * not include these anonymous names. ctf usefully includes a space as
124 * part of that name.
125 */
126 (void) mdb_ctf_type_name(id, buf, sizeof (buf));
127 if (strcmp("struct ", buf) != 0 && strcmp("union ", buf) != 0)
128 mdb_printf("%s\n", buf);
129 return (0);
130 }
131
132 static char *
typedef_join_strings(int nstr,const mdb_arg_t * args,int flags)133 typedef_join_strings(int nstr, const mdb_arg_t *args, int flags)
134 {
135 int i, size = 0;
136 char *ret, *sptr;
137
138 for (i = 0; i <= nstr; i++) {
139 /* Always account for the space or the null terminator */
140 size += strlen(args[i].a_un.a_str) + 1;
141 }
142 ret = mdb_alloc(sizeof (char) * size, flags);
143 if (ret == NULL)
144 return (NULL);
145 sptr = ret;
146 for (i = 0; i <= nstr; i++) {
147 (void) strcpy(sptr, args[i].a_un.a_str);
148 sptr += strlen(args[i].a_un.a_str);
149 *sptr = ' ';
150 sptr++;
151 }
152 *sptr = '\0';
153
154 return (ret);
155 }
156
157 static int
typedef_list(void)158 typedef_list(void)
159 {
160 (void) mdb_ctf_type_iter(MDB_CTF_SYNTHETIC_ITER, typedef_list_cb,
161 NULL);
162 return (DCMD_OK);
163 }
164
165 static int
typedef_destroy(void)166 typedef_destroy(void)
167 {
168 if (mdb_ctf_synthetics_reset() != 0) {
169 mdb_warn("failed to reset synthetic types");
170 return (DCMD_ERR);
171 }
172 return (DCMD_OK);
173 }
174
175 /*
176 * We've been asked to create the basic types that exist. We accept the
177 * following strings to indicate what we should create.
178 * - LP32, ILP32 (case insensitive)
179 * - LP64
180 */
181 static int
typedef_create(const char * arg)182 typedef_create(const char *arg)
183 {
184 int kind;
185
186 if (strcasecmp(arg, "LP32") == 0 || strcasecmp(arg, "ILP32") == 0) {
187 kind = SYNTHETIC_ILP32;
188 } else if (strcasecmp(arg, "LP64") == 0) {
189 kind = SYNTHETIC_LP64;
190 } else {
191 mdb_printf("invalid data model: %s\n", arg);
192 return (DCMD_USAGE);
193 }
194
195 if (mdb_ctf_synthetics_create_base(kind) != 0) {
196 mdb_printf("failed to create intrinsic types, maybe "
197 "they already exist\n");
198 return (DCMD_ERR);
199 }
200
201 return (DCMD_OK);
202 }
203
204 /*
205 * Search the current arguments for a complete member declaration. This function
206 * modifies the value of defn based on what's necessary for parsing. It returns
207 * the appropriate parse node in pnp.
208 */
209 static int
typedef_parse_member(char * defn,char ** next,parse_node_t ** pnp)210 typedef_parse_member(char *defn, char **next, parse_node_t **pnp)
211 {
212 char *c, *name, *array;
213 int nptrs = 0;
214 parse_node_t *pn;
215
216 c = strchr(defn, ';');
217 if (c == NULL) {
218 mdb_printf("Cannot find semi-colon to delineate the end "
219 "of a member.\n");
220 return (DCMD_ERR);
221 }
222 *c = '\0';
223 *next = c + 1;
224
225 c = strrchr(defn, ' ');
226 if (c == NULL) {
227 mdb_printf("Missing both a name and a type declaration for "
228 "a member. Instead, found '%s'\n", defn);
229 return (DCMD_ERR);
230 }
231 *c = '\0';
232 name = c + 1;
233 c--;
234 while (*c == '*' || *c == ' ') {
235 if (*c == '*')
236 nptrs++;
237 c--;
238 }
239 *(c + 1) = '\0';
240
241 pn = mdb_zalloc(sizeof (parse_node_t), UM_SLEEP | UM_GC);
242 pn->pn_type = defn;
243
244 /*
245 * Go through and prepare the name field. Note that we still have to
246 * check if this is a pointer or an array. We also need to strip the
247 * ending semi-colon.
248 */
249 while (*name == '*') {
250 name++;
251 nptrs++;
252 }
253
254 if ((c = strchr(name, '[')) != NULL) {
255 array = c;
256 if ((c = strchr(array, ']')) == NULL) {
257 mdb_printf("Found the beginning of an array size "
258 "but no closing ']' in %s\n", array);
259 return (DCMD_ERR);
260 }
261 *array = '\0';
262 array++;
263 *c = '\0';
264 pn->pn_flags |= PN_F_ARRAY;
265 pn->pn_asub = mdb_strtoullx(array, MDB_STRTOULL_F_BASE_C);
266 if (pn->pn_asub < 0) {
267 mdb_printf("Array lengths cannot be negative\n");
268 return (DCMD_ERR);
269 }
270 }
271
272 if (typedef_valid_identifier(name) != 0) {
273 mdb_printf("The name %s is not a valid C identifier.\n",
274 name);
275 return (DCMD_ERR);
276 }
277
278 if (nptrs) {
279 pn->pn_flags |= PN_F_POINTER;
280 pn->pn_nptrs = nptrs;
281 }
282 pn->pn_name = name;
283
284 *pnp = pn;
285 return (DCMD_OK);
286 }
287
288 /*
289 * We're going to parse out our types here. Note that we are not strictly
290 * speaking a truely ANSI C compliant parser. Currently we support normal
291 * declarations except for the following:
292 * o function pointers
293 * o bit-fields
294 */
295 static int
typedef_parse(char * defn,const char * name,parse_root_t ** prp)296 typedef_parse(char *defn, const char *name, parse_root_t **prp)
297 {
298 int len, ret;
299 const char *kind, *basename;
300 char *c, *brace;
301 parse_root_t *pr;
302 parse_node_t *pn;
303 mdb_ctf_id_t id;
304
305 pr = mdb_zalloc(sizeof (parse_root_t), UM_SLEEP | UM_GC);
306 basename = defn;
307
308 c = strchr(defn, ' ');
309 if (c == NULL) {
310 mdb_printf("Invalid structure definition. Structure "
311 "must start with either 'struct {' or 'union {'\n");
312 return (DCMD_ERR);
313 }
314 *c = '\0';
315
316 if (strcmp(defn, "struct") == 0)
317 pr->pr_kind = CTF_K_STRUCT;
318 else if (strcmp(defn, "union") == 0)
319 pr->pr_kind = CTF_K_UNION;
320 else {
321 mdb_printf("Invalid start of definition. "
322 "Expected 'struct' or 'union'. "
323 "Found: '%s'\n", defn);
324 return (DCMD_ERR);
325 }
326
327 /*
328 * We transform this back to a space so we can validate that a
329 * non-anonymous struct or union name is valid.
330 */
331 *c = ' ';
332
333 kind = defn;
334 defn = c + 1;
335 while (*defn == ' ')
336 defn++;
337
338 /* Check whether this is anonymous or not */
339 if (*defn != '{') {
340 brace = strchr(defn, '{');
341 c = brace;
342 if (c == NULL) {
343 mdb_printf("Missing opening brace for %s definition. "
344 "Expected '{'. "
345 "Found: '%c'\n", kind, *defn);
346 return (DCMD_ERR);
347 }
348 *c = '\0';
349 c--;
350 while (*c == ' ')
351 c--;
352 *(c+1) = '\0';
353 if (typedef_valid_identifier(defn) != 0) {
354 mdb_printf("The name %s is not a valid C identifier.\n",
355 defn);
356 return (DCMD_ERR);
357 }
358
359 if (mdb_ctf_lookup_by_name(basename, &id) != CTF_ERR) {
360 mdb_printf("type name %s already in use\n", basename);
361 return (DCMD_ERR);
362 }
363
364 pr->pr_name = defn;
365 defn = brace;
366 } else {
367 pr->pr_name = NULL;
368 }
369
370 defn++;
371 while (*defn == ' ')
372 defn++;
373
374 len = strlen(defn);
375 if (defn[len-1] != '}') {
376 mdb_printf("Missing closing brace for %s declaration. "
377 "Expected '}'.\n");
378 return (DCMD_ERR);
379 }
380 defn[len-1] = '\0';
381
382 /*
383 * Start walking all the arguments, looking for a terminating semicolon
384 * for type definitions.
385 */
386 for (;;) {
387 ret = typedef_parse_member(defn, &c, &pn);
388 if (ret == DCMD_ERR)
389 return (DCMD_ERR);
390
391 mdb_list_append(&pr->pr_nodes, pn);
392
393 while (*c == ' ')
394 c++;
395
396 if (*c == '\0')
397 break;
398
399 defn = c;
400 }
401
402 pr->pr_tname = name;
403 *prp = pr;
404
405 return (DCMD_OK);
406 }
407
408 /*
409 * Make sure that none of the member names overlap and that the type names don't
410 * already exist. If we have an array entry that is a VLA, make sure it is the
411 * last member and not the only member.
412 */
413 static int
typedef_validate(parse_root_t * pr)414 typedef_validate(parse_root_t *pr)
415 {
416 mdb_nv_t nv;
417 parse_node_t *pn;
418 mdb_ctf_id_t id;
419 int count = 0;
420
421 (void) mdb_nv_create(&nv, UM_SLEEP | UM_GC);
422 for (pn = mdb_list_next(&pr->pr_nodes); pn != NULL;
423 pn = mdb_list_next(pn)) {
424 count++;
425 if (mdb_nv_lookup(&nv, pn->pn_name) != NULL) {
426 mdb_printf("duplicate name detected: %s\n",
427 pn->pn_name);
428 return (DCMD_ERR);
429 }
430
431 /*
432 * Our parse tree won't go away before the nv, so it's simpler
433 * to just mark everything external.
434 */
435 (void) mdb_nv_insert(&nv, pn->pn_name, NULL, 0, MDB_NV_EXTNAME);
436
437 if (pn->pn_flags & PN_F_ARRAY && pn->pn_asub == 0) {
438 if (pr->pr_kind != CTF_K_STRUCT) {
439 mdb_printf("Flexible array members are only "
440 "valid in structs.\n");
441 return (DCMD_ERR);
442 }
443
444 if (&pn->pn_list != pr->pr_nodes.ml_prev) {
445 mdb_printf("Flexible array entries are only "
446 "allowed to be the last entry in a "
447 "struct\n");
448 return (DCMD_ERR);
449 }
450
451 if (count == 1) {
452 mdb_printf("Structs must have members aside "
453 "from a flexible member\n");
454 return (DCMD_ERR);
455 }
456 }
457 }
458
459 if (mdb_ctf_lookup_by_name(pr->pr_tname, &id) != CTF_ERR) {
460 mdb_printf("typedef name %s already exists\n", pr->pr_tname);
461 return (DCMD_ERR);
462 }
463
464 return (DCMD_OK);
465 }
466
467 static int
typedef_add(parse_root_t * pr)468 typedef_add(parse_root_t *pr)
469 {
470 parse_node_t *pn;
471 mdb_ctf_id_t id, aid, tid, pid;
472 mdb_ctf_arinfo_t ar;
473 int ii;
474
475 /* Pre-flight checks */
476 if (typedef_validate(pr) == DCMD_ERR)
477 return (DCMD_ERR);
478
479 if (pr->pr_kind == CTF_K_STRUCT) {
480 if (mdb_ctf_add_struct(pr->pr_name, &id) != 0) {
481 mdb_printf("failed to create struct for %s\n",
482 pr->pr_tname);
483 return (DCMD_ERR);
484 }
485 } else {
486 if (mdb_ctf_add_union(pr->pr_name, &id) != 0) {
487 mdb_printf("failed to create union for %s\n",
488 pr->pr_tname);
489 return (DCMD_ERR);
490 }
491 }
492
493 for (pn = mdb_list_next(&pr->pr_nodes); pn != NULL;
494 pn = mdb_list_next(pn)) {
495
496 if (mdb_ctf_lookup_by_name(pn->pn_type, &tid) == CTF_ERR) {
497 mdb_printf("failed to add member %s: type %s does "
498 "not exist\n", pn->pn_name, pn->pn_type);
499 goto destroy;
500 }
501
502 if (pn->pn_flags & PN_F_POINTER) {
503 for (ii = 0; ii < pn->pn_nptrs; ii++) {
504 if (mdb_ctf_add_pointer(&tid,
505 &pid) != 0) {
506 mdb_printf("failed to add a pointer "
507 "type as part of member: %s\n",
508 pn->pn_name);
509 goto destroy;
510 }
511 tid = pid;
512 }
513 }
514
515 if (pn->pn_flags & PN_F_ARRAY) {
516 if (mdb_ctf_lookup_by_name("long", &aid) != 0) {
517 mdb_printf("failed to lookup the type 'long' "
518 "for array indexes, are you running mdb "
519 "without a target or using ::typedef -c?");
520 goto destroy;
521 }
522
523 ar.mta_contents = tid;
524 ar.mta_index = aid;
525 ar.mta_nelems = pn->pn_asub;
526
527 if (mdb_ctf_add_array(&ar, &tid) != 0) {
528 mdb_printf("failed to create array type for "
529 "memeber%s\n", pn->pn_name);
530 goto destroy;
531 }
532 }
533
534 if (mdb_ctf_add_member(&id, pn->pn_name, &tid, NULL) ==
535 CTF_ERR) {
536 mdb_printf("failed to create member %s\n",
537 pn->pn_name);
538 goto destroy;
539 }
540 }
541
542 if (mdb_ctf_add_typedef(pr->pr_tname, &id, NULL) != 0) {
543 mdb_printf("failed to add typedef for %s\n",
544 pr->pr_tname);
545 goto destroy;
546 }
547
548 return (DCMD_OK);
549
550 destroy:
551 return (mdb_ctf_type_delete(&id));
552 }
553
554 static int
typedef_readfile(const char * file)555 typedef_readfile(const char *file)
556 {
557 int ret;
558
559 ret = mdb_ctf_synthetics_from_file(file);
560 if (ret != DCMD_OK)
561 mdb_warn("failed to create synthetics from file %s\n", file);
562 return (ret);
563 }
564
565 static int
typedef_writefile(const char * file)566 typedef_writefile(const char *file)
567 {
568 int ret;
569
570 ret = mdb_ctf_synthetics_to_file(file);
571 if (ret != DCMD_OK)
572 mdb_warn("failed to write synthetics to file %s", file);
573 return (ret);
574 }
575
576 /* ARGSUSED */
577 int
cmd_typedef(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)578 cmd_typedef(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
579 {
580 mdb_ctf_id_t id;
581 int i;
582 int destroy = 0, list = 0;
583 const char *cmode = NULL, *rfile = NULL, *wfile = NULL;
584 const char *dst, *src;
585 char *dup;
586 parse_root_t *pr;
587
588 if (flags & DCMD_ADDRSPEC)
589 return (DCMD_USAGE);
590
591 i = mdb_getopts(argc, argv,
592 'd', MDB_OPT_SETBITS, TRUE, &destroy,
593 'l', MDB_OPT_SETBITS, TRUE, &list,
594 'c', MDB_OPT_STR, &cmode,
595 'r', MDB_OPT_STR, &rfile,
596 'w', MDB_OPT_STR, &wfile, NULL);
597
598 argc -= i;
599 argv += i;
600
601 /*
602 * All our options are mutually exclusive currently.
603 */
604 i = 0;
605 if (destroy)
606 i++;
607 if (cmode != NULL)
608 i++;
609 if (list)
610 i++;
611 if (rfile != NULL)
612 i++;
613 if (wfile != NULL)
614 i++;
615 if (i > 1)
616 return (DCMD_USAGE);
617
618 if ((destroy || cmode != NULL || list || rfile != NULL ||
619 wfile != NULL) && argc != 0)
620 return (DCMD_USAGE);
621
622 if (destroy)
623 return (typedef_destroy());
624
625 if (cmode)
626 return (typedef_create(cmode));
627
628 if (list)
629 return (typedef_list());
630
631 if (rfile)
632 return (typedef_readfile(rfile));
633
634 if (wfile)
635 return (typedef_writefile(wfile));
636
637 if (argc < 2)
638 return (DCMD_USAGE);
639
640 /*
641 * Check to see if we are defining a struct or union. Note that we have
642 * to distinguish between struct foo and struct {. All typedef structs
643 * are annonymous structs that are only known by their typedef name. The
644 * same is true with unions. The problem that we have to deal with is
645 * that the ';' character in mdb causes mdb to begin another command. To
646 * work around that fact we require users to put the whole struct
647 * definition in a pair of "" or ''.
648 */
649 if (argc == 2 && strchr(argv[0].a_un.a_str, '{') != NULL) {
650 dup = mdb_alloc(strlen(argv[0].a_un.a_str) + 1,
651 UM_GC | UM_SLEEP);
652 (void) strcpy(dup, argv[0].a_un.a_str);
653 if (typedef_parse(dup, argv[1].a_un.a_str, &pr) == DCMD_ERR)
654 return (DCMD_ERR);
655 if (typedef_add(pr) == DCMD_ERR)
656 return (DCMD_ERR);
657
658 return (DCMD_OK);
659 }
660
661 /*
662 * Someone could give us something like struct foobar or unsigned int or
663 * even long double imaginary. In this case we end up conjoining all
664 * arguments except the last one into one large string that we look up.
665 */
666 if (argc - 1 == 1) {
667 src = argv[0].a_un.a_str;
668 } else {
669 src = typedef_join_strings(argc - 2, argv, UM_GC | UM_SLEEP);
670 }
671
672 dst = argv[argc-1].a_un.a_str;
673
674 if (mdb_ctf_lookup_by_name(dst, &id) != -1) {
675 mdb_printf("%s already exists\n", dst);
676 return (DCMD_ERR);
677 }
678
679 if (mdb_ctf_lookup_by_name(src, &id) != 0) {
680 mdb_printf("%s does not exist\n", src);
681 return (DCMD_ERR);
682 }
683
684 if (mdb_ctf_add_typedef(dst, &id, NULL) != 0) {
685 mdb_printf("failed to create typedef\n");
686 return (DCMD_ERR);
687 }
688
689 return (DCMD_OK);
690 }
691
692 static char typedef_desc[] =
693 "::typedef operates like the C typedef keyword and creates a synthetic type\n"
694 "that is usable across mdb just like a type that is embedded in CTF data.\n"
695 "This includes familiar dcmds like ::print as well as mdb's tab completion\n"
696 "engine. The \"type\" argument can either be a named structure or union\n"
697 "declaration, like \"struct proc { int p_id; }\" declartion, an anonymous\n"
698 "structure or union declaration, like \"struct { int count; }\", or simply\n"
699 "the name of an existing type, like \"uint64_t\". Either form may refer to\n"
700 "other types already defined in CTF or a previous ::typedef invocation. When\n"
701 "debugging binaries without CTF, definitions for intrinsic types may be\n"
702 "created using the -c option. See the OPTIONS section for more information.\n"
703 "If a named struct or union is used, then a type will be created for it just\n"
704 "like in C. This may be used to mimic a forward declaration and an example of\n"
705 "this is in the EXAMPLES section. Regardless of whether a struct or union is\n"
706 "anonymous or named, the \"name\" argument is always required.\n"
707 "\n"
708 "When declaring anonymous structures and unions, the entire definition must\n"
709 "be enclosed within \"\" or ''. The ';' is used by mdb to separate commands\n"
710 "in a similar fashion to the shell. The ';' cannot be escaped, therefore\n"
711 "quoting your argument is necessary. See the EXAMPLES sections for examples\n"
712 "of what this looks like.\n"
713 "\n"
714 "All member and type names must be valid C identifiers. They must start with\n"
715 "an underscore or a letter. Subsequent characters are allowed to be letters,\n"
716 "numbers, or an underscore. When specifying an array, mdb's default base is\n"
717 "not used. Instead, base 10 is assumed to make it easier to use declarations\n"
718 "from C files and transform them. (Other features remain.)\n"
719 "\n"
720 "Declaring arrays and any number of pointers in anonymous structures is \n"
721 "supported. However the following C features are not supported: \n"
722 " o function pointers (use a void * instead)\n"
723 " o bitfields (use an integer of the appropriate size instead)\n"
724 " o packed structures (all structures currently use their natural alignment)\n"
725 "\n"
726 "::typedef also allows you to read type definitions from a file. Definitions\n"
727 "can be read from any ELF file that has a CTF section that libctf can parse\n"
728 "or any raw CTF data files, such as those that can be created with ::typedef.\n"
729 "You can check if a file has such a section with elfdump(1). If a binary or\n"
730 "core dump does not have any type information, but you do have it elsewhere,\n"
731 "then you can use ::typedef -r to read in that type information.\n"
732 "\n"
733 "All built up definitions may be exported as a valid CTF container that can\n"
734 "be used again with ::typedef -r or anything that uses libctf. To write them\n"
735 "out, use ::typedef -w and specify the name of a file. For more information\n"
736 "on the CTF file format, see ctf(5).\n"
737 "\n";
738
739 static char typedef_opts[] =
740 " -c model create intrinsic types based on the specified data model.\n"
741 " The INTRINSICS section lists the built-in types and typedefs.\n"
742 " The following data models are supported:\n"
743 " o LP64 - Traditional illumos 64-bit program.\n"
744 " o LP32 - Traditional illumos 32-bit program.\n"
745 " o ILP32 - An alternate name for LP32.\n"
746 " -d delete all synthetic types\n"
747 " -l list all synthetic types\n"
748 " -r file import type definitions (CTF) from another ELF file\n"
749 " -w file write all synthetic type definitions out to file\n"
750 "\n";
751
752 static char typedef_examps[] =
753 " ::typedef -c LP64\n"
754 " ::typedef uint64_t bender_t\n"
755 " ::typedef struct proc new_proc_t\n"
756 " ::typedef \"union { int frodo; char sam; long gandalf; }\" ringbearer_t;\n"
757 " ::typedef \"struct { uintptr_t stone[7]; void **white; }\" gift_t\n"
758 " ::typedef \"struct list { struct list *l_next; struct list *l_prev; }\" "
759 "list_t\n"
760 " ::typedef -r /var/tmp/qemu-system-x86_64\n"
761 " ::typedef -w defs.ctf\n"
762 "\n";
763
764 static char typedef_intrins[] =
765 "The following C types and <stdint.h> typedefs are provided when \n"
766 "::typedef -c is used\n"
767 "\n"
768 " signed unsigned void\n"
769 " char short int\n"
770 " long long long signed char\n"
771 " signed short signed int signed long\n"
772 " singed long long unsigned char unsigned short\n"
773 " unsigned int unsigned long unsigned long long\n"
774 " _Bool float double\n"
775 " long double float imaginary double imaginary\n"
776 " long double imaginary float complex\n"
777 " double complex long double complex\n"
778 "\n"
779 " int8_t int16_t int32_t\n"
780 " int64_t intptr_t uint8_t\n"
781 " uint16_t uint32_t uint64_t\n"
782 " uchar_t ushort_t uint_t\n"
783 " ulong_t u_longlong_t ptrdiff_t\n"
784 " uintptr_t\n"
785 "\n";
786
787 void
cmd_typedef_help(void)788 cmd_typedef_help(void)
789 {
790 mdb_printf("%s", typedef_desc);
791 (void) mdb_dec_indent(2);
792 mdb_printf("%<b>OPTIONS%</b>\n");
793 (void) mdb_inc_indent(2);
794 mdb_printf("%s", typedef_opts);
795 (void) mdb_dec_indent(2);
796 mdb_printf("%<b>EXAMPLES%</b>\n");
797 (void) mdb_inc_indent(2);
798 mdb_printf("%s", typedef_examps);
799 (void) mdb_dec_indent(2);
800 mdb_printf("%<b>INTRINSICS%</b>\n");
801 (void) mdb_inc_indent(2);
802 mdb_printf("%s", typedef_intrins);
803 }
804