diff options
Diffstat (limited to 'usr/src/lib/libdemangle/common/demangle.c')
| -rw-r--r-- | usr/src/lib/libdemangle/common/demangle.c | 130 | 
1 files changed, 108 insertions, 22 deletions
| diff --git a/usr/src/lib/libdemangle/common/demangle.c b/usr/src/lib/libdemangle/common/demangle.c index e827fd8cec..4f8e9ad678 100644 --- a/usr/src/lib/libdemangle/common/demangle.c +++ b/usr/src/lib/libdemangle/common/demangle.c @@ -11,13 +11,17 @@  /*   * Copyright 2018 Jason King + * Copyright 2019, Joyent, Inc.   */  #include <stdlib.h> +#include <stdio.h>  #include <string.h>  #include <errno.h>  #include <pthread.h> +#include <sys/ctype.h>  #include <sys/debug.h> +#include <stdarg.h>  #include "demangle-sys.h"  #include "demangle_int.h" @@ -25,31 +29,63 @@  static pthread_once_t debug_once = PTHREAD_ONCE_INIT;  volatile boolean_t demangle_debug; +FILE *debugf = stderr; + +static const char * +langstr(sysdem_lang_t lang) +{ +	switch (lang) { +	case SYSDEM_LANG_AUTO: +		return ("auto"); +	case SYSDEM_LANG_CPP: +		return ("c++"); +	case SYSDEM_LANG_RUST: +		return ("rust"); +	default: +		return ("invalid"); +	} +}  static sysdem_lang_t -detect_lang(const char *str) +detect_lang(const char *str, size_t n)  { -	size_t n = strlen(str); +	const char *p = str; +	size_t len;  	if (n < 3 || str[0] != '_')  		return (SYSDEM_LANG_AUTO); -	switch (str[1]) { -	case 'Z': +	/* +	 * Check for ^_Z or ^__Z +	 */ +	p = str + 1; +	if (*p == '_') { +		p++; +	} + +	if (*p != 'Z') +		return (SYSDEM_LANG_AUTO); + +	/* +	 * Sadly, rust currently uses the same prefix as C++, however +	 * demangling rust as a C++ mangled name yields less than desirable +	 * results.  However rust names end with a hash.  We use that to +	 * attempt to disambiguate +	 */ + +	/* Find 'h'<hexdigit>+E$ */ +	if ((p = strrchr(p, 'h')) == NULL)  		return (SYSDEM_LANG_CPP); -	case '_': -		break; +	if ((len = strspn(p + 1, "0123456789abcdef")) == 0) +		return (SYSDEM_LANG_CPP); -	default: -		return (SYSDEM_LANG_AUTO); -	} +	p += len + 1; -	/* why they use ___Z sometimes is puzzling... *sigh* */ -	if (str[2] == '_' && str[3] == 'Z') +	if (p[0] != 'E' || p[1] != '\0')  		return (SYSDEM_LANG_CPP); -	return (SYSDEM_LANG_AUTO); +	return (SYSDEM_LANG_RUST);  }  static void @@ -62,26 +98,76 @@ check_debug(void)  char *  sysdemangle(const char *str, sysdem_lang_t lang, sysdem_ops_t *ops)  { +	/* +	 * While the language specific demangler code can handle non-NUL +	 * terminated strings, we currently don't expose this to consumers. +	 * Consumers should still pass in a NUL-terminated string. +	 */ +	size_t slen; +  	VERIFY0(pthread_once(&debug_once, check_debug)); +	DEMDEBUG("name = '%s'", (str == NULL) ? "(NULL)" : str); +	DEMDEBUG("lang = %s (%d)", langstr(lang), lang); + +	if (str == NULL) { +		errno = EINVAL; +		return (NULL); +	} + +	slen = strlen(str); + +	switch (lang) { +		case SYSDEM_LANG_AUTO: +		case SYSDEM_LANG_CPP: +		case SYSDEM_LANG_RUST: +			break; +		default: +			errno = EINVAL; +			return (NULL); +	} +  	if (ops == NULL)  		ops = sysdem_ops_default;  	if (lang == SYSDEM_LANG_AUTO) { -		lang = detect_lang(str); -		if (lang == SYSDEM_LANG_AUTO) { -			errno = ENOTSUP; -			return (NULL); -		} +		lang = detect_lang(str, slen); +		if (lang != SYSDEM_LANG_AUTO) +			DEMDEBUG("detected language is %s", langstr(lang));  	}  	switch (lang) { -	case SYSDEM_LANG_AUTO: -		break;  	case SYSDEM_LANG_CPP: -		return (cpp_demangle(str, ops)); +		return (cpp_demangle(str, slen, ops)); +	case SYSDEM_LANG_RUST: +		return (rust_demangle(str, slen, ops)); +	case SYSDEM_LANG_AUTO: +		DEMDEBUG("could not detect language"); +		errno = ENOTSUP; +		return (NULL); +	default: +		/* +		 * This can't happen unless there's a bug with detect_lang, +		 * but gcc doesn't know that. +		 */ +		errno = EINVAL; +		return (NULL);  	} +} -	errno = ENOTSUP; -	return (NULL); +int +demdebug(const char *fmt, ...) +{ +	va_list ap; + +	flockfile(debugf); +	(void) fprintf(debugf, "LIBDEMANGLE: "); +	va_start(ap, fmt); +	(void) vfprintf(debugf, fmt, ap); +	(void) fputc('\n', debugf); +	(void) fflush(debugf); +	va_end(ap); +	funlockfile(debugf); + +	return (0);  } | 
