File: proc_amd64_test.go

package info (click to toggle)
delve 1.24.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 14,092 kB
  • sloc: ansic: 111,943; sh: 169; asm: 141; makefile: 43; python: 23
file content (131 lines) | stat: -rw-r--r-- 5,321 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package proc_test

import (
	"go/constant"
	"path/filepath"
	"runtime"
	"testing"

	"github.com/go-delve/delve/pkg/dwarf/regnum"
	"github.com/go-delve/delve/pkg/goversion"
	"github.com/go-delve/delve/pkg/proc"
	protest "github.com/go-delve/delve/pkg/proc/test"
)

func TestStepInstructionOnBreakpoint(t *testing.T) {
	// StepInstruction should step one instruction forward when
	// PC is on a 1 byte instruction with a software breakpoint.
	protest.AllowRecording(t)
	withTestProcess("break/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
		setFileBreakpoint(p, t, filepath.ToSlash(filepath.Join(fixture.BuildDir, "break_amd64.s")), 4)

		assertNoError(grp.Continue(), t, "Continue()")

		pc := getRegisters(p, t).PC()
		assertNoError(grp.StepInstruction(false), t, "StepInstruction()")
		if pc == getRegisters(p, t).PC() {
			t.Fatal("Could not step a single instruction")
		}
	})
}

func TestNextUnknownInstr(t *testing.T) {
	if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 10) {
		t.Skip("versions of Go before 1.10 can't assemble the instruction VPUNPCKLWD")
	}
	withTestProcess("nodisasm/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
		setFunctionBreakpoint(p, t, "main.asmFunc")
		assertNoError(grp.Continue(), t, "Continue()")
		assertNoError(grp.Next(), t, "Next()")
	})
}

func TestIssue1656(t *testing.T) {
	withTestProcess("issue1656/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
		setFileBreakpoint(p, t, filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s")), 5)
		assertNoError(grp.Continue(), t, "Continue()")
		t.Logf("step1\n")
		assertNoError(grp.Step(), t, "Step()")
		assertLineNumber(p, t, 8, "wrong line number after first step")
		t.Logf("step2\n")
		assertNoError(grp.Step(), t, "Step()")
		assertLineNumber(p, t, 9, "wrong line number after second step")
	})
}

func TestBreakpointConfusionOnResume(t *testing.T) {
	// Checks that SetCurrentBreakpoint, (*Thread).StepInstruction and
	// native.(*Thread).singleStep all agree on which breakpoint the thread is
	// stopped at.
	// This test checks for a regression introduced when fixing Issue #1656
	withTestProcess("nopbreakpoint/", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
		maindots := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.s"))
		maindotgo := filepath.ToSlash(filepath.Join(fixture.BuildDir, "main.go"))
		setFileBreakpoint(p, t, maindots, 5) // line immediately after the NOP
		assertNoError(grp.Continue(), t, "First Continue")
		assertLineNumber(p, t, 5, "not on main.s:5")
		setFileBreakpoint(p, t, maindots, 4)   // sets a breakpoint on the NOP line, which will be one byte before the breakpoint we currently are stopped at.
		setFileBreakpoint(p, t, maindotgo, 18) // set one extra breakpoint so that we can recover execution and check the global variable g
		assertNoError(grp.Continue(), t, "Second Continue")
		gvar := evalVariable(p, t, "g")
		if n, _ := constant.Int64Val(gvar.Value); n != 1 {
			t.Fatalf("wrong value of global variable 'g': %v (expected 1)", gvar.Value)
		}
	})
}

func TestCallInjectionFlagCorruption(t *testing.T) {
	// debugCallV2 has a bug in amd64 where its tail corrupts the FLAGS register by running an ADD instruction.
	// Since this problem exists in many versions of Go, instead of fixing
	// debugCallV2, we work around this problem by restoring FLAGS, one extra
	// time, after stepping out of debugCallV2.
	// Fixes issue https://github.com/go-delve/delve/issues/2985
	protest.MustSupportFunctionCalls(t, testBackend)

	withTestProcessArgs("badflags", t, ".", []string{"0"}, 0, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) {
		mainfn := p.BinInfo().LookupFunc()["main.main"][0]

		// Find JNZ instruction on line :14
		var addr uint64
		text, err := proc.Disassemble(p.Memory(), nil, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End)
		assertNoError(err, t, "Disassemble")
		for _, instr := range text {
			if instr.Loc.Line != 14 {
				continue
			}
			if proc.IsJNZ(instr.Inst) {
				addr = instr.Loc.PC
			}
		}
		if addr == 0 {
			t.Fatalf("Could not find JNZ instruction at line :14")
		}

		// Create breakpoint
		_, err = p.SetBreakpoint(0, addr, proc.UserBreakpoint, nil)
		assertNoError(err, t, "SetBreakpoint")

		// Continue to breakpoint
		assertNoError(grp.Continue(), t, "Continue()")
		assertLineNumber(p, t, 14, "expected line :14")

		// Save RFLAGS register
		rflagsBeforeCall := p.BinInfo().Arch.RegistersToDwarfRegisters(0, getRegisters(p, t)).Uint64Val(regnum.AMD64_Rflags)
		t.Logf("rflags before = %#x", rflagsBeforeCall)

		// Inject call to main.g()
		assertNoError(proc.EvalExpressionWithCalls(grp, p.SelectedGoroutine(), "g()", normalLoadConfig, true), t, "Call")

		// Check RFLAGS register after the call
		rflagsAfterCall := p.BinInfo().Arch.RegistersToDwarfRegisters(0, getRegisters(p, t)).Uint64Val(regnum.AMD64_Rflags)
		t.Logf("rflags after = %#x", rflagsAfterCall)

		if rflagsBeforeCall != rflagsAfterCall {
			t.Errorf("mismatched rflags value")
		}

		// Single step and check where we end up
		assertNoError(grp.Step(), t, "Step()")
		assertLineNumber(p, t, 17, "expected line :17") // since we passed "0" as argument we should be going into the false branch at line :17
	})
}