dwarf_expr_test.go 10.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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"

15 16 17 18 19
	"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"
20 21 22 23
)

const defaultCFA = 0xc420051d00

24
func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) (*proc.BinaryInfo, *dwarf.Data) {
25
	abbrev, aranges, frame, info, line, pubnames, ranges, str, loc, err := dwb.Build()
J
Josh Soref 已提交
26
	assertNoError(err, t, "dwarfbuilder.Build")
27 28 29 30
	dwdata, err := dwarf.New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
	assertNoError(err, t, "creating dwarf")

	bi := proc.NewBinaryInfo("linux", "amd64")
31
	bi.LoadImageFromData(dwdata, frame, line, loc)
32

33
	return bi, dwdata
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
}

// 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)
		}
	}
}

A
aarzilli 已提交
82
func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, testCases map[string]uint16, fn *proc.Function) *proc.EvalScope {
83
	scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: fn}, Regs: regs, Mem: mem, BinInfo: bi}
84 85 86 87 88 89 90
	for name, value := range testCases {
		uintExprCheck(t, scope, name, uint64(value))
	}

	return scope
}

91
func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfRegisters {
92
	a := proc.AMD64Arch("linux")
93 94
	so := bi.PCToImage(regs.PC())
	dwarfRegs := a.RegistersToDwarfRegisters(so.StaticBase, regs)
95 96 97 98 99
	dwarfRegs.CFA = defaultCFA
	dwarfRegs.FrameBase = defaultCFA
	return dwarfRegs
}

100 101 102 103 104 105 106 107 108
func TestDwarfExprRegisters(t *testing.T) {
	testCases := map[string]uint16{
		"a": 0x1234,
		"b": 0x4321,
		"c": 0x2143,
	}

	dwb := dwarfbuilder.New()

109
	uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
110

111
	dwb.AddSubprogram("main.main", 0x40100, 0x41000)
112
	dwb.Attr(dwarf.AttrFrameBase, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa))
113 114 115
	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)))
116 117
	dwb.TagClose()

118
	bi, _ := fakeBinaryInfo(t, dwb)
119

A
aarzilli 已提交
120 121
	mainfn := bi.LookupFunc["main.main"]

122
	mem := newFakeMemory(defaultCFA, uint64(0), uint64(testCases["b"]), uint16(testCases["pair.v"]))
123 124 125
	regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}}
	regs.Regs.Rax = uint64(testCases["a"])
	regs.Regs.Rdx = uint64(testCases["c"])
126

127
	dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn)
128 129 130 131 132 133
}

func TestDwarfExprComposite(t *testing.T) {
	testCases := map[string]uint16{
		"pair.k": 0x8765,
		"pair.v": 0x5678,
134
		"n":      42,
135 136 137 138 139 140
	}

	const stringVal = "this is a string"

	dwb := dwarfbuilder.New()

141 142
	uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
	intoff := dwb.AddBaseType("int", dwarfbuilder.DW_ATE_signed, 8)
143

144
	byteoff := dwb.AddBaseType("uint8", dwarfbuilder.DW_ATE_unsigned, 1)
145

146
	byteptroff := dwb.AddPointerType("*uint8", byteoff)
147

148
	pairoff := dwb.AddStructType("main.pair", 4)
149
	dwb.Attr(godwarf.AttrGoKind, uint8(25))
150 151
	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)))
152 153
	dwb.TagClose()

154
	stringoff := dwb.AddStructType("string", 16)
155
	dwb.Attr(godwarf.AttrGoKind, uint8(24))
156 157
	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)))
158 159
	dwb.TagClose()

160 161
	dwb.AddSubprogram("main.main", 0x40100, 0x41000)
	dwb.AddVariable("pair", pairoff, dwarfbuilder.LocationBlock(
162 163
		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)))
164
	dwb.AddVariable("s", stringoff, dwarfbuilder.LocationBlock(
165 166
		op.DW_OP_reg1, op.DW_OP_piece, uint(8),
		op.DW_OP_reg0, op.DW_OP_piece, uint(8)))
167
	dwb.AddVariable("n", intoff, dwarfbuilder.LocationBlock(op.DW_OP_reg3))
168 169
	dwb.TagClose()

170
	bi, _ := fakeBinaryInfo(t, dwb)
171

A
aarzilli 已提交
172 173
	mainfn := bi.LookupFunc["main.main"]

174
	mem := newFakeMemory(defaultCFA, uint64(0), uint64(0), uint16(testCases["pair.v"]), []byte(stringVal))
175 176 177 178 179 180
	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"])
181

182
	scope := dwarfExprCheck(t, mem, dwarfRegisters(bi, &regs), bi, testCases, mainfn)
183 184

	thevar, err := scope.EvalExpression("s", normalLoadConfig)
185
	assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s"))
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
	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()

201
	uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
202

203 204
	dwb.AddSubprogram("main.main", 0x40100, 0x41000)
	dwb.AddVariable("a", uint16off, []dwarfbuilder.LocEntry{
205 206 207 208 209
		{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()

210
	bi, _ := fakeBinaryInfo(t, dwb)
211

A
aarzilli 已提交
212 213
	mainfn := bi.LookupFunc["main.main"]

214
	mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
215 216
	const PC = 0x40100
	regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{Rip: PC}}
217

218
	scope := &proc.EvalScope{Location: proc.Location{PC: PC, Fn: mainfn}, Regs: dwarfRegisters(bi, &regs), Mem: mem, BinInfo: bi}
219 220 221

	uintExprCheck(t, scope, "a", before)
	scope.PC = 0x40800
222
	scope.Regs.Regs[scope.Regs.PCRegNum].Uint64Val = scope.PC
223 224
	uintExprCheck(t, scope, "a", after)
}
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246

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

247
	bi, _ := fakeBinaryInfo(t, dwb)
248 249 250 251 252

	mainfn := bi.LookupFunc["main.main"]

	mem := newFakeMemory(defaultCFA)

253
	scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: mainfn}, Regs: op.DwarfRegisters{}, Mem: mem, BinInfo: bi}
254 255 256 257 258 259 260 261 262 263 264 265

	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)
	}
}
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297

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

}
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312

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