delve/pkg/proc/dwarf_expr_test.go

310 lines
10 KiB
Go
Raw Normal View History

// Tests for loading variables that have complex location expressions. They
// are only produced for optimized code (for both Go and C) therefore we can
// not get the compiler to produce them reliably enough for tests.
package proc_test
import (
"bytes"
"debug/dwarf"
"encoding/binary"
"fmt"
"go/constant"
"testing"
"github.com/go-delve/delve/pkg/dwarf/dwarfbuilder"
"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/proc"
"github.com/go-delve/delve/pkg/proc/linutil"
)
const defaultCFA = 0xc420051d00
func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) (*proc.BinaryInfo, *dwarf.Data) {
abbrev, aranges, frame, info, line, pubnames, ranges, str, loc, err := dwb.Build()
2018-03-20 10:05:35 +00:00
assertNoError(err, t, "dwarfbuilder.Build")
dwdata, err := dwarf.New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
assertNoError(err, t, "creating dwarf")
bi := proc.NewBinaryInfo("linux", "amd64")
proc: support debugging plugins (#1414) This change splits the BinaryInfo object into a slice of Image objects containing information about the base executable and each loaded shared library (note: go plugins are shared libraries). Delve backens are supposed to call BinaryInfo.AddImage whenever they detect that a new shared library has been loaded. Member fields of BinaryInfo that are used to speed up access to dwarf (Functions, packageVars, consts, etc...) remain part of BinaryInfo and are updated to reference the correct image object. This simplifies this change. This approach has a few shortcomings: 1. Multiple shared libraries can define functions or globals with the same name and we have no way to disambiguate between them. 2. We don't have a way to handle library unloading. Both of those affect C shared libraries much more than they affect go plugins. Go plugins can't be unloaded at all and a lot of name collisions are prevented by import paths. There's only one problem that is concerning: if two plugins both import the same package they will end up with multiple definition for the same function. For example if two plugins use fmt.Printf the final in-memory image (and therefore our BinaryInfo object) will end up with two copies of fmt.Printf at different memory addresses. If a user types break fmt.Printf a breakpoint should be created at *both* locations. Allowing this is a relatively complex change that should be done in a different PR than this. For this reason I consider this approach an acceptable and sustainable stopgap. Updates #865
2019-05-08 21:06:38 +00:00
bi.LoadImageFromData(dwdata, frame, line, loc)
return bi, dwdata
}
// fakeMemory implements proc.MemoryReadWriter by reading from a byte slice.
// Byte 0 of "data" is at address "base".
type fakeMemory struct {
base uint64
data []byte
}
func newFakeMemory(base uint64, contents ...interface{}) *fakeMemory {
mem := &fakeMemory{base: base}
var buf bytes.Buffer
for _, x := range contents {
binary.Write(&buf, binary.LittleEndian, x)
}
mem.data = buf.Bytes()
return mem
}
func (mem *fakeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
if uint64(addr) < mem.base {
return 0, fmt.Errorf("read out of bounds %d %#x", len(data), addr)
}
start := uint64(addr) - mem.base
end := uint64(len(data)) + start
if end > uint64(len(mem.data)) {
panic(fmt.Errorf("read out of bounds %d %#x", len(data), addr))
}
copy(data, mem.data[start:end])
return len(data), nil
}
func (mem *fakeMemory) WriteMemory(uintptr, []byte) (int, error) {
return 0, fmt.Errorf("not implemented")
}
func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64) {
thevar, err := scope.EvalExpression(expr, normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", expr))
if thevar.Unreadable != nil {
t.Errorf("variable %q unreadable: %v", expr, thevar.Unreadable)
} else {
if v, _ := constant.Uint64Val(thevar.Value); v != tgt {
t.Errorf("expected value %x got %x for %q", tgt, v, expr)
}
}
}
proc: support inlining Go 1.10 added inlined calls to debug_info, this commit adds support for DW_TAG_inlined_call to delve, both for stack traces (where inlined calls will appear as normal stack frames) and to correct the behavior of next, step and stepout. The calls to Next and Frame of stackIterator continue to work unchanged and only return real stack frames, after reading each line appendInlinedCalls is called to unpacked all the inlined calls that involve the current PC. The fake stack frames produced by appendInlinedCalls are distinguished from real stack frames by having the Inlined attribute set to true. Also their Current and Call locations are treated differently. The Call location will be changed to represent the position inside the inlined call, while the Current location will always reference the real stack frame. This is done because: * next, step and stepout need to access the debug_info entry of the real function they are stepping through * we are already manipulating Call in different ways while Current is just what we read from the call stack The strategy remains mostly the same, we disassemble the function and we set a breakpoint on each instruction corresponding to a different file:line. The function in question will be the one corresponding to the first real (i.e. non-inlined) stack frame. * If the current function contains inlined calls, 'next' will not set any breakpoints on instructions that belong to inlined calls. We do not do this for 'step'. * If we are inside an inlined call that makes other inlined functions, 'next' will not set any breakpoints that belong to inlined calls that are children of the current inlined call. * If the current function is inlined the breakpoint on the return address won't be set, because inlined frames don't have a return address. * The code we use for stepout doesn't work at all if we are inside an inlined call, instead we call 'next' but instruct it to remove all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, testCases map[string]uint16, fn *proc.Function) *proc.EvalScope {
scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi}
for name, value := range testCases {
uintExprCheck(t, scope, name, uint64(value))
}
return scope
}
proc: support debugging plugins (#1414) This change splits the BinaryInfo object into a slice of Image objects containing information about the base executable and each loaded shared library (note: go plugins are shared libraries). Delve backens are supposed to call BinaryInfo.AddImage whenever they detect that a new shared library has been loaded. Member fields of BinaryInfo that are used to speed up access to dwarf (Functions, packageVars, consts, etc...) remain part of BinaryInfo and are updated to reference the correct image object. This simplifies this change. This approach has a few shortcomings: 1. Multiple shared libraries can define functions or globals with the same name and we have no way to disambiguate between them. 2. We don't have a way to handle library unloading. Both of those affect C shared libraries much more than they affect go plugins. Go plugins can't be unloaded at all and a lot of name collisions are prevented by import paths. There's only one problem that is concerning: if two plugins both import the same package they will end up with multiple definition for the same function. For example if two plugins use fmt.Printf the final in-memory image (and therefore our BinaryInfo object) will end up with two copies of fmt.Printf at different memory addresses. If a user types break fmt.Printf a breakpoint should be created at *both* locations. Allowing this is a relatively complex change that should be done in a different PR than this. For this reason I consider this approach an acceptable and sustainable stopgap. Updates #865
2019-05-08 21:06:38 +00:00
func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfRegisters {
a := proc.AMD64Arch("linux")
proc: support debugging plugins (#1414) This change splits the BinaryInfo object into a slice of Image objects containing information about the base executable and each loaded shared library (note: go plugins are shared libraries). Delve backens are supposed to call BinaryInfo.AddImage whenever they detect that a new shared library has been loaded. Member fields of BinaryInfo that are used to speed up access to dwarf (Functions, packageVars, consts, etc...) remain part of BinaryInfo and are updated to reference the correct image object. This simplifies this change. This approach has a few shortcomings: 1. Multiple shared libraries can define functions or globals with the same name and we have no way to disambiguate between them. 2. We don't have a way to handle library unloading. Both of those affect C shared libraries much more than they affect go plugins. Go plugins can't be unloaded at all and a lot of name collisions are prevented by import paths. There's only one problem that is concerning: if two plugins both import the same package they will end up with multiple definition for the same function. For example if two plugins use fmt.Printf the final in-memory image (and therefore our BinaryInfo object) will end up with two copies of fmt.Printf at different memory addresses. If a user types break fmt.Printf a breakpoint should be created at *both* locations. Allowing this is a relatively complex change that should be done in a different PR than this. For this reason I consider this approach an acceptable and sustainable stopgap. Updates #865
2019-05-08 21:06:38 +00:00
dwarfRegs := a.RegistersToDwarfRegisters(bi, regs)
dwarfRegs.CFA = defaultCFA
dwarfRegs.FrameBase = defaultCFA
return dwarfRegs
}
func TestDwarfExprRegisters(t *testing.T) {
testCases := map[string]uint16{
"a": 0x1234,
"b": 0x4321,
"c": 0x2143,
}
dwb := dwarfbuilder.New()
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.Attr(dwarf.AttrFrameBase, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa))
dwb.AddVariable("a", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_reg0))
dwb.AddVariable("b", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_fbreg, int(8)))
dwb.AddVariable("c", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_regx, int(1)))
dwb.TagClose()
bi, _ := fakeBinaryInfo(t, dwb)
proc: support inlining Go 1.10 added inlined calls to debug_info, this commit adds support for DW_TAG_inlined_call to delve, both for stack traces (where inlined calls will appear as normal stack frames) and to correct the behavior of next, step and stepout. The calls to Next and Frame of stackIterator continue to work unchanged and only return real stack frames, after reading each line appendInlinedCalls is called to unpacked all the inlined calls that involve the current PC. The fake stack frames produced by appendInlinedCalls are distinguished from real stack frames by having the Inlined attribute set to true. Also their Current and Call locations are treated differently. The Call location will be changed to represent the position inside the inlined call, while the Current location will always reference the real stack frame. This is done because: * next, step and stepout need to access the debug_info entry of the real function they are stepping through * we are already manipulating Call in different ways while Current is just what we read from the call stack The strategy remains mostly the same, we disassemble the function and we set a breakpoint on each instruction corresponding to a different file:line. The function in question will be the one corresponding to the first real (i.e. non-inlined) stack frame. * If the current function contains inlined calls, 'next' will not set any breakpoints on instructions that belong to inlined calls. We do not do this for 'step'. * If we are inside an inlined call that makes other inlined functions, 'next' will not set any breakpoints that belong to inlined calls that are children of the current inlined call. * If the current function is inlined the breakpoint on the return address won't be set, because inlined frames don't have a return address. * The code we use for stepout doesn't work at all if we are inside an inlined call, instead we call 'next' but instruct it to remove all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
mainfn := bi.LookupFunc["main.main"]
mem := newFakeMemory(defaultCFA, uint64(0), uint64(testCases["b"]), uint16(testCases["pair.v"]))
regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}}
regs.Regs.Rax = uint64(testCases["a"])
regs.Regs.Rdx = uint64(testCases["c"])
proc: support debugging plugins (#1414) This change splits the BinaryInfo object into a slice of Image objects containing information about the base executable and each loaded shared library (note: go plugins are shared libraries). Delve backens are supposed to call BinaryInfo.AddImage whenever they detect that a new shared library has been loaded. Member fields of BinaryInfo that are used to speed up access to dwarf (Functions, packageVars, consts, etc...) remain part of BinaryInfo and are updated to reference the correct image object. This simplifies this change. This approach has a few shortcomings: 1. Multiple shared libraries can define functions or globals with the same name and we have no way to disambiguate between them. 2. We don't have a way to handle library unloading. Both of those affect C shared libraries much more than they affect go plugins. Go plugins can't be unloaded at all and a lot of name collisions are prevented by import paths. There's only one problem that is concerning: if two plugins both import the same package they will end up with multiple definition for the same function. For example if two plugins use fmt.Printf the final in-memory image (and therefore our BinaryInfo object) will end up with two copies of fmt.Printf at different memory addresses. If a user types break fmt.Printf a breakpoint should be created at *both* locations. Allowing this is a relatively complex change that should be done in a different PR than this. For this reason I consider this approach an acceptable and sustainable stopgap. Updates #865
2019-05-08 21:06:38 +00:00
dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn)
}
func TestDwarfExprComposite(t *testing.T) {
testCases := map[string]uint16{
"pair.k": 0x8765,
"pair.v": 0x5678,
"n": 42,
}
const stringVal = "this is a string"
dwb := dwarfbuilder.New()
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
intoff := dwb.AddBaseType("int", dwarfbuilder.DW_ATE_signed, 8)
byteoff := dwb.AddBaseType("uint8", dwarfbuilder.DW_ATE_unsigned, 1)
byteptroff := dwb.AddPointerType("*uint8", byteoff)
pairoff := dwb.AddStructType("main.pair", 4)
dwb.Attr(godwarf.AttrGoKind, uint8(25))
dwb.AddMember("k", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
dwb.AddMember("v", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(2)))
dwb.TagClose()
stringoff := dwb.AddStructType("string", 16)
dwb.Attr(godwarf.AttrGoKind, uint8(24))
dwb.AddMember("str", byteptroff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
dwb.AddMember("len", intoff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(8)))
dwb.TagClose()
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.AddVariable("pair", pairoff, dwarfbuilder.LocationBlock(
op.DW_OP_reg2, op.DW_OP_piece, uint(2),
op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(16), op.DW_OP_plus, op.DW_OP_piece, uint(2)))
dwb.AddVariable("s", stringoff, dwarfbuilder.LocationBlock(
op.DW_OP_reg1, op.DW_OP_piece, uint(8),
op.DW_OP_reg0, op.DW_OP_piece, uint(8)))
dwb.AddVariable("n", intoff, dwarfbuilder.LocationBlock(op.DW_OP_reg3))
dwb.TagClose()
bi, _ := fakeBinaryInfo(t, dwb)
proc: support inlining Go 1.10 added inlined calls to debug_info, this commit adds support for DW_TAG_inlined_call to delve, both for stack traces (where inlined calls will appear as normal stack frames) and to correct the behavior of next, step and stepout. The calls to Next and Frame of stackIterator continue to work unchanged and only return real stack frames, after reading each line appendInlinedCalls is called to unpacked all the inlined calls that involve the current PC. The fake stack frames produced by appendInlinedCalls are distinguished from real stack frames by having the Inlined attribute set to true. Also their Current and Call locations are treated differently. The Call location will be changed to represent the position inside the inlined call, while the Current location will always reference the real stack frame. This is done because: * next, step and stepout need to access the debug_info entry of the real function they are stepping through * we are already manipulating Call in different ways while Current is just what we read from the call stack The strategy remains mostly the same, we disassemble the function and we set a breakpoint on each instruction corresponding to a different file:line. The function in question will be the one corresponding to the first real (i.e. non-inlined) stack frame. * If the current function contains inlined calls, 'next' will not set any breakpoints on instructions that belong to inlined calls. We do not do this for 'step'. * If we are inside an inlined call that makes other inlined functions, 'next' will not set any breakpoints that belong to inlined calls that are children of the current inlined call. * If the current function is inlined the breakpoint on the return address won't be set, because inlined frames don't have a return address. * The code we use for stepout doesn't work at all if we are inside an inlined call, instead we call 'next' but instruct it to remove all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
mainfn := bi.LookupFunc["main.main"]
mem := newFakeMemory(defaultCFA, uint64(0), uint64(0), uint16(testCases["pair.v"]), []byte(stringVal))
var regs linutil.AMD64Registers
regs.Regs = &linutil.AMD64PtraceRegs{}
regs.Regs.Rax = uint64(len(stringVal))
regs.Regs.Rdx = defaultCFA + 18
regs.Regs.Rcx = uint64(testCases["pair.k"])
regs.Regs.Rbx = uint64(testCases["n"])
proc: support debugging plugins (#1414) This change splits the BinaryInfo object into a slice of Image objects containing information about the base executable and each loaded shared library (note: go plugins are shared libraries). Delve backens are supposed to call BinaryInfo.AddImage whenever they detect that a new shared library has been loaded. Member fields of BinaryInfo that are used to speed up access to dwarf (Functions, packageVars, consts, etc...) remain part of BinaryInfo and are updated to reference the correct image object. This simplifies this change. This approach has a few shortcomings: 1. Multiple shared libraries can define functions or globals with the same name and we have no way to disambiguate between them. 2. We don't have a way to handle library unloading. Both of those affect C shared libraries much more than they affect go plugins. Go plugins can't be unloaded at all and a lot of name collisions are prevented by import paths. There's only one problem that is concerning: if two plugins both import the same package they will end up with multiple definition for the same function. For example if two plugins use fmt.Printf the final in-memory image (and therefore our BinaryInfo object) will end up with two copies of fmt.Printf at different memory addresses. If a user types break fmt.Printf a breakpoint should be created at *both* locations. Allowing this is a relatively complex change that should be done in a different PR than this. For this reason I consider this approach an acceptable and sustainable stopgap. Updates #865
2019-05-08 21:06:38 +00:00
scope := dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn)
thevar, err := scope.EvalExpression("s", normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s"))
if thevar.Unreadable != nil {
t.Errorf("variable \"s\" unreadable: %v", thevar.Unreadable)
} else {
if v := constant.StringVal(thevar.Value); v != stringVal {
t.Errorf("expected value %q got %q", stringVal, v)
}
}
}
func TestDwarfExprLoclist(t *testing.T) {
const before = 0x1234
const after = 0x4321
dwb := dwarfbuilder.New()
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.AddVariable("a", uint16off, []dwarfbuilder.LocEntry{
{0x40100, 0x40700, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa)},
{0x40700, 0x41000, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(2), op.DW_OP_plus)},
})
dwb.TagClose()
bi, _ := fakeBinaryInfo(t, dwb)
proc: support inlining Go 1.10 added inlined calls to debug_info, this commit adds support for DW_TAG_inlined_call to delve, both for stack traces (where inlined calls will appear as normal stack frames) and to correct the behavior of next, step and stepout. The calls to Next and Frame of stackIterator continue to work unchanged and only return real stack frames, after reading each line appendInlinedCalls is called to unpacked all the inlined calls that involve the current PC. The fake stack frames produced by appendInlinedCalls are distinguished from real stack frames by having the Inlined attribute set to true. Also their Current and Call locations are treated differently. The Call location will be changed to represent the position inside the inlined call, while the Current location will always reference the real stack frame. This is done because: * next, step and stepout need to access the debug_info entry of the real function they are stepping through * we are already manipulating Call in different ways while Current is just what we read from the call stack The strategy remains mostly the same, we disassemble the function and we set a breakpoint on each instruction corresponding to a different file:line. The function in question will be the one corresponding to the first real (i.e. non-inlined) stack frame. * If the current function contains inlined calls, 'next' will not set any breakpoints on instructions that belong to inlined calls. We do not do this for 'step'. * If we are inside an inlined call that makes other inlined functions, 'next' will not set any breakpoints that belong to inlined calls that are children of the current inlined call. * If the current function is inlined the breakpoint on the return address won't be set, because inlined frames don't have a return address. * The code we use for stepout doesn't work at all if we are inside an inlined call, instead we call 'next' but instruct it to remove all PCs belonging to the current inlined call.
2017-11-13 15:54:08 +00:00
mainfn := bi.LookupFunc["main.main"]
mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}}
scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: mainfn}, Regs: dwarfRegisters(bi, &regs), Mem: mem, BinInfo: bi}
uintExprCheck(t, scope, "a", before)
scope.PC = 0x40800
uintExprCheck(t, scope, "a", after)
}
func TestIssue1419(t *testing.T) {
// trying to read a slice variable with a location list that tries to read
// from registers we don't have should not cause a panic.
dwb := dwarfbuilder.New()
uint64off := dwb.AddBaseType("uint64", dwarfbuilder.DW_ATE_unsigned, 8)
intoff := dwb.AddBaseType("int", dwarfbuilder.DW_ATE_signed, 8)
intptroff := dwb.AddPointerType("*int", intoff)
sliceoff := dwb.AddStructType("[]int", 24)
dwb.Attr(godwarf.AttrGoKind, uint8(23))
dwb.AddMember("array", intptroff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
dwb.AddMember("len", uint64off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(8)))
dwb.AddMember("cap", uint64off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(16)))
dwb.TagClose()
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.AddVariable("a", sliceoff, dwarfbuilder.LocationBlock(op.DW_OP_reg2, op.DW_OP_piece, uint(8), op.DW_OP_reg2, op.DW_OP_piece, uint(8), op.DW_OP_reg2, op.DW_OP_piece, uint(8)))
dwb.TagClose()
bi, _ := fakeBinaryInfo(t, dwb)
mainfn := bi.LookupFunc["main.main"]
mem := newFakeMemory(defaultCFA)
scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: mainfn}, Regs: op.DwarfRegisters{}, Mem: mem, BinInfo: bi}
va, err := scope.EvalExpression("a", normalLoadConfig)
assertNoError(err, t, "EvalExpression(a)")
t.Logf("%#x\n", va.Addr)
t.Logf("%v", va)
if va.Unreadable == nil {
t.Fatalf("expected 'a' to be unreadable but it wasn't")
}
if va.Unreadable.Error() != "could not read 8 bytes from register 2 (size: 0)" {
t.Fatalf("wrong unreadable reason for variable 'a': %v", va.Unreadable)
}
}
func TestLocationCovers(t *testing.T) {
const before = 0x1234
const after = 0x4321
dwb := dwarfbuilder.New()
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
dwb.AddCompileUnit("main", 0x0)
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
aOff := dwb.AddVariable("a", uint16off, []dwarfbuilder.LocEntry{
{0x40100, 0x40700, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa)},
{0x40700, 0x41000, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(2), op.DW_OP_plus)},
})
dwb.TagClose()
dwb.TagClose()
bi, dwdata := fakeBinaryInfo(t, dwb)
dwrdr := dwdata.Reader()
dwrdr.Seek(aOff)
aEntry, err := dwrdr.Next()
assertNoError(err, t, "reading 'a' entry")
ranges, err := bi.LocationCovers(aEntry, dwarf.AttrLocation)
assertNoError(err, t, "LocationCovers")
t.Logf("%x", ranges)
if fmt.Sprintf("%x", ranges) != "[[40100 40700] [40700 41000]]" {
t.Error("wrong value returned by LocationCover")
}
}
func TestIssue1636_InlineWithoutOrigin(t *testing.T) {
// Gcc (specifically GNU C++11 6.3.0) will emit DW_TAG_inlined_subroutine
// without a DW_AT_abstract_origin or a name. What is an inlined subroutine
// without a reference to an abstract origin or even a name? Regardless,
// Delve shouldn't crash.
dwb := dwarfbuilder.New()
dwb.AddCompileUnit("main", 0x0)
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.TagOpen(dwarf.TagInlinedSubroutine, "")
dwb.TagClose()
dwb.TagClose()
dwb.TagClose()
fakeBinaryInfo(t, dwb)
}