diff options
Diffstat (limited to 'usr/src/lib/libdemangle/common/demangle.c')
-rw-r--r-- | usr/src/lib/libdemangle/common/demangle.c | 130 |
1 files changed, 108 insertions, 22 deletions
diff --git a/usr/src/lib/libdemangle/common/demangle.c b/usr/src/lib/libdemangle/common/demangle.c index e827fd8cec..4f8e9ad678 100644 --- a/usr/src/lib/libdemangle/common/demangle.c +++ b/usr/src/lib/libdemangle/common/demangle.c @@ -11,13 +11,17 @@ /* * Copyright 2018 Jason King + * Copyright 2019, Joyent, Inc. */ #include <stdlib.h> +#include <stdio.h> #include <string.h> #include <errno.h> #include <pthread.h> +#include <sys/ctype.h> #include <sys/debug.h> +#include <stdarg.h> #include "demangle-sys.h" #include "demangle_int.h" @@ -25,31 +29,63 @@ static pthread_once_t debug_once = PTHREAD_ONCE_INIT; volatile boolean_t demangle_debug; +FILE *debugf = stderr; + +static const char * +langstr(sysdem_lang_t lang) +{ + switch (lang) { + case SYSDEM_LANG_AUTO: + return ("auto"); + case SYSDEM_LANG_CPP: + return ("c++"); + case SYSDEM_LANG_RUST: + return ("rust"); + default: + return ("invalid"); + } +} static sysdem_lang_t -detect_lang(const char *str) +detect_lang(const char *str, size_t n) { - size_t n = strlen(str); + const char *p = str; + size_t len; if (n < 3 || str[0] != '_') return (SYSDEM_LANG_AUTO); - switch (str[1]) { - case 'Z': + /* + * Check for ^_Z or ^__Z + */ + p = str + 1; + if (*p == '_') { + p++; + } + + if (*p != 'Z') + return (SYSDEM_LANG_AUTO); + + /* + * Sadly, rust currently uses the same prefix as C++, however + * demangling rust as a C++ mangled name yields less than desirable + * results. However rust names end with a hash. We use that to + * attempt to disambiguate + */ + + /* Find 'h'<hexdigit>+E$ */ + if ((p = strrchr(p, 'h')) == NULL) return (SYSDEM_LANG_CPP); - case '_': - break; + if ((len = strspn(p + 1, "0123456789abcdef")) == 0) + return (SYSDEM_LANG_CPP); - default: - return (SYSDEM_LANG_AUTO); - } + p += len + 1; - /* why they use ___Z sometimes is puzzling... *sigh* */ - if (str[2] == '_' && str[3] == 'Z') + if (p[0] != 'E' || p[1] != '\0') return (SYSDEM_LANG_CPP); - return (SYSDEM_LANG_AUTO); + return (SYSDEM_LANG_RUST); } static void @@ -62,26 +98,76 @@ check_debug(void) char * sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops) { + /* + * While the language specific demangler code can handle non-NUL + * terminated strings, we currently don't expose this to consumers. + * Consumers should still pass in a NUL-terminated string. + */ + size_t slen; + VERIFY0(pthread_once(&debug_once, check_debug)); + DEMDEBUG("name = '%s'", (str == NULL) ? "(NULL)" : str); + DEMDEBUG("lang = %s (%d)", langstr(lang), lang); + + if (str == NULL) { + errno = EINVAL; + return (NULL); + } + + slen = strlen(str); + + switch (lang) { + case SYSDEM_LANG_AUTO: + case SYSDEM_LANG_CPP: + case SYSDEM_LANG_RUST: + break; + default: + errno = EINVAL; + return (NULL); + } + if (ops == NULL) ops = sysdem_ops_default; if (lang == SYSDEM_LANG_AUTO) { - lang = detect_lang(str); - if (lang == SYSDEM_LANG_AUTO) { - errno = ENOTSUP; - return (NULL); - } + lang = detect_lang(str, slen); + if (lang != SYSDEM_LANG_AUTO) + DEMDEBUG("detected language is %s", langstr(lang)); } switch (lang) { - case SYSDEM_LANG_AUTO: - break; case SYSDEM_LANG_CPP: - return (cpp_demangle(str, ops)); + return (cpp_demangle(str, slen, ops)); + case SYSDEM_LANG_RUST: + return (rust_demangle(str, slen, ops)); + case SYSDEM_LANG_AUTO: + DEMDEBUG("could not detect language"); + errno = ENOTSUP; + return (NULL); + default: + /* + * This can't happen unless there's a bug with detect_lang, + * but gcc doesn't know that. + */ + errno = EINVAL; + return (NULL); } +} - errno = ENOTSUP; - return (NULL); +int +demdebug(const char *fmt, ...) +{ + va_list ap; + + flockfile(debugf); + (void) fprintf(debugf, "LIBDEMANGLE: "); + va_start(ap, fmt); + (void) vfprintf(debugf, fmt, ap); + (void) fputc('\n', debugf); + (void) fflush(debugf); + va_end(ap); + funlockfile(debugf); + + return (0); } |