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