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