/* * gcc2 name demangler. * * gcc2 follows the C++ Annotated Reference Manual section 7.2.1 * name mangling description with a few changes. * See gpcompare.texi, gxxint_15.html in this directory for the changes. * * Not implemented: * unicode mangling * renaming of operator functions */ /* RULES TO ADD: _10CycleTimer.cycles_per_ms_ => CycleTimer::cycles_per_ms_ */ #include #include #include #include #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; } static Chartab typetab[] = { 'b', "bool", 'c', "char", 'd', "double", 'e', "...", 'f', "float", 'i', "int", 'J', "complex", 'l', "long", 'r', "long double", 's', "short", 'v', "void", 'w', "wchar_t", 'x', "long long", 0, 0 }; static Chartab modifiertab[] = { 'C', "const", 'S', "signed", /* means static for member functions */ 'U', "unsigned", 'V', "volatile", 'G', "garbage", /* no idea what this is */ 0, 0 }; static char constructor[] = "constructor"; static char destructor[] = "destructor"; static char gconstructor[] = "$gconstructor"; /* global destructor */ static char gdestructor[] = "$gdestructor"; /* global destructor */ static char manglestarts[] = "123456789CFHQSUVt"; static int gccname(char**, char**); static char *demanglegcc2a(char*, char*); static char *demanglegcc2b(char*, char*); static char *demanglegcc2c(char*, char*); static int gccnumber(char**, int*, int); char* demanglegcc2(char *s, char *buf) { char *name, *os, *p, *t; int isfn, namelen; /* * Pick off some cases that seem not to fit the pattern. */ if((t = demanglegcc2a(s, buf)) != nil) return t; if((t = demanglegcc2b(s, buf)) != nil) return t; if((t = demanglegcc2c(s, buf)) != nil) return t; /* * First, figure out whether this is a mangled name. * The name begins with a short version of the name, then __. * Of course, some C names begin with __ too, so the ultimate * test is whether what follows __ looks reasonable. * We use a test on the first letter instead. * * Constructors have no name - they begin __ (double underscore). * Destructors break the rule - they begin _._ (underscore, dot, underscore). */ os = s; isfn = 0; if(memcmp(s, "_._", 3) == 0){ isfn = 1; name = destructor; namelen = strlen(name); s += 3; }else if(memcmp(s, "_GLOBAL_.D.__", 13) == 0){ isfn = 1; name = gdestructor; namelen = strlen(name); s += 13; }else if(memcmp(s, "_GLOBAL_.D._", 12) == 0){ isfn = 0; name = gdestructor; namelen = strlen(name); s += 12; }else if(memcmp(s, "_GLOBAL_.I.__", 13) == 0){ isfn = 1; name = gconstructor; namelen = strlen(name); s += 13; }else if(memcmp(s, "_GLOBAL_.I._", 12) == 0){ isfn = 0; name = gconstructor; namelen = strlen(name); s += 12; }else{ t = strstr(os, "__"); if(t == nil) return os; do{ s = t; if(strchr(manglestarts, *(s+2))) break; }while((t = strstr(s+1, "__")) != nil); name = os; namelen = s - os; if(namelen == 0){ isfn = 1; name = constructor; namelen = strlen(name); } s += 2; } /* * Now s points at the mangled crap (maybe). * and name is the final element of the name. */ if(strchr(manglestarts, *s) == nil) return os; p = buf; if(*s == 'F'){ /* global function, no extra name pieces, just types */ isfn = 1; }else{ /* parse extra name pieces */ if(!gccname(&s, &p)){ if(debug) fprint(2, "parsename %s: %r\n", s); return os; } /* if we have a constructor or destructor, try to use the C++ name */ t = nil; if(name == constructor || name == destructor){ *p = 0; t = strrchr(buf, ':'); if(t) t++; else t = buf; } strcpy(p, "::"); p += 2; if(t){ namelen = strlen(t)-2; if(name == destructor) *p++ = '~'; name = t; } } if(p >= buf+2 && memcmp(p-2, "::", 2) == 0 && *(p-3) == ')') p -= 2; memmove(p, name, namelen); p += namelen; if(*s == 'F'){ /* might be from above, or might follow name pieces */ s++; isfn = 1; } /* the rest of the name is argument types - could skip this */ if(*s || isfn){ *p++ = '('; while(*s != 0 && *s != '_'){ if(!gccname(&s, &p)) break; *p++ = ','; } if(*(p-1) == ',') p--; *p++ = ')'; } if(*s == '_'){ /* return type (left over from H) */ } *p = 0; return buf; } /* * _10CycleTimer.cycles_per_ms_ => CycleTimer::cycles_per_ms_ * _t12basic_string3ZcZt11char_traits1ZcZt9allocator1Zc.npos * (maybe the funny syntax means they are private) */ static char* demanglegcc2a(char *s, char *buf) { char *p; if(*s != '_' || strchr(manglestarts, *(s+1)) == nil) return nil; p = buf; s++; if(!gccname(&s, &p)) return nil; if(*s != '.') return nil; s++; strcpy(p, "::"); p += 2; strcpy(p, s); return buf; } /* * _tfb => type info for bool * __vt_7ostream => vtbl for ostream */ static char* demanglegcc2b(char *s, char *buf) { char *p; char *t; if(memcmp(s, "__ti", 4) == 0){ t = "$typeinfo"; s += 4; }else if(memcmp(s, "__tf", 4) == 0){ t = "$typeinfofn"; s += 4; }else if(memcmp(s, "__vt_", 5) == 0){ t = "$vtbl"; s += 5; }else return nil; p = buf; for(;;){ if(*s == 0 || !gccname(&s, &p)) return nil; if(*s == 0) break; if(*s != '.' && *s != '$') return nil; strcpy(p, "::"); p += 2; s++; } strcpy(p, "::"); p += 2; strcpy(p, t); return buf; } /* * __thunk_176__._Q210LogMessage9LogStream => thunk (offset -176) for LogMessage::LogStream */ static char* demanglegcc2c(char *s, char *buf) { int n; char *p; if(memcmp(s, "__thunk_", 8) != 0) return nil; s += 8; if(!gccnumber(&s, &n, 1)) return nil; if(memcmp(s, "__._", 4) != 0) /* might as well be morse code */ return nil; s += 4; p = buf; if(!gccname(&s, &p)) return nil; strcpy(p, "::$thunk"); return buf; } /* * Parse a number, a non-empty run of digits. * If many==0, then only one digit is used, even * if it is followed by more. When we need a big * number in a one-digit slot, it gets bracketed by underscores. */ static int gccnumber(char **ps, int *pn, int many) { char *s; int n, eatunderscore; s = *ps; eatunderscore = 0; if(!many && *s == '_'){ many = 1; s++; eatunderscore = 1; } if(!isdigit((uchar)*s)){ bad: werrstr("bad number %.20s", *ps); return 0; } if(many) n = strtol(s, &s, 10); else n = *s++ - '0'; if(eatunderscore){ if(*s != '_') goto bad; 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) { int i, n, m, val; char *os, *s, *t, *p, *p0, *p1; s = *ps; os = s; p = *pp; /* print("\tgccname: %s\n", s); */ /* basic types */ if((t = chartabsearch(typetab, *s)) != nil){ s++; strcpy(p, t); p += strlen(t); goto out; } /* modifiers */ if((t = chartabsearch(modifiertab, *s)) != nil){ s++; if(!gccname(&s, &p)) return 0; /* * These don't end up in the right place * and i don't care anyway * (AssertHeld__C17ReaderWriterMutex) */ /* *p++ = ' '; strcpy(p, t); p += strlen(p); */ goto out; } switch(*s){ default: bad: if(debug) fprint(2, "gccname: %s (%s)\n", os, s); werrstr("bad name %.20s", s); return 0; case '1': case '2': case '3': case '4': /* length-prefixed string */ case '5': case '6': case '7': case '8': case '9': if(!gccnumber(&s, &n, 1)) return 0; memmove(p, s, n); p += n; s += n; break; case 'A': /* array */ t = s; s++; if(!gccnumber(&s, &n, 1)) return 0; if(*s != '_'){ werrstr("bad array %.20s", t); return 0; } s++; sprint(p, "array[%d] ", n); p += strlen(p); break; case 'F': /* function */ t = s; s++; strcpy(p, "fn("); p += 3; /* arguments */ while(*s && *s != '_') if(!gccname(&s, &p)) return 0; if(*s != '_'){ werrstr("unexpected end in function: %s", t); return 0; } s++; strcpy(p, " => "); p += 4; /* return type */ if(!gccname(&s, &p)) return 0; *p++ = ')'; break; case 'H': /* template specialization */ if(memcmp(s-2, "__", 2) != 0) fprint(2, "wow: %s\n", s-2); t = s; s++; if(!gccnumber(&s, &n, 0)) return 0; p0 = p; /* template arguments */ *p++ = '<'; for(i=0; i void SetFlag(FlagSettingMode *) * SetFlag__H1Zb_15FlagSettingMode_v * => void FlagSettingMode::SetFlag() * SetFlag__H1Zb_G15FlagSettingMode_v * => void SetFlag(FlagSettingMode) */ if(strchr("ACFGPRSUVX", *s)){ /* args */ t = s; p1 = p; *p++ = '('; while(*s != '_'){ if(*s == 0 || !gccname(&s, &p)){ werrstr("bad H args: %s", t); return 0; } } *p++ = ')'; s++; }else{ p1 = p; /* name */ if(!gccname(&s, &p)) return 0; } /* * Need to do some rearrangement of <> () and names here. * Doesn't matter since we strip out the <> and () anyway. */ break; case 'M': /* M1S: pointer to member */ if(*(s+1) != '1' || *(s+2) != 'S') goto bad; s += 3; strcpy(p, "mptr "); p += 5; if(!gccname(&s, &p)) return 0; break; case 'N': /* multiply-repeated type */ s++; if(!gccnumber(&s, &n, 0) || !gccnumber(&s, &m, 0)) return 0; sprint(p, "T%dx%d", m, n); p += strlen(p); break; case 'P': /* pointer */ s++; strcpy(p, "ptr "); p += 4; if(!gccname(&s, &p)) return 0; break; case 'Q': /* qualified name */ s++; if(!gccnumber(&s, &n, 0)) return 0; for(i=0; i