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 }