summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdemangle/common/demangle.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libdemangle/common/demangle.c')
-rw-r--r--usr/src/lib/libdemangle/common/demangle.c100
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