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:
parent
3c86d68a99
commit
f5d5a681d0
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user