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.c130
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);
}