|  | #include <u.h> | 
|  | #include <libc.h> | 
|  | #include <bio.h> | 
|  | #include <mach.h> | 
|  |  | 
|  | int machdebug = 0; | 
|  |  | 
|  | Fhdr *fhdrlist; | 
|  | static Fhdr *last; | 
|  |  | 
|  | static void | 
|  | relocsym(Symbol *dst, Symbol *src, ulong base) | 
|  | { | 
|  | if(dst != src) | 
|  | *dst = *src; | 
|  | if(dst->loc.type == LADDR) | 
|  | dst->loc.addr += base; | 
|  | if(dst->hiloc.type == LADDR) | 
|  | dst->hiloc.addr += base; | 
|  | } | 
|  |  | 
|  | void | 
|  | _addhdr(Fhdr *h) | 
|  | { | 
|  | h->next = nil; | 
|  | if(fhdrlist == nil){ | 
|  | fhdrlist = h; | 
|  | last = h; | 
|  | }else{ | 
|  | last->next = h; | 
|  | last = h; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | _delhdr(Fhdr *h) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | if(h == fhdrlist) | 
|  | fhdrlist = h->next; | 
|  | else{ | 
|  | for(p=fhdrlist; p && p->next!=h; p=p->next) | 
|  | ; | 
|  | if(p){ | 
|  | p->next = h->next; | 
|  | if(p->next == nil) | 
|  | last = p; | 
|  | } | 
|  | } | 
|  | h->next = nil; | 
|  | } | 
|  |  | 
|  | Fhdr* | 
|  | findhdr(char *name) | 
|  | { | 
|  | int len, plen; | 
|  | Fhdr *p; | 
|  |  | 
|  | len = strlen(name); | 
|  | for(p=fhdrlist; p; p=p->next){ | 
|  | plen = strlen(p->filename); | 
|  | if(plen >= len) | 
|  | if(strcmp(p->filename+plen-len, name) == 0) | 
|  | if(plen == len || p->filename[plen-len-1] == '/') | 
|  | return p; | 
|  | } | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | int | 
|  | pc2file(u64int pc, char *file, uint nfile, ulong *line) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | for(p=fhdrlist; p; p=p->next) | 
|  | if(p->pc2file && p->pc2file(p, pc-p->base, file, nfile, line) >= 0) | 
|  | return 0; | 
|  | werrstr("no source file for 0x%lux", pc); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | pc2line(u64int pc, ulong *line) | 
|  | { | 
|  | char tmp[10];	/* just in case */ | 
|  | return pc2file(pc, tmp, sizeof tmp, line); | 
|  | } | 
|  |  | 
|  | int | 
|  | file2pc(char *file, ulong line, u64int *addr) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | for(p=fhdrlist; p; p=p->next) | 
|  | if(p->file2pc && p->file2pc(p, file, line, addr) >= 0){ | 
|  | *addr += p->base; | 
|  | return 0; | 
|  | } | 
|  | werrstr("no instructions at %s:%lud", file, line); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | line2pc(u64int basepc, ulong line, u64int *pc) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | for(p=fhdrlist; p; p=p->next) | 
|  | if(p->line2pc && p->line2pc(p, basepc-p->base, line, pc) >= 0){ | 
|  | *pc += p->base; | 
|  | return 0; | 
|  | } | 
|  | werrstr("no instructions on line %lud", line); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | fnbound(u64int pc, u64int *bounds) | 
|  | { | 
|  | Fhdr *p; | 
|  | Loc l; | 
|  | Symbol *s; | 
|  |  | 
|  | for(p=fhdrlist; p; p=p->next){ | 
|  | l = locaddr(pc - p->base); | 
|  | if((s = ffindsym(p, l, CANY)) != nil){ | 
|  | if(s->loc.type != LADDR){ | 
|  | werrstr("function %s has weird location %L", s->name, s->loc); | 
|  | return -1; | 
|  | } | 
|  | bounds[0] = s->loc.addr + p->base; | 
|  | if(s->hiloc.type != LADDR){ | 
|  | werrstr("can't find upper bound for function %s", s->name); | 
|  | return -1; | 
|  | } | 
|  | bounds[1] = s->hiloc.addr + p->base; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | werrstr("no function contains 0x%lux", pc); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | fileline(u64int pc, char *a, uint n) | 
|  | { | 
|  | ulong line; | 
|  |  | 
|  | if(pc2file(pc, a, n, &line) < 0) | 
|  | return -1; | 
|  | seprint(a+strlen(a), a+n, ":%lud", line); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | Symbol* | 
|  | flookupsym(Fhdr *fhdr, char *name) | 
|  | { | 
|  | Symbol **a, *t; | 
|  | uint n, m; | 
|  | int i; | 
|  |  | 
|  | a = fhdr->byname; | 
|  | n = fhdr->nsym; | 
|  | if(a == nil) | 
|  | return nil; | 
|  |  | 
|  | while(n > 0){ | 
|  | m = n/2; | 
|  | t = a[m]; | 
|  | i = strcmp(name, t->name); | 
|  | if(i < 0) | 
|  | n = m; | 
|  | else if(i > 0){ | 
|  | n -= m+1; | 
|  | a += m+1; | 
|  | }else{ | 
|  | /* found! */ | 
|  | m += a - fhdr->byname; | 
|  | a = fhdr->byname; | 
|  | assert(strcmp(name, a[m]->name) == 0); | 
|  | while(m > 0 && strcmp(name, a[m-1]->name) == 0) | 
|  | m--; | 
|  | return a[m]; | 
|  | } | 
|  | } | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | Symbol* | 
|  | flookupsymx(Fhdr *fhdr, char *name) | 
|  | { | 
|  | Symbol **a, *t; | 
|  | uint n, m; | 
|  | int i; | 
|  |  | 
|  | a = fhdr->byxname; | 
|  | n = fhdr->nsym; | 
|  | if(a == nil) | 
|  | return nil; | 
|  |  | 
|  | while(n > 0){ | 
|  | m = n/2; | 
|  | t = a[m]; | 
|  | i = strcmp(name, t->xname); | 
|  | if(i < 0) | 
|  | n = m; | 
|  | else if(i > 0){ | 
|  | n -= m+1; | 
|  | a += m+1; | 
|  | }else{ | 
|  | /* found! */ | 
|  | m += a - fhdr->byxname; | 
|  | a = fhdr->byxname; | 
|  | assert(strcmp(name, a[m]->xname) == 0); | 
|  | while(m > 0 && strcmp(name, a[m-1]->xname) == 0) | 
|  | m--; | 
|  | return a[m]; | 
|  | } | 
|  | } | 
|  | return nil; | 
|  | } | 
|  |  | 
|  | int | 
|  | lookupsym(char *fn, char *var, Symbol *s) | 
|  | { | 
|  | Symbol *t, s1; | 
|  | Fhdr *p; | 
|  | char *nam; | 
|  |  | 
|  | nam = fn ? fn : var; | 
|  | if(nam == nil) | 
|  | return -1; | 
|  | t = nil; | 
|  | for(p=fhdrlist; p; p=p->next) | 
|  | if((t=flookupsym(p, nam)) != nil | 
|  | || (t=flookupsymx(p, nam)) != nil){ | 
|  | relocsym(&s1, t, p->base); | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(t == nil) | 
|  | goto err; | 
|  | if(fn && var) | 
|  | return lookuplsym(&s1, var, s); | 
|  | *s = s1; | 
|  | return 0; | 
|  |  | 
|  | err: | 
|  | werrstr("unknown symbol %s%s%s", fn ? fn : "", | 
|  | fn && var ? ":" : "", var ? var : ""); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | findexsym(Fhdr *fp, uint i, Symbol *s) | 
|  | { | 
|  | if(i >= fp->nsym) | 
|  | return -1; | 
|  | relocsym(s, &fp->sym[i], fp->base); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | indexsym(uint ndx, Symbol *s) | 
|  | { | 
|  | uint t; | 
|  | Fhdr *p; | 
|  |  | 
|  | for(p=fhdrlist; p; p=p->next){ | 
|  | t = p->nsym; | 
|  | if(t < ndx) | 
|  | ndx -= t; | 
|  | else{ | 
|  | relocsym(s, &p->sym[ndx], p->base); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | Symbol* | 
|  | ffindsym(Fhdr *fhdr, Loc loc, uint class) | 
|  | { | 
|  | Symbol *a, *t; | 
|  | int n, i, hi, lo; | 
|  | int cmp; | 
|  |  | 
|  | a = fhdr->sym; | 
|  | n = fhdr->nsym; | 
|  | if(a == nil || n <= 0) | 
|  | return nil; | 
|  |  | 
|  | /* | 
|  | * We have a list of possibly duplicate locations in a. | 
|  | * We want to find the largest index i such that | 
|  | * a[i] <= loc.  This cannot be done with a simple | 
|  | * binary search.  Instead we binary search to find | 
|  | * where the location should be. | 
|  | */ | 
|  | lo = 0; | 
|  | hi = n; | 
|  | while(lo < hi){ | 
|  | i = (lo+hi)/2; | 
|  | cmp = loccmp(&loc, &a[i].loc); | 
|  | if(cmp < 0)	/* loc < a[i].loc */ | 
|  | hi = i; | 
|  | if(cmp > 0)	/* loc > a[i].loc */ | 
|  | lo = i+1; | 
|  | if(cmp == 0) | 
|  | goto found; | 
|  | } | 
|  |  | 
|  | /* found position where value would go, but not there -- go back one */ | 
|  | if(lo == 0) | 
|  | return nil; | 
|  | i = lo-1; | 
|  |  | 
|  | found: | 
|  | /* | 
|  | * might be in a run of all-the-same -- go back to beginning of run. | 
|  | * if runs were long, could binary search for a[i].loc instead. | 
|  | */ | 
|  | while(i > 0 && loccmp(&a[i-1].loc, &a[i].loc) == 0) | 
|  | i--; | 
|  |  | 
|  | t = &a[i]; | 
|  | if(t->hiloc.type && loccmp(&loc, &t->hiloc) >= 0) | 
|  | return nil; | 
|  | if(class != CANY && class != t->class) | 
|  | return nil; | 
|  | return t; | 
|  | } | 
|  |  | 
|  | int | 
|  | findsym(Loc loc, uint class, Symbol *s) | 
|  | { | 
|  | Fhdr *p, *bestp; | 
|  | Symbol *t, *best; | 
|  | long bestd, d; | 
|  | Loc l; | 
|  |  | 
|  | l = loc; | 
|  | best = nil; | 
|  | bestp = nil; | 
|  | bestd = 0; | 
|  | for(p=fhdrlist; p; p=p->next){ | 
|  | if(l.type == LADDR) | 
|  | l.addr = loc.addr - p->base; | 
|  | if((t = ffindsym(p, l, CANY)) != nil){ | 
|  | d = l.addr - t->loc.addr; | 
|  | if(0 <= d && d < 4096) | 
|  | if(best == nil || d < bestd){ | 
|  | best = t; | 
|  | bestp = p; | 
|  | bestd = d; | 
|  | } | 
|  | } | 
|  | } | 
|  | if(best){ | 
|  | if(class != CANY && class != best->class) | 
|  | goto err; | 
|  | relocsym(s, best, bestp->base); | 
|  | return 0; | 
|  | } | 
|  | err: | 
|  | werrstr("could not find symbol at %L", loc); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | lookuplsym(Symbol *s1, char *name, Symbol *s2) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | p = s1->fhdr; | 
|  | if(p->lookuplsym && p->lookuplsym(p, s1, name, s2) >= 0){ | 
|  | relocsym(s2, s2, p->base); | 
|  | return 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | indexlsym(Symbol *s1, uint ndx, Symbol *s2) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | p = s1->fhdr; | 
|  | if(p->indexlsym && p->indexlsym(p, s1, ndx, s2) >= 0){ | 
|  | relocsym(s2, s2, p->base); | 
|  | return 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | findlsym(Symbol *s1, Loc loc, Symbol *s2) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | p = s1->fhdr; | 
|  | if(p->findlsym && p->findlsym(p, s1, loc, s2) >= 0){ | 
|  | relocsym(s2, s2, p->base); | 
|  | return 0; | 
|  | } | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | unwindframe(Map *map, Regs *regs, u64int *next, Symbol *sym) | 
|  | { | 
|  | Fhdr *p; | 
|  |  | 
|  | for(p=fhdrlist; p; p=p->next) | 
|  | if(p->unwind && p->unwind(p, map, regs, next, sym) >= 0) | 
|  | return 0; | 
|  | if(mach->unwind && mach->unwind(map, regs, next, sym) >= 0) | 
|  | return 0; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int | 
|  | symoff(char *a, uint n, u64int addr, uint class) | 
|  | { | 
|  | Loc l; | 
|  | Symbol s; | 
|  |  | 
|  | l.type = LADDR; | 
|  | l.addr = addr; | 
|  | if(findsym(l, class, &s) < 0 || addr-s.loc.addr >= 4096){ | 
|  | snprint(a, n, "%#lux", addr); | 
|  | return -1; | 
|  | } | 
|  | if(addr != s.loc.addr) | 
|  | snprint(a, n, "%s+%#lx", s.name, addr-s.loc.addr); | 
|  | else | 
|  | snprint(a, n, "%s", s.name); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* location, class, name */ | 
|  | static int | 
|  | byloccmp(const void *va, const void *vb) | 
|  | { | 
|  | int i; | 
|  | Symbol *a, *b; | 
|  |  | 
|  | a = (Symbol*)va; | 
|  | b = (Symbol*)vb; | 
|  | i = loccmp(&a->loc, &b->loc); | 
|  | if(i != 0) | 
|  | return i; | 
|  | i = a->class - b->class; | 
|  | if(i != 0) | 
|  | return i; | 
|  | return strcmp(a->name, b->name); | 
|  | } | 
|  |  | 
|  | /* name, location, class */ | 
|  | static int | 
|  | byxnamecmp(const void *va, const void *vb) | 
|  | { | 
|  | int i; | 
|  | Symbol *a, *b; | 
|  |  | 
|  | a = *(Symbol**)va; | 
|  | b = *(Symbol**)vb; | 
|  | i = strcmp(a->xname, b->xname); | 
|  | if(i != 0) | 
|  | return i; | 
|  | i = strcmp(a->name, b->name); | 
|  | if(i != 0) | 
|  | return i; | 
|  | i = loccmp(&a->loc, &b->loc); | 
|  | if(i != 0) | 
|  | return i; | 
|  | return a->class - b->class; | 
|  | } | 
|  |  | 
|  | /* name, location, class */ | 
|  | static int | 
|  | bynamecmp(const void *va, const void *vb) | 
|  | { | 
|  | int i; | 
|  | Symbol *a, *b; | 
|  |  | 
|  | a = *(Symbol**)va; | 
|  | b = *(Symbol**)vb; | 
|  | i = strcmp(a->name, b->name); | 
|  | if(i != 0) | 
|  | return i; | 
|  | i = loccmp(&a->loc, &b->loc); | 
|  | if(i != 0) | 
|  | return i; | 
|  | return a->class - b->class; | 
|  | } | 
|  |  | 
|  | int | 
|  | symopen(Fhdr *hdr) | 
|  | { | 
|  | int i; | 
|  | Symbol *r, *w, *es; | 
|  |  | 
|  | if(hdr->syminit == 0){ | 
|  | werrstr("no debugging symbols"); | 
|  | return -1; | 
|  | } | 
|  | if(hdr->syminit(hdr) < 0) | 
|  | return -1; | 
|  |  | 
|  | qsort(hdr->sym, hdr->nsym, sizeof(hdr->sym[0]), byloccmp); | 
|  | es = hdr->sym+hdr->nsym; | 
|  | for(r=w=hdr->sym; r<es; r++){ | 
|  | if(w > hdr->sym | 
|  | && strcmp((w-1)->name, r->name) ==0 | 
|  | && loccmp(&(w-1)->loc, &r->loc) == 0){ | 
|  | /* skip it */ | 
|  | }else | 
|  | *w++ = *r; | 
|  | } | 
|  | hdr->nsym = w - hdr->sym; | 
|  |  | 
|  | hdr->byname = malloc(hdr->nsym*sizeof(hdr->byname[0])); | 
|  | if(hdr->byname == nil){ | 
|  | fprint(2, "could not allocate table to sort by name\n"); | 
|  | }else{ | 
|  | for(i=0; i<hdr->nsym; i++) | 
|  | hdr->byname[i] = &hdr->sym[i]; | 
|  | qsort(hdr->byname, hdr->nsym, sizeof(hdr->byname[0]), bynamecmp); | 
|  | } | 
|  |  | 
|  | hdr->byxname = malloc(hdr->nsym*sizeof(hdr->byxname[0])); | 
|  | if(hdr->byxname == nil){ | 
|  | fprint(2, "could not allocate table to sort by xname\n"); | 
|  | }else{ | 
|  | for(i=0; i<hdr->nsym; i++) | 
|  | hdr->byxname[i] = &hdr->sym[i]; | 
|  | qsort(hdr->byxname, hdr->nsym, sizeof(hdr->byxname[0]), byxnamecmp); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void | 
|  | symclose(Fhdr *hdr) | 
|  | { | 
|  | _delhdr(hdr); | 
|  | if(hdr->symclose) | 
|  | hdr->symclose(hdr); | 
|  | free(hdr->byname); | 
|  | hdr->byname = nil; | 
|  | free(hdr->sym); | 
|  | hdr->sym = nil; | 
|  | hdr->nsym = 0; | 
|  | } | 
|  |  | 
|  | Symbol* | 
|  | _addsym(Fhdr *fp, Symbol *sym) | 
|  | { | 
|  | char *t; | 
|  | static char buf[65536]; | 
|  | Symbol *s; | 
|  |  | 
|  | if(fp->nsym%128 == 0){ | 
|  | s = realloc(fp->sym, (fp->nsym+128)*sizeof(fp->sym[0])); | 
|  | if(s == nil) | 
|  | return nil; | 
|  | fp->sym = s; | 
|  | } | 
|  | if(machdebug) | 
|  | fprint(2, "sym %s %c %L\n", sym->name, sym->type, sym->loc); | 
|  | sym->fhdr = fp; | 
|  | t = demangle(sym->name, buf, 1); | 
|  | if(t != sym->name){ | 
|  | t = strdup(t); | 
|  | if(t == nil) | 
|  | return nil; | 
|  | } | 
|  | sym->xname = t; | 
|  | s = &fp->sym[fp->nsym++]; | 
|  | *s = *sym; | 
|  | return s; | 
|  | } | 
|  |  |