1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2001-2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright 2018 Jason King
27  */
28 
29 /*
30  * Copyright (c) 2019, Joyent, Inc.  All rights reserved.
31  */
32 
33 #include <mdb/mdb_modapi.h>
34 #include <mdb/mdb_demangle.h>
35 #include <mdb/mdb_err.h>
36 #include <mdb/mdb.h>
37 
38 #include <strings.h>
39 #include <unistd.h>
40 #include <dlfcn.h>
41 #include <link.h>
42 
43 static void *
mdb_dem_alloc(size_t len)44 mdb_dem_alloc(size_t len)
45 {
46 	return (mdb_alloc(len, UM_SLEEP));
47 }
48 
49 static void
mdb_dem_free(void * p,size_t len)50 mdb_dem_free(void *p, size_t len)
51 {
52 	mdb_free(p, len);
53 }
54 
55 static sysdem_ops_t mdb_dem_demops = {
56 	.alloc = mdb_dem_alloc,
57 	.free = mdb_dem_free
58 };
59 
60 mdb_demangler_t *
mdb_dem_load(void)61 mdb_dem_load(void)
62 {
63 	mdb_demangler_t *dmp;
64 
65 	dmp = mdb_alloc(sizeof (mdb_demangler_t), UM_SLEEP);
66 	dmp->dm_len = 0;
67 	dmp->dm_buf = NULL;
68 	dmp->dm_flags = MDB_DM_SCOPE;
69 	dmp->dm_lang = SYSDEM_LANG_AUTO;
70 
71 	return (dmp);
72 }
73 
74 void
mdb_dem_unload(mdb_demangler_t * dmp)75 mdb_dem_unload(mdb_demangler_t *dmp)
76 {
77 	mdb_free(dmp->dm_buf, dmp->dm_len);
78 	mdb_free(dmp, sizeof (mdb_demangler_t));
79 }
80 
81 static const char *
mdb_dem_filter(mdb_demangler_t * dmp,const char * name)82 mdb_dem_filter(mdb_demangler_t *dmp, const char *name)
83 {
84 	static const char s_pref[] = "static ";
85 	static const char c_suff[] = " const";
86 	static const char v_suff[] = " volatile";
87 
88 	/*
89 	 * We process dm_dem, which skips the prefix in dm_buf (if any)
90 	 */
91 	size_t len = strlen(dmp->dm_dem);
92 	char *end = dmp->dm_dem + len;
93 	size_t resid;
94 
95 	/*
96 	 * If static, const, and volatile qualifiers should not be displayed,
97 	 * rip all of them out of dmp->dm_dem.
98 	 */
99 	if (!(dmp->dm_flags & MDB_DM_QUAL)) {
100 		if (strncmp(dmp->dm_dem, s_pref, sizeof (s_pref) - 1) == 0) {
101 			bcopy(dmp->dm_dem + sizeof (s_pref) - 1, dmp->dm_dem,
102 			    len - (sizeof (s_pref) - 1) + 1);
103 			end -= sizeof (s_pref) - 1;
104 			len -= sizeof (s_pref) - 1;
105 		}
106 
107 		for (;;) {
108 			if (len > sizeof (c_suff) - 1 &&
109 			    strcmp(end - (sizeof (c_suff) - 1), c_suff) == 0) {
110 				end -= sizeof (c_suff) - 1;
111 				len -= sizeof (c_suff) - 1;
112 				*end = '\0';
113 				continue;
114 			}
115 			if (len > sizeof (v_suff) - 1 &&
116 			    strcmp(end - (sizeof (v_suff) - 1), v_suff) == 0) {
117 				end -= sizeof (v_suff) - 1;
118 				len -= sizeof (v_suff) - 1;
119 				*end = '\0';
120 				continue;
121 			}
122 			break;
123 		}
124 	}
125 
126 	/*
127 	 * If function arguments should not be displayed, remove everything
128 	 * between the outermost set of parentheses in dmp->dm_dem.
129 	 */
130 	if (!(dmp->dm_flags & MDB_DM_FUNCARG)) {
131 		char *lp = strchr(dmp->dm_dem, '(');
132 		char *rp = strrchr(dmp->dm_dem, ')');
133 
134 		if (lp != NULL && rp != NULL)
135 			bcopy(rp + 1, lp, strlen(rp) + 1);
136 	}
137 
138 	/*
139 	 * If function scope specifiers should not be displayed, remove text
140 	 * from the leftmost space to the rightmost colon prior to any paren.
141 	 */
142 	if (!(dmp->dm_flags & MDB_DM_SCOPE)) {
143 		char *c, *s, *lp = strchr(dmp->dm_dem, '(');
144 
145 		if (lp != NULL)
146 			*lp = '\0';
147 
148 		c = strrchr(dmp->dm_dem, ':');
149 		s = strchr(dmp->dm_dem, ' ');
150 
151 		if (lp != NULL)
152 			*lp = '(';
153 
154 		if (c != NULL) {
155 			if (s == NULL || s > c)
156 				bcopy(c + 1, dmp->dm_dem, strlen(c + 1) + 1);
157 			else
158 				bcopy(c + 1, s + 1, strlen(c + 1) + 1);
159 		}
160 	}
161 
162 	len = strlen(dmp->dm_dem); /* recompute length of buffer */
163 
164 	/*
165 	 * Compute bytes remaining
166 	 */
167 	resid = (dmp->dm_buf + dmp->dm_len) - (dmp->dm_dem + len);
168 
169 	/*
170 	 * If we want to append the mangled name as well and there is enough
171 	 * space for "[]\0" and at least one character, append "["+name+"]".
172 	 */
173 	if ((dmp->dm_flags & MDB_DM_MANGLED) && resid > 3) {
174 		char *p = dmp->dm_dem + len;
175 
176 		*p++ = '[';
177 		(void) strncpy(p, name, resid - 3);
178 		p[resid - 3] = '\0';
179 		p += strlen(p);
180 		(void) strcpy(p, "]");
181 	}
182 
183 	/*
184 	 * We return the whole string
185 	 */
186 	return (dmp->dm_buf);
187 }
188 
189 /*
190  * Take a name: (the foo`bar` is optional)
191  *	foo`bar`__mangled_
192  * and put:
193  *	foo`bar`demangled
194  * into dmp->dm_buf.  Point dmp->dm_dem to the beginning of the
195  * demangled section of the result.
196  */
197 static int
mdb_dem_process(mdb_demangler_t * dmp,const char * name)198 mdb_dem_process(mdb_demangler_t *dmp, const char *name)
199 {
200 	char *res = NULL;
201 	size_t reslen = 0;
202 
203 	char *prefix = strrchr(name, '`');
204 	size_t prefixlen = 0;
205 
206 	if (prefix) {
207 		prefix++;		/* the ` is part of the prefix */
208 		prefixlen = prefix - name;
209 	}
210 
211 	res = sysdemangle(name + prefixlen, dmp->dm_lang, &mdb_dem_demops);
212 	if (res == NULL) {
213 		/*
214 		 * EINVAL indicates the name is not a properly mangled name
215 		 * (or perhaps is truncated so it cannot be correctly
216 		 * demangled) while ENOTSUP means sysdemangle could not
217 		 * determine which language was used to mangle the name when
218 		 * SYSDEM_LANG_AUTO is used (the name might not be mangled,
219 		 * the name could be truncated enough to prevent determination
220 		 * of the name, etc).
221 		 *
222 		 * Both are allowed/expected failure modes, so in both cases
223 		 * do not emit a warning -- let the caller display the
224 		 * original name.
225 		 */
226 		if (errno != EINVAL && errno != ENOTSUP)
227 			mdb_warn("Error while demangling");
228 		return (-1);
229 	}
230 
231 	reslen = (res != NULL) ? strlen(res) : 0;
232 	reslen += prefixlen;
233 	reslen += 1;
234 
235 	if (reslen > dmp->dm_len) {
236 		mdb_free(dmp->dm_buf, dmp->dm_len);
237 
238 		dmp->dm_buf = mdb_zalloc(reslen, UM_SLEEP);
239 		if (dmp->dm_buf == NULL) {
240 			dmp->dm_len = 0;
241 			mdb_warn("Unable to allocate memory for demangling");
242 			return (-1);
243 		}
244 		dmp->dm_len = reslen;
245 	}
246 
247 	if (prefixlen > 0)
248 		(void) strlcpy(dmp->dm_buf, name, prefixlen + 1);
249 	else
250 		*dmp->dm_buf = '\0';
251 
252 	if (res != NULL) {
253 		(void) strlcat(dmp->dm_buf, res, dmp->dm_len);
254 		mdb_dem_free(res, strlen(res) + 1);
255 	}
256 
257 	/*
258 	 * Save the position of the demangled string for mdb_dem_filter()
259 	 */
260 	dmp->dm_dem = dmp->dm_buf + prefixlen;
261 
262 	return (0);
263 }
264 
265 /* used by mdb_io.c:iob_addr2str */
266 const char *
mdb_dem_convert(mdb_demangler_t * dmp,const char * name)267 mdb_dem_convert(mdb_demangler_t *dmp, const char *name)
268 {
269 	if (mdb_dem_process(dmp, name) != 0 ||
270 	    strcmp(dmp->dm_buf, name) == 0)
271 		return (name);
272 
273 	return (mdb_dem_filter(dmp, name));
274 }
275 
276 /*ARGSUSED*/
277 int
cmd_demangle(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)278 cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
279 {
280 	mdb_demangler_t *dmp = mdb.m_demangler;
281 
282 	if (argc > 0)
283 		return (DCMD_USAGE);
284 
285 	if (dmp != NULL && !(mdb.m_flags & MDB_FL_DEMANGLE)) {
286 		mdb_printf("C++ symbol demangling enabled\n");
287 		mdb.m_flags |= MDB_FL_DEMANGLE;
288 
289 	} else if (dmp == NULL) {
290 		if ((mdb.m_demangler = mdb_dem_load()) != NULL) {
291 			mdb_printf("C++ symbol demangling enabled\n");
292 			mdb.m_flags |= MDB_FL_DEMANGLE;
293 		} else {
294 			mdb_warn("no memory to load C++ demangler");
295 			mdb.m_flags &= ~MDB_FL_DEMANGLE;
296 		}
297 
298 	} else {
299 		mdb_dem_unload(mdb.m_demangler);
300 		mdb.m_flags &= ~MDB_FL_DEMANGLE;
301 		mdb.m_demangler = NULL;
302 		mdb_printf("C++ symbol demangling disabled\n");
303 	}
304 
305 	return (DCMD_OK);
306 }
307 
308 /*ARGSUSED*/
309 int
cmd_demflags(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)310 cmd_demflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
311 {
312 	static const char *const dm_desc[] = {
313 		"static/const/volatile member func qualifiers displayed",
314 		"scope resolution specifiers displayed",
315 		"function arguments displayed",
316 		"mangled name displayed"
317 	};
318 
319 	mdb_demangler_t *dmp = mdb.m_demangler;
320 	int i;
321 
322 	if (argc > 0)
323 		return (DCMD_USAGE);
324 
325 	if (dmp == NULL || !(mdb.m_flags & MDB_FL_DEMANGLE)) {
326 		mdb_warn("C++ demangling facility is currently disabled\n");
327 		return (DCMD_ERR);
328 	}
329 
330 	if (flags & DCMD_ADDRSPEC)
331 		dmp->dm_flags = ((uint_t)addr & MDB_DM_ALL);
332 
333 	for (i = 0; i < sizeof (dm_desc) / sizeof (dm_desc[0]); i++) {
334 		mdb_printf("0x%x\t%s\t%s\n", 1 << i,
335 		    (dmp->dm_flags & (1 << i)) ? "on" : "off", dm_desc[i]);
336 	}
337 
338 	return (DCMD_OK);
339 }
340 
341 /*ARGSUSED*/
342 int
cmd_demstr(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)343 cmd_demstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
344 {
345 	if ((flags & DCMD_ADDRSPEC) || argc == 0)
346 		return (DCMD_USAGE);
347 
348 	if (mdb.m_demangler == NULL && (mdb.m_demangler =
349 	    mdb_dem_load()) == NULL) {
350 		mdb_warn("failed to load demangler");
351 		return (DCMD_ERR);
352 	}
353 
354 	for (; argc != 0; argc--, argv++) {
355 		mdb_printf("%s == %s\n", argv->a_un.a_str,
356 		    mdb_dem_convert(mdb.m_demangler, argv->a_un.a_str));
357 	}
358 
359 	return (DCMD_OK);
360 }
361