File contents
/*ident "@(#)aoutdem:demangle.c 3.1" */
/******************************************************************************
*
* C++ Standard Components, Release 3.0.
*
* Copyright (c) 1991, 1992 AT&T and Unix System Laboratories, Inc.
* Copyright (c) 1988, 1989, 1990 AT&T. All Rights Reserved.
*
* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T and Unix System
* Laboratories, Inc. The copyright notice above does not evidence
* any actual or intended publication of such source code.
*
******************************************************************************/
#include "aoutdem.h"
#include <signal.h>
#include <assert.h>
#include <Map.h>
//Mapdeclare(String,String)
//Mapimplement(String,String)
/* globals
*/
int verbose = 0;
int veryVerbose = 0;
int verboseDbx = 0;
int doFunctions = 1;
int doOperators = 1;
int omitAllSigs = 0;
int omitFirstInstanceSigs = 0;
int overwriteAout = 1;
char *qualifierToken = "::";
int debug = 0;
int prepareDebug = 0;
char *lparQual = "_lp_"; // for (
char *rparQual = "_rp_"; // for )
char *labQual = "_la_"; // for <
char *rabQual = "_ra_"; // for >
char *lsbQual = "_ls_"; // for [
char *rsbQual = "_rs_"; // for ]
char *starQual = "_st_"; // for *
char *ampQual = "_am_"; // for &
char *comQual = "_cm_"; // for ,
char *spaQual = "_sp_"; // for space
char *tilda = "_dt_"; // for tilda
char *plus = "_pl_"; // for +
char *equal = "_eq_"; // for =
char *minus = "_mi_"; // for -
char *percent = "_pc_"; // for %
char *bn = "_bn_"; // for !
char *orr = "_or_"; // for |
char *divide = "_di_"; // for /
char *er = "_er_"; // for ^
static FILE
*sym, // a.out being demangled
*newSng, // temporary file containing the new string table (minus its first four bytes)
*newSym; // temporary file containing the new symbol table
static long newSngLoc; // ftell(newSng) + 4
static char *symb, *newSngb, *newSymb; // internal buffers for the files
static int bufsize = 6*BUFSIZ; // default buffer size
static char *obj_n; // name of a.out being demangled
static char *newSng_n;
static char *newSym_n;
static char *stringTable; // in-core, of a.out being demangled
static void postProcess(int isDataMember, const String &mangled, String &buildingResult, const char *d);
static inline int min(int i, long j)
{
return ((long)i < j) ? i : (int)j;
}
FILE *xfopen(char *file, char *mode)
{
FILE *f;
if ((f=fopen(file, mode)) == NULL)
{
cerr << "demangle: fatal error: can't open temp file " << file << endl;
perror("demangle");
exit(-1);
}
return f;
}
int xfwrite(const void *buf, int size, int nitems, FILE *f)
{
int i;
if ((i=fwrite((char*)buf, size, nitems, f)) < nitems)
{
cerr << "demangle: fatal error: write failure" << endl;
perror("demangle");
exit(-1);
}
return i;
}
int xfread(void *buf, int size, int nitems, FILE *f)
{
int i;
if ((i=fread((char*)buf, size, nitems, f)) < nitems)
{
cerr << "demangle: fatal error: read failure" << endl;
perror("demangle");
exit(-1);
}
return i;
}
#if !defined(SYSV) && !defined(V9)
void xsetbuf(FILE *f, char *buf)
{
setbuffer(f, buf, bufsize);
}
#else
void xsetbuf(FILE *, char *)
{
}
#endif
static void getopts(int argc, char *argv[])
{
extern char *optarg;
int errflg = 0;
int c;
while ((c=getopt(argc, argv, "b:dfnoq:stvwx")) != EOF)
{
switch (c)
{
int size;
case 'b':
size = atoi(optarg);
if (size < 1 || ((size << 10) < 1024))
cerr << "demangle: illegal buffer size (ignored)" << endl;
else
bufsize = size << 10;
break;
case 'd':
prepareDebug = 1;
qualifierToken = "_cc_"; //yes, fall through
case 't':
omitFirstInstanceSigs = 1;
break;
case 'f':
doFunctions = 0;
break;
case 'n':
overwriteAout = 0;
break;
case 'o':
doOperators = 0;
break;
case 's':
omitAllSigs = 1;
break;
case 'q':
qualifierToken = optarg;
break;
case 'v':
verbose = 1;
break;
case 'w':
veryVerbose = verbose = 1;
break;
case 'x':
verboseDbx = 1;
break;
case '?':
errflg++;
break;
}
}
if (errflg)
{
cerr << "usage: demangle [-ndfostvx] [-qqualstring] file ..." << endl;
exit(2);
}
}
/* overwrite file from onto to, starting at current location of to.
*/
void overwrite(FILE *from, FILE *to)
{
fseek(from, 0, 0);
char *c = new char[bufsize];
int i;
while ((i=fread(c, sizeof(char), bufsize, from)) > 0)
xfwrite(c, sizeof(char), i, to);
delete c;
}
static void overwriteOldTables(const HDR *x)
{
// write the new symbol table to the original a.out,
// beginning at the beginning of the original symbol table
fseek(sym, N_SYMOFF(*x), 0);
overwrite(newSym, sym);
// write the new string table to the original a.out,
// beginning right after the new symbol table
long len = ftell(newSng) + 4;
xfwrite(&len, 4, 1, sym);
overwrite(newSng, sym);
// rem: truncate, but probably doesn't need it.
}
static void cleanup(int)
{
//cerr << "cleaning up!" << endl;
signal(SIGINT, (SIG_TYP)SIG_IGN);
unlink(newSng_n);
unlink(newSym_n);
exit(1);
_exit(1);
}
static void fatalError(char *message)
{
cerr << "demangle: fatal error: " << obj_n << ": " << message << endl;
exit(-1);
}
/* returns the new string for n
*/
static String buildNewString(SYM *n)
{
String ss;
#ifdef SYSV
if (n->n_zeroes != 0L) // it's in n->n_name
{
char s[SYMNMLEN+1];
strncpy(s, n->n_name, SYMNMLEN);
s[SYMNMLEN] = 0;
ss = s;
}
else
#endif
if (N_STRINDEX(*n) > 0) // if it's in string table
{
#if defined(SUNOS) || defined(BSD)
ss = parseDbxString(&stringTable[N_STRINDEX(*n)], n->n_type);
// ss is now demangled and simplified
#else
ss = &stringTable[N_STRINDEX(*n)];
// ss is not yet demangled and simplified
#endif
}
#ifdef SYSV
ss = demangleAndSimplify(ss, (n->n_sclass == C_MOS || n->n_sclass == C_MOU), n->n_sclass);
#endif
#ifdef V9
ss = demangleAndSimplify(ss, (n->n_type == N_SSYM || n->n_type == N_SFLD), n->n_type);
#endif
return ss;
}
/* process a single symbol table entry.
* (notice that all non-SYSV strings are in the string table.)
*/
static void transformSymEntry(SYM *n)
{
String s = buildNewString(n);
#ifdef SYSV
n->n_zeroes = 0;
#endif
N_STRINDEX(*n) = 0;
if (s.length() > 0)
{
const char *t = (const char*)s;
#ifdef SYSV
if (s.length() <= SYMNMLEN)
strncpy(n->n_name, t, SYMNMLEN);
else
#endif
{
N_STRINDEX(*n) = newSngLoc; // fix n's index
newSngLoc += xfwrite(t, sizeof(char), s.length()+1, newSng); // write the new string
}
}
}
ostream &operator<<(ostream &oo, const SYM &
#ifdef SYSV
n
#endif
)
{
#ifdef SYSV
char s[SYMNMLEN+1];
s[SYMNMLEN] = 0;
if (n.n_zeroes != 0L) // it's in n->n_name
{
strncpy(s, n.n_name, SYMNMLEN);
oo << "(" << s << ") ";
}
oo << (int)N_AUX(n);
#endif
return oo;
}
/* build the new symbol and string tables
*/
static void buildNewTables(const HDR *x, long nsyms)
{
long remaining = nsyms; // total number of symbol table entries remaining to process
#if FAST_NONPORTABLE_STRUCT_ARRAY_READ
const int bunch = 1024; // number of symbol table entries to process at a time
#else
const int bunch = 1;
#endif
char *syms = new char[bunch*SYMSZ]; // buffer of old symbol table entries
fseek(sym, N_SYMOFF(*x), 0); // go to old symbol table
// fill the buffer
int numInBuffer = xfread(syms, SYMSZ, min(bunch, remaining), sym);
int symsi = 0; // index in buffer
char *thisSym = syms; // pointer to entry symsi in buffer
int auxEntries = 0; // number of aux entries still to skip over
while (remaining)
{
if (auxEntries) // skip over them
{
if (auxEntries < 0)
fatalError("Bad symbol table");
int skip = min(numInBuffer-symsi, auxEntries);
if (remaining < skip)
fatalError("Bad symbol table (missing auxiliary entries)");
auxEntries -= skip;
remaining -= skip;
symsi += skip;
thisSym += skip*SYMSZ;
}
else
{
transformSymEntry((SYM*)thisSym);
auxEntries = N_AUX(*(SYM*)thisSym);
remaining--;
symsi++;
thisSym += SYMSZ;
}
assert (symsi <= bunch);
if (symsi == bunch) // write and then refill the buffer
{
xfwrite(syms, SYMSZ, numInBuffer, newSym);
numInBuffer = xfread(syms, SYMSZ, min(bunch, remaining), sym);
symsi = 0;
thisSym = syms;
}
}
xfwrite(syms, SYMSZ, numInBuffer, newSym);
delete syms;
}
static void readStringTable(unsigned long offset)
{
// Read string table size
unsigned long ssize;
fseek(sym, offset, 0);
xfread(&ssize, sizeof(ssize), 1, sym);
if (ssize > MAXINT)
fatalError("String table is too big -- sorry.");
stringTable = new char[ssize+1];
fseek(sym, offset, 0);
xfread(stringTable, sizeof(char), (int)ssize, sym);
stringTable[ssize] = NULL;
}
static void demangleAout(const HDR *x, long nsyms)
{
newSym=xfopen(newSym_n, "w+");
xsetbuf(newSym, newSymb);
newSng=xfopen(newSng_n, "w+");
xsetbuf(newSng, newSngb);
newSngLoc = 4;
//cerr << "created temps!" << endl;
//sleep(5);
readStringTable(N_STROFF(*x));
buildNewTables(x, nsyms);
if (overwriteAout)
overwriteOldTables(x);
delete stringTable;
fclose(newSym);
fclose(newSng);
unlink(newSym_n);
unlink(newSng_n);
}
static void tryToDemangleAout(char *file)
{
obj_n = file;
if ((sym=fopen(obj_n, "r+")) == NULL)
cerr << "demangle: can't open " << obj_n << endl;
else
{
xsetbuf(sym, symb);
HDR x;
fread((char*)&x, HDRSZ, 1, sym);
#if 1
if (N_BADMAG(x))
#else
// on system v, used to get the magic number in the
// auxiliary unix header following the file header
aouthdr y;
fread((char*)&y, sizeof(aouthdr), 1, sym);
if (N_BADMAG(y))
#endif
{
cerr << "demangle: " << obj_n << " not an object file (magic is 0" << oct << MAGIC(x) << dec << ")" << endl;
}
else
{
long nsyms = N_SYMS(x);
if (nsyms > 0) // else it's a stripped object, and there's nothing to do.
demangleAout(&x, nsyms);
}
fclose(sym);
}
}
main(int argc, char *argv[])
{
newSng_n = mktemp("sngXXXXXX");
newSym_n = mktemp("symXXXXXX");
signal(SIGINT, (SIG_TYP)cleanup);
getopts(argc, argv);
symb = new char[bufsize];
newSymb = new char[bufsize];
newSngb = new char[bufsize];
if (optind == argc)
tryToDemangleAout("a.out");
else
{
for (; optind < argc; optind++)
tryToDemangleAout(argv[optind]);
}
delete symb;
delete newSymb;
delete newSngb;
return 0;
}
/* Returns true just if sig is the signature of the first encountered
* instance of the function with selector sel. That is, future calls with the same
* sel but a different sig will return false; future calls with the same sel and
* the same sig will return true.
*/
static Map<String,String> signature;
static int firstInstance(String &sel, const String &sig)
{
//cerr << "firstInstance(" << sel << ", " << sig << ") --";
if (signature.element(sel)) // if this selector's been seen
{
return (signature[sel] == sig);
}
else
{
signature[sel] = sig;
return 1;
}
}
static void beVerbose(String mangled, String demangled, int entryType, int isDataMember)
{
if (verbose)
{
cerr << mangled << " => " << demangled;
if (veryVerbose)
cerr << " (" << (void*)entryType << ", " << isDataMember << ")";
cerr << endl;
}
}
//There are a few char(s) eligible of being part of function name in C++
// but not in C. Replace these char(s) with strings
void translate( String& s )
{
String buildingResult;
const char *p = s;
int len = s.length();
for (int i = 0; i < len; i++)
{
switch(*p) {
case ':':
assert(p[1] == ':');
buildingResult += qualifierToken;
p += 2;
i++;
break;
case '(':
buildingResult += lparQual;
p += 1;
break;
case ')':
buildingResult += rparQual;
p += 1;
break;
case '<':
buildingResult += labQual;
p += 1;
break;
case '>':
buildingResult += rabQual;
p += 1;
break;
case '[':
buildingResult += lsbQual;
p += 1;
break;
case ']':
buildingResult += rsbQual;
p += 1;
break;
case '*':
buildingResult += starQual;
p += 1;
break;
case '&':
buildingResult += ampQual;
p += 1;
break;
case ',':
buildingResult += comQual;
p += 1;
break;
case ' ':
buildingResult += spaQual;
p += 1;
break;
case '~':
buildingResult += tilda;
p += 1;
break;
case '+':
buildingResult += plus;
p += 1;
break;
case '=':
buildingResult += equal;
p += 1;
break;
case '-':
buildingResult += minus;
p += 1;
break;
case '%':
buildingResult += percent;
p += 1;
break;
case '!':
buildingResult += bn;
p += 1;
break;
case '|':
buildingResult += orr;
p += 1;
break;
case '/':
buildingResult += divide;
p += 1;
break;
case '^':
buildingResult += er;
p += 1;
break;
default:
buildingResult += *p++;
break;
}
}
s = buildingResult;
return;
}
String demangleAndSimplify(const String & mangled, int isDataMember, int entryType)
{
String buildingResult;
const char *m = mangled;
//int mlen = mangled.length();
String saveMangled = mangled; // dem may modify the string
//const char *d = demangle_withlen(m, mlen);
char buf[MAXDBUF];
char sbuf[MAXDBUF];
DEM dm;
int putbackUnder = 0;
if (*m == '_' && hasextra_(entryType) && !isDataMember)
{
putbackUnder = 1;
}
if( dem((char*)m, &dm, sbuf) < 0 || //Error
dm.type == DEM_PTBL ||
dm.type == DEM_STI ||
dm.type == DEM_STD )
{
buildingResult = saveMangled;
beVerbose(saveMangled, saveMangled, entryType, isDataMember);
return buildingResult;
}
dem_print( &dm, buf );
postProcess(isDataMember, saveMangled, buildingResult, buf);
//postProcess may decide to use the mangled name even if the name is
// demangleable, for instance, after-first instance of overloading
// function name using -d option
if( ( buildingResult != saveMangled ) && ( putbackUnder == 1 ) )
buildingResult = "_" + buildingResult;
// convert the non C function name char in function name to a
// C string
if( prepareDebug )
translate( buildingResult );
beVerbose(saveMangled, buildingResult, entryType, isDataMember);
return buildingResult;
}
static int isOperator(const char *d, const char *colon)
{
if (strncmp(d, "operator", 8) == 0)
{
if (!isalnum(d[8]))
return 1;
}
if (colon != 0 && strncmp(colon+1, "operator", 8) == 0)
{
if (!isalnum(colon[9]))
return 1;
}
return 0;
}
// append the string p to buildingResult, changing qualifier characters along the way
static void appendReplacingQualifierTokens(String & buildingResult, const char *p)
{
// as an optimization, first append everything up to the first colon
const char *colon = strchr(p, ':');
if (colon == 0)
{
buildingResult += p;
}
else
{
buildingResult.append(p, colon - p);
for (p = colon; *p != '\0'; )
{
if (*p == ':')
{
assert(p[1] == ':');
buildingResult += qualifierToken;
p += 2;
}
else
{
buildingResult += *p++;
}
}
}
}
static void appendSelectorAndSignature(String & buildingResult, const char *selector)
{
// have to replace qualifiers in signature types
appendReplacingQualifierTokens(buildingResult, selector);
}
static void dropFirstInstanceSignature(
// const String &mangled,
String &buildingResult,
const char *d,
const char *selector,
const char *lp)
{
// have to do the lookup based on the qualified selector
String qualifiedSelector(d, lp - d);
String signature = lp;
if (firstInstance(qualifiedSelector, signature))
buildingResult.append(selector, lp - selector);
//else if (successiveSigsMangled)
// buildingResult = mangled;
else
appendSelectorAndSignature(buildingResult, selector);
}
static void postProcessFunction(
//const String &mangled,
String &buildingResult,
const char *d,
const char *selector,
const char *lp)
{
if (omitAllSigs)
buildingResult.append(selector, lp - selector);
else if (omitFirstInstanceSigs)
dropFirstInstanceSignature(buildingResult, d, selector, lp);
else
appendSelectorAndSignature(buildingResult, selector);
}
// [d..colon] delimits the *entire* qualifier
static void postProcessQualifier(int isDataMember, String & buildingResult, const char *d, const char *colon)
{
assert(colon[-1] == ':');
if (!isDataMember) // drop qualifier on data members, it's redundant
{
((char*)colon)[-1] = '\0';
appendReplacingQualifierTokens(buildingResult, d);
((char*)colon)[-1] = ':';
buildingResult += qualifierToken;
}
}
static const char *rightmost_colon_between(const char *d, const char *lp)
{
if (lp == 0)
{
return strrchr(d, ':');
}
else
{
char rem = *lp;
*(char*)lp = '\0';
const char *ret = strrchr(d, ':');
*(char*)lp = rem;
return ret;
}
}
static void postProcess(int isDataMember, const String &mangled, String &buildingResult, const char *d)
{
const char *lp = strchr(d, '(');
const char *colon = rightmost_colon_between(d, lp);
if (lp != 0 && (!doFunctions || (!doOperators && isOperator(d, colon))))
{
buildingResult = mangled;
return;
}
if (colon != 0)
{
assert(colon[-1] == ':' && colon[1] != '\0');
postProcessQualifier(isDataMember, buildingResult, d, colon);
}
const char *afterQualifier = (colon == 0)? d : colon + 1;
if (lp != 0)
{
postProcessFunction(buildingResult, d, afterQualifier, lp);
}
else
{
buildingResult += afterQualifier;
}
}