proc: do not assume abstract origins precede their uses (#2293)

The DWARF standard does not say that a DW_ATTR_abstract_origin can only
reference entries that appear before it, this change fixes BinaryInfo
to comply. See #2284 for an example of this happening.
This commit is contained in:
Alessandro Arzilli 2021-01-27 15:58:48 +01:00 committed by GitHub
parent 3c86d68a99
commit f5d5a681d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 24 deletions

@ -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 {

@ -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)

@ -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")
}
}

@ -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