| /* |
| * Dwarf info parse and search. |
| */ |
| |
| #include <u.h> |
| #include <libc.h> |
| #include <bio.h> |
| #include "elf.h" |
| #include "dwarf.h" |
| |
| enum |
| { |
| DwarfAttrSibling = 0x01, |
| DwarfAttrLocation = 0x02, |
| DwarfAttrName = 0x03, |
| DwarfAttrOrdering = 0x09, |
| DwarfAttrByteSize = 0x0B, |
| DwarfAttrBitOffset = 0x0C, |
| DwarfAttrBitSize = 0x0D, |
| DwarfAttrStmtList = 0x10, |
| DwarfAttrLowpc = 0x11, |
| DwarfAttrHighpc = 0x12, |
| DwarfAttrLanguage = 0x13, |
| DwarfAttrDiscr = 0x15, |
| DwarfAttrDiscrValue = 0x16, |
| DwarfAttrVisibility = 0x17, |
| DwarfAttrImport = 0x18, |
| DwarfAttrStringLength = 0x19, |
| DwarfAttrCommonRef = 0x1A, |
| DwarfAttrCompDir = 0x1B, |
| DwarfAttrConstValue = 0x1C, |
| DwarfAttrContainingType = 0x1D, |
| DwarfAttrDefaultValue = 0x1E, |
| DwarfAttrInline = 0x20, |
| DwarfAttrIsOptional = 0x21, |
| DwarfAttrLowerBound = 0x22, |
| DwarfAttrProducer = 0x25, |
| DwarfAttrPrototyped = 0x27, |
| DwarfAttrReturnAddr = 0x2A, |
| DwarfAttrStartScope = 0x2C, |
| DwarfAttrStrideSize = 0x2E, |
| DwarfAttrUpperBound = 0x2F, |
| DwarfAttrAbstractOrigin = 0x31, |
| DwarfAttrAccessibility = 0x32, |
| DwarfAttrAddrClass = 0x33, |
| DwarfAttrArtificial = 0x34, |
| DwarfAttrBaseTypes = 0x35, |
| DwarfAttrCalling = 0x36, |
| DwarfAttrCount = 0x37, |
| DwarfAttrDataMemberLoc = 0x38, |
| DwarfAttrDeclColumn = 0x39, |
| DwarfAttrDeclFile = 0x3A, |
| DwarfAttrDeclLine = 0x3B, |
| DwarfAttrDeclaration = 0x3C, |
| DwarfAttrDiscrList = 0x3D, |
| DwarfAttrEncoding = 0x3E, |
| DwarfAttrExternal = 0x3F, |
| DwarfAttrFrameBase = 0x40, |
| DwarfAttrFriend = 0x41, |
| DwarfAttrIdentifierCase = 0x42, |
| DwarfAttrMacroInfo = 0x43, |
| DwarfAttrNamelistItem = 0x44, |
| DwarfAttrPriority = 0x45, |
| DwarfAttrSegment = 0x46, |
| DwarfAttrSpecification = 0x47, |
| DwarfAttrStaticLink = 0x48, |
| DwarfAttrType = 0x49, |
| DwarfAttrUseLocation = 0x4A, |
| DwarfAttrVarParam = 0x4B, |
| DwarfAttrVirtuality = 0x4C, |
| DwarfAttrVtableElemLoc = 0x4D, |
| DwarfAttrAllocated = 0x4E, |
| DwarfAttrAssociated = 0x4F, |
| DwarfAttrDataLocation = 0x50, |
| DwarfAttrStride = 0x51, |
| DwarfAttrEntrypc = 0x52, |
| DwarfAttrUseUTF8 = 0x53, |
| DwarfAttrExtension = 0x54, |
| DwarfAttrRanges = 0x55, |
| DwarfAttrTrampoline = 0x56, |
| DwarfAttrCallColumn = 0x57, |
| DwarfAttrCallFile = 0x58, |
| DwarfAttrCallLine = 0x59, |
| DwarfAttrDescription = 0x5A, |
| DwarfAttrMax, |
| |
| FormAddr = 0x01, |
| FormDwarfBlock2 = 0x03, |
| FormDwarfBlock4 = 0x04, |
| FormData2 = 0x05, |
| FormData4 = 0x06, |
| FormData8 = 0x07, |
| FormString = 0x08, |
| FormDwarfBlock = 0x09, |
| FormDwarfBlock1 = 0x0A, |
| FormData1 = 0x0B, |
| FormFlag = 0x0C, |
| FormSdata = 0x0D, |
| FormStrp = 0x0E, |
| FormUdata = 0x0F, |
| FormRefAddr = 0x10, |
| FormRef1 = 0x11, |
| FormRef2 = 0x12, |
| FormRef4 = 0x13, |
| FormRef8 = 0x14, |
| FormRefUdata = 0x15, |
| FormIndirect = 0x16 |
| }; |
| |
| static int parseattrs(DwarfBuf*, ulong, DwarfAbbrev*, DwarfAttrs*); |
| static int getulong(DwarfBuf*, int, ulong, ulong*, int*); |
| static int getuchar(DwarfBuf*, int, uchar*); |
| static int getstring(DwarfBuf*, int, char**); |
| static int getblock(DwarfBuf*, int, DwarfBlock*); |
| static int skipform(DwarfBuf*, int); |
| static int constblock(Dwarf*, DwarfBlock*, ulong*); |
| |
| int |
| dwarflookupnameinunit(Dwarf *d, ulong unit, char *name, DwarfSym *s) |
| { |
| if(dwarfenumunit(d, unit, s) < 0) |
| return -1; |
| |
| dwarfnextsymat(d, s, 0); /* s is now the CompileUnit */ |
| while(dwarfnextsymat(d, s, 1) == 1) |
| if(s->attrs.name && strcmp(s->attrs.name, name) == 0) |
| return 0; |
| werrstr("symbol '%s' not found", name); |
| return -1; |
| } |
| |
| |
| int |
| dwarflookupsubname(Dwarf *d, DwarfSym *parent, char *name, DwarfSym *s) |
| { |
| *s = *parent; |
| while(dwarfnextsymat(d, s, parent->depth+1)) |
| if(s->attrs.name && strcmp(s->attrs.name, name) == 0) |
| return 0; |
| werrstr("symbol '%s' not found", name); |
| return -1; |
| } |
| |
| int |
| dwarflookuptag(Dwarf *d, ulong unit, ulong tag, DwarfSym *s) |
| { |
| if(dwarfenumunit(d, unit, s) < 0) |
| return -1; |
| |
| dwarfnextsymat(d, s, 0); /* s is now the CompileUnit */ |
| if(s->attrs.tag == tag) |
| return 0; |
| while(dwarfnextsymat(d, s, 1) == 1) |
| if(s->attrs.tag == tag) |
| return 0; |
| werrstr("symbol with tag 0x%lux not found", tag); |
| return -1; |
| } |
| |
| int |
| dwarfseeksym(Dwarf *d, ulong unit, ulong off, DwarfSym *s) |
| { |
| if(dwarfenumunit(d, unit, s) < 0) |
| return -1; |
| s->b.p = d->info.data + unit + off; |
| if(dwarfnextsymat(d, s, 0) != 1) |
| return -1; |
| return 0; |
| } |
| |
| int |
| dwarflookupfn(Dwarf *d, ulong unit, ulong pc, DwarfSym *s) |
| { |
| if(dwarfenumunit(d, unit, s) < 0) |
| return -1; |
| |
| if(dwarfnextsymat(d, s, 0) != 1) |
| return -1; |
| /* s is now the CompileUnit */ |
| |
| while(dwarfnextsymat(d, s, 1) == 1){ |
| if(s->attrs.tag != TagSubprogram) |
| continue; |
| if(s->attrs.lowpc <= pc && pc < s->attrs.highpc) |
| return 0; |
| } |
| werrstr("fn containing pc 0x%lux not found", pc); |
| return -1; |
| } |
| |
| int |
| dwarfenumunit(Dwarf *d, ulong unit, DwarfSym *s) |
| { |
| int i; |
| ulong aoff, len; |
| |
| if(unit >= d->info.len){ |
| werrstr("dwarf unit address 0x%lux >= 0x%lux out of range", unit, d->info.len); |
| return -1; |
| } |
| memset(s, 0, sizeof *s); |
| memset(&s->b, 0, sizeof s->b); |
| s->b.d = d; |
| s->b.p = d->info.data + unit; |
| s->b.ep = d->info.data + d->info.len; |
| len = dwarfget4(&s->b); |
| s->nextunit = unit + 4 + len; |
| |
| if(s->b.ep - s->b.p < len){ |
| badheader: |
| werrstr("bad dwarf unit header at unit 0x%lux", unit); |
| return -1; |
| } |
| s->b.ep = s->b.p+len; |
| if((i=dwarfget2(&s->b)) != 2) |
| goto badheader; |
| aoff = dwarfget4(&s->b); |
| s->b.addrsize = dwarfget1(&s->b); |
| if(d->addrsize == 0) |
| d->addrsize = s->b.addrsize; |
| if(s->b.p == nil) |
| goto badheader; |
| |
| s->aoff = aoff; |
| s->unit = unit; |
| s->depth = 0; |
| return 0; |
| } |
| |
| int |
| dwarfenum(Dwarf *d, DwarfSym *s) |
| { |
| if(dwarfenumunit(d, 0, s) < 0) |
| return -1; |
| s->allunits = 1; |
| return 0; |
| } |
| |
| int |
| dwarfnextsym(Dwarf *d, DwarfSym *s) |
| { |
| ulong num; |
| DwarfAbbrev *a; |
| |
| if(s->attrs.haskids) |
| s->depth++; |
| top: |
| if(s->b.p >= s->b.ep){ |
| if(s->allunits && s->nextunit < d->info.len){ |
| if(dwarfenumunit(d, s->nextunit, s) < 0) |
| return -1; |
| s->allunits = 1; |
| goto top; |
| } |
| return 0; |
| } |
| |
| s->uoff = s->b.p - (d->info.data+s->unit); |
| num = dwarfget128(&s->b); |
| if(num == 0){ |
| if(s->depth == 0) |
| return 0; |
| if(s->depth > 0) |
| s->depth--; |
| goto top; |
| } |
| |
| a = dwarfgetabbrev(d, s->aoff, num); |
| if(a == nil){ |
| fprint(2, "getabbrev %ud %ud for %ud,%ud: %r\n", s->aoff, num, s->unit, s->uoff); |
| abort(); |
| return -1; |
| } |
| if(parseattrs(&s->b, s->unit, a, &s->attrs) < 0) |
| return -1; |
| return 1; |
| } |
| |
| int |
| dwarfnextsymat(Dwarf *d, DwarfSym *s, int depth) |
| { |
| int r; |
| DwarfSym t; |
| uint sib; |
| |
| if(s->depth == depth && s->attrs.have.sibling){ |
| sib = s->attrs.sibling; |
| if(sib < d->info.len && d->info.data+sib >= s->b.p) |
| s->b.p = d->info.data+sib; |
| s->attrs.haskids = 0; |
| } |
| |
| /* |
| * The funny game with t and s make sure that |
| * if we get to the end of a run of a particular |
| * depth, we leave s so that a call to nextsymat with depth-1 |
| * will actually produce the desired guy. We could change |
| * the interface to dwarfnextsym instead, but I'm scared |
| * to touch it. |
| */ |
| t = *s; |
| for(;;){ |
| if((r = dwarfnextsym(d, &t)) != 1) |
| return r; |
| if(t.depth < depth){ |
| /* went too far - nothing to see */ |
| return 0; |
| } |
| *s = t; |
| if(t.depth == depth) |
| return 1; |
| } |
| } |
| |
| typedef struct Parse Parse; |
| struct Parse { |
| int name; |
| int off; |
| int haveoff; |
| int type; |
| }; |
| |
| #define OFFSET(x) offsetof(DwarfAttrs, x), offsetof(DwarfAttrs, have.x) |
| |
| static Parse plist[] = { /* Font Tab 4 */ |
| DwarfAttrAbstractOrigin, OFFSET(abstractorigin), TReference, |
| DwarfAttrAccessibility, OFFSET(accessibility), TConstant, |
| DwarfAttrAddrClass, OFFSET(addrclass), TConstant, |
| DwarfAttrArtificial, OFFSET(isartificial), TFlag, |
| DwarfAttrBaseTypes, OFFSET(basetypes), TReference, |
| DwarfAttrBitOffset, OFFSET(bitoffset), TConstant, |
| DwarfAttrBitSize, OFFSET(bitsize), TConstant, |
| DwarfAttrByteSize, OFFSET(bytesize), TConstant, |
| DwarfAttrCalling, OFFSET(calling), TConstant, |
| DwarfAttrCommonRef, OFFSET(commonref), TReference, |
| DwarfAttrCompDir, OFFSET(compdir), TString, |
| DwarfAttrConstValue, OFFSET(constvalue), TString|TConstant|TBlock, |
| DwarfAttrContainingType, OFFSET(containingtype), TReference, |
| DwarfAttrCount, OFFSET(count), TConstant|TReference, |
| DwarfAttrDataMemberLoc, OFFSET(datamemberloc), TBlock|TConstant|TReference, |
| DwarfAttrDeclColumn, OFFSET(declcolumn), TConstant, |
| DwarfAttrDeclFile, OFFSET(declfile), TConstant, |
| DwarfAttrDeclLine, OFFSET(declline), TConstant, |
| DwarfAttrDeclaration, OFFSET(isdeclaration), TFlag, |
| DwarfAttrDefaultValue, OFFSET(defaultvalue), TReference, |
| DwarfAttrDiscr, OFFSET(discr), TReference, |
| DwarfAttrDiscrList, OFFSET(discrlist), TBlock, |
| DwarfAttrDiscrValue, OFFSET(discrvalue), TConstant, |
| DwarfAttrEncoding, OFFSET(encoding), TConstant, |
| DwarfAttrExternal, OFFSET(isexternal), TFlag, |
| DwarfAttrFrameBase, OFFSET(framebase), TBlock|TConstant, |
| DwarfAttrFriend, OFFSET(friend), TReference, |
| DwarfAttrHighpc, OFFSET(highpc), TAddress, |
| DwarfAttrIdentifierCase, OFFSET(identifiercase), TConstant, |
| DwarfAttrImport, OFFSET(import), TReference, |
| DwarfAttrInline, OFFSET(inlined), TConstant, |
| DwarfAttrIsOptional, OFFSET(isoptional), TFlag, |
| DwarfAttrLanguage, OFFSET(language), TConstant, |
| DwarfAttrLocation, OFFSET(location), TBlock|TConstant, |
| DwarfAttrLowerBound, OFFSET(lowerbound), TConstant|TReference, |
| DwarfAttrLowpc, OFFSET(lowpc), TAddress, |
| DwarfAttrMacroInfo, OFFSET(macroinfo), TConstant, |
| DwarfAttrName, OFFSET(name), TString, |
| DwarfAttrNamelistItem, OFFSET(namelistitem), TBlock, |
| DwarfAttrOrdering, OFFSET(ordering), TConstant, |
| DwarfAttrPriority, OFFSET(priority), TReference, |
| DwarfAttrProducer, OFFSET(producer), TString, |
| DwarfAttrPrototyped, OFFSET(isprototyped), TFlag, |
| DwarfAttrRanges, OFFSET(ranges), TReference, |
| DwarfAttrReturnAddr, OFFSET(returnaddr), TBlock|TConstant, |
| DwarfAttrSegment, OFFSET(segment), TBlock|TConstant, |
| DwarfAttrSibling, OFFSET(sibling), TReference, |
| DwarfAttrSpecification, OFFSET(specification), TReference, |
| DwarfAttrStartScope, OFFSET(startscope), TConstant, |
| DwarfAttrStaticLink, OFFSET(staticlink), TBlock|TConstant, |
| DwarfAttrStmtList, OFFSET(stmtlist), TConstant, |
| DwarfAttrStrideSize, OFFSET(stridesize), TConstant, |
| DwarfAttrStringLength, OFFSET(stringlength), TBlock|TConstant, |
| DwarfAttrType, OFFSET(type), TReference, |
| DwarfAttrUpperBound, OFFSET(upperbound), TConstant|TReference, |
| DwarfAttrUseLocation, OFFSET(uselocation), TBlock|TConstant, |
| DwarfAttrVarParam, OFFSET(isvarparam), TFlag, |
| DwarfAttrVirtuality, OFFSET(virtuality), TConstant, |
| DwarfAttrVisibility, OFFSET(visibility), TConstant, |
| DwarfAttrVtableElemLoc, OFFSET(vtableelemloc), TBlock|TReference, |
| }; |
| |
| static Parse ptab[DwarfAttrMax]; |
| |
| static int |
| parseattrs(DwarfBuf *b, ulong unit, DwarfAbbrev *a, DwarfAttrs *attrs) |
| { |
| int i, f, n, got; |
| static int nbad; |
| void *v; |
| |
| /* initialize ptab first time through for quick access */ |
| if(ptab[DwarfAttrName].name != DwarfAttrName) |
| for(i=0; i<nelem(plist); i++) |
| ptab[plist[i].name] = plist[i]; |
| |
| memset(attrs, 0, sizeof *attrs); |
| attrs->tag = a->tag; |
| attrs->haskids = a->haskids; |
| |
| for(i=0; i<a->nattr; i++){ |
| n = a->attr[i].name; |
| f = a->attr[i].form; |
| if(n < 0 || n >= nelem(ptab) || ptab[n].name==0){ |
| if(++nbad == 1) |
| fprint(2, "dwarf parse attrs: unexpected attribute name 0x%ux\n", n); |
| return -1; |
| } |
| v = (char*)attrs + ptab[n].off; |
| got = 0; |
| if(f == FormIndirect) |
| f = dwarfget128(b); |
| if((ptab[n].type&(TConstant|TReference|TAddress)) |
| && getulong(b, f, unit, v, &got) >= 0) |
| ; |
| else if((ptab[n].type&TFlag) && getuchar(b, f, v) >= 0) |
| got = TFlag; |
| else if((ptab[n].type&TString) && getstring(b, f, v) >= 0) |
| got = TString; |
| else if((ptab[n].type&TBlock) && getblock(b, f, v) >= 0) |
| got = TBlock; |
| else{ |
| if(skipform(b, f) < 0){ |
| if(++nbad == 1) |
| fprint(2, "dwarf parse attrs: cannot skip form %d\n", f); |
| return -1; |
| } |
| } |
| if(got == TBlock && (ptab[n].type&TConstant)) |
| got = constblock(b->d, v, v); |
| *((uchar*)attrs+ptab[n].haveoff) = got; |
| } |
| return 0; |
| } |
| |
| static int |
| getulong(DwarfBuf *b, int form, ulong unit, ulong *u, int *type) |
| { |
| static int nbad; |
| uvlong uv; |
| |
| switch(form){ |
| default: |
| return -1; |
| |
| /* addresses */ |
| case FormAddr: |
| *type = TAddress; |
| *u = dwarfgetaddr(b); |
| return 0; |
| |
| /* references */ |
| case FormRefAddr: |
| /* absolute ref in .debug_info */ |
| *type = TReference; |
| *u = dwarfgetaddr(b); |
| return 0; |
| case FormRef1: |
| *u = dwarfget1(b); |
| goto relativeref; |
| case FormRef2: |
| *u = dwarfget2(b); |
| goto relativeref; |
| case FormRef4: |
| *u = dwarfget4(b); |
| goto relativeref; |
| case FormRef8: |
| *u = dwarfget8(b); |
| goto relativeref; |
| case FormRefUdata: |
| *u = dwarfget128(b); |
| relativeref: |
| *u += unit; |
| *type = TReference; |
| return 0; |
| |
| /* constants */ |
| case FormData1: |
| *u = dwarfget1(b); |
| goto constant; |
| case FormData2: |
| *u = dwarfget2(b); |
| goto constant; |
| case FormData4: |
| *u = dwarfget4(b); |
| goto constant; |
| case FormData8: |
| uv = dwarfget8(b); |
| *u = uv; |
| if(uv != *u && ++nbad == 1) |
| fprint(2, "dwarf: truncating 64-bit attribute constants\n"); |
| goto constant; |
| case FormSdata: |
| *u = dwarfget128s(b); |
| goto constant; |
| case FormUdata: |
| *u = dwarfget128(b); |
| constant: |
| *type = TConstant; |
| return 0; |
| } |
| } |
| |
| static int |
| getuchar(DwarfBuf *b, int form, uchar *u) |
| { |
| switch(form){ |
| default: |
| return -1; |
| |
| case FormFlag: |
| *u = dwarfget1(b); |
| return 0; |
| } |
| } |
| |
| static int |
| getstring(DwarfBuf *b, int form, char **s) |
| { |
| static int nbad; |
| ulong u; |
| |
| switch(form){ |
| default: |
| return -1; |
| |
| case FormString: |
| *s = dwarfgetstring(b); |
| return 0; |
| |
| case FormStrp: |
| u = dwarfget4(b); |
| if(u >= b->d->str.len){ |
| if(++nbad == 1) |
| fprint(2, "dwarf: bad string pointer 0x%lux in attribute\n", u); |
| /* don't return error - maybe can proceed */ |
| *s = nil; |
| }else |
| *s = (char*)b->d->str.data + u; |
| return 0; |
| |
| } |
| } |
| |
| static int |
| getblock(DwarfBuf *b, int form, DwarfBlock *bl) |
| { |
| ulong n; |
| |
| switch(form){ |
| default: |
| return -1; |
| case FormDwarfBlock: |
| n = dwarfget128(b); |
| goto copyn; |
| case FormDwarfBlock1: |
| n = dwarfget1(b); |
| goto copyn; |
| case FormDwarfBlock2: |
| n = dwarfget2(b); |
| goto copyn; |
| case FormDwarfBlock4: |
| n = dwarfget4(b); |
| copyn: |
| bl->data = dwarfgetnref(b, n); |
| bl->len = n; |
| if(bl->data == nil) |
| return -1; |
| return 0; |
| } |
| } |
| |
| static int |
| constblock(Dwarf *d, DwarfBlock *bl, ulong *pval) |
| { |
| DwarfBuf b; |
| |
| memset(&b, 0, sizeof b); |
| b.p = bl->data; |
| b.ep = bl->data+bl->len; |
| b.d = d; |
| |
| switch(dwarfget1(&b)){ |
| case OpAddr: |
| *pval = dwarfgetaddr(&b); |
| return TConstant; |
| case OpConst1u: |
| *pval = dwarfget1(&b); |
| return TConstant; |
| case OpConst1s: |
| *pval = (schar)dwarfget1(&b); |
| return TConstant; |
| case OpConst2u: |
| *pval = dwarfget2(&b); |
| return TConstant; |
| case OpConst2s: |
| *pval = (s16int)dwarfget2(&b); |
| return TConstant; |
| case OpConst4u: |
| *pval = dwarfget4(&b); |
| return TConstant; |
| case OpConst4s: |
| *pval = (s32int)dwarfget4(&b); |
| return TConstant; |
| case OpConst8u: |
| *pval = (u64int)dwarfget8(&b); |
| return TConstant; |
| case OpConst8s: |
| *pval = (s64int)dwarfget8(&b); |
| return TConstant; |
| case OpConstu: |
| *pval = dwarfget128(&b); |
| return TConstant; |
| case OpConsts: |
| *pval = dwarfget128s(&b); |
| return TConstant; |
| case OpPlusUconst: |
| *pval = dwarfget128(&b); |
| return TConstant; |
| default: |
| return TBlock; |
| } |
| } |
| |
| /* last resort */ |
| static int |
| skipform(DwarfBuf *b, int form) |
| { |
| int type; |
| DwarfVal val; |
| |
| if(getulong(b, form, 0, &val.c, &type) < 0 |
| && getuchar(b, form, (uchar*)&val) < 0 |
| && getstring(b, form, &val.s) < 0 |
| && getblock(b, form, &val.b) < 0) |
| return -1; |
| return 0; |
| } |