xref: /illumos-gate/usr/src/lib/libdemangle/common/demangle.c (revision 3b436d06bb95fd180ef7416b2b1b9972e2f2a513)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2018 Jason King
14  * Copyright 2019, Joyent, Inc.
15  */
16 
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <string.h>
20 #include <errno.h>
21 #include <pthread.h>
22 #include <sys/ctype.h>
23 #include <sys/debug.h>
24 #include <stdarg.h>
25 #include "demangle-sys.h"
26 #include "demangle_int.h"
27 
28 #define	DEMANGLE_DEBUG	"DEMANGLE_DEBUG"
29 
30 static pthread_once_t debug_once = PTHREAD_ONCE_INIT;
31 volatile boolean_t demangle_debug;
32 FILE *debugf = stderr;
33 
34 static const char *
35 langstr(sysdem_lang_t lang)
36 {
37 	switch (lang) {
38 	case SYSDEM_LANG_AUTO:
39 		return ("auto");
40 	case SYSDEM_LANG_CPP:
41 		return ("c++");
42 	case SYSDEM_LANG_RUST:
43 		return ("rust");
44 	default:
45 		return ("invalid");
46 	}
47 }
48 
49 static sysdem_lang_t
50 detect_lang(const char *str, size_t n)
51 {
52 	const char *p = str;
53 	size_t len;
54 
55 	if (n < 3 || str[0] != '_')
56 		return (SYSDEM_LANG_AUTO);
57 
58 	/*
59 	 * Check for ^_Z or ^__Z
60 	 */
61 	p = str + 1;
62 	if (*p == '_') {
63 		p++;
64 	}
65 
66 	if (*p != 'Z')
67 		return (SYSDEM_LANG_AUTO);
68 
69 	/*
70 	 * Sadly, rust currently uses the same prefix as C++, however
71 	 * demangling rust as a C++ mangled name yields less than desirable
72 	 * results.  However rust names end with a hash.  We use that to
73 	 * attempt to disambiguate
74 	 */
75 
76 	/* Find 'h'<hexdigit>+E$ */
77 	if ((p = strrchr(p, 'h')) == NULL)
78 		return (SYSDEM_LANG_CPP);
79 
80 	if ((len = strspn(p + 1, "0123456789abcdef")) == 0)
81 		return (SYSDEM_LANG_CPP);
82 
83 	p += len + 1;
84 
85 	if (p[0] != 'E' || p[1] != '\0')
86 		return (SYSDEM_LANG_CPP);
87 
88 	return (SYSDEM_LANG_RUST);
89 }
90 
91 static void
92 check_debug(void)
93 {
94 	if (getenv(DEMANGLE_DEBUG))
95 		demangle_debug = B_TRUE;
96 }
97 
98 char *
99 sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops)
100 {
101 	/*
102 	 * While the language specific demangler code can handle non-NUL
103 	 * terminated strings, we currently don't expose this to consumers.
104 	 * Consumers should still pass in a NUL-terminated string.
105 	 */
106 	size_t slen;
107 
108 	VERIFY0(pthread_once(&debug_once, check_debug));
109 
110 	DEMDEBUG("name = '%s'", (str == NULL) ? "(NULL)" : str);
111 	DEMDEBUG("lang = %s (%d)", langstr(lang), lang);
112 
113 	if (str == NULL) {
114 		errno = EINVAL;
115 		return (NULL);
116 	}
117 
118 	slen = strlen(str);
119 
120 	switch (lang) {
121 		case SYSDEM_LANG_AUTO:
122 		case SYSDEM_LANG_CPP:
123 		case SYSDEM_LANG_RUST:
124 			break;
125 		default:
126 			errno = EINVAL;
127 			return (NULL);
128 	}
129 
130 	if (ops == NULL)
131 		ops = sysdem_ops_default;
132 
133 	if (lang == SYSDEM_LANG_AUTO) {
134 		lang = detect_lang(str, slen);
135 		if (lang != SYSDEM_LANG_AUTO)
136 			DEMDEBUG("detected language is %s", langstr(lang));
137 	}
138 
139 	switch (lang) {
140 	case SYSDEM_LANG_CPP:
141 		return (cpp_demangle(str, slen, ops));
142 	case SYSDEM_LANG_RUST:
143 		return (rust_demangle(str, slen, ops));
144 	case SYSDEM_LANG_AUTO:
145 		DEMDEBUG("could not detect language");
146 		errno = ENOTSUP;
147 		return (NULL);
148 	default:
149 		/*
150 		 * This can't happen unless there's a bug with detect_lang,
151 		 * but gcc doesn't know that.
152 		 */
153 		errno = EINVAL;
154 		return (NULL);
155 	}
156 }
157 
158 int
159 demdebug(const char *fmt, ...)
160 {
161 	va_list ap;
162 
163 	flockfile(debugf);
164 	(void) fprintf(debugf, "LIBDEMANGLE: ");
165 	va_start(ap, fmt);
166 	(void) vfprintf(debugf, fmt, ap);
167 	(void) fputc('\n', debugf);
168 	(void) fflush(debugf);
169 	va_end(ap);
170 	funlockfile(debugf);
171 
172 	return (0);
173 }
174