Пример #1
0
int
dwarfaddrtounit(Dwarf *d, ulong addr, ulong *unit)
{
    DwarfBuf b;
    int segsize, i;
    ulong len, id, off, base, size;
    uchar *start, *end;

    memset(&b, 0, sizeof b);
    b.d = d;
    b.p = d->aranges.data;
    b.ep = b.p + d->aranges.len;

    while(b.p < b.ep){
        start = b.p;
        len = dwarfget4(&b);
        if (!len) { b.ep = b.p - 4; return -1; }
        if((id = dwarfget2(&b)) != 2){
            if(b.p == nil){
            underflow:
                werrstr("buffer underflow reading address ranges header");
            }else
                werrstr("bad dwarf version 0x%x in address ranges header", id);
            return -1;
        }
        off = dwarfget4(&b);
        b.addrsize = dwarfget1(&b);
        if(d->addrsize == 0)
            d->addrsize = b.addrsize;
        segsize = dwarfget1(&b);
        USED(segsize);	/* what am i supposed to do with this? */
        if(b.p == nil)
            goto underflow;
        if((i = (b.p-start) % (2*b.addrsize)) != 0)
            b.p += 2*b.addrsize - i;
        end = start+4+len;
        while(b.p!=nil && b.p<end){
            base = dwarfgetaddr(&b);
            size = dwarfgetaddr(&b);
            if (!size) continue;
            if(b.p == nil)
                goto underflow;
            if(base <= addr && addr < base+size){
                *unit = off;
                return 0;
            }
        }
        if(b.p == nil)
            goto underflow;
        b.p = end;
    }
    werrstr("address 0x%lux is not listed in dwarf debugging symbols", addr);
    return -1;
}
Пример #2
0
static int
dexec(DwarfBuf *b, State *s, int locstop)
{
	int c;
	long arg1, arg2;
	DwarfExpr *e;

	for(;;){
		if(b->p == b->ep){
			if(s->initr)
				s->loc = s->endloc;
			return 0;
		}
		c = dwarfget1(b);
		if(b->p == nil){
			werrstr("ran out of instructions during cfa program");
			if(trace) werrstr("%r\n");
			return -1;
		}
		if(trace) werrstr("+ loc=0x%lux op 0x%ux ", s->loc, c);
		switch(c>>6){
		case 1:	/* advance location */
			arg1 = c&0x3F;
		advance:
			if(trace) werrstr("loc += %ld\n", arg1*s->iquantum);
			s->loc += arg1 * s->iquantum;
			if(locstop)
				return 0;
			continue;

		case 2:	/* offset rule */
			arg1 = c&0x3F;
			arg2 = dwarfget128(b);
		offset:
			if(trace) werrstr("r%ld += %ld\n", arg1, arg2*s->dquantum);
			if(checkreg(s, arg1) < 0)
				return -1;
			s->r[arg1].type = RuleCfaOffset;
			s->r[arg1].offset = arg2 * s->dquantum;
			continue;

		case 3:	/* restore initial setting */
			arg1 = c&0x3F;
		restore:
			if(trace) werrstr("r%ld = init\n", arg1);
			if(checkreg(s, arg1) < 0)
				return -1;
			s->r[arg1] = s->initr[arg1];
			continue;
		}

		switch(c){
		case 0:	/* nop */
			if(trace) werrstr("nop\n");
			continue;

		case 0x01:	/* set location */
			s->loc = dwarfgetaddr(b);
			if(trace) werrstr("loc = 0x%lux\n", s->loc);
			if(locstop)
				return 0;
			continue;

		case 0x02:	/* advance loc1 */
			arg1 = dwarfget1(b);
			goto advance;

		case 0x03:	/* advance loc2 */
			arg1 = dwarfget2(b);
			goto advance;

		case 0x04:	/* advance loc4 */
			arg1 = dwarfget4(b);
			goto advance;

		case 0x05:	/* offset extended */
			arg1 = dwarfget128(b);
			arg2 = dwarfget128(b);
			goto offset;

		case 0x06:	/* restore extended */
			arg1 = dwarfget128(b);
			goto restore;

		case 0x07:	/* undefined */
			arg1 = dwarfget128(b);
			if(trace) werrstr("r%ld = undef\n", arg1);
			if(checkreg(s, arg1) < 0)
				return -1;
			s->r[arg1].type = RuleUndef;
			continue;

		case 0x08:	/* same value */
			arg1 = dwarfget128(b);
			if(trace) werrstr("r%ld = same\n", arg1);
			if(checkreg(s, arg1) < 0)
				return -1;
			s->r[arg1].type = RuleSame;
			continue;

		case 0x09:	/* register */
			arg1 = dwarfget128(b);
			arg2 = dwarfget128(b);
			if(trace) werrstr("r%ld = r%ld\n", arg1, arg2);
			if(checkreg(s, arg1) < 0 || checkreg(s, arg2) < 0)
				return -1;
			s->r[arg1].type = RuleRegister;
			s->r[arg1].reg = arg2;
			continue;

		case 0x0A:	/* remember state */
			e = malloc(s->nr*sizeof(e[0]));
			if(trace) werrstr("push\n");
			if(e == nil)
				return -1;
			void *newstack = malloc(s->nstack*sizeof(s->stack[0]));
			RtlMoveMemory(newstack, s->stack, s->nstack*sizeof(s->stack[0]));
			if (newstack) {
				free(s->stack);
				s->stack = newstack;
			} else {
				free(e);
				return -1;
			}
			if(b->p == nil){
				free(e);
				return -1;
			}
			s->stack[s->nstack++] = e;
			memmove(e, s->r, s->nr*sizeof(e[0]));
			continue;

		case 0x0B:	/* restore state */
			if(trace) werrstr("pop\n");
			if(s->nstack == 0){
				werrstr("restore state underflow");
				return -1;
			}
			e = s->stack[s->nstack-1];
			memmove(s->r, e, s->nr*sizeof(e[0]));
			free(e);
			s->nstack--;
			continue;

		case 0x0C:	/* def cfa */
			arg1 = dwarfget128(b);
			arg2 = dwarfget128(b);
		defcfa:
			if(trace) werrstr("cfa %ld(r%ld)\n", arg2, arg1);
			if(checkreg(s, arg1) < 0)
				return -1;
			s->cfa->type = RuleRegOff;
			s->cfa->reg = arg1;
			s->cfa->offset = arg2;
			continue;

		case 0x0D:	/* def cfa register */
			arg1 = dwarfget128(b);
			if(trace) werrstr("cfa reg r%ld\n", arg1);
			if(s->cfa->type != RuleRegOff){
				werrstr("change CFA register but CFA not in register+offset form");
				return -1;
			}
			if(checkreg(s, arg1) < 0)
				return -1;
			s->cfa->reg = arg1;
			continue;

		case 0x0E:	/* def cfa offset */
			arg1 = dwarfget128(b);
		cfaoffset:
			if(trace) werrstr("cfa off %ld\n", arg1);
			if(s->cfa->type != RuleRegOff){
				werrstr("change CFA offset but CFA not in register+offset form");
				return -1;
			}
			s->cfa->offset = arg1;
			continue;

		case 0x0F:	/* def cfa expression */
			if(trace) werrstr("cfa expr\n");
			s->cfa->type = RuleLocation;
			s->cfa->loc.len = dwarfget128(b);
			s->cfa->loc.data = dwarfgetnref(b, s->cfa->loc.len);
			continue;

		case 0x10:	/* def reg expression */
			arg1 = dwarfget128(b);
			if(trace) werrstr("reg expr r%ld\n", arg1);
			if(checkreg(s, arg1) < 0)
				return -1;
			s->r[arg1].type = RuleLocation;
			s->r[arg1].loc.len = dwarfget128(b);
			s->r[arg1].loc.data = dwarfgetnref(b, s->r[arg1].loc.len);
			continue;

		case 0x11:	/* offset extended */
			arg1 = dwarfget128(b);
			arg2 = dwarfget128s(b);
			goto offset;

		case 0x12:	/* cfa sf */
			arg1 = dwarfget128(b);
			arg2 = dwarfget128s(b);
			goto defcfa;

		case 0x13:	/* cfa offset sf */
			arg1 = dwarfget128s(b);
			goto cfaoffset;

		default:	/* unknown */
			werrstr("unknown opcode 0x%ux in cfa program", c);
			return -1;
		}
	}
	/* not reached */
}
Пример #3
0
/*
 * XXX This turns out to be much more expensive than the actual
 * running of the machine in dexec.  It probably makes sense to
 * cache the last 10 or so fde's we've found, since stack traces
 * will keep asking for the same info over and over.
 */
