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); 233 234 if (res != NULL) { 235 (void) strlcat(dmp->dm_buf, res, dmp->dm_len); 236 mdb_dem_free(res, strlen(res) + 1); 237 } 238 239 /* 240 * Save the position of the demangled string for mdb_dem_filter() 241 */ 242 dmp->dm_dem = dmp->dm_buf + prefixlen; 243 244 return (0); 245 } 246 247 /* used by mdb_io.c:iob_addr2str */ 248 const char * 249 mdb_dem_convert(mdb_demangler_t *dmp, const char *name) 250 { 251 if (mdb_dem_process(dmp, name) != 0 || 252 strcmp(dmp->dm_buf, name) == 0) 253 return (name); 254 255 return (mdb_dem_filter(dmp, name)); 256 } 257 258 /*ARGSUSED*/ 259 int 260 cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 261 { 262 mdb_demangler_t *dmp = mdb.m_demangler; 263 264 if (argc > 0) 265 return (DCMD_USAGE); 266 267 if (dmp != NULL && !(mdb.m_flags & MDB_FL_DEMANGLE)) { 268 mdb_printf("C++ symbol demangling enabled\n"); 269 mdb.m_flags |= MDB_FL_DEMANGLE; 270 271 } else if (dmp == NULL) { 272 if ((mdb.m_demangler = mdb_dem_load()) != NULL) { 273 mdb_printf("C++ symbol demangling enabled\n"); 274 mdb.m_flags |= MDB_FL_DEMANGLE; 275 } else { 276 mdb_warn("no memory to load C++ demangler"); 277 mdb.m_flags &= ~MDB_FL_DEMANGLE; 278 } 279 280 } else { 281 mdb_dem_unload(mdb.m_demangler); 282 mdb.m_flags &= ~MDB_FL_DEMANGLE; 283 mdb.m_demangler = NULL; 284 mdb_printf("C++ symbol demangling disabled\n"); 285 } 286 287 return (DCMD_OK); 288 } 289 290 /*ARGSUSED*/ 291 int 292 cmd_demflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 293 { 294 static const char *const dm_desc[] = { 295 "static/const/volatile member func qualifiers displayed", 296 "scope resolution specifiers displayed", 297 "function arguments displayed", 298 "mangled name displayed" 299 }; 300 301 mdb_demangler_t *dmp = mdb.m_demangler; 302 int i; 303 304 if (argc > 0) 305 return (DCMD_USAGE); 306 307 if (dmp == NULL || !(mdb.m_flags & MDB_FL_DEMANGLE)) { 308 mdb_warn("C++ demangling facility is currently disabled\n"); 309 return (DCMD_ERR); 310 } 311 312 if (flags & DCMD_ADDRSPEC) 313 dmp->dm_flags = ((uint_t)addr & MDB_DM_ALL); 314 315 for (i = 0; i < sizeof (dm_desc) / sizeof (dm_desc[0]); i++) { 316 mdb_printf("0x%x\t%s\t%s\n", 1 << i, 317 (dmp->dm_flags & (1 << i)) ? "on" : "off", dm_desc[i]); 318 } 319 320 return (DCMD_OK); 321 } 322 323 /*ARGSUSED*/ 324 int 325 cmd_demstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 326 { 327 if ((flags & DCMD_ADDRSPEC) || argc == 0) 328 return (DCMD_USAGE); 329 330 for (; argc != 0; argc--, argv++) { 331 mdb_printf("%s == %s\n", argv->a_un.a_str, 332 mdb_dem_convert(mdb.m_demangler, argv->a_un.a_str)); 333 } 334 335 return (DCMD_OK); 336 }