14226f635SJason King /* 24226f635SJason King * This file and its contents are supplied under the terms of the 34226f635SJason King * Common Development and Distribution License ("CDDL"), version 1.0. 44226f635SJason King * You may only use this file in accordance with the terms of version 54226f635SJason King * 1.0 of the CDDL. 64226f635SJason King * 74226f635SJason King * A full copy of the text of the CDDL should have accompanied this 84226f635SJason King * source. A copy of the CDDL is also available via the Internet at 94226f635SJason King * http://www.illumos.org/license/CDDL. 104226f635SJason King */ 114226f635SJason King 124226f635SJason King /* 13*f5ac8590SJason King * Copyright 2021 Jason King 146a6cfa5dSJason King * Copyright 2019, Joyent, Inc. 154226f635SJason King */ 164226f635SJason King 174226f635SJason King #include <stdlib.h> 186a6cfa5dSJason King #include <stdio.h> 194226f635SJason King #include <string.h> 204226f635SJason King #include <errno.h> 21*f5ac8590SJason King #include <limits.h> 224226f635SJason King #include <pthread.h> 236a6cfa5dSJason King #include <sys/ctype.h> 244226f635SJason King #include <sys/debug.h> 25d52aae23SJason King #include <sys/sysmacros.h> 266a6cfa5dSJason King #include <stdarg.h> 274226f635SJason King #include "demangle-sys.h" 284226f635SJason King #include "demangle_int.h" 29*f5ac8590SJason King #include "strview.h" 304226f635SJason King 314226f635SJason King #define DEMANGLE_DEBUG "DEMANGLE_DEBUG" 324226f635SJason King 334226f635SJason King static pthread_once_t debug_once = PTHREAD_ONCE_INIT; 344226f635SJason King volatile boolean_t demangle_debug; 356a6cfa5dSJason King FILE *debugf = stderr; 366a6cfa5dSJason King 37d52aae23SJason King static struct { 38d52aae23SJason King const char *str; 39d52aae23SJason King sysdem_lang_t lang; 40d52aae23SJason King } lang_tbl[] = { 41d52aae23SJason King { "auto", SYSDEM_LANG_AUTO }, 42d52aae23SJason King { "c++", SYSDEM_LANG_CPP }, 43d52aae23SJason King { "rust", SYSDEM_LANG_RUST }, 44d52aae23SJason King }; 45d52aae23SJason King 466a6cfa5dSJason King static const char * 476a6cfa5dSJason King langstr(sysdem_lang_t lang) 486a6cfa5dSJason King { 49d52aae23SJason King size_t i; 50d52aae23SJason King 51d52aae23SJason King for (i = 0; i < ARRAY_SIZE(lang_tbl); i++) { 52d52aae23SJason King if (lang == lang_tbl[i].lang) 53d52aae23SJason King return (lang_tbl[i].str); 54d52aae23SJason King } 556a6cfa5dSJason King return ("invalid"); 566a6cfa5dSJason King } 57d52aae23SJason King 58d52aae23SJason King boolean_t 59d52aae23SJason King sysdem_parse_lang(const char *str, sysdem_lang_t *langp) 60d52aae23SJason King { 61d52aae23SJason King size_t i; 62d52aae23SJason King 63d52aae23SJason King for (i = 0; i < ARRAY_SIZE(lang_tbl); i++) { 64d52aae23SJason King if (strcmp(str, lang_tbl[i].str) == 0) { 65d52aae23SJason King *langp = lang_tbl[i].lang; 66d52aae23SJason King return (B_TRUE); 67d52aae23SJason King } 68d52aae23SJason King } 69d52aae23SJason King 70d52aae23SJason King return (B_FALSE); 716a6cfa5dSJason King } 724226f635SJason King 73*f5ac8590SJason King /* 74*f5ac8590SJason King * A quick check if str can possibly be a mangled string. Currently, that 75*f5ac8590SJason King * means it must start with _Z or __Z. 76*f5ac8590SJason King */ 77*f5ac8590SJason King static boolean_t 78*f5ac8590SJason King is_mangled(const char *str, size_t n) 794226f635SJason King { 80*f5ac8590SJason King strview_t sv; 814226f635SJason King 82*f5ac8590SJason King sv_init_str(&sv, str, str + n); 834226f635SJason King 84*f5ac8590SJason King if (!sv_consume_if_c(&sv, '_')) 85*f5ac8590SJason King return (B_FALSE); 86*f5ac8590SJason King (void) sv_consume_if_c(&sv, '_'); 87*f5ac8590SJason King if (sv_consume_if_c(&sv, 'Z')) 88*f5ac8590SJason King return (B_TRUE); 894226f635SJason King 90*f5ac8590SJason King return (B_FALSE); 914226f635SJason King } 924226f635SJason King 934226f635SJason King static void 944226f635SJason King check_debug(void) 954226f635SJason King { 964226f635SJason King if (getenv(DEMANGLE_DEBUG)) 974226f635SJason King demangle_debug = B_TRUE; 984226f635SJason King } 994226f635SJason King 1004226f635SJason King char * 1014226f635SJason King sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops) 1024226f635SJason King { 103*f5ac8590SJason King char *res = NULL; 1046a6cfa5dSJason King /* 1056a6cfa5dSJason King * While the language specific demangler code can handle non-NUL 1066a6cfa5dSJason King * terminated strings, we currently don't expose this to consumers. 1076a6cfa5dSJason King * Consumers should still pass in a NUL-terminated string. 1086a6cfa5dSJason King */ 1096a6cfa5dSJason King size_t slen; 1106a6cfa5dSJason King 1114226f635SJason King VERIFY0(pthread_once(&debug_once, check_debug)); 1124226f635SJason King 1136a6cfa5dSJason King DEMDEBUG("name = '%s'", (str == NULL) ? "(NULL)" : str); 1146a6cfa5dSJason King DEMDEBUG("lang = %s (%d)", langstr(lang), lang); 1156a6cfa5dSJason King 1166a6cfa5dSJason King if (str == NULL) { 1176a6cfa5dSJason King errno = EINVAL; 1186a6cfa5dSJason King return (NULL); 1196a6cfa5dSJason King } 1206a6cfa5dSJason King 1216a6cfa5dSJason King slen = strlen(str); 1226a6cfa5dSJason King 1236a6cfa5dSJason King switch (lang) { 1246a6cfa5dSJason King case SYSDEM_LANG_AUTO: 1256a6cfa5dSJason King case SYSDEM_LANG_CPP: 1266a6cfa5dSJason King case SYSDEM_LANG_RUST: 1276a6cfa5dSJason King break; 1286a6cfa5dSJason King default: 1296a6cfa5dSJason King errno = EINVAL; 1306a6cfa5dSJason King return (NULL); 1316a6cfa5dSJason King } 1326a6cfa5dSJason King 1334226f635SJason King if (ops == NULL) 1344226f635SJason King ops = sysdem_ops_default; 1354226f635SJason King 136*f5ac8590SJason King /* 137*f5ac8590SJason King * If we were given an explicit language to demangle, we always 138*f5ac8590SJason King * use that. If not, we try to demangle as rust, then c++. Any 139*f5ac8590SJason King * mangled C++ symbol that manages to successfully demangle as a 140*f5ac8590SJason King * legacy rust symbol _should_ look the same as it can really 141*f5ac8590SJason King * only be a very simple C++ symbol. Otherwise, the rust demangling 142*f5ac8590SJason King * should fail and we can try C++. 143*f5ac8590SJason King */ 1444226f635SJason King switch (lang) { 1454226f635SJason King case SYSDEM_LANG_CPP: 1466a6cfa5dSJason King return (cpp_demangle(str, slen, ops)); 1476a6cfa5dSJason King case SYSDEM_LANG_RUST: 1486a6cfa5dSJason King return (rust_demangle(str, slen, ops)); 1496a6cfa5dSJason King case SYSDEM_LANG_AUTO: 150*f5ac8590SJason King break; 151*f5ac8590SJason King } 152*f5ac8590SJason King 1536a6cfa5dSJason King /* 154*f5ac8590SJason King * To save us some potential work, if the symbol cannot 155*f5ac8590SJason King * possibly be a rust or C++ mangled name, we don't 156*f5ac8590SJason King * even attempt to demangle either. 1576a6cfa5dSJason King */ 158*f5ac8590SJason King if (!is_mangled(str, slen)) { 159*f5ac8590SJason King /* 160*f5ac8590SJason King * This does mean if we somehow get a string > 2GB 161*f5ac8590SJason King * the debugging output will be truncated, but that 162*f5ac8590SJason King * seems an acceptable tradeoff. 163*f5ac8590SJason King */ 164*f5ac8590SJason King int len = slen > INT_MAX ? INT_MAX : slen; 165*f5ac8590SJason King 166*f5ac8590SJason King DEMDEBUG("ERROR: '%.*s' cannot be a mangled string", len, str); 1676a6cfa5dSJason King errno = EINVAL; 1686a6cfa5dSJason King return (NULL); 1696a6cfa5dSJason King } 170*f5ac8590SJason King 171*f5ac8590SJason King DEMDEBUG("trying rust"); 172*f5ac8590SJason King res = rust_demangle(str, slen, ops); 173*f5ac8590SJason King 174*f5ac8590SJason King IMPLY(ret != NULL, errno == 0); 175*f5ac8590SJason King if (res != NULL) 176*f5ac8590SJason King return (res); 177*f5ac8590SJason King 178*f5ac8590SJason King DEMDEBUG("trying C++"); 179*f5ac8590SJason King return (cpp_demangle(str, slen, ops)); 1806a6cfa5dSJason King } 1816a6cfa5dSJason King 1826a6cfa5dSJason King int 1836a6cfa5dSJason King demdebug(const char *fmt, ...) 1846a6cfa5dSJason King { 1856a6cfa5dSJason King va_list ap; 1866a6cfa5dSJason King 1876a6cfa5dSJason King flockfile(debugf); 1886a6cfa5dSJason King (void) fprintf(debugf, "LIBDEMANGLE: "); 1896a6cfa5dSJason King va_start(ap, fmt); 1906a6cfa5dSJason King (void) vfprintf(debugf, fmt, ap); 1916a6cfa5dSJason King (void) fputc('\n', debugf); 1926a6cfa5dSJason King (void) fflush(debugf); 1936a6cfa5dSJason King va_end(ap); 1946a6cfa5dSJason King funlockfile(debugf); 1956a6cfa5dSJason King 1966a6cfa5dSJason King return (0); 1974226f635SJason King } 198