static int
findfde(Dwarf *d, ulong pc, State *s, DwarfBuf *fde)
{
	static int nbad;
	char *aug;
	uchar *next;
	int i, vers;
	ulong len, id, base, size;
	DwarfBuf b;

	if(d->frame.data == nil){
		werrstr("no frame debugging information");
		return -1;
	}
	b.d = d;
	b.p = d->frame.data;
	b.ep = b.p + d->frame.len;
	b.addrsize = d->addrsize;
	if(b.addrsize == 0)
		b.addrsize = 4;	/* where should i find this? */

	for(; b.p < b.ep; b.p = next){
		if((i = (b.p - d->frame.data) % b.addrsize))
			b.p += b.addrsize - i;
		len = dwarfget4(&b);
		if(len > b.ep-b.p){
			werrstr("bad length in cie/fde header");
			return -1;
		}
		next = b.p+len;
		id = dwarfget4(&b);
		if(id == 0xFFFFFFFF){	/* CIE */
			vers = dwarfget1(&b);
			if(vers != 1 && vers != 2 && vers != 3){
				if(++nbad == 1)
					werrstr("unknown cie version %d (wanted 1-3)\n", vers);
				continue;
			}
			aug = dwarfgetstring(&b);
			if(aug && *aug){
				if(++nbad == 1)
					werrstr("unknown augmentation: %s\n", aug);
				continue;
			}
			s->iquantum = dwarfget128(&b);
			s->dquantum = dwarfget128s(&b);
			s->rareg = dwarfget128(&b);
			if(s->rareg > s->nr){
				werrstr("return address is register %d but only have %d registers",
					s->rareg, s->nr);
				return -1;
			}
			s->init.p = b.p;
			s->init.ep = next;
		}else{	/* FDE */
			base = dwarfgetaddr(&b);
			size = dwarfgetaddr(&b);
			fde->p = b.p;
			fde->ep = next;
			s->loc = base;
			s->endloc = base+size;
			if(base <= pc && pc < base+size)
				return 0;
		}
	}
	werrstr("cannot find call frame information for pc 0x%lux", pc);
	return -1;

}
Пример #4
0
int
dwarfpctoline(Dwarf *d, DwarfSym *proc, ulong pc, char **file, char **function, ulong *line)
{
	char *cdir;
    uchar *prog, *opcount, *end, *dirs;
    ulong off, unit, len, vers, x, start, lastline;
    int i, first, firstline, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf;
    char *files, *s;
    DwarfBuf b;
    DwarfSym sym;
    State emit, cur, reset;
    char **f, **newf;

    f = nil;
    memset(proc, 0, sizeof(*proc));

    int runit = dwarfaddrtounit(d, pc, &unit);
    if (runit < 0)
        return -1;
    int rtag = dwarflookuptag(d, unit, TagCompileUnit, &sym);
    if (rtag < 0)
        return -1;

    if(!sym.attrs.have.stmtlist){
        werrstr("no line mapping information for 0x%x", pc);
        return -1;
    }
    off = sym.attrs.stmtlist;
    if(off >= d->line.len){
        werrstr("bad stmtlist");
        goto bad;
    }

    if(trace) werrstr("unit 0x%x stmtlist 0x%x", unit, sym.attrs.stmtlist);

    memset(&b, 0, sizeof b);
    b.d = d;
    b.p = d->line.data + off;
    b.ep = b.p + d->line.len;
    b.addrsize = sym.b.addrsize;	/* should i get this from somewhere else? */

    len = dwarfget4(&b);
    if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
        werrstr("bad len");
        goto bad;
    }

    b.ep = b.p+len;
    vers = dwarfget2(&b);
    if(vers != 2){
        werrstr("bad dwarf version 0x%x", vers);
        return -1;
    }

    len = dwarfget4(&b);
    if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
        werrstr("another bad len");
        goto bad;
    }
    prog = b.p+len;

    quantum = dwarfget1(&b);
    isstmt = dwarfget1(&b);
    linebase = (schar)dwarfget1(&b);
    linerange = (schar)dwarfget1(&b);
    opcodebase = dwarfget1(&b);

    opcount = b.p-1;
    dwarfgetnref(&b, opcodebase-1);
    if(b.p == nil){
        werrstr("bad opcode chart");
        goto bad;
    }

    /* just skip the files and dirs for now; we'll come back */
    dirs = b.p;
    while (b.p && *b.p)
        dwarfgetstring(&b);
    dwarfget1(&b);

    files = (char*)b.p;
    while(b.p!=nil && *b.p!=0){
        dwarfgetstring(&b);
        dwarfget128(&b);
        dwarfget128(&b);
        dwarfget128(&b);
    }
    dwarfget1(&b);

    /* move on to the program */
    if(b.p == nil || b.p > prog){
        werrstr("bad header");
        goto bad;
    }
    b.p = prog;

    reset.addr = 0;
    reset.file = 1;
    reset.line = 1;
    reset.column = 0;
    reset.flags = isstmt ? Isstmt : 0;
    reset.isa = 0;

    cur = reset;
    emit = reset;
    nf = 0;
    start = 0;
    if(trace) werrstr("program @ %lu ... %.*H opbase = %d", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
    first = 1;
    while(b.p != nil){
        firstline = 0;
        op = dwarfget1(&b);
        if(trace) werrstr("\tline %lu, addr 0x%x, op %d %.10H", cur.line, cur.addr, op, b.p);
        if(op >= opcodebase){
            a = (op - opcodebase) / linerange;
            l = (op - opcodebase) % linerange + linebase;
            cur.line += l;
            cur.addr += a * quantum;
            if(trace) werrstr(" +%d,%d", a, l);
        emit:
            if(first){
                if(cur.addr > pc){
                    werrstr("found wrong line mapping 0x%x for pc 0x%x", cur.addr, pc);
                    /* This is an overzealous check.  gcc can produce discontiguous ranges
                       and reorder statements, so it's possible for a future line to start
                       ahead of pc and still find a matching one. */
                    /*goto out;*/
                    firstline = 1;
                }
                first = 0;
                start = cur.addr;
            }
            if(cur.addr > pc && !firstline)
                break;
            if(b.p == nil){
                werrstr("buffer underflow in line mapping");
                goto out;
            }
            emit = cur;
            if(emit.flags & EndSequence){
                werrstr("found wrong line mapping 0x%x-0x%x for pc 0x%x", start, cur.addr, pc);
                goto out;
            }
            cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
        }else{
            switch(op){
            case 0:	/* extended op code */
                if(trace) werrstr(" ext");
                len = dwarfget128(&b);
                end = b.p+len;
                if(b.p == nil || end > b.ep || end < b.p || len < 1)
                    goto bad;
                switch(dwarfget1(&b)){
                case 1:	/* end sequence */
                    if(trace) werrstr(" end");
                    cur.flags |= EndSequence;
                    goto emit;
                case 2:	/* set address */
                    cur.addr = dwarfgetaddr(&b);
                    if(trace) werrstr(" set pc 0x%x", cur.addr);
                    break;
                case 3:	/* define file */
                    newf = malloc(nf+1*sizeof(f[0]));
                    if (newf)
                        RtlMoveMemory(newf, f, nf*sizeof(f[0]));
                    if(newf == nil)
                        goto out;
					free(f);
                    f = newf;
					f[nf++] = s = dwarfgetstring(&b);
					DPRINT1("str %s", s);
                    dwarfget128(&b);
                    dwarfget128(&b);
                    dwarfget128(&b);
                    if(trace) werrstr(" def file %s", s);
                    break;
                }
                if(b.p == nil || b.p > end)
                    goto bad;
                b.p = end;
                break;
            case 1:	/* emit */
                if(trace) werrstr(" emit");
                goto emit;
            case 2:	/* advance pc */
                a = dwarfget128(&b);
                if(trace) werrstr(" advance pc + %lu", a*quantum);
                cur.addr += a * quantum;
                break;
            case 3:	/* advance line */
                l = dwarfget128s(&b);
                if(trace) werrstr(" advance line + %ld", l);
                cur.line += l;
                break;
            case 4:	/* set file */
                if(trace) werrstr(" set file");
                cur.file = dwarfget128s(&b);
                break;
            case 5:	/* set column */
                if(trace) werrstr(" set column");
                cur.column = dwarfget128(&b);
                break;
            case 6:	/* negate stmt */
                if(trace) werrstr(" negate stmt");
                cur.flags ^= Isstmt;
                break;
            case 7:	/* set basic block */
                if(trace) werrstr(" set basic block");
                cur.flags |= BasicDwarfBlock;
                break;
            case 8:	/* const add pc */
                a = (255 - opcodebase) / linerange * quantum;
                if(trace) werrstr(" const add pc + %d", a);
                cur.addr += a;
                break;
            case 9:	/* fixed advance pc */
                a = dwarfget2(&b);
                if(trace) werrstr(" fixed advance pc + %d", a);
                cur.addr += a;
                break;
            case 10:	/* set prologue end */
                if(trace) werrstr(" set prologue end");
                cur.flags |= PrologueEnd;
                break;
            case 11:	/* set epilogue begin */
                if(trace) werrstr(" set epilogue begin");
                cur.flags |= EpilogueBegin;
                break;
            case 12:	/* set isa */
                if(trace) werrstr(" set isa");
                cur.isa = dwarfget128(&b);
                break;
            default:	/* something new - skip it */
                if(trace) werrstr(" unknown %d", opcount[op]);
                for(i=0; i<opcount[op]; i++)
                    dwarfget128(&b);
                break;
            }
        }
    }
    if(b.p == nil)
        goto bad;

    /* finally!  the data we seek is in "emit" */

    if(emit.file == 0){
        werrstr("invalid file index in mapping data");
        goto out;
    }
    if(line)
        *line = emit.line;

    /* skip over first emit.file-2 guys */
    b.p = (uchar*)files;
    for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
        dwarfgetstring(&b);
        dwarfget128(&b);
        dwarfget128(&b);
        dwarfget128(&b);
    }
    if(b.p == nil){
        werrstr("problem parsing file data second time (cannot happen)");
        goto bad;
    }
    if(*b.p == 0){
        if(i >= nf){
            werrstr("bad file index in mapping data");
            goto bad;
        }
        b.p = (uchar*)f[i];
    }
    s = dwarfgetstring(&b);
	*file = s;
    i = dwarfget128(&b);		/* directory */
    x = dwarfget128(&b);
    x = dwarfget128(&b);

    /* fetch dir name */
	cdir = sym.attrs.have.compdir ? sym.attrs.compdir : 0;

	char *dwarfdir;
	dwarfdir = nil;
	b.p = dirs;
	for (x = 1; b.p && *b.p; x++) {
		dwarfdir = dwarfgetstring(&b);
		if (x == i) break;
	}

	if (!cdir && dwarfdir)
		cdir = dwarfdir;
	
	char *filefull = malloc(strlen(cdir) + strlen(*file) + 2);
	strcpy(filefull, cdir);
	strcat(filefull, "/");
	strcat(filefull, *file);
	*file = filefull;
	
    *function = nil;
    lastline = 0;
	
    runit = dwarfaddrtounit(d, pc, &unit);
    if (runit == 0) {
        DwarfSym compunit = { };
        int renum = dwarfenumunit(d, unit, &compunit);
        if (renum < 0)
            return -1;
        renum = dwarfnextsymat(d, &compunit, proc);
        while (renum == 0) {
            if (proc->attrs.tag == TagSubprogram && 
				proc->attrs.have.name)
			{
                if (proc->attrs.lowpc <= pc && proc->attrs.highpc > pc) {
                    *function = malloc(strlen(proc->attrs.name)+1);
					strcpy(*function, proc->attrs.name);
                    goto done;
				}
			}
            renum = dwarfnextsym(d, proc);
        }
    }

    // Next search by declaration
    runit = dwarfaddrtounit(d, pc, &unit);
    if (runit == 0) {
        DwarfSym compunit = { };
        int renum = dwarfenumunit(d, unit, &compunit);
        if (renum < 0)
            return -1;
        renum = dwarfnextsymat(d, &compunit, proc);
        while (renum == 0) {
            if (proc->attrs.tag == TagSubprogram && 
				proc->attrs.have.name &&
				proc->attrs.declfile == emit.file)
			{
                if (proc->attrs.declline <= *line &&
                    proc->attrs.declline > lastline) {
                    free(*function);
                    *function = malloc(strlen(proc->attrs.name)+1);
					strcpy(*function, proc->attrs.name);
                    goto done;
				}
                lastline = proc->attrs.declline;
			}
            renum = dwarfnextsym(d, proc);
        }
    }

    /* free at last, free at last */
