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 #include <mdb/mdb_modapi.h>
30 #include <mdb/mdb_demangle.h>
31 #include <mdb/mdb_err.h>
32 #include <mdb/mdb.h>
33 
34 #include <strings.h>
35 #include <unistd.h>
36 #include <dlfcn.h>
37 #include <link.h>
38 
39 static void *
40 mdb_dem_alloc(size_t len)
41 {
42 	return (mdb_alloc(len, UM_SLEEP));
43 }
44 
45 static void
46 mdb_dem_free(void *p, size_t len)
47 {
48 	mdb_free(p, len);
49 }
50 
51 static sysdem_ops_t mdb_dem_demops = {
52 	.alloc = mdb_dem_alloc,
53 	.free = mdb_dem_free
54 };
55 
56 mdb_demangler_t *
57 mdb_dem_load(void)
58 {
59 	mdb_demangler_t *dmp;
60 
61 	dmp = mdb_alloc(sizeof (mdb_demangler_t), UM_SLEEP);
62 	dmp->dm_len = 0;
63 	dmp->dm_buf = NULL;
64 	dmp->dm_flags = MDB_DM_SCOPE;
65 	/* stick with C++ for now to match old behavior */
66 	dmp->dm_lang = SYSDEM_LANG_CPP;
67 
68 	return (dmp);
69 }
70 
71 void
72 mdb_dem_unload(mdb_demangler_t *dmp)
73 {
74 	mdb_free(dmp->dm_buf, dmp->dm_len);
75 	mdb_free(dmp, sizeof (mdb_demangler_t));
76 }
77 
78 static const char *
79 mdb_dem_filter(mdb_demangler_t *dmp, const char *name)
80 {
81 	static const char s_pref[] = "static ";
82 	static const char c_suff[] = " const";
83 	static const char v_suff[] = " volatile";
84 
85 	/*
86 	 * We process dm_dem, which skips the prefix in dm_buf (if any)
87 	 */
88 	size_t len = strlen(dmp->dm_dem);
89 	char *end = dmp->dm_dem + len;
90 	size_t resid;
91 
92 	/*
93 	 * If static, const, and volatile qualifiers should not be displayed,
94 	 * rip all of them out of dmp->dm_dem.
95 	 */
96 	if (!(dmp->dm_flags & MDB_DM_QUAL)) {
97 		if (strncmp(dmp->dm_dem, s_pref, sizeof (s_pref) - 1) == 0) {
98 			bcopy(dmp->dm_dem + sizeof (s_pref) - 1, dmp->dm_dem,
99 			    len - (sizeof (s_pref) - 1) + 1);
100 			end -= sizeof (s_pref) - 1;
101 			len -= sizeof (s_pref) - 1;
102 		}
103 
104 		for (;;) {
105 			if (len > sizeof (c_suff) - 1 &&
106 			    strcmp(end - (sizeof (c_suff) - 1), c_suff) == 0) {
107 				end -= sizeof (c_suff) - 1;
108 				len -= sizeof (c_suff) - 1;
109 				*end = '\0';
110 				continue;
111 			}
112 			if (len > sizeof (v_suff) - 1 &&
113 			    strcmp(end - (sizeof (v_suff) - 1), v_suff) == 0) {
114 				end -= sizeof (v_suff) - 1;
115 				len -= sizeof (v_suff) - 1;
116 				*end = '\0';
117 				continue;
118 			}
119 			break;
120 		}
121 	}
122 
123 	/*
124 	 * If function arguments should not be displayed, remove everything
125 	 * between the outermost set of parentheses in dmp->dm_dem.
126 	 */
127 	if (!(dmp->dm_flags & MDB_DM_FUNCARG)) {
128 		char *lp = strchr(dmp->dm_dem, '(');
129 		char *rp = strrchr(dmp->dm_dem, ')');
130 
131 		if (lp != NULL && rp != NULL)
132 			bcopy(rp + 1, lp, strlen(rp) + 1);
133 	}
134 
135 	/*
136 	 * If function scope specifiers should not be displayed, remove text
137 	 * from the leftmost space to the rightmost colon prior to any paren.
138 	 */
139 	if (!(dmp->dm_flags & MDB_DM_SCOPE)) {
140 		char *c, *s, *lp = strchr(dmp->dm_dem, '(');
141 
142 		if (lp != NULL)
143 			*lp = '\0';
144 
145 		c = strrchr(dmp->dm_dem, ':');
146 		s = strchr(dmp->dm_dem, ' ');
147 
148 		if (lp != NULL)
149 			*lp = '(';
150 
151 		if (c != NULL) {
152 			if (s == NULL || s > c)
153 				bcopy(c + 1, dmp->dm_dem, strlen(c + 1) + 1);
154 			else
155 				bcopy(c + 1, s + 1, strlen(c + 1) + 1);
156 		}
157 	}
158 
159 	len = strlen(dmp->dm_dem); /* recompute length of buffer */
160 
161 	/*
162 	 * Compute bytes remaining
163 	 */
164 	resid = (dmp->dm_buf + dmp->dm_len) - (dmp->dm_dem + len);
165 
166 	/*
167 	 * If we want to append the mangled name as well and there is enough
168 	 * space for "[]\0" and at least one character, append "["+name+"]".
169 	 */
170 	if ((dmp->dm_flags & MDB_DM_MANGLED) && resid > 3) {
171 		char *p = dmp->dm_dem + len;
172 
173 		*p++ = '[';
174 		(void) strncpy(p, name, resid - 3);
175 		p[resid - 3] = '\0';
176 		p += strlen(p);
177 		(void) strcpy(p, "]");
178 	}
179 
180 	/*
181 	 * We return the whole string
182 	 */
183 	return (dmp->dm_buf);
184 }
185 
186 /*
187  * Take a name: (the foo`bar` is optional)
188  *	foo`bar`__mangled_
189  * and put:
190  *	foo`bar`demangled
191  * into dmp->dm_buf.  Point dmp->dm_dem to the beginning of the
192  * demangled section of the result.
193  */
194 static int
195 mdb_dem_process(mdb_demangler_t *dmp, const char *name)
196 {
197 	char *res = NULL;
198 	size_t reslen = 0;
199 
200 	char *prefix = strrchr(name, '`');
201 	size_t prefixlen = 0;
202 
203 	if (prefix) {
204 		prefix++;		/* the ` is part of the prefix */
205 		prefixlen = prefix - name;
206 	}
207 
208 	res = sysdemangle(name + prefixlen, dmp->dm_lang, &mdb_dem_demops);
209 	if (res == NULL) {
210 		if (errno != EINVAL)
211 			mdb_warn("Error while demangling");
212 		return (-1);
213 	}
214 
215 	reslen = (res != NULL) ? strlen(res) : 0;
216 	reslen += prefixlen;
217 	reslen += 1;
218 
219 	if (reslen > dmp->dm_len) {
220 		mdb_free(dmp->dm_buf, dmp->dm_len);
221 
222 		dmp->dm_buf = mdb_zalloc(reslen, UM_SLEEP);
223 		if (dmp->dm_buf == NULL) {
224 			dmp->dm_len = 0;
225 			mdb_warn("Unable to allocate memory for demangling");
226 			return (-1);
227 		}
228 		dmp->dm_len = reslen;
229 	}
230 
231 	if (prefixlen > 0)
232 		(void) strlcpy(dmp->dm_buf, name, prefixlen + 1);
233 	else
234 		*dmp->dm_buf = '\0';
235 
236 	if (res != NULL) {
237 		(void) strlcat(dmp->dm_buf, res, dmp->dm_len);
238 		mdb_dem_free(res, strlen(res) + 1);
239 	}
240 
241 	/*
242 	 * Save the position of the demangled string for mdb_dem_filter()
243 	 */
244 	dmp->dm_dem = dmp->dm_buf + prefixlen;
245 
246 	return (0);
247 }
248 
249 /* used by mdb_io.c:iob_addr2str */
250 const char *
251 mdb_dem_convert(mdb_demangler_t *dmp, const char *name)
252 {
253 	if (mdb_dem_process(dmp, name) != 0 ||
254 	    strcmp(dmp->dm_buf, name) == 0)
255 		return (name);
256 
257 	return (mdb_dem_filter(dmp, name));
258 }
259 
260 /*ARGSUSED*/
261 int
262 cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
263 {
264 	mdb_demangler_t *dmp = mdb.m_demangler;
265 
266 	if (argc > 0)
267 		return (DCMD_USAGE);
268 
269 	if (dmp != NULL && !(mdb.m_flags & MDB_FL_DEMANGLE)) {
270 		mdb_printf("C++ symbol demangling enabled\n");
271 		mdb.m_flags |= MDB_FL_DEMANGLE;
272 
273 	} else if (dmp == NULL) {
274 		if ((mdb.m_demangler = mdb_dem_load()) != NULL) {
275 			mdb_printf("C++ symbol demangling enabled\n");
276 			mdb.m_flags |= MDB_FL_DEMANGLE;
277 		} else {
278 			mdb_warn("no memory to load C++ demangler");
279 			mdb.m_flags &= ~MDB_FL_DEMANGLE;
280 		}
281 
282 	} else {
283 		mdb_dem_unload(mdb.m_demangler);
284 		mdb.m_flags &= ~MDB_FL_DEMANGLE;
285 		mdb.m_demangler = NULL;
286 		mdb_printf("C++ symbol demangling disabled\n");
287 	}
288 
289 	return (DCMD_OK);
290 }
291 
292 /*ARGSUSED*/
293 int
294 cmd_demflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
295 {
296 	static const char *const dm_desc[] = {
297 		"static/const/volatile member func qualifiers displayed",
298 		"scope resolution specifiers displayed",
299 		"function arguments displayed",
300 		"mangled name displayed"
301 	};
302 
303 	mdb_demangler_t *dmp = mdb.m_demangler;
304 	int i;
305 
306 	if (argc > 0)
307 		return (DCMD_USAGE);
308 
309 	if (dmp == NULL || !(mdb.m_flags & MDB_FL_DEMANGLE)) {
310 		mdb_warn("C++ demangling facility is currently disabled\n");
311 		return (DCMD_ERR);
312 	}
313 
314 	if (flags & DCMD_ADDRSPEC)
315 		dmp->dm_flags = ((uint_t)addr & MDB_DM_ALL);
316 
317 	for (i = 0; i < sizeof (dm_desc) / sizeof (dm_desc[0]); i++) {
318 		mdb_printf("0x%x\t%s\t%s\n", 1 << i,
319 		    (dmp->dm_flags & (1 << i)) ? "on" : "off", dm_desc[i]);
320 	}
321 
322 	return (DCMD_OK);
323 }
324 
325 /*ARGSUSED*/
326 int
327 cmd_demstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
328 {
329 	if ((flags & DCMD_ADDRSPEC) || argc == 0)
330 		return (DCMD_USAGE);
331 
332 	if (mdb.m_demangler == NULL && (mdb.m_demangler =
333 	    mdb_dem_load()) == NULL) {
334 		mdb_warn("failed to load demangler");
335 		return (DCMD_ERR);
336 	}
337 
338 	for (; argc != 0; argc--, argv++) {
339 		mdb_printf("%s == %s\n", argv->a_un.a_str,
340 		    mdb_dem_convert(mdb.m_demangler, argv->a_un.a_str));
341 	}
342 
343 	return (DCMD_OK);
344 }
345