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