done:
    free(f);
    return 0;
bad:
    werrstr("corrupted line mapping for 0x%x", pc);
out:
    free(f);
    return -1;
}
Пример #5
0
int
dwarfpctoline(Dwarf *d, ulong pc, char **cdir, char **dir, char **file, ulong *line, ulong *mtime, ulong *length)
{
	uchar *prog, *opcount, *end;
	ulong off, unit, len, vers, x, start;
	int i, first, op, a, l, quantum, isstmt, linebase, linerange, opcodebase, nf;
	char *files, *dirs, *s;
	DwarfBuf b;
	DwarfSym sym;
	State emit, cur, reset;
	uchar **f, **newf;

	f = nil;

	if(dwarfaddrtounit(d, pc, &unit) < 0
	|| dwarflookuptag(d, unit, TagCompileUnit, &sym) < 0)
		return -1;

	if(!sym.attrs.have.stmtlist){
		werrstr("no line mapping information for 0x%lux", pc);
		return -1;
	}
	off = sym.attrs.stmtlist;
	if(off >= d->line.len){
		fprint(2, "bad stmtlist\n");
		goto bad;
	}

	if(trace) fprint(2, "unit 0x%lux stmtlist 0x%lux\n", unit, sym.attrs.stmtlist);

	memset(&b, 0, sizeof b);
	b.d = d;
	b.p = d->line.data + off;
	b.ep = b.p + d->line.len;
	b.addrsize = sym.b.addrsize;	/* should i get this from somewhere else? */

	len = dwarfget4(&b);
	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
		fprint(2, "bad len\n");
		goto bad;
	}

	b.ep = b.p+len;
	vers = dwarfget2(&b);
	if(vers != 2){
		werrstr("bad dwarf version 0x%lux", vers);
		return -1;
	}

	len = dwarfget4(&b);
	if(b.p==nil || b.p+len > b.ep || b.p+len < b.p){
		fprint(2, "another bad len\n");
		goto bad;
	}
	prog = b.p+len;

	quantum = dwarfget1(&b);
	isstmt = dwarfget1(&b);
	linebase = (schar)dwarfget1(&b);
	linerange = (schar)dwarfget1(&b);
	opcodebase = dwarfget1(&b);

	opcount = b.p-1;
	dwarfgetnref(&b, opcodebase-1);
	if(b.p == nil){
		fprint(2, "bad opcode chart\n");
		goto bad;
	}

	/* just skip the files and dirs for now; we'll come back */
	dirs = (char*)b.p;
	while(b.p!=nil && *b.p!=0)
		dwarfgetstring(&b);
	dwarfget1(&b);

	files = (char*)b.p;
	while(b.p!=nil && *b.p!=0){
		dwarfgetstring(&b);
		dwarfget128(&b);
		dwarfget128(&b);
		dwarfget128(&b);
	}
	dwarfget1(&b);

	/* move on to the program */
	if(b.p == nil || b.p > prog){
		fprint(2, "bad header\n");
		goto bad;
	}
	b.p = prog;

	reset.addr = 0;
	reset.file = 1;
	reset.line = 1;
	reset.column = 0;
	reset.flags = isstmt ? Isstmt : 0;
	reset.isa = 0;

	cur = reset;
	emit = reset;
	nf = 0;
	start = 0;
	if(trace) fprint(2, "program @ %lud ... %.*H opbase = %d\n", b.p - d->line.data, b.ep-b.p, b.p, opcodebase);
	first = 1;
	while(b.p != nil){
		op = dwarfget1(&b);
		if(trace) fprint(2, "\tline %lud, addr 0x%lux, op %d %.10H", cur.line, cur.addr, op, b.p);
		if(op >= opcodebase){
			a = (op - opcodebase) / linerange;
			l = (op - opcodebase) % linerange + linebase;
			cur.line += l;
			cur.addr += a * quantum;
			if(trace) fprint(2, " +%d,%d\n", a, l);
		emit:
			if(first){
				if(cur.addr > pc){
					werrstr("found wrong line mapping 0x%lux for pc 0x%lux", cur.addr, pc);
					goto out;
				}
				first = 0;
				start = cur.addr;
			}
			if(cur.addr > pc)
				break;
			if(b.p == nil){
				werrstr("buffer underflow in line mapping");
				goto out;
			}
			emit = cur;
			if(emit.flags & EndSequence){
				werrstr("found wrong line mapping 0x%lux-0x%lux for pc 0x%lux", start, cur.addr, pc);
				goto out;
			}
			cur.flags &= ~(BasicDwarfBlock|PrologueEnd|EpilogueBegin);
		}else{
			switch(op){
			case 0:	/* extended op code */
				if(trace) fprint(2, " ext");
				len = dwarfget128(&b);
				end = b.p+len;
				if(b.p == nil || end > b.ep || end < b.p || len < 1)
					goto bad;
				switch(dwarfget1(&b)){
				case 1:	/* end sequence */
					if(trace) fprint(2, " end\n");
					cur.flags |= EndSequence;
					goto emit;
				case 2:	/* set address */
					cur.addr = dwarfgetaddr(&b);
					if(trace) fprint(2, " set pc 0x%lux\n", cur.addr);
					break;
				case 3:	/* define file */
					newf = realloc(f, (nf+1)*sizeof(f[0]));
					if(newf == nil)
						goto out;
					f = newf;
					f[nf++] = b.p;
					s = dwarfgetstring(&b);
					dwarfget128(&b);
					dwarfget128(&b);
					dwarfget128(&b);
					if(trace) fprint(2, " def file %s\n", s);
					break;
				}
				if(b.p == nil || b.p > end)
					goto bad;
				b.p = end;
				break;
			case 1:	/* emit */
				if(trace) fprint(2, " emit\n");
				goto emit;
			case 2:	/* advance pc */
				a = dwarfget128(&b);
				if(trace) fprint(2, " advance pc + %lud\n", a*quantum);
				cur.addr += a * quantum;
				break;
			case 3:	/* advance line */
				l = dwarfget128s(&b);
				if(trace) fprint(2, " advance line + %ld\n", l);
				cur.line += l;
				break;
			case 4:	/* set file */
				if(trace) fprint(2, " set file\n");
				cur.file = dwarfget128s(&b);
				break;
			case 5:	/* set column */
				if(trace) fprint(2, " set column\n");
				cur.column = dwarfget128(&b);
				break;
			case 6:	/* negate stmt */
				if(trace) fprint(2, " negate stmt\n");
				cur.flags ^= Isstmt;
				break;
			case 7:	/* set basic block */
				if(trace) fprint(2, " set basic block\n");
				cur.flags |= BasicDwarfBlock;
				break;
			case 8:	/* const add pc */
				a = (255 - opcodebase) / linerange * quantum;
				if(trace) fprint(2, " const add pc + %d\n", a);
				cur.addr += a;
				break;
			case 9:	/* fixed advance pc */
				a = dwarfget2(&b);
				if(trace) fprint(2, " fixed advance pc + %d\n", a);
				cur.addr += a;
				break;
			case 10:	/* set prologue end */
				if(trace) fprint(2, " set prologue end\n");
				cur.flags |= PrologueEnd;
				break;
			case 11:	/* set epilogue begin */
				if(trace) fprint(2, " set epilogue begin\n");
				cur.flags |= EpilogueBegin;
				break;
			case 12:	/* set isa */
				if(trace) fprint(2, " set isa\n");
				cur.isa = dwarfget128(&b);
				break;
			default:	/* something new - skip it */
				if(trace) fprint(2, " unknown %d\n", opcount[op]);
				for(i=0; i<opcount[op]; i++)
					dwarfget128(&b);
				break;
			}
		}
	}
	if(b.p == nil)
		goto bad;

	/* finally!  the data we seek is in "emit" */

	if(emit.file == 0){
		werrstr("invalid file index in mapping data");
		goto out;
	}
	if(line)
		*line = emit.line;

	/* skip over first emit.file-2 guys */
	b.p = (uchar*)files;
	for(i=emit.file-1; i > 0 && b.p!=nil && *b.p!=0; i--){
		dwarfgetstring(&b);
		dwarfget128(&b);
		dwarfget128(&b);
		dwarfget128(&b);
	}
	if(b.p == nil){
		werrstr("problem parsing file data second time (cannot happen)");
		goto bad;
	}
	if(*b.p == 0){
		if(i >= nf){
			werrstr("bad file index in mapping data");
			goto bad;
		}
		b.p = f[i];
	}
	s = dwarfgetstring(&b);
	if(file)
		*file = s;
	i = dwarfget128(&b);		/* directory */
	x = dwarfget128(&b);
	if(mtime)
		*mtime = x;
	x = dwarfget128(&b);
	if(length)
		*length = x;

	/* fetch dir name */
	if(cdir)
		*cdir = sym.attrs.compdir;

	if(dir){
		if(i == 0)
			*dir = nil;
		else{
			b.p = (uchar*)dirs;
			for(i--; i>0 && b.p!=nil && *b.p!=0; i--)
				dwarfgetstring(&b);
			if(b.p==nil || *b.p==0){
				werrstr("bad directory reference in line mapping");
				goto out;		/* can only happen with bad dir index */
			}
			*dir = dwarfgetstring(&b);
		}
	}

	/* free at last, free at last */
	free(f);
	return 0;

bad:
	werrstr("corrupted line mapping for 0x%lux", pc);
out:
	free(f);
	return -1;
}