diff options
Diffstat (limited to 'usr/src/lib/libdemangle/common/demangle.c')
-rw-r--r-- | usr/src/lib/libdemangle/common/demangle.c | 100 |
1 files changed, 51 insertions, 49 deletions
diff --git a/usr/src/lib/libdemangle/common/demangle.c b/usr/src/lib/libdemangle/common/demangle.c index b6db356416..bf7c9ab8c7 100644 --- a/usr/src/lib/libdemangle/common/demangle.c +++ b/usr/src/lib/libdemangle/common/demangle.c @@ -10,7 +10,7 @@ */ /* - * Copyright 2018 Jason King + * Copyright 2021 Jason King * Copyright 2019, Joyent, Inc. */ @@ -18,6 +18,7 @@ #include <stdio.h> #include <string.h> #include <errno.h> +#include <limits.h> #include <pthread.h> #include <sys/ctype.h> #include <sys/debug.h> @@ -25,6 +26,7 @@ #include <stdarg.h> #include "demangle-sys.h" #include "demangle_int.h" +#include "strview.h" #define DEMANGLE_DEBUG "DEMANGLE_DEBUG" @@ -68,46 +70,24 @@ sysdem_parse_lang(const char *str, sysdem_lang_t *langp) return (B_FALSE); } -static sysdem_lang_t -detect_lang(const char *str, size_t n) +/* + * A quick check if str can possibly be a mangled string. Currently, that + * means it must start with _Z or __Z. + */ +static boolean_t +is_mangled(const char *str, size_t n) { - const char *p = str; - size_t len; - - if (n < 3 || str[0] != '_') - return (SYSDEM_LANG_AUTO); - - /* - * Check for ^_Z or ^__Z - */ - p = str + 1; - if (*p == '_') { - p++; - } + strview_t sv; - if (*p != 'Z') - return (SYSDEM_LANG_AUTO); + sv_init_str(&sv, str, str + n); - /* - * 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); - - if ((len = strspn(p + 1, "0123456789abcdef")) == 0) - return (SYSDEM_LANG_CPP); + if (!sv_consume_if_c(&sv, '_')) + return (B_FALSE); + (void) sv_consume_if_c(&sv, '_'); + if (sv_consume_if_c(&sv, 'Z')) + return (B_TRUE); - p += len + 1; - - if (p[0] != 'E' || p[1] != '\0') - return (SYSDEM_LANG_CPP); - - return (SYSDEM_LANG_RUST); + return (B_FALSE); } static void @@ -120,6 +100,7 @@ check_debug(void) char * sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops) { + char *res = NULL; /* * While the language specific demangler code can handle non-NUL * terminated strings, we currently don't expose this to consumers. @@ -152,29 +133,50 @@ sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops) if (ops == NULL) ops = sysdem_ops_default; - if (lang == SYSDEM_LANG_AUTO) { - lang = detect_lang(str, slen); - if (lang != SYSDEM_LANG_AUTO) - DEMDEBUG("detected language is %s", langstr(lang)); - } - + /* + * If we were given an explicit language to demangle, we always + * use that. If not, we try to demangle as rust, then c++. Any + * mangled C++ symbol that manages to successfully demangle as a + * legacy rust symbol _should_ look the same as it can really + * only be a very simple C++ symbol. Otherwise, the rust demangling + * should fail and we can try C++. + */ switch (lang) { case SYSDEM_LANG_CPP: 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: + break; + } + + /* + * To save us some potential work, if the symbol cannot + * possibly be a rust or C++ mangled name, we don't + * even attempt to demangle either. + */ + if (!is_mangled(str, slen)) { /* - * This can't happen unless there's a bug with detect_lang, - * but gcc doesn't know that. + * This does mean if we somehow get a string > 2GB + * the debugging output will be truncated, but that + * seems an acceptable tradeoff. */ + int len = slen > INT_MAX ? INT_MAX : slen; + + DEMDEBUG("ERROR: '%.*s' cannot be a mangled string", len, str); errno = EINVAL; return (NULL); } + + DEMDEBUG("trying rust"); + res = rust_demangle(str, slen, ops); + + IMPLY(ret != NULL, errno == 0); + if (res != NULL) + return (res); + + DEMDEBUG("trying C++"); + return (cpp_demangle(str, slen, ops)); } int |