// SPDX-License-Identifier: GPL-2.0 #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "symbol.h" #include "demangle-java.h" #include <linux/ctype.h> #include <linux/kernel.h> enum { MODE_PREFIX = 0, MODE_CLASS = 1, MODE_FUNC = 2, MODE_TYPE = 3, MODE_CTYPE = 4, /* class arg */ }; #define BASE_ENT(c, n) [c - 'A']=n static const char *base_types['Z' - 'A' + 1] = { BASE_ENT('B', "byte" ), BASE_ENT('C', "char" ), BASE_ENT('D', "double" ), BASE_ENT('F', "float" ), BASE_ENT('I', "int" ), BASE_ENT('J', "long" ), BASE_ENT('S', "short" ), BASE_ENT('Z', "boolean" ), }; /* * demangle Java symbol between str and end positions and stores * up to maxlen characters into buf. The parser starts in mode. * * Use MODE_PREFIX to process entire prototype till end position * Use MODE_TYPE to process return type if str starts on return type char * * Return: * success: buf * error : NULL */ static char * __demangle_java_sym(const char *str, const char *end, char *buf, int maxlen, int mode) { int rlen = 0; int array = 0; int narg = 0; const char *q; if (!end) end = str + strlen(str); for (q = str; q != end; q++) { if (rlen == (maxlen - 1)) break; switch (*q) { case 'L': if (mode == MODE_PREFIX || mode == MODE_TYPE) { if (mode == MODE_TYPE) { if (narg) rlen += scnprintf(buf + rlen, maxlen - rlen, ", "); narg++; } if (mode == MODE_PREFIX) mode = MODE_CLASS; else mode = MODE_CTYPE; } else buf[rlen++] = *q; break; case 'B': case 'C': case 'D': case 'F': case 'I': case 'J': case 'S': case 'Z': if (mode == MODE_TYPE) { if (narg) rlen += scnprintf(buf + rlen, maxlen - rlen, ", "); rlen += scnprintf(buf + rlen, maxlen - rlen, "%s", base_types[*q - 'A']); while (array--) rlen += scnprintf(buf + rlen, maxlen - rlen, "[]"); array = 0; narg++; } else buf[rlen++] = *q; break; case 'V': if (mode == MODE_TYPE) { rlen += scnprintf(buf + rlen, maxlen - rlen, "void"); while (array--) rlen += scnprintf(buf + rlen, maxlen - rlen, "[]"); array = 0; } else buf[rlen++] = *q; break; case '[': if (mode != MODE_TYPE) goto error; array++; break; case '(': if (mode != MODE_FUNC) goto error; buf[rlen++] = *q; mode = MODE_TYPE; break; case ')': if (mode != MODE_TYPE) goto error; buf[rlen++] = *q; narg = 0; break; case ';': if (mode != MODE_CLASS && mode != MODE_CTYPE) goto error; /* safe because at least one other char to process */ if (isalpha(*(q + 1)) && mode == MODE_CLASS) rlen += scnprintf(buf + rlen, maxlen - rlen, "."); if (mode == MODE_CLASS) mode = MODE_FUNC; else if (mode == MODE_CTYPE) mode = MODE_TYPE; break; case '/': if (mode != MODE_CLASS && mode != MODE_CTYPE) goto error; rlen += scnprintf(buf + rlen, maxlen - rlen, "."); break; default : buf[rlen++] = *q; } } buf[rlen] = '\0'; return buf; error: return NULL; } /* * Demangle Java function signature (openJDK, not GCJ) * input: * str: string to parse. String is not modified * flags: combination of JAVA_DEMANGLE_* flags to modify demangling * return: * if input can be demangled, then a newly allocated string is returned. * if input cannot be demangled, then NULL is returned * * Note: caller is responsible for freeing demangled string */ char * java_demangle_sym(const char *str, int flags) { char *buf, *ptr; char *p; size_t len, l1 = 0; if (!str) return NULL; /* find start of return type */ p = strrchr(str, ')'); if (!p) return NULL; /* * expansion factor estimated to 3x */ len = strlen(str) * 3 + 1; buf = malloc(len); if (!buf) return NULL; buf[0] = '\0'; if (!(flags & JAVA_DEMANGLE_NORET)) { /* * get return type first */ ptr = __demangle_java_sym(p + 1, NULL, buf, len, MODE_TYPE); if (!ptr) goto error; /* add space between return type and function prototype */ l1 = strlen(buf); buf[l1++] = ' '; } /* process function up to return type */ ptr = __demangle_java_sym(str, p + 1, buf + l1, len - l1, MODE_PREFIX); if (!ptr) goto error; return buf; error: free(buf); return NULL; }