diff --git a/pkg/dwarf/dwarfbuilder/info.go b/pkg/dwarf/dwarfbuilder/info.go index 7d264b01..31eb15b0 100644 --- a/pkg/dwarf/dwarfbuilder/info.go +++ b/pkg/dwarf/dwarfbuilder/info.go @@ -123,10 +123,11 @@ func (b *Builder) TagClose() { } // Attr adds an attribute to the current DIE. -func (b *Builder) Attr(attr dwarf.Attr, val interface{}) { +func (b *Builder) Attr(attr dwarf.Attr, val interface{}) dwarf.Offset { if len(b.tagStack) < 0 { panic("Attr with no open tags") } + off := dwarf.Offset(b.info.Len()) tag := b.tagStack[len(b.tagStack)-1] if tag.children { panic("Can't add attributes after adding children") @@ -179,6 +180,16 @@ func (b *Builder) Attr(attr dwarf.Attr, val interface{}) { default: panic("unknown value type") } + + return off +} + +// PatchOffset writes the offset 'patch' at offset patchedOffset. +func (b *Builder) PatchOffset(patchedOffset, patch dwarf.Offset) { + infoBytes := b.info.Bytes() + buf := new(bytes.Buffer) + binary.Write(buf, binary.LittleEndian, patch) + copy(infoBytes[patchedOffset:], buf.Bytes()) } func sameTagDescr(a, b tagDescr) bool { diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index a4f2bb14..196280f4 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -1787,18 +1787,15 @@ func (bi *BinaryInfo) addAbstractSubprogram(entry *dwarf.Entry, ctxt *loadDebugI return } - fn := Function{ - Name: name, - offset: entry.Offset, - cu: cu, - } - if entry.Children { bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) } - bi.Functions = append(bi.Functions, fn) - ctxt.abstractOriginTable[entry.Offset] = len(bi.Functions) - 1 + originIdx := ctxt.lookupAbstractOrigin(bi, entry.Offset) + fn := &bi.Functions[originIdx] + fn.Name = name + fn.offset = entry.Offset + fn.cu = cu } // addConcreteInlinedSubprogram adds the concrete entry of a subprogram that was also inlined. @@ -1812,15 +1809,7 @@ func (bi *BinaryInfo) addConcreteInlinedSubprogram(entry *dwarf.Entry, originOff return } - originIdx, ok := ctxt.abstractOriginTable[originOffset] - if !ok { - bi.logger.Warnf("reading debug_info: could not find abstract origin of concrete inlined subprogram at %#x (origin offset %#x)", entry.Offset, originOffset) - if entry.Children { - reader.SkipChildren() - } - return - } - + originIdx := ctxt.lookupAbstractOrigin(bi, originOffset) fn := &bi.Functions[originIdx] fn.offset = entry.Offset fn.Entry = lowpc @@ -1905,12 +1894,7 @@ func (bi *BinaryInfo) loadDebugInfoMapsInlinedCalls(ctxt *loadDebugInfoMapsConte continue } - originIdx, ok := ctxt.abstractOriginTable[originOffset] - if !ok { - bi.logger.Warnf("reading debug_info: could not find abstract origin (%#x) of inlined call at %#x", originOffset, entry.Offset) - reader.SkipChildren() - continue - } + originIdx := ctxt.lookupAbstractOrigin(bi, originOffset) fn := &bi.Functions[originIdx] lowpc, highpc, ok := subprogramEntryRange(entry, cu.image) diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go index f5e34173..0ce6c83e 100644 --- a/pkg/proc/dwarf_expr_test.go +++ b/pkg/proc/dwarf_expr_test.go @@ -353,3 +353,43 @@ func TestNestedCompileUnts(t *testing.T) { t.Errorf("expected 2 variables, got %d", n) } } + +func TestAbstractOriginDefinedAfterUse(t *testing.T) { + // Tests that an abstract origin entry can appear after its uses. + dwb := dwarfbuilder.New() + dwb.AddCompileUnit("main", 0x0) + + // Concrete implementation + dwb.TagOpen(dwarf.TagSubprogram, "") + originRef1 := dwb.Attr(dwarf.AttrAbstractOrigin, dwarf.Offset(0)) + dwb.Attr(dwarf.AttrLowpc, dwarfbuilder.Address(0x40100)) + dwb.Attr(dwarf.AttrHighpc, dwarfbuilder.Address(0x41000)) + dwb.TagClose() + + // Inlined call + dwb.AddSubprogram("callingFn", 0x41100, 0x42000) + dwb.TagOpen(dwarf.TagInlinedSubroutine, "") + originRef2 := dwb.Attr(dwarf.AttrAbstractOrigin, dwarf.Offset(0)) + dwb.Attr(dwarf.AttrLowpc, dwarfbuilder.Address(0x41150)) + dwb.Attr(dwarf.AttrHighpc, dwarfbuilder.Address(0x41155)) + dwb.Attr(dwarf.AttrCallFile, uint8(1)) + dwb.Attr(dwarf.AttrCallLine, uint8(1)) + dwb.TagClose() + dwb.TagClose() + + // Abstract origin + abstractOriginOff := dwb.TagOpen(dwarf.TagSubprogram, "inlinedFn") + dwb.Attr(dwarf.AttrInline, uint8(1)) + dwb.TagClose() + + dwb.TagClose() + + dwb.PatchOffset(originRef1, abstractOriginOff) + dwb.PatchOffset(originRef2, abstractOriginOff) + + bi, _ := fakeBinaryInfo(t, dwb) + fn := bi.PCToFunc(0x40100) + if fn == nil { + t.Fatalf("could not find concrete instance of inlined function") + } +} diff --git a/pkg/proc/types.go b/pkg/proc/types.go index 16d1954e..31bb8489 100644 --- a/pkg/proc/types.go +++ b/pkg/proc/types.go @@ -98,6 +98,16 @@ func newLoadDebugInfoMapsContext(bi *BinaryInfo, image *Image, offsetToVersion m return ctxt } +func (ctxt *loadDebugInfoMapsContext) lookupAbstractOrigin(bi *BinaryInfo, off dwarf.Offset) int { + r, ok := ctxt.abstractOriginTable[off] + if !ok { + bi.Functions = append(bi.Functions, Function{}) + r = len(bi.Functions) - 1 + ctxt.abstractOriginTable[off] = r + } + return r +} + // runtimeTypeToDIE returns the DIE corresponding to the runtime._type. // This is done in three different ways depending on the version of go. // * Before go1.7 the type name is retrieved directly from the runtime._type