mirror of
				https://github.com/beego/bee.git
				synced 2025-11-04 04:13:26 +00:00 
			
		
		
		
	Merge tag '1.10.1' into develop
Merged
This commit is contained in:
		@@ -1,20 +1,17 @@
 | 
			
		||||
language: go
 | 
			
		||||
go:
 | 
			
		||||
  - 1.10.3
 | 
			
		||||
  - 1.11.9
 | 
			
		||||
  - 1.12.4
 | 
			
		||||
install:
 | 
			
		||||
  - export PATH=$PATH:$HOME/gopath/bin
 | 
			
		||||
  - go get -u github.com/opennota/check/cmd/structcheck
 | 
			
		||||
  - go get -u honnef.co/go/tools/cmd/gosimple
 | 
			
		||||
  - go get -u honnef.co/go/tools/cmd/staticcheck
 | 
			
		||||
  - go get -u honnef.co/go/tools/cmd/unused
 | 
			
		||||
  - go get -u github.com/mdempsky/unconvert
 | 
			
		||||
  - go get -u github.com/gordonklaus/ineffassign
 | 
			
		||||
script:
 | 
			
		||||
  - find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
 | 
			
		||||
  - go vet  $(go list ./... | grep -v /vendor/)
 | 
			
		||||
  - structcheck  $(go list ./... | grep -v /vendor/)
 | 
			
		||||
  - gosimple -ignore "$(cat gosimple.ignore)" $(go list ./... | grep -v /vendor/)
 | 
			
		||||
  - staticcheck -ignore "$(cat staticcheck.ignore)" $(go list ./... | grep -v /vendor/)
 | 
			
		||||
  - unused $(go list ./... | grep -v /vendor/)
 | 
			
		||||
  - unconvert $(go list ./... | grep -v /vendor/)
 | 
			
		||||
  - ineffassign .
 | 
			
		||||
 
 | 
			
		||||
@@ -28,11 +28,11 @@ import (
 | 
			
		||||
	"github.com/beego/bee/cmd/commands/version"
 | 
			
		||||
	beeLogger "github.com/beego/bee/logger"
 | 
			
		||||
	"github.com/beego/bee/utils"
 | 
			
		||||
	"github.com/derekparker/delve/service"
 | 
			
		||||
	"github.com/derekparker/delve/service/rpc2"
 | 
			
		||||
	"github.com/derekparker/delve/service/rpccommon"
 | 
			
		||||
	"github.com/derekparker/delve/terminal"
 | 
			
		||||
	"github.com/fsnotify/fsnotify"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/terminal"
 | 
			
		||||
	"github.com/go-delve/delve/service"
 | 
			
		||||
	"github.com/go-delve/delve/service/rpc2"
 | 
			
		||||
	"github.com/go-delve/delve/service/rpccommon"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var cmdDlv = &commands.Command{
 | 
			
		||||
@@ -43,7 +43,7 @@ var cmdDlv = &commands.Command{
 | 
			
		||||
 | 
			
		||||
  To debug your application using Delve, use: {{"$ bee dlv" | bold}}
 | 
			
		||||
 | 
			
		||||
  For more information on Delve: https://github.com/derekparker/delve
 | 
			
		||||
  For more information on Delve: https://github.com/go-delve/delve
 | 
			
		||||
`,
 | 
			
		||||
	PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
 | 
			
		||||
	Run:    runDlv,
 | 
			
		||||
@@ -152,7 +152,8 @@ func startDelveDebugger(addr string, ch chan int) int {
 | 
			
		||||
		APIVersion:  2,
 | 
			
		||||
		WorkingDir:  ".",
 | 
			
		||||
		ProcessArgs: []string{abs},
 | 
			
		||||
	}, false)
 | 
			
		||||
		Backend:     "default",
 | 
			
		||||
	})
 | 
			
		||||
	if err := server.Run(); err != nil {
 | 
			
		||||
		beeLogger.Log.Fatalf("Could not start debugger server: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
@@ -182,7 +183,7 @@ func startDelveDebugger(addr string, ch chan int) int {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Stop and kill the debugger server once user quits the REPL
 | 
			
		||||
	if err := server.Stop(true); err != nil {
 | 
			
		||||
	if err := server.Stop(); err != nil {
 | 
			
		||||
		beeLogger.Log.Fatalf("Could not stop Delve server: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return status
 | 
			
		||||
 
 | 
			
		||||
@@ -57,7 +57,7 @@ Prints the current Bee, Beego and Go version alongside the platform information.
 | 
			
		||||
}
 | 
			
		||||
var outputFormat string
 | 
			
		||||
 | 
			
		||||
const version = "1.10.0"
 | 
			
		||||
const version = "1.10.1"
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	fs := flag.NewFlagSet("version", flag.ContinueOnError)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/github.com/astaxie/beego/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/astaxie/beego/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
Copyright 2014 astaxie
 | 
			
		||||
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/cosiner/argv/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/cosiner/argv/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
The MIT License (MIT)
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2017 aihui zhu
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person
 | 
			
		||||
obtaining a copy of this software and associated documentation
 | 
			
		||||
files (the "Software"), to deal in the Software without
 | 
			
		||||
restriction, including without limitation the rights to use,
 | 
			
		||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the
 | 
			
		||||
Software is furnished to do so, subject to the following
 | 
			
		||||
conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be
 | 
			
		||||
included in all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
			
		||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 | 
			
		||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
			
		||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 | 
			
		||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 | 
			
		||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | 
			
		||||
OTHER DEALINGS IN THE SOFTWARE.
 | 
			
		||||
							
								
								
									
										31
									
								
								vendor/github.com/cosiner/argv/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/cosiner/argv/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
# Argv
 | 
			
		||||
 | 
			
		||||
[](https://godoc.org/github.com/cosiner/argv) 
 | 
			
		||||
[](https://travis-ci.org/cosiner/argv)
 | 
			
		||||
[](https://coveralls.io/github/cosiner/argv)
 | 
			
		||||
[](https://goreportcard.com/report/github.com/cosiner/argv)
 | 
			
		||||
 | 
			
		||||
Argv is a  library for [Go](https://golang.org) to split command line string into arguments array. 
 | 
			
		||||
 | 
			
		||||
# Documentation
 | 
			
		||||
Documentation can be found at [Godoc](https://godoc.org/github.com/cosiner/argv)
 | 
			
		||||
 | 
			
		||||
# Example
 | 
			
		||||
```Go
 | 
			
		||||
func TestArgv(t *testing.T) {
 | 
			
		||||
	args, err := argv.Argv([]rune(" ls   `echo /`   |  wc  -l "), os.Environ(), argv.Run)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	expects := [][]string{
 | 
			
		||||
	    []string{"ls", "/"},
 | 
			
		||||
	    []string{"wc", "-l"},
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepDqual(args, expects) {
 | 
			
		||||
	    t.Fatal(args)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# LICENSE
 | 
			
		||||
MIT.
 | 
			
		||||
							
								
								
									
										34
									
								
								vendor/github.com/cosiner/argv/argv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/cosiner/argv/argv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
// Package argv parse command line string into arguments array using the bash syntax.
 | 
			
		||||
package argv
 | 
			
		||||
 | 
			
		||||
import "strings"
 | 
			
		||||
 | 
			
		||||
// ParseEnv parsing environment variables as key/value pair.
 | 
			
		||||
//
 | 
			
		||||
// Item will be ignored if one of the key and value is empty.
 | 
			
		||||
func ParseEnv(env []string) map[string]string {
 | 
			
		||||
	var m map[string]string
 | 
			
		||||
	for _, e := range env {
 | 
			
		||||
		secs := strings.SplitN(e, "=", 2)
 | 
			
		||||
		if len(secs) == 2 {
 | 
			
		||||
			key := strings.TrimSpace(secs[0])
 | 
			
		||||
			val := strings.TrimSpace(secs[1])
 | 
			
		||||
			if key == "" || val == "" {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if m == nil {
 | 
			
		||||
				m = make(map[string]string)
 | 
			
		||||
			}
 | 
			
		||||
			m[key] = val
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return m
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Argv split cmdline string as array of argument array by the '|' character.
 | 
			
		||||
//
 | 
			
		||||
// The parsing rules is same as bash. The environment variable will be replaced
 | 
			
		||||
// and string surround by '`' will be passed to reverse quote parser.
 | 
			
		||||
func Argv(cmdline []rune, env map[string]string, reverseQuoteParser ReverseQuoteParser) ([][]string, error) {
 | 
			
		||||
	return NewParser(NewScanner(cmdline, env), reverseQuoteParser).Parse()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								vendor/github.com/cosiner/argv/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/cosiner/argv/cmd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
package argv
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Run execute cmdline string and return the output
 | 
			
		||||
func Run(cmdline []rune, env map[string]string) ([]rune, error) {
 | 
			
		||||
	args, err := Argv(cmdline, env, Run)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	cmds, err := Cmds(args)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	output := bytes.NewBuffer(make([]byte, 0, 1024))
 | 
			
		||||
	err = Pipe(nil, output, cmds...)
 | 
			
		||||
	str := output.String()
 | 
			
		||||
	str = strings.TrimSpace(str)
 | 
			
		||||
	return []rune(str), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Cmds generate exec.Cmd for each command.
 | 
			
		||||
func Cmds(args [][]string) ([]*exec.Cmd, error) {
 | 
			
		||||
	var cmds []*exec.Cmd
 | 
			
		||||
	for _, argv := range args {
 | 
			
		||||
		if len(argv) == 0 {
 | 
			
		||||
			return nil, errors.New("invalid cmd")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		cmds = append(cmds, exec.Command(argv[0], argv[1:]...))
 | 
			
		||||
	}
 | 
			
		||||
	return cmds, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pipe pipe previous command's stdout to next command's stdin, if in or
 | 
			
		||||
// out is nil, it will be ignored.
 | 
			
		||||
func Pipe(in io.Reader, out io.Writer, cmds ...*exec.Cmd) error {
 | 
			
		||||
	l := len(cmds)
 | 
			
		||||
	if l == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	for i := 1; i < l; i++ {
 | 
			
		||||
		cmds[i].Stdin, err = cmds[i-1].StdoutPipe()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if in != nil {
 | 
			
		||||
		cmds[0].Stdin = in
 | 
			
		||||
	}
 | 
			
		||||
	if out != nil {
 | 
			
		||||
		cmds[l-1].Stdout = out
 | 
			
		||||
	}
 | 
			
		||||
	for i := range cmds {
 | 
			
		||||
		err = cmds[i].Start()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for i := range cmds {
 | 
			
		||||
		err = cmds[i].Wait()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										222
									
								
								vendor/github.com/cosiner/argv/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								vendor/github.com/cosiner/argv/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
package argv
 | 
			
		||||
 | 
			
		||||
import "errors"
 | 
			
		||||
 | 
			
		||||
type (
 | 
			
		||||
	// ReverseQuoteParser parse strings quoted by '`' and return it's result. Commonly,
 | 
			
		||||
	// it should run it os command.
 | 
			
		||||
	ReverseQuoteParser func([]rune, map[string]string) ([]rune, error)
 | 
			
		||||
 | 
			
		||||
	// Parser take tokens from Scanner, and do syntax checking, and generate the splitted arguments array.
 | 
			
		||||
	Parser struct {
 | 
			
		||||
		s      *Scanner
 | 
			
		||||
		tokbuf []Token
 | 
			
		||||
		r      ReverseQuoteParser
 | 
			
		||||
 | 
			
		||||
		sections    [][]string
 | 
			
		||||
		currSection []string
 | 
			
		||||
 | 
			
		||||
		currStrValid bool
 | 
			
		||||
		currStr      []rune
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewParser create a cmdline string parser.
 | 
			
		||||
func NewParser(s *Scanner, r ReverseQuoteParser) *Parser {
 | 
			
		||||
	if r == nil {
 | 
			
		||||
		r = func(r []rune, env map[string]string) ([]rune, error) {
 | 
			
		||||
			return r, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &Parser{
 | 
			
		||||
		s: s,
 | 
			
		||||
		r: r,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) nextToken() (Token, error) {
 | 
			
		||||
	if l := len(p.tokbuf); l > 0 {
 | 
			
		||||
		tok := p.tokbuf[l-1]
 | 
			
		||||
		p.tokbuf = p.tokbuf[:l-1]
 | 
			
		||||
		return tok, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p.s.Next()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrInvalidSyntax was reported if there is a syntax error in command line string.
 | 
			
		||||
	ErrInvalidSyntax = errors.New("invalid syntax")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (p *Parser) unreadToken(tok Token) {
 | 
			
		||||
	p.tokbuf = append(p.tokbuf, tok)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse split command line string into arguments array.
 | 
			
		||||
//
 | 
			
		||||
// EBNF:
 | 
			
		||||
//   Cmdline = Section [ Pipe Cmdline ]
 | 
			
		||||
//   Section = [Space] SpacedSection { SpacedSection }
 | 
			
		||||
//   SpacedSection = MultipleUnit [Space]
 | 
			
		||||
//   MultipleUnit = Unit {Unit}
 | 
			
		||||
//   Unit = String | ReverseQuote
 | 
			
		||||
func (p *Parser) Parse() ([][]string, error) {
 | 
			
		||||
	err := p.cmdline()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return p.sections, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) cmdline() error {
 | 
			
		||||
	err := p.section()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	p.endSection()
 | 
			
		||||
 | 
			
		||||
	tok, err := p.nextToken()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if tok.Type == TokEOF {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !p.accept(tok.Type, TokPipe) {
 | 
			
		||||
		return ErrInvalidSyntax
 | 
			
		||||
	}
 | 
			
		||||
	return p.cmdline()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) section() error {
 | 
			
		||||
	leftSpace, err := p.optional(TokSpace)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var isFirst = true
 | 
			
		||||
	for {
 | 
			
		||||
		unit, err := p.spacedSection()
 | 
			
		||||
		if isFirst {
 | 
			
		||||
			isFirst = false
 | 
			
		||||
		} else {
 | 
			
		||||
			if err == ErrInvalidSyntax {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		p.appendUnit(leftSpace, unit)
 | 
			
		||||
		leftSpace = unit.rightSpace
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type unit struct {
 | 
			
		||||
	rightSpace bool
 | 
			
		||||
	toks       []Token
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) spacedSection() (u unit, err error) {
 | 
			
		||||
	u.toks, err = p.multipleUnit()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	u.rightSpace, err = p.optional(TokSpace)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) multipleUnit() ([]Token, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		toks    []Token
 | 
			
		||||
		isFirst = true
 | 
			
		||||
	)
 | 
			
		||||
	for {
 | 
			
		||||
		tok, err := p.unit()
 | 
			
		||||
		if isFirst {
 | 
			
		||||
			isFirst = false
 | 
			
		||||
		} else {
 | 
			
		||||
			if err == ErrInvalidSyntax {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		toks = append(toks, tok)
 | 
			
		||||
	}
 | 
			
		||||
	return toks, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) unit() (Token, error) {
 | 
			
		||||
	tok, err := p.nextToken()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return tok, err
 | 
			
		||||
	}
 | 
			
		||||
	if p.accept(tok.Type, TokString, TokReversequote) {
 | 
			
		||||
		return tok, nil
 | 
			
		||||
	}
 | 
			
		||||
	p.unreadToken(tok)
 | 
			
		||||
	return tok, ErrInvalidSyntax
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) optional(typ TokenType) (bool, error) {
 | 
			
		||||
	tok, err := p.nextToken()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	var ok bool
 | 
			
		||||
	if ok = p.accept(tok.Type, typ); !ok {
 | 
			
		||||
		p.unreadToken(tok)
 | 
			
		||||
	}
 | 
			
		||||
	return ok, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) accept(t TokenType, types ...TokenType) bool {
 | 
			
		||||
	for _, typ := range types {
 | 
			
		||||
		if t == typ {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) appendUnit(leftSpace bool, u unit) error {
 | 
			
		||||
	if leftSpace {
 | 
			
		||||
		p.currStr = p.currStr[:0]
 | 
			
		||||
	}
 | 
			
		||||
	for _, tok := range u.toks {
 | 
			
		||||
		if tok.Type == TokReversequote {
 | 
			
		||||
			val, err := p.r(tok.Value, p.s.envs())
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			p.currStr = append(p.currStr, val...)
 | 
			
		||||
		} else {
 | 
			
		||||
			p.currStr = append(p.currStr, tok.Value...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	p.currStrValid = true
 | 
			
		||||
	if u.rightSpace {
 | 
			
		||||
		p.currSection = append(p.currSection, string(p.currStr))
 | 
			
		||||
		p.currStr = p.currStr[:0]
 | 
			
		||||
		p.currStrValid = false
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Parser) endSection() {
 | 
			
		||||
	if p.currStrValid {
 | 
			
		||||
		p.currSection = append(p.currSection, string(p.currStr))
 | 
			
		||||
	}
 | 
			
		||||
	p.currStr = p.currStr[:0]
 | 
			
		||||
	p.currStrValid = false
 | 
			
		||||
	if len(p.currSection) > 0 {
 | 
			
		||||
		p.sections = append(p.sections, p.currSection)
 | 
			
		||||
		p.currSection = nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										282
									
								
								vendor/github.com/cosiner/argv/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								vendor/github.com/cosiner/argv/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,282 @@
 | 
			
		||||
package argv
 | 
			
		||||
 | 
			
		||||
import "unicode"
 | 
			
		||||
 | 
			
		||||
// Scanner is a cmdline string scanner.
 | 
			
		||||
//
 | 
			
		||||
// It split cmdline string to tokens: space, string, pipe, reverse quote string.
 | 
			
		||||
type Scanner struct {
 | 
			
		||||
	env map[string]string
 | 
			
		||||
 | 
			
		||||
	text      []rune
 | 
			
		||||
	rpos      int
 | 
			
		||||
	dollarBuf []rune
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewScanner create a scanner and init it's internal states.
 | 
			
		||||
func NewScanner(text []rune, env map[string]string) *Scanner {
 | 
			
		||||
	return &Scanner{
 | 
			
		||||
		text: text,
 | 
			
		||||
		env:  env,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) envs() map[string]string {
 | 
			
		||||
	return s.env
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _RuneEOF = 0
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) nextRune() rune {
 | 
			
		||||
	if s.rpos >= len(s.text) {
 | 
			
		||||
		return _RuneEOF
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := s.text[s.rpos]
 | 
			
		||||
	s.rpos++
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) unreadRune(r rune) {
 | 
			
		||||
	if r != _RuneEOF {
 | 
			
		||||
		s.rpos--
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) isEscapeChars(r rune) (rune, bool) {
 | 
			
		||||
	switch r {
 | 
			
		||||
	case 'a':
 | 
			
		||||
		return '\a', true
 | 
			
		||||
	case 'b':
 | 
			
		||||
		return '\b', true
 | 
			
		||||
	case 'f':
 | 
			
		||||
		return '\f', true
 | 
			
		||||
	case 'n':
 | 
			
		||||
		return '\n', true
 | 
			
		||||
	case 'r':
 | 
			
		||||
		return '\r', true
 | 
			
		||||
	case 't':
 | 
			
		||||
		return '\t', true
 | 
			
		||||
	case 'v':
 | 
			
		||||
		return '\v', true
 | 
			
		||||
	case '\\':
 | 
			
		||||
		return '\\', true
 | 
			
		||||
	case '$':
 | 
			
		||||
		return '$', true
 | 
			
		||||
	}
 | 
			
		||||
	return r, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) endEnv(r rune) bool {
 | 
			
		||||
	if r == '_' || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TokenType is the type of tokens recognized by the scanner.
 | 
			
		||||
type TokenType uint32
 | 
			
		||||
 | 
			
		||||
// Token is generated by the scanner with a type and value.
 | 
			
		||||
type Token struct {
 | 
			
		||||
	Type  TokenType
 | 
			
		||||
	Value []rune
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// TokString for string, single quoted string and double quoted string
 | 
			
		||||
	TokString TokenType = iota + 1
 | 
			
		||||
	// TokPipe is the '|' character
 | 
			
		||||
	TokPipe
 | 
			
		||||
	// TokReversequote is reverse quoted string
 | 
			
		||||
	TokReversequote
 | 
			
		||||
	// TokSpace represent space character sequence
 | 
			
		||||
	TokSpace
 | 
			
		||||
	// TokEOF means the input end.
 | 
			
		||||
	TokEOF
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) getEnv(name string) string {
 | 
			
		||||
	return s.env[name]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) specialVar(r rune) (string, bool) {
 | 
			
		||||
	switch r {
 | 
			
		||||
	case '0', '*', '#', '@', '?', '$':
 | 
			
		||||
		v, has := s.env[string(r)]
 | 
			
		||||
		return v, has
 | 
			
		||||
	default:
 | 
			
		||||
		return "", false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) checkDollarStart(tok *Token, r rune, from, switchTo uint8) uint8 {
 | 
			
		||||
	state := from
 | 
			
		||||
	nr := s.nextRune()
 | 
			
		||||
	if val, has := s.specialVar(nr); has {
 | 
			
		||||
		if val != "" {
 | 
			
		||||
			tok.Value = append(tok.Value, []rune(val)...)
 | 
			
		||||
		}
 | 
			
		||||
	} else if s.endEnv(nr) {
 | 
			
		||||
		tok.Value = append(tok.Value, r)
 | 
			
		||||
		s.unreadRune(nr)
 | 
			
		||||
	} else {
 | 
			
		||||
		state = switchTo
 | 
			
		||||
		s.dollarBuf = append(s.dollarBuf[:0], nr)
 | 
			
		||||
	}
 | 
			
		||||
	return state
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (s *Scanner) checkDollarEnd(tok *Token, r rune, from, switchTo uint8) uint8 {
 | 
			
		||||
	var state = from
 | 
			
		||||
	if s.endEnv(r) {
 | 
			
		||||
		tok.Value = append(tok.Value, []rune(s.getEnv(string(s.dollarBuf)))...)
 | 
			
		||||
		state = switchTo
 | 
			
		||||
		s.unreadRune(r)
 | 
			
		||||
	} else {
 | 
			
		||||
		s.dollarBuf = append(s.dollarBuf, r)
 | 
			
		||||
	}
 | 
			
		||||
	return state
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next return next token, if it reach the end, TOK_EOF will be returned.
 | 
			
		||||
//
 | 
			
		||||
// Error is returned for invalid syntax such as unpaired quotes.
 | 
			
		||||
func (s *Scanner) Next() (Token, error) {
 | 
			
		||||
	const (
 | 
			
		||||
		Initial = iota + 1
 | 
			
		||||
		Space
 | 
			
		||||
		ReverseQuote
 | 
			
		||||
		String
 | 
			
		||||
		StringDollar
 | 
			
		||||
		StringQuoteSingle
 | 
			
		||||
		StringQuoteDouble
 | 
			
		||||
		StringQuoteDoubleDollar
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		tok Token
 | 
			
		||||
 | 
			
		||||
		state uint8 = Initial
 | 
			
		||||
	)
 | 
			
		||||
	s.dollarBuf = s.dollarBuf[:0]
 | 
			
		||||
	for {
 | 
			
		||||
		r := s.nextRune()
 | 
			
		||||
		switch state {
 | 
			
		||||
		case Initial:
 | 
			
		||||
			switch {
 | 
			
		||||
			case r == _RuneEOF:
 | 
			
		||||
				tok.Type = TokEOF
 | 
			
		||||
				return tok, nil
 | 
			
		||||
			case r == '|':
 | 
			
		||||
				tok.Type = TokPipe
 | 
			
		||||
				return tok, nil
 | 
			
		||||
			case r == '`':
 | 
			
		||||
				state = ReverseQuote
 | 
			
		||||
			case unicode.IsSpace(r):
 | 
			
		||||
				state = Space
 | 
			
		||||
				s.unreadRune(r)
 | 
			
		||||
			default:
 | 
			
		||||
				state = String
 | 
			
		||||
				s.unreadRune(r)
 | 
			
		||||
			}
 | 
			
		||||
		case Space:
 | 
			
		||||
			if r == _RuneEOF || !unicode.IsSpace(r) {
 | 
			
		||||
				s.unreadRune(r)
 | 
			
		||||
				tok.Type = TokSpace
 | 
			
		||||
				return tok, nil
 | 
			
		||||
			}
 | 
			
		||||
		case ReverseQuote:
 | 
			
		||||
			switch r {
 | 
			
		||||
			case _RuneEOF:
 | 
			
		||||
				return tok, ErrInvalidSyntax
 | 
			
		||||
			case '`':
 | 
			
		||||
				tok.Type = TokReversequote
 | 
			
		||||
				return tok, nil
 | 
			
		||||
			default:
 | 
			
		||||
				tok.Value = append(tok.Value, r)
 | 
			
		||||
			}
 | 
			
		||||
		case String:
 | 
			
		||||
			switch {
 | 
			
		||||
			case r == _RuneEOF || r == '|' || r == '`' || unicode.IsSpace(r):
 | 
			
		||||
				tok.Type = TokString
 | 
			
		||||
				s.unreadRune(r)
 | 
			
		||||
				return tok, nil
 | 
			
		||||
			case r == '\'':
 | 
			
		||||
				state = StringQuoteSingle
 | 
			
		||||
			case r == '"':
 | 
			
		||||
				state = StringQuoteDouble
 | 
			
		||||
			case r == '\\':
 | 
			
		||||
				nr := s.nextRune()
 | 
			
		||||
				if nr == _RuneEOF {
 | 
			
		||||
					return tok, ErrInvalidSyntax
 | 
			
		||||
				}
 | 
			
		||||
				tok.Value = append(tok.Value, nr)
 | 
			
		||||
			case r == '$':
 | 
			
		||||
				state = s.checkDollarStart(&tok, r, state, StringDollar)
 | 
			
		||||
			default:
 | 
			
		||||
				tok.Value = append(tok.Value, r)
 | 
			
		||||
			}
 | 
			
		||||
		case StringDollar:
 | 
			
		||||
			state = s.checkDollarEnd(&tok, r, state, String)
 | 
			
		||||
		case StringQuoteSingle:
 | 
			
		||||
			switch r {
 | 
			
		||||
			case _RuneEOF:
 | 
			
		||||
				return tok, ErrInvalidSyntax
 | 
			
		||||
			case '\'':
 | 
			
		||||
				state = String
 | 
			
		||||
			case '\\':
 | 
			
		||||
				nr := s.nextRune()
 | 
			
		||||
				if escape, ok := s.isEscapeChars(nr); ok {
 | 
			
		||||
					tok.Value = append(tok.Value, escape)
 | 
			
		||||
				} else {
 | 
			
		||||
					tok.Value = append(tok.Value, r)
 | 
			
		||||
					s.unreadRune(nr)
 | 
			
		||||
				}
 | 
			
		||||
			default:
 | 
			
		||||
				tok.Value = append(tok.Value, r)
 | 
			
		||||
			}
 | 
			
		||||
		case StringQuoteDouble:
 | 
			
		||||
			switch r {
 | 
			
		||||
			case _RuneEOF:
 | 
			
		||||
				return tok, ErrInvalidSyntax
 | 
			
		||||
			case '"':
 | 
			
		||||
				state = String
 | 
			
		||||
			case '\\':
 | 
			
		||||
				nr := s.nextRune()
 | 
			
		||||
				if nr == _RuneEOF {
 | 
			
		||||
					return tok, ErrInvalidSyntax
 | 
			
		||||
				}
 | 
			
		||||
				if escape, ok := s.isEscapeChars(nr); ok {
 | 
			
		||||
					tok.Value = append(tok.Value, escape)
 | 
			
		||||
				} else {
 | 
			
		||||
					tok.Value = append(tok.Value, r)
 | 
			
		||||
					s.unreadRune(nr)
 | 
			
		||||
				}
 | 
			
		||||
			case '$':
 | 
			
		||||
				state = s.checkDollarStart(&tok, r, state, StringQuoteDoubleDollar)
 | 
			
		||||
			default:
 | 
			
		||||
				tok.Value = append(tok.Value, r)
 | 
			
		||||
			}
 | 
			
		||||
		case StringQuoteDoubleDollar:
 | 
			
		||||
			state = s.checkDollarEnd(&tok, r, state, StringQuoteDouble)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scan is a utility function help split input text as tokens.
 | 
			
		||||
func Scan(text []rune, env map[string]string) ([]Token, error) {
 | 
			
		||||
	s := NewScanner(text, env)
 | 
			
		||||
	var tokens []Token
 | 
			
		||||
	for {
 | 
			
		||||
		tok, err := s.Next()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		tokens = append(tokens, tok)
 | 
			
		||||
		if tok.Type == TokEOF {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return tokens, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										134
									
								
								vendor/github.com/derekparker/delve/config/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										134
									
								
								vendor/github.com/derekparker/delve/config/config.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,134 +0,0 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/user"
 | 
			
		||||
	"path"
 | 
			
		||||
 | 
			
		||||
	yaml "gopkg.in/yaml.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	configDir  string = ".dlv"
 | 
			
		||||
	configFile string = "config.yml"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Describes a rule for substitution of path to source code file.
 | 
			
		||||
type SubstitutePathRule struct {
 | 
			
		||||
	// Directory path will be substituted if it matches `From`.
 | 
			
		||||
	From string
 | 
			
		||||
	// Path to which substitution is performed.
 | 
			
		||||
	To   string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Slice of source code path substitution rules.
 | 
			
		||||
type SubstitutePathRules []SubstitutePathRule
 | 
			
		||||
 | 
			
		||||
// Config defines all configuration options available to be set through the config file.
 | 
			
		||||
type Config struct {
 | 
			
		||||
	// Commands aliases.
 | 
			
		||||
	Aliases        map[string][]string
 | 
			
		||||
	// Source code path substitution rules.
 | 
			
		||||
	SubstitutePath SubstitutePathRules `yaml:"substitute-path"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadConfig attempts to populate a Config object from the config.yml file.
 | 
			
		||||
func LoadConfig() *Config {
 | 
			
		||||
	err := createConfigPath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Could not create config directory: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	fullConfigFile, err := GetConfigFilePath(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to get config file path: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.Open(fullConfigFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		createDefaultConfig(fullConfigFile)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		err := f.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Closing config file failed: %v.", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	data, err := ioutil.ReadAll(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to read config data: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var c Config
 | 
			
		||||
	err = yaml.Unmarshal(data, &c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to decode config file: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createDefaultConfig(path string) {
 | 
			
		||||
	f, err := os.Create(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to create config file: %v.", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		err := f.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Closing config file failed: %v.", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	err = writeDefaultConfig(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to write default configuration: %v.", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeDefaultConfig(f *os.File) error {
 | 
			
		||||
	_, err := f.WriteString(
 | 
			
		||||
		`# Configuration file for the delve debugger.
 | 
			
		||||
 | 
			
		||||
# This is the default configuration file. Available options are provided, but disabled.
 | 
			
		||||
# Delete the leading hash mark to enable an item.
 | 
			
		||||
 | 
			
		||||
# Provided aliases will be added to the default aliases for a given command.
 | 
			
		||||
aliases:
 | 
			
		||||
  # command: ["alias1", "alias2"]
 | 
			
		||||
 | 
			
		||||
# Define sources path substitution rules. Can be used to rewrite a source path stored
 | 
			
		||||
# in program's debug information, if the sources were moved to a different place
 | 
			
		||||
# between compilation and debugging.
 | 
			
		||||
# Note that substitution rules will not be used for paths passed to "break" and "trace"
 | 
			
		||||
# commands.
 | 
			
		||||
substitute-path:
 | 
			
		||||
  # - {from: path, to: path}
 | 
			
		||||
`)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// createConfigPath creates the directory structure at which all config files are saved.
 | 
			
		||||
func createConfigPath() error {
 | 
			
		||||
	path, err := GetConfigFilePath("")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.MkdirAll(path, 0700)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetConfigFilePath gets the full path to the given config file name.
 | 
			
		||||
func GetConfigFilePath(file string) (string, error) {
 | 
			
		||||
	usr, err := user.Current()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return path.Join(usr.HomeDir, configDir, file), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										122
									
								
								vendor/github.com/derekparker/delve/dwarf/line/line_parser.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										122
									
								
								vendor/github.com/derekparker/delve/dwarf/line/line_parser.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,122 +0,0 @@
 | 
			
		||||
package line
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DebugLinePrologue struct {
 | 
			
		||||
	UnitLength     uint32
 | 
			
		||||
	Version        uint16
 | 
			
		||||
	Length         uint32
 | 
			
		||||
	MinInstrLength uint8
 | 
			
		||||
	InitialIsStmt  uint8
 | 
			
		||||
	LineBase       int8
 | 
			
		||||
	LineRange      uint8
 | 
			
		||||
	OpcodeBase     uint8
 | 
			
		||||
	StdOpLengths   []uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DebugLineInfo struct {
 | 
			
		||||
	Prologue     *DebugLinePrologue
 | 
			
		||||
	IncludeDirs  []string
 | 
			
		||||
	FileNames    []*FileEntry
 | 
			
		||||
	Instructions []byte
 | 
			
		||||
	Lookup       map[string]*FileEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FileEntry struct {
 | 
			
		||||
	Name        string
 | 
			
		||||
	DirIdx      uint64
 | 
			
		||||
	LastModTime uint64
 | 
			
		||||
	Length      uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DebugLines []*DebugLineInfo
 | 
			
		||||
 | 
			
		||||
func (d *DebugLines) GetLineInfo(name string) *DebugLineInfo {
 | 
			
		||||
	// Find in which table file exists and return it.
 | 
			
		||||
	for _, l := range *d {
 | 
			
		||||
		if _, ok := l.Lookup[name]; ok {
 | 
			
		||||
			return l
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Parse(data []byte) DebugLines {
 | 
			
		||||
	var (
 | 
			
		||||
		lines = make(DebugLines, 0)
 | 
			
		||||
		buf   = bytes.NewBuffer(data)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// We have to parse multiple file name tables here.
 | 
			
		||||
	for buf.Len() > 0 {
 | 
			
		||||
		dbl := new(DebugLineInfo)
 | 
			
		||||
		dbl.Lookup = make(map[string]*FileEntry)
 | 
			
		||||
 | 
			
		||||
		parseDebugLinePrologue(dbl, buf)
 | 
			
		||||
		parseIncludeDirs(dbl, buf)
 | 
			
		||||
		parseFileEntries(dbl, buf)
 | 
			
		||||
 | 
			
		||||
		// Instructions size calculation breakdown:
 | 
			
		||||
		//   - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
 | 
			
		||||
		//   - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
 | 
			
		||||
		//   - So you have UnitLength - PrologueLength - (version_length_bytes(2) + prologue_length_bytes(4)).
 | 
			
		||||
		dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6))
 | 
			
		||||
 | 
			
		||||
		lines = append(lines, dbl)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return lines
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
 | 
			
		||||
	p := new(DebugLinePrologue)
 | 
			
		||||
 | 
			
		||||
	p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4))
 | 
			
		||||
	p.Version = binary.LittleEndian.Uint16(buf.Next(2))
 | 
			
		||||
	p.Length = binary.LittleEndian.Uint32(buf.Next(4))
 | 
			
		||||
	p.MinInstrLength = uint8(buf.Next(1)[0])
 | 
			
		||||
	p.InitialIsStmt = uint8(buf.Next(1)[0])
 | 
			
		||||
	p.LineBase = int8(buf.Next(1)[0])
 | 
			
		||||
	p.LineRange = uint8(buf.Next(1)[0])
 | 
			
		||||
	p.OpcodeBase = uint8(buf.Next(1)[0])
 | 
			
		||||
 | 
			
		||||
	p.StdOpLengths = make([]uint8, p.OpcodeBase-1)
 | 
			
		||||
	binary.Read(buf, binary.LittleEndian, &p.StdOpLengths)
 | 
			
		||||
 | 
			
		||||
	dbl.Prologue = p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
 | 
			
		||||
	for {
 | 
			
		||||
		str, _ := util.ParseString(buf)
 | 
			
		||||
		if str == "" {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		info.IncludeDirs = append(info.IncludeDirs, str)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
 | 
			
		||||
	for {
 | 
			
		||||
		entry := new(FileEntry)
 | 
			
		||||
 | 
			
		||||
		name, _ := util.ParseString(buf)
 | 
			
		||||
		if name == "" {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		entry.Name = name
 | 
			
		||||
		entry.DirIdx, _ = util.DecodeULEB128(buf)
 | 
			
		||||
		entry.LastModTime, _ = util.DecodeULEB128(buf)
 | 
			
		||||
		entry.Length, _ = util.DecodeULEB128(buf)
 | 
			
		||||
 | 
			
		||||
		info.FileNames = append(info.FileNames, entry)
 | 
			
		||||
		info.Lookup[name] = entry
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										252
									
								
								vendor/github.com/derekparker/delve/dwarf/line/state_machine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										252
									
								
								vendor/github.com/derekparker/delve/dwarf/line/state_machine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,252 +0,0 @@
 | 
			
		||||
package line
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Location struct {
 | 
			
		||||
	File    string
 | 
			
		||||
	Line    int
 | 
			
		||||
	Address uint64
 | 
			
		||||
	Delta   int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StateMachine struct {
 | 
			
		||||
	dbl             *DebugLineInfo
 | 
			
		||||
	file            string
 | 
			
		||||
	line            int
 | 
			
		||||
	address         uint64
 | 
			
		||||
	column          uint
 | 
			
		||||
	isStmt          bool
 | 
			
		||||
	basicBlock      bool
 | 
			
		||||
	endSeq          bool
 | 
			
		||||
	lastWasStandard bool
 | 
			
		||||
	lastDelta       int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type opcodefn func(*StateMachine, *bytes.Buffer)
 | 
			
		||||
 | 
			
		||||
// Special opcodes
 | 
			
		||||
const (
 | 
			
		||||
	DW_LNS_copy             = 1
 | 
			
		||||
	DW_LNS_advance_pc       = 2
 | 
			
		||||
	DW_LNS_advance_line     = 3
 | 
			
		||||
	DW_LNS_set_file         = 4
 | 
			
		||||
	DW_LNS_set_column       = 5
 | 
			
		||||
	DW_LNS_negate_stmt      = 6
 | 
			
		||||
	DW_LNS_set_basic_block  = 7
 | 
			
		||||
	DW_LNS_const_add_pc     = 8
 | 
			
		||||
	DW_LNS_fixed_advance_pc = 9
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Extended opcodes
 | 
			
		||||
const (
 | 
			
		||||
	DW_LINE_end_sequence = 1
 | 
			
		||||
	DW_LINE_set_address  = 2
 | 
			
		||||
	DW_LINE_define_file  = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var standardopcodes = map[byte]opcodefn{
 | 
			
		||||
	DW_LNS_copy:             copyfn,
 | 
			
		||||
	DW_LNS_advance_pc:       advancepc,
 | 
			
		||||
	DW_LNS_advance_line:     advanceline,
 | 
			
		||||
	DW_LNS_set_file:         setfile,
 | 
			
		||||
	DW_LNS_set_column:       setcolumn,
 | 
			
		||||
	DW_LNS_negate_stmt:      negatestmt,
 | 
			
		||||
	DW_LNS_set_basic_block:  setbasicblock,
 | 
			
		||||
	DW_LNS_const_add_pc:     constaddpc,
 | 
			
		||||
	DW_LNS_fixed_advance_pc: fixedadvancepc,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var extendedopcodes = map[byte]opcodefn{
 | 
			
		||||
	DW_LINE_end_sequence: endsequence,
 | 
			
		||||
	DW_LINE_set_address:  setaddress,
 | 
			
		||||
	DW_LINE_define_file:  definefile,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newStateMachine(dbl *DebugLineInfo) *StateMachine {
 | 
			
		||||
	return &StateMachine{dbl: dbl, file: dbl.FileNames[0].Name, line: 1}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns all PCs for a given file/line. Useful for loops where the 'for' line
 | 
			
		||||
// could be split amongst 2 PCs.
 | 
			
		||||
func (dbl *DebugLines) AllPCsForFileLine(f string, l int) (pcs []uint64) {
 | 
			
		||||
	var (
 | 
			
		||||
		foundFile bool
 | 
			
		||||
		lastAddr  uint64
 | 
			
		||||
		lineInfo  = dbl.GetLineInfo(f)
 | 
			
		||||
		sm        = newStateMachine(lineInfo)
 | 
			
		||||
		buf       = bytes.NewBuffer(lineInfo.Instructions)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
 | 
			
		||||
		findAndExecOpcode(sm, buf, b)
 | 
			
		||||
		if foundFile && sm.file != f {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if sm.line == l && sm.file == f && sm.address != lastAddr {
 | 
			
		||||
			foundFile = true
 | 
			
		||||
			pcs = append(pcs, sm.address)
 | 
			
		||||
			line := sm.line
 | 
			
		||||
			// Keep going until we're on a different line. We only care about
 | 
			
		||||
			// when a line comes back around (i.e. for loop) so get to next line,
 | 
			
		||||
			// and try to find the line we care about again.
 | 
			
		||||
			for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
 | 
			
		||||
				findAndExecOpcode(sm, buf, b)
 | 
			
		||||
				if line < sm.line {
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var NoSourceError = errors.New("no source available")
 | 
			
		||||
 | 
			
		||||
func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint64, error) {
 | 
			
		||||
	lineInfo := dbl.GetLineInfo(filename)
 | 
			
		||||
	if lineInfo == nil {
 | 
			
		||||
		return nil, NoSourceError
 | 
			
		||||
	}
 | 
			
		||||
	var (
 | 
			
		||||
		pcs      []uint64
 | 
			
		||||
		lastaddr uint64
 | 
			
		||||
		sm       = newStateMachine(lineInfo)
 | 
			
		||||
		buf      = bytes.NewBuffer(lineInfo.Instructions)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
 | 
			
		||||
		findAndExecOpcode(sm, buf, b)
 | 
			
		||||
		if sm.address > end {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if sm.address >= begin && sm.address > lastaddr {
 | 
			
		||||
			lastaddr = sm.address
 | 
			
		||||
			pcs = append(pcs, sm.address)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return pcs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
 | 
			
		||||
	switch {
 | 
			
		||||
	case b == 0:
 | 
			
		||||
		execExtendedOpcode(sm, b, buf)
 | 
			
		||||
	case b < sm.dbl.Prologue.OpcodeBase:
 | 
			
		||||
		execStandardOpcode(sm, b, buf)
 | 
			
		||||
	default:
 | 
			
		||||
		execSpecialOpcode(sm, b)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func execSpecialOpcode(sm *StateMachine, instr byte) {
 | 
			
		||||
	var (
 | 
			
		||||
		opcode  = uint8(instr)
 | 
			
		||||
		decoded = opcode - sm.dbl.Prologue.OpcodeBase
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if sm.dbl.Prologue.InitialIsStmt == uint8(1) {
 | 
			
		||||
		sm.isStmt = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
 | 
			
		||||
	sm.line += sm.lastDelta
 | 
			
		||||
	sm.address += uint64(decoded / sm.dbl.Prologue.LineRange)
 | 
			
		||||
	sm.basicBlock = false
 | 
			
		||||
	sm.lastWasStandard = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func execExtendedOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
 | 
			
		||||
	_, _ = util.DecodeULEB128(buf)
 | 
			
		||||
	b, _ := buf.ReadByte()
 | 
			
		||||
	fn, ok := extendedopcodes[b]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		panic(fmt.Sprintf("Encountered unknown extended opcode %#v\n", b))
 | 
			
		||||
	}
 | 
			
		||||
	sm.lastWasStandard = false
 | 
			
		||||
 | 
			
		||||
	fn(sm, buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func execStandardOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
 | 
			
		||||
	fn, ok := standardopcodes[instr]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		panic(fmt.Sprintf("Encountered unknown standard opcode %#v\n", instr))
 | 
			
		||||
	}
 | 
			
		||||
	sm.lastWasStandard = true
 | 
			
		||||
 | 
			
		||||
	fn(sm, buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.basicBlock = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	addr, _ := util.DecodeULEB128(buf)
 | 
			
		||||
	sm.address += addr * uint64(sm.dbl.Prologue.MinInstrLength)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	line, _ := util.DecodeSLEB128(buf)
 | 
			
		||||
	sm.line += int(line)
 | 
			
		||||
	sm.lastDelta = int(line)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setfile(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	i, _ := util.DecodeULEB128(buf)
 | 
			
		||||
	sm.file = sm.dbl.FileNames[i-1].Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	c, _ := util.DecodeULEB128(buf)
 | 
			
		||||
	sm.column = uint(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.isStmt = !sm.isStmt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.basicBlock = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.address += (255 / uint64(sm.dbl.Prologue.LineRange))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	var operand uint16
 | 
			
		||||
	binary.Read(buf, binary.LittleEndian, &operand)
 | 
			
		||||
 | 
			
		||||
	sm.address += uint64(operand)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.endSeq = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	var addr uint64
 | 
			
		||||
 | 
			
		||||
	binary.Read(buf, binary.LittleEndian, &addr)
 | 
			
		||||
 | 
			
		||||
	sm.address = addr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func definefile(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	var (
 | 
			
		||||
		_, _ = util.ParseString(buf)
 | 
			
		||||
		_, _ = util.DecodeULEB128(buf)
 | 
			
		||||
		_, _ = util.DecodeULEB128(buf)
 | 
			
		||||
		_, _ = util.DecodeULEB128(buf)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// Don't do anything here yet.
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										84
									
								
								vendor/github.com/derekparker/delve/dwarf/op/op.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										84
									
								
								vendor/github.com/derekparker/delve/dwarf/op/op.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,84 +0,0 @@
 | 
			
		||||
package op
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DW_OP_addr           = 0x3
 | 
			
		||||
	DW_OP_call_frame_cfa = 0x9c
 | 
			
		||||
	DW_OP_plus           = 0x22
 | 
			
		||||
	DW_OP_consts         = 0x11
 | 
			
		||||
	DW_OP_plus_uconsts   = 0x23
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type stackfn func(*bytes.Buffer, []int64, int64) ([]int64, error)
 | 
			
		||||
 | 
			
		||||
var oplut = map[byte]stackfn{
 | 
			
		||||
	DW_OP_call_frame_cfa: callframecfa,
 | 
			
		||||
	DW_OP_plus:           plus,
 | 
			
		||||
	DW_OP_consts:         consts,
 | 
			
		||||
	DW_OP_addr:           addr,
 | 
			
		||||
	DW_OP_plus_uconsts:   plusuconsts,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
 | 
			
		||||
	stack := make([]int64, 0, 3)
 | 
			
		||||
	buf := bytes.NewBuffer(instructions)
 | 
			
		||||
 | 
			
		||||
	for opcode, err := buf.ReadByte(); err == nil; opcode, err = buf.ReadByte() {
 | 
			
		||||
		fn, ok := oplut[opcode]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return 0, fmt.Errorf("invalid instruction %#v", opcode)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		stack, err = fn(buf, stack, cfa)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(stack) == 0 {
 | 
			
		||||
		return 0, errors.New("empty OP stack")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return stack[len(stack)-1], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func callframecfa(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
 | 
			
		||||
	if cfa == 0 {
 | 
			
		||||
		return stack, fmt.Errorf("Could not retrieve CFA for current PC")
 | 
			
		||||
	}
 | 
			
		||||
	return append(stack, int64(cfa)), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addr(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
 | 
			
		||||
	return append(stack, int64(binary.LittleEndian.Uint64(buf.Next(8)))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func plus(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		slen   = len(stack)
 | 
			
		||||
		digits = stack[slen-2 : slen]
 | 
			
		||||
		st     = stack[:slen-2]
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	return append(st, digits[0]+digits[1]), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func plusuconsts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
 | 
			
		||||
	slen := len(stack)
 | 
			
		||||
	num, _ := util.DecodeULEB128(buf)
 | 
			
		||||
	stack[slen-1] = stack[slen-1] + int64(num)
 | 
			
		||||
	return stack, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func consts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
 | 
			
		||||
	num, _ := util.DecodeSLEB128(buf)
 | 
			
		||||
	return append(stack, num), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								vendor/github.com/derekparker/delve/proc/arch.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/derekparker/delve/proc/arch.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,79 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import "runtime"
 | 
			
		||||
 | 
			
		||||
// Arch defines an interface for representing a
 | 
			
		||||
// CPU architecture.
 | 
			
		||||
type Arch interface {
 | 
			
		||||
	SetGStructOffset(ver GoVersion, iscgo bool)
 | 
			
		||||
	PtrSize() int
 | 
			
		||||
	BreakpointInstruction() []byte
 | 
			
		||||
	BreakpointSize() int
 | 
			
		||||
	GStructOffset() uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64 represents the AMD64 CPU architecture.
 | 
			
		||||
type AMD64 struct {
 | 
			
		||||
	ptrSize                 int
 | 
			
		||||
	breakInstruction        []byte
 | 
			
		||||
	breakInstructionLen     int
 | 
			
		||||
	gStructOffset           uint64
 | 
			
		||||
	hardwareBreakpointUsage []bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64Arch returns an initialized AMD64
 | 
			
		||||
// struct.
 | 
			
		||||
func AMD64Arch() *AMD64 {
 | 
			
		||||
	var breakInstr = []byte{0xCC}
 | 
			
		||||
 | 
			
		||||
	return &AMD64{
 | 
			
		||||
		ptrSize:                 8,
 | 
			
		||||
		breakInstruction:        breakInstr,
 | 
			
		||||
		breakInstructionLen:     len(breakInstr),
 | 
			
		||||
		hardwareBreakpointUsage: make([]bool, 4),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetGStructOffset sets the offset of the G struct on the AMD64
 | 
			
		||||
// arch struct. The offset is dependent on the Go compiler Version
 | 
			
		||||
// and whether or not the target program was externally linked.
 | 
			
		||||
func (a *AMD64) SetGStructOffset(ver GoVersion, isextld bool) {
 | 
			
		||||
	switch runtime.GOOS {
 | 
			
		||||
	case "darwin":
 | 
			
		||||
		a.gStructOffset = 0x8a0
 | 
			
		||||
	case "linux":
 | 
			
		||||
		a.gStructOffset = 0xfffffffffffffff0
 | 
			
		||||
		if isextld || ver.AfterOrEqual(GoVersion{1, 5, -1, 2, 0}) || ver.IsDevel() {
 | 
			
		||||
			a.gStructOffset += 8
 | 
			
		||||
		}
 | 
			
		||||
	case "windows":
 | 
			
		||||
		// Use ArbitraryUserPointer (0x28) as pointer to pointer
 | 
			
		||||
		// to G struct per:
 | 
			
		||||
		// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
 | 
			
		||||
		a.gStructOffset = 0x28
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PtrSize returns the size of a pointer
 | 
			
		||||
// on this architecture.
 | 
			
		||||
func (a *AMD64) PtrSize() int {
 | 
			
		||||
	return a.ptrSize
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointInstruction returns the Breakpoint
 | 
			
		||||
// instruction for this architecture.
 | 
			
		||||
func (a *AMD64) BreakpointInstruction() []byte {
 | 
			
		||||
	return a.breakInstruction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointSize returns the size of the
 | 
			
		||||
// breakpoint instruction on this architecture.
 | 
			
		||||
func (a *AMD64) BreakpointSize() int {
 | 
			
		||||
	return a.breakInstructionLen
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GStructOffset returns the offset of the G
 | 
			
		||||
// struct in thread local storage.
 | 
			
		||||
func (a *AMD64) GStructOffset() uint64 {
 | 
			
		||||
	return a.gStructOffset
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										163
									
								
								vendor/github.com/derekparker/delve/proc/breakpoints.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										163
									
								
								vendor/github.com/derekparker/delve/proc/breakpoints.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,163 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"reflect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Breakpoint represents a breakpoint. Stores information on the break
 | 
			
		||||
// point including the byte of data that originally was stored at that
 | 
			
		||||
// address.
 | 
			
		||||
type Breakpoint struct {
 | 
			
		||||
	// File & line information for printing.
 | 
			
		||||
	FunctionName string
 | 
			
		||||
	File         string
 | 
			
		||||
	Line         int
 | 
			
		||||
 | 
			
		||||
	Addr         uint64         // Address breakpoint is set for.
 | 
			
		||||
	OriginalData []byte         // If software breakpoint, the data we replace with breakpoint instruction.
 | 
			
		||||
	Name         string         // User defined name of the breakpoint
 | 
			
		||||
	ID           int            // Monotonically increasing ID.
 | 
			
		||||
	Kind         BreakpointKind // Whether this is an internal breakpoint (for next'ing or stepping).
 | 
			
		||||
 | 
			
		||||
	// Breakpoint information
 | 
			
		||||
	Tracepoint    bool     // Tracepoint flag
 | 
			
		||||
	Goroutine     bool     // Retrieve goroutine information
 | 
			
		||||
	Stacktrace    int      // Number of stack frames to retrieve
 | 
			
		||||
	Variables     []string // Variables to evaluate
 | 
			
		||||
	LoadArgs      *LoadConfig
 | 
			
		||||
	LoadLocals    *LoadConfig
 | 
			
		||||
	HitCount      map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
 | 
			
		||||
	TotalHitCount uint64         // Number of times a breakpoint has been reached
 | 
			
		||||
 | 
			
		||||
	// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
 | 
			
		||||
	// will also check if the caller is runtime.gopanic or if the return
 | 
			
		||||
	// address is in the DeferReturns array.
 | 
			
		||||
	// Next uses NextDeferBreakpoints for the breakpoint it sets on the
 | 
			
		||||
	// deferred function, DeferReturns is populated with the
 | 
			
		||||
	// addresses of calls to runtime.deferreturn in the current
 | 
			
		||||
	// function. This insures that the breakpoint on the deferred
 | 
			
		||||
	// function only triggers on panic or on the defer call to
 | 
			
		||||
	// the function, not when the function is called directly
 | 
			
		||||
	DeferReturns []uint64
 | 
			
		||||
	// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
 | 
			
		||||
	Cond ast.Expr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Breakpoint Kind determines the behavior of delve when the
 | 
			
		||||
// breakpoint is reached.
 | 
			
		||||
type BreakpointKind int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// UserBreakpoint is a user set breakpoint
 | 
			
		||||
	UserBreakpoint BreakpointKind = iota
 | 
			
		||||
	// NextBreakpoint is a breakpoint set by Next, Continue
 | 
			
		||||
	// will stop on it and delete it
 | 
			
		||||
	NextBreakpoint
 | 
			
		||||
	// NextDeferBreakpoint is a breakpoint set by Next on the
 | 
			
		||||
	// first deferred function. In addition to checking their condition
 | 
			
		||||
	// breakpoints of this kind will also check that the function has been
 | 
			
		||||
	// called by runtime.gopanic or through runtime.deferreturn.
 | 
			
		||||
	NextDeferBreakpoint
 | 
			
		||||
	// StepBreakpoint is a breakpoint set by Step on a CALL instruction,
 | 
			
		||||
	// Continue will set a new breakpoint (of NextBreakpoint kind) on the
 | 
			
		||||
	// destination of CALL, delete this breakpoint and then continue again
 | 
			
		||||
	StepBreakpoint
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (bp *Breakpoint) String() string {
 | 
			
		||||
	return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clear this breakpoint appropriately depending on whether it is a
 | 
			
		||||
// hardware or software breakpoint.
 | 
			
		||||
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
 | 
			
		||||
	if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not clear breakpoint %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return bp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointExistsError is returned when trying to set a breakpoint at
 | 
			
		||||
// an address that already has a breakpoint set for it.
 | 
			
		||||
type BreakpointExistsError struct {
 | 
			
		||||
	file string
 | 
			
		||||
	line int
 | 
			
		||||
	addr uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bpe BreakpointExistsError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InvalidAddressError represents the result of
 | 
			
		||||
// attempting to set a breakpoint at an invalid address.
 | 
			
		||||
type InvalidAddressError struct {
 | 
			
		||||
	address uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (iae InvalidAddressError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Invalid address %#v\n", iae.address)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
 | 
			
		||||
	_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
 | 
			
		||||
	if bp.Cond == nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	if bp.Kind == NextDeferBreakpoint {
 | 
			
		||||
		frames, err := thread.Stacktrace(2)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
 | 
			
		||||
			isdeferreturn := false
 | 
			
		||||
			if len(frames) >= 1 {
 | 
			
		||||
				for _, pc := range bp.DeferReturns {
 | 
			
		||||
					if frames[0].Ret == pc {
 | 
			
		||||
						isdeferreturn = true
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if !ispanic && !isdeferreturn {
 | 
			
		||||
				return false, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	scope, err := thread.Scope()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, err
 | 
			
		||||
	}
 | 
			
		||||
	v, err := scope.evalAST(bp.Cond)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, fmt.Errorf("error evaluating expression: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if v.Unreadable != nil {
 | 
			
		||||
		return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
 | 
			
		||||
	}
 | 
			
		||||
	if v.Kind != reflect.Bool {
 | 
			
		||||
		return true, errors.New("condition expression not boolean")
 | 
			
		||||
	}
 | 
			
		||||
	return constant.BoolVal(v.Value), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Internal returns true for breakpoints not set directly by the user.
 | 
			
		||||
func (bp *Breakpoint) Internal() bool {
 | 
			
		||||
	return bp.Kind != UserBreakpoint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NoBreakpointError is returned when trying to
 | 
			
		||||
// clear a breakpoint that does not exist.
 | 
			
		||||
type NoBreakpointError struct {
 | 
			
		||||
	addr uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (nbp NoBreakpointError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										67
									
								
								vendor/github.com/derekparker/delve/proc/disasm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								vendor/github.com/derekparker/delve/proc/disasm.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,67 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
type AsmInstruction struct {
 | 
			
		||||
	Loc        Location
 | 
			
		||||
	DestLoc    *Location
 | 
			
		||||
	Bytes      []byte
 | 
			
		||||
	Breakpoint bool
 | 
			
		||||
	AtPC       bool
 | 
			
		||||
	Inst       *ArchInst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type AssemblyFlavour int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	GNUFlavour = AssemblyFlavour(iota)
 | 
			
		||||
	IntelFlavour
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Disassemble disassembles target memory between startPC and endPC
 | 
			
		||||
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
 | 
			
		||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
 | 
			
		||||
func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool) ([]AsmInstruction, error) {
 | 
			
		||||
	if thread.dbp.exited {
 | 
			
		||||
		return nil, &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	mem, err := thread.readMemory(uintptr(startPC), int(endPC-startPC))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := make([]AsmInstruction, 0, len(mem)/15)
 | 
			
		||||
	pc := startPC
 | 
			
		||||
 | 
			
		||||
	var curpc uint64
 | 
			
		||||
	var regs Registers
 | 
			
		||||
	if currentGoroutine {
 | 
			
		||||
		regs, _ = thread.Registers(false)
 | 
			
		||||
		if regs != nil {
 | 
			
		||||
			curpc = regs.PC()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for len(mem) > 0 {
 | 
			
		||||
		bp, atbp := thread.dbp.Breakpoints[pc]
 | 
			
		||||
		if atbp {
 | 
			
		||||
			for i := range bp.OriginalData {
 | 
			
		||||
				mem[i] = bp.OriginalData[i]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file, line, fn := thread.dbp.PCToLine(pc)
 | 
			
		||||
		loc := Location{PC: pc, File: file, Line: line, Fn: fn}
 | 
			
		||||
		inst, err := asmDecode(mem, pc)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			atpc := currentGoroutine && (curpc == pc)
 | 
			
		||||
			destloc := thread.resolveCallArg(inst, atpc, regs)
 | 
			
		||||
			r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
 | 
			
		||||
 | 
			
		||||
			pc += uint64(inst.Size())
 | 
			
		||||
			mem = mem[inst.Size():]
 | 
			
		||||
		} else {
 | 
			
		||||
			r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
 | 
			
		||||
			pc++
 | 
			
		||||
			mem = mem[1:]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										113
									
								
								vendor/github.com/derekparker/delve/proc/go_version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										113
									
								
								vendor/github.com/derekparker/delve/proc/go_version.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,113 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GoVersion represents the Go version of
 | 
			
		||||
// the Go compiler version used to compile
 | 
			
		||||
// the target binary.
 | 
			
		||||
type GoVersion struct {
 | 
			
		||||
	Major int
 | 
			
		||||
	Minor int
 | 
			
		||||
	Rev   int
 | 
			
		||||
	Beta  int
 | 
			
		||||
	RC    int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ParseVersionString(ver string) (GoVersion, bool) {
 | 
			
		||||
	var r GoVersion
 | 
			
		||||
	var err1, err2, err3 error
 | 
			
		||||
 | 
			
		||||
	if strings.HasPrefix(ver, "devel") {
 | 
			
		||||
		return GoVersion{-1, 0, 0, 0, 0}, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.HasPrefix(ver, "go") {
 | 
			
		||||
		ver := strings.Split(ver, " ")[0]
 | 
			
		||||
		v := strings.SplitN(ver[2:], ".", 3)
 | 
			
		||||
		switch len(v) {
 | 
			
		||||
		case 2:
 | 
			
		||||
			r.Major, err1 = strconv.Atoi(v[0])
 | 
			
		||||
			vr := strings.SplitN(v[1], "beta", 2)
 | 
			
		||||
			if len(vr) == 2 {
 | 
			
		||||
				r.Beta, err3 = strconv.Atoi(vr[1])
 | 
			
		||||
			} else {
 | 
			
		||||
				vr = strings.SplitN(v[1], "rc", 2)
 | 
			
		||||
				if len(vr) == 2 {
 | 
			
		||||
					r.RC, err3 = strconv.Atoi(vr[1])
 | 
			
		||||
				} else {
 | 
			
		||||
					r.Minor, err2 = strconv.Atoi(v[1])
 | 
			
		||||
					if err2 != nil {
 | 
			
		||||
						return GoVersion{}, false
 | 
			
		||||
					}
 | 
			
		||||
					return r, true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			r.Minor, err2 = strconv.Atoi(vr[0])
 | 
			
		||||
			r.Rev = -1
 | 
			
		||||
 | 
			
		||||
			if err1 != nil || err2 != nil || err3 != nil {
 | 
			
		||||
				return GoVersion{}, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return r, true
 | 
			
		||||
 | 
			
		||||
		case 3:
 | 
			
		||||
 | 
			
		||||
			r.Major, err1 = strconv.Atoi(v[0])
 | 
			
		||||
			r.Minor, err2 = strconv.Atoi(v[1])
 | 
			
		||||
			r.Rev, err3 = strconv.Atoi(v[2])
 | 
			
		||||
			if err1 != nil || err2 != nil || err3 != nil {
 | 
			
		||||
				return GoVersion{}, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return r, true
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return GoVersion{}, false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return GoVersion{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AfterOrEqual returns whether one GoVersion is after or
 | 
			
		||||
// equal to the other.
 | 
			
		||||
func (v *GoVersion) AfterOrEqual(b GoVersion) bool {
 | 
			
		||||
	if v.Major < b.Major {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if v.Major > b.Major {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Minor < b.Minor {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if v.Minor > b.Minor {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Rev < b.Rev {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if v.Rev > b.Rev {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Beta < b.Beta {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.RC < b.RC {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsDevel returns whether the GoVersion
 | 
			
		||||
// is a development version.
 | 
			
		||||
func (v *GoVersion) IsDevel() bool {
 | 
			
		||||
	return v.Major < 0
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								vendor/github.com/derekparker/delve/proc/mach_exc.defs
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										119
									
								
								vendor/github.com/derekparker/delve/proc/mach_exc.defs
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,119 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 | 
			
		||||
 * 
 | 
			
		||||
 * This file contains Original Code and/or Modifications of Original Code
 | 
			
		||||
 * as defined in and that are subject to the Apple Public Source License
 | 
			
		||||
 * Version 2.0 (the 'License'). You may not use this file except in
 | 
			
		||||
 * compliance with the License. The rights granted to you under the License
 | 
			
		||||
 * may not be used to create, or enable the creation or redistribution of,
 | 
			
		||||
 * unlawful or unlicensed copies of an Apple operating system, or to
 | 
			
		||||
 * circumvent, violate, or enable the circumvention or violation of, any
 | 
			
		||||
 * terms of an Apple operating system software license agreement.
 | 
			
		||||
 * 
 | 
			
		||||
 * Please obtain a copy of the License at
 | 
			
		||||
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 | 
			
		||||
 * 
 | 
			
		||||
 * The Original Code and all software distributed under the License are
 | 
			
		||||
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 | 
			
		||||
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 | 
			
		||||
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 | 
			
		||||
 * Please see the License for the specific language governing rights and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 * 
 | 
			
		||||
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 | 
			
		||||
 */
 | 
			
		||||
/*
 | 
			
		||||
 * @OSF_COPYRIGHT@
 | 
			
		||||
 */
 | 
			
		||||
/* 
 | 
			
		||||
 * Mach Operating System
 | 
			
		||||
 * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
 | 
			
		||||
 * All Rights Reserved.
 | 
			
		||||
 * 
 | 
			
		||||
 * Permission to use, copy, modify and distribute this software and its
 | 
			
		||||
 * documentation is hereby granted, provided that both the copyright
 | 
			
		||||
 * notice and this permission notice appear in all copies of the
 | 
			
		||||
 * software, derivative works or modified versions, and any portions
 | 
			
		||||
 * thereof, and that both notices appear in supporting documentation.
 | 
			
		||||
 * 
 | 
			
		||||
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 | 
			
		||||
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 | 
			
		||||
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 | 
			
		||||
 * 
 | 
			
		||||
 * Carnegie Mellon requests users of this software to return to
 | 
			
		||||
 * 
 | 
			
		||||
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 | 
			
		||||
 *  School of Computer Science
 | 
			
		||||
 *  Carnegie Mellon University
 | 
			
		||||
 *  Pittsburgh PA 15213-3890
 | 
			
		||||
 * 
 | 
			
		||||
 * any improvements or extensions that they make and grant Carnegie Mellon
 | 
			
		||||
 * the rights to redistribute these changes.
 | 
			
		||||
 */
 | 
			
		||||
/*
 | 
			
		||||
 */
 | 
			
		||||
/*
 | 
			
		||||
 * Abstract:
 | 
			
		||||
 *	MiG definitions file for Mach exception interface.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
subsystem
 | 
			
		||||
#if	KERNEL_USER
 | 
			
		||||
	  KernelUser
 | 
			
		||||
#endif
 | 
			
		||||
		     mach_exc 2405;
 | 
			
		||||
 | 
			
		||||
#include <mach/std_types.defs>
 | 
			
		||||
#include <mach/mach_types.defs>
 | 
			
		||||
 | 
			
		||||
ServerPrefix catch_;
 | 
			
		||||
 | 
			
		||||
type mach_exception_data_t	= array[*:2] of int64_t;
 | 
			
		||||
type exception_type_t		= int;
 | 
			
		||||
 | 
			
		||||
routine		mach_exception_raise(
 | 
			
		||||
#if	KERNEL_USER
 | 
			
		||||
			exception_port	: mach_port_move_send_t;
 | 
			
		||||
			thread		: mach_port_move_send_t;
 | 
			
		||||
			task		: mach_port_move_send_t;
 | 
			
		||||
#else	/* KERNEL_USER */
 | 
			
		||||
			exception_port	: mach_port_t;
 | 
			
		||||
			thread		: mach_port_t;
 | 
			
		||||
			task		: mach_port_t;
 | 
			
		||||
#endif	/* KERNEL_USER */
 | 
			
		||||
			exception	: exception_type_t;
 | 
			
		||||
			code		: mach_exception_data_t
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
routine		mach_exception_raise_state(
 | 
			
		||||
#if	KERNEL_USER
 | 
			
		||||
			exception_port	: mach_port_move_send_t;
 | 
			
		||||
#else	/* KERNEL_USER */
 | 
			
		||||
			exception_port	: mach_port_t;
 | 
			
		||||
#endif	/* KERNEL_USER */
 | 
			
		||||
			exception	: exception_type_t;
 | 
			
		||||
			code		: mach_exception_data_t, const;
 | 
			
		||||
		  inout flavor		: int;
 | 
			
		||||
			old_state	: thread_state_t, const;
 | 
			
		||||
		    out new_state	: thread_state_t);
 | 
			
		||||
 | 
			
		||||
routine		mach_exception_raise_state_identity(
 | 
			
		||||
#if	KERNEL_USER
 | 
			
		||||
			exception_port  : mach_port_move_send_t;
 | 
			
		||||
			thread		: mach_port_move_send_t;
 | 
			
		||||
			task		: mach_port_move_send_t;
 | 
			
		||||
#else	/* KERNEL_USER */
 | 
			
		||||
			exception_port  : mach_port_t;
 | 
			
		||||
			thread          : mach_port_t;
 | 
			
		||||
			task            : mach_port_t;
 | 
			
		||||
#endif	/* KERNEL_USER */
 | 
			
		||||
			exception       : exception_type_t;
 | 
			
		||||
			code            : mach_exception_data_t;
 | 
			
		||||
		  inout flavor          : int;
 | 
			
		||||
			old_state       : thread_state_t;
 | 
			
		||||
		    out new_state       : thread_state_t);
 | 
			
		||||
 | 
			
		||||
/* vim: set ft=c : */
 | 
			
		||||
							
								
								
									
										57
									
								
								vendor/github.com/derekparker/delve/proc/mem.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										57
									
								
								vendor/github.com/derekparker/delve/proc/mem.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,57 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
const cacheEnabled = true
 | 
			
		||||
 | 
			
		||||
type memoryReadWriter interface {
 | 
			
		||||
	readMemory(addr uintptr, size int) (data []byte, err error)
 | 
			
		||||
	writeMemory(addr uintptr, data []byte) (written int, err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type memCache struct {
 | 
			
		||||
	cacheAddr uintptr
 | 
			
		||||
	cache     []byte
 | 
			
		||||
	mem       memoryReadWriter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) contains(addr uintptr, size int) bool {
 | 
			
		||||
	return addr >= m.cacheAddr && addr <= (m.cacheAddr+uintptr(len(m.cache)-size))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) readMemory(addr uintptr, size int) (data []byte, err error) {
 | 
			
		||||
	if m.contains(addr, size) {
 | 
			
		||||
		d := make([]byte, size)
 | 
			
		||||
		copy(d, m.cache[addr-m.cacheAddr:])
 | 
			
		||||
		return d, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return m.mem.readMemory(addr, size)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) {
 | 
			
		||||
	return m.mem.writeMemory(addr, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cacheMemory(mem memoryReadWriter, addr uintptr, size int) memoryReadWriter {
 | 
			
		||||
	if !cacheEnabled {
 | 
			
		||||
		return mem
 | 
			
		||||
	}
 | 
			
		||||
	if size <= 0 {
 | 
			
		||||
		return mem
 | 
			
		||||
	}
 | 
			
		||||
	if cacheMem, isCache := mem.(*memCache); isCache {
 | 
			
		||||
		if cacheMem.contains(addr, size) {
 | 
			
		||||
			return mem
 | 
			
		||||
		} else {
 | 
			
		||||
			cache, err := cacheMem.mem.readMemory(addr, size)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return mem
 | 
			
		||||
			}
 | 
			
		||||
			return &memCache{addr, cache, mem}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	cache, err := mem.readMemory(addr, size)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return mem
 | 
			
		||||
	}
 | 
			
		||||
	return &memCache{addr, cache, mem}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										942
									
								
								vendor/github.com/derekparker/delve/proc/proc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										942
									
								
								vendor/github.com/derekparker/delve/proc/proc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,942 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/gosym"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/frame"
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/line"
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/reader"
 | 
			
		||||
	"golang.org/x/debug/dwarf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Process represents all of the information the debugger
 | 
			
		||||
// is holding onto regarding the process we are debugging.
 | 
			
		||||
type Process struct {
 | 
			
		||||
	Pid          int         // Process Pid
 | 
			
		||||
	Process      *os.Process // Pointer to process struct for the actual process we are debugging
 | 
			
		||||
	LastModified time.Time   // Time the executable of this process was last modified
 | 
			
		||||
 | 
			
		||||
	// Breakpoint table, holds information on breakpoints.
 | 
			
		||||
	// Maps instruction address to Breakpoint struct.
 | 
			
		||||
	Breakpoints map[uint64]*Breakpoint
 | 
			
		||||
 | 
			
		||||
	// List of threads mapped as such: pid -> *Thread
 | 
			
		||||
	Threads map[int]*Thread
 | 
			
		||||
 | 
			
		||||
	// Active thread
 | 
			
		||||
	CurrentThread *Thread
 | 
			
		||||
 | 
			
		||||
	// Goroutine that will be used by default to set breakpoint, eval variables, etc...
 | 
			
		||||
	// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
 | 
			
		||||
	SelectedGoroutine *G
 | 
			
		||||
 | 
			
		||||
	// Maps package names to package paths, needed to lookup types inside DWARF info
 | 
			
		||||
	packageMap map[string]string
 | 
			
		||||
 | 
			
		||||
	allGCache                   []*G
 | 
			
		||||
	dwarf                       *dwarf.Data
 | 
			
		||||
	goSymTable                  *gosym.Table
 | 
			
		||||
	frameEntries                frame.FrameDescriptionEntries
 | 
			
		||||
	lineInfo                    line.DebugLines
 | 
			
		||||
	os                          *OSProcessDetails
 | 
			
		||||
	arch                        Arch
 | 
			
		||||
	breakpointIDCounter         int
 | 
			
		||||
	internalBreakpointIDCounter int
 | 
			
		||||
	firstStart                  bool
 | 
			
		||||
	halt                        bool
 | 
			
		||||
	exited                      bool
 | 
			
		||||
	ptraceChan                  chan func()
 | 
			
		||||
	ptraceDoneChan              chan interface{}
 | 
			
		||||
	types                       map[string]dwarf.Offset
 | 
			
		||||
 | 
			
		||||
	loadModuleDataOnce sync.Once
 | 
			
		||||
	moduleData         []moduleData
 | 
			
		||||
	nameOfRuntimeType  map[uintptr]nameOfRuntimeTypeEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var NotExecutableErr = errors.New("not an executable file")
 | 
			
		||||
 | 
			
		||||
// New returns an initialized Process struct. Before returning,
 | 
			
		||||
// it will also launch a goroutine in order to handle ptrace(2)
 | 
			
		||||
// functions. For more information, see the documentation on
 | 
			
		||||
// `handlePtraceFuncs`.
 | 
			
		||||
func New(pid int) *Process {
 | 
			
		||||
	dbp := &Process{
 | 
			
		||||
		Pid:               pid,
 | 
			
		||||
		Threads:           make(map[int]*Thread),
 | 
			
		||||
		Breakpoints:       make(map[uint64]*Breakpoint),
 | 
			
		||||
		firstStart:        true,
 | 
			
		||||
		os:                new(OSProcessDetails),
 | 
			
		||||
		ptraceChan:        make(chan func()),
 | 
			
		||||
		ptraceDoneChan:    make(chan interface{}),
 | 
			
		||||
		nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry),
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: find better way to determine proc arch (perhaps use executable file info)
 | 
			
		||||
	switch runtime.GOARCH {
 | 
			
		||||
	case "amd64":
 | 
			
		||||
		dbp.arch = AMD64Arch()
 | 
			
		||||
	}
 | 
			
		||||
	go dbp.handlePtraceFuncs()
 | 
			
		||||
	return dbp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessExitedError indicates that the process has exited and contains both
 | 
			
		||||
// process id and exit status.
 | 
			
		||||
type ProcessExitedError struct {
 | 
			
		||||
	Pid    int
 | 
			
		||||
	Status int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (pe ProcessExitedError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Detach from the process being debugged, optionally killing it.
 | 
			
		||||
func (dbp *Process) Detach(kill bool) (err error) {
 | 
			
		||||
	if dbp.Running() {
 | 
			
		||||
		if err = dbp.Halt(); err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !kill {
 | 
			
		||||
		// Clean up any breakpoints we've set.
 | 
			
		||||
		for _, bp := range dbp.Breakpoints {
 | 
			
		||||
			if bp != nil {
 | 
			
		||||
				_, err := dbp.ClearBreakpoint(bp.Addr)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		err = PtraceDetach(dbp.Pid, 0)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if kill {
 | 
			
		||||
			err = killProcess(dbp.Pid)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Exited returns whether the debugged
 | 
			
		||||
// process has exited.
 | 
			
		||||
func (dbp *Process) Exited() bool {
 | 
			
		||||
	return dbp.exited
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Running returns whether the debugged
 | 
			
		||||
// process is currently executing.
 | 
			
		||||
func (dbp *Process) Running() bool {
 | 
			
		||||
	for _, th := range dbp.Threads {
 | 
			
		||||
		if th.running {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadInformation finds the executable and then uses it
 | 
			
		||||
// to parse the following information:
 | 
			
		||||
// * Dwarf .debug_frame section
 | 
			
		||||
// * Dwarf .debug_line section
 | 
			
		||||
// * Go symbol table.
 | 
			
		||||
func (dbp *Process) LoadInformation(path string) error {
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
 | 
			
		||||
	exe, path, err := dbp.findExecutable(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	fi, err := os.Stat(path)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		dbp.LastModified = fi.ModTime()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wg.Add(5)
 | 
			
		||||
	go dbp.loadProcessInformation(&wg)
 | 
			
		||||
	go dbp.parseDebugFrame(exe, &wg)
 | 
			
		||||
	go dbp.obtainGoSymbols(exe, &wg)
 | 
			
		||||
	go dbp.parseDebugLineInfo(exe, &wg)
 | 
			
		||||
	go dbp.loadTypeMap(&wg)
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindFileLocation returns the PC for a given file:line.
 | 
			
		||||
// Assumes that `file` is normailzed to lower case and '/' on Windows.
 | 
			
		||||
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
 | 
			
		||||
	pc, fn, err := dbp.goSymTable.LineToPC(fileName, lineno)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if fn.Entry == pc {
 | 
			
		||||
		pc, _ = dbp.FirstPCAfterPrologue(fn, true)
 | 
			
		||||
	}
 | 
			
		||||
	return pc, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindFunctionLocation finds address of a function's line
 | 
			
		||||
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
 | 
			
		||||
// If lineOffset is passed FindFunctionLocation will return the address of that line
 | 
			
		||||
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
 | 
			
		||||
// Note that setting breakpoints at that address will cause surprising behavior:
 | 
			
		||||
// https://github.com/derekparker/delve/issues/170
 | 
			
		||||
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
 | 
			
		||||
	origfn := dbp.goSymTable.LookupFunc(funcName)
 | 
			
		||||
	if origfn == nil {
 | 
			
		||||
		return 0, fmt.Errorf("Could not find function %s\n", funcName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if firstLine {
 | 
			
		||||
		return dbp.FirstPCAfterPrologue(origfn, false)
 | 
			
		||||
	} else if lineOffset > 0 {
 | 
			
		||||
		filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry)
 | 
			
		||||
		breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset)
 | 
			
		||||
		return breakAddr, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return origfn.Entry, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurrentLocation returns the location of the current thread.
 | 
			
		||||
func (dbp *Process) CurrentLocation() (*Location, error) {
 | 
			
		||||
	return dbp.CurrentThread.Location()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestManualStop sets the `halt` flag and
 | 
			
		||||
// sends SIGSTOP to all threads.
 | 
			
		||||
func (dbp *Process) RequestManualStop() error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	dbp.halt = true
 | 
			
		||||
	return dbp.requestManualStop()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
 | 
			
		||||
// break point table. Setting a break point must be thread specific due to
 | 
			
		||||
// ptrace actions needing the thread to be in a signal-delivery-stop.
 | 
			
		||||
func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
 | 
			
		||||
	tid := dbp.CurrentThread.ID
 | 
			
		||||
 | 
			
		||||
	if bp, ok := dbp.FindBreakpoint(addr); ok {
 | 
			
		||||
		return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, l, fn := dbp.goSymTable.PCToLine(uint64(addr))
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return nil, InvalidAddressError{address: addr}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newBreakpoint := &Breakpoint{
 | 
			
		||||
		FunctionName: fn.Name,
 | 
			
		||||
		File:         f,
 | 
			
		||||
		Line:         l,
 | 
			
		||||
		Addr:         addr,
 | 
			
		||||
		Kind:         kind,
 | 
			
		||||
		Cond:         cond,
 | 
			
		||||
		HitCount:     map[int]uint64{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kind != UserBreakpoint {
 | 
			
		||||
		dbp.internalBreakpointIDCounter++
 | 
			
		||||
		newBreakpoint.ID = dbp.internalBreakpointIDCounter
 | 
			
		||||
	} else {
 | 
			
		||||
		dbp.breakpointIDCounter++
 | 
			
		||||
		newBreakpoint.ID = dbp.breakpointIDCounter
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	thread := dbp.Threads[tid]
 | 
			
		||||
	originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	newBreakpoint.OriginalData = originalData
 | 
			
		||||
	dbp.Breakpoints[addr] = newBreakpoint
 | 
			
		||||
 | 
			
		||||
	return newBreakpoint, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearBreakpoint clears the breakpoint at addr.
 | 
			
		||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil, &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	bp, ok := dbp.FindBreakpoint(addr)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, NoBreakpointError{addr: addr}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := bp.Clear(dbp.CurrentThread); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(dbp.Breakpoints, addr)
 | 
			
		||||
 | 
			
		||||
	return bp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Status returns the status of the current main thread context.
 | 
			
		||||
func (dbp *Process) Status() *WaitStatus {
 | 
			
		||||
	return dbp.CurrentThread.Status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next continues execution until the next source line.
 | 
			
		||||
func (dbp *Process) Next() (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	for i := range dbp.Breakpoints {
 | 
			
		||||
		if dbp.Breakpoints[i].Internal() {
 | 
			
		||||
			return fmt.Errorf("next while nexting")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = dbp.next(false); err != nil {
 | 
			
		||||
		switch err.(type) {
 | 
			
		||||
		case ThreadBlockedError, NoReturnAddr: // Noop
 | 
			
		||||
		default:
 | 
			
		||||
			dbp.ClearInternalBreakpoints()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dbp.Continue()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Continue continues execution of the debugged
 | 
			
		||||
// process. It will continue until it hits a breakpoint
 | 
			
		||||
// or is otherwise stopped.
 | 
			
		||||
func (dbp *Process) Continue() error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		if err := dbp.resume(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dbp.allGCache = nil
 | 
			
		||||
		for _, th := range dbp.Threads {
 | 
			
		||||
			th.clearBreakpointState()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		trapthread, err := dbp.trapWait(-1)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := dbp.Halt(); err != nil {
 | 
			
		||||
			return dbp.exitGuard(err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if err := dbp.pickCurrentThread(trapthread); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch {
 | 
			
		||||
		case dbp.CurrentThread.CurrentBreakpoint == nil:
 | 
			
		||||
			// runtime.Breakpoint or manual stop
 | 
			
		||||
			if dbp.CurrentThread.onRuntimeBreakpoint() {
 | 
			
		||||
				for i := 0; i < 2; i++ {
 | 
			
		||||
					if err = dbp.CurrentThread.StepInstruction(); err != nil {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return dbp.conditionErrors()
 | 
			
		||||
		case dbp.CurrentThread.onTriggeredInternalBreakpoint():
 | 
			
		||||
			if dbp.CurrentThread.CurrentBreakpoint.Kind == StepBreakpoint {
 | 
			
		||||
				// See description of proc.(*Process).next for the meaning of StepBreakpoints
 | 
			
		||||
				if err := dbp.conditionErrors(); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				pc, err := dbp.CurrentThread.PC()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				text, err := dbp.CurrentThread.Disassemble(pc, pc+maxInstructionLength, true)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				// here we either set a breakpoint into the destination of the CALL
 | 
			
		||||
				// instruction or we determined that the called function is hidden,
 | 
			
		||||
				// either way we need to resume execution
 | 
			
		||||
				if err = dbp.setStepIntoBreakpoint(text, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if err := dbp.ClearInternalBreakpoints(); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
				return dbp.conditionErrors()
 | 
			
		||||
			}
 | 
			
		||||
		case dbp.CurrentThread.onTriggeredBreakpoint():
 | 
			
		||||
			onNextGoroutine, err := dbp.CurrentThread.onNextGoroutine()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			if onNextGoroutine {
 | 
			
		||||
				err := dbp.ClearInternalBreakpoints()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			return dbp.conditionErrors()
 | 
			
		||||
		default:
 | 
			
		||||
			// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) conditionErrors() error {
 | 
			
		||||
	var condErr error
 | 
			
		||||
	for _, th := range dbp.Threads {
 | 
			
		||||
		if th.CurrentBreakpoint != nil && th.BreakpointConditionError != nil {
 | 
			
		||||
			if condErr == nil {
 | 
			
		||||
				condErr = th.BreakpointConditionError
 | 
			
		||||
			} else {
 | 
			
		||||
				return fmt.Errorf("multiple errors evaluating conditions")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return condErr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// pick a new dbp.CurrentThread, with the following priority:
 | 
			
		||||
// 	- a thread with onTriggeredInternalBreakpoint() == true
 | 
			
		||||
// 	- a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
 | 
			
		||||
// 	- trapthread
 | 
			
		||||
func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
 | 
			
		||||
	for _, th := range dbp.Threads {
 | 
			
		||||
		if th.onTriggeredInternalBreakpoint() {
 | 
			
		||||
			return dbp.SwitchThread(th.ID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if trapthread.onTriggeredBreakpoint() {
 | 
			
		||||
		return dbp.SwitchThread(trapthread.ID)
 | 
			
		||||
	}
 | 
			
		||||
	for _, th := range dbp.Threads {
 | 
			
		||||
		if th.onTriggeredBreakpoint() {
 | 
			
		||||
			return dbp.SwitchThread(th.ID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return dbp.SwitchThread(trapthread.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Step will continue until another source line is reached.
 | 
			
		||||
// Will step into functions.
 | 
			
		||||
func (dbp *Process) Step() (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	for i := range dbp.Breakpoints {
 | 
			
		||||
		if dbp.Breakpoints[i].Internal() {
 | 
			
		||||
			return fmt.Errorf("next while nexting")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = dbp.next(true); err != nil {
 | 
			
		||||
		switch err.(type) {
 | 
			
		||||
		case ThreadBlockedError, NoReturnAddr: // Noop
 | 
			
		||||
		default:
 | 
			
		||||
			dbp.ClearInternalBreakpoints()
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dbp.Continue()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns an expression that evaluates to true when the current goroutine is g
 | 
			
		||||
func sameGoroutineCondition(g *G) ast.Expr {
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return &ast.BinaryExpr{
 | 
			
		||||
		Op: token.EQL,
 | 
			
		||||
		X: &ast.SelectorExpr{
 | 
			
		||||
			X: &ast.SelectorExpr{
 | 
			
		||||
				X:   &ast.Ident{Name: "runtime"},
 | 
			
		||||
				Sel: &ast.Ident{Name: "curg"},
 | 
			
		||||
			},
 | 
			
		||||
			Sel: &ast.Ident{Name: "goid"},
 | 
			
		||||
		},
 | 
			
		||||
		Y: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(g.ID)},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepInstruction will continue the current thread for exactly
 | 
			
		||||
// one instruction. This method affects only the thread
 | 
			
		||||
// asssociated with the selected goroutine. All other
 | 
			
		||||
// threads will remain stopped.
 | 
			
		||||
func (dbp *Process) StepInstruction() (err error) {
 | 
			
		||||
	if dbp.SelectedGoroutine == nil {
 | 
			
		||||
		return errors.New("cannot single step: no selected goroutine")
 | 
			
		||||
	}
 | 
			
		||||
	if dbp.SelectedGoroutine.thread == nil {
 | 
			
		||||
		// Step called on parked goroutine
 | 
			
		||||
		if _, err := dbp.SetBreakpoint(dbp.SelectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return dbp.Continue()
 | 
			
		||||
	}
 | 
			
		||||
	dbp.allGCache = nil
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	dbp.SelectedGoroutine.thread.clearBreakpointState()
 | 
			
		||||
	err = dbp.SelectedGoroutine.thread.StepInstruction()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp.SelectedGoroutine.thread.SetCurrentBreakpoint()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepOut will continue until the current goroutine exits the
 | 
			
		||||
// function currently being executed or a deferred function is executed
 | 
			
		||||
func (dbp *Process) StepOut() error {
 | 
			
		||||
	cond := sameGoroutineCondition(dbp.SelectedGoroutine)
 | 
			
		||||
 | 
			
		||||
	topframe, err := topframe(dbp.SelectedGoroutine, dbp.CurrentThread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pcs := []uint64{}
 | 
			
		||||
 | 
			
		||||
	var deferpc uint64 = 0
 | 
			
		||||
	if filepath.Ext(topframe.Current.File) == ".go" {
 | 
			
		||||
		if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.DeferPC != 0 {
 | 
			
		||||
			_, _, deferfn := dbp.goSymTable.PCToLine(dbp.SelectedGoroutine.DeferPC)
 | 
			
		||||
			deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			pcs = append(pcs, deferpc)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if topframe.Ret == 0 && deferpc == 0 {
 | 
			
		||||
		return errors.New("nothing to stepout to")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if deferpc != 0 && deferpc != topframe.Current.PC {
 | 
			
		||||
		bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if _, ok := err.(BreakpointExistsError); !ok {
 | 
			
		||||
				dbp.ClearInternalBreakpoints()
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if bp != nil {
 | 
			
		||||
			// For StepOut we do not want to step into the deferred function
 | 
			
		||||
			// when it's called by runtime.deferreturn so we do not populate
 | 
			
		||||
			// DeferReturns.
 | 
			
		||||
			bp.DeferReturns = []uint64{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if topframe.Ret != 0 {
 | 
			
		||||
		if err := dbp.setInternalBreakpoints(topframe.Current.PC, []uint64{topframe.Ret}, NextBreakpoint, cond); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dbp.Continue()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SwitchThread changes from current thread to the thread specified by `tid`.
 | 
			
		||||
func (dbp *Process) SwitchThread(tid int) error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	if th, ok := dbp.Threads[tid]; ok {
 | 
			
		||||
		dbp.CurrentThread = th
 | 
			
		||||
		dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("thread %d does not exist", tid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SwitchGoroutine changes from current thread to the thread
 | 
			
		||||
// running the specified goroutine.
 | 
			
		||||
func (dbp *Process) SwitchGoroutine(gid int) error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	g, err := dbp.FindGoroutine(gid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		// user specified -1 and SelectedGoroutine is nil
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if g.thread != nil {
 | 
			
		||||
		return dbp.SwitchThread(g.thread.ID)
 | 
			
		||||
	}
 | 
			
		||||
	dbp.SelectedGoroutine = g
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GoroutinesInfo returns an array of G structures representing the information
 | 
			
		||||
// Delve cares about from the internal runtime G structure.
 | 
			
		||||
func (dbp *Process) GoroutinesInfo() ([]*G, error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil, &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	if dbp.allGCache != nil {
 | 
			
		||||
		return dbp.allGCache, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		threadg = map[int]*Thread{}
 | 
			
		||||
		allg    []*G
 | 
			
		||||
		rdr     = dbp.DwarfReader()
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for i := range dbp.Threads {
 | 
			
		||||
		if dbp.Threads[i].blocked() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		g, _ := dbp.Threads[i].GetG()
 | 
			
		||||
		if g != nil {
 | 
			
		||||
			threadg[g.ID] = dbp.Threads[i]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addr, err := rdr.AddrFor("runtime.allglen")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	allglenBytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	allglen := binary.LittleEndian.Uint64(allglenBytes)
 | 
			
		||||
 | 
			
		||||
	rdr.Seek(0)
 | 
			
		||||
	allgentryaddr, err := rdr.AddrFor("runtime.allgs")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// try old name (pre Go 1.6)
 | 
			
		||||
		allgentryaddr, err = rdr.AddrFor("runtime.allg")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
 | 
			
		||||
	allgptr := binary.LittleEndian.Uint64(faddr)
 | 
			
		||||
 | 
			
		||||
	for i := uint64(0); i < allglen; i++ {
 | 
			
		||||
		gvar, err := dbp.CurrentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		g, err := gvar.parseG()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if thread, allocated := threadg[g.ID]; allocated {
 | 
			
		||||
			loc, err := thread.Location()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			g.thread = thread
 | 
			
		||||
			// Prefer actual thread location information.
 | 
			
		||||
			g.CurrentLoc = *loc
 | 
			
		||||
		}
 | 
			
		||||
		if g.Status != Gdead {
 | 
			
		||||
			allg = append(allg, g)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbp.allGCache = allg
 | 
			
		||||
	return allg, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *G) Thread() *Thread {
 | 
			
		||||
	return g.thread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Halt stops all threads.
 | 
			
		||||
func (dbp *Process) Halt() (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	for _, th := range dbp.Threads {
 | 
			
		||||
		if err := th.Halt(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers obtains register values from the
 | 
			
		||||
// "current" thread of the traced process.
 | 
			
		||||
func (dbp *Process) Registers() (Registers, error) {
 | 
			
		||||
	return dbp.CurrentThread.Registers(false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PC returns the PC of the current thread.
 | 
			
		||||
func (dbp *Process) PC() (uint64, error) {
 | 
			
		||||
	return dbp.CurrentThread.PC()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurrentBreakpoint returns the breakpoint the current thread
 | 
			
		||||
// is stopped at.
 | 
			
		||||
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
 | 
			
		||||
	return dbp.CurrentThread.CurrentBreakpoint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DwarfReader returns a reader for the dwarf data
 | 
			
		||||
func (dbp *Process) DwarfReader() *reader.Reader {
 | 
			
		||||
	return reader.New(dbp.dwarf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sources returns list of source files that comprise the debugged binary.
 | 
			
		||||
func (dbp *Process) Sources() map[string]*gosym.Obj {
 | 
			
		||||
	return dbp.goSymTable.Files
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Funcs returns list of functions present in the debugged program.
 | 
			
		||||
func (dbp *Process) Funcs() []gosym.Func {
 | 
			
		||||
	return dbp.goSymTable.Funcs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Types returns list of types present in the debugged program.
 | 
			
		||||
func (dbp *Process) Types() ([]string, error) {
 | 
			
		||||
	types := make([]string, 0, len(dbp.types))
 | 
			
		||||
	for k := range dbp.types {
 | 
			
		||||
		types = append(types, k)
 | 
			
		||||
	}
 | 
			
		||||
	return types, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PCToLine converts an instruction address to a file/line/function.
 | 
			
		||||
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
 | 
			
		||||
	return dbp.goSymTable.PCToLine(pc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindBreakpointByID finds the breakpoint for the given ID.
 | 
			
		||||
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
 | 
			
		||||
	for _, bp := range dbp.Breakpoints {
 | 
			
		||||
		if bp.ID == id {
 | 
			
		||||
			return bp, true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindBreakpoint finds the breakpoint for the given pc.
 | 
			
		||||
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
 | 
			
		||||
	// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
 | 
			
		||||
	if bp, ok := dbp.Breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok {
 | 
			
		||||
		return bp, true
 | 
			
		||||
	}
 | 
			
		||||
	// Directly use addr to lookup breakpoint.
 | 
			
		||||
	if bp, ok := dbp.Breakpoints[pc]; ok {
 | 
			
		||||
		return bp, true
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns a new Process struct.
 | 
			
		||||
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
 | 
			
		||||
	if attach {
 | 
			
		||||
		var err error
 | 
			
		||||
		dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.Pid) })
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		_, _, err = dbp.wait(dbp.Pid, 0)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	proc, err := os.FindProcess(dbp.Pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.Process = proc
 | 
			
		||||
	err = dbp.LoadInformation(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := dbp.updateThreadList(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ver, isextld, err := dbp.getGoInformation()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.arch.SetGStructOffset(ver, isextld)
 | 
			
		||||
	// SelectedGoroutine can not be set correctly by the call to updateThreadList
 | 
			
		||||
	// because without calling SetGStructOffset we can not read the G struct of CurrentThread
 | 
			
		||||
	// but without calling updateThreadList we can not examine memory to determine
 | 
			
		||||
	// the offset of g struct inside TLS
 | 
			
		||||
	dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
 | 
			
		||||
 | 
			
		||||
	panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bp, err := dbp.SetBreakpoint(panicpc, UserBreakpoint, nil)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			bp.Name = "unrecovered-panic"
 | 
			
		||||
			bp.ID = -1
 | 
			
		||||
			dbp.breakpointIDCounter--
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) ClearInternalBreakpoints() error {
 | 
			
		||||
	for _, bp := range dbp.Breakpoints {
 | 
			
		||||
		if !bp.Internal() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for i := range dbp.Threads {
 | 
			
		||||
		if dbp.Threads[i].CurrentBreakpoint != nil && dbp.Threads[i].CurrentBreakpoint.Internal() {
 | 
			
		||||
			dbp.Threads[i].CurrentBreakpoint = nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) handlePtraceFuncs() {
 | 
			
		||||
	// We must ensure here that we are running on the same thread during
 | 
			
		||||
	// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
 | 
			
		||||
	// all commands after PTRACE_ATTACH to come from the same thread.
 | 
			
		||||
	runtime.LockOSThread()
 | 
			
		||||
 | 
			
		||||
	for fn := range dbp.ptraceChan {
 | 
			
		||||
		fn()
 | 
			
		||||
		dbp.ptraceDoneChan <- nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) execPtraceFunc(fn func()) {
 | 
			
		||||
	dbp.ptraceChan <- fn
 | 
			
		||||
	<-dbp.ptraceDoneChan
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) {
 | 
			
		||||
	vv, err := dbp.EvalPackageVariable("runtime.buildVersion", LoadConfig{true, 0, 64, 0, 0})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("Could not determine version number: %v\n", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	if vv.Unreadable != nil {
 | 
			
		||||
		err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ver, ok := ParseVersionString(constant.StringVal(vv.Value))
 | 
			
		||||
	if !ok {
 | 
			
		||||
		err = fmt.Errorf("Could not parse version number: %v\n", vv.Value)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rdr := dbp.DwarfReader()
 | 
			
		||||
	rdr.Seek(0)
 | 
			
		||||
	for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return ver, isextld, err
 | 
			
		||||
		}
 | 
			
		||||
		if prod, ok := entry.Val(dwarf.AttrProducer).(string); ok && (strings.HasPrefix(prod, "GNU AS")) {
 | 
			
		||||
			isextld = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindGoroutine returns a G struct representing the goroutine
 | 
			
		||||
// specified by `gid`.
 | 
			
		||||
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
 | 
			
		||||
	if gid == -1 {
 | 
			
		||||
		return dbp.SelectedGoroutine, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gs, err := dbp.GoroutinesInfo()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for i := range gs {
 | 
			
		||||
		if gs[i].ID == gid {
 | 
			
		||||
			return gs[i], nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("Unknown goroutine %d", gid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConvertEvalScope returns a new EvalScope in the context of the
 | 
			
		||||
// specified goroutine ID and stack frame.
 | 
			
		||||
func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil, &ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	g, err := dbp.FindGoroutine(gid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		return dbp.CurrentThread.Scope()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var out EvalScope
 | 
			
		||||
 | 
			
		||||
	if g.thread == nil {
 | 
			
		||||
		out.Thread = dbp.CurrentThread
 | 
			
		||||
	} else {
 | 
			
		||||
		out.Thread = g.thread
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	locs, err := g.Stacktrace(frame)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if frame >= len(locs) {
 | 
			
		||||
		return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out.PC, out.CFA = locs[frame].Current.PC, locs[frame].CFA
 | 
			
		||||
 | 
			
		||||
	return &out, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) postExit() {
 | 
			
		||||
	dbp.exited = true
 | 
			
		||||
	close(dbp.ptraceChan)
 | 
			
		||||
	close(dbp.ptraceDoneChan)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										643
									
								
								vendor/github.com/derekparker/delve/proc/proc_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										643
									
								
								vendor/github.com/derekparker/delve/proc/proc_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,643 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/gosym"
 | 
			
		||||
	"debug/pe"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/windows"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/frame"
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/line"
 | 
			
		||||
	"golang.org/x/debug/dwarf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OSProcessDetails holds Windows specific information.
 | 
			
		||||
type OSProcessDetails struct {
 | 
			
		||||
	hProcess    syscall.Handle
 | 
			
		||||
	breakThread int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Launch creates and begins debugging a new process.
 | 
			
		||||
func Launch(cmd []string, wd string) (*Process, error) {
 | 
			
		||||
	argv0Go, err := filepath.Abs(cmd[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	peFile, err := openExecutablePath(argv0Go)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, NotExecutableErr
 | 
			
		||||
	}
 | 
			
		||||
	peFile.Close()
 | 
			
		||||
 | 
			
		||||
	// Duplicate the stdin/stdout/stderr handles
 | 
			
		||||
	files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)}
 | 
			
		||||
	p, _ := syscall.GetCurrentProcess()
 | 
			
		||||
	fd := make([]syscall.Handle, len(files))
 | 
			
		||||
	for i := range files {
 | 
			
		||||
		err := syscall.DuplicateHandle(p, syscall.Handle(files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		defer syscall.CloseHandle(syscall.Handle(fd[i]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argv0, err := syscall.UTF16PtrFromString(argv0Go)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create suitable command line for CreateProcess
 | 
			
		||||
	// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L326
 | 
			
		||||
	// adapted from standard library makeCmdLine
 | 
			
		||||
	// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L86
 | 
			
		||||
	var cmdLineGo string
 | 
			
		||||
	if len(cmd) >= 1 {
 | 
			
		||||
		for _, v := range cmd {
 | 
			
		||||
			if cmdLineGo != "" {
 | 
			
		||||
				cmdLineGo += " "
 | 
			
		||||
			}
 | 
			
		||||
			cmdLineGo += syscall.EscapeArg(v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var cmdLine *uint16
 | 
			
		||||
	if cmdLineGo != "" {
 | 
			
		||||
		if cmdLine, err = syscall.UTF16PtrFromString(cmdLineGo); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var workingDir *uint16
 | 
			
		||||
	if wd != "" {
 | 
			
		||||
		if workingDir, err = syscall.UTF16PtrFromString(wd); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Initialize the startup info and create process
 | 
			
		||||
	si := new(sys.StartupInfo)
 | 
			
		||||
	si.Cb = uint32(unsafe.Sizeof(*si))
 | 
			
		||||
	si.Flags = syscall.STARTF_USESTDHANDLES
 | 
			
		||||
	si.StdInput = sys.Handle(fd[0])
 | 
			
		||||
	si.StdOutput = sys.Handle(fd[1])
 | 
			
		||||
	si.StdErr = sys.Handle(fd[2])
 | 
			
		||||
	pi := new(sys.ProcessInformation)
 | 
			
		||||
 | 
			
		||||
	dbp := New(0)
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		if wd == "" {
 | 
			
		||||
			err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, nil, si, pi)
 | 
			
		||||
		} else {
 | 
			
		||||
			err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, workingDir, si, pi)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	sys.CloseHandle(sys.Handle(pi.Process))
 | 
			
		||||
	sys.CloseHandle(sys.Handle(pi.Thread))
 | 
			
		||||
 | 
			
		||||
	dbp.Pid = int(pi.ProcessId)
 | 
			
		||||
 | 
			
		||||
	return newDebugProcess(dbp, argv0Go)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newDebugProcess prepares process pid for debugging.
 | 
			
		||||
func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
 | 
			
		||||
	// It should not actually be possible for the
 | 
			
		||||
	// call to waitForDebugEvent to fail, since Windows
 | 
			
		||||
	// will always fire a CREATE_PROCESS_DEBUG_EVENT event
 | 
			
		||||
	// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
 | 
			
		||||
	// Attaching with DebugActiveProcess has similar effect.
 | 
			
		||||
	var err error
 | 
			
		||||
	var tid, exitCode int
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if tid == 0 {
 | 
			
		||||
		dbp.postExit()
 | 
			
		||||
		return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
 | 
			
		||||
	}
 | 
			
		||||
	// Suspend all threads so that the call to _ContinueDebugEvent will
 | 
			
		||||
	// not resume the target.
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
		_, err := _SuspendThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		err = _ContinueDebugEvent(uint32(dbp.Pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return initializeDebugProcess(dbp, exepath, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findExePath searches for process pid, and returns its executable path.
 | 
			
		||||
func findExePath(pid int) (string, error) {
 | 
			
		||||
	// Original code suggested different approach (see below).
 | 
			
		||||
	// Maybe it could be useful in the future.
 | 
			
		||||
	//
 | 
			
		||||
	// Find executable path from PID/handle on Windows:
 | 
			
		||||
	// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
 | 
			
		||||
 | 
			
		||||
	p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	defer syscall.CloseHandle(p)
 | 
			
		||||
 | 
			
		||||
	n := uint32(128)
 | 
			
		||||
	for {
 | 
			
		||||
		buf := make([]uint16, int(n))
 | 
			
		||||
		err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
 | 
			
		||||
		switch err {
 | 
			
		||||
		case syscall.ERROR_INSUFFICIENT_BUFFER:
 | 
			
		||||
			// try bigger buffer
 | 
			
		||||
			n *= 2
 | 
			
		||||
			// but stop if it gets too big
 | 
			
		||||
			if n > 10000 {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
		case nil:
 | 
			
		||||
			return syscall.UTF16ToString(buf[:n]), nil
 | 
			
		||||
		default:
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to an existing process with the given PID.
 | 
			
		||||
func Attach(pid int) (*Process, error) {
 | 
			
		||||
	// TODO: Probably should have SeDebugPrivilege before starting here.
 | 
			
		||||
	err := _DebugActiveProcess(uint32(pid))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	exepath, err := findExePath(pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return newDebugProcess(New(pid), exepath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Kill kills the process.
 | 
			
		||||
func (dbp *Process) Kill() error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !dbp.Threads[dbp.Pid].Stopped() {
 | 
			
		||||
		return errors.New("process must be stopped in order to kill it")
 | 
			
		||||
	}
 | 
			
		||||
	// TODO: Should not have to ignore failures here,
 | 
			
		||||
	// but some tests appear to Kill twice causing
 | 
			
		||||
	// this to fail on second attempt.
 | 
			
		||||
	_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
 | 
			
		||||
	dbp.exited = true
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) requestManualStop() error {
 | 
			
		||||
	return _DebugBreakProcess(dbp.os.hProcess)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) updateThreadList() error {
 | 
			
		||||
	// We ignore this request since threads are being
 | 
			
		||||
	// tracked as they are created/killed in waitForDebugEvent.
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*Thread, error) {
 | 
			
		||||
	if thread, ok := dbp.Threads[threadID]; ok {
 | 
			
		||||
		return thread, nil
 | 
			
		||||
	}
 | 
			
		||||
	thread := &Thread{
 | 
			
		||||
		ID:  threadID,
 | 
			
		||||
		dbp: dbp,
 | 
			
		||||
		os:  new(OSSpecificDetails),
 | 
			
		||||
	}
 | 
			
		||||
	thread.os.hThread = hThread
 | 
			
		||||
	dbp.Threads[threadID] = thread
 | 
			
		||||
	if dbp.CurrentThread == nil {
 | 
			
		||||
		dbp.SwitchThread(thread.ID)
 | 
			
		||||
	}
 | 
			
		||||
	if suspendNewThreads {
 | 
			
		||||
		_, err := _SuspendThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return thread, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) parseDebugFrame(exe *pe.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	debugFrameSec := exe.Section(".debug_frame")
 | 
			
		||||
	debugInfoSec := exe.Section(".debug_info")
 | 
			
		||||
 | 
			
		||||
	if debugFrameSec != nil && debugInfoSec != nil {
 | 
			
		||||
		debugFrame, err := debugFrameSec.Data()
 | 
			
		||||
		if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
 | 
			
		||||
			fmt.Println("could not get .debug_frame section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
 | 
			
		||||
			debugFrame = debugFrame[:debugFrameSec.VirtualSize]
 | 
			
		||||
		}
 | 
			
		||||
		dat, err := debugInfoSec.Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .debug_info section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("could not find .debug_frame section in binary")
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
 | 
			
		||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
 | 
			
		||||
	for _, s := range f.Symbols {
 | 
			
		||||
		if s.Name != name {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if s.SectionNumber <= 0 {
 | 
			
		||||
			return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
 | 
			
		||||
		}
 | 
			
		||||
		if len(f.Sections) < int(s.SectionNumber) {
 | 
			
		||||
			return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
 | 
			
		||||
		}
 | 
			
		||||
		return s, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("no %s symbol found", name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
 | 
			
		||||
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
 | 
			
		||||
	ssym, err := findPESymbol(f, sname)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	esym, err := findPESymbol(f, ename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if ssym.SectionNumber != esym.SectionNumber {
 | 
			
		||||
		return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
 | 
			
		||||
	}
 | 
			
		||||
	sect := f.Sections[ssym.SectionNumber-1]
 | 
			
		||||
	data, err := sect.Data()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return data[ssym.Value:esym.Value], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
 | 
			
		||||
func pcln(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
 | 
			
		||||
	var imageBase uint64
 | 
			
		||||
	switch oh := exe.OptionalHeader.(type) {
 | 
			
		||||
	case *pe.OptionalHeader32:
 | 
			
		||||
		imageBase = uint64(oh.ImageBase)
 | 
			
		||||
	case *pe.OptionalHeader64:
 | 
			
		||||
		imageBase = oh.ImageBase
 | 
			
		||||
	default:
 | 
			
		||||
		return 0, nil, nil, fmt.Errorf("pe file format not recognized")
 | 
			
		||||
	}
 | 
			
		||||
	if sect := exe.Section(".text"); sect != nil {
 | 
			
		||||
		textStart = imageBase + uint64(sect.VirtualAddress)
 | 
			
		||||
	}
 | 
			
		||||
	if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
 | 
			
		||||
		// We didn't find the symbols, so look for the names used in 1.3 and earlier.
 | 
			
		||||
		// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
 | 
			
		||||
		var err2 error
 | 
			
		||||
		if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
 | 
			
		||||
			return 0, nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
 | 
			
		||||
		// Same as above.
 | 
			
		||||
		var err2 error
 | 
			
		||||
		if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
 | 
			
		||||
			return 0, nil, nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return textStart, symtab, pclntab, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) obtainGoSymbols(exe *pe.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	_, symdat, pclndat, err := pcln(exe)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("could not get Go symbols", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
 | 
			
		||||
	tab, err := gosym.NewTable(symdat, pcln)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("could not get initialize line table", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.goSymTable = tab
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) parseDebugLineInfo(exe *pe.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	if sec := exe.Section(".debug_line"); sec != nil {
 | 
			
		||||
		debugLine, err := sec.Data()
 | 
			
		||||
		if err != nil && uint32(len(debugLine)) < sec.Size {
 | 
			
		||||
			fmt.Println("could not get .debug_line section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
 | 
			
		||||
			debugLine = debugLine[:sec.VirtualSize]
 | 
			
		||||
		}
 | 
			
		||||
		dbp.lineInfo = line.Parse(debugLine)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("could not find .debug_line section in binary")
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var UnsupportedArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) findExecutable(path string) (*pe.File, string, error) {
 | 
			
		||||
	peFile, err := openExecutablePath(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, path, err
 | 
			
		||||
	}
 | 
			
		||||
	if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
 | 
			
		||||
		return nil, path, UnsupportedArchErr
 | 
			
		||||
	}
 | 
			
		||||
	dbp.dwarf, err = dwarfFromPE(peFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, path, err
 | 
			
		||||
	}
 | 
			
		||||
	return peFile, path, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func openExecutablePath(path string) (*pe.File, error) {
 | 
			
		||||
	f, err := os.OpenFile(path, 0, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return pe.NewFile(f)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Adapted from src/debug/pe/file.go: pe.(*File).DWARF()
 | 
			
		||||
func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
 | 
			
		||||
	// There are many other DWARF sections, but these
 | 
			
		||||
	// are the ones the debug/dwarf package uses.
 | 
			
		||||
	// Don't bother loading others.
 | 
			
		||||
	var names = [...]string{"abbrev", "info", "line", "str"}
 | 
			
		||||
	var dat [len(names)][]byte
 | 
			
		||||
	for i, name := range names {
 | 
			
		||||
		name = ".debug_" + name
 | 
			
		||||
		s := f.Section(name)
 | 
			
		||||
		if s == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		b, err := s.Data()
 | 
			
		||||
		if err != nil && uint32(len(b)) < s.Size {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if 0 < s.VirtualSize && s.VirtualSize < s.Size {
 | 
			
		||||
			b = b[:s.VirtualSize]
 | 
			
		||||
		}
 | 
			
		||||
		dat[i] = b
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
 | 
			
		||||
	return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type waitForDebugEventFlags int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	waitBlocking waitForDebugEventFlags = 1 << iota
 | 
			
		||||
	waitSuspendNewThreads
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
 | 
			
		||||
	var debugEvent _DEBUG_EVENT
 | 
			
		||||
	shouldExit := false
 | 
			
		||||
	for {
 | 
			
		||||
		continueStatus := uint32(_DBG_CONTINUE)
 | 
			
		||||
		var milliseconds uint32 = 0
 | 
			
		||||
		if flags&waitBlocking != 0 {
 | 
			
		||||
			milliseconds = syscall.INFINITE
 | 
			
		||||
		}
 | 
			
		||||
		// Wait for a debug event...
 | 
			
		||||
		err := _WaitForDebugEvent(&debugEvent, milliseconds)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// ... handle each event kind ...
 | 
			
		||||
		unionPtr := unsafe.Pointer(&debugEvent.U[0])
 | 
			
		||||
		switch debugEvent.DebugEventCode {
 | 
			
		||||
		case _CREATE_PROCESS_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			hFile := debugInfo.File
 | 
			
		||||
			if hFile != 0 && hFile != syscall.InvalidHandle {
 | 
			
		||||
				err = syscall.CloseHandle(hFile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return 0, 0, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			dbp.os.hProcess = debugInfo.Process
 | 
			
		||||
			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, 0, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case _CREATE_THREAD_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, 0, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case _EXIT_THREAD_DEBUG_EVENT:
 | 
			
		||||
			delete(dbp.Threads, int(debugEvent.ThreadId))
 | 
			
		||||
			break
 | 
			
		||||
		case _OUTPUT_DEBUG_STRING_EVENT:
 | 
			
		||||
			//TODO: Handle debug output strings
 | 
			
		||||
			break
 | 
			
		||||
		case _LOAD_DLL_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			hFile := debugInfo.File
 | 
			
		||||
			if hFile != 0 && hFile != syscall.InvalidHandle {
 | 
			
		||||
				err = syscall.CloseHandle(hFile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return 0, 0, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case _UNLOAD_DLL_DEBUG_EVENT:
 | 
			
		||||
			break
 | 
			
		||||
		case _RIP_EVENT:
 | 
			
		||||
			break
 | 
			
		||||
		case _EXCEPTION_DEBUG_EVENT:
 | 
			
		||||
			exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			if code := exception.ExceptionRecord.ExceptionCode; code == _EXCEPTION_BREAKPOINT || code == _EXCEPTION_SINGLE_STEP {
 | 
			
		||||
				tid := int(debugEvent.ThreadId)
 | 
			
		||||
				dbp.os.breakThread = tid
 | 
			
		||||
				return tid, 0, nil
 | 
			
		||||
			} else {
 | 
			
		||||
				continueStatus = _DBG_EXCEPTION_NOT_HANDLED
 | 
			
		||||
			}
 | 
			
		||||
		case _EXIT_PROCESS_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			exitCode = int(debugInfo.ExitCode)
 | 
			
		||||
			shouldExit = true
 | 
			
		||||
		default:
 | 
			
		||||
			return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// .. and then continue unless we received an event that indicated we should break into debugger.
 | 
			
		||||
		err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if shouldExit {
 | 
			
		||||
			return 0, exitCode, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	var tid, exitCode int
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if tid == 0 {
 | 
			
		||||
		dbp.postExit()
 | 
			
		||||
		return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
 | 
			
		||||
	}
 | 
			
		||||
	th := dbp.Threads[tid]
 | 
			
		||||
	return th, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
 | 
			
		||||
	wg.Done()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
			
		||||
	return 0, nil, fmt.Errorf("not implemented: wait")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
 | 
			
		||||
	// While the debug event that stopped the target was being propagated
 | 
			
		||||
	// other target threads could generate other debug events.
 | 
			
		||||
	// After this function we need to know about all the threads
 | 
			
		||||
	// stopped on a breakpoint. To do that we first suspend all target
 | 
			
		||||
	// threads and then repeatedly call _ContinueDebugEvent followed by
 | 
			
		||||
	// waitForDebugEvent in non-blocking mode.
 | 
			
		||||
	// We need to explicitly call SuspendThread because otherwise the
 | 
			
		||||
	// call to _ContinueDebugEvent will resume execution of some of the
 | 
			
		||||
	// target threads.
 | 
			
		||||
 | 
			
		||||
	err := trapthread.SetCurrentBreakpoint()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
		thread.running = false
 | 
			
		||||
		_, err := _SuspendThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		var err error
 | 
			
		||||
		var tid int
 | 
			
		||||
		dbp.execPtraceFunc(func() {
 | 
			
		||||
			err = _ContinueDebugEvent(uint32(dbp.Pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if tid == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		err = dbp.Threads[tid].SetCurrentBreakpoint()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) exitGuard(err error) error {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) resume() error {
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
		if thread.CurrentBreakpoint != nil {
 | 
			
		||||
			if err := thread.StepInstruction(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			thread.CurrentBreakpoint = nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
		thread.running = true
 | 
			
		||||
		_, err := _ResumeThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func killProcess(pid int) error {
 | 
			
		||||
	p, err := os.FindProcess(pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return p.Kill()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/github.com/derekparker/delve/proc/ptrace_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/derekparker/delve/proc/ptrace_windows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,13 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func PtraceAttach(pid int) error {
 | 
			
		||||
	return fmt.Errorf("not implemented: PtraceAttach")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func PtraceDetach(tid, sig int) error {
 | 
			
		||||
	return _DebugActiveProcessStop(uint32(tid))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										325
									
								
								vendor/github.com/derekparker/delve/proc/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										325
									
								
								vendor/github.com/derekparker/delve/proc/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,325 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"rsc.io/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Regs is a wrapper for sys.PtraceRegs.
 | 
			
		||||
type Regs struct {
 | 
			
		||||
	regs   *sys.PtraceRegs
 | 
			
		||||
	fpregs []Register
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Slice() []Register {
 | 
			
		||||
	var regs = []struct {
 | 
			
		||||
		k string
 | 
			
		||||
		v uint64
 | 
			
		||||
	}{
 | 
			
		||||
		{"Rip", r.regs.Rip},
 | 
			
		||||
		{"Rsp", r.regs.Rsp},
 | 
			
		||||
		{"Rax", r.regs.Rax},
 | 
			
		||||
		{"Rbx", r.regs.Rbx},
 | 
			
		||||
		{"Rcx", r.regs.Rcx},
 | 
			
		||||
		{"Rdx", r.regs.Rdx},
 | 
			
		||||
		{"Rdi", r.regs.Rdi},
 | 
			
		||||
		{"Rsi", r.regs.Rsi},
 | 
			
		||||
		{"Rbp", r.regs.Rbp},
 | 
			
		||||
		{"R8", r.regs.R8},
 | 
			
		||||
		{"R9", r.regs.R9},
 | 
			
		||||
		{"R10", r.regs.R10},
 | 
			
		||||
		{"R11", r.regs.R11},
 | 
			
		||||
		{"R12", r.regs.R12},
 | 
			
		||||
		{"R13", r.regs.R13},
 | 
			
		||||
		{"R14", r.regs.R14},
 | 
			
		||||
		{"R15", r.regs.R15},
 | 
			
		||||
		{"Orig_rax", r.regs.Orig_rax},
 | 
			
		||||
		{"Cs", r.regs.Cs},
 | 
			
		||||
		{"Eflags", r.regs.Eflags},
 | 
			
		||||
		{"Ss", r.regs.Ss},
 | 
			
		||||
		{"Fs_base", r.regs.Fs_base},
 | 
			
		||||
		{"Gs_base", r.regs.Gs_base},
 | 
			
		||||
		{"Ds", r.regs.Ds},
 | 
			
		||||
		{"Es", r.regs.Es},
 | 
			
		||||
		{"Fs", r.regs.Fs},
 | 
			
		||||
		{"Gs", r.regs.Gs},
 | 
			
		||||
	}
 | 
			
		||||
	out := make([]Register, 0, len(regs)+len(r.fpregs))
 | 
			
		||||
	for _, reg := range regs {
 | 
			
		||||
		if reg.k == "Eflags" {
 | 
			
		||||
			out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
 | 
			
		||||
		} else {
 | 
			
		||||
			out = appendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	out = append(out, r.fpregs...)
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PC returns the value of RIP register.
 | 
			
		||||
func (r *Regs) PC() uint64 {
 | 
			
		||||
	return r.regs.PC()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SP returns the value of RSP register.
 | 
			
		||||
func (r *Regs) SP() uint64 {
 | 
			
		||||
	return r.regs.Rsp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CX returns the value of RCX register.
 | 
			
		||||
func (r *Regs) CX() uint64 {
 | 
			
		||||
	return r.regs.Rcx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TLS returns the address of the thread
 | 
			
		||||
// local storage memory segment.
 | 
			
		||||
func (r *Regs) TLS() uint64 {
 | 
			
		||||
	return r.regs.Fs_base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC sets RIP to the value specified by 'pc'.
 | 
			
		||||
func (r *Regs) SetPC(thread *Thread, pc uint64) (err error) {
 | 
			
		||||
	r.regs.SetPC(pc)
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Get(n int) (uint64, error) {
 | 
			
		||||
	reg := x86asm.Reg(n)
 | 
			
		||||
	const (
 | 
			
		||||
		mask8  = 0x000f
 | 
			
		||||
		mask16 = 0x00ff
 | 
			
		||||
		mask32 = 0xffff
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	switch reg {
 | 
			
		||||
	// 8-bit
 | 
			
		||||
	case x86asm.AL:
 | 
			
		||||
		return r.regs.Rax & mask8, nil
 | 
			
		||||
	case x86asm.CL:
 | 
			
		||||
		return r.regs.Rcx & mask8, nil
 | 
			
		||||
	case x86asm.DL:
 | 
			
		||||
		return r.regs.Rdx & mask8, nil
 | 
			
		||||
	case x86asm.BL:
 | 
			
		||||
		return r.regs.Rbx & mask8, nil
 | 
			
		||||
	case x86asm.AH:
 | 
			
		||||
		return (r.regs.Rax >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.CH:
 | 
			
		||||
		return (r.regs.Rax >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.DH:
 | 
			
		||||
		return (r.regs.Rdx >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.BH:
 | 
			
		||||
		return (r.regs.Rbx >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.SPB:
 | 
			
		||||
		return r.regs.Rsp & mask8, nil
 | 
			
		||||
	case x86asm.BPB:
 | 
			
		||||
		return r.regs.Rbp & mask8, nil
 | 
			
		||||
	case x86asm.SIB:
 | 
			
		||||
		return r.regs.Rsi & mask8, nil
 | 
			
		||||
	case x86asm.DIB:
 | 
			
		||||
		return r.regs.Rdi & mask8, nil
 | 
			
		||||
	case x86asm.R8B:
 | 
			
		||||
		return r.regs.R8 & mask8, nil
 | 
			
		||||
	case x86asm.R9B:
 | 
			
		||||
		return r.regs.R9 & mask8, nil
 | 
			
		||||
	case x86asm.R10B:
 | 
			
		||||
		return r.regs.R10 & mask8, nil
 | 
			
		||||
	case x86asm.R11B:
 | 
			
		||||
		return r.regs.R11 & mask8, nil
 | 
			
		||||
	case x86asm.R12B:
 | 
			
		||||
		return r.regs.R12 & mask8, nil
 | 
			
		||||
	case x86asm.R13B:
 | 
			
		||||
		return r.regs.R13 & mask8, nil
 | 
			
		||||
	case x86asm.R14B:
 | 
			
		||||
		return r.regs.R14 & mask8, nil
 | 
			
		||||
	case x86asm.R15B:
 | 
			
		||||
		return r.regs.R15 & mask8, nil
 | 
			
		||||
 | 
			
		||||
	// 16-bit
 | 
			
		||||
	case x86asm.AX:
 | 
			
		||||
		return r.regs.Rax & mask16, nil
 | 
			
		||||
	case x86asm.CX:
 | 
			
		||||
		return r.regs.Rcx & mask16, nil
 | 
			
		||||
	case x86asm.DX:
 | 
			
		||||
		return r.regs.Rdx & mask16, nil
 | 
			
		||||
	case x86asm.BX:
 | 
			
		||||
		return r.regs.Rbx & mask16, nil
 | 
			
		||||
	case x86asm.SP:
 | 
			
		||||
		return r.regs.Rsp & mask16, nil
 | 
			
		||||
	case x86asm.BP:
 | 
			
		||||
		return r.regs.Rbp & mask16, nil
 | 
			
		||||
	case x86asm.SI:
 | 
			
		||||
		return r.regs.Rsi & mask16, nil
 | 
			
		||||
	case x86asm.DI:
 | 
			
		||||
		return r.regs.Rdi & mask16, nil
 | 
			
		||||
	case x86asm.R8W:
 | 
			
		||||
		return r.regs.R8 & mask16, nil
 | 
			
		||||
	case x86asm.R9W:
 | 
			
		||||
		return r.regs.R9 & mask16, nil
 | 
			
		||||
	case x86asm.R10W:
 | 
			
		||||
		return r.regs.R10 & mask16, nil
 | 
			
		||||
	case x86asm.R11W:
 | 
			
		||||
		return r.regs.R11 & mask16, nil
 | 
			
		||||
	case x86asm.R12W:
 | 
			
		||||
		return r.regs.R12 & mask16, nil
 | 
			
		||||
	case x86asm.R13W:
 | 
			
		||||
		return r.regs.R13 & mask16, nil
 | 
			
		||||
	case x86asm.R14W:
 | 
			
		||||
		return r.regs.R14 & mask16, nil
 | 
			
		||||
	case x86asm.R15W:
 | 
			
		||||
		return r.regs.R15 & mask16, nil
 | 
			
		||||
 | 
			
		||||
	// 32-bit
 | 
			
		||||
	case x86asm.EAX:
 | 
			
		||||
		return r.regs.Rax & mask32, nil
 | 
			
		||||
	case x86asm.ECX:
 | 
			
		||||
		return r.regs.Rcx & mask32, nil
 | 
			
		||||
	case x86asm.EDX:
 | 
			
		||||
		return r.regs.Rdx & mask32, nil
 | 
			
		||||
	case x86asm.EBX:
 | 
			
		||||
		return r.regs.Rbx & mask32, nil
 | 
			
		||||
	case x86asm.ESP:
 | 
			
		||||
		return r.regs.Rsp & mask32, nil
 | 
			
		||||
	case x86asm.EBP:
 | 
			
		||||
		return r.regs.Rbp & mask32, nil
 | 
			
		||||
	case x86asm.ESI:
 | 
			
		||||
		return r.regs.Rsi & mask32, nil
 | 
			
		||||
	case x86asm.EDI:
 | 
			
		||||
		return r.regs.Rdi & mask32, nil
 | 
			
		||||
	case x86asm.R8L:
 | 
			
		||||
		return r.regs.R8 & mask32, nil
 | 
			
		||||
	case x86asm.R9L:
 | 
			
		||||
		return r.regs.R9 & mask32, nil
 | 
			
		||||
	case x86asm.R10L:
 | 
			
		||||
		return r.regs.R10 & mask32, nil
 | 
			
		||||
	case x86asm.R11L:
 | 
			
		||||
		return r.regs.R11 & mask32, nil
 | 
			
		||||
	case x86asm.R12L:
 | 
			
		||||
		return r.regs.R12 & mask32, nil
 | 
			
		||||
	case x86asm.R13L:
 | 
			
		||||
		return r.regs.R13 & mask32, nil
 | 
			
		||||
	case x86asm.R14L:
 | 
			
		||||
		return r.regs.R14 & mask32, nil
 | 
			
		||||
	case x86asm.R15L:
 | 
			
		||||
		return r.regs.R15 & mask32, nil
 | 
			
		||||
 | 
			
		||||
	// 64-bit
 | 
			
		||||
	case x86asm.RAX:
 | 
			
		||||
		return r.regs.Rax, nil
 | 
			
		||||
	case x86asm.RCX:
 | 
			
		||||
		return r.regs.Rcx, nil
 | 
			
		||||
	case x86asm.RDX:
 | 
			
		||||
		return r.regs.Rdx, nil
 | 
			
		||||
	case x86asm.RBX:
 | 
			
		||||
		return r.regs.Rbx, nil
 | 
			
		||||
	case x86asm.RSP:
 | 
			
		||||
		return r.regs.Rsp, nil
 | 
			
		||||
	case x86asm.RBP:
 | 
			
		||||
		return r.regs.Rbp, nil
 | 
			
		||||
	case x86asm.RSI:
 | 
			
		||||
		return r.regs.Rsi, nil
 | 
			
		||||
	case x86asm.RDI:
 | 
			
		||||
		return r.regs.Rdi, nil
 | 
			
		||||
	case x86asm.R8:
 | 
			
		||||
		return r.regs.R8, nil
 | 
			
		||||
	case x86asm.R9:
 | 
			
		||||
		return r.regs.R9, nil
 | 
			
		||||
	case x86asm.R10:
 | 
			
		||||
		return r.regs.R10, nil
 | 
			
		||||
	case x86asm.R11:
 | 
			
		||||
		return r.regs.R11, nil
 | 
			
		||||
	case x86asm.R12:
 | 
			
		||||
		return r.regs.R12, nil
 | 
			
		||||
	case x86asm.R13:
 | 
			
		||||
		return r.regs.R13, nil
 | 
			
		||||
	case x86asm.R14:
 | 
			
		||||
		return r.regs.R14, nil
 | 
			
		||||
	case x86asm.R15:
 | 
			
		||||
		return r.regs.R15, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0, UnknownRegisterError
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		regs sys.PtraceRegs
 | 
			
		||||
		err  error
 | 
			
		||||
	)
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, ®s) })
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	r := &Regs{®s, nil}
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		r.fpregs, err = thread.fpRegisters()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
 | 
			
		||||
type PtraceFpRegs struct {
 | 
			
		||||
	Cwd      uint16
 | 
			
		||||
	Swd      uint16
 | 
			
		||||
	Ftw      uint16
 | 
			
		||||
	Fop      uint16
 | 
			
		||||
	Rip      uint64
 | 
			
		||||
	Rdp      uint64
 | 
			
		||||
	Mxcsr    uint32
 | 
			
		||||
	MxcrMask uint32
 | 
			
		||||
	StSpace  [32]uint32
 | 
			
		||||
	XmmSpace [256]byte
 | 
			
		||||
	padding  [24]uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PtraceXsave struct {
 | 
			
		||||
	PtraceFpRegs
 | 
			
		||||
	AvxState bool // contains AVX state
 | 
			
		||||
	YmmSpace [256]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_X86_XSTATE_MAX_SIZE = 2688
 | 
			
		||||
	_NT_X86_XSTATE       = 0x202
 | 
			
		||||
 | 
			
		||||
	_XSAVE_HEADER_START          = 512
 | 
			
		||||
	_XSAVE_HEADER_LEN            = 64
 | 
			
		||||
	_XSAVE_EXTENDED_REGION_START = 576
 | 
			
		||||
	_XSAVE_SSE_REGION_LEN        = 416
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) fpRegisters() (regs []Register, err error) {
 | 
			
		||||
	var fpregs PtraceXsave
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
 | 
			
		||||
 | 
			
		||||
	// x87 registers
 | 
			
		||||
	regs = appendWordReg(regs, "CW", fpregs.Cwd)
 | 
			
		||||
	regs = appendWordReg(regs, "SW", fpregs.Swd)
 | 
			
		||||
	regs = appendWordReg(regs, "TW", fpregs.Ftw)
 | 
			
		||||
	regs = appendWordReg(regs, "FOP", fpregs.Fop)
 | 
			
		||||
	regs = appendQwordReg(regs, "FIP", fpregs.Rip)
 | 
			
		||||
	regs = appendQwordReg(regs, "FDP", fpregs.Rdp)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(fpregs.StSpace); i += 4 {
 | 
			
		||||
		regs = appendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SSE registers
 | 
			
		||||
	regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32)
 | 
			
		||||
	regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(fpregs.XmmSpace); i += 16 {
 | 
			
		||||
		regs = appendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
 | 
			
		||||
		if fpregs.AvxState {
 | 
			
		||||
			regs = appendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										211
									
								
								vendor/github.com/derekparker/delve/proc/stack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										211
									
								
								vendor/github.com/derekparker/delve/proc/stack.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,211 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/frame"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NoReturnAddr is returned when return address
 | 
			
		||||
// could not be found during stack trace.
 | 
			
		||||
type NoReturnAddr struct {
 | 
			
		||||
	fn string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (nra NoReturnAddr) Error() string {
 | 
			
		||||
	return fmt.Sprintf("could not find return address for %s", nra.fn)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stackframe represents a frame in a system stack.
 | 
			
		||||
type Stackframe struct {
 | 
			
		||||
	// Address the function above this one on the call stack will return to.
 | 
			
		||||
	Current Location
 | 
			
		||||
	// Address of the call instruction for the function above on the call stack.
 | 
			
		||||
	Call Location
 | 
			
		||||
	// Start address of the stack frame.
 | 
			
		||||
	CFA int64
 | 
			
		||||
	// Description of the stack frame.
 | 
			
		||||
	FDE *frame.FrameDescriptionEntry
 | 
			
		||||
	// Return address for this stack frame (as read from the stack frame itself).
 | 
			
		||||
	Ret uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scope returns a new EvalScope using this frame.
 | 
			
		||||
func (frame *Stackframe) Scope(thread *Thread) *EvalScope {
 | 
			
		||||
	return &EvalScope{Thread: thread, PC: frame.Current.PC, CFA: frame.CFA}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReturnAddress returns the return address of the function
 | 
			
		||||
// this thread is executing.
 | 
			
		||||
func (t *Thread) ReturnAddress() (uint64, error) {
 | 
			
		||||
	locations, err := t.Stacktrace(2)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(locations) < 2 {
 | 
			
		||||
		return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
 | 
			
		||||
	}
 | 
			
		||||
	return locations[1].Current.PC, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) stackIterator() (*stackIterator, error) {
 | 
			
		||||
	regs, err := t.Registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return newStackIterator(t.dbp, regs.PC(), regs.SP()), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stacktrace returns the stack trace for thread.
 | 
			
		||||
// Note the locations in the array are return addresses not call addresses.
 | 
			
		||||
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
 | 
			
		||||
	it, err := t.stackIterator()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return it.stacktrace(depth)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *G) stackIterator() (*stackIterator, error) {
 | 
			
		||||
	if g.thread != nil {
 | 
			
		||||
		return g.thread.stackIterator()
 | 
			
		||||
	}
 | 
			
		||||
	return newStackIterator(g.dbp, g.PC, g.SP), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stacktrace returns the stack trace for a goroutine.
 | 
			
		||||
// Note the locations in the array are return addresses not call addresses.
 | 
			
		||||
func (g *G) Stacktrace(depth int) ([]Stackframe, error) {
 | 
			
		||||
	it, err := g.stackIterator()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return it.stacktrace(depth)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GoroutineLocation returns the location of the given
 | 
			
		||||
// goroutine.
 | 
			
		||||
func (dbp *Process) GoroutineLocation(g *G) *Location {
 | 
			
		||||
	f, l, fn := dbp.PCToLine(g.PC)
 | 
			
		||||
	return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NullAddrError is an error for a null address.
 | 
			
		||||
type NullAddrError struct{}
 | 
			
		||||
 | 
			
		||||
func (n NullAddrError) Error() string {
 | 
			
		||||
	return "NULL address"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stackIterator holds information
 | 
			
		||||
// required to iterate and walk the program
 | 
			
		||||
// stack.
 | 
			
		||||
type stackIterator struct {
 | 
			
		||||
	pc, sp uint64
 | 
			
		||||
	top    bool
 | 
			
		||||
	atend  bool
 | 
			
		||||
	frame  Stackframe
 | 
			
		||||
	dbp    *Process
 | 
			
		||||
	err    error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newStackIterator(dbp *Process, pc, sp uint64) *stackIterator {
 | 
			
		||||
	return &stackIterator{pc: pc, sp: sp, top: true, dbp: dbp, err: nil, atend: false}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next points the iterator to the next stack frame.
 | 
			
		||||
func (it *stackIterator) Next() bool {
 | 
			
		||||
	if it.err != nil || it.atend {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top)
 | 
			
		||||
	if it.err != nil {
 | 
			
		||||
		if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
 | 
			
		||||
			it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
 | 
			
		||||
			it.atend = true
 | 
			
		||||
			it.err = nil
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if it.frame.Current.Fn == nil {
 | 
			
		||||
		if it.top {
 | 
			
		||||
			it.err = fmt.Errorf("PC not associated to any function")
 | 
			
		||||
		}
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if it.frame.Ret <= 0 {
 | 
			
		||||
		it.atend = true
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	// Look for "top of stack" functions.
 | 
			
		||||
	if it.frame.Current.Fn.Name == "runtime.goexit" || it.frame.Current.Fn.Name == "runtime.rt0_go" || it.frame.Current.Fn.Name == "runtime.mcall" {
 | 
			
		||||
		it.atend = true
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	it.top = false
 | 
			
		||||
	it.pc = it.frame.Ret
 | 
			
		||||
	it.sp = uint64(it.frame.CFA)
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Frame returns the frame the iterator is pointing at.
 | 
			
		||||
func (it *stackIterator) Frame() Stackframe {
 | 
			
		||||
	if it.err != nil {
 | 
			
		||||
		panic(it.err)
 | 
			
		||||
	}
 | 
			
		||||
	return it.frame
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Err returns the error encountered during stack iteration.
 | 
			
		||||
func (it *stackIterator) Err() error {
 | 
			
		||||
	return it.err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) frameInfo(pc, sp uint64, top bool) (Stackframe, error) {
 | 
			
		||||
	f, l, fn := dbp.PCToLine(pc)
 | 
			
		||||
	fde, err := dbp.frameEntries.FDEForPC(pc)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Stackframe{}, err
 | 
			
		||||
	}
 | 
			
		||||
	spoffset, retoffset := fde.ReturnAddressOffset(pc)
 | 
			
		||||
	cfa := int64(sp) + spoffset
 | 
			
		||||
 | 
			
		||||
	retaddr := uintptr(cfa + retoffset)
 | 
			
		||||
	if retaddr == 0 {
 | 
			
		||||
		return Stackframe{}, NullAddrError{}
 | 
			
		||||
	}
 | 
			
		||||
	data, err := dbp.CurrentThread.readMemory(retaddr, dbp.arch.PtrSize())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Stackframe{}, err
 | 
			
		||||
	}
 | 
			
		||||
	r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: binary.LittleEndian.Uint64(data)}
 | 
			
		||||
	if !top {
 | 
			
		||||
		r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1)
 | 
			
		||||
		r.Call.PC, _, _ = dbp.goSymTable.LineToPC(r.Call.File, r.Call.Line)
 | 
			
		||||
	} else {
 | 
			
		||||
		r.Call = r.Current
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
 | 
			
		||||
	if depth < 0 {
 | 
			
		||||
		return nil, errors.New("negative maximum stack depth")
 | 
			
		||||
	}
 | 
			
		||||
	frames := make([]Stackframe, 0, depth+1)
 | 
			
		||||
	for it.Next() {
 | 
			
		||||
		frames = append(frames, it.Frame())
 | 
			
		||||
		if len(frames) >= depth+1 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := it.Err(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return frames, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								vendor/github.com/derekparker/delve/proc/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/derekparker/delve/proc/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,114 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import "unsafe"
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_CONTEXT_AMD64               = 0x100000
 | 
			
		||||
	_CONTEXT_CONTROL             = (_CONTEXT_AMD64 | 0x1)
 | 
			
		||||
	_CONTEXT_INTEGER             = (_CONTEXT_AMD64 | 0x2)
 | 
			
		||||
	_CONTEXT_SEGMENTS            = (_CONTEXT_AMD64 | 0x4)
 | 
			
		||||
	_CONTEXT_FLOATING_POINT      = (_CONTEXT_AMD64 | 0x8)
 | 
			
		||||
	_CONTEXT_DEBUG_REGISTERS     = (_CONTEXT_AMD64 | 0x10)
 | 
			
		||||
	_CONTEXT_FULL                = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT)
 | 
			
		||||
	_CONTEXT_ALL                 = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_SEGMENTS | _CONTEXT_FLOATING_POINT | _CONTEXT_DEBUG_REGISTERS)
 | 
			
		||||
	_CONTEXT_EXCEPTION_ACTIVE    = 0x8000000
 | 
			
		||||
	_CONTEXT_SERVICE_ACTIVE      = 0x10000000
 | 
			
		||||
	_CONTEXT_EXCEPTION_REQUEST   = 0x40000000
 | 
			
		||||
	_CONTEXT_EXCEPTION_REPORTING = 0x80000000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type _M128A struct {
 | 
			
		||||
	Low  uint64
 | 
			
		||||
	High int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type _XMM_SAVE_AREA32 struct {
 | 
			
		||||
	ControlWord    uint16
 | 
			
		||||
	StatusWord     uint16
 | 
			
		||||
	TagWord        byte
 | 
			
		||||
	Reserved1      byte
 | 
			
		||||
	ErrorOpcode    uint16
 | 
			
		||||
	ErrorOffset    uint32
 | 
			
		||||
	ErrorSelector  uint16
 | 
			
		||||
	Reserved2      uint16
 | 
			
		||||
	DataOffset     uint32
 | 
			
		||||
	DataSelector   uint16
 | 
			
		||||
	Reserved3      uint16
 | 
			
		||||
	MxCsr          uint32
 | 
			
		||||
	MxCsr_Mask     uint32
 | 
			
		||||
	FloatRegisters [8]_M128A
 | 
			
		||||
	XmmRegisters   [256]byte
 | 
			
		||||
	Reserved4      [96]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type _CONTEXT struct {
 | 
			
		||||
	P1Home uint64
 | 
			
		||||
	P2Home uint64
 | 
			
		||||
	P3Home uint64
 | 
			
		||||
	P4Home uint64
 | 
			
		||||
	P5Home uint64
 | 
			
		||||
	P6Home uint64
 | 
			
		||||
 | 
			
		||||
	ContextFlags uint32
 | 
			
		||||
	MxCsr        uint32
 | 
			
		||||
 | 
			
		||||
	SegCs  uint16
 | 
			
		||||
	SegDs  uint16
 | 
			
		||||
	SegEs  uint16
 | 
			
		||||
	SegFs  uint16
 | 
			
		||||
	SegGs  uint16
 | 
			
		||||
	SegSs  uint16
 | 
			
		||||
	EFlags uint32
 | 
			
		||||
 | 
			
		||||
	Dr0 uint64
 | 
			
		||||
	Dr1 uint64
 | 
			
		||||
	Dr2 uint64
 | 
			
		||||
	Dr3 uint64
 | 
			
		||||
	Dr6 uint64
 | 
			
		||||
	Dr7 uint64
 | 
			
		||||
 | 
			
		||||
	Rax uint64
 | 
			
		||||
	Rcx uint64
 | 
			
		||||
	Rdx uint64
 | 
			
		||||
	Rbx uint64
 | 
			
		||||
	Rsp uint64
 | 
			
		||||
	Rbp uint64
 | 
			
		||||
	Rsi uint64
 | 
			
		||||
	Rdi uint64
 | 
			
		||||
	R8  uint64
 | 
			
		||||
	R9  uint64
 | 
			
		||||
	R10 uint64
 | 
			
		||||
	R11 uint64
 | 
			
		||||
	R12 uint64
 | 
			
		||||
	R13 uint64
 | 
			
		||||
	R14 uint64
 | 
			
		||||
	R15 uint64
 | 
			
		||||
 | 
			
		||||
	Rip uint64
 | 
			
		||||
 | 
			
		||||
	FltSave _XMM_SAVE_AREA32
 | 
			
		||||
 | 
			
		||||
	VectorRegister [26]_M128A
 | 
			
		||||
	VectorControl  uint64
 | 
			
		||||
 | 
			
		||||
	DebugControl         uint64
 | 
			
		||||
	LastBranchToRip      uint64
 | 
			
		||||
	LastBranchFromRip    uint64
 | 
			
		||||
	LastExceptionToRip   uint64
 | 
			
		||||
	LastExceptionFromRip uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newCONTEXT allocates Windows CONTEXT structure aligned to 16 bytes.
 | 
			
		||||
func newCONTEXT() *_CONTEXT {
 | 
			
		||||
	var c *_CONTEXT
 | 
			
		||||
	buf := make([]byte, unsafe.Sizeof(*c)+15)
 | 
			
		||||
	return (*_CONTEXT)(unsafe.Pointer((uintptr(unsafe.Pointer(&buf[15]))) &^ 15))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type _DEBUG_EVENT struct {
 | 
			
		||||
	DebugEventCode uint32
 | 
			
		||||
	ProcessId      uint32
 | 
			
		||||
	ThreadId       uint32
 | 
			
		||||
	_              uint32 // to align Union properly
 | 
			
		||||
	U              [160]byte
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										504
									
								
								vendor/github.com/derekparker/delve/proc/threads.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										504
									
								
								vendor/github.com/derekparker/delve/proc/threads.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,504 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/gosym"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/debug/dwarf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Thread represents a single thread in the traced process
 | 
			
		||||
// ID represents the thread id or port, Process holds a reference to the
 | 
			
		||||
// Process struct that contains info on the process as
 | 
			
		||||
// a whole, and Status represents the last result of a `wait` call
 | 
			
		||||
// on this thread.
 | 
			
		||||
type Thread struct {
 | 
			
		||||
	ID                       int         // Thread ID or mach port
 | 
			
		||||
	Status                   *WaitStatus // Status returned from last wait call
 | 
			
		||||
	CurrentBreakpoint        *Breakpoint // Breakpoint thread is currently stopped at
 | 
			
		||||
	BreakpointConditionMet   bool        // Output of evaluating the breakpoint's condition
 | 
			
		||||
	BreakpointConditionError error       // Error evaluating the breakpoint's condition
 | 
			
		||||
 | 
			
		||||
	dbp            *Process
 | 
			
		||||
	singleStepping bool
 | 
			
		||||
	running        bool
 | 
			
		||||
	os             *OSSpecificDetails
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Location represents the location of a thread.
 | 
			
		||||
// Holds information on the current instruction
 | 
			
		||||
// address, the source file:line, and the function.
 | 
			
		||||
type Location struct {
 | 
			
		||||
	PC   uint64
 | 
			
		||||
	File string
 | 
			
		||||
	Line int
 | 
			
		||||
	Fn   *gosym.Func
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Continue the execution of this thread.
 | 
			
		||||
//
 | 
			
		||||
// If we are currently at a breakpoint, we'll clear it
 | 
			
		||||
// first and then resume execution. Thread will continue until
 | 
			
		||||
// it hits a breakpoint or is signaled.
 | 
			
		||||
func (thread *Thread) Continue() error {
 | 
			
		||||
	pc, err := thread.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// Check whether we are stopped at a breakpoint, and
 | 
			
		||||
	// if so, single step over it before continuing.
 | 
			
		||||
	if _, ok := thread.dbp.FindBreakpoint(pc); ok {
 | 
			
		||||
		if err := thread.StepInstruction(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return thread.resume()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepInstruction steps a single instruction.
 | 
			
		||||
//
 | 
			
		||||
// Executes exactly one instruction and then returns.
 | 
			
		||||
// If the thread is at a breakpoint, we first clear it,
 | 
			
		||||
// execute the instruction, and then replace the breakpoint.
 | 
			
		||||
// Otherwise we simply execute the next instruction.
 | 
			
		||||
func (thread *Thread) StepInstruction() (err error) {
 | 
			
		||||
	thread.running = true
 | 
			
		||||
	thread.singleStepping = true
 | 
			
		||||
	defer func() {
 | 
			
		||||
		thread.singleStepping = false
 | 
			
		||||
		thread.running = false
 | 
			
		||||
	}()
 | 
			
		||||
	pc, err := thread.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bp, ok := thread.dbp.FindBreakpoint(pc)
 | 
			
		||||
	if ok {
 | 
			
		||||
		// Clear the breakpoint so that we can continue execution.
 | 
			
		||||
		_, err = bp.Clear(thread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Restore breakpoint now that we have passed it.
 | 
			
		||||
		defer func() {
 | 
			
		||||
			err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = thread.singleStep()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if _, exited := err.(ProcessExitedError); exited {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("step failed: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Location returns the threads location, including the file:line
 | 
			
		||||
// of the corresponding source code, the function we're in
 | 
			
		||||
// and the current instruction address.
 | 
			
		||||
func (thread *Thread) Location() (*Location, error) {
 | 
			
		||||
	pc, err := thread.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	f, l, fn := thread.dbp.PCToLine(pc)
 | 
			
		||||
	return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ThreadBlockedError is returned when the thread
 | 
			
		||||
// is blocked in the scheduler.
 | 
			
		||||
type ThreadBlockedError struct{}
 | 
			
		||||
 | 
			
		||||
func (tbe ThreadBlockedError) Error() string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// returns topmost frame of g or thread if g is nil
 | 
			
		||||
func topframe(g *G, thread *Thread) (Stackframe, error) {
 | 
			
		||||
	var frames []Stackframe
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		if thread.blocked() {
 | 
			
		||||
			return Stackframe{}, ThreadBlockedError{}
 | 
			
		||||
		}
 | 
			
		||||
		frames, err = thread.Stacktrace(0)
 | 
			
		||||
	} else {
 | 
			
		||||
		frames, err = g.Stacktrace(0)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return Stackframe{}, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(frames) < 1 {
 | 
			
		||||
		return Stackframe{}, errors.New("empty stack trace")
 | 
			
		||||
	}
 | 
			
		||||
	return frames[0], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set breakpoints at every line, and the return address. Also look for
 | 
			
		||||
// a deferred function and set a breakpoint there too.
 | 
			
		||||
// If stepInto is true it will also set breakpoints inside all
 | 
			
		||||
// functions called on the current source line, for non-absolute CALLs
 | 
			
		||||
// a breakpoint of kind StepBreakpoint is set on the CALL instruction,
 | 
			
		||||
// Continue will take care of setting a breakpoint to the destination
 | 
			
		||||
// once the CALL is reached.
 | 
			
		||||
func (dbp *Process) next(stepInto bool) error {
 | 
			
		||||
	topframe, err := topframe(dbp.SelectedGoroutine, dbp.CurrentThread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	success := false
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if !success {
 | 
			
		||||
			dbp.ClearInternalBreakpoints()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	csource := filepath.Ext(topframe.Current.File) != ".go"
 | 
			
		||||
	thread := dbp.CurrentThread
 | 
			
		||||
	currentGoroutine := false
 | 
			
		||||
	if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.thread != nil {
 | 
			
		||||
		thread = dbp.SelectedGoroutine.thread
 | 
			
		||||
		currentGoroutine = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	text, err := thread.Disassemble(topframe.FDE.Begin(), topframe.FDE.End(), currentGoroutine)
 | 
			
		||||
	if err != nil && stepInto {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cond := sameGoroutineCondition(dbp.SelectedGoroutine)
 | 
			
		||||
 | 
			
		||||
	if stepInto {
 | 
			
		||||
		for _, instr := range text {
 | 
			
		||||
			if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if instr.DestLoc != nil && instr.DestLoc.Fn != nil {
 | 
			
		||||
				if err := dbp.setStepIntoBreakpoint([]AsmInstruction{instr}, cond); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				// Non-absolute call instruction, set a StepBreakpoint here
 | 
			
		||||
				if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, cond); err != nil {
 | 
			
		||||
					if _, ok := err.(BreakpointExistsError); !ok {
 | 
			
		||||
						return err
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !csource {
 | 
			
		||||
		deferreturns := []uint64{}
 | 
			
		||||
 | 
			
		||||
		// Find all runtime.deferreturn locations in the function
 | 
			
		||||
		// See documentation of Breakpoint.DeferCond for why this is necessary
 | 
			
		||||
		for _, instr := range text {
 | 
			
		||||
			if instr.IsCall() && instr.DestLoc != nil && instr.DestLoc.Fn != nil && instr.DestLoc.Fn.Name == "runtime.deferreturn" {
 | 
			
		||||
				deferreturns = append(deferreturns, instr.Loc.PC)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Set breakpoint on the most recently deferred function (if any)
 | 
			
		||||
		var deferpc uint64 = 0
 | 
			
		||||
		if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.DeferPC != 0 {
 | 
			
		||||
			_, _, deferfn := dbp.goSymTable.PCToLine(dbp.SelectedGoroutine.DeferPC)
 | 
			
		||||
			var err error
 | 
			
		||||
			deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if deferpc != 0 && deferpc != topframe.Current.PC {
 | 
			
		||||
			bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if _, ok := err.(BreakpointExistsError); !ok {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if bp != nil {
 | 
			
		||||
				bp.DeferReturns = deferreturns
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add breakpoints on all the lines in the current function
 | 
			
		||||
	pcs, err := dbp.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !csource {
 | 
			
		||||
		var covered bool
 | 
			
		||||
		for i := range pcs {
 | 
			
		||||
			if topframe.FDE.Cover(pcs[i]) {
 | 
			
		||||
				covered = true
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !covered {
 | 
			
		||||
			fn := dbp.goSymTable.PCToFunc(topframe.Ret)
 | 
			
		||||
			if dbp.SelectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Add a breakpoint on the return address for the current frame
 | 
			
		||||
	pcs = append(pcs, topframe.Ret)
 | 
			
		||||
	success = true
 | 
			
		||||
	return dbp.setInternalBreakpoints(topframe.Current.PC, pcs, NextBreakpoint, cond)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) setStepIntoBreakpoint(text []AsmInstruction, cond ast.Expr) error {
 | 
			
		||||
	if len(text) <= 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	instr := text[0]
 | 
			
		||||
 | 
			
		||||
	if instr.DestLoc == nil || instr.DestLoc.Fn == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn := instr.DestLoc.Fn
 | 
			
		||||
 | 
			
		||||
	// Ensure PC and Entry match, otherwise StepInto is likely to set
 | 
			
		||||
	// its breakpoint before DestLoc.PC and hence run too far ahead.
 | 
			
		||||
	// Calls to runtime.duffzero and duffcopy have this problem.
 | 
			
		||||
	if fn.Entry != instr.DestLoc.PC {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Skip unexported runtime functions
 | 
			
		||||
	if strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO(aarzilli): if we want to let users hide functions
 | 
			
		||||
	// or entire packages from being stepped into with 'step'
 | 
			
		||||
	// those extra checks should be done here.
 | 
			
		||||
 | 
			
		||||
	// Set a breakpoint after the function's prologue
 | 
			
		||||
	pc, _ := dbp.FirstPCAfterPrologue(fn, false)
 | 
			
		||||
	if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
 | 
			
		||||
		if _, ok := err.(BreakpointExistsError); !ok {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// setInternalBreakpoints sets a breakpoint to all addresses specified in pcs
 | 
			
		||||
// skipping over curpc and curpc-1
 | 
			
		||||
func (dbp *Process) setInternalBreakpoints(curpc uint64, pcs []uint64, kind BreakpointKind, cond ast.Expr) error {
 | 
			
		||||
	for i := range pcs {
 | 
			
		||||
		if pcs[i] == curpc || pcs[i] == curpc-1 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := dbp.SetBreakpoint(pcs[i], kind, cond); err != nil {
 | 
			
		||||
			if _, ok := err.(BreakpointExistsError); !ok {
 | 
			
		||||
				dbp.ClearInternalBreakpoints()
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC sets the PC for this thread.
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	regs, err := thread.Registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return regs.SetPC(thread, pc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) getGVariable() (*Variable, error) {
 | 
			
		||||
	regs, err := thread.Registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if thread.dbp.arch.GStructOffset() == 0 {
 | 
			
		||||
		// GetG was called through SwitchThread / updateThreadList during initialization
 | 
			
		||||
		// thread.dbp.arch isn't setup yet (it needs a CurrentThread to read global variables from)
 | 
			
		||||
		return nil, fmt.Errorf("g struct offset not initialized")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), thread.dbp.arch.PtrSize())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	gaddr := uintptr(binary.LittleEndian.Uint64(gaddrbs))
 | 
			
		||||
 | 
			
		||||
	// On Windows, the value at TLS()+GStructOffset() is a
 | 
			
		||||
	// pointer to the G struct.
 | 
			
		||||
	needsDeref := runtime.GOOS == "windows"
 | 
			
		||||
 | 
			
		||||
	return thread.newGVariable(gaddr, needsDeref)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) {
 | 
			
		||||
	typ, err := thread.dbp.findType("runtime.g")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := ""
 | 
			
		||||
 | 
			
		||||
	if deref {
 | 
			
		||||
		typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
 | 
			
		||||
	} else {
 | 
			
		||||
		name = "runtime.curg"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return thread.newVariable(name, gaddr, typ), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetG returns information on the G (goroutine) that is executing on this thread.
 | 
			
		||||
//
 | 
			
		||||
// The G structure for a thread is stored in thread local storage. Here we simply
 | 
			
		||||
// calculate the address and read and parse the G struct.
 | 
			
		||||
//
 | 
			
		||||
// We cannot simply use the allg linked list in order to find the M that represents
 | 
			
		||||
// the given OS thread and follow its G pointer because on Darwin mach ports are not
 | 
			
		||||
// universal, so our port for this thread would not map to the `id` attribute of the M
 | 
			
		||||
// structure. Also, when linked against libc, Go prefers the libc version of clone as
 | 
			
		||||
// opposed to the runtime version. This has the consequence of not setting M.id for
 | 
			
		||||
// any thread, regardless of OS.
 | 
			
		||||
//
 | 
			
		||||
// In order to get around all this craziness, we read the address of the G structure for
 | 
			
		||||
// the current thread from the thread local storage area.
 | 
			
		||||
func (thread *Thread) GetG() (g *G, err error) {
 | 
			
		||||
	gaddr, err := thread.getGVariable()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g, err = gaddr.parseG()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		g.thread = thread
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stopped returns whether the thread is stopped at
 | 
			
		||||
// the operating system level. Actual implementation
 | 
			
		||||
// is OS dependant, look in OS thread file.
 | 
			
		||||
func (thread *Thread) Stopped() bool {
 | 
			
		||||
	return thread.stopped()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Halt stops this thread from executing. Actual
 | 
			
		||||
// implementation is OS dependant. Look in OS
 | 
			
		||||
// thread file.
 | 
			
		||||
func (thread *Thread) Halt() (err error) {
 | 
			
		||||
	defer func() {
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			thread.running = false
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	if thread.Stopped() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	err = thread.halt()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Scope returns the current EvalScope for this thread.
 | 
			
		||||
func (thread *Thread) Scope() (*EvalScope, error) {
 | 
			
		||||
	locations, err := thread.Stacktrace(0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(locations) < 1 {
 | 
			
		||||
		return nil, errors.New("could not decode first frame")
 | 
			
		||||
	}
 | 
			
		||||
	return locations[0].Scope(thread), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetCurrentBreakpoint sets the current breakpoint that this
 | 
			
		||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
 | 
			
		||||
func (thread *Thread) SetCurrentBreakpoint() error {
 | 
			
		||||
	thread.CurrentBreakpoint = nil
 | 
			
		||||
	pc, err := thread.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
 | 
			
		||||
		thread.CurrentBreakpoint = bp
 | 
			
		||||
		if err = thread.SetPC(bp.Addr); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread)
 | 
			
		||||
		if thread.onTriggeredBreakpoint() {
 | 
			
		||||
			if g, err := thread.GetG(); err == nil {
 | 
			
		||||
				thread.CurrentBreakpoint.HitCount[g.ID]++
 | 
			
		||||
			}
 | 
			
		||||
			thread.CurrentBreakpoint.TotalHitCount++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) clearBreakpointState() {
 | 
			
		||||
	thread.CurrentBreakpoint = nil
 | 
			
		||||
	thread.BreakpointConditionMet = false
 | 
			
		||||
	thread.BreakpointConditionError = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) onTriggeredBreakpoint() bool {
 | 
			
		||||
	return (thread.CurrentBreakpoint != nil) && thread.BreakpointConditionMet
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) onTriggeredInternalBreakpoint() bool {
 | 
			
		||||
	return thread.onTriggeredBreakpoint() && thread.CurrentBreakpoint.Internal()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) onRuntimeBreakpoint() bool {
 | 
			
		||||
	loc, err := thread.Location()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return loc.Fn != nil && loc.Fn.Name == "runtime.breakpoint"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command
 | 
			
		||||
func (thread *Thread) onNextGoroutine() (bool, error) {
 | 
			
		||||
	var bp *Breakpoint
 | 
			
		||||
	for i := range thread.dbp.Breakpoints {
 | 
			
		||||
		if thread.dbp.Breakpoints[i].Internal() {
 | 
			
		||||
			bp = thread.dbp.Breakpoints[i]
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if bp == nil {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
	if bp.Kind == NextDeferBreakpoint {
 | 
			
		||||
		// we just want to check the condition on the goroutine id here
 | 
			
		||||
		bp.Kind = NextBreakpoint
 | 
			
		||||
		defer func() {
 | 
			
		||||
			bp.Kind = NextDeferBreakpoint
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
	return bp.checkCondition(thread)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										108
									
								
								vendor/github.com/derekparker/delve/proc/threads_linux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										108
									
								
								vendor/github.com/derekparker/delve/proc/threads_linux.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,108 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type WaitStatus sys.WaitStatus
 | 
			
		||||
 | 
			
		||||
// OSSpecificDetails hold Linux specific
 | 
			
		||||
// process details.
 | 
			
		||||
type OSSpecificDetails struct {
 | 
			
		||||
	registers sys.PtraceRegs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) halt() (err error) {
 | 
			
		||||
	err = sys.Tgkill(t.dbp.Pid, t.ID, sys.SIGSTOP)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("halt err %s on thread %d", err, t.ID)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = t.dbp.wait(t.ID, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("wait err %s on thread %d", err, t.ID)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) stopped() bool {
 | 
			
		||||
	state := status(t.ID, t.dbp.os.comm)
 | 
			
		||||
	return state == StatusTraceStop || state == StatusTraceStopT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) resume() error {
 | 
			
		||||
	return t.resumeWithSig(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) resumeWithSig(sig int) (err error) {
 | 
			
		||||
	t.running = true
 | 
			
		||||
	t.dbp.execPtraceFunc(func() { err = PtraceCont(t.ID, sig) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) singleStep() (err error) {
 | 
			
		||||
	for {
 | 
			
		||||
		t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.ID) })
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		wpid, status, err := t.dbp.wait(t.ID, 0)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if (status == nil || status.Exited()) && wpid == t.dbp.Pid {
 | 
			
		||||
			t.dbp.postExit()
 | 
			
		||||
			rs := 0
 | 
			
		||||
			if status != nil {
 | 
			
		||||
				rs = status.ExitStatus()
 | 
			
		||||
			}
 | 
			
		||||
			return ProcessExitedError{Pid: t.dbp.Pid, Status: rs}
 | 
			
		||||
		}
 | 
			
		||||
		if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) blocked() bool {
 | 
			
		||||
	pc, _ := t.PC()
 | 
			
		||||
	fn := t.dbp.goSymTable.PCToFunc(pc)
 | 
			
		||||
	if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) saveRegisters() (Registers, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not save register contents")
 | 
			
		||||
	}
 | 
			
		||||
	return &Regs{&t.os.registers, nil}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) restoreRegisters() (err error) {
 | 
			
		||||
	t.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(t.ID, &t.os.registers) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
 | 
			
		||||
	if len(data) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, addr, data) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) readMemory(addr uintptr, size int) (data []byte, err error) {
 | 
			
		||||
	if size == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	data = make([]byte, size)
 | 
			
		||||
	t.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(t.ID, addr, data) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										715
									
								
								vendor/github.com/derekparker/delve/proc/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										715
									
								
								vendor/github.com/derekparker/delve/proc/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,715 +0,0 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/reader"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/debug/dwarf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// The kind field in runtime._type is a reflect.Kind value plus
 | 
			
		||||
// some extra flags defined here.
 | 
			
		||||
// See equivalent declaration in $GOROOT/src/reflect/type.go
 | 
			
		||||
const (
 | 
			
		||||
	kindDirectIface = 1 << 5
 | 
			
		||||
	kindGCProg      = 1 << 6 // Type.gc points to GC program
 | 
			
		||||
	kindNoPointers  = 1 << 7
 | 
			
		||||
	kindMask        = (1 << 5) - 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Value of tflag field in runtime._type.
 | 
			
		||||
// See $GOROOT/reflect/type.go for a description of these flags.
 | 
			
		||||
const (
 | 
			
		||||
	tflagUncommon  = 1 << 0
 | 
			
		||||
	tflagExtraStar = 1 << 1
 | 
			
		||||
	tflagNamed     = 1 << 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Do not call this function directly it isn't able to deal correctly with package paths
 | 
			
		||||
func (dbp *Process) findType(name string) (dwarf.Type, error) {
 | 
			
		||||
	off, found := dbp.types[name]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return nil, reader.TypeNotFoundErr
 | 
			
		||||
	}
 | 
			
		||||
	return dbp.dwarf.Type(off)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
 | 
			
		||||
	return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
 | 
			
		||||
	dbp.loadPackageMap()
 | 
			
		||||
	if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
 | 
			
		||||
		// Allow users to specify type names verbatim as quoted
 | 
			
		||||
		// string. Useful as a catch-all workaround for cases where we don't
 | 
			
		||||
		// parse/serialize types correctly or can not resolve package paths.
 | 
			
		||||
		typn, _ := strconv.Unquote(lit.Value)
 | 
			
		||||
		return dbp.findType(typn)
 | 
			
		||||
	}
 | 
			
		||||
	dbp.expandPackagesInType(expr)
 | 
			
		||||
	if snode, ok := expr.(*ast.StarExpr); ok {
 | 
			
		||||
		// Pointer types only appear in the dwarf informations when
 | 
			
		||||
		// a pointer to the type is used in the target program, here
 | 
			
		||||
		// we create a pointer type on the fly so that the user can
 | 
			
		||||
		// specify a pointer to any variable used in the target program
 | 
			
		||||
		ptyp, err := dbp.findTypeExpr(snode.X)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		return dbp.pointerTo(ptyp), nil
 | 
			
		||||
	}
 | 
			
		||||
	return dbp.findType(exprToString(expr))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func complexType(typename string) bool {
 | 
			
		||||
	for _, ch := range typename {
 | 
			
		||||
		switch ch {
 | 
			
		||||
		case '*', '[', '<', '{', '(', ' ':
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadPackageMap() error {
 | 
			
		||||
	if dbp.packageMap != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	dbp.packageMap = map[string]string{}
 | 
			
		||||
	reader := dbp.DwarfReader()
 | 
			
		||||
	for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		typename, ok := entry.Val(dwarf.AttrName).(string)
 | 
			
		||||
		if !ok || complexType(typename) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		dot := strings.LastIndex(typename, ".")
 | 
			
		||||
		if dot < 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		path := typename[:dot]
 | 
			
		||||
		slash := strings.LastIndex(path, "/")
 | 
			
		||||
		if slash < 0 || slash+1 >= len(path) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		name := path[slash+1:]
 | 
			
		||||
		dbp.packageMap[name] = path
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadTypeMap(wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
	dbp.types = make(map[string]dwarf.Offset)
 | 
			
		||||
	reader := dbp.DwarfReader()
 | 
			
		||||
	for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		name, ok := entry.Val(dwarf.AttrName).(string)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if _, exists := dbp.types[name]; !exists {
 | 
			
		||||
			dbp.types[name] = entry.Offset
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) expandPackagesInType(expr ast.Expr) {
 | 
			
		||||
	switch e := expr.(type) {
 | 
			
		||||
	case *ast.ArrayType:
 | 
			
		||||
		dbp.expandPackagesInType(e.Elt)
 | 
			
		||||
	case *ast.ChanType:
 | 
			
		||||
		dbp.expandPackagesInType(e.Value)
 | 
			
		||||
	case *ast.FuncType:
 | 
			
		||||
		for i := range e.Params.List {
 | 
			
		||||
			dbp.expandPackagesInType(e.Params.List[i].Type)
 | 
			
		||||
		}
 | 
			
		||||
		if e.Results != nil {
 | 
			
		||||
			for i := range e.Results.List {
 | 
			
		||||
				dbp.expandPackagesInType(e.Results.List[i].Type)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.MapType:
 | 
			
		||||
		dbp.expandPackagesInType(e.Key)
 | 
			
		||||
		dbp.expandPackagesInType(e.Value)
 | 
			
		||||
	case *ast.ParenExpr:
 | 
			
		||||
		dbp.expandPackagesInType(e.X)
 | 
			
		||||
	case *ast.SelectorExpr:
 | 
			
		||||
		switch x := e.X.(type) {
 | 
			
		||||
		case *ast.Ident:
 | 
			
		||||
			if path, ok := dbp.packageMap[x.Name]; ok {
 | 
			
		||||
				x.Name = path
 | 
			
		||||
			}
 | 
			
		||||
		default:
 | 
			
		||||
			dbp.expandPackagesInType(e.X)
 | 
			
		||||
		}
 | 
			
		||||
	case *ast.StarExpr:
 | 
			
		||||
		dbp.expandPackagesInType(e.X)
 | 
			
		||||
	default:
 | 
			
		||||
		// nothing to do
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nameOfRuntimeTypeEntry struct {
 | 
			
		||||
	typename string
 | 
			
		||||
	kind     int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the type name of the type described in _type.
 | 
			
		||||
// _type is a non-loaded Variable pointing to runtime._type struct in the target.
 | 
			
		||||
// The returned string is in the format that's used in DWARF data
 | 
			
		||||
func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) {
 | 
			
		||||
	if e, ok := _type.dbp.nameOfRuntimeType[_type.Addr]; ok {
 | 
			
		||||
		return e.typename, e.kind, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var tflag int64
 | 
			
		||||
 | 
			
		||||
	if tflagField := _type.toFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
 | 
			
		||||
		tflag, _ = constant.Int64Val(tflagField.Value)
 | 
			
		||||
	}
 | 
			
		||||
	if kindField := _type.toFieldNamed("kind"); kindField != nil && kindField.Value != nil {
 | 
			
		||||
		kind, _ = constant.Int64Val(kindField.Value)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Named types are defined by a 'type' expression, everything else
 | 
			
		||||
	// (for example pointers to named types) are not considered named.
 | 
			
		||||
	if tflag&tflagNamed != 0 {
 | 
			
		||||
		typename, err = nameOfNamedRuntimeType(_type, kind, tflag)
 | 
			
		||||
		return typename, kind, err
 | 
			
		||||
	} else {
 | 
			
		||||
		typename, err = nameOfUnnamedRuntimeType(_type, kind, tflag)
 | 
			
		||||
		return typename, kind, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_type.dbp.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
 | 
			
		||||
 | 
			
		||||
	return typename, kind, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The layout of a runtime._type struct is as follows:
 | 
			
		||||
//
 | 
			
		||||
// <runtime._type><kind specific struct fields><runtime.uncommontype>
 | 
			
		||||
//
 | 
			
		||||
// with the 'uncommon type struct' being optional
 | 
			
		||||
//
 | 
			
		||||
// For named types first we extract the type name from the 'str'
 | 
			
		||||
// field in the runtime._type struct.
 | 
			
		||||
// Then we prepend the package path from the runtime.uncommontype
 | 
			
		||||
// struct, when it exists.
 | 
			
		||||
//
 | 
			
		||||
// To find out the memory address of the runtime.uncommontype struct
 | 
			
		||||
// we first cast the Variable pointing to the runtime._type struct
 | 
			
		||||
// to a struct specific to the type's kind (for example, if the type
 | 
			
		||||
// being described is a slice type the variable will be specialized
 | 
			
		||||
// to a runtime.slicetype).
 | 
			
		||||
func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string, err error) {
 | 
			
		||||
	var strOff int64
 | 
			
		||||
	if strField := _type.toFieldNamed("str"); strField != nil && strField.Value != nil {
 | 
			
		||||
		strOff, _ = constant.Int64Val(strField.Value)
 | 
			
		||||
	} else {
 | 
			
		||||
		return "", errors.New("could not find str field")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The following code is adapted from reflect.(*rtype).Name.
 | 
			
		||||
	// For a description of how memory is organized for type names read
 | 
			
		||||
	// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
 | 
			
		||||
 | 
			
		||||
	typename, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(strOff))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if tflag&tflagExtraStar != 0 {
 | 
			
		||||
		typename = typename[1:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if i := strings.Index(typename, "."); i >= 0 {
 | 
			
		||||
		typename = typename[i+1:]
 | 
			
		||||
	} else {
 | 
			
		||||
		return typename, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The following code is adapted from reflect.(*rtype).PkgPath in
 | 
			
		||||
	// $GOROOT/src/reflect/type.go
 | 
			
		||||
 | 
			
		||||
	_type, err = specificRuntimeType(_type, kind)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ut := uncommon(_type, tflag); ut != nil {
 | 
			
		||||
		if pkgPathField := ut.toFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
 | 
			
		||||
			pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
 | 
			
		||||
			pkgPath, _, _, err := _type.dbp.resolveNameOff(_type.Addr, uintptr(pkgPathOff))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
			typename = pkgPath + "." + typename
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return typename, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
 | 
			
		||||
	_type, err := specificRuntimeType(_type, kind)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The types referred to here are defined in $GOROOT/src/runtime/type.go
 | 
			
		||||
	switch reflect.Kind(kind & kindMask) {
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		var len int64
 | 
			
		||||
		if lenField := _type.toFieldNamed("len"); lenField != nil && lenField.Value != nil {
 | 
			
		||||
			len, _ = constant.Int64Val(lenField.Value)
 | 
			
		||||
		}
 | 
			
		||||
		elemname, err := fieldToType(_type, "elem")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Sprintf("[%d]%s", len, elemname), nil
 | 
			
		||||
	case reflect.Chan:
 | 
			
		||||
		elemname, err := fieldToType(_type, "elem")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return "chan " + elemname, nil
 | 
			
		||||
	case reflect.Func:
 | 
			
		||||
		return nameOfFuncRuntimeType(_type, tflag, true)
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		return nameOfInterfaceRuntimeType(_type, kind, tflag)
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		keyname, err := fieldToType(_type, "key")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		elemname, err := fieldToType(_type, "elem")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return "map[" + keyname + "]" + elemname, nil
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		elemname, err := fieldToType(_type, "elem")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return "*" + elemname, nil
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		elemname, err := fieldToType(_type, "elem")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		return "[]" + elemname, nil
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		return nameOfStructRuntimeType(_type, kind, tflag)
 | 
			
		||||
	default:
 | 
			
		||||
		return nameOfNamedRuntimeType(_type, kind, tflag)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the expression describing an anonymous function type.
 | 
			
		||||
// A runtime.functype is followed by a runtime.uncommontype
 | 
			
		||||
// (optional) and then by an array of pointers to runtime._type,
 | 
			
		||||
// one for each input and output argument.
 | 
			
		||||
func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) {
 | 
			
		||||
	rtyp, err := _type.dbp.findType("runtime._type")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	prtyp := _type.dbp.pointerTo(rtyp)
 | 
			
		||||
 | 
			
		||||
	uadd := _type.RealType.Common().ByteSize
 | 
			
		||||
	if ut := uncommon(_type, tflag); ut != nil {
 | 
			
		||||
		uadd += ut.RealType.Common().ByteSize
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var inCount, outCount int64
 | 
			
		||||
	if inCountField := _type.toFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil {
 | 
			
		||||
		inCount, _ = constant.Int64Val(inCountField.Value)
 | 
			
		||||
	}
 | 
			
		||||
	if outCountField := _type.toFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil {
 | 
			
		||||
		outCount, _ = constant.Int64Val(outCountField.Value)
 | 
			
		||||
		// only the lowest 15 bits of outCount are used, rest are flags
 | 
			
		||||
		outCount = outCount & (1<<15 - 1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cursortyp := _type.newVariable("", _type.Addr+uintptr(uadd), prtyp)
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	if anonymous {
 | 
			
		||||
		buf.WriteString("func(")
 | 
			
		||||
	} else {
 | 
			
		||||
		buf.WriteString("(")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := int64(0); i < inCount; i++ {
 | 
			
		||||
		argtype := cursortyp.maybeDereference()
 | 
			
		||||
		cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
 | 
			
		||||
		argtypename, _, err := nameOfRuntimeType(argtype)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(argtypename)
 | 
			
		||||
		if i != inCount-1 {
 | 
			
		||||
			buf.WriteString(", ")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	buf.WriteString(")")
 | 
			
		||||
 | 
			
		||||
	switch outCount {
 | 
			
		||||
	case 0:
 | 
			
		||||
		// nothing to do
 | 
			
		||||
	case 1:
 | 
			
		||||
		buf.WriteString(" ")
 | 
			
		||||
		argtype := cursortyp.maybeDereference()
 | 
			
		||||
		argtypename, _, err := nameOfRuntimeType(argtype)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(argtypename)
 | 
			
		||||
	default:
 | 
			
		||||
		buf.WriteString(" (")
 | 
			
		||||
		for i := int64(0); i < outCount; i++ {
 | 
			
		||||
			argtype := cursortyp.maybeDereference()
 | 
			
		||||
			cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
 | 
			
		||||
			argtypename, _, err := nameOfRuntimeType(argtype)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
			buf.WriteString(argtypename)
 | 
			
		||||
			if i != inCount-1 {
 | 
			
		||||
				buf.WriteString(", ")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(")")
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	buf.WriteString("interface {")
 | 
			
		||||
 | 
			
		||||
	methods, _ := _type.structMember("methods")
 | 
			
		||||
	methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1})
 | 
			
		||||
	if methods.Unreadable != nil {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(methods.Children) == 0 {
 | 
			
		||||
		buf.WriteString("}")
 | 
			
		||||
		return buf.String(), nil
 | 
			
		||||
	} else {
 | 
			
		||||
		buf.WriteString(" ")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, im := range methods.Children {
 | 
			
		||||
		var methodname, methodtype string
 | 
			
		||||
		for i := range im.Children {
 | 
			
		||||
			switch im.Children[i].Name {
 | 
			
		||||
			case "name":
 | 
			
		||||
				nameoff, _ := constant.Int64Val(im.Children[i].Value)
 | 
			
		||||
				var err error
 | 
			
		||||
				methodname, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(nameoff))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case "typ":
 | 
			
		||||
				typeoff, _ := constant.Int64Val(im.Children[i].Value)
 | 
			
		||||
				typ, err := _type.dbp.resolveTypeOff(_type.Addr, uintptr(typeoff))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
				typ, err = specificRuntimeType(typ, int64(reflect.Func))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
				var tflag int64
 | 
			
		||||
				if tflagField := typ.toFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
 | 
			
		||||
					tflag, _ = constant.Int64Val(tflagField.Value)
 | 
			
		||||
				}
 | 
			
		||||
				methodtype, err = nameOfFuncRuntimeType(typ, tflag, false)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		buf.WriteString(methodname)
 | 
			
		||||
		buf.WriteString(methodtype)
 | 
			
		||||
 | 
			
		||||
		if i != len(methods.Children)-1 {
 | 
			
		||||
			buf.WriteString("; ")
 | 
			
		||||
		} else {
 | 
			
		||||
			buf.WriteString(" }")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return buf.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
	buf.WriteString("struct {")
 | 
			
		||||
 | 
			
		||||
	fields, _ := _type.structMember("fields")
 | 
			
		||||
	fields.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1})
 | 
			
		||||
	if fields.Unreadable != nil {
 | 
			
		||||
		return "", fields.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(fields.Children) == 0 {
 | 
			
		||||
		buf.WriteString("}")
 | 
			
		||||
		return buf.String(), nil
 | 
			
		||||
	} else {
 | 
			
		||||
		buf.WriteString(" ")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i, field := range fields.Children {
 | 
			
		||||
		var fieldname, fieldtypename string
 | 
			
		||||
		var typeField *Variable
 | 
			
		||||
		for i := range field.Children {
 | 
			
		||||
			switch field.Children[i].Name {
 | 
			
		||||
			case "name":
 | 
			
		||||
				nameoff, _ := constant.Int64Val(field.Children[i].Value)
 | 
			
		||||
				var err error
 | 
			
		||||
				fieldname, _, _, err = _type.dbp.loadName(uintptr(nameoff))
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
			case "typ":
 | 
			
		||||
				typeField = field.Children[i].maybeDereference()
 | 
			
		||||
				var err error
 | 
			
		||||
				fieldtypename, _, err = nameOfRuntimeType(typeField)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return "", err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// fieldname will be the empty string for anonymous fields
 | 
			
		||||
		if fieldname != "" {
 | 
			
		||||
			buf.WriteString(fieldname)
 | 
			
		||||
			buf.WriteString(" ")
 | 
			
		||||
		}
 | 
			
		||||
		buf.WriteString(fieldtypename)
 | 
			
		||||
		if i != len(fields.Children)-1 {
 | 
			
		||||
			buf.WriteString("; ")
 | 
			
		||||
		} else {
 | 
			
		||||
			buf.WriteString(" }")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buf.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fieldToType(_type *Variable, fieldName string) (string, error) {
 | 
			
		||||
	typeField, err := _type.structMember(fieldName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	typeField = typeField.maybeDereference()
 | 
			
		||||
	typename, _, err := nameOfRuntimeType(typeField)
 | 
			
		||||
	return typename, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
 | 
			
		||||
	rtyp, err := _type.dbp.findType("runtime._type")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	prtyp := _type.dbp.pointerTo(rtyp)
 | 
			
		||||
 | 
			
		||||
	uintptrtyp, err := _type.dbp.findType("uintptr")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	uint32typ := &dwarf.UintType{dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 4, Name: "uint32"}}}
 | 
			
		||||
	uint16typ := &dwarf.UintType{dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 2, Name: "uint16"}}}
 | 
			
		||||
 | 
			
		||||
	newStructType := func(name string, sz uintptr) *dwarf.StructType {
 | 
			
		||||
		return &dwarf.StructType{dwarf.CommonType{Name: name, ByteSize: int64(sz)}, name, "struct", nil, false}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	appendField := func(typ *dwarf.StructType, name string, fieldtype dwarf.Type, off uintptr) {
 | 
			
		||||
		typ.Field = append(typ.Field, &dwarf.StructField{Name: name, ByteOffset: int64(off), Type: fieldtype})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
 | 
			
		||||
		r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size()))
 | 
			
		||||
		appendField(r, "array", _type.dbp.pointerTo(elemtype), 0)
 | 
			
		||||
		appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
 | 
			
		||||
		appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
 | 
			
		||||
		return &dwarf.SliceType{StructType: *r, ElemType: elemtype}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var typ *dwarf.StructType
 | 
			
		||||
 | 
			
		||||
	type rtype struct {
 | 
			
		||||
		size       uintptr
 | 
			
		||||
		ptrdata    uintptr
 | 
			
		||||
		hash       uint32 // hash of type; avoids computation in hash tables
 | 
			
		||||
		tflag      uint8  // extra type information flags
 | 
			
		||||
		align      uint8  // alignment of variable with this type
 | 
			
		||||
		fieldAlign uint8  // alignment of struct field with this type
 | 
			
		||||
		kind       uint8  // enumeration for C
 | 
			
		||||
		alg        *byte  // algorithm table
 | 
			
		||||
		gcdata     *byte  // garbage collection data
 | 
			
		||||
		str        int32  // string form
 | 
			
		||||
		ptrToThis  int32  // type for pointer to this type, may be zero
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch reflect.Kind(kind & kindMask) {
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		// runtime.arraytype
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype
 | 
			
		||||
			elem  *rtype // array element type
 | 
			
		||||
			slice *rtype // slice type
 | 
			
		||||
			len   uintptr
 | 
			
		||||
		}
 | 
			
		||||
		typ = newStructType("runtime.arraytype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
 | 
			
		||||
		appendField(typ, "len", uintptrtyp, unsafe.Offsetof(a.len))
 | 
			
		||||
	case reflect.Chan:
 | 
			
		||||
		// runtime.chantype
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype
 | 
			
		||||
			elem *rtype  // channel element type
 | 
			
		||||
			dir  uintptr // channel direction (ChanDir)
 | 
			
		||||
		}
 | 
			
		||||
		typ = newStructType("runtime.chantype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
 | 
			
		||||
	case reflect.Func:
 | 
			
		||||
		// runtime.functype
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype    `reflect:"func"`
 | 
			
		||||
			inCount  uint16
 | 
			
		||||
			outCount uint16 // top bit is set if last input parameter is ...
 | 
			
		||||
		}
 | 
			
		||||
		typ = newStructType("runtime.functype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "inCount", uint16typ, unsafe.Offsetof(a.inCount))
 | 
			
		||||
		appendField(typ, "outCount", uint16typ, unsafe.Offsetof(a.outCount))
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		// runtime.imethod
 | 
			
		||||
		type imethod struct {
 | 
			
		||||
			name uint32 // name of method
 | 
			
		||||
			typ  uint32 // .(*FuncType) underneath
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var im imethod
 | 
			
		||||
 | 
			
		||||
		// runtime.interfacetype
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype   `reflect:"interface"`
 | 
			
		||||
			pkgPath *byte     // import path
 | 
			
		||||
			methods []imethod // sorted by hash
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		imethodtype := newStructType("runtime.imethod", unsafe.Sizeof(im))
 | 
			
		||||
		appendField(imethodtype, "name", uint32typ, unsafe.Offsetof(im.name))
 | 
			
		||||
		appendField(imethodtype, "typ", uint32typ, unsafe.Offsetof(im.typ))
 | 
			
		||||
		typ = newStructType("runtime.interfacetype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "methods", newSliceType(imethodtype), unsafe.Offsetof(a.methods))
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		// runtime.maptype
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype         `reflect:"map"`
 | 
			
		||||
			key           *rtype // map key type
 | 
			
		||||
			elem          *rtype // map element (value) type
 | 
			
		||||
			bucket        *rtype // internal bucket structure
 | 
			
		||||
			hmap          *rtype // internal map header
 | 
			
		||||
			keysize       uint8  // size of key slot
 | 
			
		||||
			indirectkey   uint8  // store ptr to key instead of key itself
 | 
			
		||||
			valuesize     uint8  // size of value slot
 | 
			
		||||
			indirectvalue uint8  // store ptr to value instead of value itself
 | 
			
		||||
			bucketsize    uint16 // size of bucket
 | 
			
		||||
			reflexivekey  bool   // true if k==k for all keys
 | 
			
		||||
			needkeyupdate bool   // true if we need to update key on an overwrite
 | 
			
		||||
		}
 | 
			
		||||
		typ = newStructType("runtime.maptype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "key", prtyp, unsafe.Offsetof(a.key))
 | 
			
		||||
		appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		// runtime.ptrtype
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype `reflect:"ptr"`
 | 
			
		||||
			elem  *rtype // pointer element (pointed at) type
 | 
			
		||||
		}
 | 
			
		||||
		typ = newStructType("runtime.ptrtype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
 | 
			
		||||
	case reflect.Slice:
 | 
			
		||||
		// runtime.slicetype
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype `reflect:"slice"`
 | 
			
		||||
			elem  *rtype // slice element type
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		typ = newStructType("runtime.slicetype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		// runtime.structtype
 | 
			
		||||
		type structField struct {
 | 
			
		||||
			name   *byte   // name is empty for embedded fields
 | 
			
		||||
			typ    *rtype  // type of field
 | 
			
		||||
			offset uintptr // byte offset of field within struct
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var sf structField
 | 
			
		||||
 | 
			
		||||
		var a struct {
 | 
			
		||||
			rtype   `reflect:"struct"`
 | 
			
		||||
			pkgPath *byte
 | 
			
		||||
			fields  []structField // sorted by offset
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fieldtype := newStructType("runtime.structtype", unsafe.Sizeof(sf))
 | 
			
		||||
		appendField(fieldtype, "name", uintptrtyp, unsafe.Offsetof(sf.name))
 | 
			
		||||
		appendField(fieldtype, "typ", prtyp, unsafe.Offsetof(sf.typ))
 | 
			
		||||
		typ = newStructType("runtime.structtype", unsafe.Sizeof(a))
 | 
			
		||||
		appendField(typ, "fields", newSliceType(fieldtype), unsafe.Offsetof(a.fields))
 | 
			
		||||
	default:
 | 
			
		||||
		return _type, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return _type.newVariable(_type.Name, _type.Addr, typ), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// See reflect.(*rtype).uncommon in $GOROOT/src/reflect/type.go
 | 
			
		||||
func uncommon(_type *Variable, tflag int64) *Variable {
 | 
			
		||||
	if tflag&tflagUncommon == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typ, err := _type.dbp.findType("runtime.uncommontype")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return _type.newVariable(_type.Name, _type.Addr+uintptr(_type.RealType.Size()), typ)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1572
									
								
								vendor/github.com/derekparker/delve/proc/variables.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1572
									
								
								vendor/github.com/derekparker/delve/proc/variables.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										833
									
								
								vendor/github.com/derekparker/delve/service/debugger/debugger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										833
									
								
								vendor/github.com/derekparker/delve/service/debugger/debugger.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,833 +0,0 @@
 | 
			
		||||
package debugger
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/gosym"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"log"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/proc"
 | 
			
		||||
	"github.com/derekparker/delve/service/api"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Debugger service.
 | 
			
		||||
//
 | 
			
		||||
// Debugger provides a higher level of
 | 
			
		||||
// abstraction over proc.Process.
 | 
			
		||||
// It handles converting from internal types to
 | 
			
		||||
// the types expected by clients. It also handles
 | 
			
		||||
// functionality needed by clients, but not needed in
 | 
			
		||||
// lower lever packages such as proc.
 | 
			
		||||
type Debugger struct {
 | 
			
		||||
	config       *Config
 | 
			
		||||
	processMutex sync.Mutex
 | 
			
		||||
	process      *proc.Process
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Config provides the configuration to start a Debugger.
 | 
			
		||||
//
 | 
			
		||||
// Only one of ProcessArgs or AttachPid should be specified. If ProcessArgs is
 | 
			
		||||
// provided, a new process will be launched. Otherwise, the debugger will try
 | 
			
		||||
// to attach to an existing process with AttachPid.
 | 
			
		||||
type Config struct {
 | 
			
		||||
	// ProcessArgs are the arguments to launch a new process.
 | 
			
		||||
	ProcessArgs []string
 | 
			
		||||
	// WorkingDir is working directory of the new process. This field is used
 | 
			
		||||
	// only when launching a new process.
 | 
			
		||||
	WorkingDir string
 | 
			
		||||
 | 
			
		||||
	// AttachPid is the PID of an existing process to which the debugger should
 | 
			
		||||
	// attach.
 | 
			
		||||
	AttachPid int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New creates a new Debugger.
 | 
			
		||||
func New(config *Config) (*Debugger, error) {
 | 
			
		||||
	d := &Debugger{
 | 
			
		||||
		config: config,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create the process by either attaching or launching.
 | 
			
		||||
	if d.config.AttachPid > 0 {
 | 
			
		||||
		log.Printf("attaching to pid %d", d.config.AttachPid)
 | 
			
		||||
		p, err := proc.Attach(d.config.AttachPid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, attachErrorMessage(d.config.AttachPid, err)
 | 
			
		||||
		}
 | 
			
		||||
		d.process = p
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Printf("launching process with args: %v", d.config.ProcessArgs)
 | 
			
		||||
		p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err != proc.NotExecutableErr && err != proc.UnsupportedArchErr {
 | 
			
		||||
				err = fmt.Errorf("could not launch process: %s", err)
 | 
			
		||||
			}
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		d.process = p
 | 
			
		||||
	}
 | 
			
		||||
	return d, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessPid returns the PID of the process
 | 
			
		||||
// the debugger is debugging.
 | 
			
		||||
func (d *Debugger) ProcessPid() int {
 | 
			
		||||
	return d.process.Pid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LastModified returns the time that the process' executable was last
 | 
			
		||||
// modified.
 | 
			
		||||
func (d *Debugger) LastModified() time.Time {
 | 
			
		||||
	return d.process.LastModified
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Detach detaches from the target process.
 | 
			
		||||
// If `kill` is true we will kill the process after
 | 
			
		||||
// detaching.
 | 
			
		||||
func (d *Debugger) Detach(kill bool) error {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	return d.detach(kill)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) detach(kill bool) error {
 | 
			
		||||
	if d.config.AttachPid != 0 {
 | 
			
		||||
		return d.process.Detach(kill)
 | 
			
		||||
	}
 | 
			
		||||
	return d.process.Kill()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Restart will restart the target process, first killing
 | 
			
		||||
// and then exec'ing it again.
 | 
			
		||||
func (d *Debugger) Restart() ([]api.DiscardedBreakpoint, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	if !d.process.Exited() {
 | 
			
		||||
		if d.process.Running() {
 | 
			
		||||
			d.process.Halt()
 | 
			
		||||
		}
 | 
			
		||||
		// Ensure the process is in a PTRACE_STOP.
 | 
			
		||||
		if err := stopProcess(d.ProcessPid()); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if err := d.detach(true); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not launch process: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	discarded := []api.DiscardedBreakpoint{}
 | 
			
		||||
	for _, oldBp := range d.breakpoints() {
 | 
			
		||||
		if oldBp.ID < 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if len(oldBp.File) > 0 {
 | 
			
		||||
			oldBp.Addr, err = p.FindFileLocation(oldBp.File, oldBp.Line)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				discarded = append(discarded, api.DiscardedBreakpoint{oldBp, err.Error()})
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		newBp, err := p.SetBreakpoint(oldBp.Addr, proc.UserBreakpoint, nil)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if err := copyBreakpointInfo(newBp, oldBp); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	d.process = p
 | 
			
		||||
	return discarded, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// State returns the current state of the debugger.
 | 
			
		||||
func (d *Debugger) State() (*api.DebuggerState, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
	return d.state()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) state() (*api.DebuggerState, error) {
 | 
			
		||||
	if d.process.Exited() {
 | 
			
		||||
		return nil, proc.ProcessExitedError{Pid: d.ProcessPid()}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		state     *api.DebuggerState
 | 
			
		||||
		goroutine *api.Goroutine
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if d.process.SelectedGoroutine != nil {
 | 
			
		||||
		goroutine = api.ConvertGoroutine(d.process.SelectedGoroutine)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state = &api.DebuggerState{
 | 
			
		||||
		SelectedGoroutine: goroutine,
 | 
			
		||||
		Exited:            d.process.Exited(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range d.process.Threads {
 | 
			
		||||
		th := api.ConvertThread(d.process.Threads[i])
 | 
			
		||||
		state.Threads = append(state.Threads, th)
 | 
			
		||||
		if i == d.process.CurrentThread.ID {
 | 
			
		||||
			state.CurrentThread = th
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, bp := range d.process.Breakpoints {
 | 
			
		||||
		if bp.Internal() {
 | 
			
		||||
			state.NextInProgress = true
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return state, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateBreakpoint creates a breakpoint.
 | 
			
		||||
func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		createdBp *api.Breakpoint
 | 
			
		||||
		addr      uint64
 | 
			
		||||
		err       error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if requestedBp.Name != "" {
 | 
			
		||||
		if err = api.ValidBreakpointName(requestedBp.Name); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if d.findBreakpointByName(requestedBp.Name) != nil {
 | 
			
		||||
			return nil, errors.New("breakpoint name already exists")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case len(requestedBp.File) > 0:
 | 
			
		||||
		fileName := requestedBp.File
 | 
			
		||||
		if runtime.GOOS == "windows" {
 | 
			
		||||
			// Accept fileName which is case-insensitive and slash-insensitive match
 | 
			
		||||
			fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
 | 
			
		||||
			for symFile := range d.process.Sources() {
 | 
			
		||||
				if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
 | 
			
		||||
					fileName = symFile
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		addr, err = d.process.FindFileLocation(fileName, requestedBp.Line)
 | 
			
		||||
	case len(requestedBp.FunctionName) > 0:
 | 
			
		||||
		if requestedBp.Line >= 0 {
 | 
			
		||||
			addr, err = d.process.FindFunctionLocation(requestedBp.FunctionName, false, requestedBp.Line)
 | 
			
		||||
		} else {
 | 
			
		||||
			addr, err = d.process.FindFunctionLocation(requestedBp.FunctionName, true, 0)
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		addr = requestedBp.Addr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bp, err := d.process.SetBreakpoint(addr, proc.UserBreakpoint, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := copyBreakpointInfo(bp, requestedBp); err != nil {
 | 
			
		||||
		if _, err1 := d.process.ClearBreakpoint(bp.Addr); err1 != nil {
 | 
			
		||||
			err = fmt.Errorf("error while creating breakpoint: %v, additionally the breakpoint could not be properly rolled back: %v", err, err1)
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	createdBp = api.ConvertBreakpoint(bp)
 | 
			
		||||
	log.Printf("created breakpoint: %#v", createdBp)
 | 
			
		||||
	return createdBp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) AmendBreakpoint(amend *api.Breakpoint) error {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	original := d.findBreakpoint(amend.ID)
 | 
			
		||||
	if original == nil {
 | 
			
		||||
		return fmt.Errorf("no breakpoint with ID %d", amend.ID)
 | 
			
		||||
	}
 | 
			
		||||
	if err := api.ValidBreakpointName(amend.Name); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return copyBreakpointInfo(original, amend)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) CancelNext() error {
 | 
			
		||||
	return d.process.ClearInternalBreakpoints()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyBreakpointInfo(bp *proc.Breakpoint, requested *api.Breakpoint) (err error) {
 | 
			
		||||
	bp.Name = requested.Name
 | 
			
		||||
	bp.Tracepoint = requested.Tracepoint
 | 
			
		||||
	bp.Goroutine = requested.Goroutine
 | 
			
		||||
	bp.Stacktrace = requested.Stacktrace
 | 
			
		||||
	bp.Variables = requested.Variables
 | 
			
		||||
	bp.LoadArgs = api.LoadConfigToProc(requested.LoadArgs)
 | 
			
		||||
	bp.LoadLocals = api.LoadConfigToProc(requested.LoadLocals)
 | 
			
		||||
	bp.Cond = nil
 | 
			
		||||
	if requested.Cond != "" {
 | 
			
		||||
		bp.Cond, err = parser.ParseExpr(requested.Cond)
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearBreakpoint clears a breakpoint.
 | 
			
		||||
func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	var clearedBp *api.Breakpoint
 | 
			
		||||
	bp, err := d.process.ClearBreakpoint(requestedBp.Addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
 | 
			
		||||
	}
 | 
			
		||||
	clearedBp = api.ConvertBreakpoint(bp)
 | 
			
		||||
	log.Printf("cleared breakpoint: %#v", clearedBp)
 | 
			
		||||
	return clearedBp, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Breakpoints returns the list of current breakpoints.
 | 
			
		||||
func (d *Debugger) Breakpoints() []*api.Breakpoint {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
	return d.breakpoints()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) breakpoints() []*api.Breakpoint {
 | 
			
		||||
	bps := []*api.Breakpoint{}
 | 
			
		||||
	for _, bp := range d.process.Breakpoints {
 | 
			
		||||
		if bp.Internal() {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		bps = append(bps, api.ConvertBreakpoint(bp))
 | 
			
		||||
	}
 | 
			
		||||
	return bps
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindBreakpoint returns the breakpoint specified by 'id'.
 | 
			
		||||
func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	bp := d.findBreakpoint(id)
 | 
			
		||||
	if bp == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return api.ConvertBreakpoint(bp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) findBreakpoint(id int) *proc.Breakpoint {
 | 
			
		||||
	for _, bp := range d.process.Breakpoints {
 | 
			
		||||
		if bp.ID == id {
 | 
			
		||||
			return bp
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindBreakpointByName returns the breakpoint specified by 'name'
 | 
			
		||||
func (d *Debugger) FindBreakpointByName(name string) *api.Breakpoint {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
	return d.findBreakpointByName(name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) findBreakpointByName(name string) *api.Breakpoint {
 | 
			
		||||
	for _, bp := range d.breakpoints() {
 | 
			
		||||
		if bp.Name == name {
 | 
			
		||||
			return bp
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Threads returns the threads of the target process.
 | 
			
		||||
func (d *Debugger) Threads() ([]*api.Thread, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	if d.process.Exited() {
 | 
			
		||||
		return nil, &proc.ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
	threads := []*api.Thread{}
 | 
			
		||||
	for _, th := range d.process.Threads {
 | 
			
		||||
		threads = append(threads, api.ConvertThread(th))
 | 
			
		||||
	}
 | 
			
		||||
	return threads, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindThread returns the thread for the given 'id'.
 | 
			
		||||
func (d *Debugger) FindThread(id int) (*api.Thread, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	if d.process.Exited() {
 | 
			
		||||
		return nil, &proc.ProcessExitedError{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, th := range d.process.Threads {
 | 
			
		||||
		if th.ID == id {
 | 
			
		||||
			return api.ConvertThread(th), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Command handles commands which control the debugger lifecycle
 | 
			
		||||
func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
 | 
			
		||||
	if command.Name == api.Halt {
 | 
			
		||||
		// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
 | 
			
		||||
		// access the process directly.
 | 
			
		||||
		log.Print("halting")
 | 
			
		||||
		err = d.process.RequestManualStop()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	switch command.Name {
 | 
			
		||||
	case api.Continue:
 | 
			
		||||
		log.Print("continuing")
 | 
			
		||||
		err = d.process.Continue()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if exitedErr, exited := err.(proc.ProcessExitedError); exited {
 | 
			
		||||
				state := &api.DebuggerState{}
 | 
			
		||||
				state.Exited = true
 | 
			
		||||
				state.ExitStatus = exitedErr.Status
 | 
			
		||||
				state.Err = errors.New(exitedErr.Error())
 | 
			
		||||
				return state, nil
 | 
			
		||||
			}
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		state, stateErr := d.state()
 | 
			
		||||
		if stateErr != nil {
 | 
			
		||||
			return state, stateErr
 | 
			
		||||
		}
 | 
			
		||||
		err = d.collectBreakpointInformation(state)
 | 
			
		||||
		return state, err
 | 
			
		||||
 | 
			
		||||
	case api.Next:
 | 
			
		||||
		log.Print("nexting")
 | 
			
		||||
		err = d.process.Next()
 | 
			
		||||
	case api.Step:
 | 
			
		||||
		log.Print("stepping")
 | 
			
		||||
		err = d.process.Step()
 | 
			
		||||
	case api.StepInstruction:
 | 
			
		||||
		log.Print("single stepping")
 | 
			
		||||
		err = d.process.StepInstruction()
 | 
			
		||||
	case api.StepOut:
 | 
			
		||||
		log.Print("step out")
 | 
			
		||||
		err = d.process.StepOut()
 | 
			
		||||
	case api.SwitchThread:
 | 
			
		||||
		log.Printf("switching to thread %d", command.ThreadID)
 | 
			
		||||
		err = d.process.SwitchThread(command.ThreadID)
 | 
			
		||||
	case api.SwitchGoroutine:
 | 
			
		||||
		log.Printf("switching to goroutine %d", command.GoroutineID)
 | 
			
		||||
		err = d.process.SwitchGoroutine(command.GoroutineID)
 | 
			
		||||
	case api.Halt:
 | 
			
		||||
		// RequestManualStop already called
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return d.state()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error {
 | 
			
		||||
	if state == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range state.Threads {
 | 
			
		||||
		if state.Threads[i].Breakpoint == nil {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		bp := state.Threads[i].Breakpoint
 | 
			
		||||
		bpi := &api.BreakpointInfo{}
 | 
			
		||||
		state.Threads[i].BreakpointInfo = bpi
 | 
			
		||||
 | 
			
		||||
		if bp.Goroutine {
 | 
			
		||||
			g, err := d.process.CurrentThread.GetG()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			bpi.Goroutine = api.ConvertGoroutine(g)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if bp.Stacktrace > 0 {
 | 
			
		||||
			rawlocs, err := d.process.CurrentThread.Stacktrace(bp.Stacktrace)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			bpi.Stacktrace, err = d.convertStacktrace(rawlocs, nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		s, err := d.process.Threads[state.Threads[i].ID].Scope()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(bp.Variables) > 0 {
 | 
			
		||||
			bpi.Variables = make([]api.Variable, len(bp.Variables))
 | 
			
		||||
		}
 | 
			
		||||
		for i := range bp.Variables {
 | 
			
		||||
			v, err := s.EvalVariable(bp.Variables[i], proc.LoadConfig{true, 1, 64, 64, -1})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			bpi.Variables[i] = *api.ConvertVar(v)
 | 
			
		||||
		}
 | 
			
		||||
		if bp.LoadArgs != nil {
 | 
			
		||||
			if vars, err := s.FunctionArguments(*api.LoadConfigToProc(bp.LoadArgs)); err == nil {
 | 
			
		||||
				bpi.Arguments = convertVars(vars)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if bp.LoadLocals != nil {
 | 
			
		||||
			if locals, err := s.LocalVariables(*api.LoadConfigToProc(bp.LoadLocals)); err == nil {
 | 
			
		||||
				bpi.Locals = convertVars(locals)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Sources returns a list of the source files for target binary.
 | 
			
		||||
func (d *Debugger) Sources(filter string) ([]string, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	regex, err := regexp.Compile(filter)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	files := []string{}
 | 
			
		||||
	for f := range d.process.Sources() {
 | 
			
		||||
		if regex.Match([]byte(f)) {
 | 
			
		||||
			files = append(files, f)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return files, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Functions returns a list of functions in the target process.
 | 
			
		||||
func (d *Debugger) Functions(filter string) ([]string, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	return regexFilterFuncs(filter, d.process.Funcs())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) Types(filter string) ([]string, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	regex, err := regexp.Compile(filter)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	types, err := d.process.Types()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := make([]string, 0, len(types))
 | 
			
		||||
	for _, typ := range types {
 | 
			
		||||
		if regex.Match([]byte(typ)) {
 | 
			
		||||
			r = append(r, typ)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
 | 
			
		||||
	regex, err := regexp.Compile(filter)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	funcs := []string{}
 | 
			
		||||
	for _, f := range allFuncs {
 | 
			
		||||
		if f.Sym != nil && regex.Match([]byte(f.Name)) {
 | 
			
		||||
			funcs = append(funcs, f.Name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return funcs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageVariables returns a list of package variables for the thread,
 | 
			
		||||
// optionally regexp filtered using regexp described in 'filter'.
 | 
			
		||||
func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadConfig) ([]api.Variable, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	regex, err := regexp.Compile(filter)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vars := []api.Variable{}
 | 
			
		||||
	thread, found := d.process.Threads[threadID]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return nil, fmt.Errorf("couldn't find thread %d", threadID)
 | 
			
		||||
	}
 | 
			
		||||
	scope, err := thread.Scope()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pv, err := scope.PackageVariables(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, v := range pv {
 | 
			
		||||
		if regex.Match([]byte(v.Name)) {
 | 
			
		||||
			vars = append(vars, *api.ConvertVar(v))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return vars, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers returns string representation of the CPU registers.
 | 
			
		||||
func (d *Debugger) Registers(threadID int, floatingPoint bool) (api.Registers, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	thread, found := d.process.Threads[threadID]
 | 
			
		||||
	if !found {
 | 
			
		||||
		return nil, fmt.Errorf("couldn't find thread %d", threadID)
 | 
			
		||||
	}
 | 
			
		||||
	regs, err := thread.Registers(floatingPoint)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return api.ConvertRegisters(regs.Slice()), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func convertVars(pv []*proc.Variable) []api.Variable {
 | 
			
		||||
	vars := make([]api.Variable, 0, len(pv))
 | 
			
		||||
	for _, v := range pv {
 | 
			
		||||
		vars = append(vars, *api.ConvertVar(v))
 | 
			
		||||
	}
 | 
			
		||||
	return vars
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LocalVariables returns a list of the local variables.
 | 
			
		||||
func (d *Debugger) LocalVariables(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pv, err := s.LocalVariables(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return convertVars(pv), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FunctionArguments returns the arguments to the current function.
 | 
			
		||||
func (d *Debugger) FunctionArguments(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pv, err := s.FunctionArguments(cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return convertVars(pv), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
 | 
			
		||||
// in the scope provided.
 | 
			
		||||
func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string, cfg proc.LoadConfig) (*api.Variable, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	v, err := s.EvalVariable(symbol, cfg)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return api.ConvertVar(v), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetVariableInScope will set the value of the variable represented by
 | 
			
		||||
// 'symbol' to the value given, in the given scope.
 | 
			
		||||
func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) error {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return s.SetVariable(symbol, value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Goroutines will return a list of goroutines in the target process.
 | 
			
		||||
func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	goroutines := []*api.Goroutine{}
 | 
			
		||||
	gs, err := d.process.GoroutinesInfo()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, g := range gs {
 | 
			
		||||
		goroutines = append(goroutines, api.ConvertGoroutine(g))
 | 
			
		||||
	}
 | 
			
		||||
	return goroutines, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stacktrace returns a list of Stackframes for the given goroutine. The
 | 
			
		||||
// length of the returned list will be min(stack_len, depth).
 | 
			
		||||
// If 'full' is true, then local vars, function args, etc will be returned as well.
 | 
			
		||||
func (d *Debugger) Stacktrace(goroutineID, depth int, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	var rawlocs []proc.Stackframe
 | 
			
		||||
 | 
			
		||||
	g, err := d.process.FindGoroutine(goroutineID)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		rawlocs, err = d.process.CurrentThread.Stacktrace(depth)
 | 
			
		||||
	} else {
 | 
			
		||||
		rawlocs, err = g.Stacktrace(depth)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return d.convertStacktrace(rawlocs, cfg)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
 | 
			
		||||
	locations := make([]api.Stackframe, 0, len(rawlocs))
 | 
			
		||||
	for i := range rawlocs {
 | 
			
		||||
		frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
 | 
			
		||||
		if cfg != nil {
 | 
			
		||||
			var err error
 | 
			
		||||
			scope := rawlocs[i].Scope(d.process.CurrentThread)
 | 
			
		||||
			locals, err := scope.LocalVariables(*cfg)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			arguments, err := scope.FunctionArguments(*cfg)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			frame.Locals = convertVars(locals)
 | 
			
		||||
			frame.Arguments = convertVars(arguments)
 | 
			
		||||
		}
 | 
			
		||||
		locations = append(locations, frame)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return locations, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindLocation will find the location specified by 'locStr'.
 | 
			
		||||
func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Location, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	loc, err := parseLocationSpec(locStr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s, _ := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
 | 
			
		||||
 | 
			
		||||
	locs, err := loc.Find(d, s, locStr)
 | 
			
		||||
	for i := range locs {
 | 
			
		||||
		file, line, fn := d.process.PCToLine(locs[i].PC)
 | 
			
		||||
		locs[i].File = file
 | 
			
		||||
		locs[i].Line = line
 | 
			
		||||
		locs[i].Function = api.ConvertFunction(fn)
 | 
			
		||||
	}
 | 
			
		||||
	return locs, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Disassembles code between startPC and endPC
 | 
			
		||||
// if endPC == 0 it will find the function containing startPC and disassemble the whole function
 | 
			
		||||
func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
 | 
			
		||||
	d.processMutex.Lock()
 | 
			
		||||
	defer d.processMutex.Unlock()
 | 
			
		||||
 | 
			
		||||
	if endPC == 0 {
 | 
			
		||||
		_, _, fn := d.process.PCToLine(startPC)
 | 
			
		||||
		if fn == nil {
 | 
			
		||||
			return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
 | 
			
		||||
		}
 | 
			
		||||
		startPC = fn.Entry
 | 
			
		||||
		endPC = fn.End
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	currentGoroutine := true
 | 
			
		||||
	thread := d.process.CurrentThread
 | 
			
		||||
 | 
			
		||||
	if s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame); err == nil {
 | 
			
		||||
		thread = s.Thread
 | 
			
		||||
		if scope.GoroutineID != -1 {
 | 
			
		||||
			g, _ := s.Thread.GetG()
 | 
			
		||||
			if g == nil || g.ID != scope.GoroutineID {
 | 
			
		||||
				currentGoroutine = false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	insts, err := thread.Disassemble(startPC, endPC, currentGoroutine)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	disass := make(api.AsmInstructions, len(insts))
 | 
			
		||||
 | 
			
		||||
	for i := range insts {
 | 
			
		||||
		disass[i] = api.ConvertAsmInstruction(insts[i], insts[i].Text(proc.AssemblyFlavour(flavour)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return disass, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										278
									
								
								vendor/github.com/go-delve/delve/pkg/config/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								vendor/github.com/go-delve/delve/pkg/config/config.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/user"
 | 
			
		||||
	"path"
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/yaml.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	configDir       string = "dlv"
 | 
			
		||||
	configDirHidden string = ".dlv"
 | 
			
		||||
	configFile      string = "config.yml"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SubstitutePathRule describes a rule for substitution of path to source code file.
 | 
			
		||||
type SubstitutePathRule struct {
 | 
			
		||||
	// Directory path will be substituted if it matches `From`.
 | 
			
		||||
	From string
 | 
			
		||||
	// Path to which substitution is performed.
 | 
			
		||||
	To string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SubstitutePathRules is a slice of source code path substitution rules.
 | 
			
		||||
type SubstitutePathRules []SubstitutePathRule
 | 
			
		||||
 | 
			
		||||
// Config defines all configuration options available to be set through the config file.
 | 
			
		||||
type Config struct {
 | 
			
		||||
	// Commands aliases.
 | 
			
		||||
	Aliases map[string][]string `yaml:"aliases"`
 | 
			
		||||
	// Source code path substitution rules.
 | 
			
		||||
	SubstitutePath SubstitutePathRules `yaml:"substitute-path"`
 | 
			
		||||
 | 
			
		||||
	// MaxStringLen is the maximum string length that the commands print,
 | 
			
		||||
	// locals, args and vars should read (in verbose mode).
 | 
			
		||||
	MaxStringLen *int `yaml:"max-string-len,omitempty"`
 | 
			
		||||
	// MaxArrayValues is the maximum number of array items that the commands
 | 
			
		||||
	// print, locals, args and vars should read (in verbose mode).
 | 
			
		||||
	MaxArrayValues *int `yaml:"max-array-values,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// If ShowLocationExpr is true whatis will print the DWARF location
 | 
			
		||||
	// expression for its argument.
 | 
			
		||||
	ShowLocationExpr bool `yaml:"show-location-expr"`
 | 
			
		||||
 | 
			
		||||
	// Source list line-number color (3/4 bit color codes as defined
 | 
			
		||||
	// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
 | 
			
		||||
	SourceListLineColor int `yaml:"source-list-line-color"`
 | 
			
		||||
 | 
			
		||||
	// DebugFileDirectories is the list of directories Delve will use
 | 
			
		||||
	// in order to resolve external debug info files.
 | 
			
		||||
	DebugInfoDirectories []string `yaml:"debug-info-directories"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadConfig attempts to populate a Config object from the config.yml file.
 | 
			
		||||
func LoadConfig() *Config {
 | 
			
		||||
	err := createConfigPath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Could not create config directory: %v.", err)
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
	fullConfigFile, err := GetConfigFilePath(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to get config file path: %v.", err)
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hasOldConfig, err := hasOldConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Unable to determine if old config exists: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasOldConfig {
 | 
			
		||||
		userHomeDir := getUserHomeDir()
 | 
			
		||||
		oldLocation := path.Join(userHomeDir, configDirHidden)
 | 
			
		||||
		if err := moveOldConfig(); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Unable to move old config: %v\n", err)
 | 
			
		||||
			return &Config{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := os.RemoveAll(oldLocation); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Unable to remove old config location: %v\n", err)
 | 
			
		||||
			return &Config{}
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Successfully moved config from: %s to: %s\n", oldLocation, fullConfigFile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.Open(fullConfigFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		f, err = createDefaultConfig(fullConfigFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Error creating default config file: %v", err)
 | 
			
		||||
			return &Config{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
		err := f.Close()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Closing config file failed: %v.", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	data, err := ioutil.ReadAll(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to read config data: %v.", err)
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var c Config
 | 
			
		||||
	err = yaml.Unmarshal(data, &c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to decode config file: %v.", err)
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.DebugInfoDirectories) == 0 {
 | 
			
		||||
		c.DebugInfoDirectories = []string{"/usr/lib/debug/.build-id"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveConfig will marshal and save the config struct
 | 
			
		||||
// to disk.
 | 
			
		||||
func SaveConfig(conf *Config) error {
 | 
			
		||||
	fullConfigFile, err := GetConfigFilePath(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	out, err := yaml.Marshal(*conf)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.Create(fullConfigFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer f.Close()
 | 
			
		||||
 | 
			
		||||
	_, err = f.Write(out)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// moveOldConfig attempts to move config to new location
 | 
			
		||||
// $HOME/.dlv to $XDG_CONFIG_HOME/dlv
 | 
			
		||||
func moveOldConfig() error {
 | 
			
		||||
	if os.Getenv("XDG_CONFIG_HOME") == "" && runtime.GOOS != "linux" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userHomeDir := getUserHomeDir()
 | 
			
		||||
 | 
			
		||||
	p := path.Join(userHomeDir, configDirHidden, configFile)
 | 
			
		||||
	_, err := os.Stat(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to read config file located at: %s", p)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newFile, err := GetConfigFilePath(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to read config file located at: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.Rename(p, newFile); err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to move %s to %s", p, newFile)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createDefaultConfig(path string) (*os.File, error) {
 | 
			
		||||
	f, err := os.Create(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("unable to create config file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = writeDefaultConfig(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("unable to write default configuration: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	f.Seek(0, io.SeekStart)
 | 
			
		||||
	return f, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func writeDefaultConfig(f *os.File) error {
 | 
			
		||||
	_, err := f.WriteString(
 | 
			
		||||
		`# Configuration file for the delve debugger.
 | 
			
		||||
 | 
			
		||||
# This is the default configuration file. Available options are provided, but disabled.
 | 
			
		||||
# Delete the leading hash mark to enable an item.
 | 
			
		||||
 | 
			
		||||
# Uncomment the following line and set your preferred ANSI foreground color
 | 
			
		||||
# for source line numbers in the (list) command (if unset, default is 34,
 | 
			
		||||
# dark blue) See https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit
 | 
			
		||||
# source-list-line-color: 34
 | 
			
		||||
 | 
			
		||||
# Provided aliases will be added to the default aliases for a given command.
 | 
			
		||||
aliases:
 | 
			
		||||
  # command: ["alias1", "alias2"]
 | 
			
		||||
 | 
			
		||||
# Define sources path substitution rules. Can be used to rewrite a source path stored
 | 
			
		||||
# in program's debug information, if the sources were moved to a different place
 | 
			
		||||
# between compilation and debugging.
 | 
			
		||||
# Note that substitution rules will not be used for paths passed to "break" and "trace"
 | 
			
		||||
# commands.
 | 
			
		||||
substitute-path:
 | 
			
		||||
  # - {from: path, to: path}
 | 
			
		||||
  
 | 
			
		||||
# Maximum number of elements loaded from an array.
 | 
			
		||||
# max-array-values: 64
 | 
			
		||||
 | 
			
		||||
# Maximum loaded string length.
 | 
			
		||||
# max-string-len: 64
 | 
			
		||||
 | 
			
		||||
# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
 | 
			
		||||
# show-location-expr: true
 | 
			
		||||
 | 
			
		||||
# List of directories to use when searching for separate debug info files.
 | 
			
		||||
debug-info-directories: ["/usr/lib/debug/.build-id"]
 | 
			
		||||
`)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// createConfigPath creates the directory structure at which all config files are saved.
 | 
			
		||||
func createConfigPath() error {
 | 
			
		||||
	path, err := GetConfigFilePath("")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return os.MkdirAll(path, 0700)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetConfigFilePath gets the full path to the given config file name.
 | 
			
		||||
func GetConfigFilePath(file string) (string, error) {
 | 
			
		||||
	if configPath := os.Getenv("XDG_CONFIG_HOME"); configPath != "" {
 | 
			
		||||
		return path.Join(configPath, configDir, file), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userHomeDir := getUserHomeDir()
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "linux" {
 | 
			
		||||
		return path.Join(userHomeDir, ".config", configDir, file), nil
 | 
			
		||||
	}
 | 
			
		||||
	return path.Join(userHomeDir, configDirHidden, file), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Checks if the user has a config at the old location: $HOME/.dlv
 | 
			
		||||
func hasOldConfig() (bool, error) {
 | 
			
		||||
	// If you don't have XDG_CONFIG_HOME set and aren't on Linux you have nothing to move
 | 
			
		||||
	if os.Getenv("XDG_CONFIG_HOME") == "" && runtime.GOOS != "linux" {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userHomeDir := getUserHomeDir()
 | 
			
		||||
 | 
			
		||||
	o := path.Join(userHomeDir, configDirHidden, configFile)
 | 
			
		||||
	_, err := os.Stat(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if os.IsNotExist(err) {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUserHomeDir() string {
 | 
			
		||||
	userHomeDir := "."
 | 
			
		||||
	usr, err := user.Current()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		userHomeDir = usr.HomeDir
 | 
			
		||||
	}
 | 
			
		||||
	return userHomeDir
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								vendor/github.com/go-delve/delve/pkg/config/split.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								vendor/github.com/go-delve/delve/pkg/config/split.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SplitQuotedFields is like strings.Fields but ignores spaces inside areas surrounded
 | 
			
		||||
// by the specified quote character.
 | 
			
		||||
// To specify a single quote use backslash to escape it: '\''
 | 
			
		||||
func SplitQuotedFields(in string, quote rune) []string {
 | 
			
		||||
	type stateEnum int
 | 
			
		||||
	const (
 | 
			
		||||
		inSpace stateEnum = iota
 | 
			
		||||
		inField
 | 
			
		||||
		inQuote
 | 
			
		||||
		inQuoteEscaped
 | 
			
		||||
	)
 | 
			
		||||
	state := inSpace
 | 
			
		||||
	r := []string{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	for _, ch := range in {
 | 
			
		||||
		switch state {
 | 
			
		||||
		case inSpace:
 | 
			
		||||
			if ch == quote {
 | 
			
		||||
				state = inQuote
 | 
			
		||||
			} else if !unicode.IsSpace(ch) {
 | 
			
		||||
				buf.WriteRune(ch)
 | 
			
		||||
				state = inField
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case inField:
 | 
			
		||||
			if ch == quote {
 | 
			
		||||
				state = inQuote
 | 
			
		||||
			} else if unicode.IsSpace(ch) {
 | 
			
		||||
				r = append(r, buf.String())
 | 
			
		||||
				buf.Reset()
 | 
			
		||||
			} else {
 | 
			
		||||
				buf.WriteRune(ch)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case inQuote:
 | 
			
		||||
			if ch == quote {
 | 
			
		||||
				state = inField
 | 
			
		||||
			} else if ch == '\\' {
 | 
			
		||||
				state = inQuoteEscaped
 | 
			
		||||
			} else {
 | 
			
		||||
				buf.WriteRune(ch)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case inQuoteEscaped:
 | 
			
		||||
			buf.WriteRune(ch)
 | 
			
		||||
			state = inQuote
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if buf.Len() != 0 {
 | 
			
		||||
		r = append(r, buf.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
@@ -17,6 +17,7 @@ type CommonInformationEntry struct {
 | 
			
		||||
	DataAlignmentFactor   int64
 | 
			
		||||
	ReturnAddressRegister uint64
 | 
			
		||||
	InitialInstructions   []byte
 | 
			
		||||
	staticBase            uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Represents a Frame Descriptor Entry in the
 | 
			
		||||
@@ -25,17 +26,14 @@ type FrameDescriptionEntry struct {
 | 
			
		||||
	Length       uint32
 | 
			
		||||
	CIE          *CommonInformationEntry
 | 
			
		||||
	Instructions []byte
 | 
			
		||||
	begin, end   uint64
 | 
			
		||||
	begin, size  uint64
 | 
			
		||||
	order        binary.ByteOrder
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns whether or not the given address is within the
 | 
			
		||||
// bounds of this frame.
 | 
			
		||||
func (fde *FrameDescriptionEntry) Cover(addr uint64) bool {
 | 
			
		||||
	if (addr - fde.begin) < fde.end {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
	return (addr - fde.begin) < fde.size
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Address of first location for this frame.
 | 
			
		||||
@@ -45,7 +43,7 @@ func (fde *FrameDescriptionEntry) Begin() uint64 {
 | 
			
		||||
 | 
			
		||||
// Address of last location for this frame.
 | 
			
		||||
func (fde *FrameDescriptionEntry) End() uint64 {
 | 
			
		||||
	return fde.begin + fde.end
 | 
			
		||||
	return fde.begin + fde.size
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Set up frame for the given PC.
 | 
			
		||||
@@ -53,43 +51,27 @@ func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext {
 | 
			
		||||
	return executeDwarfProgramUntilPC(fde, pc)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Return the offset from the current SP that the return address is stored at.
 | 
			
		||||
func (fde *FrameDescriptionEntry) ReturnAddressOffset(pc uint64) (frameOffset, returnAddressOffset int64) {
 | 
			
		||||
	frame := fde.EstablishFrame(pc)
 | 
			
		||||
	return frame.cfa.offset, frame.regs[fde.CIE.ReturnAddressRegister].offset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FrameDescriptionEntries []*FrameDescriptionEntry
 | 
			
		||||
 | 
			
		||||
func NewFrameIndex() FrameDescriptionEntries {
 | 
			
		||||
	return make(FrameDescriptionEntries, 0, 1000)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type NoFDEForPCError struct {
 | 
			
		||||
type ErrNoFDEForPC struct {
 | 
			
		||||
	PC uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err *NoFDEForPCError) Error() string {
 | 
			
		||||
func (err *ErrNoFDEForPC) Error() string {
 | 
			
		||||
	return fmt.Sprintf("could not find FDE for PC %#v", err.PC)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the Frame Description Entry for the given PC.
 | 
			
		||||
func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) {
 | 
			
		||||
	idx := sort.Search(len(fdes), func(i int) bool {
 | 
			
		||||
		if fdes[i].Cover(pc) {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
		if fdes[i].LessThan(pc) {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return true
 | 
			
		||||
		return fdes[i].Cover(pc) || fdes[i].Begin() >= pc
 | 
			
		||||
	})
 | 
			
		||||
	if idx == len(fdes) {
 | 
			
		||||
		return nil, &NoFDEForPCError{pc}
 | 
			
		||||
	if idx == len(fdes) || !fdes[idx].Cover(pc) {
 | 
			
		||||
		return nil, &ErrNoFDEForPC{pc}
 | 
			
		||||
	}
 | 
			
		||||
	return fdes[idx], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (frame *FrameDescriptionEntry) LessThan(pc uint64) bool {
 | 
			
		||||
	return frame.End() <= pc
 | 
			
		||||
}
 | 
			
		||||
@@ -7,12 +7,14 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type parsefunc func(*parseContext) parsefunc
 | 
			
		||||
 | 
			
		||||
type parseContext struct {
 | 
			
		||||
	staticBase uint64
 | 
			
		||||
 | 
			
		||||
	buf     *bytes.Buffer
 | 
			
		||||
	entries FrameDescriptionEntries
 | 
			
		||||
	common  *CommonInformationEntry
 | 
			
		||||
@@ -23,10 +25,10 @@ type parseContext struct {
 | 
			
		||||
// Parse takes in data (a byte slice) and returns a slice of
 | 
			
		||||
// commonInformationEntry structures. Each commonInformationEntry
 | 
			
		||||
// has a slice of frameDescriptionEntry structures.
 | 
			
		||||
func Parse(data []byte, order binary.ByteOrder) FrameDescriptionEntries {
 | 
			
		||||
func Parse(data []byte, order binary.ByteOrder, staticBase uint64) FrameDescriptionEntries {
 | 
			
		||||
	var (
 | 
			
		||||
		buf  = bytes.NewBuffer(data)
 | 
			
		||||
		pctx = &parseContext{buf: buf, entries: NewFrameIndex()}
 | 
			
		||||
		pctx = &parseContext{buf: buf, entries: NewFrameIndex(), staticBase: staticBase}
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for fn := parselength; buf.Len() != 0; {
 | 
			
		||||
@@ -45,12 +47,19 @@ func cieEntry(data []byte) bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parselength(ctx *parseContext) parsefunc {
 | 
			
		||||
	var data = ctx.buf.Next(8)
 | 
			
		||||
	binary.Read(ctx.buf, binary.LittleEndian, &ctx.length)
 | 
			
		||||
 | 
			
		||||
	ctx.length = binary.LittleEndian.Uint32(data[:4]) - 4 // take off the length of the CIE id / CIE pointer.
 | 
			
		||||
	if ctx.length == 0 {
 | 
			
		||||
		// ZERO terminator
 | 
			
		||||
		return parselength
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if cieEntry(data[4:]) {
 | 
			
		||||
		ctx.common = &CommonInformationEntry{Length: ctx.length}
 | 
			
		||||
	var data = ctx.buf.Next(4)
 | 
			
		||||
 | 
			
		||||
	ctx.length -= 4 // take off the length of the CIE id / CIE pointer.
 | 
			
		||||
 | 
			
		||||
	if cieEntry(data) {
 | 
			
		||||
		ctx.common = &CommonInformationEntry{Length: ctx.length, staticBase: ctx.staticBase}
 | 
			
		||||
		return parseCIE
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -61,8 +70,8 @@ func parselength(ctx *parseContext) parsefunc {
 | 
			
		||||
func parseFDE(ctx *parseContext) parsefunc {
 | 
			
		||||
	r := ctx.buf.Next(int(ctx.length))
 | 
			
		||||
 | 
			
		||||
	ctx.frame.begin = binary.LittleEndian.Uint64(r[:8])
 | 
			
		||||
	ctx.frame.end = binary.LittleEndian.Uint64(r[8:16])
 | 
			
		||||
	ctx.frame.begin = binary.LittleEndian.Uint64(r[:8]) + ctx.staticBase
 | 
			
		||||
	ctx.frame.size = binary.LittleEndian.Uint64(r[8:16])
 | 
			
		||||
 | 
			
		||||
	// Insert into the tree after setting address range begin
 | 
			
		||||
	// otherwise compares won't work.
 | 
			
		||||
@@ -5,41 +5,31 @@ import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type CurrentFrameAddress struct {
 | 
			
		||||
	register   uint64
 | 
			
		||||
	offset     int64
 | 
			
		||||
	expression []byte
 | 
			
		||||
	rule       byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DWRule struct {
 | 
			
		||||
	rule       byte
 | 
			
		||||
	offset     int64
 | 
			
		||||
	newreg     uint64
 | 
			
		||||
	expression []byte
 | 
			
		||||
	Rule       Rule
 | 
			
		||||
	Offset     int64
 | 
			
		||||
	Reg        uint64
 | 
			
		||||
	Expression []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FrameContext struct {
 | 
			
		||||
	loc           uint64
 | 
			
		||||
	order         binary.ByteOrder
 | 
			
		||||
	address       uint64
 | 
			
		||||
	cfa           CurrentFrameAddress
 | 
			
		||||
	regs          map[uint64]DWRule
 | 
			
		||||
	CFA           DWRule
 | 
			
		||||
	Regs          map[uint64]DWRule
 | 
			
		||||
	initialRegs   map[uint64]DWRule
 | 
			
		||||
	prevRegs      map[uint64]DWRule
 | 
			
		||||
	buf           *bytes.Buffer
 | 
			
		||||
	cie           *CommonInformationEntry
 | 
			
		||||
	RetAddrReg    uint64
 | 
			
		||||
	codeAlignment uint64
 | 
			
		||||
	dataAlignment int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fctx *FrameContext) CFAOffset() int64 {
 | 
			
		||||
	return fctx.cfa.offset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Instructions used to recreate the table from the .debug_frame data.
 | 
			
		||||
const (
 | 
			
		||||
	DW_CFA_nop                = 0x0        // No ops
 | 
			
		||||
@@ -73,15 +63,19 @@ const (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Rules defined for register values.
 | 
			
		||||
type Rule byte
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rule_undefined = iota
 | 
			
		||||
	rule_sameval
 | 
			
		||||
	rule_offset
 | 
			
		||||
	rule_valoffset
 | 
			
		||||
	rule_register
 | 
			
		||||
	rule_expression
 | 
			
		||||
	rule_valexpression
 | 
			
		||||
	rule_architectural
 | 
			
		||||
	RuleUndefined Rule = iota
 | 
			
		||||
	RuleSameVal
 | 
			
		||||
	RuleOffset
 | 
			
		||||
	RuleValOffset
 | 
			
		||||
	RuleRegister
 | 
			
		||||
	RuleExpression
 | 
			
		||||
	RuleValExpression
 | 
			
		||||
	RuleArchitectural
 | 
			
		||||
	RuleCFA          // Value is rule.Reg + rule.Offset
 | 
			
		||||
	RuleFramePointer // Value is stored at address rule.Reg + rule.Offset, but only if it's less than the current CFA, otherwise same value
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const low_6_offset = 0x3f
 | 
			
		||||
@@ -124,7 +118,8 @@ func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
 | 
			
		||||
	copy(initialInstructions, cie.InitialInstructions)
 | 
			
		||||
	frame := &FrameContext{
 | 
			
		||||
		cie:           cie,
 | 
			
		||||
		regs:          make(map[uint64]DWRule),
 | 
			
		||||
		Regs:          make(map[uint64]DWRule),
 | 
			
		||||
		RetAddrReg:    cie.ReturnAddressRegister,
 | 
			
		||||
		initialRegs:   make(map[uint64]DWRule),
 | 
			
		||||
		prevRegs:      make(map[uint64]DWRule),
 | 
			
		||||
		codeAlignment: cie.CodeAlignmentFactor,
 | 
			
		||||
@@ -142,9 +137,7 @@ func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameCon
 | 
			
		||||
	frame.order = fde.order
 | 
			
		||||
	frame.loc = fde.Begin()
 | 
			
		||||
	frame.address = pc
 | 
			
		||||
	fdeInstructions := make([]byte, len(fde.Instructions))
 | 
			
		||||
	copy(fdeInstructions, fde.Instructions)
 | 
			
		||||
	frame.ExecuteUntilPC(fdeInstructions)
 | 
			
		||||
	frame.ExecuteUntilPC(fde.Instructions)
 | 
			
		||||
 | 
			
		||||
	return frame
 | 
			
		||||
}
 | 
			
		||||
@@ -161,7 +154,7 @@ func (frame *FrameContext) ExecuteUntilPC(instructions []byte) {
 | 
			
		||||
	frame.buf.Write(instructions)
 | 
			
		||||
 | 
			
		||||
	// We only need to execute the instructions until
 | 
			
		||||
	// ctx.loc > ctx.addess (which is the address we
 | 
			
		||||
	// ctx.loc > ctx.address (which is the address we
 | 
			
		||||
	// are currently at in the traced process).
 | 
			
		||||
	for frame.address >= frame.loc && frame.buf.Len() > 0 {
 | 
			
		||||
		executeDwarfInstruction(frame)
 | 
			
		||||
@@ -262,7 +255,7 @@ func offset(frame *FrameContext) {
 | 
			
		||||
		offset, _ = util.DecodeULEB128(frame.buf)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.regs[uint64(reg)] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
 | 
			
		||||
	frame.Regs[uint64(reg)] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func restore(frame *FrameContext) {
 | 
			
		||||
@@ -274,9 +267,9 @@ func restore(frame *FrameContext) {
 | 
			
		||||
	reg := uint64(b & low_6_offset)
 | 
			
		||||
	oldrule, ok := frame.initialRegs[reg]
 | 
			
		||||
	if ok {
 | 
			
		||||
		frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
 | 
			
		||||
		frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
 | 
			
		||||
	} else {
 | 
			
		||||
		frame.regs[reg] = DWRule{rule: rule_undefined}
 | 
			
		||||
		frame.Regs[reg] = DWRule{Rule: RuleUndefined}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -284,7 +277,7 @@ func setloc(frame *FrameContext) {
 | 
			
		||||
	var loc uint64
 | 
			
		||||
	binary.Read(frame.buf, frame.order, &loc)
 | 
			
		||||
 | 
			
		||||
	frame.loc = loc
 | 
			
		||||
	frame.loc = loc + frame.cie.staticBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func offsetextended(frame *FrameContext) {
 | 
			
		||||
@@ -293,31 +286,31 @@ func offsetextended(frame *FrameContext) {
 | 
			
		||||
		offset, _ = util.DecodeULEB128(frame.buf)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.regs[reg] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func undefined(frame *FrameContext) {
 | 
			
		||||
	reg, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	frame.regs[reg] = DWRule{rule: rule_undefined}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Rule: RuleUndefined}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func samevalue(frame *FrameContext) {
 | 
			
		||||
	reg, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	frame.regs[reg] = DWRule{rule: rule_sameval}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Rule: RuleSameVal}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func register(frame *FrameContext) {
 | 
			
		||||
	reg1, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	reg2, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	frame.regs[reg1] = DWRule{newreg: reg2, rule: rule_register}
 | 
			
		||||
	frame.Regs[reg1] = DWRule{Reg: reg2, Rule: RuleRegister}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rememberstate(frame *FrameContext) {
 | 
			
		||||
	frame.prevRegs = frame.regs
 | 
			
		||||
	frame.prevRegs = frame.Regs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func restorestate(frame *FrameContext) {
 | 
			
		||||
	frame.regs = frame.prevRegs
 | 
			
		||||
	frame.Regs = frame.prevRegs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func restoreextended(frame *FrameContext) {
 | 
			
		||||
@@ -325,9 +318,9 @@ func restoreextended(frame *FrameContext) {
 | 
			
		||||
 | 
			
		||||
	oldrule, ok := frame.initialRegs[reg]
 | 
			
		||||
	if ok {
 | 
			
		||||
		frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
 | 
			
		||||
		frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
 | 
			
		||||
	} else {
 | 
			
		||||
		frame.regs[reg] = DWRule{rule: rule_undefined}
 | 
			
		||||
		frame.Regs[reg] = DWRule{Rule: RuleUndefined}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -335,32 +328,34 @@ func defcfa(frame *FrameContext) {
 | 
			
		||||
	reg, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	offset, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
 | 
			
		||||
	frame.cfa.register = reg
 | 
			
		||||
	frame.cfa.offset = int64(offset)
 | 
			
		||||
	frame.CFA.Rule = RuleCFA
 | 
			
		||||
	frame.CFA.Reg = reg
 | 
			
		||||
	frame.CFA.Offset = int64(offset)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defcfaregister(frame *FrameContext) {
 | 
			
		||||
	reg, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	frame.cfa.register = reg
 | 
			
		||||
	frame.CFA.Reg = reg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defcfaoffset(frame *FrameContext) {
 | 
			
		||||
	offset, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	frame.cfa.offset = int64(offset)
 | 
			
		||||
	frame.CFA.Offset = int64(offset)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defcfasf(frame *FrameContext) {
 | 
			
		||||
	reg, _ := util.DecodeULEB128(frame.buf)
 | 
			
		||||
	offset, _ := util.DecodeSLEB128(frame.buf)
 | 
			
		||||
 | 
			
		||||
	frame.cfa.register = reg
 | 
			
		||||
	frame.cfa.offset = offset * frame.dataAlignment
 | 
			
		||||
	frame.CFA.Rule = RuleCFA
 | 
			
		||||
	frame.CFA.Reg = reg
 | 
			
		||||
	frame.CFA.Offset = offset * frame.dataAlignment
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defcfaoffsetsf(frame *FrameContext) {
 | 
			
		||||
	offset, _ := util.DecodeSLEB128(frame.buf)
 | 
			
		||||
	offset *= frame.dataAlignment
 | 
			
		||||
	frame.cfa.offset = offset
 | 
			
		||||
	frame.CFA.Offset = offset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func defcfaexpression(frame *FrameContext) {
 | 
			
		||||
@@ -369,8 +364,8 @@ func defcfaexpression(frame *FrameContext) {
 | 
			
		||||
		expr = frame.buf.Next(int(l))
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.cfa.expression = expr
 | 
			
		||||
	frame.cfa.rule = rule_expression
 | 
			
		||||
	frame.CFA.Expression = expr
 | 
			
		||||
	frame.CFA.Rule = RuleExpression
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func expression(frame *FrameContext) {
 | 
			
		||||
@@ -380,7 +375,7 @@ func expression(frame *FrameContext) {
 | 
			
		||||
		expr   = frame.buf.Next(int(l))
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.regs[reg] = DWRule{rule: rule_expression, expression: expr}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Rule: RuleExpression, Expression: expr}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func offsetextendedsf(frame *FrameContext) {
 | 
			
		||||
@@ -389,7 +384,7 @@ func offsetextendedsf(frame *FrameContext) {
 | 
			
		||||
		offset, _ = util.DecodeSLEB128(frame.buf)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_offset}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleOffset}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valoffset(frame *FrameContext) {
 | 
			
		||||
@@ -398,7 +393,7 @@ func valoffset(frame *FrameContext) {
 | 
			
		||||
		offset, _ = util.DecodeULEB128(frame.buf)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.regs[reg] = DWRule{offset: int64(offset), rule: rule_valoffset}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Offset: int64(offset), Rule: RuleValOffset}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valoffsetsf(frame *FrameContext) {
 | 
			
		||||
@@ -407,7 +402,7 @@ func valoffsetsf(frame *FrameContext) {
 | 
			
		||||
		offset, _ = util.DecodeSLEB128(frame.buf)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_valoffset}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleValOffset}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func valexpression(frame *FrameContext) {
 | 
			
		||||
@@ -417,7 +412,7 @@ func valexpression(frame *FrameContext) {
 | 
			
		||||
		expr   = frame.buf.Next(int(l))
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	frame.regs[reg] = DWRule{rule: rule_valexpression, expression: expr}
 | 
			
		||||
	frame.Regs[reg] = DWRule{Rule: RuleValExpression, Expression: expr}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func louser(frame *FrameContext) {
 | 
			
		||||
							
								
								
									
										107
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/godwarf/sections.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/godwarf/sections.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,107 @@
 | 
			
		||||
package godwarf
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"compress/zlib"
 | 
			
		||||
	"debug/elf"
 | 
			
		||||
	"debug/macho"
 | 
			
		||||
	"debug/pe"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GetDebugSectionElf returns the data contents of the specified debug
 | 
			
		||||
// section, decompressing it if it is compressed.
 | 
			
		||||
// For example GetDebugSectionElf("line") will return the contents of
 | 
			
		||||
// .debug_line, if .debug_line doesn't exist it will try to return the
 | 
			
		||||
// decompressed contents of .zdebug_line.
 | 
			
		||||
func GetDebugSectionElf(f *elf.File, name string) ([]byte, error) {
 | 
			
		||||
	sec := f.Section(".debug_" + name)
 | 
			
		||||
	if sec != nil {
 | 
			
		||||
		return sec.Data()
 | 
			
		||||
	}
 | 
			
		||||
	sec = f.Section(".zdebug_" + name)
 | 
			
		||||
	if sec == nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not find .debug_%s section", name)
 | 
			
		||||
	}
 | 
			
		||||
	b, err := sec.Data()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return decompressMaybe(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDebugSectionPE returns the data contents of the specified debug
 | 
			
		||||
// section, decompressing it if it is compressed.
 | 
			
		||||
// For example GetDebugSectionPE("line") will return the contents of
 | 
			
		||||
// .debug_line, if .debug_line doesn't exist it will try to return the
 | 
			
		||||
// decompressed contents of .zdebug_line.
 | 
			
		||||
func GetDebugSectionPE(f *pe.File, name string) ([]byte, error) {
 | 
			
		||||
	sec := f.Section(".debug_" + name)
 | 
			
		||||
	if sec != nil {
 | 
			
		||||
		return peSectionData(sec)
 | 
			
		||||
	}
 | 
			
		||||
	sec = f.Section(".zdebug_" + name)
 | 
			
		||||
	if sec == nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not find .debug_%s section", name)
 | 
			
		||||
	}
 | 
			
		||||
	b, err := peSectionData(sec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return decompressMaybe(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func peSectionData(sec *pe.Section) ([]byte, error) {
 | 
			
		||||
	b, err := sec.Data()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
 | 
			
		||||
		b = b[:sec.VirtualSize]
 | 
			
		||||
	}
 | 
			
		||||
	return b, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GetDebugSectionMacho returns the data contents of the specified debug
 | 
			
		||||
// section, decompressing it if it is compressed.
 | 
			
		||||
// For example GetDebugSectionMacho("line") will return the contents of
 | 
			
		||||
// __debug_line, if __debug_line doesn't exist it will try to return the
 | 
			
		||||
// decompressed contents of __zdebug_line.
 | 
			
		||||
func GetDebugSectionMacho(f *macho.File, name string) ([]byte, error) {
 | 
			
		||||
	sec := f.Section("__debug_" + name)
 | 
			
		||||
	if sec != nil {
 | 
			
		||||
		return sec.Data()
 | 
			
		||||
	}
 | 
			
		||||
	sec = f.Section("__zdebug_" + name)
 | 
			
		||||
	if sec == nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not find .debug_%s section", name)
 | 
			
		||||
	}
 | 
			
		||||
	b, err := sec.Data()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return decompressMaybe(b)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func decompressMaybe(b []byte) ([]byte, error) {
 | 
			
		||||
	if len(b) < 12 || string(b[:4]) != "ZLIB" {
 | 
			
		||||
		// not compressed
 | 
			
		||||
		return b, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dlen := binary.BigEndian.Uint64(b[4:12])
 | 
			
		||||
	dbuf := make([]byte, dlen)
 | 
			
		||||
	r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := io.ReadFull(r, dbuf); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := r.Close(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbuf, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -6,20 +6,65 @@
 | 
			
		||||
// The format is heavily biased toward C, but for simplicity
 | 
			
		||||
// the String methods use a pseudo-Go syntax.
 | 
			
		||||
 | 
			
		||||
package dwarf
 | 
			
		||||
// Borrowed from golang.org/x/debug/dwarf/type.go
 | 
			
		||||
 | 
			
		||||
package godwarf
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/dwarf"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	AttrGoKind          dwarf.Attr = 0x2900
 | 
			
		||||
	AttrGoKey           dwarf.Attr = 0x2901
 | 
			
		||||
	AttrGoElem          dwarf.Attr = 0x2902
 | 
			
		||||
	AttrGoEmbeddedField dwarf.Attr = 0x2903
 | 
			
		||||
	AttrGoRuntimeType   dwarf.Attr = 0x2904
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry.
 | 
			
		||||
const (
 | 
			
		||||
	encAddress        = 0x01
 | 
			
		||||
	encBoolean        = 0x02
 | 
			
		||||
	encComplexFloat   = 0x03
 | 
			
		||||
	encFloat          = 0x04
 | 
			
		||||
	encSigned         = 0x05
 | 
			
		||||
	encSignedChar     = 0x06
 | 
			
		||||
	encUnsigned       = 0x07
 | 
			
		||||
	encUnsignedChar   = 0x08
 | 
			
		||||
	encImaginaryFloat = 0x09
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const cyclicalTypeStop = "<cyclical>" // guard value printed for types with a cyclical definition, to avoid inifinite recursion in Type.String
 | 
			
		||||
 | 
			
		||||
type recCheck map[dwarf.Offset]struct{}
 | 
			
		||||
 | 
			
		||||
func (recCheck recCheck) acquire(off dwarf.Offset) (release func()) {
 | 
			
		||||
	if _, rec := recCheck[off]; rec {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	recCheck[off] = struct{}{}
 | 
			
		||||
	return func() {
 | 
			
		||||
		delete(recCheck, off)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Type conventionally represents a pointer to any of the
 | 
			
		||||
// specific Type structures (CharType, StructType, etc.).
 | 
			
		||||
//TODO: remove this use dwarf.Type
 | 
			
		||||
type Type interface {
 | 
			
		||||
	Common() *CommonType
 | 
			
		||||
	String() string
 | 
			
		||||
	Size() int64
 | 
			
		||||
 | 
			
		||||
	stringIntl(recCheck) string
 | 
			
		||||
	sizeIntl(recCheck) int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A CommonType holds fields common to multiple types.
 | 
			
		||||
@@ -29,12 +74,13 @@ type CommonType struct {
 | 
			
		||||
	ByteSize    int64        // size of value of this type, in bytes
 | 
			
		||||
	Name        string       // name that can be used to refer to type
 | 
			
		||||
	ReflectKind reflect.Kind // the reflect kind of the type.
 | 
			
		||||
	Offset      Offset       // the offset at which this type was read
 | 
			
		||||
	Offset      dwarf.Offset // the offset at which this type was read
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *CommonType) Common() *CommonType { return c }
 | 
			
		||||
 | 
			
		||||
func (c *CommonType) Size() int64 { return c.ByteSize }
 | 
			
		||||
func (c *CommonType) Size() int64             { return c.ByteSize }
 | 
			
		||||
func (c *CommonType) sizeIntl(recCheck) int64 { return c.ByteSize }
 | 
			
		||||
 | 
			
		||||
// Basic types
 | 
			
		||||
 | 
			
		||||
@@ -47,7 +93,9 @@ type BasicType struct {
 | 
			
		||||
 | 
			
		||||
func (b *BasicType) Basic() *BasicType { return b }
 | 
			
		||||
 | 
			
		||||
func (t *BasicType) String() string {
 | 
			
		||||
func (t *BasicType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *BasicType) stringIntl(recCheck) string {
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -108,9 +156,27 @@ type QualType struct {
 | 
			
		||||
	Type Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *QualType) String() string { return t.Qual + " " + t.Type.String() }
 | 
			
		||||
func (t *QualType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *QualType) Size() int64 { return t.Type.Size() }
 | 
			
		||||
func (t *QualType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Qual + " " + t.Type.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *QualType) Size() int64 { return t.sizeIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *QualType) sizeIntl(recCheck recCheck) int64 {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return t.CommonType.ByteSize
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Type.sizeIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ArrayType represents a fixed size array type.
 | 
			
		||||
type ArrayType struct {
 | 
			
		||||
@@ -120,18 +186,36 @@ type ArrayType struct {
 | 
			
		||||
	Count         int64 // if == -1, an incomplete array, like char x[].
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) String() string {
 | 
			
		||||
	return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
 | 
			
		||||
func (t *ArrayType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() }
 | 
			
		||||
func (t *ArrayType) Size() int64 { return t.sizeIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) sizeIntl(recCheck recCheck) int64 {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return t.CommonType.ByteSize
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Count * t.Type.sizeIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A VoidType represents the C void type.
 | 
			
		||||
type VoidType struct {
 | 
			
		||||
	CommonType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *VoidType) String() string { return "void" }
 | 
			
		||||
func (t *VoidType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *VoidType) stringIntl(recCheck) string { return "void" }
 | 
			
		||||
 | 
			
		||||
// A PtrType represents a pointer type.
 | 
			
		||||
type PtrType struct {
 | 
			
		||||
@@ -139,7 +223,16 @@ type PtrType struct {
 | 
			
		||||
	Type Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *PtrType) String() string { return "*" + t.Type.String() }
 | 
			
		||||
func (t *PtrType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *PtrType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return "*" + t.Type.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A StructType represents a struct, union, or C++ class type.
 | 
			
		||||
type StructType struct {
 | 
			
		||||
@@ -158,16 +251,24 @@ type StructField struct {
 | 
			
		||||
	ByteSize   int64
 | 
			
		||||
	BitOffset  int64 // within the ByteSize bytes at ByteOffset
 | 
			
		||||
	BitSize    int64 // zero if not a bit field
 | 
			
		||||
	Embedded   bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *StructType) String() string {
 | 
			
		||||
func (t *StructType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *StructType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	if t.StructName != "" {
 | 
			
		||||
		return t.Kind + " " + t.StructName
 | 
			
		||||
	}
 | 
			
		||||
	return t.Defn()
 | 
			
		||||
	return t.Defn(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *StructType) Defn() string {
 | 
			
		||||
func (t *StructType) Defn(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	s := t.Kind
 | 
			
		||||
	if t.StructName != "" {
 | 
			
		||||
		s += " " + t.StructName
 | 
			
		||||
@@ -181,7 +282,7 @@ func (t *StructType) Defn() string {
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			s += "; "
 | 
			
		||||
		}
 | 
			
		||||
		s += f.Name + " " + f.Type.String()
 | 
			
		||||
		s += f.Name + " " + f.Type.stringIntl(recCheck)
 | 
			
		||||
		s += "@" + strconv.FormatInt(f.ByteOffset, 10)
 | 
			
		||||
		if f.BitSize > 0 {
 | 
			
		||||
			s += " : " + strconv.FormatInt(f.BitSize, 10)
 | 
			
		||||
@@ -199,11 +300,18 @@ type SliceType struct {
 | 
			
		||||
	ElemType Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *SliceType) String() string {
 | 
			
		||||
func (t *SliceType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *SliceType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
	return "[]" + t.ElemType.String()
 | 
			
		||||
	return "[]" + t.ElemType.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A StringType represents a Go string type. It looks like a StructType, describing
 | 
			
		||||
@@ -212,7 +320,9 @@ type StringType struct {
 | 
			
		||||
	StructType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *StringType) String() string {
 | 
			
		||||
func (t *StringType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *StringType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -224,7 +334,9 @@ type InterfaceType struct {
 | 
			
		||||
	TypedefType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *InterfaceType) String() string {
 | 
			
		||||
func (t *InterfaceType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *InterfaceType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -246,7 +358,9 @@ type EnumValue struct {
 | 
			
		||||
	Val  int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *EnumType) String() string {
 | 
			
		||||
func (t *EnumType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *EnumType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	s := "enum"
 | 
			
		||||
	if t.EnumName != "" {
 | 
			
		||||
		s += " " + t.EnumName
 | 
			
		||||
@@ -269,17 +383,24 @@ type FuncType struct {
 | 
			
		||||
	ParamType  []Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *FuncType) String() string {
 | 
			
		||||
func (t *FuncType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *FuncType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	s := "func("
 | 
			
		||||
	for i, t := range t.ParamType {
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			s += ", "
 | 
			
		||||
		}
 | 
			
		||||
		s += t.String()
 | 
			
		||||
		s += t.stringIntl(recCheck)
 | 
			
		||||
	}
 | 
			
		||||
	s += ")"
 | 
			
		||||
	if t.ReturnType != nil {
 | 
			
		||||
		s += " " + t.ReturnType.String()
 | 
			
		||||
		s += " " + t.ReturnType.stringIntl(recCheck)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
@@ -289,7 +410,9 @@ type DotDotDotType struct {
 | 
			
		||||
	CommonType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *DotDotDotType) String() string { return "..." }
 | 
			
		||||
func (t *DotDotDotType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *DotDotDotType) stringIntl(recCheck recCheck) string { return "..." }
 | 
			
		||||
 | 
			
		||||
// A TypedefType represents a named type.
 | 
			
		||||
type TypedefType struct {
 | 
			
		||||
@@ -297,9 +420,20 @@ type TypedefType struct {
 | 
			
		||||
	Type Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) String() string { return t.Name }
 | 
			
		||||
func (t *TypedefType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) Size() int64 { return t.Type.Size() }
 | 
			
		||||
func (t *TypedefType) stringIntl(recCheck recCheck) string { return t.Name }
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) Size() int64 { return t.sizeIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) sizeIntl(recCheck recCheck) int64 {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return t.CommonType.ByteSize
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Type.sizeIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A MapType represents a Go map type. It looks like a TypedefType, describing
 | 
			
		||||
// the runtime-internal structure, with extra fields.
 | 
			
		||||
@@ -309,7 +443,14 @@ type MapType struct {
 | 
			
		||||
	ElemType Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *MapType) String() string {
 | 
			
		||||
func (t *MapType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *MapType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -322,38 +463,33 @@ type ChanType struct {
 | 
			
		||||
	ElemType Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *ChanType) String() string {
 | 
			
		||||
func (t *ChanType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *ChanType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
	return "chan " + t.ElemType.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// typeReader is used to read from either the info section or the
 | 
			
		||||
// types section.
 | 
			
		||||
type typeReader interface {
 | 
			
		||||
	Seek(Offset)
 | 
			
		||||
	Next() (*Entry, error)
 | 
			
		||||
	clone() typeReader
 | 
			
		||||
	offset() Offset
 | 
			
		||||
	// AddressSize returns the size in bytes of addresses in the current
 | 
			
		||||
	// compilation unit.
 | 
			
		||||
	AddressSize() int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type reads the type at off in the DWARF ``info'' section.
 | 
			
		||||
func (d *Data) Type(off Offset) (Type, error) {
 | 
			
		||||
	return d.readType("info", d.Reader(), off, d.typeCache)
 | 
			
		||||
func ReadType(d *dwarf.Data, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
 | 
			
		||||
	return readType(d, "info", d.Reader(), off, typeCache)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getKind(e *Entry) reflect.Kind {
 | 
			
		||||
func getKind(e *dwarf.Entry) reflect.Kind {
 | 
			
		||||
	integer, _ := e.Val(AttrGoKind).(int64)
 | 
			
		||||
	return reflect.Kind(integer)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readType reads a type from r at off of name using and updating a
 | 
			
		||||
// type cache.
 | 
			
		||||
func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type) (Type, error) {
 | 
			
		||||
func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
 | 
			
		||||
	if t, ok := typeCache[off]; ok {
 | 
			
		||||
		return t, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -364,10 +500,10 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
	}
 | 
			
		||||
	addressSize := r.AddressSize()
 | 
			
		||||
	if e == nil || e.Offset != off {
 | 
			
		||||
		return nil, DecodeError{name, off, "no type at offset"}
 | 
			
		||||
		return nil, dwarf.DecodeError{name, off, "no type at offset"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Parse type from Entry.
 | 
			
		||||
	// Parse type from dwarf.Entry.
 | 
			
		||||
	// Must always set typeCache[off] before calling
 | 
			
		||||
	// d.Type recursively, to handle circular types correctly.
 | 
			
		||||
	var typ Type
 | 
			
		||||
@@ -375,7 +511,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
	nextDepth := 0
 | 
			
		||||
 | 
			
		||||
	// Get next child; set err if error happens.
 | 
			
		||||
	next := func() *Entry {
 | 
			
		||||
	next := func() *dwarf.Entry {
 | 
			
		||||
		if !e.Children {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
@@ -390,10 +526,6 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
				err = err1
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			if kid == nil {
 | 
			
		||||
				err = DecodeError{name, r.offset(), "unexpected end of DWARF entries"}
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			if kid.Tag == 0 {
 | 
			
		||||
				if nextDepth > 0 {
 | 
			
		||||
					nextDepth--
 | 
			
		||||
@@ -411,20 +543,19 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get Type referred to by Entry's attr.
 | 
			
		||||
	// Get Type referred to by dwarf.Entry's attr.
 | 
			
		||||
	// Set err if error happens.  Not having a type is an error.
 | 
			
		||||
	typeOf := func(e *Entry, attr Attr) Type {
 | 
			
		||||
	typeOf := func(e *dwarf.Entry, attr dwarf.Attr) Type {
 | 
			
		||||
		tval := e.Val(attr)
 | 
			
		||||
		var t Type
 | 
			
		||||
		switch toff := tval.(type) {
 | 
			
		||||
		case Offset:
 | 
			
		||||
			if t, err = d.readType(name, r.clone(), toff, typeCache); err != nil {
 | 
			
		||||
		case dwarf.Offset:
 | 
			
		||||
			if t, err = readType(d, name, d.Reader(), toff, typeCache); err != nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		case uint64:
 | 
			
		||||
			if t, err = d.sigToType(toff); err != nil {
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
			err = dwarf.DecodeError{name, e.Offset, "DWARFv4 section debug_types unsupported"}
 | 
			
		||||
			return nil
 | 
			
		||||
		default:
 | 
			
		||||
			// It appears that no Type means "void".
 | 
			
		||||
			return new(VoidType)
 | 
			
		||||
@@ -433,7 +564,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch e.Tag {
 | 
			
		||||
	case TagArrayType:
 | 
			
		||||
	case dwarf.TagArrayType:
 | 
			
		||||
		// Multi-dimensional array.  (DWARF v2 §5.4)
 | 
			
		||||
		// Attributes:
 | 
			
		||||
		//	AttrType:subtype [required]
 | 
			
		||||
@@ -444,16 +575,16 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		//	TagSubrangeType or TagEnumerationType giving one dimension.
 | 
			
		||||
		//	dimensions are in left to right order.
 | 
			
		||||
		t := new(ArrayType)
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.ReflectKind = getKind(e)
 | 
			
		||||
		typ = t
 | 
			
		||||
		typeCache[off] = t
 | 
			
		||||
		if t.Type = typeOf(e, AttrType); err != nil {
 | 
			
		||||
		if t.Type = typeOf(e, dwarf.AttrType); err != nil {
 | 
			
		||||
			goto Error
 | 
			
		||||
		}
 | 
			
		||||
		if bytes, ok := e.Val(AttrStride).(int64); ok {
 | 
			
		||||
		if bytes, ok := e.Val(dwarf.AttrStride).(int64); ok {
 | 
			
		||||
			t.StrideBitSize = 8 * bytes
 | 
			
		||||
		} else if bits, ok := e.Val(AttrStrideSize).(int64); ok {
 | 
			
		||||
		} else if bits, ok := e.Val(dwarf.AttrStrideSize).(int64); ok {
 | 
			
		||||
			t.StrideBitSize = bits
 | 
			
		||||
		} else {
 | 
			
		||||
			// If there's no stride specified, assume it's the size of the
 | 
			
		||||
@@ -467,11 +598,11 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
			// TODO(rsc): Can also be TagEnumerationType
 | 
			
		||||
			// but haven't seen that in the wild yet.
 | 
			
		||||
			switch kid.Tag {
 | 
			
		||||
			case TagSubrangeType:
 | 
			
		||||
				count, ok := kid.Val(AttrCount).(int64)
 | 
			
		||||
			case dwarf.TagSubrangeType:
 | 
			
		||||
				count, ok := kid.Val(dwarf.AttrCount).(int64)
 | 
			
		||||
				if !ok {
 | 
			
		||||
					// Old binaries may have an upper bound instead.
 | 
			
		||||
					count, ok = kid.Val(AttrUpperBound).(int64)
 | 
			
		||||
					count, ok = kid.Val(dwarf.AttrUpperBound).(int64)
 | 
			
		||||
					if ok {
 | 
			
		||||
						count++ // Length is one more than upper bound.
 | 
			
		||||
					} else {
 | 
			
		||||
@@ -486,8 +617,8 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
					t.Type = &ArrayType{Type: t.Type, Count: count}
 | 
			
		||||
				}
 | 
			
		||||
				ndim++
 | 
			
		||||
			case TagEnumerationType:
 | 
			
		||||
				err = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}
 | 
			
		||||
			case dwarf.TagEnumerationType:
 | 
			
		||||
				err = dwarf.DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}
 | 
			
		||||
				goto Error
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -496,7 +627,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
			t.Count = -1
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case TagBaseType:
 | 
			
		||||
	case dwarf.TagBaseType:
 | 
			
		||||
		// Basic type.  (DWARF v2 §5.1)
 | 
			
		||||
		// Attributes:
 | 
			
		||||
		//	AttrName: name of base type in programming language of the compilation unit [required]
 | 
			
		||||
@@ -504,15 +635,15 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		//	AttrByteSize: size of type in bytes [required]
 | 
			
		||||
		//	AttrBitOffset: for sub-byte types, size in bits
 | 
			
		||||
		//	AttrBitSize: for sub-byte types, bit offset of high order bit in the AttrByteSize bytes
 | 
			
		||||
		name, _ := e.Val(AttrName).(string)
 | 
			
		||||
		enc, ok := e.Val(AttrEncoding).(int64)
 | 
			
		||||
		name, _ := e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		enc, ok := e.Val(dwarf.AttrEncoding).(int64)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			err = DecodeError{name, e.Offset, "missing encoding attribute for " + name}
 | 
			
		||||
			err = dwarf.DecodeError{name, e.Offset, "missing encoding attribute for " + name}
 | 
			
		||||
			goto Error
 | 
			
		||||
		}
 | 
			
		||||
		switch enc {
 | 
			
		||||
		default:
 | 
			
		||||
			err = DecodeError{name, e.Offset, "unrecognized encoding attribute value"}
 | 
			
		||||
			err = dwarf.DecodeError{name, e.Offset, "unrecognized encoding attribute value"}
 | 
			
		||||
			goto Error
 | 
			
		||||
 | 
			
		||||
		case encAddress:
 | 
			
		||||
@@ -525,7 +656,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
				// clang writes out 'complex' instead of 'complex float' or 'complex double'.
 | 
			
		||||
				// clang also writes out a byte size that we can use to distinguish.
 | 
			
		||||
				// See issue 8694.
 | 
			
		||||
				switch byteSize, _ := e.Val(AttrByteSize).(int64); byteSize {
 | 
			
		||||
				switch byteSize, _ := e.Val(dwarf.AttrByteSize).(int64); byteSize {
 | 
			
		||||
				case 8:
 | 
			
		||||
					name = "complex float"
 | 
			
		||||
				case 16:
 | 
			
		||||
@@ -548,11 +679,11 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
			Basic() *BasicType
 | 
			
		||||
		}).Basic()
 | 
			
		||||
		t.Name = name
 | 
			
		||||
		t.BitSize, _ = e.Val(AttrBitSize).(int64)
 | 
			
		||||
		t.BitOffset, _ = e.Val(AttrBitOffset).(int64)
 | 
			
		||||
		t.BitSize, _ = e.Val(dwarf.AttrBitSize).(int64)
 | 
			
		||||
		t.BitOffset, _ = e.Val(dwarf.AttrBitOffset).(int64)
 | 
			
		||||
		t.ReflectKind = getKind(e)
 | 
			
		||||
 | 
			
		||||
	case TagClassType, TagStructType, TagUnionType:
 | 
			
		||||
	case dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType:
 | 
			
		||||
		// Structure, union, or class type.  (DWARF v2 §5.5)
 | 
			
		||||
		// Also Slices and Strings (Go-specific).
 | 
			
		||||
		// Attributes:
 | 
			
		||||
@@ -574,9 +705,10 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		switch t.ReflectKind {
 | 
			
		||||
		case reflect.Slice:
 | 
			
		||||
			slice := new(SliceType)
 | 
			
		||||
			typ = slice
 | 
			
		||||
			typeCache[off] = slice
 | 
			
		||||
			slice.ElemType = typeOf(e, AttrGoElem)
 | 
			
		||||
			t = &slice.StructType
 | 
			
		||||
			typ = slice
 | 
			
		||||
		case reflect.String:
 | 
			
		||||
			str := new(StringType)
 | 
			
		||||
			t = &str.StructType
 | 
			
		||||
@@ -586,26 +718,26 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		}
 | 
			
		||||
		typeCache[off] = typ
 | 
			
		||||
		switch e.Tag {
 | 
			
		||||
		case TagClassType:
 | 
			
		||||
		case dwarf.TagClassType:
 | 
			
		||||
			t.Kind = "class"
 | 
			
		||||
		case TagStructType:
 | 
			
		||||
		case dwarf.TagStructType:
 | 
			
		||||
			t.Kind = "struct"
 | 
			
		||||
		case TagUnionType:
 | 
			
		||||
		case dwarf.TagUnionType:
 | 
			
		||||
			t.Kind = "union"
 | 
			
		||||
		}
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.StructName, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Incomplete = e.Val(AttrDeclaration) != nil
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.StructName, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.Incomplete = e.Val(dwarf.AttrDeclaration) != nil
 | 
			
		||||
		t.Field = make([]*StructField, 0, 8)
 | 
			
		||||
		var lastFieldType Type
 | 
			
		||||
		var lastFieldBitOffset int64
 | 
			
		||||
		for kid := next(); kid != nil; kid = next() {
 | 
			
		||||
			if kid.Tag == TagMember {
 | 
			
		||||
			if kid.Tag == dwarf.TagMember {
 | 
			
		||||
				f := new(StructField)
 | 
			
		||||
				if f.Type = typeOf(kid, AttrType); err != nil {
 | 
			
		||||
				if f.Type = typeOf(kid, dwarf.AttrType); err != nil {
 | 
			
		||||
					goto Error
 | 
			
		||||
				}
 | 
			
		||||
				switch loc := kid.Val(AttrDataMemberLoc).(type) {
 | 
			
		||||
				switch loc := kid.Val(dwarf.AttrDataMemberLoc).(type) {
 | 
			
		||||
				case []byte:
 | 
			
		||||
					// TODO: Should have original compilation
 | 
			
		||||
					// unit here, not unknownFormat.
 | 
			
		||||
@@ -613,28 +745,28 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
						// Empty exprloc. f.ByteOffset=0.
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
					b := makeBuf(d, unknownFormat{}, "location", 0, loc)
 | 
			
		||||
					op := b.uint8()
 | 
			
		||||
					switch op {
 | 
			
		||||
					case opPlusUconst:
 | 
			
		||||
					b := util.MakeBuf(d, util.UnknownFormat{}, "location", 0, loc)
 | 
			
		||||
					op_ := op.Opcode(b.Uint8())
 | 
			
		||||
					switch op_ {
 | 
			
		||||
					case op.DW_OP_plus_uconst:
 | 
			
		||||
						// Handle opcode sequence [DW_OP_plus_uconst <uleb128>]
 | 
			
		||||
						f.ByteOffset = int64(b.uint())
 | 
			
		||||
						b.assertEmpty()
 | 
			
		||||
					case opConsts:
 | 
			
		||||
						f.ByteOffset = int64(b.Uint())
 | 
			
		||||
						b.AssertEmpty()
 | 
			
		||||
					case op.DW_OP_consts:
 | 
			
		||||
						// Handle opcode sequence [DW_OP_consts <sleb128> DW_OP_plus]
 | 
			
		||||
						f.ByteOffset = b.int()
 | 
			
		||||
						op = b.uint8()
 | 
			
		||||
						if op != opPlus {
 | 
			
		||||
							err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
 | 
			
		||||
						f.ByteOffset = b.Int()
 | 
			
		||||
						op_ = op.Opcode(b.Uint8())
 | 
			
		||||
						if op_ != op.DW_OP_plus {
 | 
			
		||||
							err = dwarf.DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op_)}
 | 
			
		||||
							goto Error
 | 
			
		||||
						}
 | 
			
		||||
						b.assertEmpty()
 | 
			
		||||
						b.AssertEmpty()
 | 
			
		||||
					default:
 | 
			
		||||
						err = DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op)}
 | 
			
		||||
						err = dwarf.DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op_)}
 | 
			
		||||
						goto Error
 | 
			
		||||
					}
 | 
			
		||||
					if b.err != nil {
 | 
			
		||||
						err = b.err
 | 
			
		||||
					if b.Err != nil {
 | 
			
		||||
						err = b.Err
 | 
			
		||||
						goto Error
 | 
			
		||||
					}
 | 
			
		||||
				case int64:
 | 
			
		||||
@@ -642,10 +774,11 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				haveBitOffset := false
 | 
			
		||||
				f.Name, _ = kid.Val(AttrName).(string)
 | 
			
		||||
				f.ByteSize, _ = kid.Val(AttrByteSize).(int64)
 | 
			
		||||
				f.BitOffset, haveBitOffset = kid.Val(AttrBitOffset).(int64)
 | 
			
		||||
				f.BitSize, _ = kid.Val(AttrBitSize).(int64)
 | 
			
		||||
				f.Name, _ = kid.Val(dwarf.AttrName).(string)
 | 
			
		||||
				f.ByteSize, _ = kid.Val(dwarf.AttrByteSize).(int64)
 | 
			
		||||
				f.BitOffset, haveBitOffset = kid.Val(dwarf.AttrBitOffset).(int64)
 | 
			
		||||
				f.BitSize, _ = kid.Val(dwarf.AttrBitSize).(int64)
 | 
			
		||||
				f.Embedded, _ = kid.Val(AttrGoEmbeddedField).(bool)
 | 
			
		||||
				t.Field = append(t.Field, f)
 | 
			
		||||
 | 
			
		||||
				bito := f.BitOffset
 | 
			
		||||
@@ -662,35 +795,35 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if t.Kind != "union" {
 | 
			
		||||
			b, ok := e.Val(AttrByteSize).(int64)
 | 
			
		||||
			b, ok := e.Val(dwarf.AttrByteSize).(int64)
 | 
			
		||||
			if ok && b*8 == lastFieldBitOffset {
 | 
			
		||||
				// Final field must be zero width.  Fix array length.
 | 
			
		||||
				zeroArray(lastFieldType)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case TagConstType, TagVolatileType, TagRestrictType:
 | 
			
		||||
	case dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType:
 | 
			
		||||
		// Type modifier (DWARF v2 §5.2)
 | 
			
		||||
		// Attributes:
 | 
			
		||||
		//	AttrType: subtype
 | 
			
		||||
		t := new(QualType)
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.ReflectKind = getKind(e)
 | 
			
		||||
		typ = t
 | 
			
		||||
		typeCache[off] = t
 | 
			
		||||
		if t.Type = typeOf(e, AttrType); err != nil {
 | 
			
		||||
		if t.Type = typeOf(e, dwarf.AttrType); err != nil {
 | 
			
		||||
			goto Error
 | 
			
		||||
		}
 | 
			
		||||
		switch e.Tag {
 | 
			
		||||
		case TagConstType:
 | 
			
		||||
		case dwarf.TagConstType:
 | 
			
		||||
			t.Qual = "const"
 | 
			
		||||
		case TagRestrictType:
 | 
			
		||||
		case dwarf.TagRestrictType:
 | 
			
		||||
			t.Qual = "restrict"
 | 
			
		||||
		case TagVolatileType:
 | 
			
		||||
		case dwarf.TagVolatileType:
 | 
			
		||||
			t.Qual = "volatile"
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case TagEnumerationType:
 | 
			
		||||
	case dwarf.TagEnumerationType:
 | 
			
		||||
		// Enumeration type (DWARF v2 §5.6)
 | 
			
		||||
		// Attributes:
 | 
			
		||||
		//	AttrName: enum name if any
 | 
			
		||||
@@ -703,14 +836,14 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		t.ReflectKind = getKind(e)
 | 
			
		||||
		typ = t
 | 
			
		||||
		typeCache[off] = t
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.EnumName, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.EnumName, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.Val = make([]*EnumValue, 0, 8)
 | 
			
		||||
		for kid := next(); kid != nil; kid = next() {
 | 
			
		||||
			if kid.Tag == TagEnumerator {
 | 
			
		||||
			if kid.Tag == dwarf.TagEnumerator {
 | 
			
		||||
				f := new(EnumValue)
 | 
			
		||||
				f.Name, _ = kid.Val(AttrName).(string)
 | 
			
		||||
				f.Val, _ = kid.Val(AttrConstValue).(int64)
 | 
			
		||||
				f.Name, _ = kid.Val(dwarf.AttrName).(string)
 | 
			
		||||
				f.Val, _ = kid.Val(dwarf.AttrConstValue).(int64)
 | 
			
		||||
				n := len(t.Val)
 | 
			
		||||
				if n >= cap(t.Val) {
 | 
			
		||||
					val := make([]*EnumValue, n, n*2)
 | 
			
		||||
@@ -722,23 +855,23 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case TagPointerType:
 | 
			
		||||
	case dwarf.TagPointerType:
 | 
			
		||||
		// Type modifier (DWARF v2 §5.2)
 | 
			
		||||
		// Attributes:
 | 
			
		||||
		//	AttrType: subtype [not required!  void* has no AttrType]
 | 
			
		||||
		//	AttrAddrClass: address class [ignored]
 | 
			
		||||
		t := new(PtrType)
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.ReflectKind = getKind(e)
 | 
			
		||||
		typ = t
 | 
			
		||||
		typeCache[off] = t
 | 
			
		||||
		if e.Val(AttrType) == nil {
 | 
			
		||||
		if e.Val(dwarf.AttrType) == nil {
 | 
			
		||||
			t.Type = &VoidType{}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		t.Type = typeOf(e, AttrType)
 | 
			
		||||
		t.Type = typeOf(e, dwarf.AttrType)
 | 
			
		||||
 | 
			
		||||
	case TagSubroutineType:
 | 
			
		||||
	case dwarf.TagSubroutineType:
 | 
			
		||||
		// Subroutine type.  (DWARF v2 §5.7)
 | 
			
		||||
		// Attributes:
 | 
			
		||||
		//	AttrType: type of return value if any
 | 
			
		||||
@@ -749,11 +882,11 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		//		AttrType: type of parameter
 | 
			
		||||
		//	TagUnspecifiedParameter: final ...
 | 
			
		||||
		t := new(FuncType)
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.ReflectKind = getKind(e)
 | 
			
		||||
		typ = t
 | 
			
		||||
		typeCache[off] = t
 | 
			
		||||
		if t.ReturnType = typeOf(e, AttrType); err != nil {
 | 
			
		||||
		if t.ReturnType = typeOf(e, dwarf.AttrType); err != nil {
 | 
			
		||||
			goto Error
 | 
			
		||||
		}
 | 
			
		||||
		t.ParamType = make([]Type, 0, 8)
 | 
			
		||||
@@ -762,17 +895,17 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
			switch kid.Tag {
 | 
			
		||||
			default:
 | 
			
		||||
				continue
 | 
			
		||||
			case TagFormalParameter:
 | 
			
		||||
				if tkid = typeOf(kid, AttrType); err != nil {
 | 
			
		||||
			case dwarf.TagFormalParameter:
 | 
			
		||||
				if tkid = typeOf(kid, dwarf.AttrType); err != nil {
 | 
			
		||||
					goto Error
 | 
			
		||||
				}
 | 
			
		||||
			case TagUnspecifiedParameters:
 | 
			
		||||
			case dwarf.TagUnspecifiedParameters:
 | 
			
		||||
				tkid = &DotDotDotType{}
 | 
			
		||||
			}
 | 
			
		||||
			t.ParamType = append(t.ParamType, tkid)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case TagTypedef:
 | 
			
		||||
	case dwarf.TagTypedef:
 | 
			
		||||
		// Typedef (DWARF v2 §5.3)
 | 
			
		||||
		// Also maps and channels (Go-specific).
 | 
			
		||||
		// Attributes:
 | 
			
		||||
@@ -785,34 +918,37 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
		switch t.ReflectKind {
 | 
			
		||||
		case reflect.Map:
 | 
			
		||||
			m := new(MapType)
 | 
			
		||||
			typ = m
 | 
			
		||||
			typeCache[off] = typ
 | 
			
		||||
			m.KeyType = typeOf(e, AttrGoKey)
 | 
			
		||||
			m.ElemType = typeOf(e, AttrGoElem)
 | 
			
		||||
			t = &m.TypedefType
 | 
			
		||||
			typ = m
 | 
			
		||||
		case reflect.Chan:
 | 
			
		||||
			c := new(ChanType)
 | 
			
		||||
			typ = c
 | 
			
		||||
			typeCache[off] = typ
 | 
			
		||||
			c.ElemType = typeOf(e, AttrGoElem)
 | 
			
		||||
			t = &c.TypedefType
 | 
			
		||||
			typ = c
 | 
			
		||||
		case reflect.Interface:
 | 
			
		||||
			it := new(InterfaceType)
 | 
			
		||||
			t = &it.TypedefType
 | 
			
		||||
			typ = it
 | 
			
		||||
			typeCache[off] = it
 | 
			
		||||
			t = &it.TypedefType
 | 
			
		||||
		default:
 | 
			
		||||
			typ = t
 | 
			
		||||
		}
 | 
			
		||||
		typeCache[off] = typ
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Type = typeOf(e, AttrType)
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
		t.Type = typeOf(e, dwarf.AttrType)
 | 
			
		||||
 | 
			
		||||
	case TagUnspecifiedType:
 | 
			
		||||
	case dwarf.TagUnspecifiedType:
 | 
			
		||||
		// Unspecified type (DWARF v3 §5.2)
 | 
			
		||||
		// Attributes:
 | 
			
		||||
		//      AttrName: name
 | 
			
		||||
		t := new(UnspecifiedType)
 | 
			
		||||
		typ = t
 | 
			
		||||
		typeCache[off] = t
 | 
			
		||||
		t.Name, _ = e.Val(AttrName).(string)
 | 
			
		||||
		t.Name, _ = e.Val(dwarf.AttrName).(string)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -822,7 +958,7 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
	typ.Common().Offset = off
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		b, ok := e.Val(AttrByteSize).(int64)
 | 
			
		||||
		b, ok := e.Val(dwarf.AttrByteSize).(int64)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			b = -1
 | 
			
		||||
			switch t := typ.(type) {
 | 
			
		||||
@@ -836,6 +972,9 @@ func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Off
 | 
			
		||||
				b = t.Type.Size()
 | 
			
		||||
			case *PtrType:
 | 
			
		||||
				b = int64(addressSize)
 | 
			
		||||
			case *FuncType:
 | 
			
		||||
				// on Go < 1.10 function types do not have a DW_AT_byte_size attribute.
 | 
			
		||||
				b = int64(addressSize)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		typ.Common().ByteSize = b
 | 
			
		||||
@@ -860,3 +999,13 @@ func zeroArray(t Type) {
 | 
			
		||||
		t = at.Type
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resolveTypedef(typ Type) Type {
 | 
			
		||||
	for {
 | 
			
		||||
		if tt, ok := typ.(*TypedefType); ok {
 | 
			
		||||
			typ = tt.Type
 | 
			
		||||
		} else {
 | 
			
		||||
			return typ
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										152
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/line/line_parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/line/line_parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
			
		||||
package line
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DebugLinePrologue struct {
 | 
			
		||||
	UnitLength     uint32
 | 
			
		||||
	Version        uint16
 | 
			
		||||
	Length         uint32
 | 
			
		||||
	MinInstrLength uint8
 | 
			
		||||
	InitialIsStmt  uint8
 | 
			
		||||
	LineBase       int8
 | 
			
		||||
	LineRange      uint8
 | 
			
		||||
	OpcodeBase     uint8
 | 
			
		||||
	StdOpLengths   []uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DebugLineInfo struct {
 | 
			
		||||
	Prologue     *DebugLinePrologue
 | 
			
		||||
	IncludeDirs  []string
 | 
			
		||||
	FileNames    []*FileEntry
 | 
			
		||||
	Instructions []byte
 | 
			
		||||
	Lookup       map[string]*FileEntry
 | 
			
		||||
 | 
			
		||||
	Logf func(string, ...interface{})
 | 
			
		||||
 | 
			
		||||
	// stateMachineCache[pc] is a state machine stopped at pc
 | 
			
		||||
	stateMachineCache map[uint64]*StateMachine
 | 
			
		||||
 | 
			
		||||
	// lastMachineCache[pc] is a state machine stopped at an address after pc
 | 
			
		||||
	lastMachineCache map[uint64]*StateMachine
 | 
			
		||||
	
 | 
			
		||||
	// staticBase is the address at which the executable is loaded, 0 for non-PIEs
 | 
			
		||||
	staticBase uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type FileEntry struct {
 | 
			
		||||
	Path        string
 | 
			
		||||
	DirIdx      uint64
 | 
			
		||||
	LastModTime uint64
 | 
			
		||||
	Length      uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DebugLines []*DebugLineInfo
 | 
			
		||||
 | 
			
		||||
// ParseAll parses all debug_line segments found in data
 | 
			
		||||
func ParseAll(data []byte, logfn func(string, ...interface{}), staticBase uint64) DebugLines {
 | 
			
		||||
	var (
 | 
			
		||||
		lines = make(DebugLines, 0)
 | 
			
		||||
		buf   = bytes.NewBuffer(data)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// We have to parse multiple file name tables here.
 | 
			
		||||
	for buf.Len() > 0 {
 | 
			
		||||
		lines = append(lines, Parse("", buf, logfn, staticBase))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return lines
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parse parses a single debug_line segment from buf. Compdir is the
 | 
			
		||||
// DW_AT_comp_dir attribute of the associated compile unit.
 | 
			
		||||
func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{}), staticBase uint64) *DebugLineInfo {
 | 
			
		||||
	dbl := new(DebugLineInfo)
 | 
			
		||||
	dbl.Logf = logfn
 | 
			
		||||
	dbl.staticBase = staticBase
 | 
			
		||||
	dbl.Lookup = make(map[string]*FileEntry)
 | 
			
		||||
	if compdir != "" {
 | 
			
		||||
		dbl.IncludeDirs = append(dbl.IncludeDirs, compdir)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbl.stateMachineCache = make(map[uint64]*StateMachine)
 | 
			
		||||
	dbl.lastMachineCache = make(map[uint64]*StateMachine)
 | 
			
		||||
 | 
			
		||||
	parseDebugLinePrologue(dbl, buf)
 | 
			
		||||
	parseIncludeDirs(dbl, buf)
 | 
			
		||||
	parseFileEntries(dbl, buf)
 | 
			
		||||
 | 
			
		||||
	// Instructions size calculation breakdown:
 | 
			
		||||
	//   - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
 | 
			
		||||
	//   - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
 | 
			
		||||
	//   - So you have UnitLength - PrologueLength - (version_length_bytes(2) + prologue_length_bytes(4)).
 | 
			
		||||
	dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6))
 | 
			
		||||
 | 
			
		||||
	return dbl
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
 | 
			
		||||
	p := new(DebugLinePrologue)
 | 
			
		||||
 | 
			
		||||
	p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4))
 | 
			
		||||
	p.Version = binary.LittleEndian.Uint16(buf.Next(2))
 | 
			
		||||
	p.Length = binary.LittleEndian.Uint32(buf.Next(4))
 | 
			
		||||
	p.MinInstrLength = uint8(buf.Next(1)[0])
 | 
			
		||||
	p.InitialIsStmt = uint8(buf.Next(1)[0])
 | 
			
		||||
	p.LineBase = int8(buf.Next(1)[0])
 | 
			
		||||
	p.LineRange = uint8(buf.Next(1)[0])
 | 
			
		||||
	p.OpcodeBase = uint8(buf.Next(1)[0])
 | 
			
		||||
 | 
			
		||||
	p.StdOpLengths = make([]uint8, p.OpcodeBase-1)
 | 
			
		||||
	binary.Read(buf, binary.LittleEndian, &p.StdOpLengths)
 | 
			
		||||
 | 
			
		||||
	dbl.Prologue = p
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
 | 
			
		||||
	for {
 | 
			
		||||
		str, _ := util.ParseString(buf)
 | 
			
		||||
		if str == "" {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		info.IncludeDirs = append(info.IncludeDirs, str)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
 | 
			
		||||
	for {
 | 
			
		||||
		entry := readFileEntry(info, buf, true)
 | 
			
		||||
		if entry.Path == "" {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		info.FileNames = append(info.FileNames, entry)
 | 
			
		||||
		info.Lookup[entry.Path] = entry
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool) *FileEntry {
 | 
			
		||||
	entry := new(FileEntry)
 | 
			
		||||
 | 
			
		||||
	entry.Path, _ = util.ParseString(buf)
 | 
			
		||||
	if entry.Path == "" && exitOnEmptyPath {
 | 
			
		||||
		return entry
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	entry.DirIdx, _ = util.DecodeULEB128(buf)
 | 
			
		||||
	entry.LastModTime, _ = util.DecodeULEB128(buf)
 | 
			
		||||
	entry.Length, _ = util.DecodeULEB128(buf)
 | 
			
		||||
	if !filepath.IsAbs(entry.Path) {
 | 
			
		||||
		if entry.DirIdx >= 0 && entry.DirIdx < uint64(len(info.IncludeDirs)) {
 | 
			
		||||
			entry.Path = filepath.Join(info.IncludeDirs[entry.DirIdx], entry.Path)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return entry
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										450
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/line/state_machine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										450
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/line/state_machine.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,450 @@
 | 
			
		||||
package line
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Location struct {
 | 
			
		||||
	File    string
 | 
			
		||||
	Line    int
 | 
			
		||||
	Address uint64
 | 
			
		||||
	Delta   int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type StateMachine struct {
 | 
			
		||||
	dbl           *DebugLineInfo
 | 
			
		||||
	file          string
 | 
			
		||||
	line          int
 | 
			
		||||
	address       uint64
 | 
			
		||||
	column        uint
 | 
			
		||||
	isStmt        bool
 | 
			
		||||
	basicBlock    bool
 | 
			
		||||
	endSeq        bool
 | 
			
		||||
	lastDelta     int
 | 
			
		||||
	prologueEnd   bool
 | 
			
		||||
	epilogueBegin bool
 | 
			
		||||
	// valid is true if the current value of the state machine is the address of
 | 
			
		||||
	// an instruction (using the terminology used by DWARF spec the current
 | 
			
		||||
	// value of the state machine should be appended to the matrix representing
 | 
			
		||||
	// the compilation unit)
 | 
			
		||||
	valid bool
 | 
			
		||||
 | 
			
		||||
	started bool
 | 
			
		||||
 | 
			
		||||
	buf     *bytes.Buffer // remaining instructions
 | 
			
		||||
	opcodes []opcodefn
 | 
			
		||||
 | 
			
		||||
	definedFiles []*FileEntry // files defined with DW_LINE_define_file
 | 
			
		||||
 | 
			
		||||
	lastAddress uint64
 | 
			
		||||
	lastFile    string
 | 
			
		||||
	lastLine    int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type opcodeKind uint8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	specialOpcode opcodeKind = iota
 | 
			
		||||
	standardOpcode
 | 
			
		||||
	extendedOpcode
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type opcodefn func(*StateMachine, *bytes.Buffer)
 | 
			
		||||
 | 
			
		||||
// Special opcodes
 | 
			
		||||
const (
 | 
			
		||||
	DW_LNS_copy             = 1
 | 
			
		||||
	DW_LNS_advance_pc       = 2
 | 
			
		||||
	DW_LNS_advance_line     = 3
 | 
			
		||||
	DW_LNS_set_file         = 4
 | 
			
		||||
	DW_LNS_set_column       = 5
 | 
			
		||||
	DW_LNS_negate_stmt      = 6
 | 
			
		||||
	DW_LNS_set_basic_block  = 7
 | 
			
		||||
	DW_LNS_const_add_pc     = 8
 | 
			
		||||
	DW_LNS_fixed_advance_pc = 9
 | 
			
		||||
	DW_LNS_prologue_end     = 10
 | 
			
		||||
	DW_LNS_epilogue_begin   = 11
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Extended opcodes
 | 
			
		||||
const (
 | 
			
		||||
	DW_LINE_end_sequence = 1
 | 
			
		||||
	DW_LINE_set_address  = 2
 | 
			
		||||
	DW_LINE_define_file  = 3
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var standardopcodes = map[byte]opcodefn{
 | 
			
		||||
	DW_LNS_copy:             copyfn,
 | 
			
		||||
	DW_LNS_advance_pc:       advancepc,
 | 
			
		||||
	DW_LNS_advance_line:     advanceline,
 | 
			
		||||
	DW_LNS_set_file:         setfile,
 | 
			
		||||
	DW_LNS_set_column:       setcolumn,
 | 
			
		||||
	DW_LNS_negate_stmt:      negatestmt,
 | 
			
		||||
	DW_LNS_set_basic_block:  setbasicblock,
 | 
			
		||||
	DW_LNS_const_add_pc:     constaddpc,
 | 
			
		||||
	DW_LNS_fixed_advance_pc: fixedadvancepc,
 | 
			
		||||
	DW_LNS_prologue_end:     prologueend,
 | 
			
		||||
	DW_LNS_epilogue_begin:   epiloguebegin,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var extendedopcodes = map[byte]opcodefn{
 | 
			
		||||
	DW_LINE_end_sequence: endsequence,
 | 
			
		||||
	DW_LINE_set_address:  setaddress,
 | 
			
		||||
	DW_LINE_define_file:  definefile,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newStateMachine(dbl *DebugLineInfo, instructions []byte) *StateMachine {
 | 
			
		||||
	opcodes := make([]opcodefn, len(standardopcodes)+1)
 | 
			
		||||
	opcodes[0] = execExtendedOpcode
 | 
			
		||||
	for op := range standardopcodes {
 | 
			
		||||
		opcodes[op] = standardopcodes[op]
 | 
			
		||||
	}
 | 
			
		||||
	sm := &StateMachine{dbl: dbl, file: dbl.FileNames[0].Path, line: 1, buf: bytes.NewBuffer(instructions), opcodes: opcodes, isStmt: dbl.Prologue.InitialIsStmt == uint8(1), address: dbl.staticBase}
 | 
			
		||||
	return sm
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns all PCs for a given file/line. Useful for loops where the 'for' line
 | 
			
		||||
// could be split amongst 2 PCs.
 | 
			
		||||
func (lineInfo *DebugLineInfo) AllPCsForFileLine(f string, l int) (pcs []uint64) {
 | 
			
		||||
	if lineInfo == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		lastAddr uint64
 | 
			
		||||
		sm       = newStateMachine(lineInfo, lineInfo.Instructions)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if err := sm.next(); err != nil {
 | 
			
		||||
			if lineInfo.Logf != nil {
 | 
			
		||||
				lineInfo.Logf("AllPCsForFileLine error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if sm.line == l && sm.file == f && sm.address != lastAddr && sm.isStmt && sm.valid {
 | 
			
		||||
			pcs = append(pcs, sm.address)
 | 
			
		||||
			lastAddr = sm.address
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var NoSourceError = errors.New("no source available")
 | 
			
		||||
 | 
			
		||||
// AllPCsBetween returns all PC addresses between begin and end (including both begin and end) that have the is_stmt flag set and do not belong to excludeFile:excludeLine
 | 
			
		||||
func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64, excludeFile string, excludeLine int) ([]uint64, error) {
 | 
			
		||||
	if lineInfo == nil {
 | 
			
		||||
		return nil, NoSourceError
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		pcs      []uint64
 | 
			
		||||
		lastaddr uint64
 | 
			
		||||
		sm       = newStateMachine(lineInfo, lineInfo.Instructions)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if err := sm.next(); err != nil {
 | 
			
		||||
			if lineInfo.Logf != nil {
 | 
			
		||||
				lineInfo.Logf("AllPCsBetween error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if !sm.valid {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if sm.address > end {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if (sm.address >= begin && sm.address > lastaddr) && sm.isStmt && ((sm.file != excludeFile) || (sm.line != excludeLine)) {
 | 
			
		||||
			lastaddr = sm.address
 | 
			
		||||
			pcs = append(pcs, sm.address)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return pcs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// copy returns a copy of this state machine, running the returned state
 | 
			
		||||
// machine will not affect sm.
 | 
			
		||||
func (sm *StateMachine) copy() *StateMachine {
 | 
			
		||||
	var r StateMachine
 | 
			
		||||
	r = *sm
 | 
			
		||||
	r.buf = bytes.NewBuffer(sm.buf.Bytes())
 | 
			
		||||
	return &r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (lineInfo *DebugLineInfo) stateMachineForEntry(basePC uint64) (sm *StateMachine) {
 | 
			
		||||
	sm = lineInfo.stateMachineCache[basePC]
 | 
			
		||||
	if sm == nil {
 | 
			
		||||
		sm = newStateMachine(lineInfo, lineInfo.Instructions)
 | 
			
		||||
		sm.PCToLine(basePC)
 | 
			
		||||
		lineInfo.stateMachineCache[basePC] = sm
 | 
			
		||||
	}
 | 
			
		||||
	sm = sm.copy()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PCToLine returns the filename and line number associated with pc.
 | 
			
		||||
// If pc isn't found inside lineInfo's table it will return the filename and
 | 
			
		||||
// line number associated with the closest PC address preceding pc.
 | 
			
		||||
// basePC will be used for caching, it's normally the entry point for the
 | 
			
		||||
// function containing pc.
 | 
			
		||||
func (lineInfo *DebugLineInfo) PCToLine(basePC, pc uint64) (string, int) {
 | 
			
		||||
	if lineInfo == nil {
 | 
			
		||||
		return "", 0
 | 
			
		||||
	}
 | 
			
		||||
	if basePC > pc {
 | 
			
		||||
		panic(fmt.Errorf("basePC after pc %#x %#x", basePC, pc))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var sm *StateMachine
 | 
			
		||||
	if basePC == 0 {
 | 
			
		||||
		sm = newStateMachine(lineInfo, lineInfo.Instructions)
 | 
			
		||||
	} else {
 | 
			
		||||
		// Try to use the last state machine that we used for this function, if
 | 
			
		||||
		// there isn't one or it's already past pc try to clone the cached state
 | 
			
		||||
		// machine stopped at the entry point of the function.
 | 
			
		||||
		// As a last resort start from the start of the debug_line section.
 | 
			
		||||
		sm = lineInfo.lastMachineCache[basePC]
 | 
			
		||||
		if sm == nil || sm.lastAddress > pc {
 | 
			
		||||
			sm = lineInfo.stateMachineForEntry(basePC)
 | 
			
		||||
			lineInfo.lastMachineCache[basePC] = sm
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	file, line, _ := sm.PCToLine(pc)
 | 
			
		||||
	return file, line
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sm *StateMachine) PCToLine(pc uint64) (string, int, bool) {
 | 
			
		||||
	if !sm.started {
 | 
			
		||||
		if err := sm.next(); err != nil {
 | 
			
		||||
			if sm.dbl.Logf != nil {
 | 
			
		||||
				sm.dbl.Logf("PCToLine error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			return "", 0, false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if sm.lastAddress > pc {
 | 
			
		||||
		return "", 0, false
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		if sm.valid {
 | 
			
		||||
			if sm.address > pc {
 | 
			
		||||
				return sm.lastFile, sm.lastLine, true
 | 
			
		||||
			}
 | 
			
		||||
			if sm.address == pc {
 | 
			
		||||
				return sm.file, sm.line, true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err := sm.next(); err != nil {
 | 
			
		||||
			if sm.dbl.Logf != nil {
 | 
			
		||||
				sm.dbl.Logf("PCToLine error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if sm.valid {
 | 
			
		||||
		return sm.file, sm.line, true
 | 
			
		||||
	}
 | 
			
		||||
	return "", 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LineToPC returns the first PC address associated with filename:lineno.
 | 
			
		||||
func (lineInfo *DebugLineInfo) LineToPC(filename string, lineno int) uint64 {
 | 
			
		||||
	if lineInfo == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sm := newStateMachine(lineInfo, lineInfo.Instructions)
 | 
			
		||||
 | 
			
		||||
	// if no instruction marked is_stmt is found fallback to the first
 | 
			
		||||
	// instruction assigned to the filename:line.
 | 
			
		||||
	var fallbackPC uint64
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if err := sm.next(); err != nil {
 | 
			
		||||
			if lineInfo.Logf != nil && err != io.EOF {
 | 
			
		||||
				lineInfo.Logf("LineToPC error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if sm.line == lineno && sm.file == filename && sm.valid {
 | 
			
		||||
			if sm.isStmt {
 | 
			
		||||
				return sm.address
 | 
			
		||||
			} else if fallbackPC == 0 {
 | 
			
		||||
				fallbackPC = sm.address
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fallbackPC
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrologueEndPC returns the first PC address marked as prologue_end in the half open interval [start, end)
 | 
			
		||||
func (lineInfo *DebugLineInfo) PrologueEndPC(start, end uint64) (pc uint64, file string, line int, ok bool) {
 | 
			
		||||
	sm := lineInfo.stateMachineForEntry(start)
 | 
			
		||||
	for {
 | 
			
		||||
		if sm.valid {
 | 
			
		||||
			if sm.address >= end {
 | 
			
		||||
				return 0, "", 0, false
 | 
			
		||||
			}
 | 
			
		||||
			if sm.prologueEnd {
 | 
			
		||||
				return sm.address, sm.file, sm.line, true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if err := sm.next(); err != nil {
 | 
			
		||||
			if lineInfo.Logf != nil {
 | 
			
		||||
				lineInfo.Logf("PrologueEnd error: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			return 0, "", 0, false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (sm *StateMachine) next() error {
 | 
			
		||||
	sm.started = true
 | 
			
		||||
	if sm.valid {
 | 
			
		||||
		sm.lastAddress, sm.lastFile, sm.lastLine = sm.address, sm.file, sm.line
 | 
			
		||||
 | 
			
		||||
		// valid is set by either a special opcode or a DW_LNS_copy, in both cases
 | 
			
		||||
		// we need to reset basic_block, prologue_end and epilogue_begin
 | 
			
		||||
		sm.basicBlock = false
 | 
			
		||||
		sm.prologueEnd = false
 | 
			
		||||
		sm.epilogueBegin = false
 | 
			
		||||
	}
 | 
			
		||||
	if sm.endSeq {
 | 
			
		||||
		sm.endSeq = false
 | 
			
		||||
		sm.file = sm.dbl.FileNames[0].Path
 | 
			
		||||
		sm.line = 1
 | 
			
		||||
		sm.column = 0
 | 
			
		||||
		sm.isStmt = sm.dbl.Prologue.InitialIsStmt == uint8(1)
 | 
			
		||||
		sm.basicBlock = false
 | 
			
		||||
	}
 | 
			
		||||
	b, err := sm.buf.ReadByte()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if b < sm.dbl.Prologue.OpcodeBase {
 | 
			
		||||
		if int(b) < len(sm.opcodes) {
 | 
			
		||||
			sm.valid = false
 | 
			
		||||
			sm.opcodes[b](sm, sm.buf)
 | 
			
		||||
		} else {
 | 
			
		||||
			// unimplemented standard opcode, read the number of arguments specified
 | 
			
		||||
			// in the prologue and do nothing with them
 | 
			
		||||
			opnum := sm.dbl.Prologue.StdOpLengths[b-1]
 | 
			
		||||
			for i := 0; i < int(opnum); i++ {
 | 
			
		||||
				util.DecodeSLEB128(sm.buf)
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Printf("unknown opcode\n")
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		execSpecialOpcode(sm, b)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func execSpecialOpcode(sm *StateMachine, instr byte) {
 | 
			
		||||
	var (
 | 
			
		||||
		opcode  = uint8(instr)
 | 
			
		||||
		decoded = opcode - sm.dbl.Prologue.OpcodeBase
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
 | 
			
		||||
	sm.line += sm.lastDelta
 | 
			
		||||
	sm.address += uint64(decoded/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
 | 
			
		||||
	sm.valid = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func execExtendedOpcode(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	_, _ = util.DecodeULEB128(buf)
 | 
			
		||||
	b, _ := buf.ReadByte()
 | 
			
		||||
	if fn, ok := extendedopcodes[b]; ok {
 | 
			
		||||
		fn(sm, buf)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.valid = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	addr, _ := util.DecodeULEB128(buf)
 | 
			
		||||
	sm.address += addr * uint64(sm.dbl.Prologue.MinInstrLength)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	line, _ := util.DecodeSLEB128(buf)
 | 
			
		||||
	sm.line += int(line)
 | 
			
		||||
	sm.lastDelta = int(line)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setfile(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	i, _ := util.DecodeULEB128(buf)
 | 
			
		||||
	if i-1 < uint64(len(sm.dbl.FileNames)) {
 | 
			
		||||
		sm.file = sm.dbl.FileNames[i-1].Path
 | 
			
		||||
	} else {
 | 
			
		||||
		j := (i - 1) - uint64(len(sm.dbl.FileNames))
 | 
			
		||||
		if j < uint64(len(sm.definedFiles)) {
 | 
			
		||||
			sm.file = sm.definedFiles[j].Path
 | 
			
		||||
		} else {
 | 
			
		||||
			sm.file = ""
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	c, _ := util.DecodeULEB128(buf)
 | 
			
		||||
	sm.column = uint(c)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.isStmt = !sm.isStmt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.basicBlock = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.address += uint64((255-sm.dbl.Prologue.OpcodeBase)/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	var operand uint16
 | 
			
		||||
	binary.Read(buf, binary.LittleEndian, &operand)
 | 
			
		||||
 | 
			
		||||
	sm.address += uint64(operand)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.endSeq = true
 | 
			
		||||
	sm.valid = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	var addr uint64
 | 
			
		||||
 | 
			
		||||
	binary.Read(buf, binary.LittleEndian, &addr)
 | 
			
		||||
 | 
			
		||||
	sm.address = addr + sm.dbl.staticBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func definefile(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	entry := readFileEntry(sm.dbl, sm.buf, false)
 | 
			
		||||
	sm.definedFiles = append(sm.definedFiles, entry)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func prologueend(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.prologueEnd = true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func epiloguebegin(sm *StateMachine, buf *bytes.Buffer) {
 | 
			
		||||
	sm.epilogueBegin = true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										197
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/op.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/op.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
			
		||||
package op
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Opcode byte
 | 
			
		||||
 | 
			
		||||
//go:generate go run ../../../scripts/gen-opcodes.go opcodes.table opcodes.go
 | 
			
		||||
 | 
			
		||||
type stackfn func(Opcode, *context) error
 | 
			
		||||
 | 
			
		||||
type context struct {
 | 
			
		||||
	buf    *bytes.Buffer
 | 
			
		||||
	stack  []int64
 | 
			
		||||
	pieces []Piece
 | 
			
		||||
	reg    bool
 | 
			
		||||
 | 
			
		||||
	DwarfRegisters
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Piece is a piece of memory stored either at an address or in a register.
 | 
			
		||||
type Piece struct {
 | 
			
		||||
	Size       int
 | 
			
		||||
	Addr       int64
 | 
			
		||||
	RegNum     uint64
 | 
			
		||||
	IsRegister bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExecuteStackProgram executes a DWARF location expression and returns
 | 
			
		||||
// either an address (int64), or a slice of Pieces for location expressions
 | 
			
		||||
// that don't evaluate to an address (such as register and composite expressions).
 | 
			
		||||
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, []Piece, error) {
 | 
			
		||||
	ctxt := &context{
 | 
			
		||||
		buf:            bytes.NewBuffer(instructions),
 | 
			
		||||
		stack:          make([]int64, 0, 3),
 | 
			
		||||
		DwarfRegisters: regs,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		opcodeByte, err := ctxt.buf.ReadByte()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		opcode := Opcode(opcodeByte)
 | 
			
		||||
		if ctxt.reg && opcode != DW_OP_piece {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		fn, ok := oplut[opcode]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return 0, nil, fmt.Errorf("invalid instruction %#v", opcode)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = fn(opcode, ctxt)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ctxt.pieces != nil {
 | 
			
		||||
		return 0, ctxt.pieces, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ctxt.stack) == 0 {
 | 
			
		||||
		return 0, nil, errors.New("empty OP stack")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ctxt.stack[len(ctxt.stack)-1], nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PrettyPrint prints instructions to out.
 | 
			
		||||
func PrettyPrint(out io.Writer, instructions []byte) {
 | 
			
		||||
	in := bytes.NewBuffer(instructions)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		opcode, err := in.ReadByte()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if name, hasname := opcodeName[Opcode(opcode)]; hasname {
 | 
			
		||||
			io.WriteString(out, name)
 | 
			
		||||
			out.Write([]byte{' '})
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Fprintf(out, "%#x ", opcode)
 | 
			
		||||
		}
 | 
			
		||||
		for _, arg := range opcodeArgs[Opcode(opcode)] {
 | 
			
		||||
			switch arg {
 | 
			
		||||
			case 's':
 | 
			
		||||
				n, _ := util.DecodeSLEB128(in)
 | 
			
		||||
				fmt.Fprintf(out, "%#x ", n)
 | 
			
		||||
			case 'u':
 | 
			
		||||
				n, _ := util.DecodeULEB128(in)
 | 
			
		||||
				fmt.Fprintf(out, "%#x ", n)
 | 
			
		||||
			case '1':
 | 
			
		||||
				var x uint8
 | 
			
		||||
				binary.Read(in, binary.LittleEndian, &x)
 | 
			
		||||
				fmt.Fprintf(out, "%#x ", x)
 | 
			
		||||
			case '2':
 | 
			
		||||
				var x uint16
 | 
			
		||||
				binary.Read(in, binary.LittleEndian, &x)
 | 
			
		||||
				fmt.Fprintf(out, "%#x ", x)
 | 
			
		||||
			case '4':
 | 
			
		||||
				var x uint32
 | 
			
		||||
				binary.Read(in, binary.LittleEndian, &x)
 | 
			
		||||
				fmt.Fprintf(out, "%#x ", x)
 | 
			
		||||
			case '8':
 | 
			
		||||
				var x uint64
 | 
			
		||||
				binary.Read(in, binary.LittleEndian, &x)
 | 
			
		||||
				fmt.Fprintf(out, "%#x ", x)
 | 
			
		||||
			case 'B':
 | 
			
		||||
				sz, _ := util.DecodeULEB128(in)
 | 
			
		||||
				data := make([]byte, sz)
 | 
			
		||||
				sz2, _ := in.Read(data)
 | 
			
		||||
				data = data[:sz2]
 | 
			
		||||
				fmt.Fprintf(out, "%d [%x] ", sz, data)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func callframecfa(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	if ctxt.CFA == 0 {
 | 
			
		||||
		return fmt.Errorf("Could not retrieve CFA for current PC")
 | 
			
		||||
	}
 | 
			
		||||
	ctxt.stack = append(ctxt.stack, int64(ctxt.CFA))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func addr(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	ctxt.stack = append(ctxt.stack, int64(binary.LittleEndian.Uint64(ctxt.buf.Next(8))+ctxt.StaticBase))
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func plus(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	var (
 | 
			
		||||
		slen   = len(ctxt.stack)
 | 
			
		||||
		digits = ctxt.stack[slen-2 : slen]
 | 
			
		||||
		st     = ctxt.stack[:slen-2]
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	ctxt.stack = append(st, digits[0]+digits[1])
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func plusuconsts(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	slen := len(ctxt.stack)
 | 
			
		||||
	num, _ := util.DecodeULEB128(ctxt.buf)
 | 
			
		||||
	ctxt.stack[slen-1] = ctxt.stack[slen-1] + int64(num)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func consts(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	num, _ := util.DecodeSLEB128(ctxt.buf)
 | 
			
		||||
	ctxt.stack = append(ctxt.stack, num)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func framebase(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	num, _ := util.DecodeSLEB128(ctxt.buf)
 | 
			
		||||
	ctxt.stack = append(ctxt.stack, ctxt.FrameBase+num)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func register(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	ctxt.reg = true
 | 
			
		||||
	if opcode == DW_OP_regx {
 | 
			
		||||
		n, _ := util.DecodeSLEB128(ctxt.buf)
 | 
			
		||||
		ctxt.pieces = append(ctxt.pieces, Piece{IsRegister: true, RegNum: uint64(n)})
 | 
			
		||||
	} else {
 | 
			
		||||
		ctxt.pieces = append(ctxt.pieces, Piece{IsRegister: true, RegNum: uint64(opcode - DW_OP_reg0)})
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func piece(opcode Opcode, ctxt *context) error {
 | 
			
		||||
	sz, _ := util.DecodeULEB128(ctxt.buf)
 | 
			
		||||
	if ctxt.reg {
 | 
			
		||||
		ctxt.reg = false
 | 
			
		||||
		ctxt.pieces[len(ctxt.pieces)-1].Size = int(sz)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(ctxt.stack) == 0 {
 | 
			
		||||
		return errors.New("empty OP stack")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	addr := ctxt.stack[len(ctxt.stack)-1]
 | 
			
		||||
	ctxt.pieces = append(ctxt.pieces, Piece{Size: int(sz), Addr: addr})
 | 
			
		||||
	ctxt.stack = ctxt.stack[:0]
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										515
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										515
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/opcodes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,515 @@
 | 
			
		||||
// THIS FILE IS AUTOGENERATED, EDIT opcodes.table INSTEAD
 | 
			
		||||
 | 
			
		||||
package op
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	DW_OP_addr                Opcode = 0x03
 | 
			
		||||
	DW_OP_deref               Opcode = 0x06
 | 
			
		||||
	DW_OP_const1u             Opcode = 0x08
 | 
			
		||||
	DW_OP_const1s             Opcode = 0x09
 | 
			
		||||
	DW_OP_const2u             Opcode = 0x0a
 | 
			
		||||
	DW_OP_const2s             Opcode = 0x0b
 | 
			
		||||
	DW_OP_const4u             Opcode = 0x0c
 | 
			
		||||
	DW_OP_const4s             Opcode = 0x0d
 | 
			
		||||
	DW_OP_const8u             Opcode = 0x0e
 | 
			
		||||
	DW_OP_const8s             Opcode = 0x0f
 | 
			
		||||
	DW_OP_constu              Opcode = 0x10
 | 
			
		||||
	DW_OP_consts              Opcode = 0x11
 | 
			
		||||
	DW_OP_dup                 Opcode = 0x12
 | 
			
		||||
	DW_OP_drop                Opcode = 0x13
 | 
			
		||||
	DW_OP_over                Opcode = 0x14
 | 
			
		||||
	DW_OP_pick                Opcode = 0x15
 | 
			
		||||
	DW_OP_swap                Opcode = 0x16
 | 
			
		||||
	DW_OP_rot                 Opcode = 0x17
 | 
			
		||||
	DW_OP_xderef              Opcode = 0x18
 | 
			
		||||
	DW_OP_abs                 Opcode = 0x19
 | 
			
		||||
	DW_OP_and                 Opcode = 0x1a
 | 
			
		||||
	DW_OP_div                 Opcode = 0x1b
 | 
			
		||||
	DW_OP_minus               Opcode = 0x1c
 | 
			
		||||
	DW_OP_mod                 Opcode = 0x1d
 | 
			
		||||
	DW_OP_mul                 Opcode = 0x1e
 | 
			
		||||
	DW_OP_neg                 Opcode = 0x1f
 | 
			
		||||
	DW_OP_not                 Opcode = 0x20
 | 
			
		||||
	DW_OP_or                  Opcode = 0x21
 | 
			
		||||
	DW_OP_plus                Opcode = 0x22
 | 
			
		||||
	DW_OP_plus_uconst         Opcode = 0x23
 | 
			
		||||
	DW_OP_shl                 Opcode = 0x24
 | 
			
		||||
	DW_OP_shr                 Opcode = 0x25
 | 
			
		||||
	DW_OP_shra                Opcode = 0x26
 | 
			
		||||
	DW_OP_xor                 Opcode = 0x27
 | 
			
		||||
	DW_OP_bra                 Opcode = 0x28
 | 
			
		||||
	DW_OP_eq                  Opcode = 0x29
 | 
			
		||||
	DW_OP_ge                  Opcode = 0x2a
 | 
			
		||||
	DW_OP_gt                  Opcode = 0x2b
 | 
			
		||||
	DW_OP_le                  Opcode = 0x2c
 | 
			
		||||
	DW_OP_lt                  Opcode = 0x2d
 | 
			
		||||
	DW_OP_ne                  Opcode = 0x2e
 | 
			
		||||
	DW_OP_skip                Opcode = 0x2f
 | 
			
		||||
	DW_OP_lit0                Opcode = 0x30
 | 
			
		||||
	DW_OP_lit1                Opcode = 0x31
 | 
			
		||||
	DW_OP_lit2                Opcode = 0x32
 | 
			
		||||
	DW_OP_lit3                Opcode = 0x33
 | 
			
		||||
	DW_OP_lit4                Opcode = 0x34
 | 
			
		||||
	DW_OP_lit5                Opcode = 0x35
 | 
			
		||||
	DW_OP_lit6                Opcode = 0x36
 | 
			
		||||
	DW_OP_lit7                Opcode = 0x37
 | 
			
		||||
	DW_OP_lit8                Opcode = 0x38
 | 
			
		||||
	DW_OP_lit9                Opcode = 0x39
 | 
			
		||||
	DW_OP_lit10               Opcode = 0x3a
 | 
			
		||||
	DW_OP_lit11               Opcode = 0x3b
 | 
			
		||||
	DW_OP_lit12               Opcode = 0x3c
 | 
			
		||||
	DW_OP_lit13               Opcode = 0x3d
 | 
			
		||||
	DW_OP_lit14               Opcode = 0x3e
 | 
			
		||||
	DW_OP_lit15               Opcode = 0x3f
 | 
			
		||||
	DW_OP_lit16               Opcode = 0x40
 | 
			
		||||
	DW_OP_lit17               Opcode = 0x41
 | 
			
		||||
	DW_OP_lit18               Opcode = 0x42
 | 
			
		||||
	DW_OP_lit19               Opcode = 0x43
 | 
			
		||||
	DW_OP_lit20               Opcode = 0x44
 | 
			
		||||
	DW_OP_lit21               Opcode = 0x45
 | 
			
		||||
	DW_OP_lit22               Opcode = 0x46
 | 
			
		||||
	DW_OP_lit23               Opcode = 0x47
 | 
			
		||||
	DW_OP_lit24               Opcode = 0x48
 | 
			
		||||
	DW_OP_lit25               Opcode = 0x49
 | 
			
		||||
	DW_OP_lit26               Opcode = 0x4a
 | 
			
		||||
	DW_OP_lit27               Opcode = 0x4b
 | 
			
		||||
	DW_OP_lit28               Opcode = 0x4c
 | 
			
		||||
	DW_OP_lit29               Opcode = 0x4d
 | 
			
		||||
	DW_OP_lit30               Opcode = 0x4e
 | 
			
		||||
	DW_OP_lit31               Opcode = 0x4f
 | 
			
		||||
	DW_OP_reg0                Opcode = 0x50
 | 
			
		||||
	DW_OP_reg1                Opcode = 0x51
 | 
			
		||||
	DW_OP_reg2                Opcode = 0x52
 | 
			
		||||
	DW_OP_reg3                Opcode = 0x53
 | 
			
		||||
	DW_OP_reg4                Opcode = 0x54
 | 
			
		||||
	DW_OP_reg5                Opcode = 0x55
 | 
			
		||||
	DW_OP_reg6                Opcode = 0x56
 | 
			
		||||
	DW_OP_reg7                Opcode = 0x57
 | 
			
		||||
	DW_OP_reg8                Opcode = 0x58
 | 
			
		||||
	DW_OP_reg9                Opcode = 0x59
 | 
			
		||||
	DW_OP_reg10               Opcode = 0x5a
 | 
			
		||||
	DW_OP_reg11               Opcode = 0x5b
 | 
			
		||||
	DW_OP_reg12               Opcode = 0x5c
 | 
			
		||||
	DW_OP_reg13               Opcode = 0x5d
 | 
			
		||||
	DW_OP_reg14               Opcode = 0x5e
 | 
			
		||||
	DW_OP_reg15               Opcode = 0x5f
 | 
			
		||||
	DW_OP_reg16               Opcode = 0x60
 | 
			
		||||
	DW_OP_reg17               Opcode = 0x61
 | 
			
		||||
	DW_OP_reg18               Opcode = 0x62
 | 
			
		||||
	DW_OP_reg19               Opcode = 0x63
 | 
			
		||||
	DW_OP_reg20               Opcode = 0x64
 | 
			
		||||
	DW_OP_reg21               Opcode = 0x65
 | 
			
		||||
	DW_OP_reg22               Opcode = 0x66
 | 
			
		||||
	DW_OP_reg23               Opcode = 0x67
 | 
			
		||||
	DW_OP_reg24               Opcode = 0x68
 | 
			
		||||
	DW_OP_reg25               Opcode = 0x69
 | 
			
		||||
	DW_OP_reg26               Opcode = 0x6a
 | 
			
		||||
	DW_OP_reg27               Opcode = 0x6b
 | 
			
		||||
	DW_OP_reg28               Opcode = 0x6c
 | 
			
		||||
	DW_OP_reg29               Opcode = 0x6d
 | 
			
		||||
	DW_OP_reg30               Opcode = 0x6e
 | 
			
		||||
	DW_OP_reg31               Opcode = 0x6f
 | 
			
		||||
	DW_OP_breg0               Opcode = 0x70
 | 
			
		||||
	DW_OP_breg1               Opcode = 0x71
 | 
			
		||||
	DW_OP_breg2               Opcode = 0x72
 | 
			
		||||
	DW_OP_breg3               Opcode = 0x73
 | 
			
		||||
	DW_OP_breg4               Opcode = 0x74
 | 
			
		||||
	DW_OP_breg5               Opcode = 0x75
 | 
			
		||||
	DW_OP_breg6               Opcode = 0x76
 | 
			
		||||
	DW_OP_breg7               Opcode = 0x77
 | 
			
		||||
	DW_OP_breg8               Opcode = 0x78
 | 
			
		||||
	DW_OP_breg9               Opcode = 0x79
 | 
			
		||||
	DW_OP_breg10              Opcode = 0x7a
 | 
			
		||||
	DW_OP_breg11              Opcode = 0x7b
 | 
			
		||||
	DW_OP_breg12              Opcode = 0x7c
 | 
			
		||||
	DW_OP_breg13              Opcode = 0x7d
 | 
			
		||||
	DW_OP_breg14              Opcode = 0x7e
 | 
			
		||||
	DW_OP_breg15              Opcode = 0x7f
 | 
			
		||||
	DW_OP_breg16              Opcode = 0x80
 | 
			
		||||
	DW_OP_breg17              Opcode = 0x81
 | 
			
		||||
	DW_OP_breg18              Opcode = 0x82
 | 
			
		||||
	DW_OP_breg19              Opcode = 0x83
 | 
			
		||||
	DW_OP_breg20              Opcode = 0x84
 | 
			
		||||
	DW_OP_breg21              Opcode = 0x85
 | 
			
		||||
	DW_OP_breg22              Opcode = 0x86
 | 
			
		||||
	DW_OP_breg23              Opcode = 0x87
 | 
			
		||||
	DW_OP_breg24              Opcode = 0x88
 | 
			
		||||
	DW_OP_breg25              Opcode = 0x89
 | 
			
		||||
	DW_OP_breg26              Opcode = 0x8a
 | 
			
		||||
	DW_OP_breg27              Opcode = 0x8b
 | 
			
		||||
	DW_OP_breg28              Opcode = 0x8c
 | 
			
		||||
	DW_OP_breg29              Opcode = 0x8d
 | 
			
		||||
	DW_OP_breg30              Opcode = 0x8e
 | 
			
		||||
	DW_OP_breg31              Opcode = 0x8f
 | 
			
		||||
	DW_OP_regx                Opcode = 0x90
 | 
			
		||||
	DW_OP_fbreg               Opcode = 0x91
 | 
			
		||||
	DW_OP_bregx               Opcode = 0x92
 | 
			
		||||
	DW_OP_piece               Opcode = 0x93
 | 
			
		||||
	DW_OP_deref_size          Opcode = 0x94
 | 
			
		||||
	DW_OP_xderef_size         Opcode = 0x95
 | 
			
		||||
	DW_OP_nop                 Opcode = 0x96
 | 
			
		||||
	DW_OP_push_object_address Opcode = 0x97
 | 
			
		||||
	DW_OP_call2               Opcode = 0x98
 | 
			
		||||
	DW_OP_call4               Opcode = 0x99
 | 
			
		||||
	DW_OP_call_ref            Opcode = 0x9a
 | 
			
		||||
	DW_OP_form_tls_address    Opcode = 0x9b
 | 
			
		||||
	DW_OP_call_frame_cfa      Opcode = 0x9c
 | 
			
		||||
	DW_OP_bit_piece           Opcode = 0x9d
 | 
			
		||||
	DW_OP_implicit_value      Opcode = 0x9e
 | 
			
		||||
	DW_OP_stack_value         Opcode = 0x9f
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var opcodeName = map[Opcode]string{
 | 
			
		||||
	DW_OP_addr:                "DW_OP_addr",
 | 
			
		||||
	DW_OP_deref:               "DW_OP_deref",
 | 
			
		||||
	DW_OP_const1u:             "DW_OP_const1u",
 | 
			
		||||
	DW_OP_const1s:             "DW_OP_const1s",
 | 
			
		||||
	DW_OP_const2u:             "DW_OP_const2u",
 | 
			
		||||
	DW_OP_const2s:             "DW_OP_const2s",
 | 
			
		||||
	DW_OP_const4u:             "DW_OP_const4u",
 | 
			
		||||
	DW_OP_const4s:             "DW_OP_const4s",
 | 
			
		||||
	DW_OP_const8u:             "DW_OP_const8u",
 | 
			
		||||
	DW_OP_const8s:             "DW_OP_const8s",
 | 
			
		||||
	DW_OP_constu:              "DW_OP_constu",
 | 
			
		||||
	DW_OP_consts:              "DW_OP_consts",
 | 
			
		||||
	DW_OP_dup:                 "DW_OP_dup",
 | 
			
		||||
	DW_OP_drop:                "DW_OP_drop",
 | 
			
		||||
	DW_OP_over:                "DW_OP_over",
 | 
			
		||||
	DW_OP_pick:                "DW_OP_pick",
 | 
			
		||||
	DW_OP_swap:                "DW_OP_swap",
 | 
			
		||||
	DW_OP_rot:                 "DW_OP_rot",
 | 
			
		||||
	DW_OP_xderef:              "DW_OP_xderef",
 | 
			
		||||
	DW_OP_abs:                 "DW_OP_abs",
 | 
			
		||||
	DW_OP_and:                 "DW_OP_and",
 | 
			
		||||
	DW_OP_div:                 "DW_OP_div",
 | 
			
		||||
	DW_OP_minus:               "DW_OP_minus",
 | 
			
		||||
	DW_OP_mod:                 "DW_OP_mod",
 | 
			
		||||
	DW_OP_mul:                 "DW_OP_mul",
 | 
			
		||||
	DW_OP_neg:                 "DW_OP_neg",
 | 
			
		||||
	DW_OP_not:                 "DW_OP_not",
 | 
			
		||||
	DW_OP_or:                  "DW_OP_or",
 | 
			
		||||
	DW_OP_plus:                "DW_OP_plus",
 | 
			
		||||
	DW_OP_plus_uconst:         "DW_OP_plus_uconst",
 | 
			
		||||
	DW_OP_shl:                 "DW_OP_shl",
 | 
			
		||||
	DW_OP_shr:                 "DW_OP_shr",
 | 
			
		||||
	DW_OP_shra:                "DW_OP_shra",
 | 
			
		||||
	DW_OP_xor:                 "DW_OP_xor",
 | 
			
		||||
	DW_OP_bra:                 "DW_OP_bra",
 | 
			
		||||
	DW_OP_eq:                  "DW_OP_eq",
 | 
			
		||||
	DW_OP_ge:                  "DW_OP_ge",
 | 
			
		||||
	DW_OP_gt:                  "DW_OP_gt",
 | 
			
		||||
	DW_OP_le:                  "DW_OP_le",
 | 
			
		||||
	DW_OP_lt:                  "DW_OP_lt",
 | 
			
		||||
	DW_OP_ne:                  "DW_OP_ne",
 | 
			
		||||
	DW_OP_skip:                "DW_OP_skip",
 | 
			
		||||
	DW_OP_lit0:                "DW_OP_lit0",
 | 
			
		||||
	DW_OP_lit1:                "DW_OP_lit1",
 | 
			
		||||
	DW_OP_lit2:                "DW_OP_lit2",
 | 
			
		||||
	DW_OP_lit3:                "DW_OP_lit3",
 | 
			
		||||
	DW_OP_lit4:                "DW_OP_lit4",
 | 
			
		||||
	DW_OP_lit5:                "DW_OP_lit5",
 | 
			
		||||
	DW_OP_lit6:                "DW_OP_lit6",
 | 
			
		||||
	DW_OP_lit7:                "DW_OP_lit7",
 | 
			
		||||
	DW_OP_lit8:                "DW_OP_lit8",
 | 
			
		||||
	DW_OP_lit9:                "DW_OP_lit9",
 | 
			
		||||
	DW_OP_lit10:               "DW_OP_lit10",
 | 
			
		||||
	DW_OP_lit11:               "DW_OP_lit11",
 | 
			
		||||
	DW_OP_lit12:               "DW_OP_lit12",
 | 
			
		||||
	DW_OP_lit13:               "DW_OP_lit13",
 | 
			
		||||
	DW_OP_lit14:               "DW_OP_lit14",
 | 
			
		||||
	DW_OP_lit15:               "DW_OP_lit15",
 | 
			
		||||
	DW_OP_lit16:               "DW_OP_lit16",
 | 
			
		||||
	DW_OP_lit17:               "DW_OP_lit17",
 | 
			
		||||
	DW_OP_lit18:               "DW_OP_lit18",
 | 
			
		||||
	DW_OP_lit19:               "DW_OP_lit19",
 | 
			
		||||
	DW_OP_lit20:               "DW_OP_lit20",
 | 
			
		||||
	DW_OP_lit21:               "DW_OP_lit21",
 | 
			
		||||
	DW_OP_lit22:               "DW_OP_lit22",
 | 
			
		||||
	DW_OP_lit23:               "DW_OP_lit23",
 | 
			
		||||
	DW_OP_lit24:               "DW_OP_lit24",
 | 
			
		||||
	DW_OP_lit25:               "DW_OP_lit25",
 | 
			
		||||
	DW_OP_lit26:               "DW_OP_lit26",
 | 
			
		||||
	DW_OP_lit27:               "DW_OP_lit27",
 | 
			
		||||
	DW_OP_lit28:               "DW_OP_lit28",
 | 
			
		||||
	DW_OP_lit29:               "DW_OP_lit29",
 | 
			
		||||
	DW_OP_lit30:               "DW_OP_lit30",
 | 
			
		||||
	DW_OP_lit31:               "DW_OP_lit31",
 | 
			
		||||
	DW_OP_reg0:                "DW_OP_reg0",
 | 
			
		||||
	DW_OP_reg1:                "DW_OP_reg1",
 | 
			
		||||
	DW_OP_reg2:                "DW_OP_reg2",
 | 
			
		||||
	DW_OP_reg3:                "DW_OP_reg3",
 | 
			
		||||
	DW_OP_reg4:                "DW_OP_reg4",
 | 
			
		||||
	DW_OP_reg5:                "DW_OP_reg5",
 | 
			
		||||
	DW_OP_reg6:                "DW_OP_reg6",
 | 
			
		||||
	DW_OP_reg7:                "DW_OP_reg7",
 | 
			
		||||
	DW_OP_reg8:                "DW_OP_reg8",
 | 
			
		||||
	DW_OP_reg9:                "DW_OP_reg9",
 | 
			
		||||
	DW_OP_reg10:               "DW_OP_reg10",
 | 
			
		||||
	DW_OP_reg11:               "DW_OP_reg11",
 | 
			
		||||
	DW_OP_reg12:               "DW_OP_reg12",
 | 
			
		||||
	DW_OP_reg13:               "DW_OP_reg13",
 | 
			
		||||
	DW_OP_reg14:               "DW_OP_reg14",
 | 
			
		||||
	DW_OP_reg15:               "DW_OP_reg15",
 | 
			
		||||
	DW_OP_reg16:               "DW_OP_reg16",
 | 
			
		||||
	DW_OP_reg17:               "DW_OP_reg17",
 | 
			
		||||
	DW_OP_reg18:               "DW_OP_reg18",
 | 
			
		||||
	DW_OP_reg19:               "DW_OP_reg19",
 | 
			
		||||
	DW_OP_reg20:               "DW_OP_reg20",
 | 
			
		||||
	DW_OP_reg21:               "DW_OP_reg21",
 | 
			
		||||
	DW_OP_reg22:               "DW_OP_reg22",
 | 
			
		||||
	DW_OP_reg23:               "DW_OP_reg23",
 | 
			
		||||
	DW_OP_reg24:               "DW_OP_reg24",
 | 
			
		||||
	DW_OP_reg25:               "DW_OP_reg25",
 | 
			
		||||
	DW_OP_reg26:               "DW_OP_reg26",
 | 
			
		||||
	DW_OP_reg27:               "DW_OP_reg27",
 | 
			
		||||
	DW_OP_reg28:               "DW_OP_reg28",
 | 
			
		||||
	DW_OP_reg29:               "DW_OP_reg29",
 | 
			
		||||
	DW_OP_reg30:               "DW_OP_reg30",
 | 
			
		||||
	DW_OP_reg31:               "DW_OP_reg31",
 | 
			
		||||
	DW_OP_breg0:               "DW_OP_breg0",
 | 
			
		||||
	DW_OP_breg1:               "DW_OP_breg1",
 | 
			
		||||
	DW_OP_breg2:               "DW_OP_breg2",
 | 
			
		||||
	DW_OP_breg3:               "DW_OP_breg3",
 | 
			
		||||
	DW_OP_breg4:               "DW_OP_breg4",
 | 
			
		||||
	DW_OP_breg5:               "DW_OP_breg5",
 | 
			
		||||
	DW_OP_breg6:               "DW_OP_breg6",
 | 
			
		||||
	DW_OP_breg7:               "DW_OP_breg7",
 | 
			
		||||
	DW_OP_breg8:               "DW_OP_breg8",
 | 
			
		||||
	DW_OP_breg9:               "DW_OP_breg9",
 | 
			
		||||
	DW_OP_breg10:              "DW_OP_breg10",
 | 
			
		||||
	DW_OP_breg11:              "DW_OP_breg11",
 | 
			
		||||
	DW_OP_breg12:              "DW_OP_breg12",
 | 
			
		||||
	DW_OP_breg13:              "DW_OP_breg13",
 | 
			
		||||
	DW_OP_breg14:              "DW_OP_breg14",
 | 
			
		||||
	DW_OP_breg15:              "DW_OP_breg15",
 | 
			
		||||
	DW_OP_breg16:              "DW_OP_breg16",
 | 
			
		||||
	DW_OP_breg17:              "DW_OP_breg17",
 | 
			
		||||
	DW_OP_breg18:              "DW_OP_breg18",
 | 
			
		||||
	DW_OP_breg19:              "DW_OP_breg19",
 | 
			
		||||
	DW_OP_breg20:              "DW_OP_breg20",
 | 
			
		||||
	DW_OP_breg21:              "DW_OP_breg21",
 | 
			
		||||
	DW_OP_breg22:              "DW_OP_breg22",
 | 
			
		||||
	DW_OP_breg23:              "DW_OP_breg23",
 | 
			
		||||
	DW_OP_breg24:              "DW_OP_breg24",
 | 
			
		||||
	DW_OP_breg25:              "DW_OP_breg25",
 | 
			
		||||
	DW_OP_breg26:              "DW_OP_breg26",
 | 
			
		||||
	DW_OP_breg27:              "DW_OP_breg27",
 | 
			
		||||
	DW_OP_breg28:              "DW_OP_breg28",
 | 
			
		||||
	DW_OP_breg29:              "DW_OP_breg29",
 | 
			
		||||
	DW_OP_breg30:              "DW_OP_breg30",
 | 
			
		||||
	DW_OP_breg31:              "DW_OP_breg31",
 | 
			
		||||
	DW_OP_regx:                "DW_OP_regx",
 | 
			
		||||
	DW_OP_fbreg:               "DW_OP_fbreg",
 | 
			
		||||
	DW_OP_bregx:               "DW_OP_bregx",
 | 
			
		||||
	DW_OP_piece:               "DW_OP_piece",
 | 
			
		||||
	DW_OP_deref_size:          "DW_OP_deref_size",
 | 
			
		||||
	DW_OP_xderef_size:         "DW_OP_xderef_size",
 | 
			
		||||
	DW_OP_nop:                 "DW_OP_nop",
 | 
			
		||||
	DW_OP_push_object_address: "DW_OP_push_object_address",
 | 
			
		||||
	DW_OP_call2:               "DW_OP_call2",
 | 
			
		||||
	DW_OP_call4:               "DW_OP_call4",
 | 
			
		||||
	DW_OP_call_ref:            "DW_OP_call_ref",
 | 
			
		||||
	DW_OP_form_tls_address:    "DW_OP_form_tls_address",
 | 
			
		||||
	DW_OP_call_frame_cfa:      "DW_OP_call_frame_cfa",
 | 
			
		||||
	DW_OP_bit_piece:           "DW_OP_bit_piece",
 | 
			
		||||
	DW_OP_implicit_value:      "DW_OP_implicit_value",
 | 
			
		||||
	DW_OP_stack_value:         "DW_OP_stack_value",
 | 
			
		||||
}
 | 
			
		||||
var opcodeArgs = map[Opcode]string{
 | 
			
		||||
	DW_OP_addr:                "8",
 | 
			
		||||
	DW_OP_deref:               "",
 | 
			
		||||
	DW_OP_const1u:             "1",
 | 
			
		||||
	DW_OP_const1s:             "1",
 | 
			
		||||
	DW_OP_const2u:             "2",
 | 
			
		||||
	DW_OP_const2s:             "2",
 | 
			
		||||
	DW_OP_const4u:             "4",
 | 
			
		||||
	DW_OP_const4s:             "4",
 | 
			
		||||
	DW_OP_const8u:             "8",
 | 
			
		||||
	DW_OP_const8s:             "8",
 | 
			
		||||
	DW_OP_constu:              "u",
 | 
			
		||||
	DW_OP_consts:              "s",
 | 
			
		||||
	DW_OP_dup:                 "",
 | 
			
		||||
	DW_OP_drop:                "",
 | 
			
		||||
	DW_OP_over:                "",
 | 
			
		||||
	DW_OP_pick:                "",
 | 
			
		||||
	DW_OP_swap:                "",
 | 
			
		||||
	DW_OP_rot:                 "",
 | 
			
		||||
	DW_OP_xderef:              "",
 | 
			
		||||
	DW_OP_abs:                 "",
 | 
			
		||||
	DW_OP_and:                 "",
 | 
			
		||||
	DW_OP_div:                 "",
 | 
			
		||||
	DW_OP_minus:               "",
 | 
			
		||||
	DW_OP_mod:                 "",
 | 
			
		||||
	DW_OP_mul:                 "",
 | 
			
		||||
	DW_OP_neg:                 "",
 | 
			
		||||
	DW_OP_not:                 "",
 | 
			
		||||
	DW_OP_or:                  "",
 | 
			
		||||
	DW_OP_plus:                "",
 | 
			
		||||
	DW_OP_plus_uconst:         "u",
 | 
			
		||||
	DW_OP_shl:                 "",
 | 
			
		||||
	DW_OP_shr:                 "",
 | 
			
		||||
	DW_OP_shra:                "",
 | 
			
		||||
	DW_OP_xor:                 "",
 | 
			
		||||
	DW_OP_bra:                 "2",
 | 
			
		||||
	DW_OP_eq:                  "",
 | 
			
		||||
	DW_OP_ge:                  "",
 | 
			
		||||
	DW_OP_gt:                  "",
 | 
			
		||||
	DW_OP_le:                  "",
 | 
			
		||||
	DW_OP_lt:                  "",
 | 
			
		||||
	DW_OP_ne:                  "",
 | 
			
		||||
	DW_OP_skip:                "2",
 | 
			
		||||
	DW_OP_lit0:                "",
 | 
			
		||||
	DW_OP_lit1:                "",
 | 
			
		||||
	DW_OP_lit2:                "",
 | 
			
		||||
	DW_OP_lit3:                "",
 | 
			
		||||
	DW_OP_lit4:                "",
 | 
			
		||||
	DW_OP_lit5:                "",
 | 
			
		||||
	DW_OP_lit6:                "",
 | 
			
		||||
	DW_OP_lit7:                "",
 | 
			
		||||
	DW_OP_lit8:                "",
 | 
			
		||||
	DW_OP_lit9:                "",
 | 
			
		||||
	DW_OP_lit10:               "",
 | 
			
		||||
	DW_OP_lit11:               "",
 | 
			
		||||
	DW_OP_lit12:               "",
 | 
			
		||||
	DW_OP_lit13:               "",
 | 
			
		||||
	DW_OP_lit14:               "",
 | 
			
		||||
	DW_OP_lit15:               "",
 | 
			
		||||
	DW_OP_lit16:               "",
 | 
			
		||||
	DW_OP_lit17:               "",
 | 
			
		||||
	DW_OP_lit18:               "",
 | 
			
		||||
	DW_OP_lit19:               "",
 | 
			
		||||
	DW_OP_lit20:               "",
 | 
			
		||||
	DW_OP_lit21:               "",
 | 
			
		||||
	DW_OP_lit22:               "",
 | 
			
		||||
	DW_OP_lit23:               "",
 | 
			
		||||
	DW_OP_lit24:               "",
 | 
			
		||||
	DW_OP_lit25:               "",
 | 
			
		||||
	DW_OP_lit26:               "",
 | 
			
		||||
	DW_OP_lit27:               "",
 | 
			
		||||
	DW_OP_lit28:               "",
 | 
			
		||||
	DW_OP_lit29:               "",
 | 
			
		||||
	DW_OP_lit30:               "",
 | 
			
		||||
	DW_OP_lit31:               "",
 | 
			
		||||
	DW_OP_reg0:                "",
 | 
			
		||||
	DW_OP_reg1:                "",
 | 
			
		||||
	DW_OP_reg2:                "",
 | 
			
		||||
	DW_OP_reg3:                "",
 | 
			
		||||
	DW_OP_reg4:                "",
 | 
			
		||||
	DW_OP_reg5:                "",
 | 
			
		||||
	DW_OP_reg6:                "",
 | 
			
		||||
	DW_OP_reg7:                "",
 | 
			
		||||
	DW_OP_reg8:                "",
 | 
			
		||||
	DW_OP_reg9:                "",
 | 
			
		||||
	DW_OP_reg10:               "",
 | 
			
		||||
	DW_OP_reg11:               "",
 | 
			
		||||
	DW_OP_reg12:               "",
 | 
			
		||||
	DW_OP_reg13:               "",
 | 
			
		||||
	DW_OP_reg14:               "",
 | 
			
		||||
	DW_OP_reg15:               "",
 | 
			
		||||
	DW_OP_reg16:               "",
 | 
			
		||||
	DW_OP_reg17:               "",
 | 
			
		||||
	DW_OP_reg18:               "",
 | 
			
		||||
	DW_OP_reg19:               "",
 | 
			
		||||
	DW_OP_reg20:               "",
 | 
			
		||||
	DW_OP_reg21:               "",
 | 
			
		||||
	DW_OP_reg22:               "",
 | 
			
		||||
	DW_OP_reg23:               "",
 | 
			
		||||
	DW_OP_reg24:               "",
 | 
			
		||||
	DW_OP_reg25:               "",
 | 
			
		||||
	DW_OP_reg26:               "",
 | 
			
		||||
	DW_OP_reg27:               "",
 | 
			
		||||
	DW_OP_reg28:               "",
 | 
			
		||||
	DW_OP_reg29:               "",
 | 
			
		||||
	DW_OP_reg30:               "",
 | 
			
		||||
	DW_OP_reg31:               "",
 | 
			
		||||
	DW_OP_breg0:               "s",
 | 
			
		||||
	DW_OP_breg1:               "s",
 | 
			
		||||
	DW_OP_breg2:               "s",
 | 
			
		||||
	DW_OP_breg3:               "s",
 | 
			
		||||
	DW_OP_breg4:               "s",
 | 
			
		||||
	DW_OP_breg5:               "s",
 | 
			
		||||
	DW_OP_breg6:               "s",
 | 
			
		||||
	DW_OP_breg7:               "s",
 | 
			
		||||
	DW_OP_breg8:               "s",
 | 
			
		||||
	DW_OP_breg9:               "s",
 | 
			
		||||
	DW_OP_breg10:              "s",
 | 
			
		||||
	DW_OP_breg11:              "s",
 | 
			
		||||
	DW_OP_breg12:              "s",
 | 
			
		||||
	DW_OP_breg13:              "s",
 | 
			
		||||
	DW_OP_breg14:              "s",
 | 
			
		||||
	DW_OP_breg15:              "s",
 | 
			
		||||
	DW_OP_breg16:              "s",
 | 
			
		||||
	DW_OP_breg17:              "s",
 | 
			
		||||
	DW_OP_breg18:              "s",
 | 
			
		||||
	DW_OP_breg19:              "s",
 | 
			
		||||
	DW_OP_breg20:              "s",
 | 
			
		||||
	DW_OP_breg21:              "s",
 | 
			
		||||
	DW_OP_breg22:              "s",
 | 
			
		||||
	DW_OP_breg23:              "s",
 | 
			
		||||
	DW_OP_breg24:              "s",
 | 
			
		||||
	DW_OP_breg25:              "s",
 | 
			
		||||
	DW_OP_breg26:              "s",
 | 
			
		||||
	DW_OP_breg27:              "s",
 | 
			
		||||
	DW_OP_breg28:              "s",
 | 
			
		||||
	DW_OP_breg29:              "s",
 | 
			
		||||
	DW_OP_breg30:              "s",
 | 
			
		||||
	DW_OP_breg31:              "s",
 | 
			
		||||
	DW_OP_regx:                "s",
 | 
			
		||||
	DW_OP_fbreg:               "s",
 | 
			
		||||
	DW_OP_bregx:               "us",
 | 
			
		||||
	DW_OP_piece:               "u",
 | 
			
		||||
	DW_OP_deref_size:          "1",
 | 
			
		||||
	DW_OP_xderef_size:         "1",
 | 
			
		||||
	DW_OP_nop:                 "",
 | 
			
		||||
	DW_OP_push_object_address: "",
 | 
			
		||||
	DW_OP_call2:               "2",
 | 
			
		||||
	DW_OP_call4:               "4",
 | 
			
		||||
	DW_OP_call_ref:            "4",
 | 
			
		||||
	DW_OP_form_tls_address:    "",
 | 
			
		||||
	DW_OP_call_frame_cfa:      "",
 | 
			
		||||
	DW_OP_bit_piece:           "uu",
 | 
			
		||||
	DW_OP_implicit_value:      "B",
 | 
			
		||||
	DW_OP_stack_value:         "",
 | 
			
		||||
}
 | 
			
		||||
var oplut = map[Opcode]stackfn{
 | 
			
		||||
	DW_OP_addr:           addr,
 | 
			
		||||
	DW_OP_consts:         consts,
 | 
			
		||||
	DW_OP_plus:           plus,
 | 
			
		||||
	DW_OP_plus_uconst:    plusuconsts,
 | 
			
		||||
	DW_OP_reg0:           register,
 | 
			
		||||
	DW_OP_reg1:           register,
 | 
			
		||||
	DW_OP_reg2:           register,
 | 
			
		||||
	DW_OP_reg3:           register,
 | 
			
		||||
	DW_OP_reg4:           register,
 | 
			
		||||
	DW_OP_reg5:           register,
 | 
			
		||||
	DW_OP_reg6:           register,
 | 
			
		||||
	DW_OP_reg7:           register,
 | 
			
		||||
	DW_OP_reg8:           register,
 | 
			
		||||
	DW_OP_reg9:           register,
 | 
			
		||||
	DW_OP_reg10:          register,
 | 
			
		||||
	DW_OP_reg11:          register,
 | 
			
		||||
	DW_OP_reg12:          register,
 | 
			
		||||
	DW_OP_reg13:          register,
 | 
			
		||||
	DW_OP_reg14:          register,
 | 
			
		||||
	DW_OP_reg15:          register,
 | 
			
		||||
	DW_OP_reg16:          register,
 | 
			
		||||
	DW_OP_reg17:          register,
 | 
			
		||||
	DW_OP_reg18:          register,
 | 
			
		||||
	DW_OP_reg19:          register,
 | 
			
		||||
	DW_OP_reg20:          register,
 | 
			
		||||
	DW_OP_reg21:          register,
 | 
			
		||||
	DW_OP_reg22:          register,
 | 
			
		||||
	DW_OP_reg23:          register,
 | 
			
		||||
	DW_OP_reg24:          register,
 | 
			
		||||
	DW_OP_reg25:          register,
 | 
			
		||||
	DW_OP_reg26:          register,
 | 
			
		||||
	DW_OP_reg27:          register,
 | 
			
		||||
	DW_OP_reg28:          register,
 | 
			
		||||
	DW_OP_reg29:          register,
 | 
			
		||||
	DW_OP_reg30:          register,
 | 
			
		||||
	DW_OP_reg31:          register,
 | 
			
		||||
	DW_OP_regx:           register,
 | 
			
		||||
	DW_OP_fbreg:          framebase,
 | 
			
		||||
	DW_OP_piece:          piece,
 | 
			
		||||
	DW_OP_call_frame_cfa: callframecfa,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										175
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/opcodes.table
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/opcodes.table
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,175 @@
 | 
			
		||||
// This file is used by scripts/gen-opcodes.go to generate
 | 
			
		||||
// pkg/dwarf/op/opcodes.go 
 | 
			
		||||
// Lines starting with // are comments and will be discarded.
 | 
			
		||||
// Non empty lines contain the following tab separated fields:
 | 
			
		||||
//
 | 
			
		||||
//  <opcode name> <opcode code> <arguments> <function name>
 | 
			
		||||
// 
 | 
			
		||||
// With the last column, <function name>, being optional.
 | 
			
		||||
//
 | 
			
		||||
// The arguments field should contain a string with one character for each
 | 
			
		||||
// argument of the opcode:
 | 
			
		||||
//
 | 
			
		||||
//  s		signed variable length integer
 | 
			
		||||
//  u		unsigned variable length integer
 | 
			
		||||
//  1		one byte unsigned integer
 | 
			
		||||
//  2		two bytes unsigned integer
 | 
			
		||||
//  4		four bytes unsigned integer
 | 
			
		||||
//  8		eight bytes unsigned integer
 | 
			
		||||
//  B		an unsigned variable length integer 'n' followed by n a block of n bytes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DW_OP_addr	0x03	"8"	addr
 | 
			
		||||
DW_OP_deref	0x06	""
 | 
			
		||||
DW_OP_const1u	0x08	"1"
 | 
			
		||||
DW_OP_const1s	0x09	"1"
 | 
			
		||||
DW_OP_const2u	0x0a	"2"
 | 
			
		||||
DW_OP_const2s	0x0b	"2"
 | 
			
		||||
DW_OP_const4u	0x0c	"4"
 | 
			
		||||
DW_OP_const4s	0x0d	"4"
 | 
			
		||||
DW_OP_const8u	0x0e	"8"
 | 
			
		||||
DW_OP_const8s	0x0f	"8"
 | 
			
		||||
DW_OP_constu	0x10	"u"
 | 
			
		||||
DW_OP_consts	0x11	"s"	consts
 | 
			
		||||
DW_OP_dup	0x12	""
 | 
			
		||||
DW_OP_drop	0x13	""
 | 
			
		||||
DW_OP_over	0x14	""
 | 
			
		||||
DW_OP_pick	0x15	""
 | 
			
		||||
DW_OP_swap	0x16	""
 | 
			
		||||
DW_OP_rot	0x17	""
 | 
			
		||||
DW_OP_xderef	0x18	""
 | 
			
		||||
DW_OP_abs	0x19	""
 | 
			
		||||
DW_OP_and	0x1a	""
 | 
			
		||||
DW_OP_div	0x1b	""
 | 
			
		||||
DW_OP_minus	0x1c	""
 | 
			
		||||
DW_OP_mod	0x1d	""
 | 
			
		||||
DW_OP_mul	0x1e	""
 | 
			
		||||
DW_OP_neg	0x1f	""
 | 
			
		||||
DW_OP_not	0x20	""
 | 
			
		||||
DW_OP_or	0x21	""
 | 
			
		||||
DW_OP_plus	0x22	""	plus
 | 
			
		||||
DW_OP_plus_uconst	0x23	"u"	plusuconsts
 | 
			
		||||
DW_OP_shl	0x24	""
 | 
			
		||||
DW_OP_shr	0x25	""
 | 
			
		||||
DW_OP_shra	0x26	""
 | 
			
		||||
DW_OP_xor	0x27	""
 | 
			
		||||
DW_OP_bra	0x28	"2"
 | 
			
		||||
DW_OP_eq	0x29	""
 | 
			
		||||
DW_OP_ge	0x2a	""
 | 
			
		||||
DW_OP_gt	0x2b	""
 | 
			
		||||
DW_OP_le	0x2c	""
 | 
			
		||||
DW_OP_lt	0x2d	""
 | 
			
		||||
DW_OP_ne	0x2e	""
 | 
			
		||||
DW_OP_skip	0x2f	"2"
 | 
			
		||||
DW_OP_lit0	0x30	""
 | 
			
		||||
DW_OP_lit1	0x31	""
 | 
			
		||||
DW_OP_lit2	0x32	""
 | 
			
		||||
DW_OP_lit3	0x33	""
 | 
			
		||||
DW_OP_lit4	0x34	""
 | 
			
		||||
DW_OP_lit5	0x35	""
 | 
			
		||||
DW_OP_lit6	0x36	""
 | 
			
		||||
DW_OP_lit7	0x37	""
 | 
			
		||||
DW_OP_lit8	0x38	""
 | 
			
		||||
DW_OP_lit9	0x39	""
 | 
			
		||||
DW_OP_lit10	0x3a	""
 | 
			
		||||
DW_OP_lit11	0x3b	""
 | 
			
		||||
DW_OP_lit12	0x3c	""
 | 
			
		||||
DW_OP_lit13	0x3d	""
 | 
			
		||||
DW_OP_lit14	0x3e	""
 | 
			
		||||
DW_OP_lit15	0x3f	""
 | 
			
		||||
DW_OP_lit16	0x40	""
 | 
			
		||||
DW_OP_lit17	0x41	""
 | 
			
		||||
DW_OP_lit18	0x42	""
 | 
			
		||||
DW_OP_lit19	0x43	""
 | 
			
		||||
DW_OP_lit20	0x44	""
 | 
			
		||||
DW_OP_lit21	0x45	""
 | 
			
		||||
DW_OP_lit22	0x46	""
 | 
			
		||||
DW_OP_lit23	0x47	""
 | 
			
		||||
DW_OP_lit24	0x48	""
 | 
			
		||||
DW_OP_lit25	0x49	""
 | 
			
		||||
DW_OP_lit26	0x4a	""
 | 
			
		||||
DW_OP_lit27	0x4b	""
 | 
			
		||||
DW_OP_lit28	0x4c	""
 | 
			
		||||
DW_OP_lit29	0x4d	""
 | 
			
		||||
DW_OP_lit30	0x4e	""
 | 
			
		||||
DW_OP_lit31	0x4f	""
 | 
			
		||||
DW_OP_reg0	0x50	""	register
 | 
			
		||||
DW_OP_reg1	0x51	""	register
 | 
			
		||||
DW_OP_reg2	0x52	""	register
 | 
			
		||||
DW_OP_reg3	0x53	""	register
 | 
			
		||||
DW_OP_reg4	0x54	""	register
 | 
			
		||||
DW_OP_reg5	0x55	""	register
 | 
			
		||||
DW_OP_reg6	0x56	""	register
 | 
			
		||||
DW_OP_reg7	0x57	""	register
 | 
			
		||||
DW_OP_reg8	0x58	""	register
 | 
			
		||||
DW_OP_reg9	0x59	""	register
 | 
			
		||||
DW_OP_reg10	0x5a	""	register
 | 
			
		||||
DW_OP_reg11	0x5b	""	register
 | 
			
		||||
DW_OP_reg12	0x5c	""	register
 | 
			
		||||
DW_OP_reg13	0x5d	""	register
 | 
			
		||||
DW_OP_reg14	0x5e	""	register
 | 
			
		||||
DW_OP_reg15	0x5f	""	register
 | 
			
		||||
DW_OP_reg16	0x60	""	register
 | 
			
		||||
DW_OP_reg17	0x61	""	register
 | 
			
		||||
DW_OP_reg18	0x62	""	register
 | 
			
		||||
DW_OP_reg19	0x63	""	register
 | 
			
		||||
DW_OP_reg20	0x64	""	register
 | 
			
		||||
DW_OP_reg21	0x65	""	register
 | 
			
		||||
DW_OP_reg22	0x66	""	register
 | 
			
		||||
DW_OP_reg23	0x67	""	register
 | 
			
		||||
DW_OP_reg24	0x68	""	register
 | 
			
		||||
DW_OP_reg25	0x69	""	register
 | 
			
		||||
DW_OP_reg26	0x6a	""	register
 | 
			
		||||
DW_OP_reg27	0x6b	""	register
 | 
			
		||||
DW_OP_reg28	0x6c	""	register
 | 
			
		||||
DW_OP_reg29	0x6d	""	register
 | 
			
		||||
DW_OP_reg30	0x6e	""	register
 | 
			
		||||
DW_OP_reg31	0x6f	""	register
 | 
			
		||||
DW_OP_breg0	0x70	"s"
 | 
			
		||||
DW_OP_breg1	0x71	"s"
 | 
			
		||||
DW_OP_breg2	0x72	"s"
 | 
			
		||||
DW_OP_breg3	0x73	"s"
 | 
			
		||||
DW_OP_breg4	0x74	"s"
 | 
			
		||||
DW_OP_breg5	0x75	"s"
 | 
			
		||||
DW_OP_breg6	0x76	"s"
 | 
			
		||||
DW_OP_breg7	0x77	"s"
 | 
			
		||||
DW_OP_breg8	0x78	"s"
 | 
			
		||||
DW_OP_breg9	0x79	"s"
 | 
			
		||||
DW_OP_breg10	0x7a	"s"
 | 
			
		||||
DW_OP_breg11	0x7b	"s"
 | 
			
		||||
DW_OP_breg12	0x7c	"s"
 | 
			
		||||
DW_OP_breg13	0x7d	"s"
 | 
			
		||||
DW_OP_breg14	0x7e	"s"
 | 
			
		||||
DW_OP_breg15	0x7f	"s"
 | 
			
		||||
DW_OP_breg16	0x80	"s"
 | 
			
		||||
DW_OP_breg17	0x81	"s"
 | 
			
		||||
DW_OP_breg18	0x82	"s"
 | 
			
		||||
DW_OP_breg19	0x83	"s"
 | 
			
		||||
DW_OP_breg20	0x84	"s"
 | 
			
		||||
DW_OP_breg21	0x85	"s"
 | 
			
		||||
DW_OP_breg22	0x86	"s"
 | 
			
		||||
DW_OP_breg23	0x87	"s"
 | 
			
		||||
DW_OP_breg24	0x88	"s"
 | 
			
		||||
DW_OP_breg25	0x89	"s"
 | 
			
		||||
DW_OP_breg26	0x8a	"s"
 | 
			
		||||
DW_OP_breg27	0x8b	"s"
 | 
			
		||||
DW_OP_breg28	0x8c	"s"
 | 
			
		||||
DW_OP_breg29	0x8d	"s"
 | 
			
		||||
DW_OP_breg30	0x8e	"s"
 | 
			
		||||
DW_OP_breg31	0x8f	"s"
 | 
			
		||||
DW_OP_regx	0x90	"s"	register
 | 
			
		||||
DW_OP_fbreg	0x91	"s"	framebase
 | 
			
		||||
DW_OP_bregx	0x92	"us"
 | 
			
		||||
DW_OP_piece	0x93	"u"	piece
 | 
			
		||||
DW_OP_deref_size	0x94	"1"
 | 
			
		||||
DW_OP_xderef_size	0x95	"1"
 | 
			
		||||
DW_OP_nop	0x96	""
 | 
			
		||||
DW_OP_push_object_address	0x97	""
 | 
			
		||||
DW_OP_call2	0x98	"2"
 | 
			
		||||
DW_OP_call4	0x99	"4"
 | 
			
		||||
DW_OP_call_ref	0x9a	"4"
 | 
			
		||||
DW_OP_form_tls_address	0x9b	""
 | 
			
		||||
DW_OP_call_frame_cfa	0x9c	""	callframecfa
 | 
			
		||||
DW_OP_bit_piece	0x9d	"uu"
 | 
			
		||||
DW_OP_implicit_value	0x9e	"B"
 | 
			
		||||
DW_OP_stack_value	0x9f	""
 | 
			
		||||
							
								
								
									
										102
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/regs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/op/regs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
			
		||||
package op
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DwarfRegisters struct {
 | 
			
		||||
	StaticBase uint64
 | 
			
		||||
 | 
			
		||||
	CFA       int64
 | 
			
		||||
	FrameBase int64
 | 
			
		||||
	ObjBase   int64
 | 
			
		||||
	Regs      []*DwarfRegister
 | 
			
		||||
 | 
			
		||||
	ByteOrder binary.ByteOrder
 | 
			
		||||
	PCRegNum  uint64
 | 
			
		||||
	SPRegNum  uint64
 | 
			
		||||
	BPRegNum  uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DwarfRegister struct {
 | 
			
		||||
	Uint64Val uint64
 | 
			
		||||
	Bytes     []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Uint64Val returns the uint64 value of register idx.
 | 
			
		||||
func (regs *DwarfRegisters) Uint64Val(idx uint64) uint64 {
 | 
			
		||||
	reg := regs.Reg(idx)
 | 
			
		||||
	if reg == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	return regs.Regs[idx].Uint64Val
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Bytes returns the bytes value of register idx, nil if the register is not
 | 
			
		||||
// defined.
 | 
			
		||||
func (regs *DwarfRegisters) Bytes(idx uint64) []byte {
 | 
			
		||||
	reg := regs.Reg(idx)
 | 
			
		||||
	if reg == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if reg.Bytes == nil {
 | 
			
		||||
		var buf bytes.Buffer
 | 
			
		||||
		binary.Write(&buf, regs.ByteOrder, reg.Uint64Val)
 | 
			
		||||
		reg.Bytes = buf.Bytes()
 | 
			
		||||
	}
 | 
			
		||||
	return reg.Bytes
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Reg returns register idx or nil if the register is not defined.
 | 
			
		||||
func (regs *DwarfRegisters) Reg(idx uint64) *DwarfRegister {
 | 
			
		||||
	if idx >= uint64(len(regs.Regs)) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return regs.Regs[idx]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (regs *DwarfRegisters) PC() uint64 {
 | 
			
		||||
	return regs.Uint64Val(regs.PCRegNum)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (regs *DwarfRegisters) SP() uint64 {
 | 
			
		||||
	return regs.Uint64Val(regs.SPRegNum)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (regs *DwarfRegisters) BP() uint64 {
 | 
			
		||||
	return regs.Uint64Val(regs.BPRegNum)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddReg adds register idx to regs.
 | 
			
		||||
func (regs *DwarfRegisters) AddReg(idx uint64, reg *DwarfRegister) {
 | 
			
		||||
	if idx >= uint64(len(regs.Regs)) {
 | 
			
		||||
		newRegs := make([]*DwarfRegister, idx+1)
 | 
			
		||||
		copy(newRegs, regs.Regs)
 | 
			
		||||
		regs.Regs = newRegs
 | 
			
		||||
	}
 | 
			
		||||
	regs.Regs[idx] = reg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DwarfRegisterFromUint64(v uint64) *DwarfRegister {
 | 
			
		||||
	return &DwarfRegister{Uint64Val: v}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func DwarfRegisterFromBytes(bytes []byte) *DwarfRegister {
 | 
			
		||||
	var v uint64
 | 
			
		||||
	switch len(bytes) {
 | 
			
		||||
	case 1:
 | 
			
		||||
		v = uint64(bytes[0])
 | 
			
		||||
	case 2:
 | 
			
		||||
		x := binary.LittleEndian.Uint16(bytes)
 | 
			
		||||
		v = uint64(x)
 | 
			
		||||
	case 4:
 | 
			
		||||
		x := binary.LittleEndian.Uint32(bytes)
 | 
			
		||||
		v = uint64(x)
 | 
			
		||||
	default:
 | 
			
		||||
		if len(bytes) >= 8 {
 | 
			
		||||
			v = binary.LittleEndian.Uint64(bytes[:8])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return &DwarfRegister{Uint64Val: v, Bytes: bytes}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										165
									
								
								vendor/github.com/derekparker/delve/dwarf/reader/reader.go → vendor/github.com/go-delve/delve/pkg/dwarf/reader/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										165
									
								
								vendor/github.com/derekparker/delve/dwarf/reader/reader.go → vendor/github.com/go-delve/delve/pkg/dwarf/reader/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							@@ -1,11 +1,11 @@
 | 
			
		||||
package reader
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/dwarf"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/debug/dwarf"
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Reader struct {
 | 
			
		||||
@@ -34,7 +34,7 @@ func (reader *Reader) SeekToEntry(entry *dwarf.Entry) error {
 | 
			
		||||
 | 
			
		||||
// SeekToFunctionEntry moves the reader to the function that includes the
 | 
			
		||||
// specified program counter.
 | 
			
		||||
func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) {
 | 
			
		||||
func (reader *Reader) SeekToFunction(pc RelAddr) (*dwarf.Entry, error) {
 | 
			
		||||
	reader.Seek(0)
 | 
			
		||||
	for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -55,7 +55,7 @@ func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if lowpc <= pc && highpc > pc {
 | 
			
		||||
		if lowpc <= uint64(pc) && highpc > uint64(pc) {
 | 
			
		||||
			return entry, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -64,7 +64,7 @@ func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the address for the named entry.
 | 
			
		||||
func (reader *Reader) AddrFor(name string) (uint64, error) {
 | 
			
		||||
func (reader *Reader) AddrFor(name string, staticBase uint64) (uint64, error) {
 | 
			
		||||
	entry, err := reader.FindEntryNamed(name, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
@@ -73,7 +73,7 @@ func (reader *Reader) AddrFor(name string) (uint64, error) {
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return 0, fmt.Errorf("type assertion failed")
 | 
			
		||||
	}
 | 
			
		||||
	addr, err := op.ExecuteStackProgram(0, instructions)
 | 
			
		||||
	addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{StaticBase: staticBase}, instructions)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -99,7 +99,7 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (
 | 
			
		||||
		if !ok {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...))
 | 
			
		||||
		addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...))
 | 
			
		||||
		return uint64(addr), err
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -257,31 +257,7 @@ func (reader *Reader) InstructionsForEntry(entry *dwarf.Entry) ([]byte, error) {
 | 
			
		||||
	return append([]byte{}, instructions...), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextScopeVariable moves the reader to the next debug entry that describes a local variable and returns the entry.
 | 
			
		||||
func (reader *Reader) NextScopeVariable() (*dwarf.Entry, error) {
 | 
			
		||||
	for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// All scope variables will be at the same depth
 | 
			
		||||
		reader.SkipChildren()
 | 
			
		||||
 | 
			
		||||
		// End of the current depth
 | 
			
		||||
		if entry.Tag == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if entry.Tag == dwarf.TagVariable || entry.Tag == dwarf.TagFormalParameter {
 | 
			
		||||
			return entry, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// No more items
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NextMememberVariable moves the reader to the next debug entry that describes a member variable and returns the entry.
 | 
			
		||||
// NextMemberVariable moves the reader to the next debug entry that describes a member variable and returns the entry.
 | 
			
		||||
func (reader *Reader) NextMemberVariable() (*dwarf.Entry, error) {
 | 
			
		||||
	for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -343,3 +319,128 @@ func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) {
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Entry represents a debug_info entry.
 | 
			
		||||
// When calling Val, if the entry does not have the specified attribute, the
 | 
			
		||||
// entry specified by DW_AT_abstract_origin will be searched recursively.
 | 
			
		||||
type Entry interface {
 | 
			
		||||
	Val(dwarf.Attr) interface{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type compositeEntry []*dwarf.Entry
 | 
			
		||||
 | 
			
		||||
func (ce compositeEntry) Val(attr dwarf.Attr) interface{} {
 | 
			
		||||
	for _, e := range ce {
 | 
			
		||||
		if r := e.Val(attr); r != nil {
 | 
			
		||||
			return r
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadAbstractOrigin loads the entry corresponding to the
 | 
			
		||||
// DW_AT_abstract_origin of entry and returns a combination of entry and its
 | 
			
		||||
// abstract origin.
 | 
			
		||||
func LoadAbstractOrigin(entry *dwarf.Entry, aordr *dwarf.Reader) (Entry, dwarf.Offset) {
 | 
			
		||||
	ao, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return entry, entry.Offset
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := []*dwarf.Entry{entry}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		aordr.Seek(ao)
 | 
			
		||||
		e, _ := aordr.Next()
 | 
			
		||||
		if e == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		r = append(r, e)
 | 
			
		||||
 | 
			
		||||
		ao, ok = e.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return compositeEntry(r), entry.Offset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InlineStackReader provides a way to read the stack of inlined calls at a
 | 
			
		||||
// specified PC address.
 | 
			
		||||
type InlineStackReader struct {
 | 
			
		||||
	dwarf  *dwarf.Data
 | 
			
		||||
	reader *dwarf.Reader
 | 
			
		||||
	entry  *dwarf.Entry
 | 
			
		||||
	depth  int
 | 
			
		||||
	pc     uint64
 | 
			
		||||
	err    error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InlineStack returns an InlineStackReader for the specified function and
 | 
			
		||||
// PC address.
 | 
			
		||||
// If pc is 0 then all inlined calls will be returned.
 | 
			
		||||
func InlineStack(dwarf *dwarf.Data, fnoff dwarf.Offset, pc RelAddr) *InlineStackReader {
 | 
			
		||||
	reader := dwarf.Reader()
 | 
			
		||||
	reader.Seek(fnoff)
 | 
			
		||||
	return &InlineStackReader{dwarf: dwarf, reader: reader, entry: nil, depth: 0, pc: uint64(pc)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next reads next inlined call in the stack, returns false if there aren't any.
 | 
			
		||||
func (irdr *InlineStackReader) Next() bool {
 | 
			
		||||
	if irdr.err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		irdr.entry, irdr.err = irdr.reader.Next()
 | 
			
		||||
		if irdr.entry == nil || irdr.err != nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch irdr.entry.Tag {
 | 
			
		||||
		case 0:
 | 
			
		||||
			irdr.depth--
 | 
			
		||||
			if irdr.depth == 0 {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case dwarf.TagLexDwarfBlock, dwarf.TagSubprogram, dwarf.TagInlinedSubroutine:
 | 
			
		||||
			var recur bool
 | 
			
		||||
			if irdr.pc != 0 {
 | 
			
		||||
				recur, irdr.err = entryRangesContains(irdr.dwarf, irdr.entry, irdr.pc)
 | 
			
		||||
			} else {
 | 
			
		||||
				recur = true
 | 
			
		||||
			}
 | 
			
		||||
			if recur {
 | 
			
		||||
				irdr.depth++
 | 
			
		||||
				if irdr.entry.Tag == dwarf.TagInlinedSubroutine {
 | 
			
		||||
					return true
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				if irdr.depth == 0 {
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
				irdr.reader.SkipChildren()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			irdr.reader.SkipChildren()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Entry returns the DIE for the current inlined call.
 | 
			
		||||
func (irdr *InlineStackReader) Entry() *dwarf.Entry {
 | 
			
		||||
	return irdr.entry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Err returns an error, if any was encountered.
 | 
			
		||||
func (irdr *InlineStackReader) Err() error {
 | 
			
		||||
	return irdr.err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SkipChildren skips all children of the current inlined call.
 | 
			
		||||
func (irdr *InlineStackReader) SkipChildren() {
 | 
			
		||||
	irdr.reader.SkipChildren()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/reader/variables.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/go-delve/delve/pkg/dwarf/reader/variables.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
package reader
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
 | 
			
		||||
	"debug/dwarf"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// RelAddr is an address relative to the static base. For normal executables
 | 
			
		||||
// this is just a normal memory address, for PIE it's a relative address.
 | 
			
		||||
type RelAddr uint64
 | 
			
		||||
 | 
			
		||||
func ToRelAddr(addr uint64, staticBase uint64) RelAddr {
 | 
			
		||||
	return RelAddr(addr - staticBase)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VariableReader provides a way of reading the local variables and formal
 | 
			
		||||
// parameters of a function that are visible at the specified PC address.
 | 
			
		||||
type VariableReader struct {
 | 
			
		||||
	dwarf       *dwarf.Data
 | 
			
		||||
	reader      *dwarf.Reader
 | 
			
		||||
	entry       *dwarf.Entry
 | 
			
		||||
	depth       int
 | 
			
		||||
	onlyVisible bool
 | 
			
		||||
	pc          uint64
 | 
			
		||||
	line        int
 | 
			
		||||
	err         error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Variables returns a VariableReader for the function or lexical block at off.
 | 
			
		||||
// If onlyVisible is true only variables visible at pc will be returned by
 | 
			
		||||
// the VariableReader.
 | 
			
		||||
func Variables(dwarf *dwarf.Data, off dwarf.Offset, pc RelAddr, line int, onlyVisible bool) *VariableReader {
 | 
			
		||||
	reader := dwarf.Reader()
 | 
			
		||||
	reader.Seek(off)
 | 
			
		||||
	return &VariableReader{dwarf: dwarf, reader: reader, entry: nil, depth: 0, onlyVisible: onlyVisible, pc: uint64(pc), line: line, err: nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next reads the next variable entry, returns false if there aren't any.
 | 
			
		||||
func (vrdr *VariableReader) Next() bool {
 | 
			
		||||
	if vrdr.err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		vrdr.entry, vrdr.err = vrdr.reader.Next()
 | 
			
		||||
		if vrdr.entry == nil || vrdr.err != nil {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch vrdr.entry.Tag {
 | 
			
		||||
		case 0:
 | 
			
		||||
			vrdr.depth--
 | 
			
		||||
			if vrdr.depth == 0 {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case dwarf.TagLexDwarfBlock, dwarf.TagSubprogram, dwarf.TagInlinedSubroutine:
 | 
			
		||||
			recur := true
 | 
			
		||||
			if vrdr.onlyVisible {
 | 
			
		||||
				recur, vrdr.err = entryRangesContains(vrdr.dwarf, vrdr.entry, vrdr.pc)
 | 
			
		||||
				if vrdr.err != nil {
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if recur && vrdr.entry.Children {
 | 
			
		||||
				vrdr.depth++
 | 
			
		||||
			} else {
 | 
			
		||||
				if vrdr.depth == 0 {
 | 
			
		||||
					return false
 | 
			
		||||
				}
 | 
			
		||||
				vrdr.reader.SkipChildren()
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			if vrdr.depth == 0 {
 | 
			
		||||
				vrdr.err = errors.New("offset was not lexical block or subprogram")
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
			if declLine, ok := vrdr.entry.Val(dwarf.AttrDeclLine).(int64); !ok || vrdr.line >= int(declLine) {
 | 
			
		||||
				return true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func entryRangesContains(dwarf *dwarf.Data, entry *dwarf.Entry, pc uint64) (bool, error) {
 | 
			
		||||
	rngs, err := dwarf.Ranges(entry)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	for _, rng := range rngs {
 | 
			
		||||
		if pc >= rng[0] && pc < rng[1] {
 | 
			
		||||
			return true, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Entry returns the current variable entry.
 | 
			
		||||
func (vrdr *VariableReader) Entry() *dwarf.Entry {
 | 
			
		||||
	return vrdr.entry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Depth returns the depth of the current scope
 | 
			
		||||
func (vrdr *VariableReader) Depth() int {
 | 
			
		||||
	return vrdr.depth
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Err returns the error if there was one.
 | 
			
		||||
func (vrdr *VariableReader) Err() error {
 | 
			
		||||
	return vrdr.err
 | 
			
		||||
}
 | 
			
		||||
@@ -4,23 +4,21 @@
 | 
			
		||||
 | 
			
		||||
// Buffered reading and decoding of DWARF data streams.
 | 
			
		||||
 | 
			
		||||
package dwarf
 | 
			
		||||
package util
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"debug/dwarf"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Data buffer being decoded.
 | 
			
		||||
type buf struct {
 | 
			
		||||
	dwarf  *Data
 | 
			
		||||
	order  binary.ByteOrder
 | 
			
		||||
	dwarf  *dwarf.Data
 | 
			
		||||
	format dataFormat
 | 
			
		||||
	name   string
 | 
			
		||||
	off    Offset
 | 
			
		||||
	off    dwarf.Offset
 | 
			
		||||
	data   []byte
 | 
			
		||||
	err    error
 | 
			
		||||
	Err    error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Data format, other than byte order.  This affects the handling of
 | 
			
		||||
@@ -37,22 +35,22 @@ type dataFormat interface {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Some parts of DWARF have no data format, e.g., abbrevs.
 | 
			
		||||
type unknownFormat struct{}
 | 
			
		||||
type UnknownFormat struct{}
 | 
			
		||||
 | 
			
		||||
func (u unknownFormat) version() int {
 | 
			
		||||
func (u UnknownFormat) version() int {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u unknownFormat) dwarf64() (bool, bool) {
 | 
			
		||||
func (u UnknownFormat) dwarf64() (bool, bool) {
 | 
			
		||||
	return false, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (u unknownFormat) addrsize() int {
 | 
			
		||||
func (u UnknownFormat) addrsize() int {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeBuf(d *Data, format dataFormat, name string, off Offset, data []byte) buf {
 | 
			
		||||
	return buf{d, d.order, format, name, off, data, nil}
 | 
			
		||||
func MakeBuf(d *dwarf.Data, format dataFormat, name string, off dwarf.Offset, data []byte) buf {
 | 
			
		||||
	return buf{d, format, name, off, data, nil}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *buf) slice(length int) buf {
 | 
			
		||||
@@ -63,7 +61,7 @@ func (b *buf) slice(length int) buf {
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *buf) uint8() uint8 {
 | 
			
		||||
func (b *buf) Uint8() uint8 {
 | 
			
		||||
	if len(b.data) < 1 {
 | 
			
		||||
		b.error("underflow")
 | 
			
		||||
		return 0
 | 
			
		||||
@@ -81,7 +79,7 @@ func (b *buf) bytes(n int) []byte {
 | 
			
		||||
	}
 | 
			
		||||
	data := b.data[0:n]
 | 
			
		||||
	b.data = b.data[n:]
 | 
			
		||||
	b.off += Offset(n)
 | 
			
		||||
	b.off += dwarf.Offset(n)
 | 
			
		||||
	return data
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -94,7 +92,7 @@ func (b *buf) string() string {
 | 
			
		||||
		if b.data[i] == 0 {
 | 
			
		||||
			s := string(b.data[0:i])
 | 
			
		||||
			b.data = b.data[i+1:]
 | 
			
		||||
			b.off += Offset(i + 1)
 | 
			
		||||
			b.off += dwarf.Offset(i + 1)
 | 
			
		||||
			return s
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -102,39 +100,15 @@ func (b *buf) string() string {
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *buf) uint16() uint16 {
 | 
			
		||||
	a := b.bytes(2)
 | 
			
		||||
	if a == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	return b.order.Uint16(a)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *buf) uint32() uint32 {
 | 
			
		||||
	a := b.bytes(4)
 | 
			
		||||
	if a == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	return b.order.Uint32(a)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *buf) uint64() uint64 {
 | 
			
		||||
	a := b.bytes(8)
 | 
			
		||||
	if a == nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	return b.order.Uint64(a)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read a varint, which is 7 bits per byte, little endian.
 | 
			
		||||
// the 0x80 bit means read another byte.
 | 
			
		||||
func (b *buf) varint() (c uint64, bits uint) {
 | 
			
		||||
func (b *buf) Varint() (c uint64, bits uint) {
 | 
			
		||||
	for i := 0; i < len(b.data); i++ {
 | 
			
		||||
		byte := b.data[i]
 | 
			
		||||
		c |= uint64(byte&0x7F) << bits
 | 
			
		||||
		bits += 7
 | 
			
		||||
		if byte&0x80 == 0 {
 | 
			
		||||
			b.off += Offset(i + 1)
 | 
			
		||||
			b.off += dwarf.Offset(i + 1)
 | 
			
		||||
			b.data = b.data[i+1:]
 | 
			
		||||
			return c, bits
 | 
			
		||||
		}
 | 
			
		||||
@@ -143,14 +117,14 @@ func (b *buf) varint() (c uint64, bits uint) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Unsigned int is just a varint.
 | 
			
		||||
func (b *buf) uint() uint64 {
 | 
			
		||||
	x, _ := b.varint()
 | 
			
		||||
func (b *buf) Uint() uint64 {
 | 
			
		||||
	x, _ := b.Varint()
 | 
			
		||||
	return x
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Signed int is a sign-extended varint.
 | 
			
		||||
func (b *buf) int() int64 {
 | 
			
		||||
	ux, bits := b.varint()
 | 
			
		||||
func (b *buf) Int() int64 {
 | 
			
		||||
	ux, bits := b.Varint()
 | 
			
		||||
	x := int64(ux)
 | 
			
		||||
	if x&(1<<(bits-1)) != 0 {
 | 
			
		||||
		x |= -1 << bits
 | 
			
		||||
@@ -158,24 +132,8 @@ func (b *buf) int() int64 {
 | 
			
		||||
	return x
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Address-sized uint.
 | 
			
		||||
func (b *buf) addr() uint64 {
 | 
			
		||||
	switch b.format.addrsize() {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return uint64(b.uint8())
 | 
			
		||||
	case 2:
 | 
			
		||||
		return uint64(b.uint16())
 | 
			
		||||
	case 4:
 | 
			
		||||
		return uint64(b.uint32())
 | 
			
		||||
	case 8:
 | 
			
		||||
		return uint64(b.uint64())
 | 
			
		||||
	}
 | 
			
		||||
	b.error("unknown address size")
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// assertEmpty checks that everything has been read from b.
 | 
			
		||||
func (b *buf) assertEmpty() {
 | 
			
		||||
// AssertEmpty checks that everything has been read from b.
 | 
			
		||||
func (b *buf) AssertEmpty() {
 | 
			
		||||
	if len(b.data) == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
@@ -186,18 +144,8 @@ func (b *buf) assertEmpty() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (b *buf) error(s string) {
 | 
			
		||||
	if b.err == nil {
 | 
			
		||||
	if b.Err == nil {
 | 
			
		||||
		b.data = nil
 | 
			
		||||
		b.err = DecodeError{b.name, b.off, s}
 | 
			
		||||
		b.Err = dwarf.DecodeError{b.name, b.off, s}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type DecodeError struct {
 | 
			
		||||
	Name   string
 | 
			
		||||
	Offset Offset
 | 
			
		||||
	Err    string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e DecodeError) Error() string {
 | 
			
		||||
	return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.FormatInt(int64(e.Offset), 16) + ": " + e.Err
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,12 @@
 | 
			
		||||
package util
 | 
			
		||||
 | 
			
		||||
import "bytes"
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"io"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// The Little Endian Base 128 format is defined in the DWARF v4 standard,
 | 
			
		||||
// section 7.6, page 161 and following.
 | 
			
		||||
 | 
			
		||||
// DecodeULEB128 decodes an unsigned Little Endian Base 128
 | 
			
		||||
// represented number.
 | 
			
		||||
@@ -71,6 +77,45 @@ func DecodeSLEB128(buf *bytes.Buffer) (int64, uint32) {
 | 
			
		||||
	return result, length
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeULEB128 encodes x to the unsigned Little Endian Base 128 format
 | 
			
		||||
// into out.
 | 
			
		||||
func EncodeULEB128(out io.ByteWriter, x uint64) {
 | 
			
		||||
	for {
 | 
			
		||||
		b := byte(x & 0x7f)
 | 
			
		||||
		x = x >> 7
 | 
			
		||||
		if x != 0 {
 | 
			
		||||
			b = b | 0x80
 | 
			
		||||
		}
 | 
			
		||||
		out.WriteByte(b)
 | 
			
		||||
		if x == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EncodeSLEB128 encodes x to the signed Little Endian Base 128 format
 | 
			
		||||
// into out.
 | 
			
		||||
func EncodeSLEB128(out io.ByteWriter, x int64) {
 | 
			
		||||
	for {
 | 
			
		||||
		b := byte(x & 0x7f)
 | 
			
		||||
		x >>= 7
 | 
			
		||||
 | 
			
		||||
		signb := b & 0x40
 | 
			
		||||
 | 
			
		||||
		last := false
 | 
			
		||||
		if (x == 0 && signb == 0) || (x == -1 && signb != 0) {
 | 
			
		||||
			last = true
 | 
			
		||||
		} else {
 | 
			
		||||
			b = b | 0x80
 | 
			
		||||
		}
 | 
			
		||||
		out.WriteByte(b)
 | 
			
		||||
 | 
			
		||||
		if last {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ParseString(data *bytes.Buffer) (string, uint32) {
 | 
			
		||||
	str, err := data.ReadString(0x0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
							
								
								
									
										178
									
								
								vendor/github.com/go-delve/delve/pkg/goversion/go_version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										178
									
								
								vendor/github.com/go-delve/delve/pkg/goversion/go_version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,178 @@
 | 
			
		||||
package goversion
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// GoVersion represents the Go version of
 | 
			
		||||
// the Go compiler version used to compile
 | 
			
		||||
// the target binary.
 | 
			
		||||
type GoVersion struct {
 | 
			
		||||
	Major    int
 | 
			
		||||
	Minor    int
 | 
			
		||||
	Rev      int
 | 
			
		||||
	Beta     int
 | 
			
		||||
	RC       int
 | 
			
		||||
	Proposal string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	GoVer18Beta = GoVersion{1, 8, -1, 0, 0, ""}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Parse parses a go version string
 | 
			
		||||
func Parse(ver string) (GoVersion, bool) {
 | 
			
		||||
	var r GoVersion
 | 
			
		||||
	var err1, err2, err3 error
 | 
			
		||||
 | 
			
		||||
	if strings.HasPrefix(ver, "devel") {
 | 
			
		||||
		return GoVersion{-1, 0, 0, 0, 0, ""}, true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.HasPrefix(ver, "go") {
 | 
			
		||||
		ver := strings.Split(ver, " ")[0]
 | 
			
		||||
		v := strings.SplitN(ver[2:], ".", 4)
 | 
			
		||||
		switch len(v) {
 | 
			
		||||
		case 2:
 | 
			
		||||
			r.Major, err1 = strconv.Atoi(v[0])
 | 
			
		||||
			vr := strings.SplitN(v[1], "beta", 2)
 | 
			
		||||
			if len(vr) == 2 {
 | 
			
		||||
				r.Beta, err3 = strconv.Atoi(vr[1])
 | 
			
		||||
			} else {
 | 
			
		||||
				vr = strings.SplitN(v[1], "rc", 2)
 | 
			
		||||
				if len(vr) == 2 {
 | 
			
		||||
					r.RC, err3 = strconv.Atoi(vr[1])
 | 
			
		||||
				} else {
 | 
			
		||||
					r.Minor, err2 = strconv.Atoi(v[1])
 | 
			
		||||
					if err2 != nil {
 | 
			
		||||
						return GoVersion{}, false
 | 
			
		||||
					}
 | 
			
		||||
					return r, true
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			r.Minor, err2 = strconv.Atoi(vr[0])
 | 
			
		||||
			r.Rev = -1
 | 
			
		||||
			r.Proposal = ""
 | 
			
		||||
 | 
			
		||||
			if err1 != nil || err2 != nil || err3 != nil {
 | 
			
		||||
				return GoVersion{}, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return r, true
 | 
			
		||||
 | 
			
		||||
		case 3:
 | 
			
		||||
 | 
			
		||||
			r.Major, err1 = strconv.Atoi(v[0])
 | 
			
		||||
			r.Minor, err2 = strconv.Atoi(v[1])
 | 
			
		||||
			r.Rev, err3 = strconv.Atoi(v[2])
 | 
			
		||||
			r.Proposal = ""
 | 
			
		||||
			if err1 != nil || err2 != nil || err3 != nil {
 | 
			
		||||
				return GoVersion{}, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return r, true
 | 
			
		||||
 | 
			
		||||
		case 4:
 | 
			
		||||
 | 
			
		||||
			r.Major, err1 = strconv.Atoi(v[0])
 | 
			
		||||
			r.Minor, err2 = strconv.Atoi(v[1])
 | 
			
		||||
			r.Rev, err3 = strconv.Atoi(v[2])
 | 
			
		||||
			r.Proposal = v[3]
 | 
			
		||||
			if err1 != nil || err2 != nil || err3 != nil || r.Proposal == "" {
 | 
			
		||||
				return GoVersion{}, false
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return r, true
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return GoVersion{}, false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return GoVersion{}, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AfterOrEqual returns whether one GoVersion is after or
 | 
			
		||||
// equal to the other.
 | 
			
		||||
func (v *GoVersion) AfterOrEqual(b GoVersion) bool {
 | 
			
		||||
	if v.Major < b.Major {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if v.Major > b.Major {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Minor < b.Minor {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if v.Minor > b.Minor {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Rev < b.Rev {
 | 
			
		||||
		return false
 | 
			
		||||
	} else if v.Rev > b.Rev {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.Beta < b.Beta {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if v.RC < b.RC {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsDevel returns whether the GoVersion
 | 
			
		||||
// is a development version.
 | 
			
		||||
func (v *GoVersion) IsDevel() bool {
 | 
			
		||||
	return v.Major < 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const goVersionPrefix = "go version "
 | 
			
		||||
 | 
			
		||||
// Installed runs "go version" and parses the output
 | 
			
		||||
func Installed() (GoVersion, bool) {
 | 
			
		||||
	out, err := exec.Command("go", "version").CombinedOutput()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return GoVersion{}, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	s := string(out)
 | 
			
		||||
 | 
			
		||||
	if !strings.HasPrefix(s, goVersionPrefix) {
 | 
			
		||||
		return GoVersion{}, false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return Parse(s[len(goVersionPrefix):])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VersionAfterOrEqual checks that version (as returned by runtime.Version()
 | 
			
		||||
// or go version) is major.minor or a later version, or a development
 | 
			
		||||
// version.
 | 
			
		||||
func VersionAfterOrEqual(version string, major, minor int) bool {
 | 
			
		||||
	ver, _ := Parse(version)
 | 
			
		||||
	if ver.IsDevel() {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return ver.AfterOrEqual(GoVersion{major, minor, -1, 0, 0, ""})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const producerVersionPrefix = "Go cmd/compile "
 | 
			
		||||
 | 
			
		||||
// ProducerAfterOrEqual checks that the DW_AT_producer version is
 | 
			
		||||
// major.minor or a later version, or a development version.
 | 
			
		||||
func ProducerAfterOrEqual(producer string, major, minor int) bool {
 | 
			
		||||
	if strings.HasPrefix(producer, producerVersionPrefix) {
 | 
			
		||||
		producer = producer[len(producerVersionPrefix):]
 | 
			
		||||
	}
 | 
			
		||||
	ver, _ := Parse(producer)
 | 
			
		||||
	if ver.IsDevel() {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	return ver.AfterOrEqual(GoVersion{major, minor, -1, 0, 0, ""})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										227
									
								
								vendor/github.com/go-delve/delve/pkg/logflags/logflags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								vendor/github.com/go-delve/delve/pkg/logflags/logflags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
package logflags
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var debugger = false
 | 
			
		||||
var gdbWire = false
 | 
			
		||||
var lldbServerOutput = false
 | 
			
		||||
var debugLineErrors = false
 | 
			
		||||
var rpc = false
 | 
			
		||||
var fnCall = false
 | 
			
		||||
var minidump = false
 | 
			
		||||
 | 
			
		||||
var logOut io.WriteCloser
 | 
			
		||||
 | 
			
		||||
func makeLogger(flag bool, fields logrus.Fields) *logrus.Entry {
 | 
			
		||||
	logger := logrus.New().WithFields(fields)
 | 
			
		||||
	logger.Logger.Formatter = &textFormatter{}
 | 
			
		||||
	if logOut != nil {
 | 
			
		||||
		logger.Logger.Out = logOut
 | 
			
		||||
	}
 | 
			
		||||
	logger.Logger.Level = logrus.DebugLevel
 | 
			
		||||
	if !flag {
 | 
			
		||||
		logger.Logger.Level = logrus.PanicLevel
 | 
			
		||||
	}
 | 
			
		||||
	return logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GdbWire returns true if the gdbserial package should log all the packets
 | 
			
		||||
// exchanged with the stub.
 | 
			
		||||
func GdbWire() bool {
 | 
			
		||||
	return gdbWire
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GdbWireLogger returns a configured logger for the gdbserial wire protocol.
 | 
			
		||||
func GdbWireLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(gdbWire, logrus.Fields{"layer": "gdbconn"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debugger returns true if the debugger package should log.
 | 
			
		||||
func Debugger() bool {
 | 
			
		||||
	return debugger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DebuggerLogger returns a logger for the debugger package.
 | 
			
		||||
func DebuggerLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(debugger, logrus.Fields{"layer": "debugger"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LLDBServerOutput returns true if the output of the LLDB server should be
 | 
			
		||||
// redirected to standard output instead of suppressed.
 | 
			
		||||
func LLDBServerOutput() bool {
 | 
			
		||||
	return lldbServerOutput
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DebugLineErrors returns true if pkg/dwarf/line should log its recoverable
 | 
			
		||||
// errors.
 | 
			
		||||
func DebugLineErrors() bool {
 | 
			
		||||
	return debugLineErrors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RPC returns true if RPC messages should be logged.
 | 
			
		||||
func RPC() bool {
 | 
			
		||||
	return rpc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RPCLogger returns a logger for RPC messages.
 | 
			
		||||
func RPCLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(rpc, logrus.Fields{"layer": "rpc"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FnCall returns true if the function call protocol should be logged.
 | 
			
		||||
func FnCall() bool {
 | 
			
		||||
	return fnCall
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FnCallLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(fnCall, logrus.Fields{"layer": "proc", "kind": "fncall"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Minidump returns true if the minidump loader should be logged.
 | 
			
		||||
func Minidump() bool {
 | 
			
		||||
	return minidump
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func MinidumpLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(minidump, logrus.Fields{"layer": "core", "kind": "minidump"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteAPIListeningMessage writes the "API server listening" message in headless mode.
 | 
			
		||||
func WriteAPIListeningMessage(addr string) {
 | 
			
		||||
	if logOut != nil {
 | 
			
		||||
		fmt.Fprintf(logOut, "API server listening at: %s\n", addr)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Printf("API server listening at: %s\n", addr)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errLogstrWithoutLog = errors.New("--log-output specified without --log")
 | 
			
		||||
 | 
			
		||||
// Setup sets debugger flags based on the contents of logstr.
 | 
			
		||||
// If logDest is not empty logs will be redirected to the file descriptor or
 | 
			
		||||
// file path specified by logDest.
 | 
			
		||||
func Setup(logFlag bool, logstr string, logDest string) error {
 | 
			
		||||
	if logDest != "" {
 | 
			
		||||
		n, err := strconv.Atoi(logDest)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			logOut = os.NewFile(uintptr(n), "delve-logs")
 | 
			
		||||
		} else {
 | 
			
		||||
			fh, err := os.Create(logDest)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("could not create log file: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			logOut = fh
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
 | 
			
		||||
	if !logFlag {
 | 
			
		||||
		log.SetOutput(ioutil.Discard)
 | 
			
		||||
		if logstr != "" {
 | 
			
		||||
			return errLogstrWithoutLog
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if logstr == "" {
 | 
			
		||||
		logstr = "debugger"
 | 
			
		||||
	}
 | 
			
		||||
	v := strings.Split(logstr, ",")
 | 
			
		||||
	for _, logcmd := range v {
 | 
			
		||||
		switch logcmd {
 | 
			
		||||
		case "debugger":
 | 
			
		||||
			debugger = true
 | 
			
		||||
		case "gdbwire":
 | 
			
		||||
			gdbWire = true
 | 
			
		||||
		case "lldbout":
 | 
			
		||||
			lldbServerOutput = true
 | 
			
		||||
		case "debuglineerr":
 | 
			
		||||
			debugLineErrors = true
 | 
			
		||||
		case "rpc":
 | 
			
		||||
			rpc = true
 | 
			
		||||
		case "fncall":
 | 
			
		||||
			fnCall = true
 | 
			
		||||
		case "minidump":
 | 
			
		||||
			minidump = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the logger output.
 | 
			
		||||
func Close() {
 | 
			
		||||
	if logOut != nil {
 | 
			
		||||
		logOut.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// textFormatter is a simplified version of logrus.TextFormatter that
 | 
			
		||||
// doesn't make logs unreadable when they are output to a text file or to a
 | 
			
		||||
// terminal that doesn't support colors.
 | 
			
		||||
type textFormatter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
 | 
			
		||||
	var b *bytes.Buffer
 | 
			
		||||
	if entry.Buffer != nil {
 | 
			
		||||
		b = entry.Buffer
 | 
			
		||||
	} else {
 | 
			
		||||
		b = &bytes.Buffer{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	keys := make([]string, 0, len(entry.Data))
 | 
			
		||||
	for k := range entry.Data {
 | 
			
		||||
		keys = append(keys, k)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
 | 
			
		||||
	b.WriteString(entry.Time.Format(time.RFC3339))
 | 
			
		||||
	b.WriteByte(' ')
 | 
			
		||||
	b.WriteString(entry.Level.String())
 | 
			
		||||
	b.WriteByte(' ')
 | 
			
		||||
	for i, key := range keys {
 | 
			
		||||
		b.WriteString(key)
 | 
			
		||||
		b.WriteByte('=')
 | 
			
		||||
		stringVal, ok := entry.Data[key].(string)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			stringVal = fmt.Sprint(entry.Data[key])
 | 
			
		||||
		}
 | 
			
		||||
		if f.needsQuoting(stringVal) {
 | 
			
		||||
			fmt.Fprintf(b, "%q", stringVal)
 | 
			
		||||
		} else {
 | 
			
		||||
			b.WriteString(stringVal)
 | 
			
		||||
		}
 | 
			
		||||
		if i != len(keys)-1 {
 | 
			
		||||
			b.WriteByte(',')
 | 
			
		||||
		} else {
 | 
			
		||||
			b.WriteByte(' ')
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	b.WriteString(entry.Message)
 | 
			
		||||
	b.WriteByte('\n')
 | 
			
		||||
	return b.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *textFormatter) needsQuoting(text string) bool {
 | 
			
		||||
	for _, ch := range text {
 | 
			
		||||
		if !((ch >= 'a' && ch <= 'z') ||
 | 
			
		||||
			(ch >= 'A' && ch <= 'Z') ||
 | 
			
		||||
			(ch >= '0' && ch <= '9') ||
 | 
			
		||||
			ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										306
									
								
								vendor/github.com/go-delve/delve/pkg/proc/arch.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								vendor/github.com/go-delve/delve/pkg/proc/arch.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,306 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Arch defines an interface for representing a
 | 
			
		||||
// CPU architecture.
 | 
			
		||||
type Arch interface {
 | 
			
		||||
	PtrSize() int
 | 
			
		||||
	BreakpointInstruction() []byte
 | 
			
		||||
	BreakpointSize() int
 | 
			
		||||
	DerefTLS() bool
 | 
			
		||||
	FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext
 | 
			
		||||
	RegSize(uint64) int
 | 
			
		||||
	RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters
 | 
			
		||||
	GoroutineToDwarfRegisters(*G) op.DwarfRegisters
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64 represents the AMD64 CPU architecture.
 | 
			
		||||
type AMD64 struct {
 | 
			
		||||
	ptrSize                 int
 | 
			
		||||
	breakInstruction        []byte
 | 
			
		||||
	breakInstructionLen     int
 | 
			
		||||
	gStructOffset           uint64
 | 
			
		||||
	hardwareBreakpointUsage []bool
 | 
			
		||||
	goos                    string
 | 
			
		||||
 | 
			
		||||
	// crosscall2fn is the DIE of crosscall2, a function used by the go runtime
 | 
			
		||||
	// to call C functions. This function in go 1.9 (and previous versions) had
 | 
			
		||||
	// a bad frame descriptor which needs to be fixed to generate good stack
 | 
			
		||||
	// traces.
 | 
			
		||||
	crosscall2fn *Function
 | 
			
		||||
 | 
			
		||||
	// sigreturnfn is the DIE of runtime.sigreturn, the return trampoline for
 | 
			
		||||
	// the signal handler. See comment in FixFrameUnwindContext for a
 | 
			
		||||
	// description of why this is needed.
 | 
			
		||||
	sigreturnfn *Function
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	amd64DwarfIPRegNum uint64 = 16
 | 
			
		||||
	amd64DwarfSPRegNum uint64 = 7
 | 
			
		||||
	amd64DwarfBPRegNum uint64 = 6
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AMD64Arch returns an initialized AMD64
 | 
			
		||||
// struct.
 | 
			
		||||
func AMD64Arch(goos string) *AMD64 {
 | 
			
		||||
	var breakInstr = []byte{0xCC}
 | 
			
		||||
 | 
			
		||||
	return &AMD64{
 | 
			
		||||
		ptrSize:                 8,
 | 
			
		||||
		breakInstruction:        breakInstr,
 | 
			
		||||
		breakInstructionLen:     len(breakInstr),
 | 
			
		||||
		hardwareBreakpointUsage: make([]bool, 4),
 | 
			
		||||
		goos:                    goos,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PtrSize returns the size of a pointer
 | 
			
		||||
// on this architecture.
 | 
			
		||||
func (a *AMD64) PtrSize() int {
 | 
			
		||||
	return a.ptrSize
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointInstruction returns the Breakpoint
 | 
			
		||||
// instruction for this architecture.
 | 
			
		||||
func (a *AMD64) BreakpointInstruction() []byte {
 | 
			
		||||
	return a.breakInstruction
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointSize returns the size of the
 | 
			
		||||
// breakpoint instruction on this architecture.
 | 
			
		||||
func (a *AMD64) BreakpointSize() int {
 | 
			
		||||
	return a.breakInstructionLen
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DerefTLS returns true if the value of regs.TLS()+GStructOffset() is a
 | 
			
		||||
// pointer to the G struct
 | 
			
		||||
func (a *AMD64) DerefTLS() bool {
 | 
			
		||||
	return a.goos == "windows"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	crosscall2SPOffsetBad        = 0x8
 | 
			
		||||
	crosscall2SPOffsetWindows    = 0x118
 | 
			
		||||
	crosscall2SPOffsetNonWindows = 0x58
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FixFrameUnwindContext adds default architecture rules to fctxt or returns
 | 
			
		||||
// the default frame unwind context if fctxt is nil.
 | 
			
		||||
func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext {
 | 
			
		||||
	if a.sigreturnfn == nil {
 | 
			
		||||
		a.sigreturnfn = bi.LookupFunc["runtime.sigreturn"]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if fctxt == nil || (a.sigreturnfn != nil && pc >= a.sigreturnfn.Entry && pc < a.sigreturnfn.End) {
 | 
			
		||||
		//if true {
 | 
			
		||||
		// When there's no frame descriptor entry use BP (the frame pointer) instead
 | 
			
		||||
		// - return register is [bp + a.PtrSize()] (i.e. [cfa-a.PtrSize()])
 | 
			
		||||
		// - cfa is bp + a.PtrSize()*2
 | 
			
		||||
		// - bp is [bp] (i.e. [cfa-a.PtrSize()*2])
 | 
			
		||||
		// - sp is cfa
 | 
			
		||||
 | 
			
		||||
		// When the signal handler runs it will move the execution to the signal
 | 
			
		||||
		// handling stack (installed using the sigaltstack system call).
 | 
			
		||||
		// This isn't a proper stack switch: the pointer to g in TLS will still
 | 
			
		||||
		// refer to whatever g was executing on that thread before the signal was
 | 
			
		||||
		// received.
 | 
			
		||||
		// Since go did not execute a stack switch the previous value of sp, pc
 | 
			
		||||
		// and bp is not saved inside g.sched, as it normally would.
 | 
			
		||||
		// The only way to recover is to either read sp/pc from the signal context
 | 
			
		||||
		// parameter (the ucontext_t* parameter) or to unconditionally follow the
 | 
			
		||||
		// frame pointer when we get to runtime.sigreturn (which is what we do
 | 
			
		||||
		// here).
 | 
			
		||||
 | 
			
		||||
		return &frame.FrameContext{
 | 
			
		||||
			RetAddrReg: amd64DwarfIPRegNum,
 | 
			
		||||
			Regs: map[uint64]frame.DWRule{
 | 
			
		||||
				amd64DwarfIPRegNum: frame.DWRule{
 | 
			
		||||
					Rule:   frame.RuleOffset,
 | 
			
		||||
					Offset: int64(-a.PtrSize()),
 | 
			
		||||
				},
 | 
			
		||||
				amd64DwarfBPRegNum: frame.DWRule{
 | 
			
		||||
					Rule:   frame.RuleOffset,
 | 
			
		||||
					Offset: int64(-2 * a.PtrSize()),
 | 
			
		||||
				},
 | 
			
		||||
				amd64DwarfSPRegNum: frame.DWRule{
 | 
			
		||||
					Rule:   frame.RuleValOffset,
 | 
			
		||||
					Offset: 0,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			CFA: frame.DWRule{
 | 
			
		||||
				Rule:   frame.RuleCFA,
 | 
			
		||||
				Reg:    amd64DwarfBPRegNum,
 | 
			
		||||
				Offset: int64(2 * a.PtrSize()),
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.crosscall2fn == nil {
 | 
			
		||||
		a.crosscall2fn = bi.LookupFunc["crosscall2"]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End {
 | 
			
		||||
		rule := fctxt.CFA
 | 
			
		||||
		if rule.Offset == crosscall2SPOffsetBad {
 | 
			
		||||
			switch a.goos {
 | 
			
		||||
			case "windows":
 | 
			
		||||
				rule.Offset += crosscall2SPOffsetWindows
 | 
			
		||||
			default:
 | 
			
		||||
				rule.Offset += crosscall2SPOffsetNonWindows
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		fctxt.CFA = rule
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// We assume that RBP is the frame pointer and we want to keep it updated,
 | 
			
		||||
	// so that we can use it to unwind the stack even when we encounter frames
 | 
			
		||||
	// without descriptor entries.
 | 
			
		||||
	// If there isn't a rule already we emit one.
 | 
			
		||||
	if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined {
 | 
			
		||||
		fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{
 | 
			
		||||
			Rule:   frame.RuleFramePointer,
 | 
			
		||||
			Reg:    amd64DwarfBPRegNum,
 | 
			
		||||
			Offset: 0,
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fctxt
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegSize returns the size (in bytes) of register regnum.
 | 
			
		||||
// The mapping between hardware registers and DWARF registers is specified
 | 
			
		||||
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
 | 
			
		||||
// figure 3.36
 | 
			
		||||
// https://www.uclibc.org/docs/psABI-x86_64.pdf
 | 
			
		||||
func (a *AMD64) RegSize(regnum uint64) int {
 | 
			
		||||
	// XMM registers
 | 
			
		||||
	if regnum > amd64DwarfIPRegNum && regnum <= 32 {
 | 
			
		||||
		return 16
 | 
			
		||||
	}
 | 
			
		||||
	// x87 registers
 | 
			
		||||
	if regnum >= 33 && regnum <= 40 {
 | 
			
		||||
		return 10
 | 
			
		||||
	}
 | 
			
		||||
	return 8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The mapping between hardware registers and DWARF registers is specified
 | 
			
		||||
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
 | 
			
		||||
// figure 3.36
 | 
			
		||||
// https://www.uclibc.org/docs/psABI-x86_64.pdf
 | 
			
		||||
 | 
			
		||||
var asm64DwarfToHardware = map[int]x86asm.Reg{
 | 
			
		||||
	0:  x86asm.RAX,
 | 
			
		||||
	1:  x86asm.RDX,
 | 
			
		||||
	2:  x86asm.RCX,
 | 
			
		||||
	3:  x86asm.RBX,
 | 
			
		||||
	4:  x86asm.RSI,
 | 
			
		||||
	5:  x86asm.RDI,
 | 
			
		||||
	8:  x86asm.R8,
 | 
			
		||||
	9:  x86asm.R9,
 | 
			
		||||
	10: x86asm.R10,
 | 
			
		||||
	11: x86asm.R11,
 | 
			
		||||
	12: x86asm.R12,
 | 
			
		||||
	13: x86asm.R13,
 | 
			
		||||
	14: x86asm.R14,
 | 
			
		||||
	15: x86asm.R15,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var amd64DwarfToName = map[int]string{
 | 
			
		||||
	17: "XMM0",
 | 
			
		||||
	18: "XMM1",
 | 
			
		||||
	19: "XMM2",
 | 
			
		||||
	20: "XMM3",
 | 
			
		||||
	21: "XMM4",
 | 
			
		||||
	22: "XMM5",
 | 
			
		||||
	23: "XMM6",
 | 
			
		||||
	24: "XMM7",
 | 
			
		||||
	25: "XMM8",
 | 
			
		||||
	26: "XMM9",
 | 
			
		||||
	27: "XMM10",
 | 
			
		||||
	28: "XMM11",
 | 
			
		||||
	29: "XMM12",
 | 
			
		||||
	30: "XMM13",
 | 
			
		||||
	31: "XMM14",
 | 
			
		||||
	32: "XMM15",
 | 
			
		||||
	33: "ST(0)",
 | 
			
		||||
	34: "ST(1)",
 | 
			
		||||
	35: "ST(2)",
 | 
			
		||||
	36: "ST(3)",
 | 
			
		||||
	37: "ST(4)",
 | 
			
		||||
	38: "ST(5)",
 | 
			
		||||
	39: "ST(6)",
 | 
			
		||||
	40: "ST(7)",
 | 
			
		||||
	49: "Eflags",
 | 
			
		||||
	50: "Es",
 | 
			
		||||
	51: "Cs",
 | 
			
		||||
	52: "Ss",
 | 
			
		||||
	53: "Ds",
 | 
			
		||||
	54: "Fs",
 | 
			
		||||
	55: "Gs",
 | 
			
		||||
	58: "Fs_base",
 | 
			
		||||
	59: "Gs_base",
 | 
			
		||||
	64: "MXCSR",
 | 
			
		||||
	65: "CW",
 | 
			
		||||
	66: "SW",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func maxAmd64DwarfRegister() int {
 | 
			
		||||
	max := int(amd64DwarfIPRegNum)
 | 
			
		||||
	for i := range asm64DwarfToHardware {
 | 
			
		||||
		if i > max {
 | 
			
		||||
			max = i
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	for i := range amd64DwarfToName {
 | 
			
		||||
		if i > max {
 | 
			
		||||
			max = i
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return max
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RegistersToDwarfRegisters converts hardware registers to the format used
 | 
			
		||||
// by the DWARF expression interpreter.
 | 
			
		||||
func (a *AMD64) RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters {
 | 
			
		||||
	dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1)
 | 
			
		||||
 | 
			
		||||
	dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
 | 
			
		||||
	dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP())
 | 
			
		||||
	dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP())
 | 
			
		||||
 | 
			
		||||
	for dwarfReg, asmReg := range asm64DwarfToHardware {
 | 
			
		||||
		v, err := regs.Get(int(asmReg))
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			dregs[dwarfReg] = op.DwarfRegisterFromUint64(v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, reg := range regs.Slice(true) {
 | 
			
		||||
		for dwarfReg, regName := range amd64DwarfToName {
 | 
			
		||||
			if regName == reg.Name {
 | 
			
		||||
				dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return op.DwarfRegisters{StaticBase: staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked
 | 
			
		||||
// goroutine in the format used by the DWARF expression interpreter.
 | 
			
		||||
func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters {
 | 
			
		||||
	dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1)
 | 
			
		||||
	dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC)
 | 
			
		||||
	dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP)
 | 
			
		||||
	dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP)
 | 
			
		||||
	return op.DwarfRegisters{StaticBase: g.variable.bi.staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										963
									
								
								vendor/github.com/go-delve/delve/pkg/proc/bininfo.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										963
									
								
								vendor/github.com/go-delve/delve/pkg/proc/bininfo.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,963 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"debug/dwarf"
 | 
			
		||||
	"debug/elf"
 | 
			
		||||
	"debug/macho"
 | 
			
		||||
	"debug/pe"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"encoding/hex"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/line"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/goversion"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// BinaryInfo holds information on the binaries being executed (this
 | 
			
		||||
// includes both the executable and also any loaded libraries).
 | 
			
		||||
type BinaryInfo struct {
 | 
			
		||||
	// Path on disk of the binary being executed.
 | 
			
		||||
	Path string
 | 
			
		||||
	// Architecture of this binary.
 | 
			
		||||
	Arch Arch
 | 
			
		||||
 | 
			
		||||
	// GOOS operating system this binary is executing on.
 | 
			
		||||
	GOOS string
 | 
			
		||||
 | 
			
		||||
	// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
 | 
			
		||||
	Functions []Function
 | 
			
		||||
	// Sources is a list of all source files found in debug_line.
 | 
			
		||||
	Sources []string
 | 
			
		||||
	// LookupFunc maps function names to a description of the function.
 | 
			
		||||
	LookupFunc map[string]*Function
 | 
			
		||||
 | 
			
		||||
	// Images is a list of loaded shared libraries (also known as
 | 
			
		||||
	// shared objects on linux or DLLs on windws).
 | 
			
		||||
	Images []*Image
 | 
			
		||||
 | 
			
		||||
	ElfDynamicSection ElfDynamicSection
 | 
			
		||||
 | 
			
		||||
	lastModified time.Time // Time the executable of this process was last modified
 | 
			
		||||
 | 
			
		||||
	closer         io.Closer
 | 
			
		||||
	sepDebugCloser io.Closer
 | 
			
		||||
 | 
			
		||||
	staticBase uint64
 | 
			
		||||
 | 
			
		||||
	// Maps package names to package paths, needed to lookup types inside DWARF info
 | 
			
		||||
	packageMap map[string]string
 | 
			
		||||
 | 
			
		||||
	dwarf        *dwarf.Data
 | 
			
		||||
	dwarfReader  *dwarf.Reader
 | 
			
		||||
	frameEntries frame.FrameDescriptionEntries
 | 
			
		||||
	loclist      loclistReader
 | 
			
		||||
	compileUnits []*compileUnit
 | 
			
		||||
	types        map[string]dwarf.Offset
 | 
			
		||||
	packageVars  []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
 | 
			
		||||
	typeCache    map[dwarf.Offset]godwarf.Type
 | 
			
		||||
 | 
			
		||||
	gStructOffset uint64
 | 
			
		||||
 | 
			
		||||
	loadModuleDataOnce sync.Once
 | 
			
		||||
	moduleData         []moduleData
 | 
			
		||||
	nameOfRuntimeType  map[uintptr]nameOfRuntimeTypeEntry
 | 
			
		||||
 | 
			
		||||
	// runtimeTypeToDIE maps between the offset of a runtime._type in
 | 
			
		||||
	// runtime.moduledata.types and the offset of the DIE in debug_info. This
 | 
			
		||||
	// map is filled by using the extended attribute godwarf.AttrGoRuntimeType
 | 
			
		||||
	// which was added in go 1.11.
 | 
			
		||||
	runtimeTypeToDIE map[uint64]runtimeTypeDIE
 | 
			
		||||
 | 
			
		||||
	// consts[off] lists all the constants with the type defined at offset off.
 | 
			
		||||
	consts constantsMap
 | 
			
		||||
 | 
			
		||||
	loadErrMu sync.Mutex
 | 
			
		||||
	loadErr   error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
 | 
			
		||||
var ErrUnsupportedLinuxArch = errors.New("unsupported architecture - only linux/amd64 is supported")
 | 
			
		||||
 | 
			
		||||
// ErrUnsupportedWindowsArch is returned when attempting to debug a binary compiled for an unsupported architecture.
 | 
			
		||||
var ErrUnsupportedWindowsArch = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
 | 
			
		||||
 | 
			
		||||
// ErrUnsupportedDarwinArch is returned when attempting to debug a binary compiled for an unsupported architecture.
 | 
			
		||||
var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwin/amd64 is supported")
 | 
			
		||||
 | 
			
		||||
// ErrCouldNotDetermineRelocation is an error returned when Delve could not determine the base address of a
 | 
			
		||||
// position independant executable.
 | 
			
		||||
var ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE")
 | 
			
		||||
 | 
			
		||||
// ErrNoDebugInfoFound is returned when Delve cannot open the debug_info
 | 
			
		||||
// section or find an external debug info file.
 | 
			
		||||
var ErrNoDebugInfoFound = errors.New("could not open debug info")
 | 
			
		||||
 | 
			
		||||
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
 | 
			
		||||
 | 
			
		||||
type compileUnit struct {
 | 
			
		||||
	name   string // univocal name for non-go compile units
 | 
			
		||||
	lowPC  uint64
 | 
			
		||||
	ranges [][2]uint64
 | 
			
		||||
 | 
			
		||||
	entry              *dwarf.Entry        // debug_info entry describing this compile unit
 | 
			
		||||
	isgo               bool                // true if this is the go compile unit
 | 
			
		||||
	lineInfo           *line.DebugLineInfo // debug_line segment associated with this compile unit
 | 
			
		||||
	concreteInlinedFns []inlinedFn         // list of concrete inlined functions within this compile unit
 | 
			
		||||
	optimized          bool                // this compile unit is optimized
 | 
			
		||||
	producer           string              // producer attribute
 | 
			
		||||
 | 
			
		||||
	startOffset, endOffset dwarf.Offset // interval of offsets contained in this compile unit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type partialUnitConstant struct {
 | 
			
		||||
	name  string
 | 
			
		||||
	typ   dwarf.Offset
 | 
			
		||||
	value int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type partialUnit struct {
 | 
			
		||||
	entry     *dwarf.Entry
 | 
			
		||||
	types     map[string]dwarf.Offset
 | 
			
		||||
	variables []packageVar
 | 
			
		||||
	constants []partialUnitConstant
 | 
			
		||||
	functions []Function
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// inlinedFn represents a concrete inlined function, e.g.
 | 
			
		||||
// an entry for the generated code of an inlined function.
 | 
			
		||||
type inlinedFn struct {
 | 
			
		||||
	Name          string    // Name of the function that was inlined
 | 
			
		||||
	LowPC, HighPC uint64    // Address range of the generated inlined instructions
 | 
			
		||||
	CallFile      string    // File of the call site of the inlined function
 | 
			
		||||
	CallLine      int64     // Line of the call site of the inlined function
 | 
			
		||||
	Parent        *Function // The function that contains this inlined function
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Function describes a function in the target program.
 | 
			
		||||
type Function struct {
 | 
			
		||||
	Name       string
 | 
			
		||||
	Entry, End uint64 // same as DW_AT_lowpc and DW_AT_highpc
 | 
			
		||||
	offset     dwarf.Offset
 | 
			
		||||
	cu         *compileUnit
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PackageName returns the package part of the symbol name,
 | 
			
		||||
// or the empty string if there is none.
 | 
			
		||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
 | 
			
		||||
func (fn *Function) PackageName() string {
 | 
			
		||||
	return packageName(fn.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func packageName(name string) string {
 | 
			
		||||
	pathend := strings.LastIndex(name, "/")
 | 
			
		||||
	if pathend < 0 {
 | 
			
		||||
		pathend = 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if i := strings.Index(name[pathend:], "."); i != -1 {
 | 
			
		||||
		return name[:pathend+i]
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReceiverName returns the receiver type name of this symbol,
 | 
			
		||||
// or the empty string if there is none.
 | 
			
		||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
 | 
			
		||||
func (fn *Function) ReceiverName() string {
 | 
			
		||||
	pathend := strings.LastIndex(fn.Name, "/")
 | 
			
		||||
	if pathend < 0 {
 | 
			
		||||
		pathend = 0
 | 
			
		||||
	}
 | 
			
		||||
	l := strings.Index(fn.Name[pathend:], ".")
 | 
			
		||||
	r := strings.LastIndex(fn.Name[pathend:], ".")
 | 
			
		||||
	if l == -1 || r == -1 || l == r {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return fn.Name[pathend+l+1 : pathend+r]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BaseName returns the symbol name without the package or receiver name.
 | 
			
		||||
// Borrowed from $GOROOT/debug/gosym/symtab.go
 | 
			
		||||
func (fn *Function) BaseName() string {
 | 
			
		||||
	if i := strings.LastIndex(fn.Name, "."); i != -1 {
 | 
			
		||||
		return fn.Name[i+1:]
 | 
			
		||||
	}
 | 
			
		||||
	return fn.Name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Optimized returns true if the function was optimized by the compiler.
 | 
			
		||||
func (fn *Function) Optimized() bool {
 | 
			
		||||
	return fn.cu.optimized
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type constantsMap map[dwarf.Offset]*constantType
 | 
			
		||||
 | 
			
		||||
type constantType struct {
 | 
			
		||||
	initialized bool
 | 
			
		||||
	values      []constantValue
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type constantValue struct {
 | 
			
		||||
	name      string
 | 
			
		||||
	fullName  string
 | 
			
		||||
	value     int64
 | 
			
		||||
	singleBit bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// packageVar represents a package-level variable (or a C global variable).
 | 
			
		||||
// If a global variable does not have an address (for example it's stored in
 | 
			
		||||
// a register, or non-contiguously) addr will be 0.
 | 
			
		||||
type packageVar struct {
 | 
			
		||||
	name   string
 | 
			
		||||
	offset dwarf.Offset
 | 
			
		||||
	addr   uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type loclistReader struct {
 | 
			
		||||
	data  []byte
 | 
			
		||||
	cur   int
 | 
			
		||||
	ptrSz int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rdr *loclistReader) Seek(off int) {
 | 
			
		||||
	rdr.cur = off
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rdr *loclistReader) read(sz int) []byte {
 | 
			
		||||
	r := rdr.data[rdr.cur : rdr.cur+sz]
 | 
			
		||||
	rdr.cur += sz
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rdr *loclistReader) oneAddr() uint64 {
 | 
			
		||||
	switch rdr.ptrSz {
 | 
			
		||||
	case 4:
 | 
			
		||||
		addr := binary.LittleEndian.Uint32(rdr.read(rdr.ptrSz))
 | 
			
		||||
		if addr == ^uint32(0) {
 | 
			
		||||
			return ^uint64(0)
 | 
			
		||||
		}
 | 
			
		||||
		return uint64(addr)
 | 
			
		||||
	case 8:
 | 
			
		||||
		addr := uint64(binary.LittleEndian.Uint64(rdr.read(rdr.ptrSz)))
 | 
			
		||||
		return addr
 | 
			
		||||
	default:
 | 
			
		||||
		panic("bad address size")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rdr *loclistReader) Next(e *loclistEntry) bool {
 | 
			
		||||
	e.lowpc = rdr.oneAddr()
 | 
			
		||||
	e.highpc = rdr.oneAddr()
 | 
			
		||||
 | 
			
		||||
	if e.lowpc == 0 && e.highpc == 0 {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if e.BaseAddressSelection() {
 | 
			
		||||
		e.instr = nil
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	instrlen := binary.LittleEndian.Uint16(rdr.read(2))
 | 
			
		||||
	e.instr = rdr.read(int(instrlen))
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type loclistEntry struct {
 | 
			
		||||
	lowpc, highpc uint64
 | 
			
		||||
	instr         []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type runtimeTypeDIE struct {
 | 
			
		||||
	offset dwarf.Offset
 | 
			
		||||
	kind   int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (e *loclistEntry) BaseAddressSelection() bool {
 | 
			
		||||
	return e.lowpc == ^uint64(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type buildIDHeader struct {
 | 
			
		||||
	Namesz uint32
 | 
			
		||||
	Descsz uint32
 | 
			
		||||
	Type   uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ElfDynamicSection describes the .dynamic section of an ELF executable.
 | 
			
		||||
type ElfDynamicSection struct {
 | 
			
		||||
	Addr uint64 // relocated address of where the .dynamic section is mapped in memory
 | 
			
		||||
	Size uint64 // size of the .dynamic section of the executable
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
 | 
			
		||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
 | 
			
		||||
	r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
 | 
			
		||||
 | 
			
		||||
	// TODO: find better way to determine proc arch (perhaps use executable file info).
 | 
			
		||||
	switch goarch {
 | 
			
		||||
	case "amd64":
 | 
			
		||||
		r.Arch = AMD64Arch(goos)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadBinaryInfo will load and store the information from the binary at 'path'.
 | 
			
		||||
// It is expected this will be called in parallel with other initialization steps
 | 
			
		||||
// so a sync.WaitGroup must be provided.
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string) error {
 | 
			
		||||
	fi, err := os.Stat(path)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bi.lastModified = fi.ModTime()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	defer wg.Wait()
 | 
			
		||||
	bi.Path = path
 | 
			
		||||
	switch bi.GOOS {
 | 
			
		||||
	case "linux":
 | 
			
		||||
		return bi.LoadBinaryInfoElf(path, entryPoint, debugInfoDirs, &wg)
 | 
			
		||||
	case "windows":
 | 
			
		||||
		return bi.LoadBinaryInfoPE(path, entryPoint, &wg)
 | 
			
		||||
	case "darwin":
 | 
			
		||||
		return bi.LoadBinaryInfoMacho(path, entryPoint, &wg)
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New("unsupported operating system")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GStructOffset returns the offset of the G
 | 
			
		||||
// struct in thread local storage.
 | 
			
		||||
func (bi *BinaryInfo) GStructOffset() uint64 {
 | 
			
		||||
	return bi.gStructOffset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LastModified returns the last modified time of the binary.
 | 
			
		||||
func (bi *BinaryInfo) LastModified() time.Time {
 | 
			
		||||
	return bi.lastModified
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DwarfReader returns a reader for the dwarf data
 | 
			
		||||
func (bi *BinaryInfo) DwarfReader() *reader.Reader {
 | 
			
		||||
	return reader.New(bi.dwarf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Types returns list of types present in the debugged program.
 | 
			
		||||
func (bi *BinaryInfo) Types() ([]string, error) {
 | 
			
		||||
	types := make([]string, 0, len(bi.types))
 | 
			
		||||
	for k := range bi.types {
 | 
			
		||||
		types = append(types, k)
 | 
			
		||||
	}
 | 
			
		||||
	return types, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PCToLine converts an instruction address to a file/line/function.
 | 
			
		||||
func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *Function) {
 | 
			
		||||
	fn := bi.PCToFunc(pc)
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return "", 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	f, ln := fn.cu.lineInfo.PCToLine(fn.Entry, pc)
 | 
			
		||||
	return f, ln, fn
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LineToPC converts a file:line into a memory address.
 | 
			
		||||
func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pc uint64, fn *Function, err error) {
 | 
			
		||||
	for _, cu := range bi.compileUnits {
 | 
			
		||||
		if cu.lineInfo.Lookup[filename] != nil {
 | 
			
		||||
			pc = cu.lineInfo.LineToPC(filename, lineno)
 | 
			
		||||
			if pc == 0 {
 | 
			
		||||
				// Check to see if this file:line belongs to the call site
 | 
			
		||||
				// of an inlined function.
 | 
			
		||||
				for _, ifn := range cu.concreteInlinedFns {
 | 
			
		||||
					if strings.Contains(ifn.CallFile, filename) && ifn.CallLine == int64(lineno) {
 | 
			
		||||
						pc = ifn.LowPC
 | 
			
		||||
						fn = ifn.Parent
 | 
			
		||||
						return
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			fn = bi.PCToFunc(pc)
 | 
			
		||||
			if fn != nil {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err = fmt.Errorf("could not find %s:%d", filename, lineno)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AllPCsForFileLine returns all PC addresses for the given filename:lineno.
 | 
			
		||||
func (bi *BinaryInfo) AllPCsForFileLine(filename string, lineno int) []uint64 {
 | 
			
		||||
	r := make([]uint64, 0, 1)
 | 
			
		||||
	for _, cu := range bi.compileUnits {
 | 
			
		||||
		if cu.lineInfo.Lookup[filename] != nil {
 | 
			
		||||
			r = append(r, cu.lineInfo.AllPCsForFileLine(filename, lineno)...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PCToFunc returns the function containing the given PC address
 | 
			
		||||
func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
 | 
			
		||||
	i := sort.Search(len(bi.Functions), func(i int) bool {
 | 
			
		||||
		fn := bi.Functions[i]
 | 
			
		||||
		return pc <= fn.Entry || (fn.Entry <= pc && pc < fn.End)
 | 
			
		||||
	})
 | 
			
		||||
	if i != len(bi.Functions) {
 | 
			
		||||
		fn := &bi.Functions[i]
 | 
			
		||||
		if fn.Entry <= pc && pc < fn.End {
 | 
			
		||||
			return fn
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Image represents a loaded library file (shared object on linux, DLL on windows).
 | 
			
		||||
type Image struct {
 | 
			
		||||
	Path string
 | 
			
		||||
	addr uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddImage adds the specified image to bi.
 | 
			
		||||
func (bi *BinaryInfo) AddImage(path string, addr uint64) {
 | 
			
		||||
	if !strings.HasPrefix(path, "/") {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, image := range bi.Images {
 | 
			
		||||
		if image.Path == path && image.addr == addr {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//TODO(aarzilli): actually load informations about the image here
 | 
			
		||||
	bi.Images = append(bi.Images, &Image{Path: path, addr: addr})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes all internal readers.
 | 
			
		||||
func (bi *BinaryInfo) Close() error {
 | 
			
		||||
	if bi.sepDebugCloser != nil {
 | 
			
		||||
		bi.sepDebugCloser.Close()
 | 
			
		||||
	}
 | 
			
		||||
	if bi.closer != nil {
 | 
			
		||||
		return bi.closer.Close()
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) setLoadError(fmtstr string, args ...interface{}) {
 | 
			
		||||
	bi.loadErrMu.Lock()
 | 
			
		||||
	bi.loadErr = fmt.Errorf(fmtstr, args...)
 | 
			
		||||
	bi.loadErrMu.Unlock()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadError returns any internal load error.
 | 
			
		||||
func (bi *BinaryInfo) LoadError() error {
 | 
			
		||||
	return bi.loadErr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nilCloser struct{}
 | 
			
		||||
 | 
			
		||||
func (c *nilCloser) Close() error { return nil }
 | 
			
		||||
 | 
			
		||||
// LoadFromData creates a new BinaryInfo object using the specified data.
 | 
			
		||||
// This is used for debugging BinaryInfo, you should use LoadBinary instead.
 | 
			
		||||
func (bi *BinaryInfo) LoadFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) {
 | 
			
		||||
	bi.closer = (*nilCloser)(nil)
 | 
			
		||||
	bi.sepDebugCloser = (*nilCloser)(nil)
 | 
			
		||||
	bi.dwarf = dwdata
 | 
			
		||||
 | 
			
		||||
	if debugFrameBytes != nil {
 | 
			
		||||
		bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), bi.staticBase)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.loclistInit(debugLocBytes)
 | 
			
		||||
 | 
			
		||||
	bi.loadDebugInfoMaps(debugLineBytes, nil, nil)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) loclistInit(data []byte) {
 | 
			
		||||
	bi.loclist.data = data
 | 
			
		||||
	bi.loclist.ptrSz = bi.Arch.PtrSize()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint64) ([]byte, string, error) {
 | 
			
		||||
	a := entry.Val(attr)
 | 
			
		||||
	if a == nil {
 | 
			
		||||
		return nil, "", fmt.Errorf("no location attribute %s", attr)
 | 
			
		||||
	}
 | 
			
		||||
	if instr, ok := a.([]byte); ok {
 | 
			
		||||
		var descr bytes.Buffer
 | 
			
		||||
		fmt.Fprintf(&descr, "[block] ")
 | 
			
		||||
		op.PrettyPrint(&descr, instr)
 | 
			
		||||
		return instr, descr.String(), nil
 | 
			
		||||
	}
 | 
			
		||||
	off, ok := a.(int64)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, "", fmt.Errorf("could not interpret location attribute %s", attr)
 | 
			
		||||
	}
 | 
			
		||||
	if bi.loclist.data == nil {
 | 
			
		||||
		return nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x (no debug_loc section found)", off, pc)
 | 
			
		||||
	}
 | 
			
		||||
	instr := bi.loclistEntry(off, pc)
 | 
			
		||||
	if instr == nil {
 | 
			
		||||
		return nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
 | 
			
		||||
	}
 | 
			
		||||
	var descr bytes.Buffer
 | 
			
		||||
	fmt.Fprintf(&descr, "[%#x:%#x] ", off, pc)
 | 
			
		||||
	op.PrettyPrint(&descr, instr)
 | 
			
		||||
	return instr, descr.String(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Location returns the location described by attribute attr of entry.
 | 
			
		||||
// This will either be an int64 address or a slice of Pieces for locations
 | 
			
		||||
// that don't correspond to a single memory address (registers, composite
 | 
			
		||||
// locations).
 | 
			
		||||
func (bi *BinaryInfo) Location(entry reader.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, string, error) {
 | 
			
		||||
	instr, descr, err := bi.locationExpr(entry, attr, pc)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, nil, "", err
 | 
			
		||||
	}
 | 
			
		||||
	addr, pieces, err := op.ExecuteStackProgram(regs, instr)
 | 
			
		||||
	return addr, pieces, descr, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// loclistEntry returns the loclist entry in the loclist starting at off,
 | 
			
		||||
// for address pc.
 | 
			
		||||
func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
 | 
			
		||||
	var base uint64
 | 
			
		||||
	if cu := bi.findCompileUnit(pc); cu != nil {
 | 
			
		||||
		base = cu.lowPC
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.loclist.Seek(int(off))
 | 
			
		||||
	var e loclistEntry
 | 
			
		||||
	for bi.loclist.Next(&e) {
 | 
			
		||||
		if e.BaseAddressSelection() {
 | 
			
		||||
			base = e.highpc
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if pc >= e.lowpc+base && pc < e.highpc+base {
 | 
			
		||||
			return e.instr
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findCompileUnit returns the compile unit containing address pc.
 | 
			
		||||
func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
 | 
			
		||||
	for _, cu := range bi.compileUnits {
 | 
			
		||||
		for _, rng := range cu.ranges {
 | 
			
		||||
			if pc >= rng[0] && pc < rng[1] {
 | 
			
		||||
				return cu
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
 | 
			
		||||
	for _, cu := range bi.compileUnits {
 | 
			
		||||
		if off >= cu.startOffset && off < cu.endOffset {
 | 
			
		||||
			return cu
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Producer returns the value of DW_AT_producer.
 | 
			
		||||
func (bi *BinaryInfo) Producer() string {
 | 
			
		||||
	for _, cu := range bi.compileUnits {
 | 
			
		||||
		if cu.isgo && cu.producer != "" {
 | 
			
		||||
			return cu.producer
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Type returns the Dwarf type entry at `offset`.
 | 
			
		||||
func (bi *BinaryInfo) Type(offset dwarf.Offset) (godwarf.Type, error) {
 | 
			
		||||
	return godwarf.ReadType(bi.dwarf, offset, bi.typeCache)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ELF ///////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// ErrNoBuildIDNote is used in openSeparateDebugInfo to signal there's no
 | 
			
		||||
// build-id note on the binary, so LoadBinaryInfoElf will return
 | 
			
		||||
// the error message coming from elfFile.DWARF() instead.
 | 
			
		||||
type ErrNoBuildIDNote struct{}
 | 
			
		||||
 | 
			
		||||
func (e *ErrNoBuildIDNote) Error() string {
 | 
			
		||||
	return "can't find build-id note on binary"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// openSeparateDebugInfo searches for a file containing the separate
 | 
			
		||||
// debug info for the binary using the "build ID" method as described
 | 
			
		||||
// in GDB's documentation [1], and if found returns two handles, one
 | 
			
		||||
// for the bare file, and another for its corresponding elf.File.
 | 
			
		||||
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
 | 
			
		||||
//
 | 
			
		||||
// Alternatively, if the debug file cannot be found be the build-id, Delve
 | 
			
		||||
// will look in directories specified by the debug-info-directories config value.
 | 
			
		||||
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File, debugInfoDirectories []string) (*os.File, *elf.File, error) {
 | 
			
		||||
	var debugFilePath string
 | 
			
		||||
	for _, dir := range debugInfoDirectories {
 | 
			
		||||
		var potentialDebugFilePath string
 | 
			
		||||
		if strings.Contains(dir, "build-id") {
 | 
			
		||||
			desc1, desc2, err := parseBuildID(exe)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			potentialDebugFilePath = fmt.Sprintf("%s/%s/%s.debug", dir, desc1, desc2)
 | 
			
		||||
		} else {
 | 
			
		||||
			potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(bi.Path))
 | 
			
		||||
		}
 | 
			
		||||
		_, err := os.Stat(potentialDebugFilePath)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			debugFilePath = potentialDebugFilePath
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if debugFilePath == "" {
 | 
			
		||||
		return nil, nil, ErrNoDebugInfoFound
 | 
			
		||||
	}
 | 
			
		||||
	sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, errors.New("can't open separate debug file: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	elfFile, err := elf.NewFile(sepFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sepFile.Close()
 | 
			
		||||
		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if elfFile.Machine != elf.EM_X86_64 {
 | 
			
		||||
		sepFile.Close()
 | 
			
		||||
		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, ErrUnsupportedLinuxArch.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sepFile, elfFile, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseBuildID(exe *elf.File) (string, string, error) {
 | 
			
		||||
	buildid := exe.Section(".note.gnu.build-id")
 | 
			
		||||
	if buildid == nil {
 | 
			
		||||
		return "", "", &ErrNoBuildIDNote{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	br := buildid.Open()
 | 
			
		||||
	bh := new(buildIDHeader)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
 | 
			
		||||
		return "", "", errors.New("can't read build-id header: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := make([]byte, bh.Namesz)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, name); err != nil {
 | 
			
		||||
		return "", "", errors.New("can't read build-id name: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.TrimSpace(string(name)) != "GNU\x00" {
 | 
			
		||||
		return "", "", errors.New("invalid build-id signature")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	descBinary := make([]byte, bh.Descsz)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
 | 
			
		||||
		return "", "", errors.New("can't read build-id desc: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	desc := hex.EncodeToString(descBinary)
 | 
			
		||||
	return desc[:2], desc[2:], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadBinaryInfoElf specifically loads information from an ELF binary.
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, debugInfoDirectories []string, wg *sync.WaitGroup) error {
 | 
			
		||||
	exe, err := os.OpenFile(path, 0, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	bi.closer = exe
 | 
			
		||||
	elfFile, err := elf.NewFile(exe)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if elfFile.Machine != elf.EM_X86_64 {
 | 
			
		||||
		return ErrUnsupportedLinuxArch
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if entryPoint != 0 {
 | 
			
		||||
		bi.staticBase = entryPoint - elfFile.Entry
 | 
			
		||||
	} else {
 | 
			
		||||
		if elfFile.Type == elf.ET_DYN {
 | 
			
		||||
			return ErrCouldNotDetermineRelocation
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dynsec := elfFile.Section(".dynamic"); dynsec != nil {
 | 
			
		||||
		bi.ElfDynamicSection.Addr = dynsec.Addr + bi.staticBase
 | 
			
		||||
		bi.ElfDynamicSection.Size = dynsec.Size
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dwarfFile := elfFile
 | 
			
		||||
 | 
			
		||||
	bi.dwarf, err = elfFile.DWARF()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		var sepFile *os.File
 | 
			
		||||
		var serr error
 | 
			
		||||
		sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile, debugInfoDirectories)
 | 
			
		||||
		if serr != nil {
 | 
			
		||||
			return serr
 | 
			
		||||
		}
 | 
			
		||||
		bi.sepDebugCloser = sepFile
 | 
			
		||||
		bi.dwarf, err = dwarfFile.DWARF()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.dwarfReader = bi.dwarf.Reader()
 | 
			
		||||
 | 
			
		||||
	debugLineBytes, err := godwarf.GetDebugSectionElf(dwarfFile, "line")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	debugLocBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loc")
 | 
			
		||||
	bi.loclistInit(debugLocBytes)
 | 
			
		||||
 | 
			
		||||
	wg.Add(3)
 | 
			
		||||
	go bi.parseDebugFrameElf(dwarfFile, wg)
 | 
			
		||||
	go bi.loadDebugInfoMaps(debugLineBytes, wg, nil)
 | 
			
		||||
	go bi.setGStructOffsetElf(dwarfFile, wg)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	debugFrameData, err := godwarf.GetDebugSectionElf(exe, "frame")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		bi.setLoadError("could not get .debug_frame section: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	debugInfoData, err := godwarf.GetDebugSectionElf(exe, "info")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		bi.setLoadError("could not get .debug_info section: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.frameEntries = frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoData), bi.staticBase)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	// This is a bit arcane. Essentially:
 | 
			
		||||
	// - If the program is pure Go, it can do whatever it wants, and puts the G
 | 
			
		||||
	//   pointer at %fs-8.
 | 
			
		||||
	// - Otherwise, Go asks the external linker to place the G pointer by
 | 
			
		||||
	//   emitting runtime.tlsg, a TLS symbol, which is relocated to the chosen
 | 
			
		||||
	//   offset in libc's TLS block.
 | 
			
		||||
	symbols, err := exe.Symbols()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		bi.setLoadError("could not parse ELF symbols: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var tlsg *elf.Symbol
 | 
			
		||||
	for _, symbol := range symbols {
 | 
			
		||||
		if symbol.Name == "runtime.tlsg" {
 | 
			
		||||
			s := symbol
 | 
			
		||||
			tlsg = &s
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if tlsg == nil {
 | 
			
		||||
		bi.gStructOffset = ^uint64(8) + 1 // -8
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	var tls *elf.Prog
 | 
			
		||||
	for _, prog := range exe.Progs {
 | 
			
		||||
		if prog.Type == elf.PT_TLS {
 | 
			
		||||
			tls = prog
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if tls == nil {
 | 
			
		||||
		bi.gStructOffset = ^uint64(8) + 1 // -8
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	memsz := tls.Memsz
 | 
			
		||||
 | 
			
		||||
	memsz = (memsz + uint64(bi.Arch.PtrSize()) - 1) & ^uint64(bi.Arch.PtrSize()-1) // align to pointer-sized-boundary
 | 
			
		||||
	// The TLS register points to the end of the TLS block, which is
 | 
			
		||||
	// tls.Memsz long. runtime.tlsg is an offset from the beginning of that block.
 | 
			
		||||
	bi.gStructOffset = ^(memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PE ////////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
const _IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
 | 
			
		||||
 | 
			
		||||
// LoadBinaryInfoPE specifically loads information from a PE binary.
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfoPE(path string, entryPoint uint64, wg *sync.WaitGroup) error {
 | 
			
		||||
	peFile, closer, err := openExecutablePathPE(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	bi.closer = closer
 | 
			
		||||
	if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
 | 
			
		||||
		return ErrUnsupportedWindowsArch
 | 
			
		||||
	}
 | 
			
		||||
	bi.dwarf, err = peFile.DWARF()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//TODO(aarzilli): actually test this when Go supports PIE buildmode on Windows.
 | 
			
		||||
	opth := peFile.OptionalHeader.(*pe.OptionalHeader64)
 | 
			
		||||
	if entryPoint != 0 {
 | 
			
		||||
		bi.staticBase = entryPoint - opth.ImageBase
 | 
			
		||||
	} else {
 | 
			
		||||
		if opth.DllCharacteristics&_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE != 0 {
 | 
			
		||||
			return ErrCouldNotDetermineRelocation
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.dwarfReader = bi.dwarf.Reader()
 | 
			
		||||
 | 
			
		||||
	debugLineBytes, err := godwarf.GetDebugSectionPE(peFile, "line")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	debugLocBytes, _ := godwarf.GetDebugSectionPE(peFile, "loc")
 | 
			
		||||
	bi.loclistInit(debugLocBytes)
 | 
			
		||||
 | 
			
		||||
	wg.Add(2)
 | 
			
		||||
	go bi.parseDebugFramePE(peFile, wg)
 | 
			
		||||
	go bi.loadDebugInfoMaps(debugLineBytes, wg, nil)
 | 
			
		||||
 | 
			
		||||
	// Use ArbitraryUserPointer (0x28) as pointer to pointer
 | 
			
		||||
	// to G struct per:
 | 
			
		||||
	// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
 | 
			
		||||
 | 
			
		||||
	bi.gStructOffset = 0x28
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		bi.setLoadError("could not get .debug_frame section: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	debugInfoBytes, err := godwarf.GetDebugSectionPE(exe, "info")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		bi.setLoadError("could not get .debug_info section: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), bi.staticBase)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
 | 
			
		||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
 | 
			
		||||
	for _, s := range f.Symbols {
 | 
			
		||||
		if s.Name != name {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if s.SectionNumber <= 0 {
 | 
			
		||||
			return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
 | 
			
		||||
		}
 | 
			
		||||
		if len(f.Sections) < int(s.SectionNumber) {
 | 
			
		||||
			return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
 | 
			
		||||
		}
 | 
			
		||||
		return s, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("no %s symbol found", name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MACH-O ////////////////////////////////////////////////////////////
 | 
			
		||||
 | 
			
		||||
// LoadBinaryInfoMacho specifically loads information from a Mach-O binary.
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, entryPoint uint64, wg *sync.WaitGroup) error {
 | 
			
		||||
	exe, err := macho.Open(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	bi.closer = exe
 | 
			
		||||
	if exe.Cpu != macho.CpuAmd64 {
 | 
			
		||||
		return ErrUnsupportedDarwinArch
 | 
			
		||||
	}
 | 
			
		||||
	bi.dwarf, err = exe.DWARF()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.dwarfReader = bi.dwarf.Reader()
 | 
			
		||||
 | 
			
		||||
	debugLineBytes, err := godwarf.GetDebugSectionMacho(exe, "line")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	debugLocBytes, _ := godwarf.GetDebugSectionMacho(exe, "loc")
 | 
			
		||||
	bi.loclistInit(debugLocBytes)
 | 
			
		||||
 | 
			
		||||
	wg.Add(2)
 | 
			
		||||
	go bi.parseDebugFrameMacho(exe, wg)
 | 
			
		||||
	go bi.loadDebugInfoMaps(debugLineBytes, wg, bi.setGStructOffsetMacho)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) setGStructOffsetMacho() {
 | 
			
		||||
	// In go1.11 it's 0x30, before 0x8a0, see:
 | 
			
		||||
	// https://github.com/golang/go/issues/23617
 | 
			
		||||
	// and go commit b3a854c733257c5249c3435ffcee194f8439676a
 | 
			
		||||
	producer := bi.Producer()
 | 
			
		||||
	if producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 11) {
 | 
			
		||||
		bi.gStructOffset = 0x30
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bi.gStructOffset = 0x8a0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	debugFrameBytes, err := godwarf.GetDebugSectionMacho(exe, "frame")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		bi.setLoadError("could not get __debug_frame section: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		bi.setLoadError("could not get .debug_info section: %v", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), bi.staticBase)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										440
									
								
								vendor/github.com/go-delve/delve/pkg/proc/breakpoints.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										440
									
								
								vendor/github.com/go-delve/delve/pkg/proc/breakpoints.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,440 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"reflect"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Breakpoint represents a breakpoint. Stores information on the break
 | 
			
		||||
// point including the byte of data that originally was stored at that
 | 
			
		||||
// address.
 | 
			
		||||
type Breakpoint struct {
 | 
			
		||||
	// File & line information for printing.
 | 
			
		||||
	FunctionName string
 | 
			
		||||
	File         string
 | 
			
		||||
	Line         int
 | 
			
		||||
 | 
			
		||||
	Addr         uint64 // Address breakpoint is set for.
 | 
			
		||||
	OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
 | 
			
		||||
	Name         string // User defined name of the breakpoint
 | 
			
		||||
	ID           int    // Monotonically increasing ID.
 | 
			
		||||
 | 
			
		||||
	// Kind describes whether this is an internal breakpoint (for next'ing or
 | 
			
		||||
	// stepping).
 | 
			
		||||
	// A single breakpoint can be both a UserBreakpoint and some kind of
 | 
			
		||||
	// internal breakpoint, but it can not be two different kinds of internal
 | 
			
		||||
	// breakpoint.
 | 
			
		||||
	Kind BreakpointKind
 | 
			
		||||
 | 
			
		||||
	// Breakpoint information
 | 
			
		||||
	Tracepoint    bool // Tracepoint flag
 | 
			
		||||
	TraceReturn   bool
 | 
			
		||||
	Goroutine     bool     // Retrieve goroutine information
 | 
			
		||||
	Stacktrace    int      // Number of stack frames to retrieve
 | 
			
		||||
	Variables     []string // Variables to evaluate
 | 
			
		||||
	LoadArgs      *LoadConfig
 | 
			
		||||
	LoadLocals    *LoadConfig
 | 
			
		||||
	HitCount      map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
 | 
			
		||||
	TotalHitCount uint64         // Number of times a breakpoint has been reached
 | 
			
		||||
 | 
			
		||||
	// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
 | 
			
		||||
	// will also check if the caller is runtime.gopanic or if the return
 | 
			
		||||
	// address is in the DeferReturns array.
 | 
			
		||||
	// Next uses NextDeferBreakpoints for the breakpoint it sets on the
 | 
			
		||||
	// deferred function, DeferReturns is populated with the
 | 
			
		||||
	// addresses of calls to runtime.deferreturn in the current
 | 
			
		||||
	// function. This ensures that the breakpoint on the deferred
 | 
			
		||||
	// function only triggers on panic or on the defer call to
 | 
			
		||||
	// the function, not when the function is called directly
 | 
			
		||||
	DeferReturns []uint64
 | 
			
		||||
	// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
 | 
			
		||||
	Cond ast.Expr
 | 
			
		||||
	// internalCond is the same as Cond but used for the condition of internal breakpoints
 | 
			
		||||
	internalCond ast.Expr
 | 
			
		||||
 | 
			
		||||
	// ReturnInfo describes how to collect return variables when this
 | 
			
		||||
	// breakpoint is hit as a return breakpoint.
 | 
			
		||||
	returnInfo *returnBreakpointInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointKind determines the behavior of delve when the
 | 
			
		||||
// breakpoint is reached.
 | 
			
		||||
type BreakpointKind uint16
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// UserBreakpoint is a user set breakpoint
 | 
			
		||||
	UserBreakpoint BreakpointKind = (1 << iota)
 | 
			
		||||
	// NextBreakpoint is a breakpoint set by Next, Continue
 | 
			
		||||
	// will stop on it and delete it
 | 
			
		||||
	NextBreakpoint
 | 
			
		||||
	// NextDeferBreakpoint is a breakpoint set by Next on the
 | 
			
		||||
	// first deferred function. In addition to checking their condition
 | 
			
		||||
	// breakpoints of this kind will also check that the function has been
 | 
			
		||||
	// called by runtime.gopanic or through runtime.deferreturn.
 | 
			
		||||
	NextDeferBreakpoint
 | 
			
		||||
	// StepBreakpoint is a breakpoint set by Step on a CALL instruction,
 | 
			
		||||
	// Continue will set a new breakpoint (of NextBreakpoint kind) on the
 | 
			
		||||
	// destination of CALL, delete this breakpoint and then continue again
 | 
			
		||||
	StepBreakpoint
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (bp *Breakpoint) String() string {
 | 
			
		||||
	return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointExistsError is returned when trying to set a breakpoint at
 | 
			
		||||
// an address that already has a breakpoint set for it.
 | 
			
		||||
type BreakpointExistsError struct {
 | 
			
		||||
	File string
 | 
			
		||||
	Line int
 | 
			
		||||
	Addr uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bpe BreakpointExistsError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.File, bpe.Line, bpe.Addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// InvalidAddressError represents the result of
 | 
			
		||||
// attempting to set a breakpoint at an invalid address.
 | 
			
		||||
type InvalidAddressError struct {
 | 
			
		||||
	Address uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (iae InvalidAddressError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("Invalid address %#v\n", iae.Address)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type returnBreakpointInfo struct {
 | 
			
		||||
	retFrameCond ast.Expr
 | 
			
		||||
	fn           *Function
 | 
			
		||||
	frameOffset  int64
 | 
			
		||||
	spOffset     int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckCondition evaluates bp's condition on thread.
 | 
			
		||||
func (bp *Breakpoint) CheckCondition(thread Thread) BreakpointState {
 | 
			
		||||
	bpstate := BreakpointState{Breakpoint: bp, Active: false, Internal: false, CondError: nil}
 | 
			
		||||
	if bp.Cond == nil && bp.internalCond == nil {
 | 
			
		||||
		bpstate.Active = true
 | 
			
		||||
		bpstate.Internal = bp.IsInternal()
 | 
			
		||||
		return bpstate
 | 
			
		||||
	}
 | 
			
		||||
	nextDeferOk := true
 | 
			
		||||
	if bp.Kind&NextDeferBreakpoint != 0 {
 | 
			
		||||
		frames, err := ThreadStacktrace(thread, 2)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
 | 
			
		||||
			isdeferreturn := false
 | 
			
		||||
			if len(frames) >= 1 {
 | 
			
		||||
				for _, pc := range bp.DeferReturns {
 | 
			
		||||
					if frames[0].Ret == pc {
 | 
			
		||||
						isdeferreturn = true
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			nextDeferOk = ispanic || isdeferreturn
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if bp.IsInternal() {
 | 
			
		||||
		// Check internalCondition if this is also an internal breakpoint
 | 
			
		||||
		bpstate.Active, bpstate.CondError = evalBreakpointCondition(thread, bp.internalCond)
 | 
			
		||||
		bpstate.Active = bpstate.Active && nextDeferOk
 | 
			
		||||
		if bpstate.Active || bpstate.CondError != nil {
 | 
			
		||||
			bpstate.Internal = true
 | 
			
		||||
			return bpstate
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if bp.IsUser() {
 | 
			
		||||
		// Check normal condition if this is also a user breakpoint
 | 
			
		||||
		bpstate.Active, bpstate.CondError = evalBreakpointCondition(thread, bp.Cond)
 | 
			
		||||
	}
 | 
			
		||||
	return bpstate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsInternal returns true if bp is an internal breakpoint.
 | 
			
		||||
// User-set breakpoints can overlap with internal breakpoints, in that case
 | 
			
		||||
// both IsUser and IsInternal will be true.
 | 
			
		||||
func (bp *Breakpoint) IsInternal() bool {
 | 
			
		||||
	return bp.Kind != UserBreakpoint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsUser returns true if bp is a user-set breakpoint.
 | 
			
		||||
// User-set breakpoints can overlap with internal breakpoints, in that case
 | 
			
		||||
// both IsUser and IsInternal will be true.
 | 
			
		||||
func (bp *Breakpoint) IsUser() bool {
 | 
			
		||||
	return bp.Kind&UserBreakpoint != 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func evalBreakpointCondition(thread Thread, cond ast.Expr) (bool, error) {
 | 
			
		||||
	if cond == nil {
 | 
			
		||||
		return true, nil
 | 
			
		||||
	}
 | 
			
		||||
	scope, err := GoroutineScope(thread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, err
 | 
			
		||||
	}
 | 
			
		||||
	v, err := scope.evalAST(cond)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return true, fmt.Errorf("error evaluating expression: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	if v.Kind != reflect.Bool {
 | 
			
		||||
		return true, errors.New("condition expression not boolean")
 | 
			
		||||
	}
 | 
			
		||||
	v.loadValue(loadFullValue)
 | 
			
		||||
	if v.Unreadable != nil {
 | 
			
		||||
		return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
 | 
			
		||||
	}
 | 
			
		||||
	return constant.BoolVal(v.Value), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NoBreakpointError is returned when trying to
 | 
			
		||||
// clear a breakpoint that does not exist.
 | 
			
		||||
type NoBreakpointError struct {
 | 
			
		||||
	Addr uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (nbp NoBreakpointError) Error() string {
 | 
			
		||||
	return fmt.Sprintf("no breakpoint at %#v", nbp.Addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointMap represents an (address, breakpoint) map.
 | 
			
		||||
type BreakpointMap struct {
 | 
			
		||||
	M map[uint64]*Breakpoint
 | 
			
		||||
 | 
			
		||||
	breakpointIDCounter         int
 | 
			
		||||
	internalBreakpointIDCounter int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBreakpointMap creates a new BreakpointMap.
 | 
			
		||||
func NewBreakpointMap() BreakpointMap {
 | 
			
		||||
	return BreakpointMap{
 | 
			
		||||
		M: make(map[uint64]*Breakpoint),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResetBreakpointIDCounter resets the breakpoint ID counter of bpmap.
 | 
			
		||||
func (bpmap *BreakpointMap) ResetBreakpointIDCounter() {
 | 
			
		||||
	bpmap.breakpointIDCounter = 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteBreakpointFn is a type that represents a function to be used for
 | 
			
		||||
// writting breakpoings into the target.
 | 
			
		||||
type WriteBreakpointFn func(addr uint64) (file string, line int, fn *Function, originalData []byte, err error)
 | 
			
		||||
 | 
			
		||||
type clearBreakpointFn func(*Breakpoint) error
 | 
			
		||||
 | 
			
		||||
// Set creates a breakpoint at addr calling writeBreakpoint. Do not call this
 | 
			
		||||
// function, call proc.Process.SetBreakpoint instead, this function exists
 | 
			
		||||
// to implement proc.Process.SetBreakpoint.
 | 
			
		||||
func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, writeBreakpoint WriteBreakpointFn) (*Breakpoint, error) {
 | 
			
		||||
	if bp, ok := bpmap.M[addr]; ok {
 | 
			
		||||
		// We can overlap one internal breakpoint with one user breakpoint, we
 | 
			
		||||
		// need to support this otherwise a conditional breakpoint can mask a
 | 
			
		||||
		// breakpoint set by next or step.
 | 
			
		||||
		if (kind != UserBreakpoint && bp.Kind != UserBreakpoint) || (kind == UserBreakpoint && bp.IsUser()) {
 | 
			
		||||
			return bp, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
 | 
			
		||||
		}
 | 
			
		||||
		bp.Kind |= kind
 | 
			
		||||
		if kind != UserBreakpoint {
 | 
			
		||||
			bp.internalCond = cond
 | 
			
		||||
		} else {
 | 
			
		||||
			bp.Cond = cond
 | 
			
		||||
		}
 | 
			
		||||
		return bp, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, l, fn, originalData, err := writeBreakpoint(addr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fnName := ""
 | 
			
		||||
	if fn != nil {
 | 
			
		||||
		fnName = fn.Name
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newBreakpoint := &Breakpoint{
 | 
			
		||||
		FunctionName: fnName,
 | 
			
		||||
		File:         f,
 | 
			
		||||
		Line:         l,
 | 
			
		||||
		Addr:         addr,
 | 
			
		||||
		Kind:         kind,
 | 
			
		||||
		OriginalData: originalData,
 | 
			
		||||
		HitCount:     map[int]uint64{},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if kind != UserBreakpoint {
 | 
			
		||||
		bpmap.internalBreakpointIDCounter++
 | 
			
		||||
		newBreakpoint.ID = bpmap.internalBreakpointIDCounter
 | 
			
		||||
		newBreakpoint.internalCond = cond
 | 
			
		||||
	} else {
 | 
			
		||||
		bpmap.breakpointIDCounter++
 | 
			
		||||
		newBreakpoint.ID = bpmap.breakpointIDCounter
 | 
			
		||||
		newBreakpoint.Cond = cond
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bpmap.M[addr] = newBreakpoint
 | 
			
		||||
 | 
			
		||||
	return newBreakpoint, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetWithID creates a breakpoint at addr, with the specified ID.
 | 
			
		||||
func (bpmap *BreakpointMap) SetWithID(id int, addr uint64, writeBreakpoint WriteBreakpointFn) (*Breakpoint, error) {
 | 
			
		||||
	bp, err := bpmap.Set(addr, UserBreakpoint, nil, writeBreakpoint)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bp.ID = id
 | 
			
		||||
		bpmap.breakpointIDCounter--
 | 
			
		||||
	}
 | 
			
		||||
	return bp, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clear clears the breakpoint at addr.
 | 
			
		||||
// Do not call this function call proc.Process.ClearBreakpoint instead.
 | 
			
		||||
func (bpmap *BreakpointMap) Clear(addr uint64, clearBreakpoint clearBreakpointFn) (*Breakpoint, error) {
 | 
			
		||||
	bp, ok := bpmap.M[addr]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, NoBreakpointError{Addr: addr}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bp.Kind &= ^UserBreakpoint
 | 
			
		||||
	bp.Cond = nil
 | 
			
		||||
	if bp.Kind != 0 {
 | 
			
		||||
		return bp, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := clearBreakpoint(bp); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete(bpmap.M, addr)
 | 
			
		||||
 | 
			
		||||
	return bp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearInternalBreakpoints removes all internal breakpoints from the map,
 | 
			
		||||
// calling clearBreakpoint on each one.
 | 
			
		||||
// Do not call this function, call proc.Process.ClearInternalBreakpoints
 | 
			
		||||
// instead, this function is used to implement that.
 | 
			
		||||
func (bpmap *BreakpointMap) ClearInternalBreakpoints(clearBreakpoint clearBreakpointFn) error {
 | 
			
		||||
	for addr, bp := range bpmap.M {
 | 
			
		||||
		bp.Kind = bp.Kind & UserBreakpoint
 | 
			
		||||
		bp.internalCond = nil
 | 
			
		||||
		bp.returnInfo = nil
 | 
			
		||||
		if bp.Kind != 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if err := clearBreakpoint(bp); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		delete(bpmap.M, addr)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// HasInternalBreakpoints returns true if bpmap has at least one internal
 | 
			
		||||
// breakpoint set.
 | 
			
		||||
func (bpmap *BreakpointMap) HasInternalBreakpoints() bool {
 | 
			
		||||
	for _, bp := range bpmap.M {
 | 
			
		||||
		if bp.IsInternal() {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointState describes the state of a breakpoint in a thread.
 | 
			
		||||
type BreakpointState struct {
 | 
			
		||||
	*Breakpoint
 | 
			
		||||
	// Active is true if the breakpoint condition was met.
 | 
			
		||||
	Active bool
 | 
			
		||||
	// Internal is true if the breakpoint was matched as an internal
 | 
			
		||||
	// breakpoint.
 | 
			
		||||
	Internal bool
 | 
			
		||||
	// CondError contains any error encountered while evaluating the
 | 
			
		||||
	// breakpoint's condition.
 | 
			
		||||
	CondError error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clear zeros the struct.
 | 
			
		||||
func (bpstate *BreakpointState) Clear() {
 | 
			
		||||
	bpstate.Breakpoint = nil
 | 
			
		||||
	bpstate.Active = false
 | 
			
		||||
	bpstate.Internal = false
 | 
			
		||||
	bpstate.CondError = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bpstate *BreakpointState) String() string {
 | 
			
		||||
	s := bpstate.Breakpoint.String()
 | 
			
		||||
	if bpstate.Active {
 | 
			
		||||
		s += " active"
 | 
			
		||||
	}
 | 
			
		||||
	if bpstate.Internal {
 | 
			
		||||
		s += " internal"
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func configureReturnBreakpoint(bi *BinaryInfo, bp *Breakpoint, topframe *Stackframe, retFrameCond ast.Expr) {
 | 
			
		||||
	if topframe.Current.Fn == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	bp.returnInfo = &returnBreakpointInfo{
 | 
			
		||||
		retFrameCond: retFrameCond,
 | 
			
		||||
		fn:           topframe.Current.Fn,
 | 
			
		||||
		frameOffset:  topframe.FrameOffset(),
 | 
			
		||||
		spOffset:     topframe.FrameOffset() - int64(bi.Arch.PtrSize()), // must be the value that SP had at the entry point of the function
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (rbpi *returnBreakpointInfo) Collect(thread Thread) []*Variable {
 | 
			
		||||
	if rbpi == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g, err := GetG(thread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return returnInfoError("could not get g", err, thread)
 | 
			
		||||
	}
 | 
			
		||||
	scope, err := GoroutineScope(thread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return returnInfoError("could not get scope", err, thread)
 | 
			
		||||
	}
 | 
			
		||||
	v, err := scope.evalAST(rbpi.retFrameCond)
 | 
			
		||||
	if err != nil || v.Unreadable != nil || v.Kind != reflect.Bool {
 | 
			
		||||
		// This condition was evaluated as part of the breakpoint condition
 | 
			
		||||
		// evaluation, if the errors happen they will be reported as part of the
 | 
			
		||||
		// condition errors.
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !constant.BoolVal(v.Value) {
 | 
			
		||||
		// Breakpoint not hit as a return breakpoint.
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oldFrameOffset := rbpi.frameOffset + int64(g.stackhi)
 | 
			
		||||
	oldSP := uint64(rbpi.spOffset + int64(g.stackhi))
 | 
			
		||||
	err = fakeFunctionEntryScope(scope, rbpi.fn, oldFrameOffset, oldSP)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return returnInfoError("could not read function entry", err, thread)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	vars, err := scope.Locals()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return returnInfoError("could not evaluate return variables", err, thread)
 | 
			
		||||
	}
 | 
			
		||||
	vars = filterVariables(vars, func(v *Variable) bool {
 | 
			
		||||
		return (v.Flags & VariableReturnArgument) != 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return vars
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func returnInfoError(descr string, err error, mem MemoryReadWriter) []*Variable {
 | 
			
		||||
	v := newConstant(constant.MakeString(fmt.Sprintf("%s: %v", descr, err.Error())), mem)
 | 
			
		||||
	v.Name = "return value read error"
 | 
			
		||||
	return []*Variable{v}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										498
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/core.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										498
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/core.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,498 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A SplicedMemory represents a memory space formed from multiple regions,
 | 
			
		||||
// each of which may override previously regions. For example, in the following
 | 
			
		||||
// core, the program text was loaded at 0x400000:
 | 
			
		||||
// Start               End                 Page Offset
 | 
			
		||||
// 0x0000000000400000  0x000000000044f000  0x0000000000000000
 | 
			
		||||
// but then it's partially overwritten with an RW mapping whose data is stored
 | 
			
		||||
// in the core file:
 | 
			
		||||
// Type           Offset             VirtAddr           PhysAddr
 | 
			
		||||
//                FileSiz            MemSiz              Flags  Align
 | 
			
		||||
// LOAD           0x0000000000004000 0x000000000049a000 0x0000000000000000
 | 
			
		||||
//                0x0000000000002000 0x0000000000002000  RW     1000
 | 
			
		||||
// This can be represented in a SplicedMemory by adding the original region,
 | 
			
		||||
// then putting the RW mapping on top of it.
 | 
			
		||||
type SplicedMemory struct {
 | 
			
		||||
	readers []readerEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type readerEntry struct {
 | 
			
		||||
	offset uintptr
 | 
			
		||||
	length uintptr
 | 
			
		||||
	reader proc.MemoryReader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Add adds a new region to the SplicedMemory, which may override existing regions.
 | 
			
		||||
func (r *SplicedMemory) Add(reader proc.MemoryReader, off, length uintptr) {
 | 
			
		||||
	if length == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	end := off + length - 1
 | 
			
		||||
	newReaders := make([]readerEntry, 0, len(r.readers))
 | 
			
		||||
	add := func(e readerEntry) {
 | 
			
		||||
		if e.length == 0 {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		newReaders = append(newReaders, e)
 | 
			
		||||
	}
 | 
			
		||||
	inserted := false
 | 
			
		||||
	// Walk through the list of regions, fixing up any that overlap and inserting the new one.
 | 
			
		||||
	for _, entry := range r.readers {
 | 
			
		||||
		entryEnd := entry.offset + entry.length - 1
 | 
			
		||||
		switch {
 | 
			
		||||
		case entryEnd < off:
 | 
			
		||||
			// Entry is completely before the new region.
 | 
			
		||||
			add(entry)
 | 
			
		||||
		case end < entry.offset:
 | 
			
		||||
			// Entry is completely after the new region.
 | 
			
		||||
			if !inserted {
 | 
			
		||||
				add(readerEntry{off, length, reader})
 | 
			
		||||
				inserted = true
 | 
			
		||||
			}
 | 
			
		||||
			add(entry)
 | 
			
		||||
		case off <= entry.offset && entryEnd <= end:
 | 
			
		||||
			// Entry is completely overwritten by the new region. Drop.
 | 
			
		||||
		case entry.offset < off && entryEnd <= end:
 | 
			
		||||
			// New region overwrites the end of the entry.
 | 
			
		||||
			entry.length = off - entry.offset
 | 
			
		||||
			add(entry)
 | 
			
		||||
		case off <= entry.offset && end < entryEnd:
 | 
			
		||||
			// New reader overwrites the beginning of the entry.
 | 
			
		||||
			if !inserted {
 | 
			
		||||
				add(readerEntry{off, length, reader})
 | 
			
		||||
				inserted = true
 | 
			
		||||
			}
 | 
			
		||||
			overlap := entry.offset - off
 | 
			
		||||
			entry.offset += overlap
 | 
			
		||||
			entry.length -= overlap
 | 
			
		||||
			add(entry)
 | 
			
		||||
		case entry.offset < off && end < entryEnd:
 | 
			
		||||
			// New region punches a hole in the entry. Split it in two and put the new region in the middle.
 | 
			
		||||
			add(readerEntry{entry.offset, off - entry.offset, entry.reader})
 | 
			
		||||
			add(readerEntry{off, length, reader})
 | 
			
		||||
			add(readerEntry{end + 1, entryEnd - end, entry.reader})
 | 
			
		||||
			inserted = true
 | 
			
		||||
		default:
 | 
			
		||||
			panic(fmt.Sprintf("Unhandled case: existing entry is %v len %v, new is %v len %v", entry.offset, entry.length, off, length))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if !inserted {
 | 
			
		||||
		newReaders = append(newReaders, readerEntry{off, length, reader})
 | 
			
		||||
	}
 | 
			
		||||
	r.readers = newReaders
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadMemory implements MemoryReader.ReadMemory.
 | 
			
		||||
func (r *SplicedMemory) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
 | 
			
		||||
	started := false
 | 
			
		||||
	for _, entry := range r.readers {
 | 
			
		||||
		if entry.offset+entry.length < addr {
 | 
			
		||||
			if !started {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			return n, fmt.Errorf("hit unmapped area at %v after %v bytes", addr, n)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Don't go past the region.
 | 
			
		||||
		pb := buf
 | 
			
		||||
		if addr+uintptr(len(buf)) > entry.offset+entry.length {
 | 
			
		||||
			pb = pb[:entry.offset+entry.length-addr]
 | 
			
		||||
		}
 | 
			
		||||
		pn, err := entry.reader.ReadMemory(pb, addr)
 | 
			
		||||
		n += pn
 | 
			
		||||
		if err != nil || pn != len(pb) {
 | 
			
		||||
			return n, err
 | 
			
		||||
		}
 | 
			
		||||
		buf = buf[pn:]
 | 
			
		||||
		addr += uintptr(pn)
 | 
			
		||||
		if len(buf) == 0 {
 | 
			
		||||
			// Done, don't bother scanning the rest.
 | 
			
		||||
			return n, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if n == 0 {
 | 
			
		||||
		return 0, fmt.Errorf("offset %v did not match any regions", addr)
 | 
			
		||||
	}
 | 
			
		||||
	return n, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OffsetReaderAt wraps a ReaderAt into a MemoryReader, subtracting a fixed
 | 
			
		||||
// offset from the address. This is useful to represent a mapping in an address
 | 
			
		||||
// space. For example, if program text is mapped in at 0x400000, an
 | 
			
		||||
// OffsetReaderAt with offset 0x400000 can be wrapped around file.Open(program)
 | 
			
		||||
// to return the results of a read in that part of the address space.
 | 
			
		||||
type OffsetReaderAt struct {
 | 
			
		||||
	reader io.ReaderAt
 | 
			
		||||
	offset uintptr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadMemory will read the memory at addr-offset.
 | 
			
		||||
func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
 | 
			
		||||
	return r.reader.ReadAt(buf, int64(addr-r.offset))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Process represents a core file.
 | 
			
		||||
type Process struct {
 | 
			
		||||
	mem     proc.MemoryReader
 | 
			
		||||
	Threads map[int]*Thread
 | 
			
		||||
	pid     int
 | 
			
		||||
 | 
			
		||||
	entryPoint uint64
 | 
			
		||||
 | 
			
		||||
	bi                *proc.BinaryInfo
 | 
			
		||||
	breakpoints       proc.BreakpointMap
 | 
			
		||||
	currentThread     *Thread
 | 
			
		||||
	selectedGoroutine *proc.G
 | 
			
		||||
	common            proc.CommonProcess
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Thread represents a thread in the core file being debugged.
 | 
			
		||||
type Thread struct {
 | 
			
		||||
	th     osThread
 | 
			
		||||
	p      *Process
 | 
			
		||||
	common proc.CommonThread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type osThread interface {
 | 
			
		||||
	registers(floatingPoint bool) (proc.Registers, error)
 | 
			
		||||
	pid() int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrWriteCore is returned when attempting to write to the core
 | 
			
		||||
	// process memory.
 | 
			
		||||
	ErrWriteCore = errors.New("can not write to core process")
 | 
			
		||||
 | 
			
		||||
	// ErrShortRead is returned on a short read.
 | 
			
		||||
	ErrShortRead = errors.New("short read")
 | 
			
		||||
 | 
			
		||||
	// ErrContinueCore is returned when trying to continue execution of a core process.
 | 
			
		||||
	ErrContinueCore = errors.New("can not continue execution of core process")
 | 
			
		||||
 | 
			
		||||
	// ErrChangeRegisterCore is returned when trying to change register values for core files.
 | 
			
		||||
	ErrChangeRegisterCore = errors.New("can not change register values of core process")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type openFn func(string, string) (*Process, error)
 | 
			
		||||
 | 
			
		||||
var openFns = []openFn{readLinuxAMD64Core, readAMD64Minidump}
 | 
			
		||||
 | 
			
		||||
// ErrUnrecognizedFormat is returned when the core file is not recognized as
 | 
			
		||||
// any of the supported formats.
 | 
			
		||||
var ErrUnrecognizedFormat = errors.New("unrecognized core format")
 | 
			
		||||
 | 
			
		||||
// OpenCore will open the core file and return a Process struct.
 | 
			
		||||
// If the DWARF information cannot be found in the binary, Delve will look
 | 
			
		||||
// for external debug files in the directories passed in.
 | 
			
		||||
func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	var p *Process
 | 
			
		||||
	var err error
 | 
			
		||||
	for _, openFn := range openFns {
 | 
			
		||||
		p, err = openFn(corePath, exePath)
 | 
			
		||||
		if err != ErrUnrecognizedFormat {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := p.initialize(exePath, debugInfoDirs); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize for core files doesn't do much
 | 
			
		||||
// aside from call the post initialization setup.
 | 
			
		||||
func (p *Process) initialize(path string, debugInfoDirs []string) error {
 | 
			
		||||
	return proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BinInfo will return the binary info.
 | 
			
		||||
func (p *Process) BinInfo() *proc.BinaryInfo {
 | 
			
		||||
	return p.bi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSelectedGoroutine will set internally the goroutine that should be
 | 
			
		||||
// the default for any command executed, the goroutine being actively
 | 
			
		||||
// followed.
 | 
			
		||||
func (p *Process) SetSelectedGoroutine(g *proc.G) {
 | 
			
		||||
	p.selectedGoroutine = g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EntryPoint will return the entry point address for this core file.
 | 
			
		||||
func (p *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	return p.entryPoint, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeBreakpoint is a noop function since you
 | 
			
		||||
// cannot write breakpoints into core files.
 | 
			
		||||
func (p *Process) writeBreakpoint(addr uint64) (file string, line int, fn *proc.Function, originalData []byte, err error) {
 | 
			
		||||
	return "", 0, nil, nil, errors.New("cannot write a breakpoint to a core file")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Recorded returns whether this is a live or recorded process. Always returns true for core files.
 | 
			
		||||
func (p *Process) Recorded() (bool, string) { return true, "" }
 | 
			
		||||
 | 
			
		||||
// Restart will only return an error for core files, as they are not executing.
 | 
			
		||||
func (p *Process) Restart(string) error { return ErrContinueCore }
 | 
			
		||||
 | 
			
		||||
// Direction will only return an error as you cannot continue a core process.
 | 
			
		||||
func (p *Process) Direction(proc.Direction) error { return ErrContinueCore }
 | 
			
		||||
 | 
			
		||||
// When does not apply to core files, it is to support the Mozilla 'rr' backend.
 | 
			
		||||
func (p *Process) When() (string, error) { return "", nil }
 | 
			
		||||
 | 
			
		||||
// Checkpoint for core files returns an error, there is no execution of a core file.
 | 
			
		||||
func (p *Process) Checkpoint(string) (int, error) { return -1, ErrContinueCore }
 | 
			
		||||
 | 
			
		||||
// Checkpoints returns nil on core files, you cannot set checkpoints when debugging core files.
 | 
			
		||||
func (p *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, nil }
 | 
			
		||||
 | 
			
		||||
// ClearCheckpoint clears a checkpoint, but will only return an error for core files.
 | 
			
		||||
func (p *Process) ClearCheckpoint(int) error { return errors.New("checkpoint not found") }
 | 
			
		||||
 | 
			
		||||
// ReadMemory will return memory from the core file at the specified location and put the
 | 
			
		||||
// read memory into `data`, returning the length read, and returning an error if
 | 
			
		||||
// the length read is shorter than the length of the `data` buffer.
 | 
			
		||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
 | 
			
		||||
	n, err = t.p.mem.ReadMemory(data, addr)
 | 
			
		||||
	if err == nil && n != len(data) {
 | 
			
		||||
		err = ErrShortRead
 | 
			
		||||
	}
 | 
			
		||||
	return n, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMemory will only return an error for core files, you cannot write
 | 
			
		||||
// to the memory of a core process.
 | 
			
		||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
 | 
			
		||||
	return 0, ErrWriteCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Location returns the location of this thread based on
 | 
			
		||||
// the value of the instruction pointer register.
 | 
			
		||||
func (t *Thread) Location() (*proc.Location, error) {
 | 
			
		||||
	regs, err := t.th.registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pc := regs.PC()
 | 
			
		||||
	f, l, fn := t.p.bi.PCToLine(pc)
 | 
			
		||||
	return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Breakpoint returns the current breakpoint this thread is stopped at.
 | 
			
		||||
// For core files this always returns an empty BreakpointState struct, as
 | 
			
		||||
// there are no breakpoints when debugging core files.
 | 
			
		||||
func (t *Thread) Breakpoint() proc.BreakpointState {
 | 
			
		||||
	return proc.BreakpointState{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ThreadID returns the ID for this thread.
 | 
			
		||||
func (t *Thread) ThreadID() int {
 | 
			
		||||
	return int(t.th.pid())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers returns the current value of the registers for this thread.
 | 
			
		||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	return t.th.registers(floatingPoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RestoreRegisters will only return an error for core files,
 | 
			
		||||
// you cannot change register values for core files.
 | 
			
		||||
func (t *Thread) RestoreRegisters(proc.Registers) error {
 | 
			
		||||
	return ErrChangeRegisterCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Arch returns the architecture the target is built for and executing on.
 | 
			
		||||
func (t *Thread) Arch() proc.Arch {
 | 
			
		||||
	return t.p.bi.Arch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BinInfo returns information about the binary.
 | 
			
		||||
func (t *Thread) BinInfo() *proc.BinaryInfo {
 | 
			
		||||
	return t.p.bi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepInstruction will only return an error for core files,
 | 
			
		||||
// you cannot execute a core file.
 | 
			
		||||
func (t *Thread) StepInstruction() error {
 | 
			
		||||
	return ErrContinueCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Blocked will return false always for core files as there is
 | 
			
		||||
// no execution.
 | 
			
		||||
func (t *Thread) Blocked() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetCurrentBreakpoint will always just return nil
 | 
			
		||||
// for core files, as there are no breakpoints in core files.
 | 
			
		||||
func (t *Thread) SetCurrentBreakpoint() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Common returns a struct containing common information
 | 
			
		||||
// across thread implementations.
 | 
			
		||||
func (t *Thread) Common() *proc.CommonThread {
 | 
			
		||||
	return &t.common
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC will always return an error, you cannot
 | 
			
		||||
// change register values when debugging core files.
 | 
			
		||||
func (t *Thread) SetPC(uint64) error {
 | 
			
		||||
	return ErrChangeRegisterCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP will always return an error, you cannot
 | 
			
		||||
// change register values when debugging core files.
 | 
			
		||||
func (t *Thread) SetSP(uint64) error {
 | 
			
		||||
	return ErrChangeRegisterCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDX will always return an error, you cannot
 | 
			
		||||
// change register values when debugging core files.
 | 
			
		||||
func (t *Thread) SetDX(uint64) error {
 | 
			
		||||
	return ErrChangeRegisterCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Breakpoints will return all breakpoints for the process.
 | 
			
		||||
func (p *Process) Breakpoints() *proc.BreakpointMap {
 | 
			
		||||
	return &p.breakpoints
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearBreakpoint will always return an error as you cannot set or clear
 | 
			
		||||
// breakpoints on core files.
 | 
			
		||||
func (p *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
 | 
			
		||||
	return nil, proc.NoBreakpointError{Addr: addr}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearInternalBreakpoints will always return nil and have no
 | 
			
		||||
// effect since you cannot set breakpoints on core files.
 | 
			
		||||
func (p *Process) ClearInternalBreakpoints() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContinueOnce will always return an error because you
 | 
			
		||||
// cannot control execution of a core file.
 | 
			
		||||
func (p *Process) ContinueOnce() (proc.Thread, error) {
 | 
			
		||||
	return nil, ErrContinueCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepInstruction will always return an error
 | 
			
		||||
// as you cannot control execution of a core file.
 | 
			
		||||
func (p *Process) StepInstruction() error {
 | 
			
		||||
	return ErrContinueCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestManualStop will return nil and have no effect
 | 
			
		||||
// as you cannot control execution of a core file.
 | 
			
		||||
func (p *Process) RequestManualStop() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckAndClearManualStopRequest will always return false and
 | 
			
		||||
// have no effect since there are no manual stop requests as
 | 
			
		||||
// there is no controlling execution of a core file.
 | 
			
		||||
func (p *Process) CheckAndClearManualStopRequest() bool {
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurrentThread returns the current active thread.
 | 
			
		||||
func (p *Process) CurrentThread() proc.Thread {
 | 
			
		||||
	return p.currentThread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Detach will always return nil and have no
 | 
			
		||||
// effect as you cannot detach from a core file
 | 
			
		||||
// and have it continue execution or exit.
 | 
			
		||||
func (p *Process) Detach(bool) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Valid returns whether the process is active. Always returns true
 | 
			
		||||
// for core files as it cannot exit or be otherwise detached from.
 | 
			
		||||
func (p *Process) Valid() (bool, error) {
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Common returns common information across Process
 | 
			
		||||
// implementations.
 | 
			
		||||
func (p *Process) Common() *proc.CommonProcess {
 | 
			
		||||
	return &p.common
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pid returns the process ID of this process.
 | 
			
		||||
func (p *Process) Pid() int {
 | 
			
		||||
	return p.pid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResumeNotify is a no-op on core files as we cannot
 | 
			
		||||
// control execution.
 | 
			
		||||
func (p *Process) ResumeNotify(chan<- struct{}) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SelectedGoroutine returns the current active and selected
 | 
			
		||||
// goroutine.
 | 
			
		||||
func (p *Process) SelectedGoroutine() *proc.G {
 | 
			
		||||
	return p.selectedGoroutine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetBreakpoint will always return an error for core files as you cannot write memory or control execution.
 | 
			
		||||
func (p *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
 | 
			
		||||
	return nil, ErrWriteCore
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SwitchGoroutine will change the selected and active goroutine.
 | 
			
		||||
func (p *Process) SwitchGoroutine(gid int) error {
 | 
			
		||||
	g, err := proc.FindGoroutine(p, gid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		// user specified -1 and selectedGoroutine is nil
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if g.Thread != nil {
 | 
			
		||||
		return p.SwitchThread(g.Thread.ThreadID())
 | 
			
		||||
	}
 | 
			
		||||
	p.selectedGoroutine = g
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SwitchThread will change the selected and active thread.
 | 
			
		||||
func (p *Process) SwitchThread(tid int) error {
 | 
			
		||||
	if th, ok := p.Threads[tid]; ok {
 | 
			
		||||
		p.currentThread = th
 | 
			
		||||
		p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("thread %d does not exist", tid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ThreadList will return a list of all threads currently in the process.
 | 
			
		||||
func (p *Process) ThreadList() []proc.Thread {
 | 
			
		||||
	r := make([]proc.Thread, 0, len(p.Threads))
 | 
			
		||||
	for _, v := range p.Threads {
 | 
			
		||||
		r = append(r, v)
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindThread will return the thread with the corresponding thread ID.
 | 
			
		||||
func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
 | 
			
		||||
	t, ok := p.Threads[threadID]
 | 
			
		||||
	return t, ok
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										351
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/linux_amd64_core.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/linux_amd64_core.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,351 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"debug/elf"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Copied from golang.org/x/sys/unix.Timeval since it's not available on all
 | 
			
		||||
// systems.
 | 
			
		||||
type LinuxCoreTimeval struct {
 | 
			
		||||
	Sec  int64
 | 
			
		||||
	Usec int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NT_FILE is file mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
 | 
			
		||||
const NT_FILE elf.NType = 0x46494c45 // "FILE".
 | 
			
		||||
 | 
			
		||||
// NT_X86_XSTATE is other registers, including AVX and such.
 | 
			
		||||
const NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAVE area.
 | 
			
		||||
 | 
			
		||||
// NT_AUXV is the note type for notes containing a copy of the Auxv array
 | 
			
		||||
const NT_AUXV elf.NType = 0x6
 | 
			
		||||
 | 
			
		||||
const elfErrorBadMagicNumber = "bad magic number"
 | 
			
		||||
 | 
			
		||||
// readLinuxAMD64Core reads a core file from corePath corresponding to the executable at
 | 
			
		||||
// exePath. For details on the Linux ELF core format, see:
 | 
			
		||||
// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
 | 
			
		||||
// http://uhlo.blogspot.fr/2012/05/brief-look-into-core-dumps.html,
 | 
			
		||||
// elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c,
 | 
			
		||||
// and, if absolutely desperate, readelf.c from the binutils source.
 | 
			
		||||
func readLinuxAMD64Core(corePath, exePath string) (*Process, error) {
 | 
			
		||||
	coreFile, err := elf.Open(corePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if _, isfmterr := err.(*elf.FormatError); isfmterr && (strings.Contains(err.Error(), elfErrorBadMagicNumber) || strings.Contains(err.Error(), " at offset 0x0: too short")) {
 | 
			
		||||
			// Go >=1.11 and <1.11 produce different errors when reading a non-elf file.
 | 
			
		||||
			return nil, ErrUnrecognizedFormat
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	exe, err := os.Open(exePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	exeELF, err := elf.NewFile(exe)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if coreFile.Type != elf.ET_CORE {
 | 
			
		||||
		return nil, fmt.Errorf("%v is not a core file", coreFile)
 | 
			
		||||
	}
 | 
			
		||||
	if exeELF.Type != elf.ET_EXEC && exeELF.Type != elf.ET_DYN {
 | 
			
		||||
		return nil, fmt.Errorf("%v is not an exe file", exeELF)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	notes, err := readNotes(coreFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	memory := buildMemory(coreFile, exeELF, exe, notes)
 | 
			
		||||
	entryPoint := findEntryPoint(notes)
 | 
			
		||||
 | 
			
		||||
	p := &Process{
 | 
			
		||||
		mem:         memory,
 | 
			
		||||
		Threads:     map[int]*Thread{},
 | 
			
		||||
		entryPoint:  entryPoint,
 | 
			
		||||
		bi:          proc.NewBinaryInfo("linux", "amd64"),
 | 
			
		||||
		breakpoints: proc.NewBreakpointMap(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var lastThread *linuxAMD64Thread
 | 
			
		||||
	for _, note := range notes {
 | 
			
		||||
		switch note.Type {
 | 
			
		||||
		case elf.NT_PRSTATUS:
 | 
			
		||||
			t := note.Desc.(*LinuxPrStatus)
 | 
			
		||||
			lastThread = &linuxAMD64Thread{linutil.AMD64Registers{Regs: &t.Reg}, t}
 | 
			
		||||
			p.Threads[int(t.Pid)] = &Thread{lastThread, p, proc.CommonThread{}}
 | 
			
		||||
			if p.currentThread == nil {
 | 
			
		||||
				p.currentThread = p.Threads[int(t.Pid)]
 | 
			
		||||
			}
 | 
			
		||||
		case NT_X86_XSTATE:
 | 
			
		||||
			if lastThread != nil {
 | 
			
		||||
				lastThread.regs.Fpregs = note.Desc.(*linutil.AMD64Xstate).Decode()
 | 
			
		||||
			}
 | 
			
		||||
		case elf.NT_PRPSINFO:
 | 
			
		||||
			p.pid = int(note.Desc.(*LinuxPrPsInfo).Pid)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type linuxAMD64Thread struct {
 | 
			
		||||
	regs linutil.AMD64Registers
 | 
			
		||||
	t    *LinuxPrStatus
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *linuxAMD64Thread) registers(floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	var r linutil.AMD64Registers
 | 
			
		||||
	r.Regs = t.regs.Regs
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		r.Fpregs = t.regs.Fpregs
 | 
			
		||||
	}
 | 
			
		||||
	return &r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *linuxAMD64Thread) pid() int {
 | 
			
		||||
	return int(t.t.Pid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Note is a note from the PT_NOTE prog.
 | 
			
		||||
// Relevant types:
 | 
			
		||||
// - NT_FILE: File mapping information, e.g. program text mappings. Desc is a LinuxNTFile.
 | 
			
		||||
// - NT_PRPSINFO: Information about a process, including PID and signal. Desc is a LinuxPrPsInfo.
 | 
			
		||||
// - NT_PRSTATUS: Information about a thread, including base registers, state, etc. Desc is a LinuxPrStatus.
 | 
			
		||||
// - NT_FPREGSET (Not implemented): x87 floating point registers.
 | 
			
		||||
// - NT_X86_XSTATE: Other registers, including AVX and such.
 | 
			
		||||
type Note struct {
 | 
			
		||||
	Type elf.NType
 | 
			
		||||
	Name string
 | 
			
		||||
	Desc interface{} // Decoded Desc from the
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readNotes reads all the notes from the notes prog in core.
 | 
			
		||||
func readNotes(core *elf.File) ([]*Note, error) {
 | 
			
		||||
	var notesProg *elf.Prog
 | 
			
		||||
	for _, prog := range core.Progs {
 | 
			
		||||
		if prog.Type == elf.PT_NOTE {
 | 
			
		||||
			notesProg = prog
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := notesProg.Open()
 | 
			
		||||
	notes := []*Note{}
 | 
			
		||||
	for {
 | 
			
		||||
		note, err := readNote(r)
 | 
			
		||||
		if err == io.EOF {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		notes = append(notes, note)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return notes, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readNote reads a single note from r, decoding the descriptor if possible.
 | 
			
		||||
func readNote(r io.ReadSeeker) (*Note, error) {
 | 
			
		||||
	// Notes are laid out as described in the SysV ABI:
 | 
			
		||||
	// http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section
 | 
			
		||||
	note := &Note{}
 | 
			
		||||
	hdr := &ELFNotesHdr{}
 | 
			
		||||
 | 
			
		||||
	err := binary.Read(r, binary.LittleEndian, hdr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err // don't wrap so readNotes sees EOF.
 | 
			
		||||
	}
 | 
			
		||||
	note.Type = elf.NType(hdr.Type)
 | 
			
		||||
 | 
			
		||||
	name := make([]byte, hdr.Namesz)
 | 
			
		||||
	if _, err := r.Read(name); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("reading name: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	note.Name = string(name)
 | 
			
		||||
	if err := skipPadding(r, 4); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("aligning after name: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	desc := make([]byte, hdr.Descsz)
 | 
			
		||||
	if _, err := r.Read(desc); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("reading desc: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	descReader := bytes.NewReader(desc)
 | 
			
		||||
	switch note.Type {
 | 
			
		||||
	case elf.NT_PRSTATUS:
 | 
			
		||||
		note.Desc = &LinuxPrStatus{}
 | 
			
		||||
		if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("reading NT_PRSTATUS: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	case elf.NT_PRPSINFO:
 | 
			
		||||
		note.Desc = &LinuxPrPsInfo{}
 | 
			
		||||
		if err := binary.Read(descReader, binary.LittleEndian, note.Desc); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("reading NT_PRPSINFO: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	case NT_FILE:
 | 
			
		||||
		// No good documentation reference, but the structure is
 | 
			
		||||
		// simply a header, including entry count, followed by that
 | 
			
		||||
		// many entries, and then the file name of each entry,
 | 
			
		||||
		// null-delimited. Not reading the names here.
 | 
			
		||||
		data := &LinuxNTFile{}
 | 
			
		||||
		if err := binary.Read(descReader, binary.LittleEndian, &data.LinuxNTFileHdr); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("reading NT_FILE header: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		for i := 0; i < int(data.Count); i++ {
 | 
			
		||||
			entry := &LinuxNTFileEntry{}
 | 
			
		||||
			if err := binary.Read(descReader, binary.LittleEndian, entry); err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("reading NT_FILE entry %v: %v", i, err)
 | 
			
		||||
			}
 | 
			
		||||
			data.entries = append(data.entries, entry)
 | 
			
		||||
		}
 | 
			
		||||
		note.Desc = data
 | 
			
		||||
	case NT_X86_XSTATE:
 | 
			
		||||
		var fpregs linutil.AMD64Xstate
 | 
			
		||||
		if err := linutil.AMD64XstateRead(desc, true, &fpregs); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		note.Desc = &fpregs
 | 
			
		||||
	case NT_AUXV:
 | 
			
		||||
		note.Desc = desc
 | 
			
		||||
	}
 | 
			
		||||
	if err := skipPadding(r, 4); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("aligning after desc: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return note, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// skipPadding moves r to the next multiple of pad.
 | 
			
		||||
func skipPadding(r io.ReadSeeker, pad int64) error {
 | 
			
		||||
	pos, err := r.Seek(0, os.SEEK_CUR)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if pos%pad == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if _, err := r.Seek(pad-(pos%pad), os.SEEK_CUR); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildMemory(core, exeELF *elf.File, exe io.ReaderAt, notes []*Note) proc.MemoryReader {
 | 
			
		||||
	memory := &SplicedMemory{}
 | 
			
		||||
 | 
			
		||||
	// For now, assume all file mappings are to the exe.
 | 
			
		||||
	for _, note := range notes {
 | 
			
		||||
		if note.Type == NT_FILE {
 | 
			
		||||
			fileNote := note.Desc.(*LinuxNTFile)
 | 
			
		||||
			for _, entry := range fileNote.entries {
 | 
			
		||||
				r := &OffsetReaderAt{
 | 
			
		||||
					reader: exe,
 | 
			
		||||
					offset: uintptr(entry.Start - (entry.FileOfs * fileNote.PageSize)),
 | 
			
		||||
				}
 | 
			
		||||
				memory.Add(r, uintptr(entry.Start), uintptr(entry.End-entry.Start))
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Load memory segments from exe and then from the core file,
 | 
			
		||||
	// allowing the corefile to overwrite previously loaded segments
 | 
			
		||||
	for _, elfFile := range []*elf.File{exeELF, core} {
 | 
			
		||||
		for _, prog := range elfFile.Progs {
 | 
			
		||||
			if prog.Type == elf.PT_LOAD {
 | 
			
		||||
				if prog.Filesz == 0 {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				r := &OffsetReaderAt{
 | 
			
		||||
					reader: prog.ReaderAt,
 | 
			
		||||
					offset: uintptr(prog.Vaddr),
 | 
			
		||||
				}
 | 
			
		||||
				memory.Add(r, uintptr(prog.Vaddr), uintptr(prog.Filesz))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return memory
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findEntryPoint(notes []*Note) uint64 {
 | 
			
		||||
	for _, note := range notes {
 | 
			
		||||
		if note.Type == NT_AUXV {
 | 
			
		||||
			return linutil.EntryPointFromAuxvAMD64(note.Desc.([]byte))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LinuxPrPsInfo has various structures from the ELF spec and the Linux kernel.
 | 
			
		||||
// AMD64 specific primarily because of unix.PtraceRegs, but also
 | 
			
		||||
// because some of the fields are word sized.
 | 
			
		||||
// See http://lxr.free-electrons.com/source/include/uapi/linux/elfcore.h
 | 
			
		||||
type LinuxPrPsInfo struct {
 | 
			
		||||
	State                uint8
 | 
			
		||||
	Sname                int8
 | 
			
		||||
	Zomb                 uint8
 | 
			
		||||
	Nice                 int8
 | 
			
		||||
	_                    [4]uint8
 | 
			
		||||
	Flag                 uint64
 | 
			
		||||
	Uid, Gid             uint32
 | 
			
		||||
	Pid, Ppid, Pgrp, Sid int32
 | 
			
		||||
	Fname                [16]uint8
 | 
			
		||||
	Args                 [80]uint8
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LinuxPrStatus is a copy of the prstatus kernel struct.
 | 
			
		||||
type LinuxPrStatus struct {
 | 
			
		||||
	Siginfo                      LinuxSiginfo
 | 
			
		||||
	Cursig                       uint16
 | 
			
		||||
	_                            [2]uint8
 | 
			
		||||
	Sigpend                      uint64
 | 
			
		||||
	Sighold                      uint64
 | 
			
		||||
	Pid, Ppid, Pgrp, Sid         int32
 | 
			
		||||
	Utime, Stime, CUtime, CStime LinuxCoreTimeval
 | 
			
		||||
	Reg                          linutil.AMD64PtraceRegs
 | 
			
		||||
	Fpvalid                      int32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LinuxSiginfo is a copy of the
 | 
			
		||||
// siginfo kernel struct.
 | 
			
		||||
type LinuxSiginfo struct {
 | 
			
		||||
	Signo int32
 | 
			
		||||
	Code  int32
 | 
			
		||||
	Errno int32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LinuxNTFile contains information on mapped files.
 | 
			
		||||
type LinuxNTFile struct {
 | 
			
		||||
	LinuxNTFileHdr
 | 
			
		||||
	entries []*LinuxNTFileEntry
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LinuxNTFileHdr is a header struct for NTFile.
 | 
			
		||||
type LinuxNTFileHdr struct {
 | 
			
		||||
	Count    uint64
 | 
			
		||||
	PageSize uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LinuxNTFileEntry is an entry of an NT_FILE note.
 | 
			
		||||
type LinuxNTFileEntry struct {
 | 
			
		||||
	Start   uint64
 | 
			
		||||
	End     uint64
 | 
			
		||||
	FileOfs uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ELFNotesHdr is the ELF Notes header.
 | 
			
		||||
// Same size on 64 and 32-bit machines.
 | 
			
		||||
type ELFNotesHdr struct {
 | 
			
		||||
	Namesz uint32
 | 
			
		||||
	Descsz uint32
 | 
			
		||||
	Type   uint32
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										154
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/fileflags_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/fileflags_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
			
		||||
// Code generated by "stringer -type FileFlags,StreamType,Arch,MemoryState,MemoryType,MemoryProtection"; DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
package minidump
 | 
			
		||||
 | 
			
		||||
import "strconv"
 | 
			
		||||
 | 
			
		||||
const _FileFlags_name = "FileNormalFileWithDataSegsFileWithFullMemoryFileWithHandleDataFileFilterMemoryFileScanMemoryFileWithUnloadedModulesFileWithIncorrectlyReferencedMemoryFileFilterModulePathsFileWithProcessThreadDataFileWithPrivateReadWriteMemoryFileWithoutOptionalDataFileWithFullMemoryInfoFileWithThreadInfoFileWithCodeSegsFileWithoutAuxilliarySegsFileWithFullAuxilliaryStateFileWithPrivateCopyMemoryFileIgnoreInaccessibleMemoryFileWithTokenInformation"
 | 
			
		||||
 | 
			
		||||
var _FileFlags_map = map[FileFlags]string{
 | 
			
		||||
	0:      _FileFlags_name[0:10],
 | 
			
		||||
	1:      _FileFlags_name[10:26],
 | 
			
		||||
	2:      _FileFlags_name[26:44],
 | 
			
		||||
	4:      _FileFlags_name[44:62],
 | 
			
		||||
	8:      _FileFlags_name[62:78],
 | 
			
		||||
	16:     _FileFlags_name[78:92],
 | 
			
		||||
	32:     _FileFlags_name[92:115],
 | 
			
		||||
	64:     _FileFlags_name[115:150],
 | 
			
		||||
	128:    _FileFlags_name[150:171],
 | 
			
		||||
	256:    _FileFlags_name[171:196],
 | 
			
		||||
	512:    _FileFlags_name[196:226],
 | 
			
		||||
	1024:   _FileFlags_name[226:249],
 | 
			
		||||
	2048:   _FileFlags_name[249:271],
 | 
			
		||||
	4096:   _FileFlags_name[271:289],
 | 
			
		||||
	8192:   _FileFlags_name[289:305],
 | 
			
		||||
	16384:  _FileFlags_name[305:330],
 | 
			
		||||
	32768:  _FileFlags_name[330:357],
 | 
			
		||||
	65536:  _FileFlags_name[357:382],
 | 
			
		||||
	131072: _FileFlags_name[382:410],
 | 
			
		||||
	262144: _FileFlags_name[410:434],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i FileFlags) String() string {
 | 
			
		||||
	if str, ok := _FileFlags_map[i]; ok {
 | 
			
		||||
		return str
 | 
			
		||||
	}
 | 
			
		||||
	return "FileFlags(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _StreamType_name = "UnusedStreamReservedStream0ReservedStream1ThreadListStreamModuleListStreamMemoryListStreamExceptionStreamSystemInfoStreamThreadExListStreamMemory64ListStreamCommentStreamACommentStreamWHandleDataStreamFunctionTableStreamUnloadedModuleStreamMiscInfoStreamMemoryInfoListStreamThreadInfoListStreamHandleOperationListStreamTokenStreamJavascriptDataStreamSystemMemoryInfoStreamProcessVMCounterStream"
 | 
			
		||||
 | 
			
		||||
var _StreamType_index = [...]uint16{0, 12, 27, 42, 58, 74, 90, 105, 121, 139, 157, 171, 185, 201, 220, 240, 254, 274, 294, 319, 330, 350, 372, 394}
 | 
			
		||||
 | 
			
		||||
func (i StreamType) String() string {
 | 
			
		||||
	if i >= StreamType(len(_StreamType_index)-1) {
 | 
			
		||||
		return "StreamType(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
	return _StreamType_name[_StreamType_index[i]:_StreamType_index[i+1]]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_Arch_name_0 = "CpuArchitectureX86CpuArchitectureMipsCpuArchitectureAlphaCpuArchitecturePPCCpuArchitectureSHXCpuArchitectureARMCpuArchitectureIA64CpuArchitectureAlpha64CpuArchitectureMSILCpuArchitectureAMD64CpuArchitectureWoW64"
 | 
			
		||||
	_Arch_name_1 = "CpuArchitectureARM64"
 | 
			
		||||
	_Arch_name_2 = "CpuArchitectureUnknown"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_Arch_index_0 = [...]uint8{0, 18, 37, 57, 75, 93, 111, 130, 152, 171, 191, 211}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i Arch) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case 0 <= i && i <= 10:
 | 
			
		||||
		return _Arch_name_0[_Arch_index_0[i]:_Arch_index_0[i+1]]
 | 
			
		||||
	case i == 12:
 | 
			
		||||
		return _Arch_name_1
 | 
			
		||||
	case i == 65535:
 | 
			
		||||
		return _Arch_name_2
 | 
			
		||||
	default:
 | 
			
		||||
		return "Arch(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_MemoryState_name_0 = "MemoryStateCommit"
 | 
			
		||||
	_MemoryState_name_1 = "MemoryStateReserve"
 | 
			
		||||
	_MemoryState_name_2 = "MemoryStateFree"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i MemoryState) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case i == 4096:
 | 
			
		||||
		return _MemoryState_name_0
 | 
			
		||||
	case i == 8192:
 | 
			
		||||
		return _MemoryState_name_1
 | 
			
		||||
	case i == 65536:
 | 
			
		||||
		return _MemoryState_name_2
 | 
			
		||||
	default:
 | 
			
		||||
		return "MemoryState(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_MemoryType_name_0 = "MemoryTypePrivate"
 | 
			
		||||
	_MemoryType_name_1 = "MemoryTypeMapped"
 | 
			
		||||
	_MemoryType_name_2 = "MemoryTypeImage"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i MemoryType) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case i == 131072:
 | 
			
		||||
		return _MemoryType_name_0
 | 
			
		||||
	case i == 262144:
 | 
			
		||||
		return _MemoryType_name_1
 | 
			
		||||
	case i == 16777216:
 | 
			
		||||
		return _MemoryType_name_2
 | 
			
		||||
	default:
 | 
			
		||||
		return "MemoryType(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_MemoryProtection_name_0 = "MemoryProtectNoAccessMemoryProtectReadOnly"
 | 
			
		||||
	_MemoryProtection_name_1 = "MemoryProtectReadWrite"
 | 
			
		||||
	_MemoryProtection_name_2 = "MemoryProtectWriteCopy"
 | 
			
		||||
	_MemoryProtection_name_3 = "MemoryProtectExecute"
 | 
			
		||||
	_MemoryProtection_name_4 = "MemoryProtectExecuteRead"
 | 
			
		||||
	_MemoryProtection_name_5 = "MemoryProtectExecuteReadWrite"
 | 
			
		||||
	_MemoryProtection_name_6 = "MemoryProtectExecuteWriteCopy"
 | 
			
		||||
	_MemoryProtection_name_7 = "MemoryProtectPageGuard"
 | 
			
		||||
	_MemoryProtection_name_8 = "MemoryProtectNoCache"
 | 
			
		||||
	_MemoryProtection_name_9 = "MemoryProtectWriteCombine"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_MemoryProtection_index_0 = [...]uint8{0, 21, 42}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i MemoryProtection) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case 1 <= i && i <= 2:
 | 
			
		||||
		i -= 1
 | 
			
		||||
		return _MemoryProtection_name_0[_MemoryProtection_index_0[i]:_MemoryProtection_index_0[i+1]]
 | 
			
		||||
	case i == 4:
 | 
			
		||||
		return _MemoryProtection_name_1
 | 
			
		||||
	case i == 8:
 | 
			
		||||
		return _MemoryProtection_name_2
 | 
			
		||||
	case i == 16:
 | 
			
		||||
		return _MemoryProtection_name_3
 | 
			
		||||
	case i == 32:
 | 
			
		||||
		return _MemoryProtection_name_4
 | 
			
		||||
	case i == 64:
 | 
			
		||||
		return _MemoryProtection_name_5
 | 
			
		||||
	case i == 128:
 | 
			
		||||
		return _MemoryProtection_name_6
 | 
			
		||||
	case i == 256:
 | 
			
		||||
		return _MemoryProtection_name_7
 | 
			
		||||
	case i == 512:
 | 
			
		||||
		return _MemoryProtection_name_8
 | 
			
		||||
	case i == 1024:
 | 
			
		||||
		return _MemoryProtection_name_9
 | 
			
		||||
	default:
 | 
			
		||||
		return "MemoryProtection(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										686
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										686
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,686 @@
 | 
			
		||||
package minidump
 | 
			
		||||
 | 
			
		||||
// Package minidump provides a loader for Windows Minidump files.
 | 
			
		||||
// Minidump files are the Windows equivalent of unix core dumps.
 | 
			
		||||
// They can be created by the kernel when a program crashes (however this is
 | 
			
		||||
// disabled for Go programs) or programmatically using either WinDbg or the
 | 
			
		||||
// ProcDump utility.
 | 
			
		||||
//
 | 
			
		||||
// The file format is described on MSDN starting at:
 | 
			
		||||
//  https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_header
 | 
			
		||||
// which is the structure found at offset 0 on a minidump file.
 | 
			
		||||
//
 | 
			
		||||
// Further information on the format can be found reading
 | 
			
		||||
// chromium-breakpad's minidump loading code, specifically:
 | 
			
		||||
//  https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_cpu_amd64.h
 | 
			
		||||
// and:
 | 
			
		||||
//  https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_format.h
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"unicode/utf16"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type minidumpBuf struct {
 | 
			
		||||
	buf  []byte
 | 
			
		||||
	kind string
 | 
			
		||||
	off  int
 | 
			
		||||
	err  error
 | 
			
		||||
	ctx  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (buf *minidumpBuf) u16() uint16 {
 | 
			
		||||
	const stride = 2
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if buf.off+stride >= len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
 | 
			
		||||
	}
 | 
			
		||||
	r := binary.LittleEndian.Uint16(buf.buf[buf.off : buf.off+stride])
 | 
			
		||||
	buf.off += stride
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (buf *minidumpBuf) u32() uint32 {
 | 
			
		||||
	const stride = 4
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if buf.off+stride >= len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
 | 
			
		||||
	}
 | 
			
		||||
	r := binary.LittleEndian.Uint32(buf.buf[buf.off : buf.off+stride])
 | 
			
		||||
	buf.off += stride
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (buf *minidumpBuf) u64() uint64 {
 | 
			
		||||
	const stride = 8
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if buf.off+stride >= len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
 | 
			
		||||
	}
 | 
			
		||||
	r := binary.LittleEndian.Uint64(buf.buf[buf.off : buf.off+stride])
 | 
			
		||||
	buf.off += stride
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func streamBuf(stream *Stream, buf *minidumpBuf, name string) *minidumpBuf {
 | 
			
		||||
	return &minidumpBuf{
 | 
			
		||||
		buf:  buf.buf,
 | 
			
		||||
		kind: "stream",
 | 
			
		||||
		off:  stream.Offset,
 | 
			
		||||
		err:  nil,
 | 
			
		||||
		ctx:  fmt.Sprintf("reading %s stream at %#x", name, stream.Offset),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrNotAMinidump is the error returned when the file being loaded is not a
 | 
			
		||||
// minidump file.
 | 
			
		||||
type ErrNotAMinidump struct {
 | 
			
		||||
	what string
 | 
			
		||||
	got  uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err ErrNotAMinidump) Error() string {
 | 
			
		||||
	return fmt.Sprintf("not a minidump, invalid %s %#x", err.what, err.got)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	minidumpSignature = 0x504d444d // 'MDMP'
 | 
			
		||||
	minidumpVersion   = 0xa793
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Minidump represents a minidump file
 | 
			
		||||
type Minidump struct {
 | 
			
		||||
	Timestamp uint32
 | 
			
		||||
	Flags     FileFlags
 | 
			
		||||
 | 
			
		||||
	Streams []Stream
 | 
			
		||||
 | 
			
		||||
	Threads []Thread
 | 
			
		||||
	Modules []Module
 | 
			
		||||
 | 
			
		||||
	Pid uint32
 | 
			
		||||
 | 
			
		||||
	MemoryRanges []MemoryRange
 | 
			
		||||
	MemoryInfo   []MemoryInfo
 | 
			
		||||
 | 
			
		||||
	streamNum uint32
 | 
			
		||||
	streamOff uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stream represents one (uninterpreted) stream in a minidump file.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_directory
 | 
			
		||||
type Stream struct {
 | 
			
		||||
	Type    StreamType
 | 
			
		||||
	Offset  int
 | 
			
		||||
	RawData []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Thread represents an entry in the ThreadList stream.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_thread
 | 
			
		||||
type Thread struct {
 | 
			
		||||
	ID            uint32
 | 
			
		||||
	SuspendCount  uint32
 | 
			
		||||
	PriorityClass uint32
 | 
			
		||||
	Priority      uint32
 | 
			
		||||
	TEB           uint64
 | 
			
		||||
	Context       winutil.CONTEXT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Module represents an entry in the ModuleList stream.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_module
 | 
			
		||||
type Module struct {
 | 
			
		||||
	BaseOfImage   uint64
 | 
			
		||||
	SizeOfImage   uint32
 | 
			
		||||
	Checksum      uint32
 | 
			
		||||
	TimeDateStamp uint32
 | 
			
		||||
	Name          string
 | 
			
		||||
	VersionInfo   VSFixedFileInfo
 | 
			
		||||
 | 
			
		||||
	// CVRecord stores a CodeView record and is populated when a module's debug information resides in a PDB file.  It identifies the PDB file.
 | 
			
		||||
	CVRecord []byte
 | 
			
		||||
 | 
			
		||||
	// MiscRecord is populated when a module's debug information resides in a DBG file.  It identifies the DBG file.  This field is effectively obsolete with modules built by recent toolchains.
 | 
			
		||||
	MiscRecord []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VSFixedFileInfo: Visual Studio Fixed File Info.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/verrsrc/ns-verrsrc-tagvs_fixedfileinfo
 | 
			
		||||
type VSFixedFileInfo struct {
 | 
			
		||||
	Signature        uint32
 | 
			
		||||
	StructVersion    uint32
 | 
			
		||||
	FileVersionHi    uint32
 | 
			
		||||
	FileVersionLo    uint32
 | 
			
		||||
	ProductVersionHi uint32
 | 
			
		||||
	ProductVersionLo uint32
 | 
			
		||||
	FileFlagsMask    uint32
 | 
			
		||||
	FileFlags        uint32
 | 
			
		||||
	FileOS           uint32
 | 
			
		||||
	FileType         uint32
 | 
			
		||||
	FileSubtype      uint32
 | 
			
		||||
	FileDateHi       uint32
 | 
			
		||||
	FileDateLo       uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemoryRange represents a region of memory saved to the core file, it's constructed after either:
 | 
			
		||||
// 1. parsing an entry in the Memory64List stream.
 | 
			
		||||
// 2. parsing the stack field of an entry in the ThreadList stream.
 | 
			
		||||
type MemoryRange struct {
 | 
			
		||||
	Addr uint64
 | 
			
		||||
	Data []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadMemory reads len(buf) bytes of memory starting at addr into buf from this memory region.
 | 
			
		||||
func (m *MemoryRange) ReadMemory(buf []byte, addr uintptr) (int, error) {
 | 
			
		||||
	if len(buf) == 0 {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	if (uint64(addr) < m.Addr) || (uint64(addr)+uint64(len(buf)) > m.Addr+uint64(len(m.Data))) {
 | 
			
		||||
		return 0, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	copy(buf, m.Data[uint64(addr)-m.Addr:])
 | 
			
		||||
	return len(buf), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemoryInfo reprents an entry in the MemoryInfoList stream.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory_info
 | 
			
		||||
type MemoryInfo struct {
 | 
			
		||||
	Addr       uint64
 | 
			
		||||
	Size       uint64
 | 
			
		||||
	State      MemoryState
 | 
			
		||||
	Protection MemoryProtection
 | 
			
		||||
	Type       MemoryType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//go:generate stringer -type FileFlags,StreamType,Arch,MemoryState,MemoryType,MemoryProtection
 | 
			
		||||
 | 
			
		||||
// MemoryState is the type of the State field of MINIDUMP_MEMORY_INFO
 | 
			
		||||
type MemoryState uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MemoryStateCommit  MemoryState = 0x1000
 | 
			
		||||
	MemoryStateReserve MemoryState = 0x2000
 | 
			
		||||
	MemoryStateFree    MemoryState = 0x10000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MemoryType is the type of the Type field of MINIDUMP_MEMORY_INFO
 | 
			
		||||
type MemoryType uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MemoryTypePrivate MemoryType = 0x20000
 | 
			
		||||
	MemoryTypeMapped  MemoryType = 0x40000
 | 
			
		||||
	MemoryTypeImage   MemoryType = 0x1000000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MemoryProtection is the type of the Protection field of MINIDUMP_MEMORY_INFO
 | 
			
		||||
type MemoryProtection uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MemoryProtectNoAccess         MemoryProtection = 0x01 // PAGE_NOACCESS
 | 
			
		||||
	MemoryProtectReadOnly         MemoryProtection = 0x02 // PAGE_READONLY
 | 
			
		||||
	MemoryProtectReadWrite        MemoryProtection = 0x04 // PAGE_READWRITE
 | 
			
		||||
	MemoryProtectWriteCopy        MemoryProtection = 0x08 // PAGE_WRITECOPY
 | 
			
		||||
	MemoryProtectExecute          MemoryProtection = 0x10 // PAGE_EXECUTE
 | 
			
		||||
	MemoryProtectExecuteRead      MemoryProtection = 0x20 // PAGE_EXECUTE_READ
 | 
			
		||||
	MemoryProtectExecuteReadWrite MemoryProtection = 0x40 // PAGE_EXECUTE_READWRITE
 | 
			
		||||
	MemoryProtectExecuteWriteCopy MemoryProtection = 0x80 // PAGE_EXECUTE_WRITECOPY
 | 
			
		||||
	// These options can be combined with the previous flags
 | 
			
		||||
	MemoryProtectPageGuard    MemoryProtection = 0x100 // PAGE_GUARD
 | 
			
		||||
	MemoryProtectNoCache      MemoryProtection = 0x200 // PAGE_NOCACHE
 | 
			
		||||
	MemoryProtectWriteCombine MemoryProtection = 0x400 // PAGE_WRITECOMBINE
 | 
			
		||||
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FileFlags is the type of the Flags field of MINIDUMP_HEADER
 | 
			
		||||
type FileFlags uint64
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FileNormal                          FileFlags = 0x00000000
 | 
			
		||||
	FileWithDataSegs                    FileFlags = 0x00000001
 | 
			
		||||
	FileWithFullMemory                  FileFlags = 0x00000002
 | 
			
		||||
	FileWithHandleData                  FileFlags = 0x00000004
 | 
			
		||||
	FileFilterMemory                    FileFlags = 0x00000008
 | 
			
		||||
	FileScanMemory                      FileFlags = 0x00000010
 | 
			
		||||
	FileWithUnloadedModules             FileFlags = 0x00000020
 | 
			
		||||
	FileWithIncorrectlyReferencedMemory FileFlags = 0x00000040
 | 
			
		||||
	FileFilterModulePaths               FileFlags = 0x00000080
 | 
			
		||||
	FileWithProcessThreadData           FileFlags = 0x00000100
 | 
			
		||||
	FileWithPrivateReadWriteMemory      FileFlags = 0x00000200
 | 
			
		||||
	FileWithoutOptionalData             FileFlags = 0x00000400
 | 
			
		||||
	FileWithFullMemoryInfo              FileFlags = 0x00000800
 | 
			
		||||
	FileWithThreadInfo                  FileFlags = 0x00001000
 | 
			
		||||
	FileWithCodeSegs                    FileFlags = 0x00002000
 | 
			
		||||
	FileWithoutAuxilliarySegs           FileFlags = 0x00004000
 | 
			
		||||
	FileWithFullAuxilliaryState         FileFlags = 0x00008000
 | 
			
		||||
	FileWithPrivateCopyMemory           FileFlags = 0x00010000
 | 
			
		||||
	FileIgnoreInaccessibleMemory        FileFlags = 0x00020000
 | 
			
		||||
	FileWithTokenInformation            FileFlags = 0x00040000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// StreamType is the type of the StreamType field of MINIDUMP_DIRECTORY
 | 
			
		||||
type StreamType uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	UnusedStream              StreamType = 0
 | 
			
		||||
	ReservedStream0           StreamType = 1
 | 
			
		||||
	ReservedStream1           StreamType = 2
 | 
			
		||||
	ThreadListStream          StreamType = 3
 | 
			
		||||
	ModuleListStream          StreamType = 4
 | 
			
		||||
	MemoryListStream          StreamType = 5
 | 
			
		||||
	ExceptionStream           StreamType = 6
 | 
			
		||||
	SystemInfoStream          StreamType = 7
 | 
			
		||||
	ThreadExListStream        StreamType = 8
 | 
			
		||||
	Memory64ListStream        StreamType = 9
 | 
			
		||||
	CommentStreamA            StreamType = 10
 | 
			
		||||
	CommentStreamW            StreamType = 11
 | 
			
		||||
	HandleDataStream          StreamType = 12
 | 
			
		||||
	FunctionTableStream       StreamType = 13
 | 
			
		||||
	UnloadedModuleStream      StreamType = 14
 | 
			
		||||
	MiscInfoStream            StreamType = 15
 | 
			
		||||
	MemoryInfoListStream      StreamType = 16
 | 
			
		||||
	ThreadInfoListStream      StreamType = 17
 | 
			
		||||
	HandleOperationListStream StreamType = 18
 | 
			
		||||
	TokenStream               StreamType = 19
 | 
			
		||||
	JavascriptDataStream      StreamType = 20
 | 
			
		||||
	SystemMemoryInfoStream    StreamType = 21
 | 
			
		||||
	ProcessVMCounterStream    StreamType = 22
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Arch is the type of the ProcessorArchitecture field of MINIDUMP_SYSTEM_INFO.
 | 
			
		||||
type Arch uint16
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	CpuArchitectureX86     Arch = 0
 | 
			
		||||
	CpuArchitectureMips    Arch = 1
 | 
			
		||||
	CpuArchitectureAlpha   Arch = 2
 | 
			
		||||
	CpuArchitecturePPC     Arch = 3
 | 
			
		||||
	CpuArchitectureSHX     Arch = 4 // Super-H
 | 
			
		||||
	CpuArchitectureARM     Arch = 5
 | 
			
		||||
	CpuArchitectureIA64    Arch = 6
 | 
			
		||||
	CpuArchitectureAlpha64 Arch = 7
 | 
			
		||||
	CpuArchitectureMSIL    Arch = 8 // Microsoft Intermediate Language
 | 
			
		||||
	CpuArchitectureAMD64   Arch = 9
 | 
			
		||||
	CpuArchitectureWoW64   Arch = 10
 | 
			
		||||
	CpuArchitectureARM64   Arch = 12
 | 
			
		||||
	CpuArchitectureUnknown Arch = 0xffff
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Open reads the minidump file at path and returns it as a Minidump structure.
 | 
			
		||||
func Open(path string, logfn func(fmt string, args ...interface{})) (*Minidump, error) {
 | 
			
		||||
	rawbuf, err := ioutil.ReadFile(path) //TODO(aarzilli): mmap?
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf := &minidumpBuf{buf: rawbuf, kind: "file"}
 | 
			
		||||
 | 
			
		||||
	var mdmp Minidump
 | 
			
		||||
 | 
			
		||||
	readMinidumpHeader(&mdmp, buf)
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return nil, buf.err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if logfn != nil {
 | 
			
		||||
		logfn("Minidump Header\n")
 | 
			
		||||
		logfn("Num Streams: %d\n", mdmp.streamNum)
 | 
			
		||||
		logfn("Streams offset: %#x\n", mdmp.streamOff)
 | 
			
		||||
		logfn("File flags: %s\n", fileFlagsToString(mdmp.Flags))
 | 
			
		||||
		logfn("Offset after header %#x\n", buf.off)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	readDirectory(&mdmp, buf)
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return nil, buf.err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Streams {
 | 
			
		||||
		stream := &mdmp.Streams[i]
 | 
			
		||||
		if stream.Type != SystemInfoStream {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sb := streamBuf(stream, buf, "system info")
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return nil, buf.err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		arch := Arch(sb.u16())
 | 
			
		||||
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("Found processor architecture %s\n", arch.String())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if arch != CpuArchitectureAMD64 {
 | 
			
		||||
			return nil, fmt.Errorf("unsupported architecture %s", arch.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Streams {
 | 
			
		||||
		stream := &mdmp.Streams[i]
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("Stream %d: type:%s off:%#x size:%#x\n", i, stream.Type, stream.Offset, len(stream.RawData))
 | 
			
		||||
		}
 | 
			
		||||
		switch stream.Type {
 | 
			
		||||
		case ThreadListStream:
 | 
			
		||||
			readThreadList(&mdmp, streamBuf(stream, buf, "thread list"))
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				for i := range mdmp.Threads {
 | 
			
		||||
					logfn("\tID:%#x TEB:%#x\n", mdmp.Threads[i].ID, mdmp.Threads[i].TEB)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case ModuleListStream:
 | 
			
		||||
			readModuleList(&mdmp, streamBuf(stream, buf, "module list"))
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				for i := range mdmp.Modules {
 | 
			
		||||
					logfn("\tName:%q BaseOfImage:%#x SizeOfImage:%#x\n", mdmp.Modules[i].Name, mdmp.Modules[i].BaseOfImage, mdmp.Modules[i].SizeOfImage)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case ExceptionStream:
 | 
			
		||||
			//TODO(aarzilli): this stream contains the exception that made the
 | 
			
		||||
			//process stop and caused the minidump to be taken. If we ever start
 | 
			
		||||
			//caring about this we should parse this.
 | 
			
		||||
		case Memory64ListStream:
 | 
			
		||||
			readMemory64List(&mdmp, streamBuf(stream, buf, "memory64 list"), logfn)
 | 
			
		||||
		case MemoryInfoListStream:
 | 
			
		||||
			readMemoryInfoList(&mdmp, streamBuf(stream, buf, "memory info list"), logfn)
 | 
			
		||||
		case MiscInfoStream:
 | 
			
		||||
			readMiscInfo(&mdmp, streamBuf(stream, buf, "misc info"))
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				logfn("\tPid: %#x\n", mdmp.Pid)
 | 
			
		||||
			}
 | 
			
		||||
		case CommentStreamW:
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				logfn("\t%q\n", decodeUTF16(stream.RawData))
 | 
			
		||||
			}
 | 
			
		||||
		case CommentStreamA:
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				logfn("\t%s\n", string(stream.RawData))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return nil, buf.err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &mdmp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// decodeUTF16 converts a NUL-terminated UTF16LE string to (non NUL-terminated) UTF8.
 | 
			
		||||
func decodeUTF16(in []byte) string {
 | 
			
		||||
	utf16encoded := []uint16{}
 | 
			
		||||
	for i := 0; i+1 < len(in); i += 2 {
 | 
			
		||||
		var ch uint16
 | 
			
		||||
		ch = uint16(in[i]) + uint16(in[i+1])<<8
 | 
			
		||||
		utf16encoded = append(utf16encoded, ch)
 | 
			
		||||
	}
 | 
			
		||||
	s := string(utf16.Decode(utf16encoded))
 | 
			
		||||
	if len(s) > 0 && s[len(s)-1] == 0 {
 | 
			
		||||
		s = s[:len(s)-1]
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileFlagsToString(flags FileFlags) string {
 | 
			
		||||
	out := []byte{}
 | 
			
		||||
	for i, name := range _FileFlags_map {
 | 
			
		||||
		if i == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if flags&i != 0 {
 | 
			
		||||
			if len(out) > 0 {
 | 
			
		||||
				out = append(out, '|')
 | 
			
		||||
			}
 | 
			
		||||
			out = append(out, name...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(out) == 0 {
 | 
			
		||||
		return flags.String()
 | 
			
		||||
	}
 | 
			
		||||
	return string(out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMinidumpHeader reads the minidump file header
 | 
			
		||||
func readMinidumpHeader(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	buf.ctx = "reading minidump header"
 | 
			
		||||
 | 
			
		||||
	if sig := buf.u32(); sig != minidumpSignature {
 | 
			
		||||
		buf.err = ErrNotAMinidump{"signature", sig}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ver := buf.u16(); ver != minidumpVersion {
 | 
			
		||||
		buf.err = ErrNotAMinidump{"version", uint32(ver)}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf.u16() // implementation specific version
 | 
			
		||||
	mdmp.streamNum = buf.u32()
 | 
			
		||||
	mdmp.streamOff = buf.u32()
 | 
			
		||||
	buf.u32() // checksum, but it's always 0
 | 
			
		||||
	mdmp.Timestamp = buf.u32()
 | 
			
		||||
	mdmp.Flags = FileFlags(buf.u64())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readDirectory reads the list of streams (i.e. the minidum "directory")
 | 
			
		||||
func readDirectory(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	buf.off = int(mdmp.streamOff)
 | 
			
		||||
 | 
			
		||||
	mdmp.Streams = make([]Stream, mdmp.streamNum)
 | 
			
		||||
	for i := range mdmp.Streams {
 | 
			
		||||
		buf.ctx = fmt.Sprintf("reading stream directory entry %d", i)
 | 
			
		||||
		stream := &mdmp.Streams[i]
 | 
			
		||||
		stream.Type = StreamType(buf.u32())
 | 
			
		||||
		stream.Offset, stream.RawData = readLocationDescriptor(buf)
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readLocationDescriptor reads a location descriptor structure (a structure
 | 
			
		||||
// which describes a subregion of the file), and returns the destination
 | 
			
		||||
// offset and a slice into the minidump file's buffer.
 | 
			
		||||
func readLocationDescriptor(buf *minidumpBuf) (off int, rawData []byte) {
 | 
			
		||||
	sz := buf.u32()
 | 
			
		||||
	off = int(buf.u32())
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return off, nil
 | 
			
		||||
	}
 | 
			
		||||
	end := off + int(sz)
 | 
			
		||||
	if off >= len(buf.buf) || end > len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("location starting at %#x of size %#x is past the end of file, while %s", off, sz, buf.ctx)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	rawData = buf.buf[off:end]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readString(buf *minidumpBuf) string {
 | 
			
		||||
	startOff := buf.off
 | 
			
		||||
	sz := buf.u32()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	end := buf.off + int(sz)
 | 
			
		||||
	if buf.off >= len(buf.buf) || end > len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("string starting at %#x of size %#x is past the end of file, while %s", startOff, sz, buf.ctx)
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return decodeUTF16(buf.buf[buf.off:end])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readThreadList reads a thread list stream and adds the threads to the minidump.
 | 
			
		||||
func readThreadList(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	threadNum := buf.u32()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mdmp.Threads = make([]Thread, threadNum)
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Threads {
 | 
			
		||||
		buf.ctx = fmt.Sprintf("reading thread list entry %d", i)
 | 
			
		||||
		thread := &mdmp.Threads[i]
 | 
			
		||||
 | 
			
		||||
		thread.ID = buf.u32()
 | 
			
		||||
		thread.SuspendCount = buf.u32()
 | 
			
		||||
		thread.PriorityClass = buf.u32()
 | 
			
		||||
		thread.Priority = buf.u32()
 | 
			
		||||
		thread.TEB = buf.u64()
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		readMemoryDescriptor(mdmp, buf)                    // thread stack
 | 
			
		||||
		_, rawThreadContext := readLocationDescriptor(buf) // thread context
 | 
			
		||||
		thread.Context = *((*winutil.CONTEXT)(unsafe.Pointer(&rawThreadContext[0])))
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readModuleList reads a module list stream and adds the modules to the minidump.
 | 
			
		||||
func readModuleList(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	moduleNum := buf.u32()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mdmp.Modules = make([]Module, moduleNum)
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Modules {
 | 
			
		||||
		buf.ctx = fmt.Sprintf("reading module list entry %d", i)
 | 
			
		||||
		module := &mdmp.Modules[i]
 | 
			
		||||
 | 
			
		||||
		module.BaseOfImage = buf.u64()
 | 
			
		||||
		module.SizeOfImage = buf.u32()
 | 
			
		||||
		module.Checksum = buf.u32()
 | 
			
		||||
		module.TimeDateStamp = buf.u32()
 | 
			
		||||
		nameOff := int(buf.u32())
 | 
			
		||||
 | 
			
		||||
		versionInfoVec := make([]uint32, unsafe.Sizeof(VSFixedFileInfo{})/unsafe.Sizeof(uint32(0)))
 | 
			
		||||
		for j := range versionInfoVec {
 | 
			
		||||
			versionInfoVec[j] = buf.u32()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		module.VersionInfo = *(*VSFixedFileInfo)(unsafe.Pointer(&versionInfoVec[0]))
 | 
			
		||||
 | 
			
		||||
		_, module.CVRecord = readLocationDescriptor(buf)
 | 
			
		||||
		_, module.MiscRecord = readLocationDescriptor(buf)
 | 
			
		||||
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nameBuf := minidumpBuf{buf: buf.buf, kind: "file", off: nameOff, err: nil, ctx: buf.ctx}
 | 
			
		||||
		module.Name = readString(&nameBuf)
 | 
			
		||||
		if nameBuf.err != nil {
 | 
			
		||||
			buf.err = nameBuf.err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMemory64List reads a _MINIDUMP_MEMORY64_LIST structure, containing
 | 
			
		||||
// the description of the process memory.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory64_list
 | 
			
		||||
// And: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory_descriptor
 | 
			
		||||
func readMemory64List(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
 | 
			
		||||
	rangesNum := buf.u64()
 | 
			
		||||
	baseOff := int(buf.u64())
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint64(0); i < rangesNum; i++ {
 | 
			
		||||
		addr := buf.u64()
 | 
			
		||||
		sz := buf.u64()
 | 
			
		||||
 | 
			
		||||
		end := baseOff + int(sz)
 | 
			
		||||
		if baseOff >= len(buf.buf) || end > len(buf.buf) {
 | 
			
		||||
			buf.err = fmt.Errorf("memory range at %#x of size %#x is past the end of file, while %s", baseOff, sz, buf.ctx)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mdmp.addMemory(addr, buf.buf[baseOff:end])
 | 
			
		||||
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("\tMemory %d addr:%#x size:%#x FileOffset:%#x\n", i, addr, sz, baseOff)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		baseOff = end
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readMemoryInfoList(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
 | 
			
		||||
	startOff := buf.off
 | 
			
		||||
	sizeOfHeader := int(buf.u32())
 | 
			
		||||
	sizeOfEntry := int(buf.u32())
 | 
			
		||||
	numEntries := buf.u64()
 | 
			
		||||
 | 
			
		||||
	buf.off = startOff + sizeOfHeader
 | 
			
		||||
 | 
			
		||||
	mdmp.MemoryInfo = make([]MemoryInfo, numEntries)
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.MemoryInfo {
 | 
			
		||||
		memInfo := &mdmp.MemoryInfo[i]
 | 
			
		||||
		startOff := buf.off
 | 
			
		||||
 | 
			
		||||
		memInfo.Addr = buf.u64()
 | 
			
		||||
		buf.u64() // allocation_base
 | 
			
		||||
 | 
			
		||||
		buf.u32() // allocation_protection
 | 
			
		||||
		buf.u32() // alignment
 | 
			
		||||
 | 
			
		||||
		memInfo.Size = buf.u64()
 | 
			
		||||
 | 
			
		||||
		memInfo.State = MemoryState(buf.u32())
 | 
			
		||||
		memInfo.Protection = MemoryProtection(buf.u32())
 | 
			
		||||
		memInfo.Type = MemoryType(buf.u32())
 | 
			
		||||
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("\tMemoryInfo %d Addr:%#x Size:%#x %s %s %s\n", i, memInfo.Addr, memInfo.Size, memInfo.State, memInfo.Protection, memInfo.Type)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		buf.off = startOff + sizeOfEntry
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMiscInfo reads the process_id from a MiscInfo stream.
 | 
			
		||||
func readMiscInfo(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	buf.u32() // size of info
 | 
			
		||||
	buf.u32() // flags1
 | 
			
		||||
 | 
			
		||||
	mdmp.Pid = buf.u32() // process_id
 | 
			
		||||
	// there are more fields here, but we don't care about them
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMemoryDescriptor reads a memory descriptor struct and adds it to the memory map of the minidump.
 | 
			
		||||
func readMemoryDescriptor(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	addr := buf.u64()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	_, rawData := readLocationDescriptor(buf)
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	mdmp.addMemory(addr, rawData)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mdmp *Minidump) addMemory(addr uint64, data []byte) {
 | 
			
		||||
	mdmp.MemoryRanges = append(mdmp.MemoryRanges, MemoryRange{addr, data})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/windows_amd64_minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/windows_amd64_minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/go-delve/delve/pkg/logflags"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/core/minidump"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func readAMD64Minidump(minidumpPath, exePath string) (*Process, error) {
 | 
			
		||||
	var logfn func(string, ...interface{})
 | 
			
		||||
	if logflags.Minidump() {
 | 
			
		||||
		logfn = logflags.MinidumpLogger().Infof
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mdmp, err := minidump.Open(minidumpPath, logfn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if _, isNotAMinidump := err.(minidump.ErrNotAMinidump); isNotAMinidump {
 | 
			
		||||
			return nil, ErrUnrecognizedFormat
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memory := &SplicedMemory{}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.MemoryRanges {
 | 
			
		||||
		m := &mdmp.MemoryRanges[i]
 | 
			
		||||
		memory.Add(m, uintptr(m.Addr), uintptr(len(m.Data)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := &Process{
 | 
			
		||||
		mem:         memory,
 | 
			
		||||
		Threads:     map[int]*Thread{},
 | 
			
		||||
		bi:          proc.NewBinaryInfo("windows", "amd64"),
 | 
			
		||||
		breakpoints: proc.NewBreakpointMap(),
 | 
			
		||||
		pid:         int(mdmp.Pid),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Threads {
 | 
			
		||||
		th := &mdmp.Threads[i]
 | 
			
		||||
		p.Threads[int(th.ID)] = &Thread{&windowsAMD64Thread{th}, p, proc.CommonThread{}}
 | 
			
		||||
		if p.currentThread == nil {
 | 
			
		||||
			p.currentThread = p.Threads[int(th.ID)]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type windowsAMD64Thread struct {
 | 
			
		||||
	th *minidump.Thread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (th *windowsAMD64Thread) pid() int {
 | 
			
		||||
	return int(th.th.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (th *windowsAMD64Thread) registers(floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	return winutil.NewAMD64Registers(&th.th.Context, th.th.TEB, floatingPoint), nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										121
									
								
								vendor/github.com/go-delve/delve/pkg/proc/disasm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								vendor/github.com/go-delve/delve/pkg/proc/disasm.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,121 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import "sort"
 | 
			
		||||
 | 
			
		||||
// AsmInstruction represents one assembly instruction.
 | 
			
		||||
type AsmInstruction struct {
 | 
			
		||||
	Loc        Location
 | 
			
		||||
	DestLoc    *Location
 | 
			
		||||
	Bytes      []byte
 | 
			
		||||
	Breakpoint bool
 | 
			
		||||
	AtPC       bool
 | 
			
		||||
	Inst       *archInst
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AssemblyFlavour is the assembly syntax to display.
 | 
			
		||||
type AssemblyFlavour int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// GNUFlavour will display GNU assembly syntax.
 | 
			
		||||
	GNUFlavour = AssemblyFlavour(iota)
 | 
			
		||||
	// IntelFlavour will display Intel assembly syntax.
 | 
			
		||||
	IntelFlavour
 | 
			
		||||
	// GoFlavour will display Go assembly syntax.
 | 
			
		||||
	GoFlavour
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Disassemble disassembles target memory between startPC and endPC, marking
 | 
			
		||||
// the current instruction being executed in goroutine g.
 | 
			
		||||
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
 | 
			
		||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
 | 
			
		||||
func Disassemble(dbp Process, g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
 | 
			
		||||
	if _, err := dbp.Valid(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		ct := dbp.CurrentThread()
 | 
			
		||||
		regs, _ := ct.Registers(false)
 | 
			
		||||
		return disassemble(ct, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var regs Registers
 | 
			
		||||
	var mem MemoryReadWriter = dbp.CurrentThread()
 | 
			
		||||
	if g.Thread != nil {
 | 
			
		||||
		mem = g.Thread
 | 
			
		||||
		regs, _ = g.Thread.Registers(false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startPC, endPC uint64, singleInstr bool) ([]AsmInstruction, error) {
 | 
			
		||||
	mem := make([]byte, int(endPC-startPC))
 | 
			
		||||
	_, err := memrw.ReadMemory(mem, uintptr(startPC))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := make([]AsmInstruction, 0, len(mem)/15)
 | 
			
		||||
	pc := startPC
 | 
			
		||||
 | 
			
		||||
	var curpc uint64
 | 
			
		||||
	if regs != nil {
 | 
			
		||||
		curpc = regs.PC()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for len(mem) > 0 {
 | 
			
		||||
		bp, atbp := breakpoints.M[pc]
 | 
			
		||||
		if atbp {
 | 
			
		||||
			for i := range bp.OriginalData {
 | 
			
		||||
				mem[i] = bp.OriginalData[i]
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		file, line, fn := bi.PCToLine(pc)
 | 
			
		||||
		loc := Location{PC: pc, File: file, Line: line, Fn: fn}
 | 
			
		||||
		inst, err := asmDecode(mem, pc)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			atpc := (regs != nil) && (curpc == pc)
 | 
			
		||||
			destloc := resolveCallArg(inst, atpc, regs, memrw, bi)
 | 
			
		||||
			r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
 | 
			
		||||
 | 
			
		||||
			pc += uint64(inst.Size())
 | 
			
		||||
			mem = mem[inst.Size():]
 | 
			
		||||
		} else {
 | 
			
		||||
			r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
 | 
			
		||||
			pc++
 | 
			
		||||
			mem = mem[1:]
 | 
			
		||||
		}
 | 
			
		||||
		if singleInstr {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Looks up symbol (either functions or global variables) at address addr.
 | 
			
		||||
// Used by disassembly formatter.
 | 
			
		||||
func (bi *BinaryInfo) symLookup(addr uint64) (string, uint64) {
 | 
			
		||||
	fn := bi.PCToFunc(addr)
 | 
			
		||||
	if fn != nil {
 | 
			
		||||
		if fn.Entry == addr {
 | 
			
		||||
			// only report the function name if it's the exact address because it's
 | 
			
		||||
			// easier to read the absolute address than function_name+offset.
 | 
			
		||||
			return fn.Name, fn.Entry
 | 
			
		||||
		}
 | 
			
		||||
		return "", 0
 | 
			
		||||
	}
 | 
			
		||||
	i := sort.Search(len(bi.packageVars), func(i int) bool {
 | 
			
		||||
		return bi.packageVars[i].addr >= addr
 | 
			
		||||
	})
 | 
			
		||||
	if i >= len(bi.packageVars) {
 | 
			
		||||
		return "", 0
 | 
			
		||||
	}
 | 
			
		||||
	if bi.packageVars[i].addr > addr {
 | 
			
		||||
		// report previous variable + offset if i-th variable starts after addr
 | 
			
		||||
		i--
 | 
			
		||||
	}
 | 
			
		||||
	if i > 0 {
 | 
			
		||||
		return bi.packageVars[i].name, bi.packageVars[i].addr
 | 
			
		||||
	}
 | 
			
		||||
	return "", 0
 | 
			
		||||
}
 | 
			
		||||
@@ -1,26 +1,26 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/gosym"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"rsc.io/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var maxInstructionLength uint64 = 15
 | 
			
		||||
 | 
			
		||||
type ArchInst x86asm.Inst
 | 
			
		||||
type archInst x86asm.Inst
 | 
			
		||||
 | 
			
		||||
func asmDecode(mem []byte, pc uint64) (*ArchInst, error) {
 | 
			
		||||
func asmDecode(mem []byte, pc uint64) (*archInst, error) {
 | 
			
		||||
	inst, err := x86asm.Decode(mem, 64)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	patchPCRel(pc, &inst)
 | 
			
		||||
	r := ArchInst(inst)
 | 
			
		||||
	r := archInst(inst)
 | 
			
		||||
	return &r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (inst *ArchInst) Size() int {
 | 
			
		||||
func (inst *archInst) Size() int {
 | 
			
		||||
	return inst.Len
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -32,10 +32,11 @@ func patchPCRel(pc uint64, inst *x86asm.Inst) {
 | 
			
		||||
			inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
 | 
			
		||||
// Text will return the assembly instructions in human readable format according to
 | 
			
		||||
// the flavour specified.
 | 
			
		||||
func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
 | 
			
		||||
	if inst.Inst == nil {
 | 
			
		||||
		return "?"
 | 
			
		||||
	}
 | 
			
		||||
@@ -44,25 +45,35 @@ func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
 | 
			
		||||
 | 
			
		||||
	switch flavour {
 | 
			
		||||
	case GNUFlavour:
 | 
			
		||||
		text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
 | 
			
		||||
		text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
 | 
			
		||||
	case GoFlavour:
 | 
			
		||||
		text = x86asm.GoSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
 | 
			
		||||
	case IntelFlavour:
 | 
			
		||||
		fallthrough
 | 
			
		||||
	default:
 | 
			
		||||
		text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
 | 
			
		||||
		text += " " + inst.DestLoc.Fn.Name
 | 
			
		||||
		text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return text
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsCall returns true if the instruction is a CALL or LCALL instruction.
 | 
			
		||||
func (inst *AsmInstruction) IsCall() bool {
 | 
			
		||||
	if inst.Inst == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers) *Location {
 | 
			
		||||
// IsRet returns true if the instruction is a RET or LRET instruction.
 | 
			
		||||
func (inst *AsmInstruction) IsRet() bool {
 | 
			
		||||
	if inst.Inst == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return inst.Inst.Op == x86asm.RET || inst.Inst.Op == x86asm.LRET
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
 | 
			
		||||
	if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -88,10 +99,6 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
 | 
			
		||||
		if arg.Segment != 0 {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		regs, err := thread.Registers(false)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
		base, err1 := regs.Get(int(arg.Base))
 | 
			
		||||
		index, err2 := regs.Get(int(arg.Index))
 | 
			
		||||
		if err1 != nil || err2 != nil {
 | 
			
		||||
@@ -99,7 +106,8 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
 | 
			
		||||
		}
 | 
			
		||||
		addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
 | 
			
		||||
		//TODO: should this always be 64 bits instead of inst.MemBytes?
 | 
			
		||||
		pcbytes, err := thread.readMemory(addr, inst.MemBytes)
 | 
			
		||||
		pcbytes := make([]byte, inst.MemBytes)
 | 
			
		||||
		_, err := mem.ReadMemory(pcbytes, addr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil
 | 
			
		||||
		}
 | 
			
		||||
@@ -108,9 +116,9 @@ func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	file, line, fn := thread.dbp.PCToLine(pc)
 | 
			
		||||
	file, line, fn := bininfo.PCToLine(pc)
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
		return &Location{PC: pc}
 | 
			
		||||
	}
 | 
			
		||||
	return &Location{PC: pc, File: file, Line: line, Fn: fn}
 | 
			
		||||
}
 | 
			
		||||
@@ -142,10 +150,16 @@ func init() {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
 | 
			
		||||
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
 | 
			
		||||
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
 | 
			
		||||
	text, err := dbp.CurrentThread.Disassemble(fn.Entry, fn.End, false)
 | 
			
		||||
// firstPCAfterPrologueDisassembly returns the address of the first
 | 
			
		||||
// instruction after the prologue for function fn by disassembling fn and
 | 
			
		||||
// matching the instructions against known split-stack prologue patterns.
 | 
			
		||||
// If sameline is set firstPCAfterPrologueDisassembly will always return an
 | 
			
		||||
// address associated with the same line as fn.Entry
 | 
			
		||||
func firstPCAfterPrologueDisassembly(p Process, fn *Function, sameline bool) (uint64, error) {
 | 
			
		||||
	var mem MemoryReadWriter = p.CurrentThread()
 | 
			
		||||
	breakpoints := p.Breakpoints()
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fn.Entry, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -3,6 +3,7 @@ package proc
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
@@ -10,11 +11,16 @@ import (
 | 
			
		||||
	"go/printer"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/reader"
 | 
			
		||||
	"golang.org/x/debug/dwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/goversion"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
 | 
			
		||||
 | 
			
		||||
// EvalExpression returns the value of the given expression.
 | 
			
		||||
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
 | 
			
		||||
	t, err := parser.ParseExpr(expr)
 | 
			
		||||
@@ -22,7 +28,10 @@ func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable,
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ev, err := scope.evalAST(t)
 | 
			
		||||
	ev, err := scope.evalToplevelTypeCast(t, cfg)
 | 
			
		||||
	if ev == nil && err == nil {
 | 
			
		||||
		ev, err = scope.evalAST(t)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -33,6 +42,133 @@ func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable,
 | 
			
		||||
	return ev, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// evalToplevelTypeCast implements certain type casts that we only support
 | 
			
		||||
// at the outermost levels of an expression.
 | 
			
		||||
func (scope *EvalScope) evalToplevelTypeCast(t ast.Expr, cfg LoadConfig) (*Variable, error) {
 | 
			
		||||
	call, _ := t.(*ast.CallExpr)
 | 
			
		||||
	if call == nil || len(call.Args) != 1 {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	targetTypeStr := exprToString(removeParen(call.Fun))
 | 
			
		||||
	var targetType godwarf.Type
 | 
			
		||||
	switch targetTypeStr {
 | 
			
		||||
	case "[]byte", "[]uint8":
 | 
			
		||||
		targetType = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "uint8"}, BitSize: 8, BitOffset: 0}})
 | 
			
		||||
	case "[]int32", "[]rune":
 | 
			
		||||
		targetType = fakeSliceType(&godwarf.IntType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "int32"}, BitSize: 32, BitOffset: 0}})
 | 
			
		||||
	case "string":
 | 
			
		||||
		var err error
 | 
			
		||||
		targetType, err = scope.BinInfo.findType("string")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argv, err := scope.evalToplevelTypeCast(call.Args[0], cfg)
 | 
			
		||||
	if argv == nil && err == nil {
 | 
			
		||||
		argv, err = scope.evalAST(call.Args[0])
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	argv.loadValue(cfg)
 | 
			
		||||
	if argv.Unreadable != nil {
 | 
			
		||||
		return nil, argv.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v := newVariable("", 0, targetType, scope.BinInfo, scope.Mem)
 | 
			
		||||
	v.loaded = true
 | 
			
		||||
 | 
			
		||||
	converr := fmt.Errorf("can not convert %q to %s", exprToString(call.Args[0]), targetTypeStr)
 | 
			
		||||
 | 
			
		||||
	switch targetTypeStr {
 | 
			
		||||
	case "[]byte", "[]uint8":
 | 
			
		||||
		if argv.Kind != reflect.String {
 | 
			
		||||
			return nil, converr
 | 
			
		||||
		}
 | 
			
		||||
		for i, ch := range []byte(constant.StringVal(argv.Value)) {
 | 
			
		||||
			e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType, argv.mem)
 | 
			
		||||
			e.loaded = true
 | 
			
		||||
			e.Value = constant.MakeInt64(int64(ch))
 | 
			
		||||
			v.Children = append(v.Children, *e)
 | 
			
		||||
		}
 | 
			
		||||
		v.Len = int64(len(v.Children))
 | 
			
		||||
		v.Cap = v.Len
 | 
			
		||||
		return v, nil
 | 
			
		||||
 | 
			
		||||
	case "[]int32", "[]rune":
 | 
			
		||||
		if argv.Kind != reflect.String {
 | 
			
		||||
			return nil, converr
 | 
			
		||||
		}
 | 
			
		||||
		for i, ch := range constant.StringVal(argv.Value) {
 | 
			
		||||
			e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType, argv.mem)
 | 
			
		||||
			e.loaded = true
 | 
			
		||||
			e.Value = constant.MakeInt64(int64(ch))
 | 
			
		||||
			v.Children = append(v.Children, *e)
 | 
			
		||||
		}
 | 
			
		||||
		v.Len = int64(len(v.Children))
 | 
			
		||||
		v.Cap = v.Len
 | 
			
		||||
		return v, nil
 | 
			
		||||
 | 
			
		||||
	case "string":
 | 
			
		||||
		switch argv.Kind {
 | 
			
		||||
		case reflect.String:
 | 
			
		||||
			s := constant.StringVal(argv.Value)
 | 
			
		||||
			v.Value = constant.MakeString(s)
 | 
			
		||||
			v.Len = int64(len(s))
 | 
			
		||||
			return v, nil
 | 
			
		||||
		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
 | 
			
		||||
			b, _ := constant.Int64Val(argv.Value)
 | 
			
		||||
			s := string(b)
 | 
			
		||||
			v.Value = constant.MakeString(s)
 | 
			
		||||
			v.Len = int64(len(s))
 | 
			
		||||
			return v, nil
 | 
			
		||||
		case reflect.Slice, reflect.Array:
 | 
			
		||||
			var elem godwarf.Type
 | 
			
		||||
			if argv.Kind == reflect.Slice {
 | 
			
		||||
				elem = argv.RealType.(*godwarf.SliceType).ElemType
 | 
			
		||||
			} else {
 | 
			
		||||
				elem = argv.RealType.(*godwarf.ArrayType).Type
 | 
			
		||||
			}
 | 
			
		||||
			switch elemType := elem.(type) {
 | 
			
		||||
			case *godwarf.UintType:
 | 
			
		||||
				if elemType.Name != "uint8" && elemType.Name != "byte" {
 | 
			
		||||
					return nil, nil
 | 
			
		||||
				}
 | 
			
		||||
				bytes := make([]byte, len(argv.Children))
 | 
			
		||||
				for i := range argv.Children {
 | 
			
		||||
					n, _ := constant.Int64Val(argv.Children[i].Value)
 | 
			
		||||
					bytes[i] = byte(n)
 | 
			
		||||
				}
 | 
			
		||||
				v.Value = constant.MakeString(string(bytes))
 | 
			
		||||
 | 
			
		||||
			case *godwarf.IntType:
 | 
			
		||||
				if elemType.Name != "int32" && elemType.Name != "rune" {
 | 
			
		||||
					return nil, nil
 | 
			
		||||
				}
 | 
			
		||||
				runes := make([]rune, len(argv.Children))
 | 
			
		||||
				for i := range argv.Children {
 | 
			
		||||
					n, _ := constant.Int64Val(argv.Children[i].Value)
 | 
			
		||||
					runes[i] = rune(n)
 | 
			
		||||
				}
 | 
			
		||||
				v.Value = constant.MakeString(string(runes))
 | 
			
		||||
 | 
			
		||||
			default:
 | 
			
		||||
				return nil, nil
 | 
			
		||||
			}
 | 
			
		||||
			v.Len = int64(len(constant.StringVal(v.Value)))
 | 
			
		||||
			return v, nil
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			return nil, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
 | 
			
		||||
	switch node := t.(type) {
 | 
			
		||||
	case *ast.CallExpr:
 | 
			
		||||
@@ -64,11 +200,25 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
 | 
			
		||||
		// try to interpret the selector as a package variable
 | 
			
		||||
		if maybePkg, ok := node.X.(*ast.Ident); ok {
 | 
			
		||||
			if maybePkg.Name == "runtime" && node.Sel.Name == "curg" {
 | 
			
		||||
				return scope.Thread.getGVariable()
 | 
			
		||||
			} else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
 | 
			
		||||
				if scope.Gvar == nil {
 | 
			
		||||
					return nilVariable, nil
 | 
			
		||||
				}
 | 
			
		||||
				return scope.Gvar.clone(), nil
 | 
			
		||||
			} else if maybePkg.Name == "runtime" && node.Sel.Name == "frameoff" {
 | 
			
		||||
				return newConstant(constant.MakeInt64(scope.frameOffset), scope.Mem), nil
 | 
			
		||||
			} else if v, err := scope.findGlobal(maybePkg.Name + "." + node.Sel.Name); err == nil {
 | 
			
		||||
				return v, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// try to accept "package/path".varname syntax for package variables
 | 
			
		||||
		if maybePkg, ok := node.X.(*ast.BasicLit); ok && maybePkg.Kind == token.STRING {
 | 
			
		||||
			pkgpath, err := strconv.Unquote(maybePkg.Value)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				if v, err := scope.findGlobal(pkgpath + "." + node.Sel.Name); err == nil {
 | 
			
		||||
					return v, nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// if it's not a package variable then it must be a struct member access
 | 
			
		||||
		return scope.evalStructSelector(node)
 | 
			
		||||
 | 
			
		||||
@@ -102,7 +252,7 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
 | 
			
		||||
		return scope.evalBinary(node)
 | 
			
		||||
 | 
			
		||||
	case *ast.BasicLit:
 | 
			
		||||
		return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Thread), nil
 | 
			
		||||
		return newConstant(constant.MakeFromLiteral(node.Value, node.Kind, 0), scope.Mem), nil
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("expression %T not implemented", t)
 | 
			
		||||
@@ -116,6 +266,17 @@ func exprToString(t ast.Expr) string {
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func removeParen(n ast.Expr) ast.Expr {
 | 
			
		||||
	for {
 | 
			
		||||
		p, ok := n.(*ast.ParenExpr)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		n = p.X
 | 
			
		||||
	}
 | 
			
		||||
	return n
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Eval type cast expressions
 | 
			
		||||
func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
			
		||||
	argv, err := scope.evalAST(node.Args[0])
 | 
			
		||||
@@ -130,15 +291,9 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
			
		||||
	fnnode := node.Fun
 | 
			
		||||
 | 
			
		||||
	// remove all enclosing parenthesis from the type name
 | 
			
		||||
	for {
 | 
			
		||||
		p, ok := fnnode.(*ast.ParenExpr)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		fnnode = p.X
 | 
			
		||||
	}
 | 
			
		||||
	fnnode = removeParen(fnnode)
 | 
			
		||||
 | 
			
		||||
	styp, err := scope.Thread.dbp.findTypeExpr(fnnode)
 | 
			
		||||
	styp, err := scope.BinInfo.findTypeExpr(fnnode)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -146,11 +301,11 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
			
		||||
 | 
			
		||||
	converr := fmt.Errorf("can not convert %q to %s", exprToString(node.Args[0]), typ.String())
 | 
			
		||||
 | 
			
		||||
	v := newVariable("", 0, styp, scope.Thread.dbp, scope.Thread)
 | 
			
		||||
	v := newVariable("", 0, styp, scope.BinInfo, scope.Mem)
 | 
			
		||||
	v.loaded = true
 | 
			
		||||
 | 
			
		||||
	switch ttyp := typ.(type) {
 | 
			
		||||
	case *dwarf.PtrType:
 | 
			
		||||
	case *godwarf.PtrType:
 | 
			
		||||
		switch argv.Kind {
 | 
			
		||||
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
			// ok
 | 
			
		||||
@@ -162,10 +317,10 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
			
		||||
 | 
			
		||||
		n, _ := constant.Int64Val(argv.Value)
 | 
			
		||||
 | 
			
		||||
		v.Children = []Variable{*(scope.newVariable("", uintptr(n), ttyp.Type))}
 | 
			
		||||
		v.Children = []Variable{*(scope.newVariable("", uintptr(n), ttyp.Type, scope.Mem))}
 | 
			
		||||
		return v, nil
 | 
			
		||||
 | 
			
		||||
	case *dwarf.UintType:
 | 
			
		||||
	case *godwarf.UintType:
 | 
			
		||||
		switch argv.Kind {
 | 
			
		||||
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
			n, _ := constant.Int64Val(argv.Value)
 | 
			
		||||
@@ -179,8 +334,11 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
			
		||||
			x, _ := constant.Float64Val(argv.Value)
 | 
			
		||||
			v.Value = constant.MakeUint64(uint64(x))
 | 
			
		||||
			return v, nil
 | 
			
		||||
		case reflect.Ptr:
 | 
			
		||||
			v.Value = constant.MakeUint64(uint64(argv.Children[0].Addr))
 | 
			
		||||
			return v, nil
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.IntType:
 | 
			
		||||
	case *godwarf.IntType:
 | 
			
		||||
		switch argv.Kind {
 | 
			
		||||
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
			n, _ := constant.Int64Val(argv.Value)
 | 
			
		||||
@@ -195,7 +353,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
			
		||||
			v.Value = constant.MakeInt64(int64(x))
 | 
			
		||||
			return v, nil
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.FloatType:
 | 
			
		||||
	case *godwarf.FloatType:
 | 
			
		||||
		switch argv.Kind {
 | 
			
		||||
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
			fallthrough
 | 
			
		||||
@@ -205,7 +363,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
 | 
			
		||||
			v.Value = argv.Value
 | 
			
		||||
			return v, nil
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.ComplexType:
 | 
			
		||||
	case *godwarf.ComplexType:
 | 
			
		||||
		switch argv.Kind {
 | 
			
		||||
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
			
		||||
			fallthrough
 | 
			
		||||
@@ -370,10 +528,10 @@ func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | 
			
		||||
 | 
			
		||||
	sz := int64(0)
 | 
			
		||||
	if realev.RealType != nil {
 | 
			
		||||
		sz = realev.RealType.(*dwarf.FloatType).Size()
 | 
			
		||||
		sz = realev.RealType.(*godwarf.FloatType).Size()
 | 
			
		||||
	}
 | 
			
		||||
	if imagev.RealType != nil {
 | 
			
		||||
		isz := imagev.RealType.(*dwarf.FloatType).Size()
 | 
			
		||||
		isz := imagev.RealType.(*godwarf.FloatType).Size()
 | 
			
		||||
		if isz > sz {
 | 
			
		||||
			sz = isz
 | 
			
		||||
		}
 | 
			
		||||
@@ -383,9 +541,9 @@ func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | 
			
		||||
		sz = 128
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typ := &dwarf.ComplexType{BasicType: dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, BitSize: sz, BitOffset: 0}}
 | 
			
		||||
	typ := &godwarf.ComplexType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, BitSize: sz, BitOffset: 0}}
 | 
			
		||||
 | 
			
		||||
	r := realev.newVariable("", 0, typ)
 | 
			
		||||
	r := realev.newVariable("", 0, typ, nil)
 | 
			
		||||
	r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value))
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -432,33 +590,29 @@ func realBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
 | 
			
		||||
func (scope *EvalScope) evalIdent(node *ast.Ident) (*Variable, error) {
 | 
			
		||||
	switch node.Name {
 | 
			
		||||
	case "true", "false":
 | 
			
		||||
		return newConstant(constant.MakeBool(node.Name == "true"), scope.Thread), nil
 | 
			
		||||
		return newConstant(constant.MakeBool(node.Name == "true"), scope.Mem), nil
 | 
			
		||||
	case "nil":
 | 
			
		||||
		return nilVariable, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// try to interpret this as a local variable
 | 
			
		||||
	v, err := scope.extractVarInfo(node.Name)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return v, nil
 | 
			
		||||
	vars, err := scope.Locals()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	origErr := err
 | 
			
		||||
	// workaround: sometimes go inserts an entry for '&varname' instead of varname
 | 
			
		||||
	v, err = scope.extractVarInfo("&" + node.Name)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		v = v.maybeDereference()
 | 
			
		||||
		v.Name = node.Name
 | 
			
		||||
		return v, nil
 | 
			
		||||
	for i := range vars {
 | 
			
		||||
		if vars[i].Name == node.Name && vars[i].Flags&VariableShadowed == 0 {
 | 
			
		||||
			return vars[i], nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// if it's not a local variable then it could be a package variable w/o explicit package name
 | 
			
		||||
	_, _, fn := scope.Thread.dbp.PCToLine(scope.PC)
 | 
			
		||||
	if fn != nil {
 | 
			
		||||
		if v, err = scope.packageVarAddr(fn.PackageName() + "." + node.Name); err == nil {
 | 
			
		||||
	if scope.Fn != nil {
 | 
			
		||||
		if v, err := scope.findGlobal(scope.Fn.PackageName() + "." + node.Name); err == nil {
 | 
			
		||||
			v.Name = node.Name
 | 
			
		||||
			return v, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, origErr
 | 
			
		||||
	return nil, fmt.Errorf("could not find symbol value for %s", node.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Evaluates expressions <subexpr>.<field name> where subexpr is not a package name
 | 
			
		||||
@@ -467,6 +621,13 @@ func (scope *EvalScope) evalStructSelector(node *ast.SelectorExpr) (*Variable, e
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	rv, err := xv.findMethod(node.Sel.Name)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if rv != nil {
 | 
			
		||||
		return rv, nil
 | 
			
		||||
	}
 | 
			
		||||
	return xv.structMember(node.Sel.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -489,13 +650,23 @@ func (scope *EvalScope) evalTypeAssert(node *ast.TypeAssertExpr) (*Variable, err
 | 
			
		||||
	if xv.Children[0].Addr == 0 {
 | 
			
		||||
		return nil, fmt.Errorf("interface conversion: %s is nil, not %s", xv.DwarfType.String(), exprToString(node.Type))
 | 
			
		||||
	}
 | 
			
		||||
	typ, err := scope.Thread.dbp.findTypeExpr(node.Type)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if xv.Children[0].DwarfType.Common().Name != typ.Common().Name {
 | 
			
		||||
		return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
 | 
			
		||||
	// Accept .(data) as a type assertion that always succeeds, so that users
 | 
			
		||||
	// can access the data field of an interface without actually having to
 | 
			
		||||
	// type the concrete type.
 | 
			
		||||
	if idtyp, isident := node.Type.(*ast.Ident); !isident || idtyp.Name != "data" {
 | 
			
		||||
		typ, err := scope.BinInfo.findTypeExpr(node.Type)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if xv.Children[0].DwarfType.Common().Name != typ.Common().Name {
 | 
			
		||||
			return nil, fmt.Errorf("interface conversion: %s is %s, not %s", xv.DwarfType.Common().Name, xv.Children[0].TypeString(), typ.Common().Name)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// loadInterface will set OnlyAddr for the data member since here we are
 | 
			
		||||
	// passing false to loadData, however returning the variable with OnlyAddr
 | 
			
		||||
	// set here would be wrong since, once the expression evaluation
 | 
			
		||||
	// terminates, the value of this variable will be loaded.
 | 
			
		||||
	xv.Children[0].OnlyAddr = false
 | 
			
		||||
	return &xv.Children[0], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -509,12 +680,27 @@ func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
 | 
			
		||||
		return nil, xev.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xev = xev.maybeDereference()
 | 
			
		||||
 | 
			
		||||
	idxev, err := scope.evalAST(node.Index)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cantindex := fmt.Errorf("expression \"%s\" (%s) does not support indexing", exprToString(node.X), xev.TypeString())
 | 
			
		||||
 | 
			
		||||
	switch xev.Kind {
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		if xev == nilVariable {
 | 
			
		||||
			return nil, cantindex
 | 
			
		||||
		}
 | 
			
		||||
		_, isarrptr := xev.RealType.(*godwarf.PtrType).Type.(*godwarf.ArrayType)
 | 
			
		||||
		if !isarrptr {
 | 
			
		||||
			return nil, cantindex
 | 
			
		||||
		}
 | 
			
		||||
		xev = xev.maybeDereference()
 | 
			
		||||
		fallthrough
 | 
			
		||||
 | 
			
		||||
	case reflect.Slice, reflect.Array, reflect.String:
 | 
			
		||||
		if xev.Base == 0 {
 | 
			
		||||
			return nil, fmt.Errorf("can not index \"%s\"", exprToString(node.X))
 | 
			
		||||
@@ -532,8 +718,7 @@ func (scope *EvalScope) evalIndex(node *ast.IndexExpr) (*Variable, error) {
 | 
			
		||||
		}
 | 
			
		||||
		return xev.mapAccess(idxev)
 | 
			
		||||
	default:
 | 
			
		||||
		return nil, fmt.Errorf("expression \"%s\" (%s) does not support indexing", exprToString(node.X), xev.TypeString())
 | 
			
		||||
 | 
			
		||||
		return nil, cantindex
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -585,9 +770,9 @@ func (scope *EvalScope) evalReslice(node *ast.SliceExpr) (*Variable, error) {
 | 
			
		||||
			return nil, fmt.Errorf("second slice argument must be empty for maps")
 | 
			
		||||
		}
 | 
			
		||||
		xev.mapSkip += int(low)
 | 
			
		||||
		xev.loadValue(loadFullValue)
 | 
			
		||||
		if xev.Unreadable != nil {
 | 
			
		||||
			return nil, xev.Unreadable
 | 
			
		||||
		xev.mapIterator() // reads map length
 | 
			
		||||
		if int64(xev.mapSkip) >= xev.Len {
 | 
			
		||||
			return nil, fmt.Errorf("map index out of bounds")
 | 
			
		||||
		}
 | 
			
		||||
		return xev, nil
 | 
			
		||||
	default:
 | 
			
		||||
@@ -631,14 +816,18 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
 | 
			
		||||
		return nil, fmt.Errorf("can not take address of \"%s\"", exprToString(node.X))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xev.OnlyAddr = true
 | 
			
		||||
	return xev.pointerToVariable(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	typename := "*" + xev.DwarfType.Common().Name
 | 
			
		||||
	rv := scope.newVariable("", 0, &dwarf.PtrType{CommonType: dwarf.CommonType{ByteSize: int64(scope.Thread.dbp.arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
 | 
			
		||||
	rv.Children = []Variable{*xev}
 | 
			
		||||
func (v *Variable) pointerToVariable() *Variable {
 | 
			
		||||
	v.OnlyAddr = true
 | 
			
		||||
 | 
			
		||||
	typename := "*" + v.DwarfType.Common().Name
 | 
			
		||||
	rv := v.newVariable("", 0, &godwarf.PtrType{CommonType: godwarf.CommonType{ByteSize: int64(v.bi.Arch.PtrSize()), Name: typename}, Type: v.DwarfType}, v.mem)
 | 
			
		||||
	rv.Children = []Variable{*v}
 | 
			
		||||
	rv.loaded = true
 | 
			
		||||
 | 
			
		||||
	return rv, nil
 | 
			
		||||
	return rv
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func constantUnaryOp(op token.Token, y constant.Value) (r constant.Value, err error) {
 | 
			
		||||
@@ -688,6 +877,9 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
 | 
			
		||||
	if xv.Unreadable != nil {
 | 
			
		||||
		return nil, xv.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
	if xv.FloatSpecial != 0 {
 | 
			
		||||
		return nil, errOperationOnSpecialFloat
 | 
			
		||||
	}
 | 
			
		||||
	if xv.Value == nil {
 | 
			
		||||
		return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
 | 
			
		||||
	}
 | 
			
		||||
@@ -696,14 +888,14 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if xv.DwarfType != nil {
 | 
			
		||||
		r := xv.newVariable("", 0, xv.DwarfType)
 | 
			
		||||
		r := xv.newVariable("", 0, xv.DwarfType, scope.Mem)
 | 
			
		||||
		r.Value = rc
 | 
			
		||||
		return r, nil
 | 
			
		||||
	}
 | 
			
		||||
	return newConstant(rc, xv.mem), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func negotiateType(op token.Token, xv, yv *Variable) (dwarf.Type, error) {
 | 
			
		||||
func negotiateType(op token.Token, xv, yv *Variable) (godwarf.Type, error) {
 | 
			
		||||
	if xv == nilVariable {
 | 
			
		||||
		return nil, negotiateTypeNil(op, yv)
 | 
			
		||||
	}
 | 
			
		||||
@@ -777,23 +969,36 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	xv.loadValue(loadFullValue)
 | 
			
		||||
	if xv.Unreadable != nil {
 | 
			
		||||
		return nil, xv.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// short circuits logical operators
 | 
			
		||||
	switch node.Op {
 | 
			
		||||
	case token.LAND:
 | 
			
		||||
		if !constant.BoolVal(xv.Value) {
 | 
			
		||||
			return newConstant(xv.Value, xv.mem), nil
 | 
			
		||||
		}
 | 
			
		||||
	case token.LOR:
 | 
			
		||||
		if constant.BoolVal(xv.Value) {
 | 
			
		||||
			return newConstant(xv.Value, xv.mem), nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	yv, err := scope.evalAST(node.Y)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	xv.loadValue(loadFullValue)
 | 
			
		||||
	yv.loadValue(loadFullValue)
 | 
			
		||||
 | 
			
		||||
	if xv.Unreadable != nil {
 | 
			
		||||
		return nil, xv.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if yv.Unreadable != nil {
 | 
			
		||||
		return nil, yv.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
 | 
			
		||||
		return nil, errOperationOnSpecialFloat
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typ, err := negotiateType(node.Op, xv, yv)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -801,8 +1006,8 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
 | 
			
		||||
 | 
			
		||||
	op := node.Op
 | 
			
		||||
	if typ != nil && (op == token.QUO) {
 | 
			
		||||
		_, isint := typ.(*dwarf.IntType)
 | 
			
		||||
		_, isuint := typ.(*dwarf.UintType)
 | 
			
		||||
		_, isint := typ.(*godwarf.IntType)
 | 
			
		||||
		_, isuint := typ.(*godwarf.UintType)
 | 
			
		||||
		if isint || isuint {
 | 
			
		||||
			// forces integer division if the result type is integer
 | 
			
		||||
			op = token.QUO_ASSIGN
 | 
			
		||||
@@ -835,13 +1040,16 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
 | 
			
		||||
			return newConstant(rc, xv.mem), nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		r := xv.newVariable("", 0, typ)
 | 
			
		||||
		r := xv.newVariable("", 0, typ, scope.Mem)
 | 
			
		||||
		r.Value = rc
 | 
			
		||||
		if r.Kind == reflect.String {
 | 
			
		||||
			r.Len = xv.Len + yv.Len
 | 
			
		||||
		}
 | 
			
		||||
		return r, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Comapres xv to yv using operator op
 | 
			
		||||
// Compares xv to yv using operator op
 | 
			
		||||
// Both xv and yv must be loaded and have a compatible type (as determined by negotiateType)
 | 
			
		||||
func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
 | 
			
		||||
	switch xv.Kind {
 | 
			
		||||
@@ -854,6 +1062,14 @@ func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
 | 
			
		||||
	case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
 | 
			
		||||
		return constantCompare(op, xv.Value, yv.Value)
 | 
			
		||||
	case reflect.String:
 | 
			
		||||
		if xv.Len != yv.Len {
 | 
			
		||||
			switch op {
 | 
			
		||||
			case token.EQL:
 | 
			
		||||
				return false, nil
 | 
			
		||||
			case token.NEQ:
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if int64(len(constant.StringVal(xv.Value))) != xv.Len || int64(len(constant.StringVal(yv.Value))) != yv.Len {
 | 
			
		||||
			return false, fmt.Errorf("string too long for comparison")
 | 
			
		||||
		}
 | 
			
		||||
@@ -898,7 +1114,7 @@ func compareOp(op token.Token, xv *Variable, yv *Variable) (bool, error) {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
		if int64(len(xv.Children)) != xv.Len || int64(len(yv.Children)) != yv.Len {
 | 
			
		||||
			return false, fmt.Errorf("sturcture too deep for comparison")
 | 
			
		||||
			return false, fmt.Errorf("structure too deep for comparison")
 | 
			
		||||
		}
 | 
			
		||||
		eql, err = equalChildren(xv, yv, false)
 | 
			
		||||
	case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
 | 
			
		||||
@@ -924,7 +1140,7 @@ func (v *Variable) isNil() bool {
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		return v.Children[0].Addr == 0
 | 
			
		||||
	case reflect.Interface:
 | 
			
		||||
		return false
 | 
			
		||||
		return v.Children[0].Addr == 0 && v.Children[0].Kind == reflect.Invalid
 | 
			
		||||
	case reflect.Slice, reflect.Map, reflect.Func, reflect.Chan:
 | 
			
		||||
		return v.Base == 0
 | 
			
		||||
	}
 | 
			
		||||
@@ -956,7 +1172,7 @@ func (v *Variable) asInt() (int64, error) {
 | 
			
		||||
		if v.Unreadable != nil {
 | 
			
		||||
			return 0, v.Unreadable
 | 
			
		||||
		}
 | 
			
		||||
		if _, ok := v.DwarfType.(*dwarf.IntType); !ok {
 | 
			
		||||
		if _, ok := v.DwarfType.(*godwarf.IntType); !ok {
 | 
			
		||||
			return 0, fmt.Errorf("can not convert value of type %s to int", v.DwarfType.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -974,7 +1190,7 @@ func (v *Variable) asUint() (uint64, error) {
 | 
			
		||||
		if v.Unreadable != nil {
 | 
			
		||||
			return 0, v.Unreadable
 | 
			
		||||
		}
 | 
			
		||||
		if _, ok := v.DwarfType.(*dwarf.UintType); !ok {
 | 
			
		||||
		if _, ok := v.DwarfType.(*godwarf.UintType); !ok {
 | 
			
		||||
			return 0, fmt.Errorf("can not convert value of type %s to uint", v.DwarfType.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -982,10 +1198,18 @@ func (v *Variable) asUint() (uint64, error) {
 | 
			
		||||
	return n, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Variable) isType(typ dwarf.Type, kind reflect.Kind) error {
 | 
			
		||||
type typeConvErr struct {
 | 
			
		||||
	srcType, dstType godwarf.Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err *typeConvErr) Error() string {
 | 
			
		||||
	return fmt.Sprintf("can not convert value of type %s to %s", err.srcType.String(), err.dstType.String())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
 | 
			
		||||
	if v.DwarfType != nil {
 | 
			
		||||
		if typ != nil && typ.String() != v.RealType.String() {
 | 
			
		||||
			return fmt.Errorf("can not convert value of type %s to %s", v.DwarfType.String(), typ.String())
 | 
			
		||||
		if typ == nil || !sameType(typ, v.RealType) {
 | 
			
		||||
			return &typeConvErr{v.DwarfType, typ}
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -1010,27 +1234,27 @@ func (v *Variable) isType(typ dwarf.Type, kind reflect.Kind) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch typ.(type) {
 | 
			
		||||
	case *dwarf.IntType:
 | 
			
		||||
	case *godwarf.IntType:
 | 
			
		||||
		if v.Value.Kind() != constant.Int {
 | 
			
		||||
			return converr
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.UintType:
 | 
			
		||||
	case *godwarf.UintType:
 | 
			
		||||
		if v.Value.Kind() != constant.Int {
 | 
			
		||||
			return converr
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.FloatType:
 | 
			
		||||
	case *godwarf.FloatType:
 | 
			
		||||
		if (v.Value.Kind() != constant.Int) && (v.Value.Kind() != constant.Float) {
 | 
			
		||||
			return converr
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.BoolType:
 | 
			
		||||
	case *godwarf.BoolType:
 | 
			
		||||
		if v.Value.Kind() != constant.Bool {
 | 
			
		||||
			return converr
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.StringType:
 | 
			
		||||
	case *godwarf.StringType:
 | 
			
		||||
		if v.Value.Kind() != constant.String {
 | 
			
		||||
			return converr
 | 
			
		||||
		}
 | 
			
		||||
	case *dwarf.ComplexType:
 | 
			
		||||
	case *godwarf.ComplexType:
 | 
			
		||||
		if v.Value.Kind() != constant.Complex && v.Value.Kind() != constant.Float && v.Value.Kind() != constant.Int {
 | 
			
		||||
			return converr
 | 
			
		||||
		}
 | 
			
		||||
@@ -1041,11 +1265,43 @@ func (v *Variable) isType(typ dwarf.Type, kind reflect.Kind) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sameType(t1, t2 godwarf.Type) bool {
 | 
			
		||||
	// Because of a bug in the go linker a type that refers to another type
 | 
			
		||||
	// (for example a pointer type) will usually use the typedef but rarely use
 | 
			
		||||
	// the non-typedef entry directly.
 | 
			
		||||
	// For types that we read directly from go this is fine because it's
 | 
			
		||||
	// consistent, however we also synthesize some types ourselves
 | 
			
		||||
	// (specifically pointers and slices) and we always use a reference through
 | 
			
		||||
	// a typedef.
 | 
			
		||||
	t1 = resolveTypedef(t1)
 | 
			
		||||
	t2 = resolveTypedef(t2)
 | 
			
		||||
 | 
			
		||||
	if tt1, isptr1 := t1.(*godwarf.PtrType); isptr1 {
 | 
			
		||||
		tt2, isptr2 := t2.(*godwarf.PtrType)
 | 
			
		||||
		if !isptr2 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return sameType(tt1.Type, tt2.Type)
 | 
			
		||||
	}
 | 
			
		||||
	if tt1, isslice1 := t1.(*godwarf.SliceType); isslice1 {
 | 
			
		||||
		tt2, isslice2 := t2.(*godwarf.SliceType)
 | 
			
		||||
		if !isslice2 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return sameType(tt1.ElemType, tt2.ElemType)
 | 
			
		||||
	}
 | 
			
		||||
	return t1.String() == t2.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Variable) sliceAccess(idx int) (*Variable, error) {
 | 
			
		||||
	if idx < 0 || int64(idx) >= v.Len {
 | 
			
		||||
		return nil, fmt.Errorf("index out of bounds")
 | 
			
		||||
	}
 | 
			
		||||
	return v.newVariable("", v.Base+uintptr(int64(idx)*v.stride), v.fieldType), nil
 | 
			
		||||
	mem := v.mem
 | 
			
		||||
	if v.Kind != reflect.Array {
 | 
			
		||||
		mem = DereferenceMemory(mem)
 | 
			
		||||
	}
 | 
			
		||||
	return v.newVariable("", v.Base+uintptr(int64(idx)*v.stride), v.fieldType, mem), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
 | 
			
		||||
@@ -1063,7 +1319,7 @@ func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
 | 
			
		||||
		}
 | 
			
		||||
		if first {
 | 
			
		||||
			first = false
 | 
			
		||||
			if err := idx.isType(key.DwarfType, key.Kind); err != nil {
 | 
			
		||||
			if err := idx.isType(key.RealType, key.Kind); err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -1095,22 +1351,16 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typ := v.DwarfType
 | 
			
		||||
	if _, isarr := v.DwarfType.(*dwarf.ArrayType); isarr {
 | 
			
		||||
		typ = &dwarf.SliceType{
 | 
			
		||||
			StructType: dwarf.StructType{
 | 
			
		||||
				CommonType: dwarf.CommonType{
 | 
			
		||||
					ByteSize: 24,
 | 
			
		||||
					Name:     "",
 | 
			
		||||
				},
 | 
			
		||||
				StructName: fmt.Sprintf("[]%s", v.fieldType.Common().Name),
 | 
			
		||||
				Kind:       "struct",
 | 
			
		||||
				Field:      nil,
 | 
			
		||||
			},
 | 
			
		||||
			ElemType: v.fieldType,
 | 
			
		||||
		}
 | 
			
		||||
	if _, isarr := v.DwarfType.(*godwarf.ArrayType); isarr {
 | 
			
		||||
		typ = fakeSliceType(v.fieldType)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := v.newVariable("", 0, typ)
 | 
			
		||||
	mem := v.mem
 | 
			
		||||
	if v.Kind != reflect.Array {
 | 
			
		||||
		mem = DereferenceMemory(mem)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := v.newVariable("", 0, typ, mem)
 | 
			
		||||
	r.Cap = len
 | 
			
		||||
	r.Len = len
 | 
			
		||||
	r.Base = base
 | 
			
		||||
@@ -1119,3 +1369,147 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
 | 
			
		||||
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findMethod finds method mname in the type of variable v
 | 
			
		||||
func (v *Variable) findMethod(mname string) (*Variable, error) {
 | 
			
		||||
	if _, isiface := v.RealType.(*godwarf.InterfaceType); isiface {
 | 
			
		||||
		v.loadInterface(0, false, loadFullValue)
 | 
			
		||||
		if v.Unreadable != nil {
 | 
			
		||||
			return nil, v.Unreadable
 | 
			
		||||
		}
 | 
			
		||||
		return v.Children[0].findMethod(mname)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typ := v.DwarfType
 | 
			
		||||
	ptyp, isptr := typ.(*godwarf.PtrType)
 | 
			
		||||
	if isptr {
 | 
			
		||||
		typ = ptyp.Type
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, istypedef := typ.(*godwarf.TypedefType); !istypedef {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	typePath := typ.Common().Name
 | 
			
		||||
	dot := strings.LastIndex(typePath, ".")
 | 
			
		||||
	if dot < 0 {
 | 
			
		||||
		// probably just a C type
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pkg := typePath[:dot]
 | 
			
		||||
	receiver := typePath[dot+1:]
 | 
			
		||||
 | 
			
		||||
	if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.%s.%s", pkg, receiver, mname)]; ok {
 | 
			
		||||
		r, err := functionToVariable(fn, v.bi, v.mem)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if isptr {
 | 
			
		||||
			r.Children = append(r.Children, *(v.maybeDereference()))
 | 
			
		||||
		} else {
 | 
			
		||||
			r.Children = append(r.Children, *v)
 | 
			
		||||
		}
 | 
			
		||||
		return r, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if fn, ok := v.bi.LookupFunc[fmt.Sprintf("%s.(*%s).%s", pkg, receiver, mname)]; ok {
 | 
			
		||||
		r, err := functionToVariable(fn, v.bi, v.mem)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		if isptr {
 | 
			
		||||
			r.Children = append(r.Children, *v)
 | 
			
		||||
		} else {
 | 
			
		||||
			r.Children = append(r.Children, *(v.pointerToVariable()))
 | 
			
		||||
		}
 | 
			
		||||
		return r, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func functionToVariable(fn *Function, bi *BinaryInfo, mem MemoryReadWriter) (*Variable, error) {
 | 
			
		||||
	typ, err := fn.fakeType(bi, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	v := newVariable(fn.Name, 0, typ, bi, mem)
 | 
			
		||||
	v.Value = constant.MakeString(fn.Name)
 | 
			
		||||
	v.loaded = true
 | 
			
		||||
	v.Base = uintptr(fn.Entry)
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fakeSliceType(fieldType godwarf.Type) godwarf.Type {
 | 
			
		||||
	return &godwarf.SliceType{
 | 
			
		||||
		StructType: godwarf.StructType{
 | 
			
		||||
			CommonType: godwarf.CommonType{
 | 
			
		||||
				ByteSize: 24,
 | 
			
		||||
				Name:     "",
 | 
			
		||||
			},
 | 
			
		||||
			StructName: fmt.Sprintf("[]%s", fieldType.Common().Name),
 | 
			
		||||
			Kind:       "struct",
 | 
			
		||||
			Field:      nil,
 | 
			
		||||
		},
 | 
			
		||||
		ElemType: fieldType,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errMethodEvalUnsupported = errors.New("evaluating methods not supported on this version of Go")
 | 
			
		||||
 | 
			
		||||
func (fn *Function) fakeType(bi *BinaryInfo, removeReceiver bool) (*godwarf.FuncType, error) {
 | 
			
		||||
	if producer := bi.Producer(); producer == "" || !goversion.ProducerAfterOrEqual(producer, 1, 10) {
 | 
			
		||||
		// versions of Go prior to 1.10 do not distinguish between parameters and
 | 
			
		||||
		// return values, therefore we can't use a subprogram DIE to derive a
 | 
			
		||||
		// function type.
 | 
			
		||||
		return nil, errMethodEvalUnsupported
 | 
			
		||||
	}
 | 
			
		||||
	_, formalArgs, err := funcCallArgs(fn, bi, true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if removeReceiver {
 | 
			
		||||
		formalArgs = formalArgs[1:]
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args := make([]string, 0, len(formalArgs))
 | 
			
		||||
	rets := make([]string, 0, len(formalArgs))
 | 
			
		||||
 | 
			
		||||
	for _, formalArg := range formalArgs {
 | 
			
		||||
		var s string
 | 
			
		||||
		if strings.HasPrefix(formalArg.name, "~") {
 | 
			
		||||
			s = formalArg.typ.String()
 | 
			
		||||
		} else {
 | 
			
		||||
			s = fmt.Sprintf("%s %s", formalArg.name, formalArg.typ.String())
 | 
			
		||||
		}
 | 
			
		||||
		if formalArg.isret {
 | 
			
		||||
			rets = append(rets, s)
 | 
			
		||||
		} else {
 | 
			
		||||
			args = append(args, s)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argstr := strings.Join(args, ", ")
 | 
			
		||||
	var retstr string
 | 
			
		||||
	switch len(rets) {
 | 
			
		||||
	case 0:
 | 
			
		||||
		retstr = ""
 | 
			
		||||
	case 1:
 | 
			
		||||
		retstr = " " + rets[0]
 | 
			
		||||
	default:
 | 
			
		||||
		retstr = " (" + strings.Join(rets, ", ") + ")"
 | 
			
		||||
	}
 | 
			
		||||
	return &godwarf.FuncType{
 | 
			
		||||
		CommonType: godwarf.CommonType{
 | 
			
		||||
			Name:        "func(" + argstr + ")" + retstr,
 | 
			
		||||
			ReflectKind: reflect.Func,
 | 
			
		||||
		},
 | 
			
		||||
		//TODO(aarzilli): at the moment we aren't using the ParamType and
 | 
			
		||||
		// ReturnType fields of FuncType anywhere (when this is returned to the
 | 
			
		||||
		// client it's first converted to a string and the function calling code
 | 
			
		||||
		// reads the subroutine entry because it needs to know the stack offsets).
 | 
			
		||||
		// If we start using them they should be filled here.
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										565
									
								
								vendor/github.com/go-delve/delve/pkg/proc/fncall.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								vendor/github.com/go-delve/delve/pkg/proc/fncall.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,565 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/dwarf"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/logflags"
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This file implements the function call injection introduced in go1.11.
 | 
			
		||||
//
 | 
			
		||||
// The protocol is described in $GOROOT/src/runtime/asm_amd64.s in the
 | 
			
		||||
// comments for function runtime·debugCallV1.
 | 
			
		||||
//
 | 
			
		||||
// There are two main entry points here. The first one is CallFunction which
 | 
			
		||||
// evaluates a function call expression, sets up the function call on the
 | 
			
		||||
// selected goroutine and resumes execution of the process.
 | 
			
		||||
//
 | 
			
		||||
// The second one is (*FunctionCallState).step() which is called every time
 | 
			
		||||
// the process stops at a breakpoint inside one of the debug injcetion
 | 
			
		||||
// functions.
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	debugCallFunctionNamePrefix1 = "debugCall"
 | 
			
		||||
	debugCallFunctionNamePrefix2 = "runtime.debugCall"
 | 
			
		||||
	debugCallFunctionName        = "runtime.debugCallV1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	errFuncCallUnsupported        = errors.New("function calls not supported by this version of Go")
 | 
			
		||||
	errFuncCallUnsupportedBackend = errors.New("backend does not support function calls")
 | 
			
		||||
	errFuncCallInProgress         = errors.New("cannot call function while another function call is already in progress")
 | 
			
		||||
	errNotACallExpr               = errors.New("not a function call")
 | 
			
		||||
	errNoGoroutine                = errors.New("no goroutine selected")
 | 
			
		||||
	errGoroutineNotRunning        = errors.New("selected goroutine not running")
 | 
			
		||||
	errNotEnoughStack             = errors.New("not enough stack space")
 | 
			
		||||
	errTooManyArguments           = errors.New("too many arguments")
 | 
			
		||||
	errNotEnoughArguments         = errors.New("not enough arguments")
 | 
			
		||||
	errNoAddrUnsupported          = errors.New("arguments to a function call must have an address")
 | 
			
		||||
	errNotAGoFunction             = errors.New("not a Go function")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type functionCallState struct {
 | 
			
		||||
	// inProgress is true if a function call is in progress
 | 
			
		||||
	inProgress bool
 | 
			
		||||
	// finished is true if the function call terminated
 | 
			
		||||
	finished bool
 | 
			
		||||
	// savedRegs contains the saved registers
 | 
			
		||||
	savedRegs Registers
 | 
			
		||||
	// expr contains an expression describing the current function call
 | 
			
		||||
	expr string
 | 
			
		||||
	// err contains a saved error
 | 
			
		||||
	err error
 | 
			
		||||
	// fn is the function that is being called
 | 
			
		||||
	fn *Function
 | 
			
		||||
	// closureAddr is the address of the closure being called
 | 
			
		||||
	closureAddr uint64
 | 
			
		||||
	// argmem contains the argument frame of this function call
 | 
			
		||||
	argmem []byte
 | 
			
		||||
	// retvars contains the return variables after the function call terminates without panic'ing
 | 
			
		||||
	retvars []*Variable
 | 
			
		||||
	// retLoadCfg is the load configuration used to load return values
 | 
			
		||||
	retLoadCfg *LoadConfig
 | 
			
		||||
	// panicvar is a variable used to store the value of the panic, if the
 | 
			
		||||
	// called function panics.
 | 
			
		||||
	panicvar *Variable
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CallFunction starts a debugger injected function call on the current thread of p.
 | 
			
		||||
// See runtime.debugCallV1 in $GOROOT/src/runtime/asm_amd64.s for a
 | 
			
		||||
// description of the protocol.
 | 
			
		||||
func CallFunction(p Process, expr string, retLoadCfg *LoadConfig, checkEscape bool) error {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	if !p.Common().fncallEnabled {
 | 
			
		||||
		return errFuncCallUnsupportedBackend
 | 
			
		||||
	}
 | 
			
		||||
	fncall := &p.Common().fncallState
 | 
			
		||||
	if fncall.inProgress {
 | 
			
		||||
		return errFuncCallInProgress
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*fncall = functionCallState{}
 | 
			
		||||
 | 
			
		||||
	dbgcallfn := bi.LookupFunc[debugCallFunctionName]
 | 
			
		||||
	if dbgcallfn == nil {
 | 
			
		||||
		return errFuncCallUnsupported
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check that the selected goroutine is running
 | 
			
		||||
	g := p.SelectedGoroutine()
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		return errNoGoroutine
 | 
			
		||||
	}
 | 
			
		||||
	if g.Status != Grunning || g.Thread == nil {
 | 
			
		||||
		return errGoroutineNotRunning
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// check that there are at least 256 bytes free on the stack
 | 
			
		||||
	regs, err := g.Thread.Registers(true)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	regs = regs.Copy()
 | 
			
		||||
	if regs.SP()-256 <= g.stacklo {
 | 
			
		||||
		return errNotEnoughStack
 | 
			
		||||
	}
 | 
			
		||||
	_, err = regs.Get(int(x86asm.RAX))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errFuncCallUnsupportedBackend
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fn, closureAddr, argvars, err := funcCallEvalExpr(p, expr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argmem, err := funcCallArgFrame(fn, argvars, g, bi, checkEscape)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := callOP(bi, g.Thread, regs, dbgcallfn.Entry); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// write the desired argument frame size at SP-(2*pointer_size) (the extra pointer is the saved PC)
 | 
			
		||||
	if err := writePointer(bi, g.Thread, regs.SP()-3*uint64(bi.Arch.PtrSize()), uint64(len(argmem))); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fncall.inProgress = true
 | 
			
		||||
	fncall.savedRegs = regs
 | 
			
		||||
	fncall.expr = expr
 | 
			
		||||
	fncall.fn = fn
 | 
			
		||||
	fncall.closureAddr = closureAddr
 | 
			
		||||
	fncall.argmem = argmem
 | 
			
		||||
	fncall.retLoadCfg = retLoadCfg
 | 
			
		||||
 | 
			
		||||
	fncallLog("function call initiated %v frame size %d\n", fn, len(argmem))
 | 
			
		||||
 | 
			
		||||
	return Continue(p)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fncallLog(fmtstr string, args ...interface{}) {
 | 
			
		||||
	logflags.FnCallLogger().Infof(fmtstr, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writePointer writes val as an architecture pointer at addr in mem.
 | 
			
		||||
func writePointer(bi *BinaryInfo, mem MemoryReadWriter, addr, val uint64) error {
 | 
			
		||||
	ptrbuf := make([]byte, bi.Arch.PtrSize())
 | 
			
		||||
 | 
			
		||||
	// TODO: use target architecture endianness instead of LittleEndian
 | 
			
		||||
	switch len(ptrbuf) {
 | 
			
		||||
	case 4:
 | 
			
		||||
		binary.LittleEndian.PutUint32(ptrbuf, uint32(val))
 | 
			
		||||
	case 8:
 | 
			
		||||
		binary.LittleEndian.PutUint64(ptrbuf, val)
 | 
			
		||||
	default:
 | 
			
		||||
		panic(fmt.Errorf("unsupported pointer size %d", len(ptrbuf)))
 | 
			
		||||
	}
 | 
			
		||||
	_, err := mem.WriteMemory(uintptr(addr), ptrbuf)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// callOP simulates a call instruction on the given thread:
 | 
			
		||||
// * pushes the current value of PC on the stack (adjusting SP)
 | 
			
		||||
// * changes the value of PC to callAddr
 | 
			
		||||
// Note: regs are NOT updated!
 | 
			
		||||
func callOP(bi *BinaryInfo, thread Thread, regs Registers, callAddr uint64) error {
 | 
			
		||||
	sp := regs.SP()
 | 
			
		||||
	// push PC on the stack
 | 
			
		||||
	sp -= uint64(bi.Arch.PtrSize())
 | 
			
		||||
	if err := thread.SetSP(sp); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := writePointer(bi, thread, sp, regs.PC()); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return thread.SetPC(callAddr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// funcCallEvalExpr evaluates expr, which must be a function call, returns
 | 
			
		||||
// the function being called and its arguments.
 | 
			
		||||
func funcCallEvalExpr(p Process, expr string) (fn *Function, closureAddr uint64, argvars []*Variable, err error) {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	scope, err := GoroutineScope(p.CurrentThread())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, 0, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	t, err := parser.ParseExpr(expr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, 0, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	callexpr, iscall := t.(*ast.CallExpr)
 | 
			
		||||
	if !iscall {
 | 
			
		||||
		return nil, 0, nil, errNotACallExpr
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fnvar, err := scope.evalAST(callexpr.Fun)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, 0, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if fnvar.Kind != reflect.Func {
 | 
			
		||||
		return nil, 0, nil, fmt.Errorf("expression %q is not a function", exprToString(callexpr.Fun))
 | 
			
		||||
	}
 | 
			
		||||
	fnvar.loadValue(LoadConfig{false, 0, 0, 0, 0, 0})
 | 
			
		||||
	if fnvar.Unreadable != nil {
 | 
			
		||||
		return nil, 0, nil, fnvar.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
	if fnvar.Base == 0 {
 | 
			
		||||
		return nil, 0, nil, errors.New("nil pointer dereference")
 | 
			
		||||
	}
 | 
			
		||||
	fn = bi.PCToFunc(uint64(fnvar.Base))
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return nil, 0, nil, fmt.Errorf("could not find DIE for function %q", exprToString(callexpr.Fun))
 | 
			
		||||
	}
 | 
			
		||||
	if !fn.cu.isgo {
 | 
			
		||||
		return nil, 0, nil, errNotAGoFunction
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argvars = make([]*Variable, 0, len(callexpr.Args)+1)
 | 
			
		||||
	if len(fnvar.Children) > 0 {
 | 
			
		||||
		// receiver argument
 | 
			
		||||
		argvars = append(argvars, &fnvar.Children[0])
 | 
			
		||||
	}
 | 
			
		||||
	for i := range callexpr.Args {
 | 
			
		||||
		argvar, err := scope.evalAST(callexpr.Args[i])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, 0, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		argvar.Name = exprToString(callexpr.Args[i])
 | 
			
		||||
		argvars = append(argvars, argvar)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return fn, fnvar.funcvalAddr(), argvars, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type funcCallArg struct {
 | 
			
		||||
	name  string
 | 
			
		||||
	typ   godwarf.Type
 | 
			
		||||
	off   int64
 | 
			
		||||
	isret bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// funcCallArgFrame checks type and pointer escaping for the arguments and
 | 
			
		||||
// returns the argument frame.
 | 
			
		||||
func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo, checkEscape bool) (argmem []byte, err error) {
 | 
			
		||||
	argFrameSize, formalArgs, err := funcCallArgs(fn, bi, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if len(actualArgs) > len(formalArgs) {
 | 
			
		||||
		return nil, errTooManyArguments
 | 
			
		||||
	}
 | 
			
		||||
	if len(actualArgs) < len(formalArgs) {
 | 
			
		||||
		return nil, errNotEnoughArguments
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// constructs arguments frame
 | 
			
		||||
	argmem = make([]byte, argFrameSize)
 | 
			
		||||
	argmemWriter := &bufferMemoryReadWriter{argmem}
 | 
			
		||||
	for i := range formalArgs {
 | 
			
		||||
		formalArg := &formalArgs[i]
 | 
			
		||||
		actualArg := actualArgs[i]
 | 
			
		||||
 | 
			
		||||
		if checkEscape {
 | 
			
		||||
			//TODO(aarzilli): only apply the escapeCheck to leaking parameters.
 | 
			
		||||
			if err := escapeCheck(actualArg, formalArg.name, g); err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("cannot use %s as argument %s in function %s: %v", actualArg.Name, formalArg.name, fn.Name, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//TODO(aarzilli): autmoatic wrapping in interfaces for cases not handled
 | 
			
		||||
		// by convertToEface.
 | 
			
		||||
 | 
			
		||||
		formalArgVar := newVariable(formalArg.name, uintptr(formalArg.off+fakeAddress), formalArg.typ, bi, argmemWriter)
 | 
			
		||||
		if err := formalArgVar.setValue(actualArg, actualArg.Name); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return argmem, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize int64, formalArgs []funcCallArg, err error) {
 | 
			
		||||
	const CFA = 0x1000
 | 
			
		||||
	vrdr := reader.Variables(bi.dwarf, fn.offset, reader.ToRelAddr(fn.Entry, bi.staticBase), int(^uint(0)>>1), false)
 | 
			
		||||
 | 
			
		||||
	// typechecks arguments, calculates argument frame size
 | 
			
		||||
	for vrdr.Next() {
 | 
			
		||||
		e := vrdr.Entry()
 | 
			
		||||
		if e.Tag != dwarf.TagFormalParameter {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		entry, argname, typ, err := readVarEntry(e, bi)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, nil, err
 | 
			
		||||
		}
 | 
			
		||||
		typ = resolveTypedef(typ)
 | 
			
		||||
		locprog, _, err := bi.locationExpr(entry, dwarf.AttrLocation, fn.Entry)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, nil, fmt.Errorf("could not get argument location of %s: %v", argname, err)
 | 
			
		||||
		}
 | 
			
		||||
		off, _, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: CFA, FrameBase: CFA}, locprog)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, nil, fmt.Errorf("unsupported location expression for argument %s: %v", argname, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		off -= CFA
 | 
			
		||||
 | 
			
		||||
		if e := off + typ.Size(); e > argFrameSize {
 | 
			
		||||
			argFrameSize = e
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if isret, _ := entry.Val(dwarf.AttrVarParam).(bool); !isret || includeRet {
 | 
			
		||||
			formalArgs = append(formalArgs, funcCallArg{name: argname, typ: typ, off: off, isret: isret})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err := vrdr.Err(); err != nil {
 | 
			
		||||
		return 0, nil, fmt.Errorf("DWARF read error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sort.Slice(formalArgs, func(i, j int) bool {
 | 
			
		||||
		return formalArgs[i].off < formalArgs[j].off
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	return argFrameSize, formalArgs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func escapeCheck(v *Variable, name string, g *G) error {
 | 
			
		||||
	switch v.Kind {
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		var w *Variable
 | 
			
		||||
		if len(v.Children) == 1 {
 | 
			
		||||
			// this branch is here to support pointers constructed with typecasts from ints or the '&' operator
 | 
			
		||||
			w = &v.Children[0]
 | 
			
		||||
		} else {
 | 
			
		||||
			w = v.maybeDereference()
 | 
			
		||||
		}
 | 
			
		||||
		return escapeCheckPointer(w.Addr, name, g)
 | 
			
		||||
	case reflect.Chan, reflect.String, reflect.Slice:
 | 
			
		||||
		return escapeCheckPointer(v.Base, name, g)
 | 
			
		||||
	case reflect.Map:
 | 
			
		||||
		sv := v.clone()
 | 
			
		||||
		sv.RealType = resolveTypedef(&(v.RealType.(*godwarf.MapType).TypedefType))
 | 
			
		||||
		sv = sv.maybeDereference()
 | 
			
		||||
		return escapeCheckPointer(sv.Addr, name, g)
 | 
			
		||||
	case reflect.Struct:
 | 
			
		||||
		t := v.RealType.(*godwarf.StructType)
 | 
			
		||||
		for _, field := range t.Field {
 | 
			
		||||
			fv, _ := v.toField(field)
 | 
			
		||||
			if err := escapeCheck(fv, fmt.Sprintf("%s.%s", name, field.Name), g); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Array:
 | 
			
		||||
		for i := int64(0); i < v.Len; i++ {
 | 
			
		||||
			sv, _ := v.sliceAccess(int(i))
 | 
			
		||||
			if err := escapeCheck(sv, fmt.Sprintf("%s[%d]", name, i), g); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case reflect.Func:
 | 
			
		||||
		if err := escapeCheckPointer(uintptr(v.funcvalAddr()), name, g); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func escapeCheckPointer(addr uintptr, name string, g *G) error {
 | 
			
		||||
	if uint64(addr) >= g.stacklo && uint64(addr) < g.stackhi {
 | 
			
		||||
		return fmt.Errorf("stack object passed to escaping pointer: %s", name)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	debugCallAXPrecheckFailed   = 8
 | 
			
		||||
	debugCallAXCompleteCall     = 0
 | 
			
		||||
	debugCallAXReadReturn       = 1
 | 
			
		||||
	debugCallAXReadPanic        = 2
 | 
			
		||||
	debugCallAXRestoreRegisters = 16
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (fncall *functionCallState) step(p Process) {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
 | 
			
		||||
	thread := p.CurrentThread()
 | 
			
		||||
	regs, err := thread.Registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fncall.err = err
 | 
			
		||||
		fncall.finished = true
 | 
			
		||||
		fncall.inProgress = false
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	regs = regs.Copy()
 | 
			
		||||
 | 
			
		||||
	rax, _ := regs.Get(int(x86asm.RAX))
 | 
			
		||||
 | 
			
		||||
	if logflags.FnCall() {
 | 
			
		||||
		loc, _ := thread.Location()
 | 
			
		||||
		var pc uint64
 | 
			
		||||
		var fnname string
 | 
			
		||||
		if loc != nil {
 | 
			
		||||
			pc = loc.PC
 | 
			
		||||
			if loc.Fn != nil {
 | 
			
		||||
				fnname = loc.Fn.Name
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		fncallLog("function call interrupt rax=%#x (PC=%#x in %s)\n", rax, pc, fnname)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch rax {
 | 
			
		||||
	case debugCallAXPrecheckFailed:
 | 
			
		||||
		// get error from top of the stack and return it to user
 | 
			
		||||
		errvar, err := readTopstackVariable(thread, regs, "string", loadFullValue)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not get precheck error reason: %v", err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		errvar.Name = "err"
 | 
			
		||||
		fncall.err = fmt.Errorf("%v", constant.StringVal(errvar.Value))
 | 
			
		||||
 | 
			
		||||
	case debugCallAXCompleteCall:
 | 
			
		||||
		// write arguments to the stack, call final function
 | 
			
		||||
		n, err := thread.WriteMemory(uintptr(regs.SP()), fncall.argmem)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not write arguments: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if n != len(fncall.argmem) {
 | 
			
		||||
			fncall.err = fmt.Errorf("short argument write: %d %d", n, len(fncall.argmem))
 | 
			
		||||
		}
 | 
			
		||||
		if fncall.closureAddr != 0 {
 | 
			
		||||
			// When calling a function pointer we must set the DX register to the
 | 
			
		||||
			// address of the function pointer itself.
 | 
			
		||||
			thread.SetDX(fncall.closureAddr)
 | 
			
		||||
		}
 | 
			
		||||
		callOP(bi, thread, regs, fncall.fn.Entry)
 | 
			
		||||
 | 
			
		||||
	case debugCallAXRestoreRegisters:
 | 
			
		||||
		// runtime requests that we restore the registers (all except pc and sp),
 | 
			
		||||
		// this is also the last step of the function call protocol.
 | 
			
		||||
		fncall.finished = true
 | 
			
		||||
		pc, sp := regs.PC(), regs.SP()
 | 
			
		||||
		if err := thread.RestoreRegisters(fncall.savedRegs); err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not restore registers: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := thread.SetPC(pc); err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not restore PC: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := thread.SetSP(sp); err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not restore SP: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if err := stepInstructionOut(p, thread, debugCallFunctionName, debugCallFunctionName); err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not step out of %s: %v", debugCallFunctionName, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	case debugCallAXReadReturn:
 | 
			
		||||
		// read return arguments from stack
 | 
			
		||||
		if fncall.retLoadCfg == nil || fncall.panicvar != nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		scope, err := ThreadScope(thread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not get return values: %v", err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// pretend we are still inside the function we called
 | 
			
		||||
		fakeFunctionEntryScope(scope, fncall.fn, int64(regs.SP()), regs.SP()-uint64(bi.Arch.PtrSize()))
 | 
			
		||||
 | 
			
		||||
		fncall.retvars, err = scope.Locals()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not get return values: %v", err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		fncall.retvars = filterVariables(fncall.retvars, func(v *Variable) bool {
 | 
			
		||||
			return (v.Flags & VariableReturnArgument) != 0
 | 
			
		||||
		})
 | 
			
		||||
 | 
			
		||||
		loadValues(fncall.retvars, *fncall.retLoadCfg)
 | 
			
		||||
 | 
			
		||||
	case debugCallAXReadPanic:
 | 
			
		||||
		// read panic value from stack
 | 
			
		||||
		if fncall.retLoadCfg == nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		fncall.panicvar, err = readTopstackVariable(thread, regs, "interface {}", *fncall.retLoadCfg)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not get panic: %v", err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		fncall.panicvar.Name = "~panic"
 | 
			
		||||
		fncall.panicvar.loadValue(*fncall.retLoadCfg)
 | 
			
		||||
		if fncall.panicvar.Unreadable != nil {
 | 
			
		||||
			fncall.err = fmt.Errorf("could not get panic: %v", fncall.panicvar.Unreadable)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	default:
 | 
			
		||||
		// Got an unknown AX value, this is probably bad but the safest thing
 | 
			
		||||
		// possible is to ignore it and hope it didn't matter.
 | 
			
		||||
		fncallLog("unknown value of AX %#x", rax)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readTopstackVariable(thread Thread, regs Registers, typename string, loadCfg LoadConfig) (*Variable, error) {
 | 
			
		||||
	bi := thread.BinInfo()
 | 
			
		||||
	scope, err := ThreadScope(thread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	typ, err := bi.findType(typename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	v := scope.newVariable("", uintptr(regs.SP()), typ, scope.Mem)
 | 
			
		||||
	v.loadValue(loadCfg)
 | 
			
		||||
	if v.Unreadable != nil {
 | 
			
		||||
		return nil, v.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
	return v, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fakeEntryScope alters scope to pretend that we are at the entry point of
 | 
			
		||||
// fn and CFA and SP are the ones passed as argument.
 | 
			
		||||
// This function is used to create a scope for a call frame that doesn't
 | 
			
		||||
// exist anymore, to read the return variables of an injected function call,
 | 
			
		||||
// or after a stepout command.
 | 
			
		||||
func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64) error {
 | 
			
		||||
	scope.PC = fn.Entry
 | 
			
		||||
	scope.Fn = fn
 | 
			
		||||
	scope.File, scope.Line, _ = scope.BinInfo.PCToLine(fn.Entry)
 | 
			
		||||
 | 
			
		||||
	scope.Regs.CFA = cfa
 | 
			
		||||
	scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = sp
 | 
			
		||||
 | 
			
		||||
	scope.BinInfo.dwarfReader.Seek(fn.offset)
 | 
			
		||||
	e, err := scope.BinInfo.dwarfReader.Next()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	scope.Regs.FrameBase, _, _, _ = scope.BinInfo.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (fncall *functionCallState) returnValues() []*Variable {
 | 
			
		||||
	if fncall.panicvar != nil {
 | 
			
		||||
		return []*Variable{fncall.panicvar}
 | 
			
		||||
	}
 | 
			
		||||
	return fncall.retvars
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										1884
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1884
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1234
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver_conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1234
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver_conn.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										16
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver_unix.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
// +build linux darwin
 | 
			
		||||
 | 
			
		||||
package gdbserial
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"syscall"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func sysProcAttr(foreground bool) *syscall.SysProcAttr {
 | 
			
		||||
	return &syscall.SysProcAttr{Setpgid: true, Pgid: 0, Foreground: foreground}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func foregroundSignalsIgnore() {
 | 
			
		||||
	signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/gdbserver_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
			
		||||
package gdbserial
 | 
			
		||||
 | 
			
		||||
import "syscall"
 | 
			
		||||
 | 
			
		||||
func sysProcAttr(foreground bool) *syscall.SysProcAttr {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func foregroundSignalsIgnore() {
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										267
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/rr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								vendor/github.com/go-delve/delve/pkg/proc/gdbserial/rr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,267 @@
 | 
			
		||||
package gdbserial
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bufio"
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Record uses rr to record the execution of the specified program and
 | 
			
		||||
// returns the trace directory's path.
 | 
			
		||||
func Record(cmd []string, wd string, quiet bool) (tracedir string, err error) {
 | 
			
		||||
	if err := checkRRAvailabe(); err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rfd, wfd, err := os.Pipe()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	args := make([]string, 0, len(cmd)+2)
 | 
			
		||||
	args = append(args, "record", "--print-trace-dir=3")
 | 
			
		||||
	args = append(args, cmd...)
 | 
			
		||||
	rrcmd := exec.Command("rr", args...)
 | 
			
		||||
	rrcmd.Stdin = os.Stdin
 | 
			
		||||
	if !quiet {
 | 
			
		||||
		rrcmd.Stdout = os.Stdout
 | 
			
		||||
		rrcmd.Stderr = os.Stderr
 | 
			
		||||
	}
 | 
			
		||||
	rrcmd.ExtraFiles = []*os.File{wfd}
 | 
			
		||||
	rrcmd.Dir = wd
 | 
			
		||||
 | 
			
		||||
	done := make(chan struct{})
 | 
			
		||||
	go func() {
 | 
			
		||||
		bs, _ := ioutil.ReadAll(rfd)
 | 
			
		||||
		tracedir = strings.TrimSpace(string(bs))
 | 
			
		||||
		close(done)
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	err = rrcmd.Run()
 | 
			
		||||
	// ignore run errors, it could be the program crashing
 | 
			
		||||
	wfd.Close()
 | 
			
		||||
	<-done
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Replay starts an instance of rr in replay mode, with the specified trace
 | 
			
		||||
// directory, and connects to it.
 | 
			
		||||
func Replay(tracedir string, quiet bool, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	if err := checkRRAvailabe(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rrcmd := exec.Command("rr", "replay", "--dbgport=0", tracedir)
 | 
			
		||||
	rrcmd.Stdout = os.Stdout
 | 
			
		||||
	stderr, err := rrcmd.StderrPipe()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	rrcmd.SysProcAttr = sysProcAttr(false)
 | 
			
		||||
 | 
			
		||||
	initch := make(chan rrInit)
 | 
			
		||||
	go rrStderrParser(stderr, initch, quiet)
 | 
			
		||||
 | 
			
		||||
	err = rrcmd.Start()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	init := <-initch
 | 
			
		||||
	if init.err != nil {
 | 
			
		||||
		rrcmd.Process.Kill()
 | 
			
		||||
		return nil, init.err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := New(rrcmd.Process)
 | 
			
		||||
	p.tracedir = tracedir
 | 
			
		||||
	err = p.Dial(init.port, init.exe, 0, debugInfoDirs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rrcmd.Process.Kill()
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrPerfEventParanoid is the error returned by Reply and Record if
 | 
			
		||||
// /proc/sys/kernel/perf_event_paranoid is greater than 1.
 | 
			
		||||
type ErrPerfEventParanoid struct {
 | 
			
		||||
	actual int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err ErrPerfEventParanoid) Error() string {
 | 
			
		||||
	return fmt.Sprintf("rr needs /proc/sys/kernel/perf_event_paranoid <= 1, but it is %d", err.actual)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func checkRRAvailabe() error {
 | 
			
		||||
	if _, err := exec.LookPath("rr"); err != nil {
 | 
			
		||||
		return &ErrBackendUnavailable{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check that /proc/sys/kernel/perf_event_paranoid doesn't exist or is <= 1.
 | 
			
		||||
	buf, err := ioutil.ReadFile("/proc/sys/kernel/perf_event_paranoid")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		perfEventParanoid, _ := strconv.Atoi(strings.TrimSpace(string(buf)))
 | 
			
		||||
		if perfEventParanoid > 1 {
 | 
			
		||||
			return ErrPerfEventParanoid{perfEventParanoid}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type rrInit struct {
 | 
			
		||||
	port string
 | 
			
		||||
	exe  string
 | 
			
		||||
	err  error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	rrGdbCommandPrefix = "  gdb "
 | 
			
		||||
	rrGdbLaunchPrefix  = "Launch gdb with"
 | 
			
		||||
	targetCmd          = "target extended-remote "
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func rrStderrParser(stderr io.Reader, initch chan<- rrInit, quiet bool) {
 | 
			
		||||
	rd := bufio.NewReader(stderr)
 | 
			
		||||
	for {
 | 
			
		||||
		line, err := rd.ReadString('\n')
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			initch <- rrInit{"", "", err}
 | 
			
		||||
			close(initch)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(line, rrGdbCommandPrefix) {
 | 
			
		||||
			initch <- rrParseGdbCommand(line[len(rrGdbCommandPrefix):])
 | 
			
		||||
			close(initch)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if strings.HasPrefix(line, rrGdbLaunchPrefix) {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !quiet {
 | 
			
		||||
			os.Stderr.Write([]byte(line))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	io.Copy(os.Stderr, rd)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type ErrMalformedRRGdbCommand struct {
 | 
			
		||||
	line, reason string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err *ErrMalformedRRGdbCommand) Error() string {
 | 
			
		||||
	return fmt.Sprintf("malformed gdb command %q: %s", err.line, err.reason)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func rrParseGdbCommand(line string) rrInit {
 | 
			
		||||
	port := ""
 | 
			
		||||
	fields := splitQuotedFields(line)
 | 
			
		||||
	for i := 0; i < len(fields); i++ {
 | 
			
		||||
		switch fields[i] {
 | 
			
		||||
		case "-ex":
 | 
			
		||||
			if i+1 >= len(fields) {
 | 
			
		||||
				return rrInit{err: &ErrMalformedRRGdbCommand{line, "-ex not followed by an argument"}}
 | 
			
		||||
			}
 | 
			
		||||
			arg := fields[i+1]
 | 
			
		||||
 | 
			
		||||
			if !strings.HasPrefix(arg, targetCmd) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			port = arg[len(targetCmd):]
 | 
			
		||||
			i++
 | 
			
		||||
 | 
			
		||||
		case "-l":
 | 
			
		||||
			// skip argument
 | 
			
		||||
			i++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if port == "" {
 | 
			
		||||
		return rrInit{err: &ErrMalformedRRGdbCommand{line, "could not find -ex argument"}}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	exe := fields[len(fields)-1]
 | 
			
		||||
 | 
			
		||||
	return rrInit{port: port, exe: exe}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Like strings.Fields but ignores spaces inside areas surrounded
 | 
			
		||||
// by single quotes.
 | 
			
		||||
// To specify a single quote use backslash to escape it: '\''
 | 
			
		||||
func splitQuotedFields(in string) []string {
 | 
			
		||||
	type stateEnum int
 | 
			
		||||
	const (
 | 
			
		||||
		inSpace stateEnum = iota
 | 
			
		||||
		inField
 | 
			
		||||
		inQuote
 | 
			
		||||
		inQuoteEscaped
 | 
			
		||||
	)
 | 
			
		||||
	state := inSpace
 | 
			
		||||
	r := []string{}
 | 
			
		||||
	var buf bytes.Buffer
 | 
			
		||||
 | 
			
		||||
	for _, ch := range in {
 | 
			
		||||
		switch state {
 | 
			
		||||
		case inSpace:
 | 
			
		||||
			if ch == '\'' {
 | 
			
		||||
				state = inQuote
 | 
			
		||||
			} else if !unicode.IsSpace(ch) {
 | 
			
		||||
				buf.WriteRune(ch)
 | 
			
		||||
				state = inField
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case inField:
 | 
			
		||||
			if ch == '\'' {
 | 
			
		||||
				state = inQuote
 | 
			
		||||
			} else if unicode.IsSpace(ch) {
 | 
			
		||||
				r = append(r, buf.String())
 | 
			
		||||
				buf.Reset()
 | 
			
		||||
			} else {
 | 
			
		||||
				buf.WriteRune(ch)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case inQuote:
 | 
			
		||||
			if ch == '\'' {
 | 
			
		||||
				state = inField
 | 
			
		||||
			} else if ch == '\\' {
 | 
			
		||||
				state = inQuoteEscaped
 | 
			
		||||
			} else {
 | 
			
		||||
				buf.WriteRune(ch)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case inQuoteEscaped:
 | 
			
		||||
			buf.WriteRune(ch)
 | 
			
		||||
			state = inQuote
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if buf.Len() != 0 {
 | 
			
		||||
		r = append(r, buf.String())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RecordAndReplay acts like calling Record and then Replay.
 | 
			
		||||
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (p *Process, tracedir string, err error) {
 | 
			
		||||
	tracedir, err = Record(cmd, wd, quiet)
 | 
			
		||||
	if tracedir == "" {
 | 
			
		||||
		return nil, "", err
 | 
			
		||||
	}
 | 
			
		||||
	p, err = Replay(tracedir, quiet, debugInfoDirs)
 | 
			
		||||
	return p, tracedir, err
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										131
									
								
								vendor/github.com/go-delve/delve/pkg/proc/interface.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								vendor/github.com/go-delve/delve/pkg/proc/interface.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"go/ast"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Process represents the target of the debugger. This
 | 
			
		||||
// target could be a system process, core file, etc.
 | 
			
		||||
//
 | 
			
		||||
// Implementations of Process are not required to be thread safe and users
 | 
			
		||||
// of Process should not assume they are.
 | 
			
		||||
// There is one exception to this rule: it is safe to call RequestManualStop
 | 
			
		||||
// concurrently with ContinueOnce.
 | 
			
		||||
type Process interface {
 | 
			
		||||
	Info
 | 
			
		||||
	ProcessManipulation
 | 
			
		||||
	BreakpointManipulation
 | 
			
		||||
	RecordingManipulation
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RecordingManipulation is an interface for manipulating process recordings.
 | 
			
		||||
type RecordingManipulation interface {
 | 
			
		||||
	// Recorded returns true if the current process is a recording and the path
 | 
			
		||||
	// to the trace directory.
 | 
			
		||||
	Recorded() (recorded bool, tracedir string)
 | 
			
		||||
	// Restart restarts the recording from the specified position, or from the
 | 
			
		||||
	// last checkpoint if pos == "".
 | 
			
		||||
	// If pos starts with 'c' it's a checkpoint ID, otherwise it's an event
 | 
			
		||||
	// number.
 | 
			
		||||
	Restart(pos string) error
 | 
			
		||||
	// Direction changes execution direction.
 | 
			
		||||
	Direction(Direction) error
 | 
			
		||||
	// When returns current recording position.
 | 
			
		||||
	When() (string, error)
 | 
			
		||||
	// Checkpoint sets a checkpoint at the current position.
 | 
			
		||||
	Checkpoint(where string) (id int, err error)
 | 
			
		||||
	// Checkpoints returns the list of currently set checkpoint.
 | 
			
		||||
	Checkpoints() ([]Checkpoint, error)
 | 
			
		||||
	// ClearCheckpoint removes a checkpoint.
 | 
			
		||||
	ClearCheckpoint(id int) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Direction is the direction of execution for the target process.
 | 
			
		||||
type Direction int8
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	// Forward direction executes the target normally.
 | 
			
		||||
	Forward Direction = 0
 | 
			
		||||
	// Backward direction executes the target in reverse.
 | 
			
		||||
	Backward Direction = 1
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Checkpoint is a checkpoint
 | 
			
		||||
type Checkpoint struct {
 | 
			
		||||
	ID    int
 | 
			
		||||
	When  string
 | 
			
		||||
	Where string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Info is an interface that provides general information on the target.
 | 
			
		||||
type Info interface {
 | 
			
		||||
	Pid() int
 | 
			
		||||
	// ResumeNotify specifies a channel that will be closed the next time
 | 
			
		||||
	// ContinueOnce finishes resuming the target.
 | 
			
		||||
	ResumeNotify(chan<- struct{})
 | 
			
		||||
	// Valid returns true if this Process can be used. When it returns false it
 | 
			
		||||
	// also returns an error describing why the Process is invalid (either
 | 
			
		||||
	// ErrProcessExited or ProcessDetachedError).
 | 
			
		||||
	Valid() (bool, error)
 | 
			
		||||
	BinInfo() *BinaryInfo
 | 
			
		||||
	EntryPoint() (uint64, error)
 | 
			
		||||
	// Common returns a struct with fields common to all backends
 | 
			
		||||
	Common() *CommonProcess
 | 
			
		||||
 | 
			
		||||
	ThreadInfo
 | 
			
		||||
	GoroutineInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ThreadInfo is an interface for getting information on active threads
 | 
			
		||||
// in the process.
 | 
			
		||||
type ThreadInfo interface {
 | 
			
		||||
	FindThread(threadID int) (Thread, bool)
 | 
			
		||||
	ThreadList() []Thread
 | 
			
		||||
	CurrentThread() Thread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GoroutineInfo is an interface for getting information on running goroutines.
 | 
			
		||||
type GoroutineInfo interface {
 | 
			
		||||
	SelectedGoroutine() *G
 | 
			
		||||
	SetSelectedGoroutine(*G)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessManipulation is an interface for changing the execution state of a process.
 | 
			
		||||
type ProcessManipulation interface {
 | 
			
		||||
	ContinueOnce() (trapthread Thread, err error)
 | 
			
		||||
	StepInstruction() error
 | 
			
		||||
	SwitchThread(int) error
 | 
			
		||||
	SwitchGoroutine(int) error
 | 
			
		||||
	RequestManualStop() error
 | 
			
		||||
	// CheckAndClearManualStopRequest returns true the first time it's called
 | 
			
		||||
	// after a call to RequestManualStop.
 | 
			
		||||
	CheckAndClearManualStopRequest() bool
 | 
			
		||||
	Detach(bool) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BreakpointManipulation is an interface for managing breakpoints.
 | 
			
		||||
type BreakpointManipulation interface {
 | 
			
		||||
	Breakpoints() *BreakpointMap
 | 
			
		||||
	SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error)
 | 
			
		||||
	ClearBreakpoint(addr uint64) (*Breakpoint, error)
 | 
			
		||||
	ClearInternalBreakpoints() error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CommonProcess contains fields used by this package, common to all
 | 
			
		||||
// implementations of the Process interface.
 | 
			
		||||
type CommonProcess struct {
 | 
			
		||||
	allGCache     []*G
 | 
			
		||||
	fncallState   functionCallState
 | 
			
		||||
	fncallEnabled bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewCommonProcess returns a struct with fields common across
 | 
			
		||||
// all process implementations.
 | 
			
		||||
func NewCommonProcess(fncallEnabled bool) CommonProcess {
 | 
			
		||||
	return CommonProcess{fncallEnabled: fncallEnabled}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearAllGCache clears the cached contents of the cache for runtime.allgs.
 | 
			
		||||
func (p *CommonProcess) ClearAllGCache() {
 | 
			
		||||
	p.allGCache = nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/auxv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/auxv.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
package linutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_AT_NULL_AMD64  = 0
 | 
			
		||||
	_AT_ENTRY_AMD64 = 9
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// EntryPointFromAuxv searches the elf auxiliary vector for the entry point
 | 
			
		||||
// address.
 | 
			
		||||
// For a description of the auxiliary vector (auxv) format see:
 | 
			
		||||
// System V Application Binary Interface, AMD64 Architecture Processor
 | 
			
		||||
// Supplement, section 3.4.3
 | 
			
		||||
func EntryPointFromAuxvAMD64(auxv []byte) uint64 {
 | 
			
		||||
	rd := bytes.NewBuffer(auxv)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		var tag, val uint64
 | 
			
		||||
		err := binary.Read(rd, binary.LittleEndian, &tag)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0
 | 
			
		||||
		}
 | 
			
		||||
		err = binary.Read(rd, binary.LittleEndian, &val)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch tag {
 | 
			
		||||
		case _AT_NULL_AMD64:
 | 
			
		||||
			return 0
 | 
			
		||||
		case _AT_ENTRY_AMD64:
 | 
			
		||||
			return val
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										4
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
// This package contains functions and data structures used by both the
 | 
			
		||||
// linux implementation of the native backend and the core backend to deal
 | 
			
		||||
// with structures used by the linux kernel.
 | 
			
		||||
package linutil
 | 
			
		||||
							
								
								
									
										172
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/dynamic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/dynamic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
package linutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	maxNumLibraries      = 1000000 // maximum number of loaded libraries, to avoid loading forever on corrupted memory
 | 
			
		||||
	maxLibraryPathLength = 1000000 // maximum length for the path of a library, to avoid loading forever on corrupted memory
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ErrTooManyLibraries = errors.New("number of loaded libraries exceeds maximum")
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_DT_NULL  = 0  // DT_NULL as defined by SysV ABI specification
 | 
			
		||||
	_DT_DEBUG = 21 // DT_DEBUG as defined by SysV ABI specification
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// dynamicSearchDebug searches for the DT_DEBUG entry in the .dynamic section
 | 
			
		||||
func dynamicSearchDebug(p proc.Process) (uint64, error) {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	mem := p.CurrentThread()
 | 
			
		||||
 | 
			
		||||
	dynbuf := make([]byte, bi.ElfDynamicSection.Size)
 | 
			
		||||
	_, err := mem.ReadMemory(dynbuf, uintptr(bi.ElfDynamicSection.Addr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rd := bytes.NewReader(dynbuf)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		var tag, val uint64
 | 
			
		||||
		if err := binary.Read(rd, binary.LittleEndian, &tag); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		if err := binary.Read(rd, binary.LittleEndian, &val); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		switch tag {
 | 
			
		||||
		case _DT_NULL:
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		case _DT_DEBUG:
 | 
			
		||||
			return val, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hard-coded offsets of the fields of the r_debug and link_map structs, see
 | 
			
		||||
// /usr/include/elf/link.h for a full description of those structs.
 | 
			
		||||
const (
 | 
			
		||||
	_R_DEBUG_MAP_OFFSET   = 8
 | 
			
		||||
	_LINK_MAP_ADDR_OFFSET = 0  // offset of link_map.l_addr field (base address shared object is loaded at)
 | 
			
		||||
	_LINK_MAP_NAME_OFFSET = 8  // offset of link_map.l_name field (absolute file name object was found in)
 | 
			
		||||
	_LINK_MAP_LD          = 16 // offset of link_map.l_ld field (dynamic section of the shared object)
 | 
			
		||||
	_LINK_MAP_NEXT        = 24 // offset of link_map.l_next field
 | 
			
		||||
	_LINK_MAP_PREV        = 32 // offset of link_map.l_prev field
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func readPtr(p proc.Process, addr uint64) (uint64, error) {
 | 
			
		||||
	ptrbuf := make([]byte, p.BinInfo().Arch.PtrSize())
 | 
			
		||||
	_, err := p.CurrentThread().ReadMemory(ptrbuf, uintptr(addr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return binary.LittleEndian.Uint64(ptrbuf), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type linkMap struct {
 | 
			
		||||
	addr       uint64
 | 
			
		||||
	name       string
 | 
			
		||||
	ld         uint64
 | 
			
		||||
	next, prev uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readLinkMapNode(p proc.Process, r_map uint64) (*linkMap, error) {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
 | 
			
		||||
	var lm linkMap
 | 
			
		||||
	var ptrs [5]uint64
 | 
			
		||||
	for i := range ptrs {
 | 
			
		||||
		var err error
 | 
			
		||||
		ptrs[i], err = readPtr(p, r_map+uint64(bi.Arch.PtrSize()*i))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	lm.addr = ptrs[0]
 | 
			
		||||
	var err error
 | 
			
		||||
	lm.name, err = readCString(p, ptrs[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	lm.ld = ptrs[2]
 | 
			
		||||
	lm.next = ptrs[3]
 | 
			
		||||
	lm.prev = ptrs[4]
 | 
			
		||||
	return &lm, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readCString(p proc.Process, addr uint64) (string, error) {
 | 
			
		||||
	if addr == 0 {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	mem := p.CurrentThread()
 | 
			
		||||
	buf := make([]byte, 1)
 | 
			
		||||
	r := []byte{}
 | 
			
		||||
	for {
 | 
			
		||||
		if len(r) > maxLibraryPathLength {
 | 
			
		||||
			return "", fmt.Errorf("error reading libraries: string too long (%d)", len(r))
 | 
			
		||||
		}
 | 
			
		||||
		_, err := mem.ReadMemory(buf, uintptr(addr))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		if buf[0] == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		r = append(r, buf[0])
 | 
			
		||||
		addr++
 | 
			
		||||
	}
 | 
			
		||||
	return string(r), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ElfUpdateSharedObjects reads the list of dynamic libraries loaded by the
 | 
			
		||||
// dynamic linker from the .dynamic section and uses it to update p.BinInfo().
 | 
			
		||||
// See the SysV ABI for a description of how the .dynamic section works:
 | 
			
		||||
// http://www.sco.com/developers/gabi/latest/contents.html
 | 
			
		||||
func ElfUpdateSharedObjects(p proc.Process) error {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	if bi.ElfDynamicSection.Addr == 0 {
 | 
			
		||||
		// no dynamic section, therefore nothing to do here
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	debugAddr, err := dynamicSearchDebug(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if debugAddr == 0 {
 | 
			
		||||
		// no DT_DEBUG entry
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r_map, err := readPtr(p, debugAddr+_R_DEBUG_MAP_OFFSET)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libs := []string{}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if r_map == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if len(libs) > maxNumLibraries {
 | 
			
		||||
			return ErrTooManyLibraries
 | 
			
		||||
		}
 | 
			
		||||
		lm, err := readLinkMapNode(p, r_map)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		bi.AddImage(lm.name, lm.addr)
 | 
			
		||||
		libs = append(libs, lm.name)
 | 
			
		||||
		r_map = lm.next
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										399
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/regs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/regs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,399 @@
 | 
			
		||||
package linutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AMD64Registers implements the proc.Registers interface for the native/linux
 | 
			
		||||
// backend and core/linux backends, on AMD64.
 | 
			
		||||
type AMD64Registers struct {
 | 
			
		||||
	Regs     *AMD64PtraceRegs
 | 
			
		||||
	Fpregs   []proc.Register
 | 
			
		||||
	Fpregset *AMD64Xstate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64PtraceRegs is the struct used by the linux kernel to return the
 | 
			
		||||
// general purpose registers for AMD64 CPUs.
 | 
			
		||||
type AMD64PtraceRegs struct {
 | 
			
		||||
	R15      uint64
 | 
			
		||||
	R14      uint64
 | 
			
		||||
	R13      uint64
 | 
			
		||||
	R12      uint64
 | 
			
		||||
	Rbp      uint64
 | 
			
		||||
	Rbx      uint64
 | 
			
		||||
	R11      uint64
 | 
			
		||||
	R10      uint64
 | 
			
		||||
	R9       uint64
 | 
			
		||||
	R8       uint64
 | 
			
		||||
	Rax      uint64
 | 
			
		||||
	Rcx      uint64
 | 
			
		||||
	Rdx      uint64
 | 
			
		||||
	Rsi      uint64
 | 
			
		||||
	Rdi      uint64
 | 
			
		||||
	Orig_rax uint64
 | 
			
		||||
	Rip      uint64
 | 
			
		||||
	Cs       uint64
 | 
			
		||||
	Eflags   uint64
 | 
			
		||||
	Rsp      uint64
 | 
			
		||||
	Ss       uint64
 | 
			
		||||
	Fs_base  uint64
 | 
			
		||||
	Gs_base  uint64
 | 
			
		||||
	Ds       uint64
 | 
			
		||||
	Es       uint64
 | 
			
		||||
	Fs       uint64
 | 
			
		||||
	Gs       uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Slice returns the registers as a list of (name, value) pairs.
 | 
			
		||||
func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register {
 | 
			
		||||
	var regs = []struct {
 | 
			
		||||
		k string
 | 
			
		||||
		v uint64
 | 
			
		||||
	}{
 | 
			
		||||
		{"Rip", r.Regs.Rip},
 | 
			
		||||
		{"Rsp", r.Regs.Rsp},
 | 
			
		||||
		{"Rax", r.Regs.Rax},
 | 
			
		||||
		{"Rbx", r.Regs.Rbx},
 | 
			
		||||
		{"Rcx", r.Regs.Rcx},
 | 
			
		||||
		{"Rdx", r.Regs.Rdx},
 | 
			
		||||
		{"Rdi", r.Regs.Rdi},
 | 
			
		||||
		{"Rsi", r.Regs.Rsi},
 | 
			
		||||
		{"Rbp", r.Regs.Rbp},
 | 
			
		||||
		{"R8", r.Regs.R8},
 | 
			
		||||
		{"R9", r.Regs.R9},
 | 
			
		||||
		{"R10", r.Regs.R10},
 | 
			
		||||
		{"R11", r.Regs.R11},
 | 
			
		||||
		{"R12", r.Regs.R12},
 | 
			
		||||
		{"R13", r.Regs.R13},
 | 
			
		||||
		{"R14", r.Regs.R14},
 | 
			
		||||
		{"R15", r.Regs.R15},
 | 
			
		||||
		{"Orig_rax", r.Regs.Orig_rax},
 | 
			
		||||
		{"Cs", r.Regs.Cs},
 | 
			
		||||
		{"Eflags", r.Regs.Eflags},
 | 
			
		||||
		{"Ss", r.Regs.Ss},
 | 
			
		||||
		{"Fs_base", r.Regs.Fs_base},
 | 
			
		||||
		{"Gs_base", r.Regs.Gs_base},
 | 
			
		||||
		{"Ds", r.Regs.Ds},
 | 
			
		||||
		{"Es", r.Regs.Es},
 | 
			
		||||
		{"Fs", r.Regs.Fs},
 | 
			
		||||
		{"Gs", r.Regs.Gs},
 | 
			
		||||
	}
 | 
			
		||||
	out := make([]proc.Register, 0, len(regs)+len(r.Fpregs))
 | 
			
		||||
	for _, reg := range regs {
 | 
			
		||||
		if reg.k == "Eflags" {
 | 
			
		||||
			out = proc.AppendEflagReg(out, reg.k, reg.v)
 | 
			
		||||
		} else {
 | 
			
		||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		out = append(out, r.Fpregs...)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PC returns the value of RIP register.
 | 
			
		||||
func (r *AMD64Registers) PC() uint64 {
 | 
			
		||||
	return r.Regs.Rip
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SP returns the value of RSP register.
 | 
			
		||||
func (r *AMD64Registers) SP() uint64 {
 | 
			
		||||
	return r.Regs.Rsp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *AMD64Registers) BP() uint64 {
 | 
			
		||||
	return r.Regs.Rbp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CX returns the value of RCX register.
 | 
			
		||||
func (r *AMD64Registers) CX() uint64 {
 | 
			
		||||
	return r.Regs.Rcx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TLS returns the address of the thread local storage memory segment.
 | 
			
		||||
func (r *AMD64Registers) TLS() uint64 {
 | 
			
		||||
	return r.Regs.Fs_base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GAddr returns the address of the G variable if it is known, 0 and false
 | 
			
		||||
// otherwise.
 | 
			
		||||
func (r *AMD64Registers) GAddr() (uint64, bool) {
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns the value of the n-th register (in x86asm order).
 | 
			
		||||
func (r *AMD64Registers) Get(n int) (uint64, error) {
 | 
			
		||||
	reg := x86asm.Reg(n)
 | 
			
		||||
	const (
 | 
			
		||||
		mask8  = 0x000f
 | 
			
		||||
		mask16 = 0x00ff
 | 
			
		||||
		mask32 = 0xffff
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	switch reg {
 | 
			
		||||
	// 8-bit
 | 
			
		||||
	case x86asm.AL:
 | 
			
		||||
		return r.Regs.Rax & mask8, nil
 | 
			
		||||
	case x86asm.CL:
 | 
			
		||||
		return r.Regs.Rcx & mask8, nil
 | 
			
		||||
	case x86asm.DL:
 | 
			
		||||
		return r.Regs.Rdx & mask8, nil
 | 
			
		||||
	case x86asm.BL:
 | 
			
		||||
		return r.Regs.Rbx & mask8, nil
 | 
			
		||||
	case x86asm.AH:
 | 
			
		||||
		return (r.Regs.Rax >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.CH:
 | 
			
		||||
		return (r.Regs.Rcx >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.DH:
 | 
			
		||||
		return (r.Regs.Rdx >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.BH:
 | 
			
		||||
		return (r.Regs.Rbx >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.SPB:
 | 
			
		||||
		return r.Regs.Rsp & mask8, nil
 | 
			
		||||
	case x86asm.BPB:
 | 
			
		||||
		return r.Regs.Rbp & mask8, nil
 | 
			
		||||
	case x86asm.SIB:
 | 
			
		||||
		return r.Regs.Rsi & mask8, nil
 | 
			
		||||
	case x86asm.DIB:
 | 
			
		||||
		return r.Regs.Rdi & mask8, nil
 | 
			
		||||
	case x86asm.R8B:
 | 
			
		||||
		return r.Regs.R8 & mask8, nil
 | 
			
		||||
	case x86asm.R9B:
 | 
			
		||||
		return r.Regs.R9 & mask8, nil
 | 
			
		||||
	case x86asm.R10B:
 | 
			
		||||
		return r.Regs.R10 & mask8, nil
 | 
			
		||||
	case x86asm.R11B:
 | 
			
		||||
		return r.Regs.R11 & mask8, nil
 | 
			
		||||
	case x86asm.R12B:
 | 
			
		||||
		return r.Regs.R12 & mask8, nil
 | 
			
		||||
	case x86asm.R13B:
 | 
			
		||||
		return r.Regs.R13 & mask8, nil
 | 
			
		||||
	case x86asm.R14B:
 | 
			
		||||
		return r.Regs.R14 & mask8, nil
 | 
			
		||||
	case x86asm.R15B:
 | 
			
		||||
		return r.Regs.R15 & mask8, nil
 | 
			
		||||
 | 
			
		||||
	// 16-bit
 | 
			
		||||
	case x86asm.AX:
 | 
			
		||||
		return r.Regs.Rax & mask16, nil
 | 
			
		||||
	case x86asm.CX:
 | 
			
		||||
		return r.Regs.Rcx & mask16, nil
 | 
			
		||||
	case x86asm.DX:
 | 
			
		||||
		return r.Regs.Rdx & mask16, nil
 | 
			
		||||
	case x86asm.BX:
 | 
			
		||||
		return r.Regs.Rbx & mask16, nil
 | 
			
		||||
	case x86asm.SP:
 | 
			
		||||
		return r.Regs.Rsp & mask16, nil
 | 
			
		||||
	case x86asm.BP:
 | 
			
		||||
		return r.Regs.Rbp & mask16, nil
 | 
			
		||||
	case x86asm.SI:
 | 
			
		||||
		return r.Regs.Rsi & mask16, nil
 | 
			
		||||
	case x86asm.DI:
 | 
			
		||||
		return r.Regs.Rdi & mask16, nil
 | 
			
		||||
	case x86asm.R8W:
 | 
			
		||||
		return r.Regs.R8 & mask16, nil
 | 
			
		||||
	case x86asm.R9W:
 | 
			
		||||
		return r.Regs.R9 & mask16, nil
 | 
			
		||||
	case x86asm.R10W:
 | 
			
		||||
		return r.Regs.R10 & mask16, nil
 | 
			
		||||
	case x86asm.R11W:
 | 
			
		||||
		return r.Regs.R11 & mask16, nil
 | 
			
		||||
	case x86asm.R12W:
 | 
			
		||||
		return r.Regs.R12 & mask16, nil
 | 
			
		||||
	case x86asm.R13W:
 | 
			
		||||
		return r.Regs.R13 & mask16, nil
 | 
			
		||||
	case x86asm.R14W:
 | 
			
		||||
		return r.Regs.R14 & mask16, nil
 | 
			
		||||
	case x86asm.R15W:
 | 
			
		||||
		return r.Regs.R15 & mask16, nil
 | 
			
		||||
 | 
			
		||||
	// 32-bit
 | 
			
		||||
	case x86asm.EAX:
 | 
			
		||||
		return r.Regs.Rax & mask32, nil
 | 
			
		||||
	case x86asm.ECX:
 | 
			
		||||
		return r.Regs.Rcx & mask32, nil
 | 
			
		||||
	case x86asm.EDX:
 | 
			
		||||
		return r.Regs.Rdx & mask32, nil
 | 
			
		||||
	case x86asm.EBX:
 | 
			
		||||
		return r.Regs.Rbx & mask32, nil
 | 
			
		||||
	case x86asm.ESP:
 | 
			
		||||
		return r.Regs.Rsp & mask32, nil
 | 
			
		||||
	case x86asm.EBP:
 | 
			
		||||
		return r.Regs.Rbp & mask32, nil
 | 
			
		||||
	case x86asm.ESI:
 | 
			
		||||
		return r.Regs.Rsi & mask32, nil
 | 
			
		||||
	case x86asm.EDI:
 | 
			
		||||
		return r.Regs.Rdi & mask32, nil
 | 
			
		||||
	case x86asm.R8L:
 | 
			
		||||
		return r.Regs.R8 & mask32, nil
 | 
			
		||||
	case x86asm.R9L:
 | 
			
		||||
		return r.Regs.R9 & mask32, nil
 | 
			
		||||
	case x86asm.R10L:
 | 
			
		||||
		return r.Regs.R10 & mask32, nil
 | 
			
		||||
	case x86asm.R11L:
 | 
			
		||||
		return r.Regs.R11 & mask32, nil
 | 
			
		||||
	case x86asm.R12L:
 | 
			
		||||
		return r.Regs.R12 & mask32, nil
 | 
			
		||||
	case x86asm.R13L:
 | 
			
		||||
		return r.Regs.R13 & mask32, nil
 | 
			
		||||
	case x86asm.R14L:
 | 
			
		||||
		return r.Regs.R14 & mask32, nil
 | 
			
		||||
	case x86asm.R15L:
 | 
			
		||||
		return r.Regs.R15 & mask32, nil
 | 
			
		||||
 | 
			
		||||
	// 64-bit
 | 
			
		||||
	case x86asm.RAX:
 | 
			
		||||
		return r.Regs.Rax, nil
 | 
			
		||||
	case x86asm.RCX:
 | 
			
		||||
		return r.Regs.Rcx, nil
 | 
			
		||||
	case x86asm.RDX:
 | 
			
		||||
		return r.Regs.Rdx, nil
 | 
			
		||||
	case x86asm.RBX:
 | 
			
		||||
		return r.Regs.Rbx, nil
 | 
			
		||||
	case x86asm.RSP:
 | 
			
		||||
		return r.Regs.Rsp, nil
 | 
			
		||||
	case x86asm.RBP:
 | 
			
		||||
		return r.Regs.Rbp, nil
 | 
			
		||||
	case x86asm.RSI:
 | 
			
		||||
		return r.Regs.Rsi, nil
 | 
			
		||||
	case x86asm.RDI:
 | 
			
		||||
		return r.Regs.Rdi, nil
 | 
			
		||||
	case x86asm.R8:
 | 
			
		||||
		return r.Regs.R8, nil
 | 
			
		||||
	case x86asm.R9:
 | 
			
		||||
		return r.Regs.R9, nil
 | 
			
		||||
	case x86asm.R10:
 | 
			
		||||
		return r.Regs.R10, nil
 | 
			
		||||
	case x86asm.R11:
 | 
			
		||||
		return r.Regs.R11, nil
 | 
			
		||||
	case x86asm.R12:
 | 
			
		||||
		return r.Regs.R12, nil
 | 
			
		||||
	case x86asm.R13:
 | 
			
		||||
		return r.Regs.R13, nil
 | 
			
		||||
	case x86asm.R14:
 | 
			
		||||
		return r.Regs.R14, nil
 | 
			
		||||
	case x86asm.R15:
 | 
			
		||||
		return r.Regs.R15, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0, proc.ErrUnknownRegister
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Copy returns a copy of these registers that is guarenteed not to change.
 | 
			
		||||
func (r *AMD64Registers) Copy() proc.Registers {
 | 
			
		||||
	var rr AMD64Registers
 | 
			
		||||
	rr.Regs = &AMD64PtraceRegs{}
 | 
			
		||||
	rr.Fpregset = &AMD64Xstate{}
 | 
			
		||||
	*(rr.Regs) = *(r.Regs)
 | 
			
		||||
	if r.Fpregset != nil {
 | 
			
		||||
		*(rr.Fpregset) = *(r.Fpregset)
 | 
			
		||||
	}
 | 
			
		||||
	if r.Fpregs != nil {
 | 
			
		||||
		rr.Fpregs = make([]proc.Register, len(r.Fpregs))
 | 
			
		||||
		copy(rr.Fpregs, r.Fpregs)
 | 
			
		||||
	}
 | 
			
		||||
	return &rr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
 | 
			
		||||
type AMD64PtraceFpRegs struct {
 | 
			
		||||
	Cwd      uint16
 | 
			
		||||
	Swd      uint16
 | 
			
		||||
	Ftw      uint16
 | 
			
		||||
	Fop      uint16
 | 
			
		||||
	Rip      uint64
 | 
			
		||||
	Rdp      uint64
 | 
			
		||||
	Mxcsr    uint32
 | 
			
		||||
	MxcrMask uint32
 | 
			
		||||
	StSpace  [32]uint32
 | 
			
		||||
	XmmSpace [256]byte
 | 
			
		||||
	Padding  [24]uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64Xstate represents amd64 XSAVE area. See Section 13.1 (and
 | 
			
		||||
// following) of Intel® 64 and IA-32 Architectures Software Developer’s
 | 
			
		||||
// Manual, Volume 1: Basic Architecture.
 | 
			
		||||
type AMD64Xstate struct {
 | 
			
		||||
	AMD64PtraceFpRegs
 | 
			
		||||
	Xsave    []byte // raw xsave area
 | 
			
		||||
	AvxState bool   // contains AVX state
 | 
			
		||||
	YmmSpace [256]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode decodes an XSAVE area to a list of name/value pairs of registers.
 | 
			
		||||
func (xsave *AMD64Xstate) Decode() (regs []proc.Register) {
 | 
			
		||||
	// x87 registers
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "CW", xsave.Cwd)
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "SW", xsave.Swd)
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "TW", xsave.Ftw)
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "FOP", xsave.Fop)
 | 
			
		||||
	regs = proc.AppendQwordReg(regs, "FIP", xsave.Rip)
 | 
			
		||||
	regs = proc.AppendQwordReg(regs, "FDP", xsave.Rdp)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(xsave.StSpace); i += 4 {
 | 
			
		||||
		regs = proc.AppendX87Reg(regs, i/4, uint16(xsave.StSpace[i+2]), uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SSE registers
 | 
			
		||||
	regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(xsave.Mxcsr))
 | 
			
		||||
	regs = proc.AppendDwordReg(regs, "MXCSR_MASK", xsave.MxcrMask)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(xsave.XmmSpace); i += 16 {
 | 
			
		||||
		regs = proc.AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16])
 | 
			
		||||
		if xsave.AvxState {
 | 
			
		||||
			regs = proc.AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), xsave.YmmSpace[i:i+16])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_XSAVE_HEADER_START          = 512
 | 
			
		||||
	_XSAVE_HEADER_LEN            = 64
 | 
			
		||||
	_XSAVE_EXTENDED_REGION_START = 576
 | 
			
		||||
	_XSAVE_SSE_REGION_LEN        = 416
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LinuxX86XstateRead reads a byte array containing an XSAVE area into regset.
 | 
			
		||||
// If readLegacy is true regset.PtraceFpRegs will be filled with the
 | 
			
		||||
// contents of the legacy region of the XSAVE area.
 | 
			
		||||
// See Section 13.1 (and following) of Intel® 64 and IA-32 Architectures
 | 
			
		||||
// Software Developer’s Manual, Volume 1: Basic Architecture.
 | 
			
		||||
func AMD64XstateRead(xstateargs []byte, readLegacy bool, regset *AMD64Xstate) error {
 | 
			
		||||
	if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= len(xstateargs) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if readLegacy {
 | 
			
		||||
		rdr := bytes.NewReader(xstateargs[:_XSAVE_HEADER_START])
 | 
			
		||||
		if err := binary.Read(rdr, binary.LittleEndian, ®set.AMD64PtraceFpRegs); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
 | 
			
		||||
	xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
 | 
			
		||||
	xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
 | 
			
		||||
 | 
			
		||||
	if xcomp_bv&(1<<63) != 0 {
 | 
			
		||||
		// compact format not supported
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if xstate_bv&(1<<2) == 0 {
 | 
			
		||||
		// AVX state not present
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:]
 | 
			
		||||
	regset.AvxState = true
 | 
			
		||||
	copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										160
									
								
								vendor/github.com/go-delve/delve/pkg/proc/mem.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								vendor/github.com/go-delve/delve/pkg/proc/mem.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
			
		||||
package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const cacheEnabled = true
 | 
			
		||||
 | 
			
		||||
// MemoryReader is like io.ReaderAt, but the offset is a uintptr so that it
 | 
			
		||||
// can address all of 64-bit memory.
 | 
			
		||||
// Redundant with memoryReadWriter but more easily suited to working with
 | 
			
		||||
// the standard io package.
 | 
			
		||||
type MemoryReader interface {
 | 
			
		||||
	// ReadMemory is just like io.ReaderAt.ReadAt.
 | 
			
		||||
	ReadMemory(buf []byte, addr uintptr) (n int, err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemoryReadWriter is an interface for reading or writing to
 | 
			
		||||
// the targets memory. This allows us to read from the actual
 | 
			
		||||
// target memory or possibly a cache.
 | 
			
		||||
type MemoryReadWriter interface {
 | 
			
		||||
	MemoryReader
 | 
			
		||||
	WriteMemory(addr uintptr, data []byte) (written int, err error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type memCache struct {
 | 
			
		||||
	loaded    bool
 | 
			
		||||
	cacheAddr uintptr
 | 
			
		||||
	cache     []byte
 | 
			
		||||
	mem       MemoryReadWriter
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) contains(addr uintptr, size int) bool {
 | 
			
		||||
	return addr >= m.cacheAddr && addr <= (m.cacheAddr+uintptr(len(m.cache)-size))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) ReadMemory(data []byte, addr uintptr) (n int, err error) {
 | 
			
		||||
	if m.contains(addr, len(data)) {
 | 
			
		||||
		if !m.loaded {
 | 
			
		||||
			_, err := m.mem.ReadMemory(m.cache, m.cacheAddr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, err
 | 
			
		||||
			}
 | 
			
		||||
			m.loaded = true
 | 
			
		||||
		}
 | 
			
		||||
		copy(data, m.cache[addr-m.cacheAddr:])
 | 
			
		||||
		return len(data), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return m.mem.ReadMemory(data, addr)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *memCache) WriteMemory(addr uintptr, data []byte) (written int, err error) {
 | 
			
		||||
	return m.mem.WriteMemory(addr, data)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter {
 | 
			
		||||
	if !cacheEnabled {
 | 
			
		||||
		return mem
 | 
			
		||||
	}
 | 
			
		||||
	if size <= 0 {
 | 
			
		||||
		return mem
 | 
			
		||||
	}
 | 
			
		||||
	switch cacheMem := mem.(type) {
 | 
			
		||||
	case *memCache:
 | 
			
		||||
		if cacheMem.contains(addr, size) {
 | 
			
		||||
			return mem
 | 
			
		||||
		}
 | 
			
		||||
	case *compositeMemory:
 | 
			
		||||
		return mem
 | 
			
		||||
	}
 | 
			
		||||
	return &memCache{false, addr, make([]byte, size), mem}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fakeAddress used by extractVarInfoFromEntry for variables that do not
 | 
			
		||||
// have a memory address, we can't use 0 because a lot of code (likely
 | 
			
		||||
// including client code) assumes that addr == 0 is nil
 | 
			
		||||
const fakeAddress = 0xbeef0000
 | 
			
		||||
 | 
			
		||||
// compositeMemory represents a chunk of memory that is stored in CPU
 | 
			
		||||
// registers or non-contiguously.
 | 
			
		||||
//
 | 
			
		||||
// When optimizations are enabled the compiler will store some variables
 | 
			
		||||
// into registers and sometimes it will also store structs non-contiguously
 | 
			
		||||
// with some fields stored into CPU registers and other fields stored in
 | 
			
		||||
// memory.
 | 
			
		||||
type compositeMemory struct {
 | 
			
		||||
	realmem MemoryReadWriter
 | 
			
		||||
	regs    op.DwarfRegisters
 | 
			
		||||
	pieces  []op.Piece
 | 
			
		||||
	data    []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) {
 | 
			
		||||
	cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}}
 | 
			
		||||
	for _, piece := range pieces {
 | 
			
		||||
		if piece.IsRegister {
 | 
			
		||||
			reg := regs.Bytes(piece.RegNum)
 | 
			
		||||
			sz := piece.Size
 | 
			
		||||
			if sz == 0 && len(pieces) == 1 {
 | 
			
		||||
				sz = len(reg)
 | 
			
		||||
			}
 | 
			
		||||
			if sz > len(reg) {
 | 
			
		||||
				return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d)", sz, piece.RegNum, len(reg))
 | 
			
		||||
			}
 | 
			
		||||
			cmem.data = append(cmem.data, reg[:sz]...)
 | 
			
		||||
		} else {
 | 
			
		||||
			buf := make([]byte, piece.Size)
 | 
			
		||||
			mem.ReadMemory(buf, uintptr(piece.Addr))
 | 
			
		||||
			cmem.data = append(cmem.data, buf...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cmem, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mem *compositeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
 | 
			
		||||
	addr -= fakeAddress
 | 
			
		||||
	if addr >= uintptr(len(mem.data)) || addr+uintptr(len(data)) > uintptr(len(mem.data)) {
 | 
			
		||||
		return 0, errors.New("read out of bounds")
 | 
			
		||||
	}
 | 
			
		||||
	copy(data, mem.data[addr:addr+uintptr(len(data))])
 | 
			
		||||
	return len(data), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mem *compositeMemory) WriteMemory(addr uintptr, data []byte) (int, error) {
 | 
			
		||||
	//TODO(aarzilli): implement
 | 
			
		||||
	return 0, errors.New("can't write composite memory")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DereferenceMemory returns a MemoryReadWriter that can read and write the
 | 
			
		||||
// memory pointed to by pointers in this memory.
 | 
			
		||||
// Normally mem and mem.Dereference are the same object, they are different
 | 
			
		||||
// only if this MemoryReadWriter is used to access memory outside of the
 | 
			
		||||
// normal address space of the inferior process (such as data contained in
 | 
			
		||||
// registers, or composite memory).
 | 
			
		||||
func DereferenceMemory(mem MemoryReadWriter) MemoryReadWriter {
 | 
			
		||||
	switch mem := mem.(type) {
 | 
			
		||||
	case *compositeMemory:
 | 
			
		||||
		return mem.realmem
 | 
			
		||||
	}
 | 
			
		||||
	return mem
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// bufferMemoryReadWriter is dummy a MemoryReadWriter backed by a []byte.
 | 
			
		||||
type bufferMemoryReadWriter struct {
 | 
			
		||||
	buf []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mem *bufferMemoryReadWriter) ReadMemory(buf []byte, addr uintptr) (n int, err error) {
 | 
			
		||||
	copy(buf, mem.buf[addr-fakeAddress:][:len(buf)])
 | 
			
		||||
	return len(buf), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mem *bufferMemoryReadWriter) WriteMemory(addr uintptr, data []byte) (written int, err error) {
 | 
			
		||||
	copy(mem.buf[addr-fakeAddress:], data)
 | 
			
		||||
	return len(data), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -11,11 +11,11 @@ type moduleData struct {
 | 
			
		||||
	typemapVar    *Variable
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadModuleData() (err error) {
 | 
			
		||||
	dbp.loadModuleDataOnce.Do(func() {
 | 
			
		||||
		scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
 | 
			
		||||
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) {
 | 
			
		||||
	bi.loadModuleDataOnce.Do(func() {
 | 
			
		||||
		scope := globalScope(bi, mem)
 | 
			
		||||
		var md *Variable
 | 
			
		||||
		md, err = scope.packageVarAddr("runtime.firstmoduledata")
 | 
			
		||||
		md, err = scope.findGlobal("runtime.firstmoduledata")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
@@ -43,7 +43,7 @@ func (dbp *Process) loadModuleData() (err error) {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			dbp.moduleData = append(dbp.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
 | 
			
		||||
			bi.moduleData = append(bi.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
 | 
			
		||||
 | 
			
		||||
			md = nextVar.maybeDereference()
 | 
			
		||||
			if md.Unreadable != nil {
 | 
			
		||||
@@ -56,56 +56,65 @@ func (dbp *Process) loadModuleData() (err error) {
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, error) {
 | 
			
		||||
	// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
 | 
			
		||||
	if err := dbp.loadModuleData(); err != nil {
 | 
			
		||||
func findModuleDataForType(bi *BinaryInfo, typeAddr uintptr, mem MemoryReadWriter) (*moduleData, error) {
 | 
			
		||||
	if err := loadModuleData(bi, mem); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var md *moduleData
 | 
			
		||||
	for i := range dbp.moduleData {
 | 
			
		||||
		if typeAddr >= dbp.moduleData[i].types && typeAddr < dbp.moduleData[i].etypes {
 | 
			
		||||
			md = &dbp.moduleData[i]
 | 
			
		||||
	for i := range bi.moduleData {
 | 
			
		||||
		if typeAddr >= bi.moduleData[i].types && typeAddr < bi.moduleData[i].etypes {
 | 
			
		||||
			md = &bi.moduleData[i]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rtyp, err := dbp.findType("runtime._type")
 | 
			
		||||
	return md, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (*Variable, error) {
 | 
			
		||||
	// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
 | 
			
		||||
	md, err := findModuleDataForType(bi, typeAddr, mem)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rtyp, err := bi.findType("runtime._type")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if md == nil {
 | 
			
		||||
		v, err := dbp.reflectOffsMapAccess(off)
 | 
			
		||||
		v, err := reflectOffsMapAccess(bi, off, mem)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		v.loadValue(LoadConfig{false, 1, 0, 0, -1})
 | 
			
		||||
		v.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
 | 
			
		||||
		addr, _ := constant.Int64Val(v.Value)
 | 
			
		||||
		return v.newVariable(v.Name, uintptr(addr), rtyp), nil
 | 
			
		||||
		return v.newVariable(v.Name, uintptr(addr), rtyp, mem), nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.CurrentThread)); t != nil {
 | 
			
		||||
	if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
 | 
			
		||||
		return t, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	res := md.types + uintptr(off)
 | 
			
		||||
 | 
			
		||||
	return dbp.CurrentThread.newVariable("", res, rtyp), nil
 | 
			
		||||
	return newVariable("", res, rtyp, bi, mem), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag string, pkgpathoff int32, err error) {
 | 
			
		||||
func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
 | 
			
		||||
	// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
 | 
			
		||||
	if err = dbp.loadModuleData(); err != nil {
 | 
			
		||||
	if err = loadModuleData(bi, mem); err != nil {
 | 
			
		||||
		return "", "", 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, md := range dbp.moduleData {
 | 
			
		||||
	for _, md := range bi.moduleData {
 | 
			
		||||
		if typeAddr >= md.types && typeAddr < md.etypes {
 | 
			
		||||
			return dbp.loadName(md.types + off)
 | 
			
		||||
			return loadName(bi, md.types+off, mem)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v, err := dbp.reflectOffsMapAccess(off)
 | 
			
		||||
	v, err := reflectOffsMapAccess(bi, off, mem)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", 0, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -115,12 +124,12 @@ func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag str
 | 
			
		||||
		return "", "", 0, resv.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dbp.loadName(resv.Addr)
 | 
			
		||||
	return loadName(bi, resv.Addr, mem)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
 | 
			
		||||
	scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
 | 
			
		||||
	reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
 | 
			
		||||
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) {
 | 
			
		||||
	scope := globalScope(bi, mem)
 | 
			
		||||
	reflectOffs, err := scope.findGlobal("runtime.reflectOffs")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -130,7 +139,7 @@ func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.CurrentThread))
 | 
			
		||||
	return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -140,17 +149,19 @@ const (
 | 
			
		||||
	nameflagHasPkg   = 1 << 2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32, err error) {
 | 
			
		||||
func loadName(bi *BinaryInfo, addr uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
 | 
			
		||||
	off := addr
 | 
			
		||||
	namedata, err := dbp.CurrentThread.readMemory(off, 3)
 | 
			
		||||
	namedata := make([]byte, 3)
 | 
			
		||||
	_, err = mem.ReadMemory(namedata, off)
 | 
			
		||||
	off += 3
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namelen := uint16(namedata[1]<<8) | uint16(namedata[2])
 | 
			
		||||
	namelen := uint16(namedata[1])<<8 | uint16(namedata[2])
 | 
			
		||||
 | 
			
		||||
	rawstr, err := dbp.CurrentThread.readMemory(off, int(namelen))
 | 
			
		||||
	rawstr := make([]byte, int(namelen))
 | 
			
		||||
	_, err = mem.ReadMemory(rawstr, off)
 | 
			
		||||
	off += uintptr(namelen)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", "", 0, err
 | 
			
		||||
@@ -159,14 +170,16 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
 | 
			
		||||
	name = string(rawstr)
 | 
			
		||||
 | 
			
		||||
	if namedata[0]&nameflagHasTag != 0 {
 | 
			
		||||
		taglendata, err := dbp.CurrentThread.readMemory(off, 2)
 | 
			
		||||
		taglendata := make([]byte, 2)
 | 
			
		||||
		_, err = mem.ReadMemory(taglendata, off)
 | 
			
		||||
		off += 2
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", "", 0, err
 | 
			
		||||
		}
 | 
			
		||||
		taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1])
 | 
			
		||||
		taglen := uint16(taglendata[0])<<8 | uint16(taglendata[1])
 | 
			
		||||
 | 
			
		||||
		rawstr, err := dbp.CurrentThread.readMemory(off, int(taglen))
 | 
			
		||||
		rawstr := make([]byte, int(taglen))
 | 
			
		||||
		_, err = mem.ReadMemory(rawstr, off)
 | 
			
		||||
		off += uintptr(taglen)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", "", 0, err
 | 
			
		||||
@@ -176,7 +189,8 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if namedata[0]&nameflagHasPkg != 0 {
 | 
			
		||||
		pkgdata, err := dbp.CurrentThread.readMemory(off, 4)
 | 
			
		||||
		pkgdata := make([]byte, 4)
 | 
			
		||||
		_, err = mem.ReadMemory(pkgdata, off)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", "", 0, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * IDENTIFICATION:
 | 
			
		||||
 * stub generated Sun Feb 22 20:54:31 2015
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
#include "exec_darwin.h"
 | 
			
		||||
#include "stdio.h"
 | 
			
		||||
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
#include "proc_darwin.h"
 | 
			
		||||
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * IDENTIFICATION:
 | 
			
		||||
 * stub generated Sat Feb 21 18:10:52 2015
 | 
			
		||||
							
								
								
									
										132
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/nonative_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/nonative_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,132 @@
 | 
			
		||||
//+build darwin,!macnative
 | 
			
		||||
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
 | 
			
		||||
 | 
			
		||||
// Launch returns ErrNativeBackendDisabled.
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
 | 
			
		||||
	return nil, ErrNativeBackendDisabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach returns ErrNativeBackendDisabled.
 | 
			
		||||
func Attach(pid int, _ []string) (*Process, error) {
 | 
			
		||||
	return nil, ErrNativeBackendDisabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WaitStatus is a synonym for the platform-specific WaitStatus
 | 
			
		||||
type WaitStatus struct{}
 | 
			
		||||
 | 
			
		||||
// OSSpecificDetails holds information specific to the OSX/Darwin
 | 
			
		||||
// operating system / kernel.
 | 
			
		||||
type OSSpecificDetails struct{}
 | 
			
		||||
 | 
			
		||||
// OSProcessDetails holds Darwin specific information.
 | 
			
		||||
type OSProcessDetails struct{}
 | 
			
		||||
 | 
			
		||||
func findExecutable(path string, pid int) string {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func killProcess(pid int) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) requestManualStop() (err error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) resume() error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) updateThreadList() error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) kill() (err error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EntryPoint returns the entry point for the process,
 | 
			
		||||
// useful for PIEs.
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Blocked returns true if the thread is blocked
 | 
			
		||||
func (t *Thread) Blocked() bool {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC sets the value of the PC register.
 | 
			
		||||
func (t *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets the value of the SP register.
 | 
			
		||||
func (t *Thread) SetSP(sp uint64) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDX sets the value of the DX register.
 | 
			
		||||
func (t *Thread) SetDX(dx uint64) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadMemory reads len(buf) bytes at addr into buf.
 | 
			
		||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteMemory writes the contents of data at addr.
 | 
			
		||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) resume() error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) singleStep() error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) restoreRegisters(sr proc.Registers) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stopped returns whether the thread is stopped at
 | 
			
		||||
// the operating system level.
 | 
			
		||||
func (t *Thread) Stopped() bool {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initialize(dbp *Process) error { return nil }
 | 
			
		||||
							
								
								
									
										422
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/proc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										422
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/proc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,422 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Process represents all of the information the debugger
 | 
			
		||||
// is holding onto regarding the process we are debugging.
 | 
			
		||||
type Process struct {
 | 
			
		||||
	bi *proc.BinaryInfo
 | 
			
		||||
 | 
			
		||||
	pid int // Process Pid
 | 
			
		||||
 | 
			
		||||
	// Breakpoint table, holds information on breakpoints.
 | 
			
		||||
	// Maps instruction address to Breakpoint struct.
 | 
			
		||||
	breakpoints proc.BreakpointMap
 | 
			
		||||
 | 
			
		||||
	// List of threads mapped as such: pid -> *Thread
 | 
			
		||||
	threads map[int]*Thread
 | 
			
		||||
 | 
			
		||||
	// Active thread
 | 
			
		||||
	currentThread *Thread
 | 
			
		||||
 | 
			
		||||
	// Goroutine that will be used by default to set breakpoint, eval variables, etc...
 | 
			
		||||
	// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
 | 
			
		||||
	selectedGoroutine *proc.G
 | 
			
		||||
 | 
			
		||||
	common              proc.CommonProcess
 | 
			
		||||
	os                  *OSProcessDetails
 | 
			
		||||
	firstStart          bool
 | 
			
		||||
	stopMu              sync.Mutex
 | 
			
		||||
	resumeChan          chan<- struct{}
 | 
			
		||||
	ptraceChan          chan func()
 | 
			
		||||
	ptraceDoneChan      chan interface{}
 | 
			
		||||
	childProcess        bool // this process was launched, not attached to
 | 
			
		||||
	manualStopRequested bool
 | 
			
		||||
 | 
			
		||||
	exited, detached bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// New returns an initialized Process struct. Before returning,
 | 
			
		||||
// it will also launch a goroutine in order to handle ptrace(2)
 | 
			
		||||
// functions. For more information, see the documentation on
 | 
			
		||||
// `handlePtraceFuncs`.
 | 
			
		||||
func New(pid int) *Process {
 | 
			
		||||
	dbp := &Process{
 | 
			
		||||
		pid:            pid,
 | 
			
		||||
		threads:        make(map[int]*Thread),
 | 
			
		||||
		breakpoints:    proc.NewBreakpointMap(),
 | 
			
		||||
		firstStart:     true,
 | 
			
		||||
		os:             new(OSProcessDetails),
 | 
			
		||||
		ptraceChan:     make(chan func()),
 | 
			
		||||
		ptraceDoneChan: make(chan interface{}),
 | 
			
		||||
		bi:             proc.NewBinaryInfo(runtime.GOOS, runtime.GOARCH),
 | 
			
		||||
	}
 | 
			
		||||
	go dbp.handlePtraceFuncs()
 | 
			
		||||
	return dbp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BinInfo will return the binary info struct associated with this process.
 | 
			
		||||
func (dbp *Process) BinInfo() *proc.BinaryInfo {
 | 
			
		||||
	return dbp.bi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Recorded always returns false for the native proc backend.
 | 
			
		||||
func (dbp *Process) Recorded() (bool, string) { return false, "" }
 | 
			
		||||
 | 
			
		||||
// Restart will always return an error in the native proc backend, only for
 | 
			
		||||
// recorded traces.
 | 
			
		||||
func (dbp *Process) Restart(string) error { return proc.ErrNotRecorded }
 | 
			
		||||
 | 
			
		||||
// Direction will always return an error in the native proc backend, only for
 | 
			
		||||
// recorded traces.
 | 
			
		||||
func (dbp *Process) Direction(proc.Direction) error { return proc.ErrNotRecorded }
 | 
			
		||||
 | 
			
		||||
// When will always return an empty string and nil, not supported on native proc backend.
 | 
			
		||||
func (dbp *Process) When() (string, error) { return "", nil }
 | 
			
		||||
 | 
			
		||||
// Checkpoint will always return an error on the native proc backend,
 | 
			
		||||
// only supported for recorded traces.
 | 
			
		||||
func (dbp *Process) Checkpoint(string) (int, error) { return -1, proc.ErrNotRecorded }
 | 
			
		||||
 | 
			
		||||
// Checkpoints will always return an error on the native proc backend,
 | 
			
		||||
// only supported for recorded traces.
 | 
			
		||||
func (dbp *Process) Checkpoints() ([]proc.Checkpoint, error) { return nil, proc.ErrNotRecorded }
 | 
			
		||||
 | 
			
		||||
// ClearCheckpoint will always return an error on the native proc backend,
 | 
			
		||||
// only supported in recorded traces.
 | 
			
		||||
func (dbp *Process) ClearCheckpoint(int) error { return proc.ErrNotRecorded }
 | 
			
		||||
 | 
			
		||||
// Detach from the process being debugged, optionally killing it.
 | 
			
		||||
func (dbp *Process) Detach(kill bool) (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if kill && dbp.childProcess {
 | 
			
		||||
		err := dbp.kill()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		dbp.bi.Close()
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !kill {
 | 
			
		||||
		// Clean up any breakpoints we've set.
 | 
			
		||||
		for _, bp := range dbp.breakpoints.M {
 | 
			
		||||
			if bp != nil {
 | 
			
		||||
				_, err := dbp.ClearBreakpoint(bp.Addr)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		err = dbp.detach(kill)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if kill {
 | 
			
		||||
			err = killProcess(dbp.pid)
 | 
			
		||||
		}
 | 
			
		||||
	})
 | 
			
		||||
	dbp.detached = true
 | 
			
		||||
	dbp.postExit()
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Valid returns whether the process is still attached to and
 | 
			
		||||
// has not exited.
 | 
			
		||||
func (dbp *Process) Valid() (bool, error) {
 | 
			
		||||
	if dbp.detached {
 | 
			
		||||
		return false, &proc.ProcessDetachedError{}
 | 
			
		||||
	}
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return false, &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResumeNotify specifies a channel that will be closed the next time
 | 
			
		||||
// ContinueOnce finishes resuming the target.
 | 
			
		||||
func (dbp *Process) ResumeNotify(ch chan<- struct{}) {
 | 
			
		||||
	dbp.resumeChan = ch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Pid returns the process ID.
 | 
			
		||||
func (dbp *Process) Pid() int {
 | 
			
		||||
	return dbp.pid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SelectedGoroutine returns the current selected,
 | 
			
		||||
// active goroutine.
 | 
			
		||||
func (dbp *Process) SelectedGoroutine() *proc.G {
 | 
			
		||||
	return dbp.selectedGoroutine
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ThreadList returns a list of threads in the process.
 | 
			
		||||
func (dbp *Process) ThreadList() []proc.Thread {
 | 
			
		||||
	r := make([]proc.Thread, 0, len(dbp.threads))
 | 
			
		||||
	for _, v := range dbp.threads {
 | 
			
		||||
		r = append(r, v)
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindThread attempts to find the thread with the specified ID.
 | 
			
		||||
func (dbp *Process) FindThread(threadID int) (proc.Thread, bool) {
 | 
			
		||||
	th, ok := dbp.threads[threadID]
 | 
			
		||||
	return th, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CurrentThread returns the current selected, active thread.
 | 
			
		||||
func (dbp *Process) CurrentThread() proc.Thread {
 | 
			
		||||
	return dbp.currentThread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Breakpoints returns a list of breakpoints currently set.
 | 
			
		||||
func (dbp *Process) Breakpoints() *proc.BreakpointMap {
 | 
			
		||||
	return &dbp.breakpoints
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestManualStop sets the `halt` flag and
 | 
			
		||||
// sends SIGSTOP to all threads.
 | 
			
		||||
func (dbp *Process) RequestManualStop() error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	dbp.stopMu.Lock()
 | 
			
		||||
	defer dbp.stopMu.Unlock()
 | 
			
		||||
	dbp.manualStopRequested = true
 | 
			
		||||
	return dbp.requestManualStop()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CheckAndClearManualStopRequest checks if a manual stop has
 | 
			
		||||
// been requested, and then clears that state.
 | 
			
		||||
func (dbp *Process) CheckAndClearManualStopRequest() bool {
 | 
			
		||||
	dbp.stopMu.Lock()
 | 
			
		||||
	defer dbp.stopMu.Unlock()
 | 
			
		||||
 | 
			
		||||
	msr := dbp.manualStopRequested
 | 
			
		||||
	dbp.manualStopRequested = false
 | 
			
		||||
 | 
			
		||||
	return msr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) {
 | 
			
		||||
	f, l, fn := dbp.bi.PCToLine(uint64(addr))
 | 
			
		||||
 | 
			
		||||
	originalData := make([]byte, dbp.bi.Arch.BreakpointSize())
 | 
			
		||||
	_, err := dbp.currentThread.ReadMemory(originalData, uintptr(addr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", 0, nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := dbp.writeSoftwareBreakpoint(dbp.currentThread, addr); err != nil {
 | 
			
		||||
		return "", 0, nil, nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return f, l, fn, originalData, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
 | 
			
		||||
// break point table.
 | 
			
		||||
func (dbp *Process) SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error) {
 | 
			
		||||
	return dbp.breakpoints.Set(addr, kind, cond, dbp.writeBreakpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearBreakpoint clears the breakpoint at addr.
 | 
			
		||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*proc.Breakpoint, error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	return dbp.breakpoints.Clear(addr, dbp.currentThread.ClearBreakpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ContinueOnce will continue the target until it stops.
 | 
			
		||||
// This could be the result of a breakpoint or signal.
 | 
			
		||||
func (dbp *Process) ContinueOnce() (proc.Thread, error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil, &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := dbp.resume(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.common.ClearAllGCache()
 | 
			
		||||
	for _, th := range dbp.threads {
 | 
			
		||||
		th.CurrentBreakpoint.Clear()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dbp.resumeChan != nil {
 | 
			
		||||
		close(dbp.resumeChan)
 | 
			
		||||
		dbp.resumeChan = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trapthread, err := dbp.trapWait(-1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := dbp.stop(trapthread); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return trapthread, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepInstruction will continue the current thread for exactly
 | 
			
		||||
// one instruction. This method affects only the thread
 | 
			
		||||
// associated with the selected goroutine. All other
 | 
			
		||||
// threads will remain stopped.
 | 
			
		||||
func (dbp *Process) StepInstruction() (err error) {
 | 
			
		||||
	thread := dbp.currentThread
 | 
			
		||||
	if dbp.selectedGoroutine != nil {
 | 
			
		||||
		if dbp.selectedGoroutine.Thread == nil {
 | 
			
		||||
			// Step called on parked goroutine
 | 
			
		||||
			if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, proc.NextBreakpoint, proc.SameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return proc.Continue(dbp)
 | 
			
		||||
		}
 | 
			
		||||
		thread = dbp.selectedGoroutine.Thread.(*Thread)
 | 
			
		||||
	}
 | 
			
		||||
	dbp.common.ClearAllGCache()
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	thread.CurrentBreakpoint.Clear()
 | 
			
		||||
	err = thread.StepInstruction()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	err = thread.SetCurrentBreakpoint()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if g, _ := proc.GetG(thread); g != nil {
 | 
			
		||||
		dbp.selectedGoroutine = g
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SwitchThread changes from current thread to the thread specified by `tid`.
 | 
			
		||||
func (dbp *Process) SwitchThread(tid int) error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	if th, ok := dbp.threads[tid]; ok {
 | 
			
		||||
		dbp.currentThread = th
 | 
			
		||||
		dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread)
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Errorf("thread %d does not exist", tid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SwitchGoroutine changes from current thread to the thread
 | 
			
		||||
// running the specified goroutine.
 | 
			
		||||
func (dbp *Process) SwitchGoroutine(gid int) error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	g, err := proc.FindGoroutine(dbp, gid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if g == nil {
 | 
			
		||||
		// user specified -1 and selectedGoroutine is nil
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if g.Thread != nil {
 | 
			
		||||
		return dbp.SwitchThread(g.Thread.ThreadID())
 | 
			
		||||
	}
 | 
			
		||||
	dbp.selectedGoroutine = g
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindBreakpoint finds the breakpoint for the given pc.
 | 
			
		||||
func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
 | 
			
		||||
	// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
 | 
			
		||||
	if bp, ok := dbp.breakpoints.M[pc-uint64(dbp.bi.Arch.BreakpointSize())]; ok {
 | 
			
		||||
		return bp, true
 | 
			
		||||
	}
 | 
			
		||||
	// Directly use addr to lookup breakpoint.
 | 
			
		||||
	if bp, ok := dbp.breakpoints.M[pc]; ok {
 | 
			
		||||
		return bp, true
 | 
			
		||||
	}
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize will ensure that all relevant information is loaded
 | 
			
		||||
// so the process is ready to be debugged.
 | 
			
		||||
func (dbp *Process) initialize(path string, debugInfoDirs []string) error {
 | 
			
		||||
	if err := initialize(dbp); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if err := dbp.updateThreadList(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return proc.PostInitializationSetup(dbp, path, debugInfoDirs, dbp.writeBreakpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSelectedGoroutine will set internally the goroutine that should be
 | 
			
		||||
// the default for any command executed, the goroutine being actively
 | 
			
		||||
// followed.
 | 
			
		||||
func (dbp *Process) SetSelectedGoroutine(g *proc.G) {
 | 
			
		||||
	dbp.selectedGoroutine = g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearInternalBreakpoints will clear all non-user set breakpoints. These
 | 
			
		||||
// breakpoints are set for internal operations such as 'next'.
 | 
			
		||||
func (dbp *Process) ClearInternalBreakpoints() error {
 | 
			
		||||
	return dbp.breakpoints.ClearInternalBreakpoints(func(bp *proc.Breakpoint) error {
 | 
			
		||||
		if err := dbp.currentThread.ClearBreakpoint(bp); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, thread := range dbp.threads {
 | 
			
		||||
			if thread.CurrentBreakpoint.Breakpoint == bp {
 | 
			
		||||
				thread.CurrentBreakpoint.Clear()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) handlePtraceFuncs() {
 | 
			
		||||
	// We must ensure here that we are running on the same thread during
 | 
			
		||||
	// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
 | 
			
		||||
	// all commands after PTRACE_ATTACH to come from the same thread.
 | 
			
		||||
	runtime.LockOSThread()
 | 
			
		||||
 | 
			
		||||
	for fn := range dbp.ptraceChan {
 | 
			
		||||
		fn()
 | 
			
		||||
		dbp.ptraceDoneChan <- nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) execPtraceFunc(fn func()) {
 | 
			
		||||
	dbp.ptraceChan <- fn
 | 
			
		||||
	<-dbp.ptraceDoneChan
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) postExit() {
 | 
			
		||||
	dbp.exited = true
 | 
			
		||||
	close(dbp.ptraceChan)
 | 
			
		||||
	close(dbp.ptraceDoneChan)
 | 
			
		||||
	dbp.bi.Close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
 | 
			
		||||
	_, err := thread.WriteMemory(uintptr(addr), dbp.bi.Arch.BreakpointInstruction())
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Common returns common information across Process
 | 
			
		||||
// implementations
 | 
			
		||||
func (dbp *Process) Common() *proc.CommonProcess {
 | 
			
		||||
	return &dbp.common
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
#include "proc_darwin.h"
 | 
			
		||||
 | 
			
		||||
static const unsigned char info_plist[]
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
package proc
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
// #include "proc_darwin.h"
 | 
			
		||||
// #include "threads_darwin.h"
 | 
			
		||||
@@ -6,20 +8,16 @@ package proc
 | 
			
		||||
// #include <stdlib.h>
 | 
			
		||||
import "C"
 | 
			
		||||
import (
 | 
			
		||||
	"debug/gosym"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/debug/macho"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/frame"
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/line"
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OSProcessDetails holds Darwin specific information.
 | 
			
		||||
@@ -28,6 +26,7 @@ type OSProcessDetails struct {
 | 
			
		||||
	exceptionPort    C.mach_port_t // mach port for receiving mach exceptions.
 | 
			
		||||
	notificationPort C.mach_port_t // mach port for dead name notification (process exit).
 | 
			
		||||
	initialized      bool
 | 
			
		||||
	halt             bool
 | 
			
		||||
 | 
			
		||||
	// the main port we use, will return messages from both the
 | 
			
		||||
	// exception and notification ports.
 | 
			
		||||
@@ -38,10 +37,10 @@ type OSProcessDetails struct {
 | 
			
		||||
// custom fork/exec process in order to take advantage of
 | 
			
		||||
// PT_SIGEXC on Darwin which will turn Unix signals into
 | 
			
		||||
// Mach exceptions.
 | 
			
		||||
func Launch(cmd []string, wd string) (*Process, error) {
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, 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, NotExecutableErr
 | 
			
		||||
		return nil, proc.ErrNotExecutable
 | 
			
		||||
	}
 | 
			
		||||
	argv0Go, err := filepath.Abs(cmd[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -77,7 +76,8 @@ func Launch(cmd []string, wd string) (*Process, error) {
 | 
			
		||||
	if pid <= 0 {
 | 
			
		||||
		return nil, fmt.Errorf("could not fork/exec")
 | 
			
		||||
	}
 | 
			
		||||
	dbp.Pid = pid
 | 
			
		||||
	dbp.pid = pid
 | 
			
		||||
	dbp.childProcess = true
 | 
			
		||||
	for i := range argvSlice {
 | 
			
		||||
		C.free(unsafe.Pointer(argvSlice[i]))
 | 
			
		||||
	}
 | 
			
		||||
@@ -86,12 +86,17 @@ func Launch(cmd []string, wd string) (*Process, error) {
 | 
			
		||||
	// trapWait to wait until the child process calls execve.
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		err = dbp.updateThreadListForTask(C.get_task_for_pid(C.int(dbp.Pid)))
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if err != couldNotGetThreadCount && err != couldNotGetThreadList {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		task := C.get_task_for_pid(C.int(dbp.pid))
 | 
			
		||||
		// The task_for_pid call races with the fork call. This can
 | 
			
		||||
		// result in the parent task being returned instead of the child.
 | 
			
		||||
		if task != dbp.os.task {
 | 
			
		||||
			err = dbp.updateThreadListForTask(task)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			if err != couldNotGetThreadCount && err != couldNotGetThreadList {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -99,26 +104,21 @@ func Launch(cmd []string, wd string) (*Process, error) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.allGCache = nil
 | 
			
		||||
	for _, th := range dbp.Threads {
 | 
			
		||||
		th.clearBreakpointState()
 | 
			
		||||
	dbp.common.ClearAllGCache()
 | 
			
		||||
	for _, th := range dbp.threads {
 | 
			
		||||
		th.CurrentBreakpoint.Clear()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trapthread, err := dbp.trapWait(-1)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if err := dbp.Halt(); err != nil {
 | 
			
		||||
		return nil, dbp.exitGuard(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = dbp.waitForStop()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	if err := dbp.stop(nil); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.os.initialized = true
 | 
			
		||||
	dbp, err = initializeDebugProcess(dbp, argv0Go, false)
 | 
			
		||||
	err = dbp.initialize(argv0Go, []string{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -131,7 +131,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to an existing process with the given PID.
 | 
			
		||||
func Attach(pid int) (*Process, error) {
 | 
			
		||||
func Attach(pid int, _ []string) (*Process, error) {
 | 
			
		||||
	dbp := New(pid)
 | 
			
		||||
 | 
			
		||||
	kret := C.acquire_mach_task(C.int(pid),
 | 
			
		||||
@@ -144,19 +144,34 @@ func Attach(pid int) (*Process, error) {
 | 
			
		||||
 | 
			
		||||
	dbp.os.initialized = true
 | 
			
		||||
 | 
			
		||||
	return initializeDebugProcess(dbp, "", true)
 | 
			
		||||
	var err error
 | 
			
		||||
	dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = dbp.wait(dbp.pid, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = dbp.initialize("", []string{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		dbp.Detach(false)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Kill kills the process.
 | 
			
		||||
func (dbp *Process) Kill() (err error) {
 | 
			
		||||
func (dbp *Process) kill() (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	err = sys.Kill(-dbp.Pid, sys.SIGKILL)
 | 
			
		||||
	err = sys.Kill(-dbp.pid, sys.SIGKILL)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return errors.New("could not deliver signal: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	for port := range dbp.Threads {
 | 
			
		||||
	for port := range dbp.threads {
 | 
			
		||||
		if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
 | 
			
		||||
			return errors.New("could not resume task")
 | 
			
		||||
		}
 | 
			
		||||
@@ -175,9 +190,10 @@ func (dbp *Process) Kill() (err error) {
 | 
			
		||||
func (dbp *Process) requestManualStop() (err error) {
 | 
			
		||||
	var (
 | 
			
		||||
		task          = C.mach_port_t(dbp.os.task)
 | 
			
		||||
		thread        = C.mach_port_t(dbp.CurrentThread.os.threadAct)
 | 
			
		||||
		thread        = C.mach_port_t(dbp.currentThread.os.threadAct)
 | 
			
		||||
		exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
 | 
			
		||||
	)
 | 
			
		||||
	dbp.os.halt = true
 | 
			
		||||
	kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
 | 
			
		||||
	if kret != C.KERN_SUCCESS {
 | 
			
		||||
		return fmt.Errorf("could not raise mach exception")
 | 
			
		||||
@@ -218,12 +234,12 @@ func (dbp *Process) updateThreadListForTask(task C.task_t) error {
 | 
			
		||||
		return couldNotGetThreadList
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		thread.os.exists = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, port := range list {
 | 
			
		||||
		thread, ok := dbp.Threads[int(port)]
 | 
			
		||||
		thread, ok := dbp.threads[int(port)]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			thread, err = dbp.addThread(int(port), false)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
@@ -233,9 +249,9 @@ func (dbp *Process) updateThreadListForTask(task C.task_t) error {
 | 
			
		||||
		thread.os.exists = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for threadID, thread := range dbp.Threads {
 | 
			
		||||
	for threadID, thread := range dbp.threads {
 | 
			
		||||
		if !thread.os.exists {
 | 
			
		||||
			delete(dbp.Threads, threadID)
 | 
			
		||||
			delete(dbp.threads, threadID)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -243,7 +259,7 @@ func (dbp *Process) updateThreadListForTask(task C.task_t) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
 | 
			
		||||
	if thread, ok := dbp.Threads[port]; ok {
 | 
			
		||||
	if thread, ok := dbp.threads[port]; ok {
 | 
			
		||||
		return thread, nil
 | 
			
		||||
	}
 | 
			
		||||
	thread := &Thread{
 | 
			
		||||
@@ -251,107 +267,19 @@ func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
 | 
			
		||||
		dbp: dbp,
 | 
			
		||||
		os:  new(OSSpecificDetails),
 | 
			
		||||
	}
 | 
			
		||||
	dbp.Threads[port] = thread
 | 
			
		||||
	dbp.threads[port] = thread
 | 
			
		||||
	thread.os.threadAct = C.thread_act_t(port)
 | 
			
		||||
	if dbp.CurrentThread == nil {
 | 
			
		||||
	if dbp.currentThread == nil {
 | 
			
		||||
		dbp.SwitchThread(thread.ID)
 | 
			
		||||
	}
 | 
			
		||||
	return thread, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	debugFrameSec := exe.Section("__debug_frame")
 | 
			
		||||
	debugInfoSec := exe.Section("__debug_info")
 | 
			
		||||
 | 
			
		||||
	if debugFrameSec != nil && debugInfoSec != nil {
 | 
			
		||||
		debugFrame, err := exe.Section("__debug_frame").Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get __debug_frame section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		dat, err := debugInfoSec.Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .debug_info section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("could not find __debug_frame section in binary")
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		symdat  []byte
 | 
			
		||||
		pclndat []byte
 | 
			
		||||
		err     error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if sec := exe.Section("__gosymtab"); sec != nil {
 | 
			
		||||
		symdat, err = sec.Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .gosymtab section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if sec := exe.Section("__gopclntab"); sec != nil {
 | 
			
		||||
		pclndat, err = sec.Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .gopclntab section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
 | 
			
		||||
	tab, err := gosym.NewTable(symdat, pcln)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("could not get initialize line table", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.goSymTable = tab
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	if sec := exe.Section("__debug_line"); sec != nil {
 | 
			
		||||
		debugLine, err := exe.Section("__debug_line").Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get __debug_line section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		dbp.lineInfo = line.Parse(debugLine)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("could not find __debug_line section in binary")
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var UnsupportedArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) findExecutable(path string) (*macho.File, string, error) {
 | 
			
		||||
func findExecutable(path string, pid int) string {
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		path = C.GoString(C.find_executable(C.int(dbp.Pid)))
 | 
			
		||||
		path = C.GoString(C.find_executable(C.int(pid)))
 | 
			
		||||
	}
 | 
			
		||||
	exe, err := macho.Open(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, path, err
 | 
			
		||||
	}
 | 
			
		||||
	if exe.Cpu != macho.CpuAmd64 {
 | 
			
		||||
		return nil, path, UnsupportedArchErr
 | 
			
		||||
	}
 | 
			
		||||
	dbp.dwarf, err = exe.DWARF()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, path, err
 | 
			
		||||
	}
 | 
			
		||||
	return exe, path, nil
 | 
			
		||||
	return path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
@@ -369,19 +297,22 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if !dbp.os.initialized {
 | 
			
		||||
				if pidtask := C.get_task_for_pid(C.int(dbp.Pid)); pidtask != 0 && dbp.os.task != pidtask {
 | 
			
		||||
				if pidtask := C.get_task_for_pid(C.int(dbp.pid)); pidtask != 0 && dbp.os.task != pidtask {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			_, status, err := dbp.wait(dbp.Pid, 0)
 | 
			
		||||
			_, status, err := dbp.wait(dbp.pid, 0)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			dbp.postExit()
 | 
			
		||||
			return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
 | 
			
		||||
			return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
 | 
			
		||||
 | 
			
		||||
		case C.MACH_RCV_INTERRUPTED:
 | 
			
		||||
			if !dbp.halt {
 | 
			
		||||
			dbp.stopMu.Lock()
 | 
			
		||||
			halt := dbp.os.halt
 | 
			
		||||
			dbp.stopMu.Unlock()
 | 
			
		||||
			if !halt {
 | 
			
		||||
				// Call trapWait again, it seems
 | 
			
		||||
				// MACH_RCV_INTERRUPTED is emitted before
 | 
			
		||||
				// process natural death _sometimes_.
 | 
			
		||||
@@ -407,10 +338,13 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
		// Since we cannot be notified of new threads on OS X
 | 
			
		||||
		// this is as good a time as any to check for them.
 | 
			
		||||
		dbp.updateThreadList()
 | 
			
		||||
		th, ok := dbp.Threads[int(port)]
 | 
			
		||||
		th, ok := dbp.threads[int(port)]
 | 
			
		||||
		if !ok {
 | 
			
		||||
			if dbp.halt {
 | 
			
		||||
				dbp.halt = false
 | 
			
		||||
			dbp.stopMu.Lock()
 | 
			
		||||
			halt := dbp.os.halt
 | 
			
		||||
			dbp.stopMu.Unlock()
 | 
			
		||||
			if halt {
 | 
			
		||||
				dbp.os.halt = false
 | 
			
		||||
				return th, nil
 | 
			
		||||
			}
 | 
			
		||||
			if dbp.firstStart || th.singleStepping {
 | 
			
		||||
@@ -427,7 +361,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) waitForStop() ([]int, error) {
 | 
			
		||||
	ports := make([]int, 0, len(dbp.Threads))
 | 
			
		||||
	ports := make([]int, 0, len(dbp.threads))
 | 
			
		||||
	count := 0
 | 
			
		||||
	for {
 | 
			
		||||
		var task C.task_t
 | 
			
		||||
@@ -448,27 +382,6 @@ func (dbp *Process) waitForStop() ([]int, error) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
 | 
			
		||||
	ports, err := dbp.waitForStop()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	trapthread.SetCurrentBreakpoint()
 | 
			
		||||
	for _, port := range ports {
 | 
			
		||||
		if th, ok := dbp.Threads[port]; ok {
 | 
			
		||||
			err := th.SetCurrentBreakpoint()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
 | 
			
		||||
	wg.Done()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
			
		||||
	var status sys.WaitStatus
 | 
			
		||||
	wpid, err := sys.Wait4(pid, &status, options, nil)
 | 
			
		||||
@@ -483,29 +396,72 @@ func (dbp *Process) exitGuard(err error) error {
 | 
			
		||||
	if err != ErrContinueThread {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	_, status, werr := dbp.wait(dbp.Pid, sys.WNOHANG)
 | 
			
		||||
	_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
 | 
			
		||||
	if werr == nil && status.Exited() {
 | 
			
		||||
		dbp.postExit()
 | 
			
		||||
		return ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
 | 
			
		||||
		return proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
 | 
			
		||||
	}
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) resume() error {
 | 
			
		||||
	// all threads stopped over a breakpoint are made to step over it
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
		if thread.CurrentBreakpoint != nil {
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		if thread.CurrentBreakpoint.Breakpoint != nil {
 | 
			
		||||
			if err := thread.StepInstruction(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			thread.CurrentBreakpoint = nil
 | 
			
		||||
			thread.CurrentBreakpoint.Clear()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// everything is resumed
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		if err := thread.resume(); err != nil {
 | 
			
		||||
			return dbp.exitGuard(err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stop stops all running threads and sets breakpoints
 | 
			
		||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	for _, th := range dbp.threads {
 | 
			
		||||
		if !th.Stopped() {
 | 
			
		||||
			if err := th.stop(); err != nil {
 | 
			
		||||
				return dbp.exitGuard(err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ports, err := dbp.waitForStop()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if !dbp.os.initialized {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	trapthread.SetCurrentBreakpoint()
 | 
			
		||||
	for _, port := range ports {
 | 
			
		||||
		if th, ok := dbp.threads[port]; ok {
 | 
			
		||||
			err := th.SetCurrentBreakpoint()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	return PtraceDetach(dbp.pid, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	//TODO(aarzilli): implement this
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initialize(dbp *Process) error { return nil }
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <libproc.h>
 | 
			
		||||
#include <mach/mach.h>
 | 
			
		||||
@@ -1,26 +1,26 @@
 | 
			
		||||
package proc
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"debug/gosym"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"os/signal"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/frame"
 | 
			
		||||
	"github.com/derekparker/delve/dwarf/line"
 | 
			
		||||
	"golang.org/x/debug/elf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
 | 
			
		||||
	isatty "github.com/mattn/go-isatty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Process statuses
 | 
			
		||||
@@ -46,55 +46,120 @@ type OSProcessDetails struct {
 | 
			
		||||
// Launch creates and begins debugging a new process. First entry in
 | 
			
		||||
// `cmd` is the program to run, and then rest are the arguments
 | 
			
		||||
// to be supplied to that process. `wd` is working directory of the program.
 | 
			
		||||
func Launch(cmd []string, wd string) (*Process, error) {
 | 
			
		||||
// If the DWARF information cannot be found in the binary, Delve will look
 | 
			
		||||
// for external debug files in the directories passed in.
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		proc *exec.Cmd
 | 
			
		||||
		err  error
 | 
			
		||||
		process *exec.Cmd
 | 
			
		||||
		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, NotExecutableErr
 | 
			
		||||
		return nil, proc.ErrNotExecutable
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !isatty.IsTerminal(os.Stdin.Fd()) {
 | 
			
		||||
		// exec.(*Process).Start will fail if we try to send a process to
 | 
			
		||||
		// foreground but we are not attached to a terminal.
 | 
			
		||||
		foreground = false
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp := New(0)
 | 
			
		||||
	dbp.common = proc.NewCommonProcess(true)
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		proc = exec.Command(cmd[0])
 | 
			
		||||
		proc.Args = cmd
 | 
			
		||||
		proc.Stdout = os.Stdout
 | 
			
		||||
		proc.Stderr = os.Stderr
 | 
			
		||||
		proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
 | 
			
		||||
		if wd != "" {
 | 
			
		||||
			proc.Dir = wd
 | 
			
		||||
		process = exec.Command(cmd[0])
 | 
			
		||||
		process.Args = cmd
 | 
			
		||||
		process.Stdout = os.Stdout
 | 
			
		||||
		process.Stderr = os.Stderr
 | 
			
		||||
		process.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true, Foreground: foreground}
 | 
			
		||||
		if foreground {
 | 
			
		||||
			signal.Ignore(syscall.SIGTTOU, syscall.SIGTTIN)
 | 
			
		||||
			process.Stdin = os.Stdin
 | 
			
		||||
		}
 | 
			
		||||
		err = proc.Start()
 | 
			
		||||
		if wd != "" {
 | 
			
		||||
			process.Dir = wd
 | 
			
		||||
		}
 | 
			
		||||
		err = process.Start()
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	dbp.Pid = proc.Process.Pid
 | 
			
		||||
	_, _, err = dbp.wait(proc.Process.Pid, 0)
 | 
			
		||||
	dbp.pid = process.Process.Pid
 | 
			
		||||
	dbp.childProcess = true
 | 
			
		||||
	_, _, err = dbp.wait(process.Process.Pid, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("waiting for target execve failed: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return initializeDebugProcess(dbp, proc.Path, false)
 | 
			
		||||
	if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to an existing process with the given PID.
 | 
			
		||||
func Attach(pid int) (*Process, error) {
 | 
			
		||||
	return initializeDebugProcess(New(pid), "", true)
 | 
			
		||||
// Attach to an existing process with the given PID. Once attached, if
 | 
			
		||||
// the DWARF information cannot be found in the binary, Delve will look
 | 
			
		||||
// for external debug files in the directories passed in.
 | 
			
		||||
func Attach(pid int, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	dbp := New(pid)
 | 
			
		||||
	dbp.common = proc.NewCommonProcess(true)
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	_, _, err = dbp.wait(dbp.pid, 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		dbp.Detach(false)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Kill kills the target process.
 | 
			
		||||
func (dbp *Process) Kill() (err error) {
 | 
			
		||||
func initialize(dbp *Process) error {
 | 
			
		||||
	comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		// removes newline character
 | 
			
		||||
		comm = bytes.TrimSuffix(comm, []byte("\n"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if comm == nil || len(comm) <= 0 {
 | 
			
		||||
		stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("could not read proc stat: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
 | 
			
		||||
		rexp, err := regexp.Compile(expr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("regexp compile error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		match := rexp.FindSubmatch(stat)
 | 
			
		||||
		if match == nil {
 | 
			
		||||
			return fmt.Errorf("no match found using regexp '%s' in /proc/%d/stat", expr, dbp.pid)
 | 
			
		||||
		}
 | 
			
		||||
		comm = match[1]
 | 
			
		||||
	}
 | 
			
		||||
	dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// kill kills the target process.
 | 
			
		||||
func (dbp *Process) kill() (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if !dbp.Threads[dbp.Pid].Stopped() {
 | 
			
		||||
	if !dbp.threads[dbp.pid].Stopped() {
 | 
			
		||||
		return errors.New("process must be stopped in order to kill it")
 | 
			
		||||
	}
 | 
			
		||||
	if err = sys.Kill(-dbp.Pid, sys.SIGKILL); err != nil {
 | 
			
		||||
	if err = sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
 | 
			
		||||
		return errors.New("could not deliver signal " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	if _, _, err = dbp.wait(dbp.Pid, 0); err != nil {
 | 
			
		||||
	if _, _, err = dbp.wait(dbp.pid, 0); err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	dbp.postExit()
 | 
			
		||||
@@ -102,13 +167,13 @@ func (dbp *Process) Kill() (err error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) requestManualStop() (err error) {
 | 
			
		||||
	return sys.Kill(dbp.Pid, sys.SIGTRAP)
 | 
			
		||||
	return sys.Kill(dbp.pid, sys.SIGTRAP)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to a newly created thread, and store that thread in our list of
 | 
			
		||||
// known threads.
 | 
			
		||||
func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
 | 
			
		||||
	if thread, ok := dbp.Threads[tid]; ok {
 | 
			
		||||
	if thread, ok := dbp.threads[tid]; ok {
 | 
			
		||||
		return thread, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -122,7 +187,7 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
 | 
			
		||||
			// if we truly don't have permissions.
 | 
			
		||||
			return nil, fmt.Errorf("could not attach to new thread %d %s", tid, err)
 | 
			
		||||
		}
 | 
			
		||||
		pid, status, err := dbp.wait(tid, 0)
 | 
			
		||||
		pid, status, err := dbp.waitFast(tid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
@@ -133,7 +198,7 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
 | 
			
		||||
 | 
			
		||||
	dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
 | 
			
		||||
	if err == syscall.ESRCH {
 | 
			
		||||
		if _, _, err = dbp.wait(tid, 0); err != nil {
 | 
			
		||||
		if _, _, err = dbp.waitFast(tid); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
 | 
			
		||||
		}
 | 
			
		||||
		dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
 | 
			
		||||
@@ -145,132 +210,44 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.Threads[tid] = &Thread{
 | 
			
		||||
	dbp.threads[tid] = &Thread{
 | 
			
		||||
		ID:  tid,
 | 
			
		||||
		dbp: dbp,
 | 
			
		||||
		os:  new(OSSpecificDetails),
 | 
			
		||||
	}
 | 
			
		||||
	if dbp.CurrentThread == nil {
 | 
			
		||||
	if dbp.currentThread == nil {
 | 
			
		||||
		dbp.SwitchThread(tid)
 | 
			
		||||
	}
 | 
			
		||||
	return dbp.Threads[tid], nil
 | 
			
		||||
	return dbp.threads[tid], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) updateThreadList() error {
 | 
			
		||||
	tids, _ := filepath.Glob(fmt.Sprintf("/proc/%d/task/*", dbp.Pid))
 | 
			
		||||
	tids, _ := filepath.Glob(fmt.Sprintf("/proc/%d/task/*", dbp.pid))
 | 
			
		||||
	for _, tidpath := range tids {
 | 
			
		||||
		tidstr := filepath.Base(tidpath)
 | 
			
		||||
		tid, err := strconv.Atoi(tidstr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := dbp.addThread(tid, tid != dbp.Pid); err != nil {
 | 
			
		||||
		if _, err := dbp.addThread(tid, tid != dbp.pid); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return linutil.ElfUpdateSharedObjects(dbp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var UnsupportedArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) findExecutable(path string) (*elf.File, string, error) {
 | 
			
		||||
func findExecutable(path string, pid int) string {
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		path = fmt.Sprintf("/proc/%d/exe", dbp.Pid)
 | 
			
		||||
	}
 | 
			
		||||
	f, err := os.OpenFile(path, 0, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, path, err
 | 
			
		||||
	}
 | 
			
		||||
	elfFile, err := elf.NewFile(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, path, err
 | 
			
		||||
	}
 | 
			
		||||
	if elfFile.Machine != elf.EM_X86_64 {
 | 
			
		||||
		return nil, path, UnsupportedArchErr
 | 
			
		||||
	}
 | 
			
		||||
	dbp.dwarf, err = elfFile.DWARF()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, path, err
 | 
			
		||||
	}
 | 
			
		||||
	return elfFile, path, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	debugFrameSec := exe.Section(".debug_frame")
 | 
			
		||||
	debugInfoSec := exe.Section(".debug_info")
 | 
			
		||||
 | 
			
		||||
	if debugFrameSec != nil && debugInfoSec != nil {
 | 
			
		||||
		debugFrame, err := exe.Section(".debug_frame").Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .debug_frame section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		dat, err := debugInfoSec.Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .debug_info section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("could not find .debug_frame section in binary")
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		symdat  []byte
 | 
			
		||||
		pclndat []byte
 | 
			
		||||
		err     error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	if sec := exe.Section(".gosymtab"); sec != nil {
 | 
			
		||||
		symdat, err = sec.Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .gosymtab section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if sec := exe.Section(".gopclntab"); sec != nil {
 | 
			
		||||
		pclndat, err = sec.Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .gopclntab section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
 | 
			
		||||
	tab, err := gosym.NewTable(symdat, pcln)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println("could not get initialize line table", err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.goSymTable = tab
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	if sec := exe.Section(".debug_line"); sec != nil {
 | 
			
		||||
		debugLine, err := exe.Section(".debug_line").Data()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("could not get .debug_line section", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		dbp.lineInfo = line.Parse(debugLine)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Println("could not find .debug_line section in binary")
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
		path = fmt.Sprintf("/proc/%d/exe", pid)
 | 
			
		||||
	}
 | 
			
		||||
	return path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
	return dbp.trapWaitInternal(pid, false)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) trapWaitInternal(pid int, halt bool) (*Thread, error) {
 | 
			
		||||
	for {
 | 
			
		||||
		wpid, status, err := dbp.wait(pid, 0)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -279,16 +256,16 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
		if wpid == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		th, ok := dbp.Threads[wpid]
 | 
			
		||||
		th, ok := dbp.threads[wpid]
 | 
			
		||||
		if ok {
 | 
			
		||||
			th.Status = (*WaitStatus)(status)
 | 
			
		||||
		}
 | 
			
		||||
		if status.Exited() {
 | 
			
		||||
			if wpid == dbp.Pid {
 | 
			
		||||
			if wpid == dbp.pid {
 | 
			
		||||
				dbp.postExit()
 | 
			
		||||
				return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
 | 
			
		||||
				return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()}
 | 
			
		||||
			}
 | 
			
		||||
			delete(dbp.Threads, wpid)
 | 
			
		||||
			delete(dbp.threads, wpid)
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
 | 
			
		||||
@@ -307,19 +284,25 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				if err == sys.ESRCH {
 | 
			
		||||
					// thread died while we were adding it
 | 
			
		||||
					delete(dbp.threads, int(cloned))
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
			if halt {
 | 
			
		||||
				th.os.running = false
 | 
			
		||||
				dbp.threads[int(wpid)].os.running = false
 | 
			
		||||
				return nil, nil
 | 
			
		||||
			}
 | 
			
		||||
			if err = th.Continue(); err != nil {
 | 
			
		||||
				if err == sys.ESRCH {
 | 
			
		||||
					// thread died while we were adding it
 | 
			
		||||
					delete(dbp.Threads, th.ID)
 | 
			
		||||
					delete(dbp.threads, th.ID)
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
				return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
 | 
			
		||||
			}
 | 
			
		||||
			if err = dbp.Threads[int(wpid)].Continue(); err != nil {
 | 
			
		||||
			if err = dbp.threads[int(wpid)].Continue(); err != nil {
 | 
			
		||||
				if err != sys.ESRCH {
 | 
			
		||||
					return nil, fmt.Errorf("could not continue existing thread %d %s", wpid, err)
 | 
			
		||||
				}
 | 
			
		||||
@@ -330,20 +313,15 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
			// Sometimes we get an unknown thread, ignore it?
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if status.StopSignal() == sys.SIGTRAP && dbp.halt {
 | 
			
		||||
			th.running = false
 | 
			
		||||
			dbp.halt = false
 | 
			
		||||
			return th, nil
 | 
			
		||||
		}
 | 
			
		||||
		if status.StopSignal() == sys.SIGTRAP {
 | 
			
		||||
			th.running = false
 | 
			
		||||
		if (halt && status.StopSignal() == sys.SIGSTOP) || (status.StopSignal() == sys.SIGTRAP) {
 | 
			
		||||
			th.os.running = false
 | 
			
		||||
			return th, nil
 | 
			
		||||
		}
 | 
			
		||||
		if th != nil {
 | 
			
		||||
			// TODO(dp) alert user about unexpected signals here.
 | 
			
		||||
			if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
 | 
			
		||||
				if err == sys.ESRCH {
 | 
			
		||||
					return nil, ProcessExitedError{Pid: dbp.Pid}
 | 
			
		||||
					return nil, proc.ErrProcessExited{Pid: dbp.pid}
 | 
			
		||||
				}
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
@@ -351,35 +329,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
 | 
			
		||||
	defer wg.Done()
 | 
			
		||||
 | 
			
		||||
	comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.Pid))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		// removes newline character
 | 
			
		||||
		comm = bytes.TrimSuffix(comm, []byte("\n"))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if comm == nil || len(comm) <= 0 {
 | 
			
		||||
		stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.Pid))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Could not read proc stat: %v\n", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.Pid)
 | 
			
		||||
		rexp, err := regexp.Compile(expr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Regexp compile error: %v\n", err)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		match := rexp.FindSubmatch(stat)
 | 
			
		||||
		if match == nil {
 | 
			
		||||
			fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.Pid)
 | 
			
		||||
			os.Exit(1)
 | 
			
		||||
		}
 | 
			
		||||
		comm = match[1]
 | 
			
		||||
	}
 | 
			
		||||
	dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
 | 
			
		||||
func (dbp *Process) loadProcessInformation() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func status(pid int, comm string) rune {
 | 
			
		||||
@@ -402,9 +352,16 @@ func status(pid int, comm string) rune {
 | 
			
		||||
	return state
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// waitFast is like wait but does not handle process-exit correctly
 | 
			
		||||
func (dbp *Process) waitFast(pid int) (int, *sys.WaitStatus, error) {
 | 
			
		||||
	var s sys.WaitStatus
 | 
			
		||||
	wpid, err := sys.Wait4(pid, &s, sys.WALL, nil)
 | 
			
		||||
	return wpid, &s, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
			
		||||
	var s sys.WaitStatus
 | 
			
		||||
	if (pid != dbp.Pid) || (options != 0) {
 | 
			
		||||
	if (pid != dbp.pid) || (options != 0) {
 | 
			
		||||
		wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
 | 
			
		||||
		return wpid, &s, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -434,24 +391,12 @@ func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
 | 
			
		||||
	for _, th := range dbp.Threads {
 | 
			
		||||
		if th.CurrentBreakpoint == nil {
 | 
			
		||||
			err := th.SetCurrentBreakpoint()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) exitGuard(err error) error {
 | 
			
		||||
	if err != sys.ESRCH {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if status(dbp.Pid, dbp.os.comm) == StatusZombie {
 | 
			
		||||
		_, err := dbp.trapWait(-1)
 | 
			
		||||
	if status(dbp.pid, dbp.os.comm) == StatusZombie {
 | 
			
		||||
		_, err := dbp.trapWaitInternal(-1, false)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -460,16 +405,16 @@ func (dbp *Process) exitGuard(err error) error {
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) resume() error {
 | 
			
		||||
	// all threads stopped over a breakpoint are made to step over it
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
		if thread.CurrentBreakpoint != nil {
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		if thread.CurrentBreakpoint.Breakpoint != nil {
 | 
			
		||||
			if err := thread.StepInstruction(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			thread.CurrentBreakpoint = nil
 | 
			
		||||
			thread.CurrentBreakpoint.Clear()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	// everything is resumed
 | 
			
		||||
	for _, thread := range dbp.Threads {
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		if err := thread.resume(); err != nil && err != sys.ESRCH {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -477,6 +422,84 @@ func (dbp *Process) resume() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stop stops all running threads threads and sets breakpoints
 | 
			
		||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
	for _, th := range dbp.threads {
 | 
			
		||||
		if !th.Stopped() {
 | 
			
		||||
			if err := th.stop(); err != nil {
 | 
			
		||||
				return dbp.exitGuard(err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// wait for all threads to stop
 | 
			
		||||
	for {
 | 
			
		||||
		allstopped := true
 | 
			
		||||
		for _, th := range dbp.threads {
 | 
			
		||||
			if th.os.running {
 | 
			
		||||
				allstopped = false
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if allstopped {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		_, err := dbp.trapWaitInternal(-1, true)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := linutil.ElfUpdateSharedObjects(dbp); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set breakpoints on all threads
 | 
			
		||||
	for _, th := range dbp.threads {
 | 
			
		||||
		if th.CurrentBreakpoint.Breakpoint == nil {
 | 
			
		||||
			if err := th.SetCurrentBreakpoint(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	for threadID := range dbp.threads {
 | 
			
		||||
		err := PtraceDetach(threadID, 0)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if kill {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	// For some reason the process will sometimes enter stopped state after a
 | 
			
		||||
	// detach, this doesn't happen immediately either.
 | 
			
		||||
	// We have to wait a bit here, then check if the main thread is stopped and
 | 
			
		||||
	// SIGCONT it if it is.
 | 
			
		||||
	time.Sleep(50 * time.Millisecond)
 | 
			
		||||
	if s := status(dbp.pid, dbp.os.comm); s == 'T' {
 | 
			
		||||
		sys.Kill(dbp.pid, sys.SIGCONT)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EntryPoint will return the process entry point address, useful for
 | 
			
		||||
// debugging PIEs.
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	auxvbuf, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/auxv", dbp.pid))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, fmt.Errorf("could not read auxiliary vector: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return linutil.EntryPointFromAuxvAMD64(auxvbuf), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func killProcess(pid int) error {
 | 
			
		||||
	return sys.Kill(pid, sys.SIGINT)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										495
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/proc_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										495
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/proc_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,495 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"debug/pe"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/windows"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OSProcessDetails holds Windows specific information.
 | 
			
		||||
type OSProcessDetails struct {
 | 
			
		||||
	hProcess    syscall.Handle
 | 
			
		||||
	breakThread int
 | 
			
		||||
	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.
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
 | 
			
		||||
	argv0Go, err := filepath.Abs(cmd[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		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()
 | 
			
		||||
 | 
			
		||||
	var p *os.Process
 | 
			
		||||
	dbp := New(0)
 | 
			
		||||
	dbp.common = proc.NewCommonProcess(true)
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		attr := &os.ProcAttr{
 | 
			
		||||
			Dir:   wd,
 | 
			
		||||
			Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
 | 
			
		||||
			Sys: &syscall.SysProcAttr{
 | 
			
		||||
				CreationFlags: _DEBUG_ONLY_THIS_PROCESS,
 | 
			
		||||
			},
 | 
			
		||||
		}
 | 
			
		||||
		p, err = os.StartProcess(argv0Go, cmd, attr)
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	defer p.Release()
 | 
			
		||||
 | 
			
		||||
	dbp.pid = p.Pid
 | 
			
		||||
	dbp.childProcess = true
 | 
			
		||||
 | 
			
		||||
	if err = dbp.initialize(argv0Go, []string{}); err != nil {
 | 
			
		||||
		dbp.Detach(true)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initialize(dbp *Process) error {
 | 
			
		||||
	// It should not actually be possible for the
 | 
			
		||||
	// call to waitForDebugEvent to fail, since Windows
 | 
			
		||||
	// will always fire a CREATE_PROCESS_DEBUG_EVENT event
 | 
			
		||||
	// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
 | 
			
		||||
	// Attaching with DebugActiveProcess has similar effect.
 | 
			
		||||
	var err error
 | 
			
		||||
	var tid, exitCode int
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if tid == 0 {
 | 
			
		||||
		dbp.postExit()
 | 
			
		||||
		return proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
 | 
			
		||||
	}
 | 
			
		||||
	// Suspend all threads so that the call to _ContinueDebugEvent will
 | 
			
		||||
	// not resume the target.
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		_, err := _SuspendThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
 | 
			
		||||
	})
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findExePath searches for process pid, and returns its executable path.
 | 
			
		||||
func findExePath(pid int) (string, error) {
 | 
			
		||||
	// Original code suggested different approach (see below).
 | 
			
		||||
	// Maybe it could be useful in the future.
 | 
			
		||||
	//
 | 
			
		||||
	// Find executable path from PID/handle on Windows:
 | 
			
		||||
	// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
 | 
			
		||||
 | 
			
		||||
	p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	defer syscall.CloseHandle(p)
 | 
			
		||||
 | 
			
		||||
	n := uint32(128)
 | 
			
		||||
	for {
 | 
			
		||||
		buf := make([]uint16, int(n))
 | 
			
		||||
		err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
 | 
			
		||||
		switch err {
 | 
			
		||||
		case syscall.ERROR_INSUFFICIENT_BUFFER:
 | 
			
		||||
			// try bigger buffer
 | 
			
		||||
			n *= 2
 | 
			
		||||
			// but stop if it gets too big
 | 
			
		||||
			if n > 10000 {
 | 
			
		||||
				return "", err
 | 
			
		||||
			}
 | 
			
		||||
		case nil:
 | 
			
		||||
			return syscall.UTF16ToString(buf[:n]), nil
 | 
			
		||||
		default:
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to an existing process with the given PID.
 | 
			
		||||
func Attach(pid int, _ []string) (*Process, error) {
 | 
			
		||||
	// TODO: Probably should have SeDebugPrivilege before starting here.
 | 
			
		||||
	err := _DebugActiveProcess(uint32(pid))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	exepath, err := findExePath(pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	dbp := New(pid)
 | 
			
		||||
	if err = dbp.initialize(exepath, []string{}); err != nil {
 | 
			
		||||
		dbp.Detach(true)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// kill kills the process.
 | 
			
		||||
func (dbp *Process) kill() error {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p, err := os.FindProcess(dbp.pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer p.Release()
 | 
			
		||||
 | 
			
		||||
	// TODO: Should not have to ignore failures here,
 | 
			
		||||
	// but some tests appear to Kill twice causing
 | 
			
		||||
	// this to fail on second attempt.
 | 
			
		||||
	_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
 | 
			
		||||
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		dbp.waitForDebugEvent(waitBlocking | waitDontHandleExceptions)
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	p.Wait()
 | 
			
		||||
 | 
			
		||||
	dbp.postExit()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) requestManualStop() error {
 | 
			
		||||
	return _DebugBreakProcess(dbp.os.hProcess)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) updateThreadList() error {
 | 
			
		||||
	// We ignore this request since threads are being
 | 
			
		||||
	// tracked as they are created/killed in waitForDebugEvent.
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*Thread, error) {
 | 
			
		||||
	if thread, ok := dbp.threads[threadID]; ok {
 | 
			
		||||
		return thread, nil
 | 
			
		||||
	}
 | 
			
		||||
	thread := &Thread{
 | 
			
		||||
		ID:  threadID,
 | 
			
		||||
		dbp: dbp,
 | 
			
		||||
		os:  new(OSSpecificDetails),
 | 
			
		||||
	}
 | 
			
		||||
	thread.os.hThread = hThread
 | 
			
		||||
	dbp.threads[threadID] = thread
 | 
			
		||||
	if dbp.currentThread == nil {
 | 
			
		||||
		dbp.SwitchThread(thread.ID)
 | 
			
		||||
	}
 | 
			
		||||
	if suspendNewThreads {
 | 
			
		||||
		_, err := _SuspendThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return thread, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findExecutable(path string, pid int) string {
 | 
			
		||||
	return path
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type waitForDebugEventFlags int
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	waitBlocking waitForDebugEventFlags = 1 << iota
 | 
			
		||||
	waitSuspendNewThreads
 | 
			
		||||
	waitDontHandleExceptions
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const _MS_VC_EXCEPTION = 0x406D1388 // part of VisualC protocol to set thread names
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
 | 
			
		||||
	var debugEvent _DEBUG_EVENT
 | 
			
		||||
	shouldExit := false
 | 
			
		||||
	for {
 | 
			
		||||
		continueStatus := uint32(_DBG_CONTINUE)
 | 
			
		||||
		var milliseconds uint32 = 0
 | 
			
		||||
		if flags&waitBlocking != 0 {
 | 
			
		||||
			milliseconds = syscall.INFINITE
 | 
			
		||||
		}
 | 
			
		||||
		// Wait for a debug event...
 | 
			
		||||
		err := _WaitForDebugEvent(&debugEvent, milliseconds)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// ... handle each event kind ...
 | 
			
		||||
		unionPtr := unsafe.Pointer(&debugEvent.U[0])
 | 
			
		||||
		switch debugEvent.DebugEventCode {
 | 
			
		||||
		case _CREATE_PROCESS_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			hFile := debugInfo.File
 | 
			
		||||
			if hFile != 0 && hFile != syscall.InvalidHandle {
 | 
			
		||||
				err = syscall.CloseHandle(hFile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return 0, 0, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			dbp.os.entryPoint = uint64(debugInfo.BaseOfImage)
 | 
			
		||||
			dbp.os.hProcess = debugInfo.Process
 | 
			
		||||
			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, 0, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case _CREATE_THREAD_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return 0, 0, err
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case _EXIT_THREAD_DEBUG_EVENT:
 | 
			
		||||
			delete(dbp.threads, int(debugEvent.ThreadId))
 | 
			
		||||
			break
 | 
			
		||||
		case _OUTPUT_DEBUG_STRING_EVENT:
 | 
			
		||||
			//TODO: Handle debug output strings
 | 
			
		||||
			break
 | 
			
		||||
		case _LOAD_DLL_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			hFile := debugInfo.File
 | 
			
		||||
			if hFile != 0 && hFile != syscall.InvalidHandle {
 | 
			
		||||
				err = syscall.CloseHandle(hFile)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return 0, 0, err
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			break
 | 
			
		||||
		case _UNLOAD_DLL_DEBUG_EVENT:
 | 
			
		||||
			break
 | 
			
		||||
		case _RIP_EVENT:
 | 
			
		||||
			break
 | 
			
		||||
		case _EXCEPTION_DEBUG_EVENT:
 | 
			
		||||
			if flags&waitDontHandleExceptions != 0 {
 | 
			
		||||
				continueStatus = _DBG_EXCEPTION_NOT_HANDLED
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			tid := int(debugEvent.ThreadId)
 | 
			
		||||
 | 
			
		||||
			switch code := exception.ExceptionRecord.ExceptionCode; code {
 | 
			
		||||
			case _EXCEPTION_BREAKPOINT:
 | 
			
		||||
 | 
			
		||||
				// check if the exception address really is a breakpoint instruction, if
 | 
			
		||||
				// it isn't we already removed that breakpoint and we can't deal with
 | 
			
		||||
				// this exception anymore.
 | 
			
		||||
				atbp := true
 | 
			
		||||
				if thread, found := dbp.threads[tid]; found {
 | 
			
		||||
					data := make([]byte, dbp.bi.Arch.BreakpointSize())
 | 
			
		||||
					if _, err := thread.ReadMemory(data, exception.ExceptionRecord.ExceptionAddress); err == nil {
 | 
			
		||||
						instr := dbp.bi.Arch.BreakpointInstruction()
 | 
			
		||||
						for i := range instr {
 | 
			
		||||
							if data[i] != instr[i] {
 | 
			
		||||
								atbp = false
 | 
			
		||||
								break
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if !atbp {
 | 
			
		||||
						thread.SetPC(uint64(exception.ExceptionRecord.ExceptionAddress))
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if atbp {
 | 
			
		||||
					dbp.os.breakThread = tid
 | 
			
		||||
					return tid, 0, nil
 | 
			
		||||
				} else {
 | 
			
		||||
					continueStatus = _DBG_CONTINUE
 | 
			
		||||
				}
 | 
			
		||||
			case _EXCEPTION_SINGLE_STEP:
 | 
			
		||||
				dbp.os.breakThread = tid
 | 
			
		||||
				return tid, 0, nil
 | 
			
		||||
			case _MS_VC_EXCEPTION:
 | 
			
		||||
				// This exception is sent to set the thread name in VisualC, we should
 | 
			
		||||
				// mask it or it might crash the program.
 | 
			
		||||
				continueStatus = _DBG_CONTINUE
 | 
			
		||||
			default:
 | 
			
		||||
				continueStatus = _DBG_EXCEPTION_NOT_HANDLED
 | 
			
		||||
			}
 | 
			
		||||
		case _EXIT_PROCESS_DEBUG_EVENT:
 | 
			
		||||
			debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
 | 
			
		||||
			exitCode = int(debugInfo.ExitCode)
 | 
			
		||||
			shouldExit = true
 | 
			
		||||
		default:
 | 
			
		||||
			return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// .. and then continue unless we received an event that indicated we should break into debugger.
 | 
			
		||||
		err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return 0, 0, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if shouldExit {
 | 
			
		||||
			return 0, exitCode, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
	var err error
 | 
			
		||||
	var tid, exitCode int
 | 
			
		||||
	dbp.execPtraceFunc(func() {
 | 
			
		||||
		tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if tid == 0 {
 | 
			
		||||
		dbp.postExit()
 | 
			
		||||
		return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
 | 
			
		||||
	}
 | 
			
		||||
	th := dbp.threads[tid]
 | 
			
		||||
	return th, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
			
		||||
	return 0, nil, fmt.Errorf("not implemented: wait")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) exitGuard(err error) error {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) resume() error {
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		if thread.CurrentBreakpoint.Breakpoint != nil {
 | 
			
		||||
			if err := thread.StepInstruction(); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			thread.CurrentBreakpoint.Clear()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		_, err := _ResumeThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// stop stops all running threads threads and sets breakpoints
 | 
			
		||||
func (dbp *Process) stop(trapthread *Thread) (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
		return &proc.ErrProcessExited{Pid: dbp.Pid()}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// While the debug event that stopped the target was being propagated
 | 
			
		||||
	// other target threads could generate other debug events.
 | 
			
		||||
	// After this function we need to know about all the threads
 | 
			
		||||
	// stopped on a breakpoint. To do that we first suspend all target
 | 
			
		||||
	// threads and then repeatedly call _ContinueDebugEvent followed by
 | 
			
		||||
	// waitForDebugEvent in non-blocking mode.
 | 
			
		||||
	// We need to explicitly call SuspendThread because otherwise the
 | 
			
		||||
	// call to _ContinueDebugEvent will resume execution of some of the
 | 
			
		||||
	// target threads.
 | 
			
		||||
 | 
			
		||||
	err = trapthread.SetCurrentBreakpoint()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, thread := range dbp.threads {
 | 
			
		||||
		_, err := _SuspendThread(thread.os.hThread)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		var err error
 | 
			
		||||
		var tid int
 | 
			
		||||
		dbp.execPtraceFunc(func() {
 | 
			
		||||
			err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		if tid == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		err = dbp.threads[tid].SetCurrentBreakpoint()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	if !kill {
 | 
			
		||||
		for _, thread := range dbp.threads {
 | 
			
		||||
			_, err := _ResumeThread(thread.os.hThread)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return _DebugActiveProcessStop(uint32(dbp.pid))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	return dbp.os.entryPoint, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func killProcess(pid int) error {
 | 
			
		||||
	p, err := os.FindProcess(pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	defer p.Release()
 | 
			
		||||
 | 
			
		||||
	return p.Kill()
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
package proc
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
@@ -1,11 +1,12 @@
 | 
			
		||||
package proc
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PtraceAttach executes the sys.PtraceAttach call.
 | 
			
		||||
@@ -56,9 +57,10 @@ func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
 | 
			
		||||
// See amd64_linux_fetch_inferior_registers in gdb/amd64-linux-nat.c.html
 | 
			
		||||
// and amd64_supply_xsave in gdb/amd64-tdep.c.html
 | 
			
		||||
// and Section 13.1 (and following) of Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture
 | 
			
		||||
func PtraceGetRegset(tid int) (regset PtraceXsave, err error) {
 | 
			
		||||
	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.PtraceFpRegs)), 0, 0)
 | 
			
		||||
	if err == syscall.Errno(0) {
 | 
			
		||||
func PtraceGetRegset(tid int) (regset linutil.AMD64Xstate, err error) {
 | 
			
		||||
	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.AMD64PtraceFpRegs)), 0, 0)
 | 
			
		||||
	if err == syscall.Errno(0) || err == syscall.ENODEV {
 | 
			
		||||
		// ignore ENODEV, it just means this CPU doesn't have X87 registers (??)
 | 
			
		||||
		err = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -66,31 +68,17 @@ func PtraceGetRegset(tid int) (regset PtraceXsave, err error) {
 | 
			
		||||
	iov := sys.Iovec{Base: &xstateargs[0], Len: _X86_XSTATE_MAX_SIZE}
 | 
			
		||||
	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETREGSET, uintptr(tid), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
 | 
			
		||||
	if err != syscall.Errno(0) {
 | 
			
		||||
		if err == syscall.ENODEV || err == syscall.EIO {
 | 
			
		||||
			// ignore ENODEV, it just means this CPU or kernel doesn't support XSTATE, see https://github.com/go-delve/delve/issues/1022
 | 
			
		||||
			// also ignore EIO, it means that we are running on an old kernel (pre 2.6.34) and PTRACE_GETREGSET is not implemented
 | 
			
		||||
			err = nil
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	} else {
 | 
			
		||||
		err = nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= iov.Len {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
 | 
			
		||||
	xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
 | 
			
		||||
	xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
 | 
			
		||||
 | 
			
		||||
	if xcomp_bv&(1<<63) != 0 {
 | 
			
		||||
		// compact format not supported
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if xstate_bv&(1<<2) == 0 {
 | 
			
		||||
		// AVX state not present
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:iov.Len]
 | 
			
		||||
	regset.AvxState = true
 | 
			
		||||
	copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
	regset.Xsave = xstateargs[:iov.Len]
 | 
			
		||||
	err = linutil.AMD64XstateRead(regset.Xsave, false, ®set)
 | 
			
		||||
	return regset, err
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +1,18 @@
 | 
			
		||||
package proc
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
// #include "threads_darwin.h"
 | 
			
		||||
import "C"
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"rsc.io/x86/x86asm"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Regs represents CPU registers on an AMD64 processor.
 | 
			
		||||
@@ -33,10 +39,10 @@ type Regs struct {
 | 
			
		||||
	fs     uint64
 | 
			
		||||
	gs     uint64
 | 
			
		||||
	gsBase uint64
 | 
			
		||||
	fpregs []Register
 | 
			
		||||
	fpregs []proc.Register
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Slice() []Register {
 | 
			
		||||
func (r *Regs) Slice(floatingPoint bool) []proc.Register {
 | 
			
		||||
	var regs = []struct {
 | 
			
		||||
		k string
 | 
			
		||||
		v uint64
 | 
			
		||||
@@ -64,15 +70,17 @@ func (r *Regs) Slice() []Register {
 | 
			
		||||
		{"Gs", r.gs},
 | 
			
		||||
		{"Gs_base", r.gsBase},
 | 
			
		||||
	}
 | 
			
		||||
	out := make([]Register, 0, len(regs)+len(r.fpregs))
 | 
			
		||||
	out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
 | 
			
		||||
	for _, reg := range regs {
 | 
			
		||||
		if reg.k == "Rflags" {
 | 
			
		||||
			out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
 | 
			
		||||
			out = proc.AppendEflagReg(out, reg.k, reg.v)
 | 
			
		||||
		} else {
 | 
			
		||||
			out = appendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	out = append(out, r.fpregs...)
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		out = append(out, r.fpregs...)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -88,6 +96,10 @@ func (r *Regs) SP() uint64 {
 | 
			
		||||
	return r.rsp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) BP() uint64 {
 | 
			
		||||
	return r.rbp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CX returns the value of the RCX register.
 | 
			
		||||
func (r *Regs) CX() uint64 {
 | 
			
		||||
	return r.rcx
 | 
			
		||||
@@ -100,8 +112,12 @@ func (r *Regs) TLS() uint64 {
 | 
			
		||||
	return r.gsBase
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) GAddr() (uint64, bool) {
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC sets the RIP register to the value specified by `pc`.
 | 
			
		||||
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
 | 
			
		||||
	if kret != C.KERN_SUCCESS {
 | 
			
		||||
		return fmt.Errorf("could not set pc")
 | 
			
		||||
@@ -109,6 +125,15 @@ func (r *Regs) SetPC(thread *Thread, pc uint64) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets the RSP register to the value specified by `pc`.
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) error {
 | 
			
		||||
	return errors.New("not implemented")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) error {
 | 
			
		||||
	return errors.New("not implemented")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Get(n int) (uint64, error) {
 | 
			
		||||
	reg := x86asm.Reg(n)
 | 
			
		||||
	const (
 | 
			
		||||
@@ -130,7 +155,7 @@ func (r *Regs) Get(n int) (uint64, error) {
 | 
			
		||||
	case x86asm.AH:
 | 
			
		||||
		return (r.rax >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.CH:
 | 
			
		||||
		return (r.rax >> 8) & mask8, nil
 | 
			
		||||
		return (r.rcx >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.DH:
 | 
			
		||||
		return (r.rdx >> 8) & mask8, nil
 | 
			
		||||
	case x86asm.BH:
 | 
			
		||||
@@ -263,10 +288,10 @@ func (r *Regs) Get(n int) (uint64, error) {
 | 
			
		||||
		return r.r15, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0, UnknownRegisterError
 | 
			
		||||
	return 0, proc.ErrUnknownRegister
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	var state C.x86_thread_state64_t
 | 
			
		||||
	var identity C.thread_identifier_info_data_t
 | 
			
		||||
	kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state)
 | 
			
		||||
@@ -322,42 +347,31 @@ func registers(thread *Thread, floatingPoint bool) (Registers, error) {
 | 
			
		||||
			return nil, fmt.Errorf("could not get floating point registers")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		regs.fpregs = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
 | 
			
		||||
		regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
 | 
			
		||||
		regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
 | 
			
		||||
		regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
 | 
			
		||||
		regs.fpregs = appendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
 | 
			
		||||
		regs.fpregs = appendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
 | 
			
		||||
		regs.fpregs = proc.AppendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
 | 
			
		||||
		regs.fpregs = proc.AppendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
 | 
			
		||||
		regs.fpregs = proc.AppendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
 | 
			
		||||
		regs.fpregs = proc.AppendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
 | 
			
		||||
		regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
 | 
			
		||||
		regs.fpregs = proc.AppendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
 | 
			
		||||
 | 
			
		||||
		for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} {
 | 
			
		||||
			stb := C.GoBytes(unsafe.Pointer(st), 10)
 | 
			
		||||
			mantissa := binary.LittleEndian.Uint64(stb[:8])
 | 
			
		||||
			exponent := binary.LittleEndian.Uint16(stb[8:])
 | 
			
		||||
			regs.fpregs = appendX87Reg(regs.fpregs, i, exponent, mantissa)
 | 
			
		||||
			regs.fpregs = proc.AppendX87Reg(regs.fpregs, i, exponent, mantissa)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		regs.fpregs = appendFlagReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr), mxcsrDescription, 32)
 | 
			
		||||
		regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
 | 
			
		||||
		regs.fpregs = proc.AppendMxcsrReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr))
 | 
			
		||||
		regs.fpregs = proc.AppendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
 | 
			
		||||
 | 
			
		||||
		for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} {
 | 
			
		||||
			regs.fpregs = appendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
 | 
			
		||||
			regs.fpregs = proc.AppendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return regs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) saveRegisters() (Registers, error) {
 | 
			
		||||
	kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
 | 
			
		||||
	if kret != C.KERN_SUCCESS {
 | 
			
		||||
		return nil, fmt.Errorf("could not save register contents")
 | 
			
		||||
	}
 | 
			
		||||
	return &Regs{rip: uint64(thread.os.registers.__rip), rsp: uint64(thread.os.registers.__rsp)}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) restoreRegisters() error {
 | 
			
		||||
	kret := C.set_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
 | 
			
		||||
	if kret != C.KERN_SUCCESS {
 | 
			
		||||
		return fmt.Errorf("could not save register contents")
 | 
			
		||||
	}
 | 
			
		||||
func (r *Regs) Copy() proc.Registers {
 | 
			
		||||
	//TODO(aarzilli): implement this to support function calls
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										87
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SetPC sets RIP to the value specified by 'pc'.
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	ir, err := registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*linutil.AMD64Registers)
 | 
			
		||||
	r.Regs.Rip = pc
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets RSP to the value specified by 'sp'
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) (err error) {
 | 
			
		||||
	var ir proc.Registers
 | 
			
		||||
	ir, err = registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*linutil.AMD64Registers)
 | 
			
		||||
	r.Regs.Rsp = sp
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) (err error) {
 | 
			
		||||
	var ir proc.Registers
 | 
			
		||||
	ir, err = registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*linutil.AMD64Registers)
 | 
			
		||||
	r.Regs.Rdx = dx
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		regs linutil.AMD64PtraceRegs
 | 
			
		||||
		err  error
 | 
			
		||||
	)
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, (*sys.PtraceRegs)(®s)) })
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	r := &linutil.AMD64Registers{®s, nil, nil}
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		var fpregset linutil.AMD64Xstate
 | 
			
		||||
		r.Fpregs, fpregset, err = thread.fpRegisters()
 | 
			
		||||
		r.Fpregset = &fpregset
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_X86_XSTATE_MAX_SIZE = 2688
 | 
			
		||||
	_NT_X86_XSTATE       = 0x202
 | 
			
		||||
 | 
			
		||||
	_XSAVE_HEADER_START          = 512
 | 
			
		||||
	_XSAVE_HEADER_LEN            = 64
 | 
			
		||||
	_XSAVE_EXTENDED_REGION_START = 576
 | 
			
		||||
	_XSAVE_SSE_REGION_LEN        = 416
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) fpRegisters() (regs []proc.Register, fpregs linutil.AMD64Xstate, err error) {
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
 | 
			
		||||
	regs = fpregs.Decode()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("could not get floating point registers: %v", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SetPC sets the RIP register to the value specified by `pc`.
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	context := winutil.NewCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Rip = pc
 | 
			
		||||
 | 
			
		||||
	return _SetThreadContext(thread.os.hThread, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets the RSP register to the value specified by `sp`.
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) error {
 | 
			
		||||
	context := winutil.NewCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Rsp = sp
 | 
			
		||||
 | 
			
		||||
	return _SetThreadContext(thread.os.hThread, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) error {
 | 
			
		||||
	context := winutil.NewCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Rdx = dx
 | 
			
		||||
 | 
			
		||||
	return _SetThreadContext(thread.os.hThread, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	context := winutil.NewCONTEXT()
 | 
			
		||||
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var threadInfo _THREAD_BASIC_INFORMATION
 | 
			
		||||
	status := _NtQueryInformationThread(thread.os.hThread, _ThreadBasicInformation, uintptr(unsafe.Pointer(&threadInfo)), uint32(unsafe.Sizeof(threadInfo)), nil)
 | 
			
		||||
	if !_NT_SUCCESS(status) {
 | 
			
		||||
		return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return winutil.NewAMD64Registers(context, uint64(threadInfo.TebBaseAddress), floatingPoint), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
 | 
			
		||||
 | 
			
		||||
package proc
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type _NTSTATUS int32
 | 
			
		||||
@@ -97,6 +99,11 @@ func _NT_SUCCESS(x _NTSTATUS) bool {
 | 
			
		||||
	return x >= 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// zsyscall_windows.go, an autogenerated file, wants to refer to the context
 | 
			
		||||
// structure as _CONTEXT, but we need to have it in pkg/proc/winutil.CONTEXT
 | 
			
		||||
// because it's also used on non-windows operating systems.
 | 
			
		||||
type _CONTEXT = winutil.CONTEXT
 | 
			
		||||
 | 
			
		||||
//sys	_NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) = ntdll.NtQueryInformationThread
 | 
			
		||||
//sys	_GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.GetThreadContext
 | 
			
		||||
//sys	_SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.SetThreadContext
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_CONTEXT_AMD64               = 0x100000
 | 
			
		||||
	_CONTEXT_CONTROL             = (_CONTEXT_AMD64 | 0x1)
 | 
			
		||||
	_CONTEXT_INTEGER             = (_CONTEXT_AMD64 | 0x2)
 | 
			
		||||
	_CONTEXT_SEGMENTS            = (_CONTEXT_AMD64 | 0x4)
 | 
			
		||||
	_CONTEXT_FLOATING_POINT      = (_CONTEXT_AMD64 | 0x8)
 | 
			
		||||
	_CONTEXT_DEBUG_REGISTERS     = (_CONTEXT_AMD64 | 0x10)
 | 
			
		||||
	_CONTEXT_FULL                = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT)
 | 
			
		||||
	_CONTEXT_ALL                 = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_SEGMENTS | _CONTEXT_FLOATING_POINT | _CONTEXT_DEBUG_REGISTERS)
 | 
			
		||||
	_CONTEXT_EXCEPTION_ACTIVE    = 0x8000000
 | 
			
		||||
	_CONTEXT_SERVICE_ACTIVE      = 0x10000000
 | 
			
		||||
	_CONTEXT_EXCEPTION_REQUEST   = 0x40000000
 | 
			
		||||
	_CONTEXT_EXCEPTION_REPORTING = 0x80000000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type _DEBUG_EVENT struct {
 | 
			
		||||
	DebugEventCode uint32
 | 
			
		||||
	ProcessId      uint32
 | 
			
		||||
	ThreadId       uint32
 | 
			
		||||
	_              uint32 // to align Union properly
 | 
			
		||||
	U              [160]byte
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										174
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/threads.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/threads.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,174 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Thread represents a single thread in the traced process
 | 
			
		||||
// ID represents the thread id or port, Process holds a reference to the
 | 
			
		||||
// Process struct that contains info on the process as
 | 
			
		||||
// a whole, and Status represents the last result of a `wait` call
 | 
			
		||||
// on this thread.
 | 
			
		||||
type Thread struct {
 | 
			
		||||
	ID                int                  // Thread ID or mach port
 | 
			
		||||
	Status            *WaitStatus          // Status returned from last wait call
 | 
			
		||||
	CurrentBreakpoint proc.BreakpointState // Breakpoint thread is currently stopped at
 | 
			
		||||
 | 
			
		||||
	dbp            *Process
 | 
			
		||||
	singleStepping bool
 | 
			
		||||
	os             *OSSpecificDetails
 | 
			
		||||
	common         proc.CommonThread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Continue the execution of this thread.
 | 
			
		||||
//
 | 
			
		||||
// If we are currently at a breakpoint, we'll clear it
 | 
			
		||||
// first and then resume execution. Thread will continue until
 | 
			
		||||
// it hits a breakpoint or is signaled.
 | 
			
		||||
func (t *Thread) Continue() error {
 | 
			
		||||
	pc, err := t.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	// Check whether we are stopped at a breakpoint, and
 | 
			
		||||
	// if so, single step over it before continuing.
 | 
			
		||||
	if _, ok := t.dbp.FindBreakpoint(pc); ok {
 | 
			
		||||
		if err := t.StepInstruction(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return t.resume()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepInstruction steps a single instruction.
 | 
			
		||||
//
 | 
			
		||||
// Executes exactly one instruction and then returns.
 | 
			
		||||
// If the thread is at a breakpoint, we first clear it,
 | 
			
		||||
// execute the instruction, and then replace the breakpoint.
 | 
			
		||||
// Otherwise we simply execute the next instruction.
 | 
			
		||||
func (t *Thread) StepInstruction() (err error) {
 | 
			
		||||
	t.singleStepping = true
 | 
			
		||||
	defer func() {
 | 
			
		||||
		t.singleStepping = false
 | 
			
		||||
	}()
 | 
			
		||||
	pc, err := t.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bp, ok := t.dbp.FindBreakpoint(pc)
 | 
			
		||||
	if ok {
 | 
			
		||||
		// Clear the breakpoint so that we can continue execution.
 | 
			
		||||
		err = t.ClearBreakpoint(bp)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Restore breakpoint now that we have passed it.
 | 
			
		||||
		defer func() {
 | 
			
		||||
			err = t.dbp.writeSoftwareBreakpoint(t, bp.Addr)
 | 
			
		||||
		}()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = t.singleStep()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if _, exited := err.(proc.ErrProcessExited); exited {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		return fmt.Errorf("step failed: %s", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Location returns the threads location, including the file:line
 | 
			
		||||
// of the corresponding source code, the function we're in
 | 
			
		||||
// and the current instruction address.
 | 
			
		||||
func (t *Thread) Location() (*proc.Location, error) {
 | 
			
		||||
	pc, err := t.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	f, l, fn := t.dbp.bi.PCToLine(pc)
 | 
			
		||||
	return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Arch returns the architecture the binary is
 | 
			
		||||
// compiled for and executing on.
 | 
			
		||||
func (t *Thread) Arch() proc.Arch {
 | 
			
		||||
	return t.dbp.bi.Arch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BinInfo returns information on the binary.
 | 
			
		||||
func (t *Thread) BinInfo() *proc.BinaryInfo {
 | 
			
		||||
	return t.dbp.bi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Common returns information common across Process
 | 
			
		||||
// implementations.
 | 
			
		||||
func (t *Thread) Common() *proc.CommonThread {
 | 
			
		||||
	return &t.common
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetCurrentBreakpoint sets the current breakpoint that this
 | 
			
		||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
 | 
			
		||||
func (t *Thread) SetCurrentBreakpoint() error {
 | 
			
		||||
	t.CurrentBreakpoint.Clear()
 | 
			
		||||
	pc, err := t.PC()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if bp, ok := t.dbp.FindBreakpoint(pc); ok {
 | 
			
		||||
		if err = t.SetPC(bp.Addr); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		t.CurrentBreakpoint = bp.CheckCondition(t)
 | 
			
		||||
		if t.CurrentBreakpoint.Breakpoint != nil && t.CurrentBreakpoint.Active {
 | 
			
		||||
			if g, err := proc.GetG(t); err == nil {
 | 
			
		||||
				t.CurrentBreakpoint.HitCount[g.ID]++
 | 
			
		||||
			}
 | 
			
		||||
			t.CurrentBreakpoint.TotalHitCount++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Breakpoint returns the current breakpoint that is active
 | 
			
		||||
// on this thread.
 | 
			
		||||
func (t *Thread) Breakpoint() proc.BreakpointState {
 | 
			
		||||
	return t.CurrentBreakpoint
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ThreadID returns the ID of this thread.
 | 
			
		||||
func (t *Thread) ThreadID() int {
 | 
			
		||||
	return t.ID
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearBreakpoint clears the specified breakpoint.
 | 
			
		||||
func (t *Thread) ClearBreakpoint(bp *proc.Breakpoint) error {
 | 
			
		||||
	if _, err := t.WriteMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
 | 
			
		||||
		return fmt.Errorf("could not clear breakpoint %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers obtains register values from the debugged process.
 | 
			
		||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	return registers(t, floatingPoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RestoreRegisters will set the value of the CPU registers to those
 | 
			
		||||
// passed in via 'savedRegs'.
 | 
			
		||||
func (t *Thread) RestoreRegisters(savedRegs proc.Registers) error {
 | 
			
		||||
	return t.restoreRegisters(savedRegs)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PC returns the current program counter value for this thread.
 | 
			
		||||
func (t *Thread) PC() (uint64, error) {
 | 
			
		||||
	regs, err := t.Registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return regs.PC(), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -1,3 +1,5 @@
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
#include "threads_darwin.h"
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
@@ -1,12 +1,18 @@
 | 
			
		||||
package proc
 | 
			
		||||
//+build darwin,macnative
 | 
			
		||||
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
// #include "threads_darwin.h"
 | 
			
		||||
// #include "proc_darwin.h"
 | 
			
		||||
import "C"
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WaitStatus is a synonym for the platform-specific WaitStatus
 | 
			
		||||
@@ -24,7 +30,7 @@ type OSSpecificDetails struct {
 | 
			
		||||
// be continued.
 | 
			
		||||
var ErrContinueThread = fmt.Errorf("could not continue thread")
 | 
			
		||||
 | 
			
		||||
func (t *Thread) halt() (err error) {
 | 
			
		||||
func (t *Thread) stop() (err error) {
 | 
			
		||||
	kret := C.thread_suspend(t.os.threadAct)
 | 
			
		||||
	if kret != C.KERN_SUCCESS {
 | 
			
		||||
		errStr := C.GoString(C.mach_error_string(C.mach_error_t(kret)))
 | 
			
		||||
@@ -35,7 +41,7 @@ func (t *Thread) halt() (err error) {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if _, ok := t.dbp.Threads[t.ID]; ok {
 | 
			
		||||
		if _, ok := t.dbp.threads[t.ID]; ok {
 | 
			
		||||
			err = fmt.Errorf("could not suspend thread %d %s", t.ID, errStr)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
@@ -49,7 +55,7 @@ func (t *Thread) singleStep() error {
 | 
			
		||||
		return fmt.Errorf("could not single step")
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		twthread, err := t.dbp.trapWait(t.dbp.Pid)
 | 
			
		||||
		twthread, err := t.dbp.trapWait(t.dbp.pid)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -66,10 +72,9 @@ func (t *Thread) singleStep() error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) resume() error {
 | 
			
		||||
	t.running = true
 | 
			
		||||
	// TODO(dp) set flag for ptrace stops
 | 
			
		||||
	var err error
 | 
			
		||||
	t.dbp.execPtraceFunc(func() { err = PtraceCont(t.dbp.Pid, 0) })
 | 
			
		||||
	t.dbp.execPtraceFunc(func() { err = PtraceCont(t.dbp.pid, 0) })
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -80,29 +85,35 @@ func (t *Thread) resume() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) blocked() bool {
 | 
			
		||||
func (t *Thread) Blocked() bool {
 | 
			
		||||
	// TODO(dp) cache the func pc to remove this lookup
 | 
			
		||||
	pc, err := t.PC()
 | 
			
		||||
	regs, err := t.Registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	fn := t.dbp.goSymTable.PCToFunc(pc)
 | 
			
		||||
	pc := regs.PC()
 | 
			
		||||
	fn := t.BinInfo().PCToFunc(pc)
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	switch fn.Name {
 | 
			
		||||
	case "runtime.kevent", "runtime.mach_semaphore_wait", "runtime.usleep":
 | 
			
		||||
	case "runtime.kevent", "runtime.mach_semaphore_wait", "runtime.usleep", "runtime.mach_semaphore_timedwait":
 | 
			
		||||
		return true
 | 
			
		||||
	default:
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) stopped() bool {
 | 
			
		||||
// Stopped returns whether the thread is stopped at
 | 
			
		||||
// the operating system level.
 | 
			
		||||
func (t *Thread) Stopped() bool {
 | 
			
		||||
	return C.thread_blocked(t.os.threadAct) > C.int(0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
 | 
			
		||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
 | 
			
		||||
	if t.dbp.exited {
 | 
			
		||||
		return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
 | 
			
		||||
	}
 | 
			
		||||
	if len(data) == 0 {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -117,20 +128,26 @@ func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
 | 
			
		||||
	return len(data), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
 | 
			
		||||
	if size == 0 {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
 | 
			
		||||
	if t.dbp.exited {
 | 
			
		||||
		return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
 | 
			
		||||
	}
 | 
			
		||||
	if len(buf) == 0 {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	var (
 | 
			
		||||
		buf    = make([]byte, size)
 | 
			
		||||
		vmData = unsafe.Pointer(&buf[0])
 | 
			
		||||
		vmAddr = C.mach_vm_address_t(addr)
 | 
			
		||||
		length = C.mach_msg_type_number_t(size)
 | 
			
		||||
		length = C.mach_msg_type_number_t(len(buf))
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	ret := C.read_memory(t.dbp.os.task, vmAddr, vmData, length)
 | 
			
		||||
	if ret < 0 {
 | 
			
		||||
		return nil, fmt.Errorf("could not read memory")
 | 
			
		||||
		return 0, fmt.Errorf("could not read memory")
 | 
			
		||||
	}
 | 
			
		||||
	return buf, nil
 | 
			
		||||
	return len(buf), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) restoreRegisters(sr proc.Registers) error {
 | 
			
		||||
	return errors.New("not implemented")
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user