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 * 44 mdb_dem_alloc(size_t len) 45 { 46 return (mdb_alloc(len, UM_SLEEP)); 47 } 48 49 static void 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 * 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 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 * 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 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 * 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 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 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 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