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