blob: 035ffd357654a05f02561f5264eeb7915ff25373 [file] [log] [blame]
/*
* gcc2 name demangler.
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define debug 0
typedef struct Chartab Chartab;
struct Chartab
{
char c;
char *s;
};
static char*
chartabsearch(Chartab *ct, int c)
{
for(; ct->c; ct++)
if(ct->c == c)
return ct->s;
return nil;
}
typedef struct Gccstate Gccstate;
struct Gccstate
{
char *name[128];
int nname;
};
static int gccname(char**, char**, Gccstate*);
char*
demanglegcc2(char *s, char *buf)
{
char *p, *os, *name, *t;
int namelen;
Gccstate state;
state.nname = 0;
os = s;
p = buf;
if(memcmp(os, "_._", 3) == 0){
name = "destructor";
namelen = strlen(name);
s = os+3;
}else{
/* the mangled part begins with the final __ */
if((s = strstr(os, "__")) == nil)
return os;
do{
t = s;
if(strchr("123456789FHQt", s[2]))
break;
}while((s = strstr(t+1, "__")) != nil);
s = t;
name = os;
namelen = t - os;
if(namelen == 0){
name = "constructor";
namelen = strlen(name);
}
s += 2;
}
switch(*s){
default:
return os;
case 'F': /* plain function */
s++;
break;
case 'Q':
case 'H':
case 't':
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
if(!gccname(&s, &p, &state)){
if(debug) fprint(2, "bad name: %s\n", os);
return os;
}
strcpy(p, "::");
p += 2;
break;
}
memmove(p, name, namelen);
p += namelen;
if(*s && *s != '_'){
/* the rest of the name is the argument types */
*p++ = '(';
while(*s != 0 && *s != '_' && gccname(&s, &p, &state))
*p++ = ',';
if(*(p-1) == ',')
p--;
*p++ = ')';
}
if(*s == '_'){
/* the remainder is the type of the return value */
}
*p = 0;
return buf;
}
static Chartab typetab[] =
{
'b', "bool",
'c', "char",
'd', "double",
'i', "int",
'l', "long",
'v', "void",
0, 0
};
static int
gccnumber(char **ps, int *pn)
{
char *s;
int n;
s = *ps;
if(!isdigit((uchar)*s))
return 0;
n = strtol(s, &s, 10);
if(*s == '_')
s++;
*ps = s;
*pn = n;
return 1;
}
/*
* Pick apart the next mangled name section.
* Names and types are treated as the same.
* Let's see how far we can go before that becomes a problem.
*/
static int
gccname(char **ps, char **pp, Gccstate *state)
{
int i, n, m, val;
char c, *os, *s, *t, *p;
s = *ps;
os = s;
p = *pp;
/* print("\tgccname: %s\n", s); */
#if 0
/* overloaded operators */
for(i=0; operators[i].shrt; i++){
if(memcmp(operators[i].shrt, s, 2) == 0){
strcpy(p, "operator$");
strcat(p, operators[i].lng);
p += strlen(p);
s += 2;
goto suffix;
}
}
#endif
/* basic types */
if((t = chartabsearch(typetab, *s)) != nil){
s++;
strcpy(p, t);
p += strlen(t);
goto suffix;
}
switch(*s){
default:
bad:
if(debug) fprint(2, "gccname: %s (%s)\n", os, s);
return 0;
case '1': case '2': case '3': case '4': /* name length */
case '5': case '6': case '7': case '8': case '9':
n = strtol(s, &s, 10);
memmove(p, s, n);
p += n;
s += n;
break;
case 'C': /* const */
s++;
strcpy(p, "const ");
p += strlen(p);
if(!gccname(&s, &p, state))
return 0;
break;
case 'U': /* unsigned */
s++;
strcpy(p, "unsigned ");
p += strlen(p);
if(!gccname(&s, &p, state))
return 0;
break;
#if 0
case 'L': /* default value */
t = s;
s++;
if(!gccname(&s, &p, state))
return 0;
if(!isdigit((uchar)*s)){
fprint(2, "bad value: %s\n", t);
return 0;
}
n = strtol(s, &s, 10);
if(*s != 'E'){
fprint(2, "bad value2: %s\n", t);
return 0;
}
sprint(p, "=%d", n);
p += strlen(p);
s++;
break;
#endif
case 'N': /* repeated name/type */
case 'X':
c = *s++;
if(!isdigit((uchar)*s) || !isdigit((uchar)*(s+1)))
goto bad;
n = *s++ - '0';
m = *s++ - '0';
sprint(p, "%c%d/%d", c, n, m);
p += strlen(p);
break;
case 'Q': /* hierarchical name */
s++;
if(!isdigit((uchar)*s))
goto bad;
n = *s++ - '0';
for(i=0; i<n; i++){
if(!gccname(&s, &p, state)){
if(debug) fprint(2, "bad name in hierarchy: %s in %s\n", s, os);
return 0;
}
if(i+1 < n){
strcpy(p, "::");
p += 2;
}
}
break;
case 'P': /* pointer to */
s++;
if(!gccname(&s, &p, state))
return 0;
*p++ = '*';
break;
case 'R': /* reference to */
s++;
if(!gccname(&s, &p, state))
return 0;
*p++ = '&';
break;
case 'S': /* standard or previously-seen name */
s++;
if('0' <= *s && *s <= '9'){
/* previously seen */
t = s-1;
n = strtol(s, &s, 10);
if(*s != '_'){
fprint(2, "bad S: %s\n", t);
return 0;
}
s++;
sprint(p, "S%d_", n);
p += strlen(p);
break;
}
goto bad;
case 't': /* named template */
c = *s++;
if(!gccname(&s, &p, state))
return 0;
goto template;
case 'H': /* nameless template */
c = *s++;
template:
if(!gccnumber(&s, &n))
goto bad;
*p++ = '<';
for(i=0; i<n; i++){
val = 1;
if(*s == 'Z'){
val = 0;
s++;
}
if(!gccname(&s, &p, state))
goto bad;
if(val){
if(!gccnumber(&s, &m))
goto bad;
sprint(p, "=%d", m);
p += strlen(p);
}
if(i+1 < n)
*p++ = ',';
}
*p++ = '>';
if(c == 'H'){
if(*s != '_')
goto bad;
s++;
}
break;
case 'T': /* previously-seen type??? e.g., T2 */
t = s;
for(s++; isdigit((uchar)*s); s++)
;
memmove(p, t, s-t);
p += s-t;
break;
}
suffix:
*ps = s;
*pp = p;
return 1;
}