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 }