14226f635SJason King /*
24226f635SJason King * This file and its contents are supplied under the terms of the
34226f635SJason King * Common Development and Distribution License ("CDDL"), version 1.0.
44226f635SJason King * You may only use this file in accordance with the terms of version
54226f635SJason King * 1.0 of the CDDL.
64226f635SJason King *
74226f635SJason King * A full copy of the text of the CDDL should have accompanied this
84226f635SJason King * source. A copy of the CDDL is also available via the Internet at
94226f635SJason King * http://www.illumos.org/license/CDDL.
104226f635SJason King */
114226f635SJason King
124226f635SJason King /*
13f5ac8590SJason King * Copyright 2021 Jason King
14*1cd08393SJason King * Copyright 2019 Joyent, Inc.
154226f635SJason King */
164226f635SJason King
174226f635SJason King #include <stdlib.h>
186a6cfa5dSJason King #include <stdio.h>
194226f635SJason King #include <string.h>
204226f635SJason King #include <errno.h>
21f5ac8590SJason King #include <limits.h>
224226f635SJason King #include <pthread.h>
236a6cfa5dSJason King #include <sys/ctype.h>
244226f635SJason King #include <sys/debug.h>
25d52aae23SJason King #include <sys/sysmacros.h>
266a6cfa5dSJason King #include <stdarg.h>
274226f635SJason King #include "demangle-sys.h"
284226f635SJason King #include "demangle_int.h"
29f5ac8590SJason King #include "strview.h"
304226f635SJason King
314226f635SJason King #define DEMANGLE_DEBUG "DEMANGLE_DEBUG"
324226f635SJason King
334226f635SJason King static pthread_once_t debug_once = PTHREAD_ONCE_INIT;
344226f635SJason King volatile boolean_t demangle_debug;
356a6cfa5dSJason King FILE *debugf = stderr;
366a6cfa5dSJason King
37d52aae23SJason King static struct {
38d52aae23SJason King const char *str;
39d52aae23SJason King sysdem_lang_t lang;
40d52aae23SJason King } lang_tbl[] = {
41d52aae23SJason King { "auto", SYSDEM_LANG_AUTO },
42d52aae23SJason King { "c++", SYSDEM_LANG_CPP },
43d52aae23SJason King { "rust", SYSDEM_LANG_RUST },
44d52aae23SJason King };
45d52aae23SJason King
466a6cfa5dSJason King static const char *
langstr(sysdem_lang_t lang)476a6cfa5dSJason King langstr(sysdem_lang_t lang)
486a6cfa5dSJason King {
49d52aae23SJason King size_t i;
50d52aae23SJason King
51d52aae23SJason King for (i = 0; i < ARRAY_SIZE(lang_tbl); i++) {
52d52aae23SJason King if (lang == lang_tbl[i].lang)
53d52aae23SJason King return (lang_tbl[i].str);
54d52aae23SJason King }
556a6cfa5dSJason King return ("invalid");
566a6cfa5dSJason King }
57d52aae23SJason King
58d52aae23SJason King boolean_t
sysdem_parse_lang(const char * str,sysdem_lang_t * langp)59d52aae23SJason King sysdem_parse_lang(const char *str, sysdem_lang_t *langp)
60d52aae23SJason King {
61d52aae23SJason King size_t i;
62d52aae23SJason King
63d52aae23SJason King for (i = 0; i < ARRAY_SIZE(lang_tbl); i++) {
64d52aae23SJason King if (strcmp(str, lang_tbl[i].str) == 0) {
65d52aae23SJason King *langp = lang_tbl[i].lang;
66d52aae23SJason King return (B_TRUE);
67d52aae23SJason King }
68d52aae23SJason King }
69d52aae23SJason King
70d52aae23SJason King return (B_FALSE);
716a6cfa5dSJason King }
724226f635SJason King
73f5ac8590SJason King /*
74f5ac8590SJason King * A quick check if str can possibly be a mangled string. Currently, that
75f5ac8590SJason King * means it must start with _Z or __Z.
76f5ac8590SJason King */
77f5ac8590SJason King static boolean_t
is_mangled(const char * str,size_t n)78f5ac8590SJason King is_mangled(const char *str, size_t n)
794226f635SJason King {
80f5ac8590SJason King strview_t sv;
814226f635SJason King
82f5ac8590SJason King sv_init_str(&sv, str, str + n);
834226f635SJason King
84f5ac8590SJason King if (!sv_consume_if_c(&sv, '_'))
85f5ac8590SJason King return (B_FALSE);
86f5ac8590SJason King (void) sv_consume_if_c(&sv, '_');
87f5ac8590SJason King if (sv_consume_if_c(&sv, 'Z'))
88f5ac8590SJason King return (B_TRUE);
89*1cd08393SJason King if (sv_consume_if_c(&sv, 'R'))
90*1cd08393SJason King return (B_TRUE);
914226f635SJason King
92f5ac8590SJason King return (B_FALSE);
934226f635SJason King }
944226f635SJason King
954226f635SJason King static void
check_debug(void)964226f635SJason King check_debug(void)
974226f635SJason King {
984226f635SJason King if (getenv(DEMANGLE_DEBUG))
994226f635SJason King demangle_debug = B_TRUE;
1004226f635SJason King }
1014226f635SJason King
1024226f635SJason King char *
sysdemangle(const char * str,sysdem_lang_t lang,sysdem_ops_t * ops)1034226f635SJason King sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops)
1044226f635SJason King {
105f5ac8590SJason King char *res = NULL;
106*1cd08393SJason King
1076a6cfa5dSJason King /*
1086a6cfa5dSJason King * While the language specific demangler code can handle non-NUL
1096a6cfa5dSJason King * terminated strings, we currently don't expose this to consumers.
1106a6cfa5dSJason King * Consumers should still pass in a NUL-terminated string.
1116a6cfa5dSJason King */
1126a6cfa5dSJason King size_t slen;
1136a6cfa5dSJason King
1144226f635SJason King VERIFY0(pthread_once(&debug_once, check_debug));
1154226f635SJason King
1166a6cfa5dSJason King DEMDEBUG("name = '%s'", (str == NULL) ? "(NULL)" : str);
1176a6cfa5dSJason King DEMDEBUG("lang = %s (%d)", langstr(lang), lang);
1186a6cfa5dSJason King
1196a6cfa5dSJason King if (str == NULL) {
1206a6cfa5dSJason King errno = EINVAL;
1216a6cfa5dSJason King return (NULL);
1226a6cfa5dSJason King }
1236a6cfa5dSJason King
1246a6cfa5dSJason King slen = strlen(str);
1256a6cfa5dSJason King
1266a6cfa5dSJason King switch (lang) {
1276a6cfa5dSJason King case SYSDEM_LANG_AUTO:
1286a6cfa5dSJason King case SYSDEM_LANG_CPP:
1296a6cfa5dSJason King case SYSDEM_LANG_RUST:
1306a6cfa5dSJason King break;
1316a6cfa5dSJason King default:
1326a6cfa5dSJason King errno = EINVAL;
1336a6cfa5dSJason King return (NULL);
1346a6cfa5dSJason King }
1356a6cfa5dSJason King
1364226f635SJason King if (ops == NULL)
1374226f635SJason King ops = sysdem_ops_default;
1384226f635SJason King
139f5ac8590SJason King /*
140f5ac8590SJason King * If we were given an explicit language to demangle, we always
141f5ac8590SJason King * use that. If not, we try to demangle as rust, then c++. Any
142f5ac8590SJason King * mangled C++ symbol that manages to successfully demangle as a
143f5ac8590SJason King * legacy rust symbol _should_ look the same as it can really
144f5ac8590SJason King * only be a very simple C++ symbol. Otherwise, the rust demangling
145f5ac8590SJason King * should fail and we can try C++.
146f5ac8590SJason King */
1474226f635SJason King switch (lang) {
1484226f635SJason King case SYSDEM_LANG_CPP:
1496a6cfa5dSJason King return (cpp_demangle(str, slen, ops));
1506a6cfa5dSJason King case SYSDEM_LANG_RUST:
1516a6cfa5dSJason King return (rust_demangle(str, slen, ops));
1526a6cfa5dSJason King case SYSDEM_LANG_AUTO:
153f5ac8590SJason King break;
154f5ac8590SJason King }
155f5ac8590SJason King
1566a6cfa5dSJason King /*
157f5ac8590SJason King * To save us some potential work, if the symbol cannot
158f5ac8590SJason King * possibly be a rust or C++ mangled name, we don't
159f5ac8590SJason King * even attempt to demangle either.
1606a6cfa5dSJason King */
161f5ac8590SJason King if (!is_mangled(str, slen)) {
162f5ac8590SJason King /*
163f5ac8590SJason King * This does mean if we somehow get a string > 2GB
164f5ac8590SJason King * the debugging output will be truncated, but that
165f5ac8590SJason King * seems an acceptable tradeoff.
166f5ac8590SJason King */
167f5ac8590SJason King int len = slen > INT_MAX ? INT_MAX : slen;
168f5ac8590SJason King
169f5ac8590SJason King DEMDEBUG("ERROR: '%.*s' cannot be a mangled string", len, str);
1706a6cfa5dSJason King errno = EINVAL;
1716a6cfa5dSJason King return (NULL);
1726a6cfa5dSJason King }
173f5ac8590SJason King
174f5ac8590SJason King DEMDEBUG("trying rust");
175f5ac8590SJason King res = rust_demangle(str, slen, ops);
176f5ac8590SJason King
177f5ac8590SJason King IMPLY(ret != NULL, errno == 0);
178f5ac8590SJason King if (res != NULL)
179f5ac8590SJason King return (res);
180f5ac8590SJason King
181f5ac8590SJason King DEMDEBUG("trying C++");
182f5ac8590SJason King return (cpp_demangle(str, slen, ops));
1836a6cfa5dSJason King }
1846a6cfa5dSJason King
1856a6cfa5dSJason King int
demdebug(const char * fmt,...)1866a6cfa5dSJason King demdebug(const char *fmt, ...)
1876a6cfa5dSJason King {
1886a6cfa5dSJason King va_list ap;
1896a6cfa5dSJason King
1906a6cfa5dSJason King flockfile(debugf);
1916a6cfa5dSJason King (void) fprintf(debugf, "LIBDEMANGLE: ");
1926a6cfa5dSJason King va_start(ap, fmt);
1936a6cfa5dSJason King (void) vfprintf(debugf, fmt, ap);
1946a6cfa5dSJason King (void) fputc('\n', debugf);
1956a6cfa5dSJason King (void) fflush(debugf);
1966a6cfa5dSJason King va_end(ap);
1976a6cfa5dSJason King funlockfile(debugf);
1986a6cfa5dSJason King
1996a6cfa5dSJason King return (0);
2004226f635SJason King }
201