parent
72eeb5ae84
commit
aa0b4eb180
@ -322,14 +322,8 @@ func getLdEnvVars() []string {
|
|||||||
// it to launch the specified target program with the specified arguments
|
// it to launch the specified target program with the specified arguments
|
||||||
// (cmd) on the specified directory wd.
|
// (cmd) on the specified directory wd.
|
||||||
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) {
|
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*proc.Target, error) {
|
||||||
switch runtime.GOOS {
|
if runtime.GOOS == "windows" {
|
||||||
case "windows":
|
|
||||||
return nil, ErrUnsupportedOS
|
return nil, ErrUnsupportedOS
|
||||||
default:
|
|
||||||
// check that the argument to Launch is an executable file
|
|
||||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
|
||||||
return nil, proc.ErrNotExecutable
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if foreground {
|
if foreground {
|
||||||
|
@ -38,10 +38,6 @@ type osProcessDetails struct {
|
|||||||
// PT_SIGEXC on Darwin which will turn Unix signals into
|
// PT_SIGEXC on Darwin which will turn Unix signals into
|
||||||
// Mach exceptions.
|
// Mach exceptions.
|
||||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
|
||||||
// check that the argument to Launch is an executable file
|
|
||||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
|
||||||
return nil, proc.ErrNotExecutable
|
|
||||||
}
|
|
||||||
argv0Go, err := filepath.Abs(cmd[0])
|
argv0Go, err := filepath.Abs(cmd[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -48,10 +48,6 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
|||||||
process *exec.Cmd
|
process *exec.Cmd
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
// check that the argument to Launch is an executable file
|
|
||||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
|
||||||
return nil, proc.ErrNotExecutable
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
||||||
// exec.(*Process).Start will fail if we try to send a process to
|
// exec.(*Process).Start will fail if we try to send a process to
|
||||||
|
@ -54,10 +54,6 @@ func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*
|
|||||||
process *exec.Cmd
|
process *exec.Cmd
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
// check that the argument to Launch is an executable file
|
|
||||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
|
||||||
return nil, proc.ErrNotExecutable
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
if !isatty.IsTerminal(os.Stdin.Fd()) {
|
||||||
// exec.(*Process).Start will fail if we try to send a process to
|
// exec.(*Process).Start will fail if we try to send a process to
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package native
|
package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"debug/pe"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -22,19 +19,6 @@ type osProcessDetails struct {
|
|||||||
entryPoint uint64
|
entryPoint uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
|
||||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
peFile, err := pe.NewFile(f)
|
|
||||||
if err != nil {
|
|
||||||
f.Close()
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return peFile, f, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch creates and begins debugging a new process.
|
// Launch creates and begins debugging a new process.
|
||||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
|
func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target, error) {
|
||||||
argv0Go, err := filepath.Abs(cmd[0])
|
argv0Go, err := filepath.Abs(cmd[0])
|
||||||
@ -42,19 +26,6 @@ func Launch(cmd []string, wd string, foreground bool, _ []string) (*proc.Target,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the binary exists and is an executable file
|
|
||||||
if filepath.Base(cmd[0]) == cmd[0] {
|
|
||||||
if _, err := exec.LookPath(cmd[0]); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, closer, err := openExecutablePathPE(argv0Go)
|
|
||||||
if err != nil {
|
|
||||||
return nil, proc.ErrNotExecutable
|
|
||||||
}
|
|
||||||
closer.Close()
|
|
||||||
|
|
||||||
env := proc.DisableAsyncPreemptEnv()
|
env := proc.DisableAsyncPreemptEnv()
|
||||||
|
|
||||||
var p *os.Process
|
var p *os.Process
|
||||||
|
@ -2037,32 +2037,6 @@ func TestStepParked(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIssue509(t *testing.T) {
|
|
||||||
fixturesDir := protest.FindFixturesDir()
|
|
||||||
nomaindir := filepath.Join(fixturesDir, "nomaindir")
|
|
||||||
cmd := exec.Command("go", "build", "-gcflags=-N -l", "-o", "debug")
|
|
||||||
cmd.Dir = nomaindir
|
|
||||||
assertNoError(cmd.Run(), t, "go build")
|
|
||||||
exepath := filepath.Join(nomaindir, "debug")
|
|
||||||
defer os.Remove(exepath)
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch testBackend {
|
|
||||||
case "native":
|
|
||||||
_, err = native.Launch([]string{exepath}, ".", false, []string{})
|
|
||||||
case "lldb":
|
|
||||||
_, err = gdbserial.LLDBLaunch([]string{exepath}, ".", false, []string{})
|
|
||||||
default:
|
|
||||||
t.Skip("test not valid for this backend")
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("expected error but none was generated")
|
|
||||||
}
|
|
||||||
if err != proc.ErrNotExecutable {
|
|
||||||
t.Fatalf("expected error \"%v\" got \"%v\"", proc.ErrNotExecutable, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnsupportedArch(t *testing.T) {
|
func TestUnsupportedArch(t *testing.T) {
|
||||||
ver, _ := goversion.Parse(runtime.Version())
|
ver, _ := goversion.Parse(runtime.Version())
|
||||||
if ver.Major < 0 || !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 6, Rev: -1}) || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) {
|
if ver.Major < 0 || !ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 6, Rev: -1}) || ver.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 7, Rev: -1}) {
|
||||||
|
@ -11,10 +11,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrNotExecutable is returned after attempting to execute a non-executable file
|
|
||||||
// to begin a debug session.
|
|
||||||
ErrNotExecutable = errors.New("not an executable file")
|
|
||||||
|
|
||||||
// ErrNotRecorded is returned when an action is requested that is
|
// ErrNotRecorded is returned when an action is requested that is
|
||||||
// only possible on recorded (traced) programs.
|
// only possible on recorded (traced) programs.
|
||||||
ErrNotRecorded = errors.New("not a recording")
|
ErrNotRecorded = errors.New("not a recording")
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
// ErrNotExecutable is an error returned when trying
|
// ErrNotExecutable is an error returned when trying
|
||||||
// to debug a non-executable file.
|
// to debug a non-executable file.
|
||||||
var ErrNotExecutable = proc.ErrNotExecutable
|
var ErrNotExecutable = errors.New("not an executable file")
|
||||||
|
|
||||||
// DebuggerState represents the current context of the debugger.
|
// DebuggerState represents the current context of the debugger.
|
||||||
type DebuggerState struct {
|
type DebuggerState struct {
|
||||||
@ -212,7 +212,7 @@ const (
|
|||||||
// that may outlive the stack frame are allocated on the heap instead and
|
// that may outlive the stack frame are allocated on the heap instead and
|
||||||
// only the address is recorded on the stack. These variables will be
|
// only the address is recorded on the stack. These variables will be
|
||||||
// marked with this flag.
|
// marked with this flag.
|
||||||
VariableEscaped = (1 << iota)
|
VariableEscaped = 1 << iota
|
||||||
|
|
||||||
// VariableShadowed is set for local variables that are shadowed by a
|
// VariableShadowed is set for local variables that are shadowed by a
|
||||||
// variable with the same name in another scope
|
// variable with the same name in another scope
|
||||||
|
@ -26,6 +26,17 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrCanNotRestart is returned when the target cannot be restarted.
|
||||||
|
// This is returned for targets that have been attached to, or when
|
||||||
|
// debugging core files.
|
||||||
|
ErrCanNotRestart = errors.New("can not restart this target")
|
||||||
|
|
||||||
|
// ErrNotRecording is returned when StopRecording is called while the
|
||||||
|
// debugger is not recording the target.
|
||||||
|
ErrNotRecording = errors.New("debugger is not recording")
|
||||||
|
)
|
||||||
|
|
||||||
// Debugger service.
|
// Debugger service.
|
||||||
//
|
//
|
||||||
// Debugger provides a higher level of
|
// Debugger provides a higher level of
|
||||||
@ -179,6 +190,9 @@ func (d *Debugger) checkGoVersion() error {
|
|||||||
|
|
||||||
// Launch will start a process with the given args and working directory.
|
// Launch will start a process with the given args and working directory.
|
||||||
func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error) {
|
func (d *Debugger) Launch(processArgs []string, wd string) (*proc.Target, error) {
|
||||||
|
if err := verifyBinaryFormat(processArgs[0]); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
case "native":
|
case "native":
|
||||||
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)
|
return native.Launch(processArgs, wd, d.config.Foreground, d.config.DebugInfoDirectories)
|
||||||
@ -260,11 +274,6 @@ func (d *Debugger) recordingRun(run func() (string, error)) (*proc.Target, error
|
|||||||
return gdbserial.Replay(tracedir, false, true, d.config.DebugInfoDirectories)
|
return gdbserial.Replay(tracedir, false, true, d.config.DebugInfoDirectories)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrNoAttachPath is the error returned when the client tries to attach to
|
|
||||||
// a process on macOS using the lldb backend without specifying the path to
|
|
||||||
// the target's executable.
|
|
||||||
var ErrNoAttachPath = errors.New("must specify executable path on macOS")
|
|
||||||
|
|
||||||
// Attach will attach to the process specified by 'pid'.
|
// Attach will attach to the process specified by 'pid'.
|
||||||
func (d *Debugger) Attach(pid int, path string) (*proc.Target, error) {
|
func (d *Debugger) Attach(pid int, path string) (*proc.Target, error) {
|
||||||
switch d.config.Backend {
|
switch d.config.Backend {
|
||||||
@ -369,12 +378,6 @@ func (d *Debugger) detach(kill bool) error {
|
|||||||
return d.target.Detach(kill)
|
return d.target.Detach(kill)
|
||||||
}
|
}
|
||||||
|
|
||||||
var ErrCanNotRestart = errors.New("can not restart this target")
|
|
||||||
|
|
||||||
// ErrNotRecording is returned when StopRecording is called while the
|
|
||||||
// debugger is not recording the target.
|
|
||||||
var ErrNotRecording = errors.New("debugger is not recording")
|
|
||||||
|
|
||||||
// Restart will restart the target process, first killing
|
// Restart will restart the target process, first killing
|
||||||
// and then exec'ing it again.
|
// and then exec'ing it again.
|
||||||
// If the target process is a recording it will restart it from the given
|
// If the target process is a recording it will restart it from the given
|
||||||
|
70
service/debugger/debugger_test.go
Normal file
70
service/debugger/debugger_test.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package debugger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-delve/delve/pkg/gobuild"
|
||||||
|
protest "github.com/go-delve/delve/pkg/proc/test"
|
||||||
|
"github.com/go-delve/delve/service/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDebugger_LaunchNoMain(t *testing.T) {
|
||||||
|
fixturesDir := protest.FindFixturesDir()
|
||||||
|
nomaindir := filepath.Join(fixturesDir, "nomaindir")
|
||||||
|
debugname := "debug"
|
||||||
|
exepath := filepath.Join(nomaindir, debugname)
|
||||||
|
defer os.Remove(exepath)
|
||||||
|
if err := gobuild.GoBuild(debugname, []string{nomaindir}, fmt.Sprintf("-o %s", exepath)); err != nil {
|
||||||
|
t.Fatalf("go build error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
d := new(Debugger)
|
||||||
|
_, err := d.Launch([]string{exepath}, ".")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error but none was generated")
|
||||||
|
}
|
||||||
|
if err != api.ErrNotExecutable {
|
||||||
|
t.Fatalf("expected error \"%v\" got \"%v\"", api.ErrNotExecutable, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDebugger_LaunchInvalidFormat(t *testing.T) {
|
||||||
|
goos := os.Getenv("GOOS")
|
||||||
|
goarch := os.Getenv("GOARCH")
|
||||||
|
defer func() {
|
||||||
|
// restore environment values
|
||||||
|
os.Setenv("GOOS", goos)
|
||||||
|
os.Setenv("GOARCH", goarch)
|
||||||
|
}()
|
||||||
|
fixturesDir := protest.FindFixturesDir()
|
||||||
|
buildtestdir := filepath.Join(fixturesDir, "buildtest")
|
||||||
|
debugname := "debug"
|
||||||
|
switchOS := map[string]string{
|
||||||
|
"darwin": "linux",
|
||||||
|
"windows": "linux",
|
||||||
|
"freebsd": "windows",
|
||||||
|
"linux": "windows",
|
||||||
|
}
|
||||||
|
if runtime.GOARCH == "arm64" && runtime.GOOS == "linux" {
|
||||||
|
os.Setenv("GOARCH", "amd64")
|
||||||
|
}
|
||||||
|
os.Setenv("GOOS", switchOS[runtime.GOOS])
|
||||||
|
exepath := filepath.Join(buildtestdir, debugname)
|
||||||
|
if err := gobuild.GoBuild(debugname, []string{buildtestdir}, fmt.Sprintf("-o %s", exepath)); err != nil {
|
||||||
|
t.Fatalf("go build error %v", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(exepath)
|
||||||
|
|
||||||
|
d := new(Debugger)
|
||||||
|
_, err := d.Launch([]string{exepath}, ".")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error but none was generated")
|
||||||
|
}
|
||||||
|
if err != api.ErrNotExecutable {
|
||||||
|
t.Fatalf("expected error \"%s\" got \"%v\"", api.ErrNotExecutable, err)
|
||||||
|
}
|
||||||
|
}
|
42
service/debugger/debugger_unix.go
Normal file
42
service/debugger/debugger_unix.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package debugger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"debug/elf"
|
||||||
|
"debug/macho"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/go-delve/delve/service/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func verifyBinaryFormat(exePath string) error {
|
||||||
|
f, err := os.Open(exePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if (fi.Mode() & 0111) == 0 {
|
||||||
|
return api.ErrNotExecutable
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that the binary format is what we expect for the host system
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "darwin":
|
||||||
|
_, err = macho.NewFile(f)
|
||||||
|
case "linux", "freebsd":
|
||||||
|
_, err = elf.NewFile(f)
|
||||||
|
default:
|
||||||
|
panic("attempting to open file Delve cannot parse")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return api.ErrNotExecutable
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
47
service/debugger/debugger_unix_test.go
Normal file
47
service/debugger/debugger_unix_test.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package debugger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-delve/delve/pkg/gobuild"
|
||||||
|
protest "github.com/go-delve/delve/pkg/proc/test"
|
||||||
|
"github.com/go-delve/delve/service/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDebugger_LaunchNoExecutablePerm(t *testing.T) {
|
||||||
|
fixturesDir := protest.FindFixturesDir()
|
||||||
|
buildtestdir := filepath.Join(fixturesDir, "buildtest")
|
||||||
|
debugname := "debug"
|
||||||
|
switchOS := map[string]string{
|
||||||
|
"darwin": "linux",
|
||||||
|
"windows": "linux",
|
||||||
|
"freebsd": "windows",
|
||||||
|
"linux": "windows",
|
||||||
|
}
|
||||||
|
if runtime.GOARCH == "arm64" && runtime.GOOS == "linux" {
|
||||||
|
os.Setenv("GOARCH", "amd64")
|
||||||
|
}
|
||||||
|
os.Setenv("GOOS", switchOS[runtime.GOOS])
|
||||||
|
exepath := filepath.Join(buildtestdir, debugname)
|
||||||
|
if err := gobuild.GoBuild(debugname, []string{buildtestdir}, fmt.Sprintf("-o %s", exepath)); err != nil {
|
||||||
|
t.Fatalf("go build error %v", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(exepath)
|
||||||
|
if err := os.Chmod(exepath, 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
d := new(Debugger)
|
||||||
|
_, err := d.Launch([]string{exepath}, ".")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected error but none was generated")
|
||||||
|
}
|
||||||
|
if err != api.ErrNotExecutable {
|
||||||
|
t.Fatalf("expected error \"%s\" got \"%v\"", api.ErrNotExecutable, err)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,13 @@
|
|||||||
package debugger
|
package debugger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"debug/pe"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/go-delve/delve/service/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
func attachErrorMessage(pid int, err error) error {
|
func attachErrorMessage(pid int, err error) error {
|
||||||
@ -14,3 +20,23 @@ func stopProcess(pid int) error {
|
|||||||
// the process.
|
// the process.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func verifyBinaryFormat(exePath string) error {
|
||||||
|
f, err := os.Open(exePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Make sure the binary exists and is an executable file
|
||||||
|
if filepath.Base(exePath) == exePath {
|
||||||
|
if _, err := exec.LookPath(exePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = pe.NewFile(f); err != nil {
|
||||||
|
return api.ErrNotExecutable
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user