xref: /illumos-gate/usr/src/lib/libdemangle/common/demangle.c (revision f5ac85908213ce2217329b835bf3c91f1c04b793)
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 /*
13*f5ac8590SJason King  * Copyright 2021 Jason King
146a6cfa5dSJason 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>
21*f5ac8590SJason 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"
29*f5ac8590SJason 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 *
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
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 
73*f5ac8590SJason King /*
74*f5ac8590SJason King  * A quick check if str can possibly be a mangled string. Currently, that
75*f5ac8590SJason King  * means it must start with _Z or __Z.
76*f5ac8590SJason King  */
77*f5ac8590SJason King static boolean_t
78*f5ac8590SJason King is_mangled(const char *str, size_t n)
794226f635SJason King {
80*f5ac8590SJason King 	strview_t sv;
814226f635SJason King 
82*f5ac8590SJason King 	sv_init_str(&sv, str, str + n);
834226f635SJason King 
84*f5ac8590SJason King 	if (!sv_consume_if_c(&sv, '_'))
85*f5ac8590SJason King 		return (B_FALSE);
86*f5ac8590SJason King 	(void) sv_consume_if_c(&sv, '_');
87*f5ac8590SJason King 	if (sv_consume_if_c(&sv, 'Z'))
88*f5ac8590SJason King 		return (B_TRUE);
894226f635SJason King 
90*f5ac8590SJason King 	return (B_FALSE);
914226f635SJason King }
924226f635SJason King 
934226f635SJason King static void
944226f635SJason King check_debug(void)
954226f635SJason King {
964226f635SJason King 	if (getenv(DEMANGLE_DEBUG))
974226f635SJason King 		demangle_debug = B_TRUE;
984226f635SJason King }
994226f635SJason King 
1004226f635SJason King char *
1014226f635SJason King sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops)
1024226f635SJason King {
103*f5ac8590SJason King 	char *res = NULL;
1046a6cfa5dSJason King 	/*
1056a6cfa5dSJason King 	 * While the language specific demangler code can handle non-NUL
1066a6cfa5dSJason King 	 * terminated strings, we currently don't expose this to consumers.
1076a6cfa5dSJason King 	 * Consumers should still pass in a NUL-terminated string.
1086a6cfa5dSJason King 	 */
1096a6cfa5dSJason King 	size_t slen;
1106a6cfa5dSJason King 
1114226f635SJason King 	VERIFY0(pthread_once(&debug_once, check_debug));
1124226f635SJason King 
1136a6cfa5dSJason King 	DEMDEBUG("name = '%s'", (str == NULL) ? "(NULL)" : str);
1146a6cfa5dSJason King 	DEMDEBUG("lang = %s (%d)", langstr(lang), lang);
1156a6cfa5dSJason King 
1166a6cfa5dSJason King 	if (str == NULL) {
1176a6cfa5dSJason King 		errno = EINVAL;
1186a6cfa5dSJason King 		return (NULL);
1196a6cfa5dSJason King 	}
1206a6cfa5dSJason King 
1216a6cfa5dSJason King 	slen = strlen(str);
1226a6cfa5dSJason King 
1236a6cfa5dSJason King 	switch (lang) {
1246a6cfa5dSJason King 		case SYSDEM_LANG_AUTO:
1256a6cfa5dSJason King 		case SYSDEM_LANG_CPP:
1266a6cfa5dSJason King 		case SYSDEM_LANG_RUST:
1276a6cfa5dSJason King 			break;
1286a6cfa5dSJason King 		default:
1296a6cfa5dSJason King 			errno = EINVAL;
1306a6cfa5dSJason King 			return (NULL);
1316a6cfa5dSJason King 	}
1326a6cfa5dSJason King 
1334226f635SJason King 	if (ops == NULL)
1344226f635SJason King 		ops = sysdem_ops_default;
1354226f635SJason King 
136*f5ac8590SJason King 	/*
137*f5ac8590SJason King 	 * If we were given an explicit language to demangle, we always
138*f5ac8590SJason King 	 * use that. If not, we try to demangle as rust, then c++. Any
139*f5ac8590SJason King 	 * mangled C++ symbol that manages to successfully demangle as a
140*f5ac8590SJason King 	 * legacy rust symbol _should_ look the same as it can really
141*f5ac8590SJason King 	 * only be a very simple C++ symbol. Otherwise, the rust demangling
142*f5ac8590SJason King 	 * should fail and we can try C++.
143*f5ac8590SJason King 	 */
1444226f635SJason King 	switch (lang) {
1454226f635SJason King 	case SYSDEM_LANG_CPP:
1466a6cfa5dSJason King 		return (cpp_demangle(str, slen, ops));
1476a6cfa5dSJason King 	case SYSDEM_LANG_RUST:
1486a6cfa5dSJason King 		return (rust_demangle(str, slen, ops));
1496a6cfa5dSJason King 	case SYSDEM_LANG_AUTO:
150*f5ac8590SJason King 		break;
151*f5ac8590SJason King 	}
152*f5ac8590SJason King 
1536a6cfa5dSJason King 	/*
154*f5ac8590SJason King 	 * To save us some potential work, if the symbol cannot
155*f5ac8590SJason King 	 * possibly be a rust or C++ mangled name, we don't
156*f5ac8590SJason King 	 * even attempt to demangle either.
1576a6cfa5dSJason King 	 */
158*f5ac8590SJason King 	if (!is_mangled(str, slen)) {
159*f5ac8590SJason King 		/*
160*f5ac8590SJason King 		 * This does mean if we somehow get a string > 2GB
161*f5ac8590SJason King 		 * the debugging output will be truncated, but that
162*f5ac8590SJason King 		 * seems an acceptable tradeoff.
163*f5ac8590SJason King 		 */
164*f5ac8590SJason King 		int len = slen > INT_MAX ? INT_MAX : slen;
165*f5ac8590SJason King 
166*f5ac8590SJason King 		DEMDEBUG("ERROR: '%.*s' cannot be a mangled string", len, str);
1676a6cfa5dSJason King 		errno = EINVAL;
1686a6cfa5dSJason King 		return (NULL);
1696a6cfa5dSJason King 	}
170*f5ac8590SJason King 
171*f5ac8590SJason King 	DEMDEBUG("trying rust");
172*f5ac8590SJason King 	res = rust_demangle(str, slen, ops);
173*f5ac8590SJason King 
174*f5ac8590SJason King 	IMPLY(ret != NULL, errno == 0);
175*f5ac8590SJason King 	if (res != NULL)
176*f5ac8590SJason King 		return (res);
177*f5ac8590SJason King 
178*f5ac8590SJason King 	DEMDEBUG("trying C++");
179*f5ac8590SJason King 	return (cpp_demangle(str, slen, ops));
1806a6cfa5dSJason King }
1816a6cfa5dSJason King 
1826a6cfa5dSJason King int
1836a6cfa5dSJason King demdebug(const char *fmt, ...)
1846a6cfa5dSJason King {
1856a6cfa5dSJason King 	va_list ap;
1866a6cfa5dSJason King 
1876a6cfa5dSJason King 	flockfile(debugf);
1886a6cfa5dSJason King 	(void) fprintf(debugf, "LIBDEMANGLE: ");
1896a6cfa5dSJason King 	va_start(ap, fmt);
1906a6cfa5dSJason King 	(void) vfprintf(debugf, fmt, ap);
1916a6cfa5dSJason King 	(void) fputc('\n', debugf);
1926a6cfa5dSJason King 	(void) fflush(debugf);
1936a6cfa5dSJason King 	va_end(ap);
1946a6cfa5dSJason King 	funlockfile(debugf);
1956a6cfa5dSJason King 
1966a6cfa5dSJason King 	return (0);
1974226f635SJason King }
198