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 2018 Jason King 14 * Copyright 2019, Joyent, Inc. 15 */ 16 17 #include <stdlib.h> 18 #include <stdio.h> 19 #include <string.h> 20 #include <errno.h> 21 #include <pthread.h> 22 #include <sys/ctype.h> 23 #include <sys/debug.h> 24 #include <sys/sysmacros.h> 25 #include <stdarg.h> 26 #include "demangle-sys.h" 27 #include "demangle_int.h" 28 29 #define DEMANGLE_DEBUG "DEMANGLE_DEBUG" 30 31 static pthread_once_t debug_once = PTHREAD_ONCE_INIT; 32 volatile boolean_t demangle_debug; 33 FILE *debugf = stderr; 34 35 static struct { 36 const char *str; 37 sysdem_lang_t lang; 38 } lang_tbl[] = { 39 { "auto", SYSDEM_LANG_AUTO }, 40 { "c++", SYSDEM_LANG_CPP }, 41 { "rust", SYSDEM_LANG_RUST }, 42 }; 43 44 static const char * 45 langstr(sysdem_lang_t lang) 46 { 47 size_t i; 48 49 for (i = 0; i < ARRAY_SIZE(lang_tbl); i++) { 50 if (lang == lang_tbl[i].lang) 51 return (lang_tbl[i].str); 52 } 53 return ("invalid"); 54 } 55 56 boolean_t 57 sysdem_parse_lang(const char *str, sysdem_lang_t *langp) 58 { 59 size_t i; 60 61 for (i = 0; i < ARRAY_SIZE(lang_tbl); i++) { 62 if (strcmp(str, lang_tbl[i].str) == 0) { 63 *langp = lang_tbl[i].lang; 64 return (B_TRUE); 65 } 66 } 67 68 return (B_FALSE); 69 } 70 71 static sysdem_lang_t 72 detect_lang(const char *str, size_t n) 73 { 74 const char *p = str; 75 size_t len; 76 77 if (n < 3 || str[0] != '_') 78 return (SYSDEM_LANG_AUTO); 79 80 /* 81 * Check for ^_Z or ^__Z 82 */ 83 p = str + 1; 84 if (*p == '_') { 85 p++; 86 } 87 88 if (*p != 'Z') 89 return (SYSDEM_LANG_AUTO); 90 91 /* 92 * Sadly, rust currently uses the same prefix as C++, however 93 * demangling rust as a C++ mangled name yields less than desirable 94 * results. However rust names end with a hash. We use that to 95 * attempt to disambiguate 96 */ 97 98 /* Find 'h'<hexdigit>+E$ */ 99 if ((p = strrchr(p, 'h')) == NULL) 100 return (SYSDEM_LANG_CPP); 101 102 if ((len = strspn(p + 1, "0123456789abcdef")) == 0) 103 return (SYSDEM_LANG_CPP); 104 105 p += len + 1; 106 107 if (p[0] != 'E' || p[1] != '\0') 108 return (SYSDEM_LANG_CPP); 109 110 return (SYSDEM_LANG_RUST); 111 } 112 113 static void 114 check_debug(void) 115 { 116 if (getenv(DEMANGLE_DEBUG)) 117 demangle_debug = B_TRUE; 118 } 119 120 char * 121 sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops) 122 { 123 /* 124 * While the language specific demangler code can handle non-NUL 125 * terminated strings, we currently don't expose this to consumers. 126 * Consumers should still pass in a NUL-terminated string. 127 */ 128 size_t slen; 129 130 VERIFY0(pthread_once(&debug_once, check_debug)); 131 132 DEMDEBUG("name = '%s'", (str == NULL) ? "(NULL)" : str); 133 DEMDEBUG("lang = %s (%d)", langstr(lang), lang); 134 135 if (str == NULL) { 136 errno = EINVAL; 137 return (NULL); 138 } 139 140 slen = strlen(str); 141 142 switch (lang) { 143 case SYSDEM_LANG_AUTO: 144 case SYSDEM_LANG_CPP: 145 case SYSDEM_LANG_RUST: 146 break; 147 default: 148 errno = EINVAL; 149 return (NULL); 150 } 151 152 if (ops == NULL) 153 ops = sysdem_ops_default; 154 155 if (lang == SYSDEM_LANG_AUTO) { 156 lang = detect_lang(str, slen); 157 if (lang != SYSDEM_LANG_AUTO) 158 DEMDEBUG("detected language is %s", langstr(lang)); 159 } 160 161 switch (lang) { 162 case SYSDEM_LANG_CPP: 163 return (cpp_demangle(str, slen, ops)); 164 case SYSDEM_LANG_RUST: 165 return (rust_demangle(str, slen, ops)); 166 case SYSDEM_LANG_AUTO: 167 DEMDEBUG("could not detect language"); 168 errno = ENOTSUP; 169 return (NULL); 170 default: 171 /* 172 * This can't happen unless there's a bug with detect_lang, 173 * but gcc doesn't know that. 174 */ 175 errno = EINVAL; 176 return (NULL); 177 } 178 } 179 180 int 181 demdebug(const char *fmt, ...) 182 { 183 va_list ap; 184 185 flockfile(debugf); 186 (void) fprintf(debugf, "LIBDEMANGLE: "); 187 va_start(ap, fmt); 188 (void) vfprintf(debugf, fmt, ap); 189 (void) fputc('\n', debugf); 190 (void) fflush(debugf); 191 va_end(ap); 192 funlockfile(debugf); 193 194 return (0); 195 } 196