| #include <u.h> |
| #include <errno.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include <mach.h> |
| #include <stabs.h> |
| #include <ctype.h> |
| #include "dat.h" |
| |
| static jmp_buf kaboom; |
| |
| static Type *parsename(char*, char**); |
| static Type *parseinfo(char*, char**); |
| static int parsenum(char*, int*, int*, char**); |
| static int parseattr(char*, char**, char**); |
| static Type *parsedefn(char *p, Type *t, char **pp); |
| static int parsebound(char**); |
| static vlong parsebigint(char**); |
| |
| typedef struct Ftypes Ftypes; |
| struct Ftypes |
| { |
| Ftypes *down; |
| Ftypes *next; |
| char *file; |
| TypeList *list; |
| }; |
| |
| Ftypes *fstack; |
| Ftypes *allftypes; |
| |
| static char* |
| estrndup(char *s, int n) |
| { |
| char *t; |
| |
| t = emalloc(n+1); |
| memmove(t, s, n); |
| return t; |
| } |
| |
| static char* |
| mkpath(char *dir, char *name) |
| { |
| char *s; |
| if(name[0] == '/') |
| return estrdup(name); |
| else{ |
| s = emalloc(strlen(dir)+strlen(name)+1); |
| strcpy(s, dir); |
| strcat(s, name); |
| return s; |
| } |
| } |
| |
| static Ftypes* |
| mkftypes(char *dir, char *name) |
| { |
| Ftypes *f; |
| |
| f = emalloc(sizeof(*f)); |
| f->file = mkpath(dir, name); |
| f->next = allftypes; |
| allftypes = f; |
| return f; |
| } |
| |
| static Ftypes* |
| findftypes(char *dir, char *name) |
| { |
| char *s; |
| Ftypes *f, *found; |
| |
| found = nil; |
| s = mkpath(dir, name); |
| for(f=allftypes; f; f=f->next) |
| if(strcmp(f->file, s) == 0) |
| found = f; |
| return found; |
| } |
| |
| static void |
| oops(void) |
| { |
| longjmp(kaboom, 1); |
| } |
| |
| /* find a : but skip over :: */ |
| static char* |
| findcolon(char *p) |
| { |
| while((p = strchr(p, ':')) != nil && *(p+1) == ':') |
| p += 2; |
| if(p == nil) |
| oops(); |
| return p; |
| } |
| |
| static void |
| semi(char **p) |
| { |
| if(**p != ';') |
| oops(); |
| (*p)++; |
| } |
| |
| static void |
| comma(char **p) |
| { |
| if(**p != ',') |
| oops(); |
| (*p)++; |
| } |
| |
| static int |
| parseint(char **pp) |
| { |
| if(!isdigit(**pp)) |
| oops(); |
| return strtol(*pp, pp, 10); |
| } |
| |
| /* |
| name ::= symbol_opt info |
| */ |
| static Type* |
| parsename(char *desc, char **pp) |
| { |
| if(*desc == 'c') |
| return nil; |
| |
| if(isdigit(*desc) || *desc=='-' || *desc=='(') |
| return parseinfo(desc, pp); |
| if(*desc == 0) |
| oops(); |
| return parseinfo(desc+1, pp); |
| } |
| |
| /* |
| info ::= num | num '=' attr* defn |
| */ |
| static Type* |
| parseinfo(char *desc, char **pp) |
| { |
| int n1, n2; |
| Type *t; |
| char *attr; |
| |
| parsenum(desc, &n1, &n2, &desc); |
| t = typebynum(n1, n2); |
| if(*desc != '='){ |
| *pp = desc; |
| return t; |
| } |
| desc++; |
| if(fstack) |
| fstack->list = mktl(t, fstack->list); |
| while(parseattr(desc, &attr, &desc) >= 0){ |
| if(*attr == 's') |
| t->xsizeof = atoi(attr+1)/8; |
| } |
| return parsedefn(desc, t, pp); |
| } |
| |
| /* |
| num ::= integer | '(' integer ',' integer ')' |
| */ |
| static int |
| parsenum(char *p, int *n1, int *n2, char **pp) |
| { |
| if(isdigit(*p)){ |
| *n1 = strtol(p, &p, 10); |
| *n2 = 0; |
| *pp = p; |
| return 0; |
| } |
| if(*p == '('){ |
| *n1 = strtol(p+1, &p, 10); |
| if(*p != ',') |
| oops(); |
| *n2 = strtol(p+1, &p, 10); |
| if(*p != ')') |
| oops(); |
| *pp = p+1; |
| return 0; |
| } |
| oops(); |
| return -1; |
| } |
| |
| /* |
| attr ::= '@' text ';' |
| |
| text is |
| 'a' integer (alignment) |
| 'p' integer (pointer class) |
| 'P' (packed type) |
| 's' integer (size of type in bits) |
| 'S' (string instead of array of chars) |
| */ |
| static int |
| parseattr(char *p, char **text, char **pp) |
| { |
| if(*p != '@') |
| return -1; |
| *text = p+1; |
| if((p = strchr(p, ';')) == nil) |
| oops(); |
| *pp = p+1; |
| return 0; |
| } |
| |
| |
| typedef struct Basic Basic; |
| struct Basic |
| { |
| int size; |
| int fmt; |
| }; |
| |
| static Basic baseints[] = |
| { |
| 0, 0, |
| /*1*/ 4, 'd', /* int32 */ |
| /*2*/ 1, 'd', /* char8 */ |
| /*3*/ 2, 'd', /* int16 */ |
| /*4*/ 4, 'd', /* long int32 */ |
| /*5*/ 1, 'x', /* uchar8 */ |
| /*6*/ 1, 'd', /* schar8 */ |
| /*7*/ 2, 'x', /* uint16 */ |
| /*8*/ 4, 'x', /* uint32 */ |
| /*9*/ 4, 'x', /* uint32 */ |
| /*10*/ 4, 'x', /* ulong32 */ |
| /*11*/ 0, 0, /* void */ |
| /*12*/ 4, 'f', /* float */ |
| /*13*/ 8, 'f', /* double */ |
| /*14*/ 10, 'f', /* long double */ |
| /*15*/ 4, 'd', /* int32 */ |
| /*16*/ 4, 'd', /* bool32 */ |
| /*17*/ 2, 'f', /* short real */ |
| /*18*/ 4, 'f', /* real */ |
| /*19*/ 4, 'x', /* stringptr */ |
| /*20*/ 1, 'd', /* character8 */ |
| /*21*/ 1, 'x', /* logical*1 */ |
| /*22*/ 2, 'x', /* logical*2 */ |
| /*23*/ 4, 'X', /* logical*4 */ |
| /*24*/ 4, 'X', /* logical32 */ |
| /*25*/ 8, 'F', /* complex (two single) */ |
| /*26*/ 16, 'F', /* complex (two double) */ |
| /*27*/ 1, 'd', /* integer*1 */ |
| /*28*/ 2, 'd', /* integer*2 */ |
| /*29*/ 4, 'd', /* integer*4 */ |
| /*30*/ 2, 'x', /* wide char */ |
| /*31*/ 8, 'd', /* int64 */ |
| /*32*/ 8, 'x', /* uint64 */ |
| /*33*/ 8, 'x', /* logical*8 */ |
| /*34*/ 8, 'd', /* integer*8 */ |
| }; |
| |
| static Basic basefloats[] = |
| { |
| 0,0, |
| /*1*/ 4, 'f', /* 32-bit */ |
| /*2*/ 8, 'f', /* 64-bit */ |
| /*3*/ 8, 'F', /* complex */ |
| /*4*/ 4, 'F', /* complex16 */ |
| /*5*/ 8, 'F', /* complex32 */ |
| /*6*/ 10, 'f', /* long double */ |
| }; |
| |
| /* |
| defn ::= info |
| | 'b' ('u' | 's') 'c'? width; offset; nbits; (builtin, signed/unsigned, char/not, width in bytes, offset & nbits of type) |
| | 'w' (aix wide char type, not used) |
| | 'R' fptype; bytes; (fptype 1=32-bit, 2=64-bit, 3=complex, 4=complex16, 5=complex32, 6=long double) |
| | 'g' typeinfo ';' nbits (aix floating, not used) |
| | 'c' typeinfo ';' nbits (aix complex, not used) |
| | 'b' typeinfo ';' bytes (ibm, no idea) |
| | 'B' typeinfo (volatile typref) |
| | 'd' typeinfo (file of typeref) |
| | 'k' typeinfo (const typeref) |
| | 'M' typeinfo ';' length (multiple instance type, fortran) |
| | 'S' typeinfo (set, typeref must have small number of values) |
| | '*' typeinfo (pointer to typeref) |
| | 'x' ('s'|'u'|'e') name ':' (struct, union, enum reference. name can have '::' in it) |
| | 'r' typeinfo ';' low ';' high ';' (subrange. typeref can be type being defined for base types!) |
| low and high are bounds |
| if bound is octal power of two, it's a big negative number |
| | ('a'|'P') indextypedef arraytypeinfo (array, index should be range type) |
| indextype is type definition not typeinfo (need not say typenum=) |
| P means packed array |
| | 'A' arraytypeinfo (open array (no index bounds)) |
| | 'D' dims ';' typeinfo (dims-dimensional dynamic array) |
| | 'E' dims ';' typeinfo (subarray of N-dimensional array) |
| | 'n' typeinfo ';' bytes (max length string) |
| | 'z' typeinfo ';' bytes (no idea what difference is from 'n') |
| | 'N' (pascal stringptr) |
| | 'e' (name ':' bigint ',')* ';' (enum listing) |
| | ('s'|'u') bytes (name ':' type ',' bitoffset ',' bitsize ';')* ';' (struct/union defn) |
| tag is given as name in stabs entry, with 'T' symbol |
| | 'f' typeinfo ';' (function returning type) |
| | 'f' rettypeinfo ',' paramcount ';' (typeinfo ',' (0|1) ';')* ';' |
| | 'p' paramcount ';' (typeinfo ',' (0|1) ';')* ';' |
| | 'F' rettypeinfo ',' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';' |
| | 'R' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';' |
| (the 0 or 1 is pass-by-reference vs pass-by-value) |
| (the 0 or 1 is pass-by-reference vs pass-by-value) |
| */ |
| |
| static Type* |
| parsedefn(char *p, Type *t, char **pp) |
| { |
| char c, *name; |
| int ischar, namelen, n, wid, offset, bits, sign; |
| long val; |
| Type *tt; |
| |
| if(*p == '(' || isdigit(*p)){ |
| t->ty = Defer; |
| t->sub = parseinfo(p, pp); |
| return t; |
| } |
| |
| switch(c = *p){ |
| case '-': /* builtin */ |
| n = strtol(p+1, &p, 10); |
| if(n >= nelem(baseints) || n < 0) |
| n = 0; |
| t->ty = Base; |
| t->xsizeof = baseints[n].size; |
| t->printfmt = baseints[n].fmt; |
| break; |
| case 'b': /* builtin */ |
| p++; |
| if(*p != 'u' && *p != 's') |
| oops(); |
| sign = (*p == 's'); |
| p++; |
| if(*p == 'c'){ |
| ischar = 1; |
| p++; |
| } |
| wid = parseint(&p); |
| semi(&p); |
| offset = parseint(&p); |
| semi(&p); |
| bits = parseint(&p); |
| semi(&p); |
| t->ty = Base; |
| t->xsizeof = wid; |
| if(sign == 1) |
| t->printfmt = 'd'; |
| else |
| t->printfmt = 'x'; |
| break; |
| case 'R': /* fp type */ |
| n = parseint(&p); |
| semi(&p); |
| wid = parseint(&p); |
| semi(&p); |
| t->ty = Base; |
| t->xsizeof = wid; |
| if(n < 0 || n >= nelem(basefloats)) |
| n = 0; |
| t->xsizeof = basefloats[n].size; |
| t->printfmt = basefloats[n].fmt; |
| break; |
| case 'r': /* subrange */ |
| t->ty = Range; |
| t->sub = parseinfo(p+1, &p); |
| if(*(p-1) == ';' && *p != ';') |
| p--; |
| semi(&p); |
| t->lo = parsebound(&p); |
| semi(&p); |
| t->hi = parsebound(&p); |
| semi(&p); |
| break; |
| case 'B': /* volatile */ |
| case 'k': /* const */ |
| t->ty = Defer; |
| t->sub = parseinfo(p+1, &p); |
| break; |
| case '*': /* pointer */ |
| case 'A': /* open array */ |
| t->ty = Pointer; |
| t->sub = parseinfo(p+1, &p); |
| break; |
| case 'a': /* array */ |
| case 'P': /* packed array */ |
| t->ty = Pointer; |
| tt = newtype(); |
| parsedefn(p+1, tt, &p); /* index type */ |
| if(*p == ';') |
| p++; |
| tt = newtype(); |
| t->sub = tt; |
| parsedefn(p, tt, &p); /* element type */ |
| break; |
| case 'e': /* enum listing */ |
| p++; |
| t->sue = 'e'; |
| t->ty = Enum; |
| while(*p != ';'){ |
| name = p; |
| p = findcolon(p)+1; |
| namelen = (p-name)-1; |
| val = parsebigint(&p); |
| comma(&p); |
| if(t->n%32 == 0){ |
| t->tname = erealloc(t->tname, (t->n+32)*sizeof(t->tname[0])); |
| t->val = erealloc(t->val, (t->n+32)*sizeof(t->val[0])); |
| } |
| t->tname[t->n] = estrndup(name, namelen); |
| t->val[t->n] = val; |
| t->n++; |
| } |
| semi(&p); |
| break; |
| |
| case 's': /* struct */ |
| case 'u': /* union */ |
| p++; |
| t->sue = c; |
| t->ty = Aggr; |
| n = parseint(&p); |
| while(*p != ';'){ |
| name = p; |
| p = findcolon(p)+1; |
| namelen = (p-name)-1; |
| tt = parseinfo(p, &p); |
| comma(&p); |
| offset = parseint(&p); |
| comma(&p); |
| bits = parseint(&p); |
| semi(&p); |
| if(t->n%32 == 0){ |
| t->tname = erealloc(t->tname, (t->n+32)*sizeof(t->tname[0])); |
| t->val = erealloc(t->val, (t->n+32)*sizeof(t->val[0])); |
| t->t = erealloc(t->t, (t->n+32)*sizeof(t->t[0])); |
| } |
| t->tname[t->n] = estrndup(name, namelen); |
| t->val[t->n] = offset; |
| t->t[t->n] = tt; |
| t->n++; |
| } |
| semi(&p); |
| break; |
| |
| case 'x': /* struct, union, enum reference */ |
| p++; |
| t->ty = Defer; |
| if(*p != 's' && *p != 'u' && *p != 'e') |
| oops(); |
| c = *p; |
| name = p+1; |
| p = findcolon(p+1); |
| name = estrndup(name, p-name); |
| t->sub = typebysue(c, name); |
| p++; |
| break; |
| |
| #if 0 /* AIX */ |
| case 'f': /* function */ |
| case 'p': /* procedure */ |
| case 'F': /* Pascal function */ |
| /* case 'R': /* Pascal procedure */ |
| /* |
| * Even though we don't use the info, we have |
| * to parse it in case it is embedded in other descriptions. |
| */ |
| t->ty = Function; |
| p++; |
| if(c == 'f' || c == 'F'){ |
| t->sub = parseinfo(p, &p); |
| if(*p != ','){ |
| if(*p == ';') |
| p++; |
| break; |
| } |
| comma(&p); |
| } |
| n = parseint(&p); /* number of params */ |
| semi(&p); |
| while(*p != ';'){ |
| if(c == 'F' || c == 'R'){ |
| name = p; /* parameter name */ |
| p = findcolon(p)+1; |
| } |
| parseinfo(p, &p); /* param type */ |
| comma(&p); |
| parseint(&p); /* bool: passed by value? */ |
| semi(&p); |
| } |
| semi(&p); |
| break; |
| #endif |
| |
| case 'f': /* static function */ |
| case 'F': /* global function */ |
| t->ty = Function; |
| p++; |
| t->sub = parseinfo(p, &p); |
| break; |
| |
| /* |
| * We'll never see any of this stuff. |
| * When we do, we can worry about it. |
| */ |
| case 'D': /* n-dimensional array */ |
| case 'E': /* subarray of n-dimensional array */ |
| case 'M': /* fortran multiple instance type */ |
| case 'N': /* pascal string ptr */ |
| case 'S': /* set */ |
| case 'c': /* aix complex */ |
| case 'd': /* file of */ |
| case 'g': /* aix float */ |
| case 'n': /* max length string */ |
| case 'w': /* aix wide char */ |
| case 'z': /* another max length string */ |
| default: |
| fprint(2, "unsupported type char %c (%d)\n", *p, *p); |
| oops(); |
| } |
| *pp = p; |
| return t; |
| } |
| |
| /* |
| bound ::= |
| 'A' offset (bound is on stack by ref at offset offset from arg list) |
| | 'T' offset (bound is on stack by val at offset offset from arg list) |
| | 'a' regnum (bound passed by reference in register) |
| | 't' regnum (bound passed by value in register) |
| | 'J' (no bound) |
| | bigint |
| */ |
| static int |
| parsebound(char **pp) |
| { |
| char *p; |
| int n; |
| |
| n = 0; |
| p = *pp; |
| switch(*p){ |
| case 'A': /* bound is on stack by reference at offset n from arg list */ |
| case 'T': /* bound is on stack by value at offset n from arg list */ |
| case 'a': /* bound is passed by reference in register n */ |
| case 't': /* bound is passed by value in register n */ |
| p++; |
| parseint(&p); |
| break; |
| case 'J': /* no bound */ |
| p++; |
| break; |
| default: |
| n = parsebigint(&p); |
| break; |
| } |
| *pp = p; |
| return n; |
| } |
| |
| /* |
| bigint ::= '-'? decimal |
| | 0 octal |
| | -1 |
| */ |
| static vlong |
| parsebigint(char **pp) |
| { |
| char *p; |
| int n, neg; |
| |
| p = *pp; |
| if(*p == '0'){ |
| errno = 0; |
| n = strtoll(p, &p, 8); |
| if(errno) |
| n = 0; |
| goto out; |
| } |
| neg = 0; |
| if(*p == '-'){ |
| neg = 1; |
| p++; |
| } |
| if(!isdigit(*p)) |
| oops(); |
| n = strtol(p, &p, 10); |
| if(neg) |
| n = -n; |
| |
| out: |
| *pp = p; |
| return n; |
| } |
| |
| int |
| stabs2acid(Stab *stabs, Biobuf *b) |
| { |
| volatile int fno, i; |
| char c, *file, *desc, *p; |
| char *volatile dir, *volatile fn, *volatile name; |
| Ftypes *f; |
| Type *t, *tt; |
| StabSym sym; |
| |
| dir = nil; |
| file = nil; |
| fno = 0; |
| fn = nil; |
| for(i=0; stabsym(stabs, i, &sym)>=0; i++){ |
| switch(sym.type){ |
| case N_SO: |
| if(sym.name){ |
| if(sym.name[0] && sym.name[strlen(sym.name)-1] == '/') |
| dir = sym.name; |
| else |
| file = sym.name; |
| } |
| denumber(); |
| fstack = nil; |
| fno = 0; |
| break; |
| case N_BINCL: |
| fno++; |
| f = mkftypes(dir, sym.name); |
| f->down = fstack; |
| fstack = f; |
| break; |
| case N_EINCL: |
| if(fstack) |
| fstack = fstack->down; |
| break; |
| case N_EXCL: |
| fno++; |
| if((f = findftypes(dir, sym.name)) == nil){ |
| fprint(2, "cannot find remembered %s\n", sym.name); |
| continue; |
| } |
| renumber(f->list, fno); |
| break; |
| case N_GSYM: |
| case N_FUN: |
| case N_PSYM: |
| case N_LSYM: |
| case N_LCSYM: |
| case N_STSYM: |
| case N_RSYM: |
| name = sym.name; |
| if(name == nil){ |
| if(sym.type==N_FUN) |
| fn = nil; |
| continue; |
| } |
| if((p = strchr(name, ':')) == nil) |
| continue; |
| name = estrndup(name, p-name); |
| desc = ++p; |
| c = *desc; |
| if(c == 'c'){ |
| fprint(2, "skip constant %s\n", name); |
| continue; |
| } |
| if(setjmp(kaboom)){ |
| fprint(2, "cannot parse %s\n", name); |
| continue; |
| } |
| t = parsename(desc, &p); |
| if(t == nil) |
| continue; |
| if(*p != 0) |
| fprint(2, "extra desc '%s' in '%s'\n", p, desc); |
| /* void is defined as itself */ |
| if(t->ty==Defer && t->sub==t && strcmp(name, "void")==0){ |
| t->ty = Base; |
| t->xsizeof = 0; |
| t->printfmt = '0'; |
| } |
| if(*name==' ' && *(name+1) == 0) |
| *name = 0; |
| /* attach names to structs, unions, enums */ |
| if(c=='T' && *name && t->sue){ |
| t->suename = name; |
| if(t->name == nil) |
| t->name = name; |
| tt = typebysue(t->sue, name); |
| tt->ty = Defer; |
| tt->sub = t; |
| } |
| if(c=='t'){ |
| tt = newtype(); |
| tt->ty = Typedef; |
| tt->name = name; |
| tt->sub = t; |
| } |
| /* define base c types */ |
| if(t->ty==None || t->ty==Range){ |
| if(strcmp(name, "char") == 0){ |
| t->ty = Base; |
| t->xsizeof = 1; |
| t->printfmt = 'x'; |
| } |
| if(strcmp(name, "int") == 0){ |
| t->ty = Base; |
| t->xsizeof = 4; |
| t->printfmt = 'd'; |
| } |
| } |
| /* record declaration in list for later. */ |
| if(c != 't' && c != 'T') |
| switch(sym.type){ |
| case N_GSYM: |
| addsymx(nil, name, t); |
| break; |
| case N_FUN: |
| fn = name; |
| break; |
| case N_PSYM: |
| case N_LSYM: |
| case N_LCSYM: |
| case N_STSYM: |
| case N_RSYM: |
| addsymx(fn, name, t); |
| break; |
| } |
| break; |
| } |
| } |
| |
| printtypes(b); |
| dumpsyms(b); |
| freetypes(); |
| |
| return 0; |
| } |