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 2017 Jason King 14 */ 15 16 #include <string.h> 17 #include <errno.h> 18 #include <stdlib.h> 19 #include "demangle_int.h" 20 #include "cxx.h" 21 22 #define CHUNK_SIZE (8U) 23 24 /* 25 * A name_t is essentially a stack of str_pair_t's. Generally, the parsing 26 * code will push (via name_add() or the like) portions of the demangled 27 * name into a name_t, and periodically combine them via name_join(). 28 * 29 * As such it should be noted that since items are added at the end of 30 * name_t->nm_items, the numbering of name_at() starts at the end 31 * of name_t->nm_items, i.e. name_at(n, 0) == &n->nm_items[n->nm_len - 1]. 32 * 33 * It should also be noted that for name_t's, adding is a move operation in 34 * that it takes ownership of the passed in string/str_t/etc 35 */ 36 37 void 38 name_init(name_t *n, sysdem_ops_t *ops) 39 { 40 (void) memset(n, 0, sizeof (*n)); 41 n->nm_ops = (ops != NULL) ? ops : sysdem_ops_default; 42 } 43 44 void 45 name_fini(name_t *n) 46 { 47 if (n == NULL) 48 return; 49 50 name_clear(n); 51 xfree(n->nm_ops, n->nm_items, n->nm_size); 52 n->nm_items = NULL; 53 n->nm_size = 0; 54 } 55 56 size_t 57 name_len(const name_t *n) 58 { 59 return (n->nm_len); 60 } 61 62 boolean_t 63 name_empty(const name_t *n) 64 { 65 return (name_len(n) == 0 ? B_TRUE : B_FALSE); 66 } 67 68 void 69 name_clear(name_t *n) 70 { 71 if (n == NULL) 72 return; 73 74 for (size_t i = 0; i < n->nm_len; i++) { 75 str_pair_t *sp = &n->nm_items[i]; 76 sysdem_ops_t *ops = sp->strp_l.str_ops; 77 78 str_pair_fini(sp); 79 (void) str_pair_init(sp, ops); 80 } 81 82 n->nm_len = 0; 83 } 84 85 static boolean_t 86 name_reserve(name_t *n, size_t amt) 87 { 88 size_t newlen = n->nm_len + amt; 89 90 if (newlen == cpp_name_max_depth) { 91 errno = ENAMETOOLONG; 92 return (B_FALSE); 93 } 94 95 if (newlen < n->nm_size) 96 return (B_TRUE); 97 98 size_t newsize = roundup(newlen, CHUNK_SIZE); 99 if (newsize > cpp_name_max_depth) 100 newsize = cpp_name_max_depth; 101 102 void *temp = xrealloc(n->nm_ops, n->nm_items, 103 n->nm_size * sizeof (str_pair_t), newsize * sizeof (str_pair_t)); 104 105 if (temp == NULL) 106 return (B_FALSE); 107 108 n->nm_items = temp; 109 n->nm_size = newsize; 110 return (B_TRUE); 111 } 112 113 boolean_t 114 name_add(name_t *n, const char *l, size_t l_len, const char *r, size_t r_len) 115 { 116 str_t sl = { 0 }; 117 str_t sr = { 0 }; 118 119 str_init(&sl, n->nm_ops); 120 str_init(&sr, n->nm_ops); 121 str_set(&sl, l, l_len); 122 str_set(&sr, r, r_len); 123 return (name_add_str(n, &sl, &sr)); 124 } 125 126 boolean_t 127 name_add_str(name_t *n, str_t *l, str_t *r) 128 { 129 str_pair_t sp; 130 131 (void) str_pair_init(&sp, n->nm_ops); 132 133 if (!name_reserve(n, 1)) 134 return (B_FALSE); 135 136 if (l != NULL) { 137 sp.strp_l = *l; 138 (void) memset(l, 0, sizeof (*l)); 139 } 140 141 if (r != NULL) { 142 sp.strp_r = *r; 143 (void) memset(r, 0, sizeof (*r)); 144 } 145 146 n->nm_items[n->nm_len++] = sp; 147 148 return (B_TRUE); 149 } 150 151 str_pair_t * 152 name_at(const name_t *n, size_t idx) 153 { 154 VERIFY(!name_empty(n)); 155 VERIFY3U(idx, <, n->nm_len); 156 return (&n->nm_items[n->nm_len - idx - 1]); 157 } 158 159 str_pair_t * 160 name_top(name_t *n) 161 { 162 return (name_at(n, 0)); 163 } 164 165 void 166 name_pop(name_t *n, str_pair_t *sp) 167 { 168 if (n->nm_len == 0) 169 return; 170 171 str_pair_t *top = name_top(n); 172 173 if (sp != NULL) { 174 *sp = *top; 175 (void) memset(top, 0, sizeof (*top)); 176 } else { 177 str_pair_fini(top); 178 } 179 180 n->nm_len--; 181 } 182 183 boolean_t 184 name_join(name_t *n, size_t amt, const char *sep) 185 { 186 str_pair_t *sp = NULL; 187 str_t res = { 0 }; 188 size_t seplen = strlen(sep); 189 190 VERIFY3U(amt, <=, n->nm_len); 191 192 /* 193 * A join of 0 elements places an empty string on the stack. This 194 * simplifies code that wants to do things like: 195 * name_join(...); name_fmt(.., "({0})", ...) 196 */ 197 if (amt == 0) { 198 (void) name_add(n, "", 0, "", 0); 199 return (B_TRUE); 200 } 201 202 /* A join of 1 element just implies merging the top str_pair_t */ 203 if (amt == 1) { 204 VERIFY3U(name_len(n), >, 0); 205 return (str_pair_merge(name_top(n))); 206 } 207 208 (void) str_init(&res, n->nm_ops); 209 210 sp = name_at(n, amt - 1); 211 for (size_t i = 0; i < amt; i++) { 212 if (i > 0) { 213 if (!str_append(&res, sep, seplen)) 214 goto error; 215 } 216 217 if (!str_append_str(&res, &sp->strp_l)) 218 goto error; 219 if (!str_append_str(&res, &sp->strp_r)) 220 goto error; 221 222 sp++; 223 } 224 225 for (size_t i = 0; i < amt; i++) 226 name_pop(n, NULL); 227 228 /* since we've removed at least 1 entry, this should always succeed */ 229 VERIFY(name_add_str(n, &res, NULL)); 230 return (B_TRUE); 231 232 error: 233 str_fini(&res); 234 return (B_FALSE); 235 } 236 237 static boolean_t 238 name_fmt_s(name_t *n, str_t *s, const char *fmt, long *maxp) 239 { 240 const char *p; 241 long max = -1; 242 243 if (fmt == NULL) 244 return (B_TRUE); 245 246 for (p = fmt; *p != '\0'; p++) { 247 if (*p != '{') { 248 (void) str_append_c(s, *p); 249 continue; 250 } 251 252 errno = 0; 253 char *q = NULL; 254 long val = strtol(p + 1, &q, 10); 255 256 VERIFY(val != 0 || errno == 0); 257 VERIFY3U(val, <, n->nm_len); 258 259 str_pair_t *sp = name_at(n, val); 260 261 if (val > max) 262 max = val; 263 264 switch (q[0]) { 265 case '}': 266 if (!str_append_str(s, &sp->strp_l)) 267 return (B_FALSE); 268 if (!str_append_str(s, &sp->strp_r)) 269 return (B_FALSE); 270 p = q; 271 continue; 272 case ':': 273 switch (q[1]) { 274 case 'L': 275 if (!str_append_str(s, &sp->strp_l)) 276 return (B_FALSE); 277 break; 278 case 'R': 279 if (!str_append_str(s, &sp->strp_r)) 280 return (B_FALSE); 281 break; 282 } 283 284 p = q + 2; 285 VERIFY(*p == '}'); 286 break; 287 } 288 } 289 290 if (*maxp < max) 291 *maxp = max; 292 293 return (B_TRUE); 294 } 295 296 /* 297 * Replace a number of elements in the name stack with a formatted string 298 * for format is a plain string with optional {nnn} or {nnn:L|R} substitutions 299 * where nnn is the stack position of an element and it's contents (both 300 * left and right pieces) are inserted. Optionally, only the left or 301 * right piece can specified using :L|R e.g. {2:L}{3}{2:R} would insert 302 * the left piece of element 2, all of element 3, then the right piece of 303 * element 2. 304 * 305 * Once complete, all elements up to the deepest one references are popped 306 * off the stack, and the resulting formatted string is pushed into n. 307 * 308 * This could be done as a sequence of push & pops, but this makes the 309 * intended output far clearer to see. 310 */ 311 boolean_t 312 name_fmt(name_t *n, const char *fmt_l, const char *fmt_r) 313 { 314 str_pair_t res; 315 long max = -1; 316 317 (void) str_pair_init(&res, n->nm_ops); 318 319 if (!name_reserve(n, 1)) 320 return (B_FALSE); 321 322 if (!name_fmt_s(n, &res.strp_l, fmt_l, &max)) 323 goto error; 324 if (!name_fmt_s(n, &res.strp_r, fmt_r, &max)) 325 goto error; 326 327 if (max >= 0) { 328 for (size_t i = 0; i <= max; i++) 329 name_pop(n, NULL); 330 } 331 332 n->nm_items[n->nm_len++] = res; 333 return (B_TRUE); 334 335 error: 336 str_pair_fini(&res); 337 return (B_FALSE); 338 } 339 340 /* 341 * The substitution list is a list of name_t's that get added as the 342 * demangled name is parsed. Adding a name_t to the substitution list 343 * is a copy operation, and likewise inserting a substitution into a name_t 344 * is also a copy operation. 345 */ 346 void 347 sub_init(sub_t *sub, sysdem_ops_t *ops) 348 { 349 (void) memset(sub, 0, sizeof (*sub)); 350 sub->sub_ops = (ops != NULL) ? ops : sysdem_ops_default; 351 } 352 353 void 354 sub_fini(sub_t *sub) 355 { 356 if (sub == NULL) 357 return; 358 359 sub_clear(sub); 360 xfree(sub->sub_ops, sub->sub_items, sub->sub_size); 361 sub->sub_items = NULL; 362 sub->sub_size = 0; 363 } 364 365 void 366 sub_clear(sub_t *sub) 367 { 368 if (sub == NULL) 369 return; 370 371 for (size_t i = 0; i < sub->sub_len; i++) 372 name_fini(&sub->sub_items[i]); 373 374 sub->sub_len = 0; 375 } 376 377 boolean_t 378 sub_empty(const sub_t *sub) 379 { 380 return ((sub->sub_len == 0) ? B_TRUE : B_FALSE); 381 } 382 383 size_t 384 sub_len(const sub_t *sub) 385 { 386 return (sub->sub_len); 387 } 388 389 static boolean_t 390 sub_reserve(sub_t *sub, size_t amt) 391 { 392 if (sub->sub_len + amt < sub->sub_size) 393 return (B_TRUE); 394 395 size_t newsize = roundup(sub->sub_size + amt, CHUNK_SIZE); 396 void *temp = xrealloc(sub->sub_ops, sub->sub_items, 397 sub->sub_size * sizeof (name_t), newsize * sizeof (name_t)); 398 399 if (temp == NULL) 400 return (B_FALSE); 401 402 sub->sub_items = temp; 403 sub->sub_size = newsize; 404 405 return (B_TRUE); 406 } 407 408 /* save the element of n (up to depth elements deep) as a substitution */ 409 boolean_t 410 sub_save(sub_t *sub, const name_t *n, size_t depth) 411 { 412 if (depth == 0) 413 return (B_TRUE); 414 415 if (!sub_reserve(sub, 1)) 416 return (B_FALSE); 417 418 name_t *dest = &sub->sub_items[sub->sub_len++]; 419 name_init(dest, sub->sub_ops); 420 421 if (!name_reserve(dest, depth)) { 422 name_fini(dest); 423 sub->sub_len--; 424 return (B_FALSE); 425 } 426 427 const str_pair_t *src_sp = name_at(n, depth - 1); 428 429 for (size_t i = 0; i < depth; i++, src_sp++) { 430 str_pair_t copy = { 0 }; 431 (void) str_pair_init(©, n->nm_ops); 432 if (!str_pair_copy(src_sp, ©)) { 433 str_pair_fini(©); 434 name_fini(dest); 435 return (B_FALSE); 436 } 437 438 VERIFY(name_add_str(dest, ©.strp_l, ©.strp_r)); 439 } 440 441 return (B_TRUE); 442 } 443 444 /* push substitution idx onto n */ 445 boolean_t 446 sub_substitute(const sub_t *sub, size_t idx, name_t *n) 447 { 448 VERIFY3U(idx, <, sub->sub_len); 449 450 const name_t *src = &sub->sub_items[idx]; 451 const str_pair_t *sp = src->nm_items; 452 size_t save = name_len(n); 453 454 for (size_t i = 0; i < src->nm_len; i++, sp++) { 455 str_pair_t copy = { 0 }; 456 457 if (!str_pair_copy(sp, ©)) 458 goto fail; 459 460 if (!name_add_str(n, ©.strp_l, ©.strp_r)) 461 goto fail; 462 } 463 464 return (B_TRUE); 465 466 fail: 467 for (size_t i = 0; i < name_len(n) - save; i++) 468 name_pop(n, NULL); 469 return (B_FALSE); 470 } 471 472 void 473 sub_pop(sub_t *sub) 474 { 475 name_t *top = &sub->sub_items[--sub->sub_len]; 476 name_fini(top); 477 } 478 479 /* 480 * Templates can use substitutions for it's arguments (using T instead of 481 * S). Since templates can nest however, each nesting requires a new 482 * set of substitutions. As such a new, empty list of template substitutions 483 * is pushed onto cpp_templ each time templates are nested, and popped at 484 * the end of the current template argument list. 485 */ 486 static boolean_t 487 templ_reserve(templ_t *tpl, size_t n) 488 { 489 if (tpl->tpl_len + n < tpl->tpl_size) 490 return (B_TRUE); 491 492 size_t newsize = tpl->tpl_size + CHUNK_SIZE; 493 void *temp = xrealloc(tpl->tpl_ops, tpl->tpl_items, 494 tpl->tpl_size * sizeof (sub_t), newsize * sizeof (sub_t)); 495 496 if (temp == NULL) 497 return (B_FALSE); 498 499 tpl->tpl_items = temp; 500 tpl->tpl_size = newsize; 501 return (B_TRUE); 502 } 503 504 void 505 templ_init(templ_t *tpl, sysdem_ops_t *ops) 506 { 507 (void) memset(tpl, 0, sizeof (*tpl)); 508 tpl->tpl_ops = ops; 509 } 510 511 void 512 templ_fini(templ_t *tpl) 513 { 514 if (tpl == NULL) 515 return; 516 517 for (size_t i = 0; i < tpl->tpl_len; i++) 518 sub_fini(&tpl->tpl_items[i]); 519 520 xfree(tpl->tpl_ops, tpl->tpl_items, tpl->tpl_size * sizeof (sub_t)); 521 sysdem_ops_t *ops = tpl->tpl_ops; 522 (void) memset(tpl, 0, sizeof (*tpl)); 523 tpl->tpl_ops = ops; 524 } 525 526 boolean_t 527 templ_push(templ_t *tpl) 528 { 529 if (!templ_reserve(tpl, 1)) 530 return (B_FALSE); 531 532 sub_t *sub = &tpl->tpl_items[tpl->tpl_len++]; 533 sub_init(sub, tpl->tpl_ops); 534 return (B_TRUE); 535 } 536 537 void 538 templ_pop(templ_t *tpl) 539 { 540 VERIFY(!templ_empty(tpl)); 541 542 sub_t *sub = &tpl->tpl_items[--tpl->tpl_len]; 543 sub_fini(sub); 544 } 545 546 sub_t * 547 templ_top(templ_t *tpl) 548 { 549 if (tpl->tpl_len == 0) 550 return (NULL); 551 return (&tpl->tpl_items[tpl->tpl_len - 1]); 552 } 553 554 boolean_t 555 templ_empty(const templ_t *tpl) 556 { 557 return ((tpl->tpl_len == 0) ? B_TRUE : B_FALSE); 558 } 559 560 size_t 561 templ_top_len(const templ_t *tpl) 562 { 563 const sub_t *sub = templ_top((templ_t *)tpl); 564 565 return (sub->sub_len); 566 } 567 568 boolean_t 569 templ_sub(const templ_t *tpl, size_t idx, name_t *n) 570 { 571 const sub_t *sub = templ_top((templ_t *)tpl); 572 573 return (sub_substitute(sub, idx, n)); 574 } 575 576 boolean_t 577 templ_save(const name_t *n, size_t amt, templ_t *tpl) 578 { 579 VERIFY3U(tpl->tpl_len, >, 0); 580 581 sub_t *s = templ_top(tpl); 582 boolean_t res = B_TRUE; 583 584 /* a bit of a hack -- want an 'empty' entry when saving 0 params */ 585 if (amt == 0) { 586 name_t name = { 0 }; 587 588 name_init(&name, tpl->tpl_ops); 589 res &= name_add(&name, "", 0, "", 0); 590 if (res) 591 res &= sub_save(s, &name, 1); 592 name_fini(&name); 593 } else { 594 res &= sub_save(s, n, amt); 595 } 596 597 return (res); 598 } 599