| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License, Version 1.0 only |
| * (the "License"). You may not use this file except in compliance |
| * with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| * |
| * Copyright 2018 Jason King |
| */ |
| |
| #include <mdb/mdb_modapi.h> |
| #include <mdb/mdb_demangle.h> |
| #include <mdb/mdb_err.h> |
| #include <mdb/mdb.h> |
| |
| #include <strings.h> |
| #include <unistd.h> |
| #include <dlfcn.h> |
| #include <link.h> |
| |
| static void * |
| mdb_dem_alloc(size_t len) |
| { |
| return (mdb_alloc(len, UM_SLEEP)); |
| } |
| |
| static void |
| mdb_dem_free(void *p, size_t len) |
| { |
| mdb_free(p, len); |
| } |
| |
| static sysdem_ops_t mdb_dem_demops = { |
| .alloc = mdb_dem_alloc, |
| .free = mdb_dem_free |
| }; |
| |
| mdb_demangler_t * |
| mdb_dem_load(void) |
| { |
| mdb_demangler_t *dmp; |
| |
| dmp = mdb_alloc(sizeof (mdb_demangler_t), UM_SLEEP); |
| dmp->dm_len = 0; |
| dmp->dm_buf = NULL; |
| dmp->dm_flags = MDB_DM_SCOPE; |
| /* stick with C++ for now to match old behavior */ |
| dmp->dm_lang = SYSDEM_LANG_CPP; |
| |
| return (dmp); |
| } |
| |
| void |
| mdb_dem_unload(mdb_demangler_t *dmp) |
| { |
| mdb_free(dmp->dm_buf, dmp->dm_len); |
| mdb_free(dmp, sizeof (mdb_demangler_t)); |
| } |
| |
| static const char * |
| mdb_dem_filter(mdb_demangler_t *dmp, const char *name) |
| { |
| static const char s_pref[] = "static "; |
| static const char c_suff[] = " const"; |
| static const char v_suff[] = " volatile"; |
| |
| /* |
| * We process dm_dem, which skips the prefix in dm_buf (if any) |
| */ |
| size_t len = strlen(dmp->dm_dem); |
| char *end = dmp->dm_dem + len; |
| size_t resid; |
| |
| /* |
| * If static, const, and volatile qualifiers should not be displayed, |
| * rip all of them out of dmp->dm_dem. |
| */ |
| if (!(dmp->dm_flags & MDB_DM_QUAL)) { |
| if (strncmp(dmp->dm_dem, s_pref, sizeof (s_pref) - 1) == 0) { |
| bcopy(dmp->dm_dem + sizeof (s_pref) - 1, dmp->dm_dem, |
| len - (sizeof (s_pref) - 1) + 1); |
| end -= sizeof (s_pref) - 1; |
| len -= sizeof (s_pref) - 1; |
| } |
| |
| for (;;) { |
| if (len > sizeof (c_suff) - 1 && |
| strcmp(end - (sizeof (c_suff) - 1), c_suff) == 0) { |
| end -= sizeof (c_suff) - 1; |
| len -= sizeof (c_suff) - 1; |
| *end = '\0'; |
| continue; |
| } |
| if (len > sizeof (v_suff) - 1 && |
| strcmp(end - (sizeof (v_suff) - 1), v_suff) == 0) { |
| end -= sizeof (v_suff) - 1; |
| len -= sizeof (v_suff) - 1; |
| *end = '\0'; |
| continue; |
| } |
| break; |
| } |
| } |
| |
| /* |
| * If function arguments should not be displayed, remove everything |
| * between the outermost set of parentheses in dmp->dm_dem. |
| */ |
| if (!(dmp->dm_flags & MDB_DM_FUNCARG)) { |
| char *lp = strchr(dmp->dm_dem, '('); |
| char *rp = strrchr(dmp->dm_dem, ')'); |
| |
| if (lp != NULL && rp != NULL) |
| bcopy(rp + 1, lp, strlen(rp) + 1); |
| } |
| |
| /* |
| * If function scope specifiers should not be displayed, remove text |
| * from the leftmost space to the rightmost colon prior to any paren. |
| */ |
| if (!(dmp->dm_flags & MDB_DM_SCOPE)) { |
| char *c, *s, *lp = strchr(dmp->dm_dem, '('); |
| |
| if (lp != NULL) |
| *lp = '\0'; |
| |
| c = strrchr(dmp->dm_dem, ':'); |
| s = strchr(dmp->dm_dem, ' '); |
| |
| if (lp != NULL) |
| *lp = '('; |
| |
| if (c != NULL) { |
| if (s == NULL || s > c) |
| bcopy(c + 1, dmp->dm_dem, strlen(c + 1) + 1); |
| else |
| bcopy(c + 1, s + 1, strlen(c + 1) + 1); |
| } |
| } |
| |
| len = strlen(dmp->dm_dem); /* recompute length of buffer */ |
| |
| /* |
| * Compute bytes remaining |
| */ |
| resid = (dmp->dm_buf + dmp->dm_len) - (dmp->dm_dem + len); |
| |
| /* |
| * If we want to append the mangled name as well and there is enough |
| * space for "[]\0" and at least one character, append "["+name+"]". |
| */ |
| if ((dmp->dm_flags & MDB_DM_MANGLED) && resid > 3) { |
| char *p = dmp->dm_dem + len; |
| |
| *p++ = '['; |
| (void) strncpy(p, name, resid - 3); |
| p[resid - 3] = '\0'; |
| p += strlen(p); |
| (void) strcpy(p, "]"); |
| } |
| |
| /* |
| * We return the whole string |
| */ |
| return (dmp->dm_buf); |
| } |
| |
| /* |
| * Take a name: (the foo`bar` is optional) |
| * foo`bar`__mangled_ |
| * and put: |
| * foo`bar`demangled |
| * into dmp->dm_buf. Point dmp->dm_dem to the beginning of the |
| * demangled section of the result. |
| */ |
| static int |
| mdb_dem_process(mdb_demangler_t *dmp, const char *name) |
| { |
| char *res = NULL; |
| size_t reslen = 0; |
| |
| char *prefix = strrchr(name, '`'); |
| size_t prefixlen = 0; |
| |
| if (prefix) { |
| prefix++; /* the ` is part of the prefix */ |
| prefixlen = prefix - name; |
| } |
| |
| res = sysdemangle(name + prefixlen, dmp->dm_lang, &mdb_dem_demops); |
| if (res == NULL) { |
| if (errno != EINVAL) |
| mdb_warn("Error while demangling"); |
| return (-1); |
| } |
| |
| reslen = (res != NULL) ? strlen(res) : 0; |
| reslen += prefixlen; |
| reslen += 1; |
| |
| if (reslen > dmp->dm_len) { |
| mdb_free(dmp->dm_buf, dmp->dm_len); |
| |
| dmp->dm_buf = mdb_zalloc(reslen, UM_SLEEP); |
| if (dmp->dm_buf == NULL) { |
| dmp->dm_len = 0; |
| mdb_warn("Unable to allocate memory for demangling"); |
| return (-1); |
| } |
| dmp->dm_len = reslen; |
| } |
| |
| if (prefixlen > 0) |
| (void) strlcpy(dmp->dm_buf, name, prefixlen + 1); |
| else |
| *dmp->dm_buf = '\0'; |
| |
| if (res != NULL) { |
| (void) strlcat(dmp->dm_buf, res, dmp->dm_len); |
| mdb_dem_free(res, strlen(res) + 1); |
| } |
| |
| /* |
| * Save the position of the demangled string for mdb_dem_filter() |
| */ |
| dmp->dm_dem = dmp->dm_buf + prefixlen; |
| |
| return (0); |
| } |
| |
| /* used by mdb_io.c:iob_addr2str */ |
| const char * |
| mdb_dem_convert(mdb_demangler_t *dmp, const char *name) |
| { |
| if (mdb_dem_process(dmp, name) != 0 || |
| strcmp(dmp->dm_buf, name) == 0) |
| return (name); |
| |
| return (mdb_dem_filter(dmp, name)); |
| } |
| |
| /*ARGSUSED*/ |
| int |
| cmd_demangle(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| mdb_demangler_t *dmp = mdb.m_demangler; |
| |
| if (argc > 0) |
| return (DCMD_USAGE); |
| |
| if (dmp != NULL && !(mdb.m_flags & MDB_FL_DEMANGLE)) { |
| mdb_printf("C++ symbol demangling enabled\n"); |
| mdb.m_flags |= MDB_FL_DEMANGLE; |
| |
| } else if (dmp == NULL) { |
| if ((mdb.m_demangler = mdb_dem_load()) != NULL) { |
| mdb_printf("C++ symbol demangling enabled\n"); |
| mdb.m_flags |= MDB_FL_DEMANGLE; |
| } else { |
| mdb_warn("no memory to load C++ demangler"); |
| mdb.m_flags &= ~MDB_FL_DEMANGLE; |
| } |
| |
| } else { |
| mdb_dem_unload(mdb.m_demangler); |
| mdb.m_flags &= ~MDB_FL_DEMANGLE; |
| mdb.m_demangler = NULL; |
| mdb_printf("C++ symbol demangling disabled\n"); |
| } |
| |
| return (DCMD_OK); |
| } |
| |
| /*ARGSUSED*/ |
| int |
| cmd_demflags(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| static const char *const dm_desc[] = { |
| "static/const/volatile member func qualifiers displayed", |
| "scope resolution specifiers displayed", |
| "function arguments displayed", |
| "mangled name displayed" |
| }; |
| |
| mdb_demangler_t *dmp = mdb.m_demangler; |
| int i; |
| |
| if (argc > 0) |
| return (DCMD_USAGE); |
| |
| if (dmp == NULL || !(mdb.m_flags & MDB_FL_DEMANGLE)) { |
| mdb_warn("C++ demangling facility is currently disabled\n"); |
| return (DCMD_ERR); |
| } |
| |
| if (flags & DCMD_ADDRSPEC) |
| dmp->dm_flags = ((uint_t)addr & MDB_DM_ALL); |
| |
| for (i = 0; i < sizeof (dm_desc) / sizeof (dm_desc[0]); i++) { |
| mdb_printf("0x%x\t%s\t%s\n", 1 << i, |
| (dmp->dm_flags & (1 << i)) ? "on" : "off", dm_desc[i]); |
| } |
| |
| return (DCMD_OK); |
| } |
| |
| /*ARGSUSED*/ |
| int |
| cmd_demstr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) |
| { |
| if ((flags & DCMD_ADDRSPEC) || argc == 0) |
| return (DCMD_USAGE); |
| |
| if (mdb.m_demangler == NULL && (mdb.m_demangler = |
| mdb_dem_load()) == NULL) { |
| mdb_warn("failed to load demangler"); |
| return (DCMD_ERR); |
| } |
| |
| for (; argc != 0; argc--, argv++) { |
| mdb_printf("%s == %s\n", argv->a_un.a_str, |
| mdb_dem_convert(mdb.m_demangler, argv->a_un.a_str)); |
| } |
| |
| return (DCMD_OK); |
| } |