mirror of
https://github.com/beego/bee.git
synced 2025-11-05 00:44:00 +00:00
Updated vendor
This commit is contained in:
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
|
||||
}
|
||||
197
vendor/github.com/go-delve/delve/pkg/proc/disasm_amd64.go
generated
vendored
Normal file
197
vendor/github.com/go-delve/delve/pkg/proc/disasm_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
|
||||
var maxInstructionLength uint64 = 15
|
||||
|
||||
type archInst x86asm.Inst
|
||||
|
||||
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)
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func (inst *archInst) Size() int {
|
||||
return inst.Len
|
||||
}
|
||||
|
||||
// converts PC relative arguments to absolute addresses
|
||||
func patchPCRel(pc uint64, inst *x86asm.Inst) {
|
||||
for i := range inst.Args {
|
||||
rel, isrel := inst.Args[i].(x86asm.Rel)
|
||||
if isrel {
|
||||
inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 "?"
|
||||
}
|
||||
|
||||
var text string
|
||||
|
||||
switch flavour {
|
||||
case GNUFlavour:
|
||||
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), 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var pc uint64
|
||||
var err error
|
||||
|
||||
switch arg := inst.Args[0].(type) {
|
||||
case x86asm.Imm:
|
||||
pc = uint64(arg)
|
||||
case x86asm.Reg:
|
||||
if !currentGoroutine || regs == nil {
|
||||
return nil
|
||||
}
|
||||
pc, err = regs.Get(int(arg))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
case x86asm.Mem:
|
||||
if !currentGoroutine || regs == nil {
|
||||
return nil
|
||||
}
|
||||
if arg.Segment != 0 {
|
||||
return nil
|
||||
}
|
||||
base, err1 := regs.Get(int(arg.Base))
|
||||
index, err2 := regs.Get(int(arg.Index))
|
||||
if err1 != nil || err2 != nil {
|
||||
return nil
|
||||
}
|
||||
addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
|
||||
//TODO: should this always be 64 bits instead of inst.MemBytes?
|
||||
pcbytes := make([]byte, inst.MemBytes)
|
||||
_, err := mem.ReadMemory(pcbytes, addr)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pc = binary.LittleEndian.Uint64(pcbytes)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
file, line, fn := bininfo.PCToLine(pc)
|
||||
if fn == nil {
|
||||
return &Location{PC: pc}
|
||||
}
|
||||
return &Location{PC: pc, File: file, Line: line, Fn: fn}
|
||||
}
|
||||
|
||||
type instrseq []x86asm.Op
|
||||
|
||||
// Possible stacksplit prologues are inserted by stacksplit in
|
||||
// $GOROOT/src/cmd/internal/obj/x86/obj6.go.
|
||||
// The stacksplit prologue will always begin with loading curg in CX, this
|
||||
// instruction is added by load_g_cx in the same file and is either 1 or 2
|
||||
// MOVs.
|
||||
var prologues []instrseq
|
||||
|
||||
func init() {
|
||||
var tinyStacksplit = instrseq{x86asm.CMP, x86asm.JBE}
|
||||
var smallStacksplit = instrseq{x86asm.LEA, x86asm.CMP, x86asm.JBE}
|
||||
var bigStacksplit = instrseq{x86asm.MOV, x86asm.CMP, x86asm.JE, x86asm.LEA, x86asm.SUB, x86asm.CMP, x86asm.JBE}
|
||||
var unixGetG = instrseq{x86asm.MOV}
|
||||
var windowsGetG = instrseq{x86asm.MOV, x86asm.MOV}
|
||||
|
||||
prologues = make([]instrseq, 0, 2*3)
|
||||
for _, getG := range []instrseq{unixGetG, windowsGetG} {
|
||||
for _, stacksplit := range []instrseq{tinyStacksplit, smallStacksplit, bigStacksplit} {
|
||||
prologue := make(instrseq, 0, len(getG)+len(stacksplit))
|
||||
prologue = append(prologue, getG...)
|
||||
prologue = append(prologue, stacksplit...)
|
||||
prologues = append(prologues, prologue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
if len(text) <= 0 {
|
||||
return fn.Entry, nil
|
||||
}
|
||||
|
||||
for _, prologue := range prologues {
|
||||
if len(prologue) >= len(text) {
|
||||
continue
|
||||
}
|
||||
if checkPrologue(text, prologue) {
|
||||
r := &text[len(prologue)]
|
||||
if sameline {
|
||||
if r.Loc.Line != text[0].Loc.Line {
|
||||
return fn.Entry, nil
|
||||
}
|
||||
}
|
||||
return r.Loc.PC, nil
|
||||
}
|
||||
}
|
||||
|
||||
return fn.Entry, nil
|
||||
}
|
||||
|
||||
func checkPrologue(s []AsmInstruction, prologuePattern instrseq) bool {
|
||||
line := s[0].Loc.Line
|
||||
for i, op := range prologuePattern {
|
||||
if s[i].Inst.Op != op || s[i].Loc.Line != line {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
9
vendor/github.com/go-delve/delve/pkg/proc/doc.go
generated
vendored
Normal file
9
vendor/github.com/go-delve/delve/pkg/proc/doc.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Package proc is a low-level package that provides methods to manipulate
|
||||
// the process we are debugging.
|
||||
//
|
||||
// proc implements all core functionality including:
|
||||
// * creating / attaching to a process
|
||||
// * process manipulation (step, next, continue, halt)
|
||||
// * methods to explore the memory of the process
|
||||
//
|
||||
package proc
|
||||
1515
vendor/github.com/go-delve/delve/pkg/proc/eval.go
generated
vendored
Normal file
1515
vendor/github.com/go-delve/delve/pkg/proc/eval.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
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
|
||||
}
|
||||
203
vendor/github.com/go-delve/delve/pkg/proc/moduledata.go
generated
vendored
Normal file
203
vendor/github.com/go-delve/delve/pkg/proc/moduledata.go
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// delve counterpart to runtime.moduledata
|
||||
type moduleData struct {
|
||||
types, etypes uintptr
|
||||
typemapVar *Variable
|
||||
}
|
||||
|
||||
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) {
|
||||
bi.loadModuleDataOnce.Do(func() {
|
||||
scope := globalScope(bi, mem)
|
||||
var md *Variable
|
||||
md, err = scope.findGlobal("runtime.firstmoduledata")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for md.Addr != 0 {
|
||||
var typesVar, etypesVar, nextVar, typemapVar *Variable
|
||||
var types, etypes uint64
|
||||
|
||||
if typesVar, err = md.structMember("types"); err != nil {
|
||||
return
|
||||
}
|
||||
if etypesVar, err = md.structMember("etypes"); err != nil {
|
||||
return
|
||||
}
|
||||
if nextVar, err = md.structMember("next"); err != nil {
|
||||
return
|
||||
}
|
||||
if typemapVar, err = md.structMember("typemap"); err != nil {
|
||||
return
|
||||
}
|
||||
if types, err = typesVar.asUint(); err != nil {
|
||||
return
|
||||
}
|
||||
if etypes, err = etypesVar.asUint(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
bi.moduleData = append(bi.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
|
||||
|
||||
md = nextVar.maybeDereference()
|
||||
if md.Unreadable != nil {
|
||||
err = md.Unreadable
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
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 bi.moduleData {
|
||||
if typeAddr >= bi.moduleData[i].types && typeAddr < bi.moduleData[i].etypes {
|
||||
md = &bi.moduleData[i]
|
||||
}
|
||||
}
|
||||
|
||||
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 := reflectOffsMapAccess(bi, off, mem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
|
||||
addr, _ := constant.Int64Val(v.Value)
|
||||
return v.newVariable(v.Name, uintptr(addr), rtyp, mem), nil
|
||||
}
|
||||
|
||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
res := md.types + uintptr(off)
|
||||
|
||||
return newVariable("", res, rtyp, bi, mem), nil
|
||||
}
|
||||
|
||||
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 = loadModuleData(bi, mem); err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
for _, md := range bi.moduleData {
|
||||
if typeAddr >= md.types && typeAddr < md.etypes {
|
||||
return loadName(bi, md.types+off, mem)
|
||||
}
|
||||
}
|
||||
|
||||
v, err := reflectOffsMapAccess(bi, off, mem)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
resv := v.maybeDereference()
|
||||
if resv.Unreadable != nil {
|
||||
return "", "", 0, resv.Unreadable
|
||||
}
|
||||
|
||||
return loadName(bi, resv.Addr, mem)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
reflectOffsm, err := reflectOffs.structMember("m")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem))
|
||||
}
|
||||
|
||||
const (
|
||||
// flags for the name struct (see 'type name struct' in $GOROOT/src/reflect/type.go)
|
||||
nameflagExported = 1 << 0
|
||||
nameflagHasTag = 1 << 1
|
||||
nameflagHasPkg = 1 << 2
|
||||
)
|
||||
|
||||
func loadName(bi *BinaryInfo, addr uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
|
||||
off := addr
|
||||
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])
|
||||
|
||||
rawstr := make([]byte, int(namelen))
|
||||
_, err = mem.ReadMemory(rawstr, off)
|
||||
off += uintptr(namelen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
name = string(rawstr)
|
||||
|
||||
if namedata[0]&nameflagHasTag != 0 {
|
||||
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])
|
||||
|
||||
rawstr := make([]byte, int(taglen))
|
||||
_, err = mem.ReadMemory(rawstr, off)
|
||||
off += uintptr(taglen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
tag = string(rawstr)
|
||||
}
|
||||
|
||||
if namedata[0]&nameflagHasPkg != 0 {
|
||||
pkgdata := make([]byte, 4)
|
||||
_, err = mem.ReadMemory(pkgdata, off)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
// see func pkgPath in $GOROOT/src/reflect/type.go
|
||||
copy((*[4]byte)(unsafe.Pointer(&pkgpathoff))[:], pkgdata)
|
||||
}
|
||||
|
||||
return name, tag, pkgpathoff, nil
|
||||
}
|
||||
283
vendor/github.com/go-delve/delve/pkg/proc/native/exc.h
generated
vendored
Normal file
283
vendor/github.com/go-delve/delve/pkg/proc/native/exc.h
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
#ifndef _exc_user_
|
||||
#define _exc_user_
|
||||
|
||||
/* Module exc */
|
||||
|
||||
#include <string.h>
|
||||
#include <mach/ndr.h>
|
||||
#include <mach/boolean.h>
|
||||
#include <mach/kern_return.h>
|
||||
#include <mach/notify.h>
|
||||
#include <mach/mach_types.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/mig_errors.h>
|
||||
#include <mach/port.h>
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifndef KERNEL
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<mach/mig_voucher_support.h>)
|
||||
#ifndef USING_VOUCHERS
|
||||
#define USING_VOUCHERS
|
||||
#endif
|
||||
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#define __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#endif // __has_include(<mach/mach_voucher_types.h>)
|
||||
#endif // __has_include
|
||||
#endif // !KERNEL
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
|
||||
#ifdef AUTOTEST
|
||||
#ifndef FUNCTION_PTR_T
|
||||
#define FUNCTION_PTR_T
|
||||
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
|
||||
typedef struct {
|
||||
char *name;
|
||||
function_ptr_t function;
|
||||
} function_table_entry;
|
||||
typedef function_table_entry *function_table_t;
|
||||
#endif /* FUNCTION_PTR_T */
|
||||
#endif /* AUTOTEST */
|
||||
|
||||
#ifndef exc_MSG_COUNT
|
||||
#define exc_MSG_COUNT 3
|
||||
#endif /* exc_MSG_COUNT */
|
||||
|
||||
#include <mach/std_types.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#ifdef __BeforeMigUserHeader
|
||||
__BeforeMigUserHeader
|
||||
#endif /* __BeforeMigUserHeader */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
|
||||
/* Routine exception_raise */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
);
|
||||
|
||||
/* Routine exception_raise_state */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
/* Routine exception_raise_state_identity */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
/********************** Caution **************************/
|
||||
/* The following data types should be used to calculate */
|
||||
/* maximum message sizes only. The actual message may be */
|
||||
/* smaller, and the position of the arguments within the */
|
||||
/* message layout may vary from what is presented here. */
|
||||
/* For example, if any of the arguments are variable- */
|
||||
/* sized, and less than the maximum is sent, the data */
|
||||
/* will be packed tight in the actual message to reduce */
|
||||
/* the presence of holes. */
|
||||
/********************** Caution **************************/
|
||||
|
||||
/* typedefs for all requests */
|
||||
|
||||
#ifndef __Request__exc_subsystem__defined
|
||||
#define __Request__exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
} __Request__exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Request__exc_subsystem__defined */
|
||||
|
||||
/* union of all requests */
|
||||
|
||||
#ifndef __RequestUnion__exc_subsystem__defined
|
||||
#define __RequestUnion__exc_subsystem__defined
|
||||
union __RequestUnion__exc_subsystem {
|
||||
__Request__exception_raise_t Request_exception_raise;
|
||||
__Request__exception_raise_state_t Request_exception_raise_state;
|
||||
__Request__exception_raise_state_identity_t Request_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__exc_subsystem__defined */
|
||||
/* typedefs for all replies */
|
||||
|
||||
#ifndef __Reply__exc_subsystem__defined
|
||||
#define __Reply__exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply__exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Reply__exc_subsystem__defined */
|
||||
|
||||
/* union of all replies */
|
||||
|
||||
#ifndef __ReplyUnion__exc_subsystem__defined
|
||||
#define __ReplyUnion__exc_subsystem__defined
|
||||
union __ReplyUnion__exc_subsystem {
|
||||
__Reply__exception_raise_t Reply_exception_raise;
|
||||
__Reply__exception_raise_state_t Reply_exception_raise_state;
|
||||
__Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__exc_subsystem__defined */
|
||||
|
||||
#ifndef subsystem_to_name_map_exc
|
||||
#define subsystem_to_name_map_exc \
|
||||
{ "exception_raise", 2401 },\
|
||||
{ "exception_raise_state", 2402 },\
|
||||
{ "exception_raise_state_identity", 2403 }
|
||||
#endif
|
||||
|
||||
#ifdef __AfterMigUserHeader
|
||||
__AfterMigUserHeader
|
||||
#endif /* __AfterMigUserHeader */
|
||||
|
||||
#endif /* _exc_user_ */
|
||||
770
vendor/github.com/go-delve/delve/pkg/proc/native/exc_user_darwin.c
generated
vendored
Normal file
770
vendor/github.com/go-delve/delve/pkg/proc/native/exc_user_darwin.c
generated
vendored
Normal file
@@ -0,0 +1,770 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
/*
|
||||
* IDENTIFICATION:
|
||||
* stub generated Sun Feb 22 20:54:31 2015
|
||||
* with a MiG generated by bootstrap_cmds-91
|
||||
* OPTIONS:
|
||||
*/
|
||||
#define __MIG_check__Reply__exc_subsystem__ 1
|
||||
|
||||
#include "exc.h"
|
||||
|
||||
|
||||
#ifndef mig_internal
|
||||
#define mig_internal static __inline__
|
||||
#endif /* mig_internal */
|
||||
|
||||
#ifndef mig_external
|
||||
#define mig_external
|
||||
#endif /* mig_external */
|
||||
|
||||
#if !defined(__MigTypeCheck) && defined(TypeCheck)
|
||||
#define __MigTypeCheck TypeCheck /* Legacy setting */
|
||||
#endif /* !defined(__MigTypeCheck) */
|
||||
|
||||
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
|
||||
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
|
||||
#endif /* !defined(__MigKernelSpecificCode) */
|
||||
|
||||
#ifndef LimitCheck
|
||||
#define LimitCheck 0
|
||||
#endif /* LimitCheck */
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ( ((a) < (b))? (a): (b) )
|
||||
#endif /* min */
|
||||
|
||||
#if !defined(_WALIGN_)
|
||||
#define _WALIGN_(x) (((x) + 3) & ~3)
|
||||
#endif /* !defined(_WALIGN_) */
|
||||
|
||||
#if !defined(_WALIGNSZ_)
|
||||
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
|
||||
#endif /* !defined(_WALIGNSZ_) */
|
||||
|
||||
#ifndef UseStaticTemplates
|
||||
#define UseStaticTemplates 0
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#ifndef __MachMsgErrorWithTimeout
|
||||
#define __MachMsgErrorWithTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
case MACH_SEND_TIMED_OUT: \
|
||||
case MACH_RCV_TIMED_OUT: \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithTimeout */
|
||||
|
||||
#ifndef __MachMsgErrorWithoutTimeout
|
||||
#define __MachMsgErrorWithoutTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithoutTimeout */
|
||||
|
||||
#ifndef __DeclareSendRpc
|
||||
#define __DeclareSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendRpc */
|
||||
|
||||
#ifndef __BeforeSendRpc
|
||||
#define __BeforeSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendRpc */
|
||||
|
||||
#ifndef __AfterSendRpc
|
||||
#define __AfterSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendRpc */
|
||||
|
||||
#ifndef __DeclareSendSimple
|
||||
#define __DeclareSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendSimple */
|
||||
|
||||
#ifndef __BeforeSendSimple
|
||||
#define __BeforeSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendSimple */
|
||||
|
||||
#ifndef __AfterSendSimple
|
||||
#define __AfterSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendSimple */
|
||||
|
||||
#define msgh_request_port msgh_remote_port
|
||||
#define msgh_reply_port msgh_local_port
|
||||
|
||||
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_t(__Reply__exception_raise_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_t __Reply;
|
||||
if (Out0P->Head.msgh_id != 2501) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
(Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
{
|
||||
return Out0P->RetCode;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise */
|
||||
mig_external kern_return_t exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_t__defined */
|
||||
|
||||
__DeclareSendRpc(2401, "exception_raise")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 8) + ((4 * codeCnt));
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2401;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2401, "exception_raise")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2401, "exception_raise")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_t((__Reply__exception_raise_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_t__defined) */
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_state_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_state_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_t(__Reply__exception_raise_state_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_state_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2502) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_state_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise_state */
|
||||
mig_external kern_return_t exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_state_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_state_t__defined */
|
||||
|
||||
__DeclareSendRpc(2402, "exception_raise_state")
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (4 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits =
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2402;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2402, "exception_raise_state")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2402, "exception_raise_state")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_state_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_state_t((__Reply__exception_raise_state_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_state_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_state_identity_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_identity_t(__Reply__exception_raise_state_identity_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_state_identity_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2503) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise_state_identity */
|
||||
mig_external kern_return_t exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_state_identity_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_state_identity_t__defined */
|
||||
|
||||
__DeclareSendRpc(2403, "exception_raise_state_identity")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (4 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2403;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2403, "exception_raise_state_identity")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2403, "exception_raise_state_identity")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_state_identity_t((__Reply__exception_raise_state_identity_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
114
vendor/github.com/go-delve/delve/pkg/proc/native/exec_darwin.c
generated
vendored
Normal file
114
vendor/github.com/go-delve/delve/pkg/proc/native/exec_darwin.c
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "exec_darwin.h"
|
||||
#include "stdio.h"
|
||||
|
||||
extern char** environ;
|
||||
|
||||
int
|
||||
close_exec_pipe(int fd[2]) {
|
||||
if (pipe(fd) < 0) return -1;
|
||||
if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1;
|
||||
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fork_exec(char *argv0, char **argv, int size,
|
||||
char *wd,
|
||||
task_t *task,
|
||||
mach_port_t *port_set,
|
||||
mach_port_t *exception_port,
|
||||
mach_port_t *notification_port)
|
||||
{
|
||||
// Since we're using mach exceptions instead of signals,
|
||||
// we need to coordinate between parent and child via pipes
|
||||
// to ensure that the parent has set the exception ports on
|
||||
// the child task before it execs.
|
||||
int fd[2];
|
||||
if (close_exec_pipe(fd) < 0) return -1;
|
||||
|
||||
// Create another pipe to signal the parent on exec.
|
||||
int efd[2];
|
||||
if (close_exec_pipe(efd) < 0) return -1;
|
||||
|
||||
kern_return_t kret;
|
||||
pid_t pid = fork();
|
||||
if (pid > 0) {
|
||||
// In parent.
|
||||
close(fd[0]);
|
||||
close(efd[1]);
|
||||
kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
char msg = 'c';
|
||||
write(fd[1], &msg, 1);
|
||||
close(fd[1]);
|
||||
|
||||
char w;
|
||||
size_t n = read(efd[0], &w, 1);
|
||||
close(efd[0]);
|
||||
if (n != 0) {
|
||||
// Child died, reap it.
|
||||
waitpid(pid, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
// Fork succeeded, we are in the child.
|
||||
int pret, cret;
|
||||
char sig;
|
||||
|
||||
close(fd[1]);
|
||||
read(fd[0], &sig, 1);
|
||||
close(fd[0]);
|
||||
|
||||
// Create a new process group.
|
||||
if (setpgid(0, 0) < 0) {
|
||||
perror("setpgid");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set errno to zero before a call to ptrace.
|
||||
// It is documented that ptrace can return -1 even
|
||||
// for successful calls.
|
||||
errno = 0;
|
||||
pret = ptrace(PT_TRACE_ME, 0, 0, 0);
|
||||
if (pret != 0 && errno != 0) {
|
||||
perror("ptrace");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Change working directory if wd is not empty.
|
||||
if (wd && wd[0]) {
|
||||
errno = 0;
|
||||
cret = chdir(wd);
|
||||
if (cret != 0 && errno != 0) {
|
||||
char *error_msg;
|
||||
asprintf(&error_msg, "%s '%s'", "chdir", wd);
|
||||
perror(error_msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
pret = ptrace(PT_SIGEXC, 0, 0, 0);
|
||||
if (pret != 0 && errno != 0) {
|
||||
perror("ptrace");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
// Create the child process.
|
||||
execve(argv0, argv, environ);
|
||||
|
||||
// We should never reach here, but if we did something went wrong.
|
||||
// Write a message to parent to alert that exec failed.
|
||||
char msg = 'd';
|
||||
write(efd[1], &msg, 1);
|
||||
close(efd[1]);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
12
vendor/github.com/go-delve/delve/pkg/proc/native/exec_darwin.h
generated
vendored
Normal file
12
vendor/github.com/go-delve/delve/pkg/proc/native/exec_darwin.h
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "proc_darwin.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int
|
||||
fork_exec(char *, char **, int, char *, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
|
||||
283
vendor/github.com/go-delve/delve/pkg/proc/native/mach_exc.h
generated
vendored
Normal file
283
vendor/github.com/go-delve/delve/pkg/proc/native/mach_exc.h
generated
vendored
Normal file
@@ -0,0 +1,283 @@
|
||||
#ifndef _mach_exc_user_
|
||||
#define _mach_exc_user_
|
||||
|
||||
/* Module mach_exc */
|
||||
|
||||
#include <string.h>
|
||||
#include <mach/ndr.h>
|
||||
#include <mach/boolean.h>
|
||||
#include <mach/kern_return.h>
|
||||
#include <mach/notify.h>
|
||||
#include <mach/mach_types.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/mig_errors.h>
|
||||
#include <mach/port.h>
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifndef KERNEL
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<mach/mig_voucher_support.h>)
|
||||
#ifndef USING_VOUCHERS
|
||||
#define USING_VOUCHERS
|
||||
#endif
|
||||
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#define __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#endif // __has_include(<mach/mach_voucher_types.h>)
|
||||
#endif // __has_include
|
||||
#endif // !KERNEL
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
|
||||
#ifdef AUTOTEST
|
||||
#ifndef FUNCTION_PTR_T
|
||||
#define FUNCTION_PTR_T
|
||||
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
|
||||
typedef struct {
|
||||
char *name;
|
||||
function_ptr_t function;
|
||||
} function_table_entry;
|
||||
typedef function_table_entry *function_table_t;
|
||||
#endif /* FUNCTION_PTR_T */
|
||||
#endif /* AUTOTEST */
|
||||
|
||||
#ifndef mach_exc_MSG_COUNT
|
||||
#define mach_exc_MSG_COUNT 3
|
||||
#endif /* mach_exc_MSG_COUNT */
|
||||
|
||||
#include <mach/std_types.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#ifdef __BeforeMigUserHeader
|
||||
__BeforeMigUserHeader
|
||||
#endif /* __BeforeMigUserHeader */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
|
||||
/* Routine mach_exception_raise */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
);
|
||||
|
||||
/* Routine mach_exception_raise_state */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
/* Routine mach_exception_raise_state_identity */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
/********************** Caution **************************/
|
||||
/* The following data types should be used to calculate */
|
||||
/* maximum message sizes only. The actual message may be */
|
||||
/* smaller, and the position of the arguments within the */
|
||||
/* message layout may vary from what is presented here. */
|
||||
/* For example, if any of the arguments are variable- */
|
||||
/* sized, and less than the maximum is sent, the data */
|
||||
/* will be packed tight in the actual message to reduce */
|
||||
/* the presence of holes. */
|
||||
/********************** Caution **************************/
|
||||
|
||||
/* typedefs for all requests */
|
||||
|
||||
#ifndef __Request__mach_exc_subsystem__defined
|
||||
#define __Request__mach_exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
} __Request__mach_exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__mach_exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__mach_exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Request__mach_exc_subsystem__defined */
|
||||
|
||||
/* union of all requests */
|
||||
|
||||
#ifndef __RequestUnion__mach_exc_subsystem__defined
|
||||
#define __RequestUnion__mach_exc_subsystem__defined
|
||||
union __RequestUnion__mach_exc_subsystem {
|
||||
__Request__mach_exception_raise_t Request_mach_exception_raise;
|
||||
__Request__mach_exception_raise_state_t Request_mach_exception_raise_state;
|
||||
__Request__mach_exception_raise_state_identity_t Request_mach_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
|
||||
/* typedefs for all replies */
|
||||
|
||||
#ifndef __Reply__mach_exc_subsystem__defined
|
||||
#define __Reply__mach_exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply__mach_exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__mach_exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__mach_exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Reply__mach_exc_subsystem__defined */
|
||||
|
||||
/* union of all replies */
|
||||
|
||||
#ifndef __ReplyUnion__mach_exc_subsystem__defined
|
||||
#define __ReplyUnion__mach_exc_subsystem__defined
|
||||
union __ReplyUnion__mach_exc_subsystem {
|
||||
__Reply__mach_exception_raise_t Reply_mach_exception_raise;
|
||||
__Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state;
|
||||
__Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
|
||||
|
||||
#ifndef subsystem_to_name_map_mach_exc
|
||||
#define subsystem_to_name_map_mach_exc \
|
||||
{ "mach_exception_raise", 2401 },\
|
||||
{ "mach_exception_raise_state", 2402 },\
|
||||
{ "mach_exception_raise_state_identity", 2403 }
|
||||
#endif
|
||||
|
||||
#ifdef __AfterMigUserHeader
|
||||
__AfterMigUserHeader
|
||||
#endif /* __AfterMigUserHeader */
|
||||
|
||||
#endif /* _mach_exc_user_ */
|
||||
770
vendor/github.com/go-delve/delve/pkg/proc/native/mach_exc_user_darwin.c
generated
vendored
Normal file
770
vendor/github.com/go-delve/delve/pkg/proc/native/mach_exc_user_darwin.c
generated
vendored
Normal file
@@ -0,0 +1,770 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
/*
|
||||
* IDENTIFICATION:
|
||||
* stub generated Sat Feb 21 18:10:52 2015
|
||||
* with a MiG generated by bootstrap_cmds-91
|
||||
* OPTIONS:
|
||||
*/
|
||||
#define __MIG_check__Reply__mach_exc_subsystem__ 1
|
||||
|
||||
#include "mach_exc.h"
|
||||
|
||||
|
||||
#ifndef mig_internal
|
||||
#define mig_internal static __inline__
|
||||
#endif /* mig_internal */
|
||||
|
||||
#ifndef mig_external
|
||||
#define mig_external
|
||||
#endif /* mig_external */
|
||||
|
||||
#if !defined(__MigTypeCheck) && defined(TypeCheck)
|
||||
#define __MigTypeCheck TypeCheck /* Legacy setting */
|
||||
#endif /* !defined(__MigTypeCheck) */
|
||||
|
||||
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
|
||||
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
|
||||
#endif /* !defined(__MigKernelSpecificCode) */
|
||||
|
||||
#ifndef LimitCheck
|
||||
#define LimitCheck 0
|
||||
#endif /* LimitCheck */
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ( ((a) < (b))? (a): (b) )
|
||||
#endif /* min */
|
||||
|
||||
#if !defined(_WALIGN_)
|
||||
#define _WALIGN_(x) (((x) + 3) & ~3)
|
||||
#endif /* !defined(_WALIGN_) */
|
||||
|
||||
#if !defined(_WALIGNSZ_)
|
||||
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
|
||||
#endif /* !defined(_WALIGNSZ_) */
|
||||
|
||||
#ifndef UseStaticTemplates
|
||||
#define UseStaticTemplates 0
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#ifndef __MachMsgErrorWithTimeout
|
||||
#define __MachMsgErrorWithTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
case MACH_SEND_TIMED_OUT: \
|
||||
case MACH_RCV_TIMED_OUT: \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithTimeout */
|
||||
|
||||
#ifndef __MachMsgErrorWithoutTimeout
|
||||
#define __MachMsgErrorWithoutTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithoutTimeout */
|
||||
|
||||
#ifndef __DeclareSendRpc
|
||||
#define __DeclareSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendRpc */
|
||||
|
||||
#ifndef __BeforeSendRpc
|
||||
#define __BeforeSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendRpc */
|
||||
|
||||
#ifndef __AfterSendRpc
|
||||
#define __AfterSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendRpc */
|
||||
|
||||
#ifndef __DeclareSendSimple
|
||||
#define __DeclareSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendSimple */
|
||||
|
||||
#ifndef __BeforeSendSimple
|
||||
#define __BeforeSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendSimple */
|
||||
|
||||
#ifndef __AfterSendSimple
|
||||
#define __AfterSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendSimple */
|
||||
|
||||
#define msgh_request_port msgh_remote_port
|
||||
#define msgh_reply_port msgh_local_port
|
||||
|
||||
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_t(__Reply__mach_exception_raise_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_t __Reply;
|
||||
if (Out0P->Head.msgh_id != 2505) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
(Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
{
|
||||
return Out0P->RetCode;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise */
|
||||
mig_external kern_return_t mach_exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_t__defined */
|
||||
|
||||
__DeclareSendRpc(2405, "mach_exception_raise")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 16) + ((8 * codeCnt));
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2405;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2405, "mach_exception_raise")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2405, "mach_exception_raise")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_t((__Reply__mach_exception_raise_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_t__defined) */
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_state_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_state_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_state_t(__Reply__mach_exception_raise_state_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_state_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2506) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_state_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise_state */
|
||||
mig_external kern_return_t mach_exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_state_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_state_t__defined */
|
||||
|
||||
__DeclareSendRpc(2406, "mach_exception_raise_state")
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (8 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 912) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 16);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits =
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2406;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2406, "mach_exception_raise_state")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2406, "mach_exception_raise_state")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_state_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_state_t((__Reply__mach_exception_raise_state_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_state_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_state_identity_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_state_identity_t(__Reply__mach_exception_raise_state_identity_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_state_identity_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2507) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise_state_identity */
|
||||
mig_external kern_return_t mach_exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_state_identity_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_state_identity_t__defined */
|
||||
|
||||
__DeclareSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (8 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 912) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 16);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2407;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_state_identity_t((__Reply__mach_exception_raise_state_identity_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
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
|
||||
}
|
||||
233
vendor/github.com/go-delve/delve/pkg/proc/native/proc_darwin.c
generated
vendored
Normal file
233
vendor/github.com/go-delve/delve/pkg/proc/native/proc_darwin.c
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "proc_darwin.h"
|
||||
|
||||
static const unsigned char info_plist[]
|
||||
__attribute__ ((section ("__TEXT,__info_plist"),used)) =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
|
||||
" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
||||
"<plist version=\"1.0\">\n"
|
||||
"<dict>\n"
|
||||
" <key>CFBundleIdentifier</key>\n"
|
||||
" <string>org.dlv</string>\n"
|
||||
" <key>CFBundleName</key>\n"
|
||||
" <string>delve</string>\n"
|
||||
" <key>CFBundleVersion</key>\n"
|
||||
" <string>1.0</string>\n"
|
||||
" <key>SecTaskAccess</key>\n"
|
||||
" <array>\n"
|
||||
" <string>allowed</string>\n"
|
||||
" <string>debug</string>\n"
|
||||
" </array>\n"
|
||||
"</dict>\n"
|
||||
"</plist>\n";
|
||||
|
||||
kern_return_t
|
||||
acquire_mach_task(int tid,
|
||||
task_t *task,
|
||||
mach_port_t *port_set,
|
||||
mach_port_t *exception_port,
|
||||
mach_port_t *notification_port)
|
||||
{
|
||||
kern_return_t kret;
|
||||
mach_port_t prev_not;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
kret = task_for_pid(self, tid, task);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Allocate exception port.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, exception_port);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_insert_right(self, *exception_port, *exception_port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = task_set_exception_ports(*task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
|
||||
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Allocate notification port to alert of when the process dies.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, notification_port);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_insert_right(self, *notification_port, *notification_port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_request_notification(self, *task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
|
||||
MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Create port set.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_PORT_SET, port_set);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Move exception and notification ports to port set.
|
||||
kret = mach_port_move_member(self, *exception_port, *port_set);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return mach_port_move_member(self, *notification_port, *port_set);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
reset_exception_ports(task_t task, mach_port_t *exception_port, mach_port_t *notification_port) {
|
||||
kern_return_t kret;
|
||||
mach_port_t prev_not;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
kret = task_set_exception_ports(task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
|
||||
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_request_notification(self, task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
|
||||
MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
char *
|
||||
find_executable(int pid) {
|
||||
static char pathbuf[PATH_MAX];
|
||||
proc_pidpath(pid, pathbuf, PATH_MAX);
|
||||
return pathbuf;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_threads(task_t task, void *slice, int limit) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) {
|
||||
return kret;
|
||||
}
|
||||
|
||||
if (count > limit) {
|
||||
vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
return -2;
|
||||
}
|
||||
|
||||
memcpy(slice, (void*)list, count*sizeof(list[0]));
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return (kern_return_t)0;
|
||||
}
|
||||
|
||||
int
|
||||
thread_count(task_t task) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
mach_port_t
|
||||
mach_port_wait(mach_port_t port_set, task_t *task, int nonblocking) {
|
||||
kern_return_t kret;
|
||||
thread_act_t thread;
|
||||
NDR_record_t *ndr;
|
||||
integer_t *data;
|
||||
union
|
||||
{
|
||||
mach_msg_header_t hdr;
|
||||
char data[256];
|
||||
} msg;
|
||||
mach_msg_option_t opts = MACH_RCV_MSG|MACH_RCV_INTERRUPT;
|
||||
if (nonblocking) {
|
||||
opts |= MACH_RCV_TIMEOUT;
|
||||
}
|
||||
|
||||
// Wait for mach msg.
|
||||
kret = mach_msg(&msg.hdr, opts,
|
||||
0, sizeof(msg.data), port_set, 10, MACH_PORT_NULL);
|
||||
if (kret == MACH_RCV_INTERRUPTED) return kret;
|
||||
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||
|
||||
|
||||
switch (msg.hdr.msgh_id) {
|
||||
case 2401: { // Exception
|
||||
// 2401 is the exception_raise event, defined in:
|
||||
// http://opensource.apple.com/source/xnu/xnu-2422.1.72/osfmk/mach/exc.defs?txt
|
||||
// compile this file with mig to get the C version of the description
|
||||
|
||||
mach_msg_body_t *bod = (mach_msg_body_t*)(&msg.hdr + 1);
|
||||
mach_msg_port_descriptor_t *desc = (mach_msg_port_descriptor_t *)(bod + 1);
|
||||
thread = desc[0].name;
|
||||
*task = desc[1].name;
|
||||
ndr = (NDR_record_t *)(desc + 2);
|
||||
data = (integer_t *)(ndr + 1);
|
||||
|
||||
if (thread_suspend(thread) != KERN_SUCCESS) return 0;
|
||||
// Send our reply back so the kernel knows this exception has been handled.
|
||||
kret = mach_send_reply(msg.hdr);
|
||||
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||
if (data[2] == EXC_SOFT_SIGNAL) {
|
||||
if (data[3] != SIGTRAP) {
|
||||
if (thread_resume(thread) != KERN_SUCCESS) return 0;
|
||||
return mach_port_wait(port_set, task, nonblocking);
|
||||
}
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
case 72: { // Death
|
||||
// 72 is mach_notify_dead_name, defined in:
|
||||
// https://opensource.apple.com/source/xnu/xnu-1228.7.58/osfmk/mach/notify.defs?txt
|
||||
// compile this file with mig to get the C version of the description
|
||||
ndr = (NDR_record_t *)(&msg.hdr + 1);
|
||||
*task = *((mach_port_name_t *)(ndr + 1));
|
||||
return msg.hdr.msgh_local_port;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t hdr) {
|
||||
mig_reply_error_t reply;
|
||||
mach_msg_header_t *rh = &reply.Head;
|
||||
rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(hdr.msgh_bits), 0);
|
||||
rh->msgh_remote_port = hdr.msgh_remote_port;
|
||||
rh->msgh_size = (mach_msg_size_t) sizeof(mig_reply_error_t);
|
||||
rh->msgh_local_port = MACH_PORT_NULL;
|
||||
rh->msgh_id = hdr.msgh_id + 100;
|
||||
|
||||
reply.NDR = NDR_record;
|
||||
reply.RetCode = KERN_SUCCESS;
|
||||
|
||||
return mach_msg(&reply.Head, MACH_SEND_MSG|MACH_SEND_INTERRUPT, rh->msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t task, mach_port_t thread, mach_port_t exception_port, exception_type_t exception) {
|
||||
return exception_raise(exception_port, thread, task, exception, 0, 0);
|
||||
}
|
||||
|
||||
task_t
|
||||
get_task_for_pid(int pid) {
|
||||
task_t task = 0;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
task_for_pid(self, pid, &task);
|
||||
return task;
|
||||
}
|
||||
|
||||
int
|
||||
task_is_valid(task_t task) {
|
||||
struct task_basic_info info;
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
return task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS;
|
||||
}
|
||||
467
vendor/github.com/go-delve/delve/pkg/proc/native/proc_darwin.go
generated
vendored
Normal file
467
vendor/github.com/go-delve/delve/pkg/proc/native/proc_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,467 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
// #include "proc_darwin.h"
|
||||
// #include "threads_darwin.h"
|
||||
// #include "exec_darwin.h"
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Darwin specific information.
|
||||
type OSProcessDetails struct {
|
||||
task C.task_t // mach task for the debugged process.
|
||||
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.
|
||||
portSet C.mach_port_t
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process. Uses a
|
||||
// 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, 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, proc.ErrNotExecutable
|
||||
}
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Make sure the binary exists.
|
||||
if filepath.Base(cmd[0]) == cmd[0] {
|
||||
if _, err := exec.LookPath(cmd[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(argv0Go); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
argv0 := C.CString(argv0Go)
|
||||
argvSlice := make([]*C.char, 0, len(cmd)+1)
|
||||
for _, arg := range cmd {
|
||||
argvSlice = append(argvSlice, C.CString(arg))
|
||||
}
|
||||
// argv array must be null terminated.
|
||||
argvSlice = append(argvSlice, nil)
|
||||
|
||||
dbp := New(0)
|
||||
var pid int
|
||||
dbp.execPtraceFunc(func() {
|
||||
ret := C.fork_exec(argv0, &argvSlice[0], C.int(len(argvSlice)),
|
||||
C.CString(wd),
|
||||
&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
|
||||
&dbp.os.notificationPort)
|
||||
pid = int(ret)
|
||||
})
|
||||
if pid <= 0 {
|
||||
return nil, fmt.Errorf("could not fork/exec")
|
||||
}
|
||||
dbp.pid = pid
|
||||
dbp.childProcess = true
|
||||
for i := range argvSlice {
|
||||
C.free(unsafe.Pointer(argvSlice[i]))
|
||||
}
|
||||
|
||||
// Initialize enough of the Process state so that we can use resume and
|
||||
// trapWait to wait until the child process calls execve.
|
||||
|
||||
for {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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.stop(nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.os.initialized = true
|
||||
err = dbp.initialize(argv0Go, []string{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dbp.SwitchThread(trapthread.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbp, err
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int, _ []string) (*Process, error) {
|
||||
dbp := New(pid)
|
||||
|
||||
kret := C.acquire_mach_task(C.int(pid),
|
||||
&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
|
||||
&dbp.os.notificationPort)
|
||||
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not attach to %d", pid)
|
||||
}
|
||||
|
||||
dbp.os.initialized = 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) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
err = sys.Kill(-dbp.pid, sys.SIGKILL)
|
||||
if err != nil {
|
||||
return errors.New("could not deliver signal: " + err.Error())
|
||||
}
|
||||
for port := range dbp.threads {
|
||||
if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
|
||||
return errors.New("could not resume task")
|
||||
}
|
||||
}
|
||||
for {
|
||||
var task C.task_t
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
|
||||
if port == dbp.os.notificationPort {
|
||||
break
|
||||
}
|
||||
}
|
||||
dbp.postExit()
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() (err error) {
|
||||
var (
|
||||
task = C.mach_port_t(dbp.os.task)
|
||||
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")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var couldNotGetThreadCount = errors.New("could not get thread count")
|
||||
var couldNotGetThreadList = errors.New("could not get thread list")
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
return dbp.updateThreadListForTask(dbp.os.task)
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadListForTask(task C.task_t) error {
|
||||
var (
|
||||
err error
|
||||
kret C.kern_return_t
|
||||
count C.int
|
||||
list []uint32
|
||||
)
|
||||
|
||||
for {
|
||||
count = C.thread_count(task)
|
||||
if count == -1 {
|
||||
return couldNotGetThreadCount
|
||||
}
|
||||
list = make([]uint32, count)
|
||||
|
||||
// TODO(dp) might be better to malloc mem in C and then free it here
|
||||
// instead of getting count above and passing in a slice
|
||||
kret = C.get_threads(task, unsafe.Pointer(&list[0]), count)
|
||||
if kret != -2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return couldNotGetThreadList
|
||||
}
|
||||
|
||||
for _, thread := range dbp.threads {
|
||||
thread.os.exists = false
|
||||
}
|
||||
|
||||
for _, port := range list {
|
||||
thread, ok := dbp.threads[int(port)]
|
||||
if !ok {
|
||||
thread, err = dbp.addThread(int(port), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
thread.os.exists = true
|
||||
}
|
||||
|
||||
for threadID, thread := range dbp.threads {
|
||||
if !thread.os.exists {
|
||||
delete(dbp.threads, threadID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
|
||||
if thread, ok := dbp.threads[port]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
thread := &Thread{
|
||||
ID: port,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
dbp.threads[port] = thread
|
||||
thread.os.threadAct = C.thread_act_t(port)
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(thread.ID)
|
||||
}
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func findExecutable(path string, pid int) string {
|
||||
if path == "" {
|
||||
path = C.GoString(C.find_executable(C.int(pid)))
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
for {
|
||||
task := dbp.os.task
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
|
||||
|
||||
switch port {
|
||||
case dbp.os.notificationPort:
|
||||
// on macOS >= 10.12.1 the task_t changes after an execve, we could
|
||||
// receive the notification for the death of the pre-execve task_t,
|
||||
// this could also happen *before* we are notified that our task_t has
|
||||
// changed.
|
||||
if dbp.os.task != task {
|
||||
continue
|
||||
}
|
||||
if !dbp.os.initialized {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp.postExit()
|
||||
return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
|
||||
case C.MACH_RCV_INTERRUPTED:
|
||||
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_.
|
||||
continue
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
case 0:
|
||||
return nil, fmt.Errorf("error while waiting for task")
|
||||
}
|
||||
|
||||
// In macOS 10.12.1 if we received a notification for a task other than
|
||||
// the inferior's task and the inferior's task is no longer valid, this
|
||||
// means inferior called execve and its task_t changed.
|
||||
if dbp.os.task != task && C.task_is_valid(dbp.os.task) == 0 {
|
||||
dbp.os.task = task
|
||||
kret := C.reset_exception_ports(dbp.os.task, &dbp.os.exceptionPort, &dbp.os.notificationPort)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not follow task across exec: %d\n", kret)
|
||||
}
|
||||
}
|
||||
|
||||
// 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)]
|
||||
if !ok {
|
||||
dbp.stopMu.Lock()
|
||||
halt := dbp.os.halt
|
||||
dbp.stopMu.Unlock()
|
||||
if halt {
|
||||
dbp.os.halt = false
|
||||
return th, nil
|
||||
}
|
||||
if dbp.firstStart || th.singleStepping {
|
||||
dbp.firstStart = false
|
||||
return th, nil
|
||||
}
|
||||
if err := th.Continue(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return th, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) waitForStop() ([]int, error) {
|
||||
ports := make([]int, 0, len(dbp.threads))
|
||||
count := 0
|
||||
for {
|
||||
var task C.task_t
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(1))
|
||||
if port != 0 && port != dbp.os.notificationPort && port != C.MACH_RCV_INTERRUPTED {
|
||||
count = 0
|
||||
ports = append(ports, int(port))
|
||||
} else {
|
||||
n := C.num_running_threads(dbp.os.task)
|
||||
if n == 0 {
|
||||
return ports, nil
|
||||
} else if n < 0 {
|
||||
return nil, fmt.Errorf("error waiting for thread stop %d", n)
|
||||
} else if count > 16 {
|
||||
return nil, fmt.Errorf("could not stop process %d", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
var status sys.WaitStatus
|
||||
wpid, err := sys.Wait4(pid, &status, options, nil)
|
||||
return wpid, &status, err
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGINT)
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
if err != ErrContinueThread {
|
||||
return err
|
||||
}
|
||||
_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
|
||||
if werr == nil && status.Exited() {
|
||||
dbp.postExit()
|
||||
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.Breakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
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 }
|
||||
56
vendor/github.com/go-delve/delve/pkg/proc/native/proc_darwin.h
generated
vendored
Normal file
56
vendor/github.com/go-delve/delve/pkg/proc/native/proc_darwin.h
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <libproc.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include "mach_exc.h"
|
||||
#include "exc.h"
|
||||
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
boolean_t exc_server(
|
||||
mach_msg_header_t *InHeadP,
|
||||
mach_msg_header_t *OutHeadP);
|
||||
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
boolean_t mach_exc_server(
|
||||
mach_msg_header_t *InHeadP,
|
||||
mach_msg_header_t *OutHeadP);
|
||||
|
||||
kern_return_t
|
||||
acquire_mach_task(int, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
|
||||
|
||||
char *
|
||||
find_executable(int pid);
|
||||
|
||||
kern_return_t
|
||||
get_threads(task_t task, void *data,int limit);
|
||||
|
||||
int
|
||||
thread_count(task_t task);
|
||||
|
||||
mach_port_t
|
||||
mach_port_wait(mach_port_t, task_t*, int);
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t);
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t, mach_port_t, mach_port_t, exception_type_t);
|
||||
|
||||
kern_return_t
|
||||
reset_exception_ports(task_t task, mach_port_t *exception_port, mach_port_t *notification_port);
|
||||
|
||||
task_t
|
||||
get_task_for_pid(int pid);
|
||||
|
||||
int
|
||||
task_is_valid(task_t task);
|
||||
505
vendor/github.com/go-delve/delve/pkg/proc/native/proc_linux.go
generated
vendored
Normal file
505
vendor/github.com/go-delve/delve/pkg/proc/native/proc_linux.go
generated
vendored
Normal file
@@ -0,0 +1,505 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
"github.com/go-delve/delve/pkg/proc/linutil"
|
||||
|
||||
isatty "github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
// Process statuses
|
||||
const (
|
||||
StatusSleeping = 'S'
|
||||
StatusRunning = 'R'
|
||||
StatusTraceStop = 't'
|
||||
StatusZombie = 'Z'
|
||||
|
||||
// Kernel 2.6 has TraceStop as T
|
||||
// TODO(derekparker) Since this means something different based on the
|
||||
// version of the kernel ('T' is job control stop on modern 3.x+ kernels) we
|
||||
// may want to differentiate at some point.
|
||||
StatusTraceStopT = 'T'
|
||||
)
|
||||
|
||||
// OSProcessDetails contains Linux specific
|
||||
// process details.
|
||||
type OSProcessDetails struct {
|
||||
comm string
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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 (
|
||||
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, 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() {
|
||||
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
|
||||
}
|
||||
if wd != "" {
|
||||
process.Dir = wd
|
||||
}
|
||||
err = process.Start()
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
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)
|
||||
}
|
||||
if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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() {
|
||||
return errors.New("process must be stopped in order to kill it")
|
||||
}
|
||||
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 {
|
||||
return
|
||||
}
|
||||
dbp.postExit()
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() (err error) {
|
||||
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 {
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if attach {
|
||||
dbp.execPtraceFunc(func() { err = sys.PtraceAttach(tid) })
|
||||
if err != nil && err != sys.EPERM {
|
||||
// Do not return err if err == EPERM,
|
||||
// we may already be tracing this thread due to
|
||||
// PTRACE_O_TRACECLONE. We will surely blow up later
|
||||
// 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.waitFast(tid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if status.Exited() {
|
||||
return nil, fmt.Errorf("thread already exited %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
||||
if err == syscall.ESRCH {
|
||||
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) })
|
||||
if err == syscall.ESRCH {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not set options for new traced thread %d %s", tid, err)
|
||||
}
|
||||
}
|
||||
|
||||
dbp.threads[tid] = &Thread{
|
||||
ID: tid,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(tid)
|
||||
}
|
||||
return dbp.threads[tid], nil
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return linutil.ElfUpdateSharedObjects(dbp)
|
||||
}
|
||||
|
||||
func findExecutable(path string, pid int) string {
|
||||
if path == "" {
|
||||
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 {
|
||||
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
||||
}
|
||||
if wpid == 0 {
|
||||
continue
|
||||
}
|
||||
th, ok := dbp.threads[wpid]
|
||||
if ok {
|
||||
th.Status = (*WaitStatus)(status)
|
||||
}
|
||||
if status.Exited() {
|
||||
if wpid == dbp.pid {
|
||||
dbp.postExit()
|
||||
return nil, proc.ErrProcessExited{Pid: wpid, Status: status.ExitStatus()}
|
||||
}
|
||||
delete(dbp.threads, wpid)
|
||||
continue
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
||||
// A traced thread has cloned a new thread, grab the pid and
|
||||
// add it to our list of traced threads.
|
||||
var cloned uint
|
||||
dbp.execPtraceFunc(func() { cloned, err = sys.PtraceGetEventMsg(wpid) })
|
||||
if err != nil {
|
||||
if err == sys.ESRCH {
|
||||
// thread died while we were adding it
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not get event message: %s", err)
|
||||
}
|
||||
th, err = dbp.addThread(int(cloned), false)
|
||||
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)
|
||||
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 != sys.ESRCH {
|
||||
return nil, fmt.Errorf("could not continue existing thread %d %s", wpid, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if th == nil {
|
||||
// Sometimes we get an unknown thread, ignore it?
|
||||
continue
|
||||
}
|
||||
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, proc.ErrProcessExited{Pid: dbp.pid}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation() {
|
||||
}
|
||||
|
||||
func status(pid int, comm string) rune {
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
|
||||
if err != nil {
|
||||
return '\000'
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var (
|
||||
p int
|
||||
state rune
|
||||
)
|
||||
|
||||
// The second field of /proc/pid/stat is the name of the task in parenthesis.
|
||||
// The name of the task is the base name of the executable for this process limited to TASK_COMM_LEN characters
|
||||
// Since both parenthesis and spaces can appear inside the name of the task and no escaping happens we need to read the name of the executable first
|
||||
// See: include/linux/sched.c:315 and include/linux/sched.c:1510
|
||||
fmt.Fscanf(f, "%d ("+comm+") %c", &p, &state)
|
||||
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) {
|
||||
wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
|
||||
return wpid, &s, err
|
||||
}
|
||||
// If we call wait4/waitpid on a thread that is the leader of its group,
|
||||
// with options == 0, while ptracing and the thread leader has exited leaving
|
||||
// zombies of its own then waitpid hangs forever this is apparently intended
|
||||
// behaviour in the linux kernel because it's just so convenient.
|
||||
// Therefore we call wait4 in a loop with WNOHANG, sleeping a while between
|
||||
// calls and exiting when either wait4 succeeds or we find out that the thread
|
||||
// has become a zombie.
|
||||
// References:
|
||||
// https://sourceware.org/bugzilla/show_bug.cgi?id=12702
|
||||
// https://sourceware.org/bugzilla/show_bug.cgi?id=10095
|
||||
// https://sourceware.org/bugzilla/attachment.cgi?id=5685
|
||||
for {
|
||||
wpid, err := sys.Wait4(pid, &s, sys.WNOHANG|sys.WALL|options, nil)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if wpid != 0 {
|
||||
return wpid, &s, err
|
||||
}
|
||||
if status(pid, dbp.os.comm) == StatusZombie {
|
||||
return pid, nil, nil
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
if err != sys.ESRCH {
|
||||
return err
|
||||
}
|
||||
if status(dbp.pid, dbp.os.comm) == StatusZombie {
|
||||
_, err := dbp.trapWaitInternal(-1, false)
|
||||
return err
|
||||
}
|
||||
|
||||
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.Breakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint.Clear()
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
for _, thread := range dbp.threads {
|
||||
if err := thread.resume(); err != nil && err != sys.ESRCH {
|
||||
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()}
|
||||
}
|
||||
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()
|
||||
}
|
||||
30
vendor/github.com/go-delve/delve/pkg/proc/native/ptrace_darwin.go
generated
vendored
Normal file
30
vendor/github.com/go-delve/delve/pkg/proc/native/ptrace_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
import sys "golang.org/x/sys/unix"
|
||||
|
||||
// PtraceAttach executes the sys.PtraceAttach call.
|
||||
func PtraceAttach(pid int) error {
|
||||
return sys.PtraceAttach(pid)
|
||||
}
|
||||
|
||||
// PtraceDetach executes the PT_DETACH ptrace call.
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
return ptrace(sys.PT_DETACH, tid, 1, uintptr(sig))
|
||||
}
|
||||
|
||||
// PtraceCont executes the PTRACE_CONT ptrace call.
|
||||
func PtraceCont(tid, sig int) error {
|
||||
return ptrace(sys.PTRACE_CONT, tid, 1, 0)
|
||||
}
|
||||
|
||||
// PtraceSingleStep returns PT_STEP ptrace call.
|
||||
func PtraceSingleStep(tid int) error {
|
||||
return ptrace(sys.PT_STEP, tid, 1, 0)
|
||||
}
|
||||
|
||||
func ptrace(request, pid int, addr uintptr, data uintptr) (err error) {
|
||||
_, _, err = sys.Syscall6(sys.SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
|
||||
return
|
||||
}
|
||||
84
vendor/github.com/go-delve/delve/pkg/proc/native/ptrace_linux.go
generated
vendored
Normal file
84
vendor/github.com/go-delve/delve/pkg/proc/native/ptrace_linux.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc/linutil"
|
||||
)
|
||||
|
||||
// PtraceAttach executes the sys.PtraceAttach call.
|
||||
func PtraceAttach(pid int) error {
|
||||
return sys.PtraceAttach(pid)
|
||||
}
|
||||
|
||||
// PtraceDetach calls ptrace(PTRACE_DETACH).
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
_, _, err := sys.Syscall6(sys.SYS_PTRACE, sys.PTRACE_DETACH, uintptr(tid), 1, uintptr(sig), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtraceCont executes ptrace PTRACE_CONT
|
||||
func PtraceCont(tid, sig int) error {
|
||||
return sys.PtraceCont(tid, sig)
|
||||
}
|
||||
|
||||
// PtraceSingleStep executes ptrace PTRACE_SINGLE_STEP.
|
||||
func PtraceSingleStep(tid int) error {
|
||||
return sys.PtraceSingleStep(tid)
|
||||
}
|
||||
|
||||
// PtracePokeUser execute ptrace PTRACE_POKE_USER.
|
||||
func PtracePokeUser(tid int, off, addr uintptr) error {
|
||||
_, _, err := sys.Syscall6(sys.SYS_PTRACE, sys.PTRACE_POKEUSR, uintptr(tid), uintptr(off), uintptr(addr), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtracePeekUser execute ptrace PTRACE_PEEK_USER.
|
||||
func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
|
||||
var val uintptr
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_PTRACE, syscall.PTRACE_PEEKUSR, uintptr(tid), uintptr(off), uintptr(unsafe.Pointer(&val)), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// PtraceGetRegset returns floating point registers of the specified thread
|
||||
// using PTRACE.
|
||||
// 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 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
|
||||
}
|
||||
|
||||
var xstateargs [_X86_XSTATE_MAX_SIZE]byte
|
||||
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
|
||||
}
|
||||
|
||||
regset.Xsave = xstateargs[:iov.Len]
|
||||
err = linutil.AMD64XstateRead(regset.Xsave, false, ®set)
|
||||
return regset, err
|
||||
}
|
||||
377
vendor/github.com/go-delve/delve/pkg/proc/native/registers_darwin_amd64.go
generated
vendored
Normal file
377
vendor/github.com/go-delve/delve/pkg/proc/native/registers_darwin_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,377 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// Regs represents CPU registers on an AMD64 processor.
|
||||
type Regs struct {
|
||||
rax uint64
|
||||
rbx uint64
|
||||
rcx uint64
|
||||
rdx uint64
|
||||
rdi uint64
|
||||
rsi uint64
|
||||
rbp uint64
|
||||
rsp uint64
|
||||
r8 uint64
|
||||
r9 uint64
|
||||
r10 uint64
|
||||
r11 uint64
|
||||
r12 uint64
|
||||
r13 uint64
|
||||
r14 uint64
|
||||
r15 uint64
|
||||
rip uint64
|
||||
rflags uint64
|
||||
cs uint64
|
||||
fs uint64
|
||||
gs uint64
|
||||
gsBase uint64
|
||||
fpregs []proc.Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice(floatingPoint bool) []proc.Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.rip},
|
||||
{"Rsp", r.rsp},
|
||||
{"Rax", r.rax},
|
||||
{"Rbx", r.rbx},
|
||||
{"Rcx", r.rcx},
|
||||
{"Rdx", r.rdx},
|
||||
{"Rdi", r.rdi},
|
||||
{"Rsi", r.rsi},
|
||||
{"Rbp", r.rbp},
|
||||
{"R8", r.r8},
|
||||
{"R9", r.r9},
|
||||
{"R10", r.r10},
|
||||
{"R11", r.r11},
|
||||
{"R12", r.r12},
|
||||
{"R13", r.r13},
|
||||
{"R14", r.r14},
|
||||
{"R15", r.r15},
|
||||
{"Rflags", r.rflags},
|
||||
{"Cs", r.cs},
|
||||
{"Fs", r.fs},
|
||||
{"Gs", r.gs},
|
||||
{"Gs_base", r.gsBase},
|
||||
}
|
||||
out := make([]proc.Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Rflags" {
|
||||
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 current program counter
|
||||
// i.e. the RIP CPU register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.rip
|
||||
}
|
||||
|
||||
// SP returns the stack pointer location,
|
||||
// i.e. the RSP register.
|
||||
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
|
||||
}
|
||||
|
||||
// TLS returns the value of the register
|
||||
// that contains the location of the thread
|
||||
// local storage segment.
|
||||
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 (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")
|
||||
}
|
||||
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 (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.r8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.r9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.r10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.r11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.r12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.r13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.r14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.r15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.r8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.r9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.r10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.r11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.r12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.r13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.r14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.r15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.r8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.r9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.r10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.r11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.r12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.r13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.r14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.r15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.r8, nil
|
||||
case x86asm.R9:
|
||||
return r.r9, nil
|
||||
case x86asm.R10:
|
||||
return r.r10, nil
|
||||
case x86asm.R11:
|
||||
return r.r11, nil
|
||||
case x86asm.R12:
|
||||
return r.r12, nil
|
||||
case x86asm.R13:
|
||||
return r.r13, nil
|
||||
case x86asm.R14:
|
||||
return r.r14, nil
|
||||
case x86asm.R15:
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, proc.ErrUnknownRegister
|
||||
}
|
||||
|
||||
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)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get registers")
|
||||
}
|
||||
kret = C.get_identity(C.mach_port_name_t(thread.os.threadAct), &identity)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get thread identity informations")
|
||||
}
|
||||
/*
|
||||
thread_identifier_info::thread_handle contains the base of the
|
||||
thread-specific data area, which on x86 and x86_64 is the thread’s base
|
||||
address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
|
||||
thread_info_internal() gets the value from
|
||||
machine_thread::cthread_self, which is the same value used to set the
|
||||
%gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
|
||||
act_machine_switch_pcb().
|
||||
--
|
||||
comment copied from chromium's crashpad
|
||||
https://chromium.googlesource.com/crashpad/crashpad/+/master/snapshot/mac/process_reader.cc
|
||||
*/
|
||||
regs := &Regs{
|
||||
rax: uint64(state.__rax),
|
||||
rbx: uint64(state.__rbx),
|
||||
rcx: uint64(state.__rcx),
|
||||
rdx: uint64(state.__rdx),
|
||||
rdi: uint64(state.__rdi),
|
||||
rsi: uint64(state.__rsi),
|
||||
rbp: uint64(state.__rbp),
|
||||
rsp: uint64(state.__rsp),
|
||||
r8: uint64(state.__r8),
|
||||
r9: uint64(state.__r9),
|
||||
r10: uint64(state.__r10),
|
||||
r11: uint64(state.__r11),
|
||||
r12: uint64(state.__r12),
|
||||
r13: uint64(state.__r13),
|
||||
r14: uint64(state.__r14),
|
||||
r15: uint64(state.__r15),
|
||||
rip: uint64(state.__rip),
|
||||
rflags: uint64(state.__rflags),
|
||||
cs: uint64(state.__cs),
|
||||
fs: uint64(state.__fs),
|
||||
gs: uint64(state.__gs),
|
||||
gsBase: uint64(identity.thread_handle),
|
||||
}
|
||||
|
||||
if floatingPoint {
|
||||
// https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/thread_status.h?txt
|
||||
var fpstate C.x86_float_state64_t
|
||||
kret = C.get_fpu_registers(C.mach_port_name_t(thread.os.threadAct), &fpstate)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get floating point registers")
|
||||
}
|
||||
|
||||
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 = proc.AppendX87Reg(regs.fpregs, i, exponent, mantissa)
|
||||
}
|
||||
|
||||
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 = proc.AppendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
|
||||
}
|
||||
}
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
119
vendor/github.com/go-delve/delve/pkg/proc/native/syscall_windows.go
generated
vendored
Normal file
119
vendor/github.com/go-delve/delve/pkg/proc/native/syscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc/winutil"
|
||||
)
|
||||
|
||||
type _NTSTATUS int32
|
||||
|
||||
type _CLIENT_ID struct {
|
||||
UniqueProcess syscall.Handle
|
||||
UniqueThread syscall.Handle
|
||||
}
|
||||
|
||||
type _THREAD_BASIC_INFORMATION struct {
|
||||
ExitStatus _NTSTATUS
|
||||
TebBaseAddress uintptr
|
||||
ClientId _CLIENT_ID
|
||||
AffinityMask uintptr
|
||||
Priority int32
|
||||
BasePriority int32
|
||||
}
|
||||
|
||||
type _CREATE_PROCESS_DEBUG_INFO struct {
|
||||
File syscall.Handle
|
||||
Process syscall.Handle
|
||||
Thread syscall.Handle
|
||||
BaseOfImage uintptr
|
||||
DebugInfoFileOffset uint32
|
||||
DebugInfoSize uint32
|
||||
ThreadLocalBase uintptr
|
||||
StartAddress uintptr
|
||||
ImageName uintptr
|
||||
Unicode uint16
|
||||
}
|
||||
|
||||
type _CREATE_THREAD_DEBUG_INFO struct {
|
||||
Thread syscall.Handle
|
||||
ThreadLocalBase uintptr
|
||||
StartAddress uintptr
|
||||
}
|
||||
|
||||
type _EXIT_PROCESS_DEBUG_INFO struct {
|
||||
ExitCode uint32
|
||||
}
|
||||
|
||||
type _LOAD_DLL_DEBUG_INFO struct {
|
||||
File syscall.Handle
|
||||
BaseOfDll uintptr
|
||||
DebugInfoFileOffset uint32
|
||||
DebugInfoSize uint32
|
||||
ImageName uintptr
|
||||
Unicode uint16
|
||||
}
|
||||
|
||||
type _EXCEPTION_DEBUG_INFO struct {
|
||||
ExceptionRecord _EXCEPTION_RECORD
|
||||
FirstChance uint32
|
||||
}
|
||||
|
||||
type _EXCEPTION_RECORD struct {
|
||||
ExceptionCode uint32
|
||||
ExceptionFlags uint32
|
||||
ExceptionRecord *_EXCEPTION_RECORD
|
||||
ExceptionAddress uintptr
|
||||
NumberParameters uint32
|
||||
ExceptionInformation [_EXCEPTION_MAXIMUM_PARAMETERS]uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
_ThreadBasicInformation = 0
|
||||
|
||||
_DBG_CONTINUE = 0x00010002
|
||||
_DBG_EXCEPTION_NOT_HANDLED = 0x80010001
|
||||
|
||||
_EXCEPTION_DEBUG_EVENT = 1
|
||||
_CREATE_THREAD_DEBUG_EVENT = 2
|
||||
_CREATE_PROCESS_DEBUG_EVENT = 3
|
||||
_EXIT_THREAD_DEBUG_EVENT = 4
|
||||
_EXIT_PROCESS_DEBUG_EVENT = 5
|
||||
_LOAD_DLL_DEBUG_EVENT = 6
|
||||
_UNLOAD_DLL_DEBUG_EVENT = 7
|
||||
_OUTPUT_DEBUG_STRING_EVENT = 8
|
||||
_RIP_EVENT = 9
|
||||
|
||||
// DEBUG_ONLY_THIS_PROCESS tracks https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
|
||||
_DEBUG_ONLY_THIS_PROCESS = 0x00000002
|
||||
|
||||
_EXCEPTION_BREAKPOINT = 0x80000003
|
||||
_EXCEPTION_SINGLE_STEP = 0x80000004
|
||||
|
||||
_EXCEPTION_MAXIMUM_PARAMETERS = 15
|
||||
)
|
||||
|
||||
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
|
||||
//sys _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.SuspendThread
|
||||
//sys _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.ResumeThread
|
||||
//sys _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) = kernel32.ContinueDebugEvent
|
||||
//sys _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) = kernel32.WriteProcessMemory
|
||||
//sys _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) = kernel32.ReadProcessMemory
|
||||
//sys _DebugBreakProcess(process syscall.Handle) (err error) = kernel32.DebugBreakProcess
|
||||
//sys _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) = kernel32.WaitForDebugEvent
|
||||
//sys _DebugActiveProcess(processid uint32) (err error) = kernel32.DebugActiveProcess
|
||||
//sys _DebugActiveProcessStop(processid uint32) (err error) = kernel32.DebugActiveProcessStop
|
||||
//sys _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) = kernel32.QueryFullProcessImageNameW
|
||||
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
|
||||
}
|
||||
179
vendor/github.com/go-delve/delve/pkg/proc/native/threads_darwin.c
generated
vendored
Normal file
179
vendor/github.com/go-delve/delve/pkg/proc/native/threads_darwin.c
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include "threads_darwin.h"
|
||||
|
||||
int
|
||||
write_memory(task_t task, mach_vm_address_t addr, void *d, mach_msg_type_number_t len) {
|
||||
kern_return_t kret;
|
||||
vm_region_submap_short_info_data_64_t info;
|
||||
mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
|
||||
mach_vm_size_t l = len;
|
||||
mach_port_t objname;
|
||||
|
||||
if (len == 1) l = 2;
|
||||
kret = mach_vm_region((vm_map_t)task, &(mach_vm_address_t){addr}, (mach_vm_size_t*)&l, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &objname);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
// Set permissions to enable writting to this memory location
|
||||
kret = mach_vm_protect(task, addr, len, FALSE, VM_PROT_WRITE|VM_PROT_COPY|VM_PROT_READ);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
kret = mach_vm_write((vm_map_t)task, addr, (vm_offset_t)d, len);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
// Restore virtual memory permissions
|
||||
kret = mach_vm_protect(task, addr, len, FALSE, info.protection);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
read_memory(task_t task, mach_vm_address_t addr, void *d, mach_msg_type_number_t len) {
|
||||
kern_return_t kret;
|
||||
pointer_t data;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = mach_vm_read((vm_map_t)task, addr, len, &data, &count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
memcpy(d, (void *)data, len);
|
||||
|
||||
kret = vm_deallocate(task, data, len);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_registers(mach_port_name_t task, x86_thread_state64_t *state) {
|
||||
kern_return_t kret;
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
// TODO(dp) - possible memory leak - vm_deallocate state
|
||||
return thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)state, &stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_fpu_registers(mach_port_name_t task, x86_float_state64_t *state) {
|
||||
kern_return_t kret;
|
||||
mach_msg_type_number_t stateCount = x86_FLOAT_STATE64_COUNT;
|
||||
return thread_get_state(task, x86_FLOAT_STATE64, (thread_state_t)state, &stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_identity(mach_port_name_t task, thread_identifier_info_data_t *idinfo) {
|
||||
mach_msg_type_number_t idinfoCount = THREAD_IDENTIFIER_INFO_COUNT;
|
||||
return thread_info(task, THREAD_IDENTIFIER_INFO, (thread_info_t)idinfo, &idinfoCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
set_registers(mach_port_name_t task, x86_thread_state64_t *state) {
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
return thread_set_state(task, x86_THREAD_STATE64, (thread_state_t)state, stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
set_pc(thread_act_t task, uint64_t pc) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t state;
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)&state, &stateCount);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
state.__rip = pc;
|
||||
|
||||
return thread_set_state(task, x86_THREAD_STATE64, (thread_state_t)&state, stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
single_step(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t regs;
|
||||
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, &count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Set trap bit in rflags
|
||||
regs.__rflags |= 0x100UL;
|
||||
|
||||
kret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return resume_thread(thread);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
resume_thread(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
for (int i = 0; i < info.suspend_count; i++) {
|
||||
kret = thread_resume(thread);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
}
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
clear_trap_flag(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t regs;
|
||||
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, &count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Clear trap bit in rflags
|
||||
regs.__rflags ^= 0x100UL;
|
||||
|
||||
return thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, count);
|
||||
}
|
||||
|
||||
int
|
||||
thread_blocked(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return info.suspend_count;
|
||||
}
|
||||
|
||||
int
|
||||
num_running_threads(task_t task) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
int i, n = 0;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) {
|
||||
return -kret;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
thread_act_t thread = list[i];
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
|
||||
if (kret == KERN_SUCCESS) {
|
||||
if (info.suspend_count == 0) {
|
||||
++n;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return -kret;
|
||||
|
||||
return n;
|
||||
}
|
||||
153
vendor/github.com/go-delve/delve/pkg/proc/native/threads_darwin.go
generated
vendored
Normal file
153
vendor/github.com/go-delve/delve/pkg/proc/native/threads_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
package native
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
// #include "proc_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails holds information specific to the OSX/Darwin
|
||||
// operating system / kernel.
|
||||
type OSSpecificDetails struct {
|
||||
threadAct C.thread_act_t
|
||||
registers C.x86_thread_state64_t
|
||||
exists bool
|
||||
}
|
||||
|
||||
// ErrContinueThread is the error returned when a thread could not
|
||||
// be continued.
|
||||
var ErrContinueThread = fmt.Errorf("could not continue thread")
|
||||
|
||||
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)))
|
||||
// check that the thread still exists before complaining
|
||||
err2 := t.dbp.updateThreadList()
|
||||
if err2 != nil {
|
||||
err = fmt.Errorf("could not suspend thread %d %s (additionally could not update thread list: %v)", t.ID, errStr, err2)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := t.dbp.threads[t.ID]; ok {
|
||||
err = fmt.Errorf("could not suspend thread %d %s", t.ID, errStr)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
kret := C.single_step(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not single step")
|
||||
}
|
||||
for {
|
||||
twthread, err := t.dbp.trapWait(t.dbp.pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if twthread.ID == t.ID {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
kret = C.clear_trap_flag(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not clear CPU trap flag")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
// TODO(dp) set flag for ptrace stops
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.dbp.pid, 0) })
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
kret := C.resume_thread(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return ErrContinueThread
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thread) Blocked() bool {
|
||||
// TODO(dp) cache the func pc to remove this lookup
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
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", "runtime.mach_semaphore_timedwait":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var (
|
||||
vmData = unsafe.Pointer(&data[0])
|
||||
vmAddr = C.mach_vm_address_t(addr)
|
||||
length = C.mach_msg_type_number_t(len(data))
|
||||
)
|
||||
if ret := C.write_memory(t.dbp.os.task, vmAddr, vmData, length); ret < 0 {
|
||||
return 0, fmt.Errorf("could not write memory")
|
||||
}
|
||||
return len(data), 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 (
|
||||
vmData = unsafe.Pointer(&buf[0])
|
||||
vmAddr = C.mach_vm_address_t(addr)
|
||||
length = C.mach_msg_type_number_t(len(buf))
|
||||
)
|
||||
|
||||
ret := C.read_memory(t.dbp.os.task, vmAddr, vmData, length)
|
||||
if ret < 0 {
|
||||
return 0, fmt.Errorf("could not read memory")
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters(sr proc.Registers) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
43
vendor/github.com/go-delve/delve/pkg/proc/native/threads_darwin.h
generated
vendored
Normal file
43
vendor/github.com/go-delve/delve/pkg/proc/native/threads_darwin.h
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
//+build darwin,macnative
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/thread_info.h>
|
||||
|
||||
int
|
||||
write_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
|
||||
|
||||
int
|
||||
read_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
|
||||
|
||||
kern_return_t
|
||||
get_registers(mach_port_name_t, x86_thread_state64_t*);
|
||||
|
||||
kern_return_t
|
||||
get_fpu_registers(mach_port_name_t, x86_float_state64_t *);
|
||||
|
||||
kern_return_t
|
||||
set_pc(thread_act_t, uint64_t);
|
||||
|
||||
kern_return_t
|
||||
single_step(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
clear_trap_flag(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
resume_thread(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
set_registers(mach_port_name_t, x86_thread_state64_t*);
|
||||
|
||||
kern_return_t
|
||||
get_identity(mach_port_name_t, thread_identifier_info_data_t *);
|
||||
|
||||
int
|
||||
thread_blocked(thread_act_t thread);
|
||||
|
||||
int
|
||||
num_running_threads(task_t task);
|
||||
133
vendor/github.com/go-delve/delve/pkg/proc/native/threads_linux.go
generated
vendored
Normal file
133
vendor/github.com/go-delve/delve/pkg/proc/native/threads_linux.go
generated
vendored
Normal file
@@ -0,0 +1,133 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
"github.com/go-delve/delve/pkg/proc/linutil"
|
||||
)
|
||||
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails hold Linux specific
|
||||
// process details.
|
||||
type OSSpecificDetails struct {
|
||||
registers sys.PtraceRegs
|
||||
running bool
|
||||
}
|
||||
|
||||
func (t *Thread) stop() (err error) {
|
||||
err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("stop err %s on thread %d", err, t.ID)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at
|
||||
// the operating system level.
|
||||
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.os.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.waitFast(t.ID)
|
||||
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 proc.ErrProcessExited{Pid: t.dbp.pid, Status: rs}
|
||||
}
|
||||
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Thread) Blocked() bool {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
pc := regs.PC()
|
||||
fn := t.BinInfo().PCToFunc(pc)
|
||||
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
|
||||
sr := savedRegs.(*linutil.AMD64Registers)
|
||||
|
||||
var restoreRegistersErr error
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
restoreRegistersErr = sys.PtraceSetRegs(t.ID, (*sys.PtraceRegs)(sr.Regs))
|
||||
if restoreRegistersErr != nil {
|
||||
return
|
||||
}
|
||||
if sr.Fpregset.Xsave != nil {
|
||||
iov := sys.Iovec{Base: &sr.Fpregset.Xsave[0], Len: uint64(len(sr.Fpregset.Xsave))}
|
||||
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.Fpregset.AMD64PtraceFpRegs)), 0, 0)
|
||||
return
|
||||
})
|
||||
if restoreRegistersErr == syscall.Errno(0) {
|
||||
restoreRegistersErr = nil
|
||||
}
|
||||
return restoreRegistersErr
|
||||
}
|
||||
|
||||
func (t *Thread) WriteMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, addr, data) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
|
||||
if t.dbp.exited {
|
||||
return 0, proc.ErrProcessExited{Pid: t.dbp.pid}
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
t.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(t.ID, addr, data) })
|
||||
if err == nil {
|
||||
n = len(data)
|
||||
}
|
||||
return
|
||||
}
|
||||
159
vendor/github.com/go-delve/delve/pkg/proc/native/threads_windows.go
generated
vendored
Normal file
159
vendor/github.com/go-delve/delve/pkg/proc/native/threads_windows.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
"github.com/go-delve/delve/pkg/proc/winutil"
|
||||
)
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails holds information specific to the Windows
|
||||
// operating system / kernel.
|
||||
type OSSpecificDetails struct {
|
||||
hThread syscall.Handle
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
context := winutil.NewCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
// Set the processor TRAP flag
|
||||
err := _GetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.EFlags |= 0x100
|
||||
|
||||
err = _SetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = _ResumeThread(t.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
var tid, exitCode int
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = t.dbp.waitForDebugEvent(waitBlocking | waitSuspendNewThreads)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tid == 0 {
|
||||
t.dbp.postExit()
|
||||
return proc.ErrProcessExited{Pid: t.dbp.pid, Status: exitCode}
|
||||
}
|
||||
|
||||
if t.dbp.os.breakThread == t.ID {
|
||||
break
|
||||
}
|
||||
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
|
||||
})
|
||||
}
|
||||
|
||||
_, err = _SuspendThread(t.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unset the processor TRAP flag
|
||||
err = _GetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.EFlags &= ^uint32(0x100)
|
||||
|
||||
return _SetThreadContext(t.os.hThread, context)
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
||||
//thread that we last broke on.
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Thread) Blocked() bool {
|
||||
// TODO: Probably incorrect - what are the runtime functions that
|
||||
// indicate blocking on Windows?
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
pc := regs.PC()
|
||||
fn := t.BinInfo().PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.kevent", "runtime.usleep":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at the operating system
|
||||
// level. On windows this always returns true.
|
||||
func (t *Thread) Stopped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
var count uintptr
|
||||
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
var ErrShortRead = errors.New("short read")
|
||||
|
||||
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 count uintptr
|
||||
err := _ReadProcessMemory(t.dbp.os.hProcess, addr, &buf[0], uintptr(len(buf)), &count)
|
||||
if err == nil && count != uintptr(len(buf)) {
|
||||
err = ErrShortRead
|
||||
}
|
||||
return int(count), err
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
|
||||
return _SetThreadContext(t.os.hThread, savedRegs.(*winutil.AMD64Registers).Context)
|
||||
}
|
||||
181
vendor/github.com/go-delve/delve/pkg/proc/native/zsyscall_windows.go
generated
vendored
Normal file
181
vendor/github.com/go-delve/delve/pkg/proc/native/zsyscall_windows.go
generated
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package native
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread")
|
||||
procGetThreadContext = modkernel32.NewProc("GetThreadContext")
|
||||
procSetThreadContext = modkernel32.NewProc("SetThreadContext")
|
||||
procSuspendThread = modkernel32.NewProc("SuspendThread")
|
||||
procResumeThread = modkernel32.NewProc("ResumeThread")
|
||||
procContinueDebugEvent = modkernel32.NewProc("ContinueDebugEvent")
|
||||
procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory")
|
||||
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
||||
procDebugBreakProcess = modkernel32.NewProc("DebugBreakProcess")
|
||||
procWaitForDebugEvent = modkernel32.NewProc("WaitForDebugEvent")
|
||||
procDebugActiveProcess = modkernel32.NewProc("DebugActiveProcess")
|
||||
procDebugActiveProcessStop = modkernel32.NewProc("DebugActiveProcessStop")
|
||||
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
|
||||
)
|
||||
|
||||
func _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) {
|
||||
r0, _, _ := syscall.Syscall6(procNtQueryInformationThread.Addr(), 5, uintptr(threadHandle), uintptr(infoclass), uintptr(info), uintptr(infolen), uintptr(unsafe.Pointer(retlen)), 0)
|
||||
status = _NTSTATUS(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procSuspendThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||
prevsuspcount = uint32(r0)
|
||||
if prevsuspcount == 0xffffffff {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procResumeThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||
prevsuspcount = uint32(r0)
|
||||
if prevsuspcount == 0xffffffff {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procContinueDebugEvent.Addr(), 3, uintptr(processid), uintptr(threadid), uintptr(continuestatus))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procWriteProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(byteswritten)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(bytesread)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugBreakProcess(process syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugBreakProcess.Addr(), 1, uintptr(process), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procWaitForDebugEvent.Addr(), 2, uintptr(unsafe.Pointer(debugevent)), uintptr(milliseconds), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugActiveProcess(processid uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugActiveProcess.Addr(), 1, uintptr(processid), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugActiveProcessStop(processid uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugActiveProcessStop.Addr(), 1, uintptr(processid), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procQueryFullProcessImageNameW.Addr(), 4, uintptr(process), uintptr(flags), uintptr(unsafe.Pointer(exename)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
794
vendor/github.com/go-delve/delve/pkg/proc/proc.go
generated
vendored
Normal file
794
vendor/github.com/go-delve/delve/pkg/proc/proc.go
generated
vendored
Normal file
@@ -0,0 +1,794 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrNotExecutable is returned after attempting to execute a non-executable file
|
||||
// to begin a debug session.
|
||||
var ErrNotExecutable = errors.New("not an executable file")
|
||||
|
||||
// ErrNotRecorded is returned when an action is requested that is
|
||||
// only possible on recorded (traced) programs.
|
||||
var ErrNotRecorded = errors.New("not a recording")
|
||||
|
||||
const (
|
||||
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
|
||||
UnrecoveredPanic = "unrecovered-panic"
|
||||
|
||||
// FatalThrow is the name given to the breakpoint triggered when the target process dies because of a fatal runtime error
|
||||
FatalThrow = "runtime-fatal-throw"
|
||||
|
||||
unrecoveredPanicID = -1
|
||||
fatalThrowID = -2
|
||||
)
|
||||
|
||||
// ErrProcessExited indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
type ErrProcessExited struct {
|
||||
Pid int
|
||||
Status int
|
||||
}
|
||||
|
||||
func (pe ErrProcessExited) Error() string {
|
||||
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
// ProcessDetachedError indicates that we detached from the target process.
|
||||
type ProcessDetachedError struct {
|
||||
}
|
||||
|
||||
func (pe ProcessDetachedError) Error() string {
|
||||
return "detached from the process"
|
||||
}
|
||||
|
||||
// PostInitializationSetup handles all of the initialization procedures
|
||||
// that must happen after Delve creates or attaches to a process.
|
||||
func PostInitializationSetup(p Process, path string, debugInfoDirs []string, writeBreakpoint WriteBreakpointFn) error {
|
||||
entryPoint, err := p.EntryPoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.BinInfo().LoadBinaryInfo(path, entryPoint, debugInfoDirs)
|
||||
if err == nil {
|
||||
err = p.BinInfo().LoadError()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
g, _ := GetG(p.CurrentThread())
|
||||
p.SetSelectedGoroutine(g)
|
||||
|
||||
createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
|
||||
createFatalThrowBreakpoint(p, writeBreakpoint)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindFileLocation returns the PC for a given file:line.
|
||||
// Assumes that `file` is normalized to lower case and '/' on Windows.
|
||||
func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
|
||||
pc, fn, err := p.BinInfo().LineToPC(fileName, lineno)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if fn.Entry == pc {
|
||||
pc, _ = FirstPCAfterPrologue(p, fn, true)
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// ErrFunctionNotFound is returned when failing to find the
|
||||
// function named 'FuncName' within the binary.
|
||||
type ErrFunctionNotFound struct {
|
||||
FuncName string
|
||||
}
|
||||
|
||||
func (err *ErrFunctionNotFound) Error() string {
|
||||
return fmt.Sprintf("Could not find function %s\n", err.FuncName)
|
||||
}
|
||||
|
||||
// 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/go-delve/delve/issues/170
|
||||
func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
bi := p.BinInfo()
|
||||
origfn := bi.LookupFunc[funcName]
|
||||
if origfn == nil {
|
||||
return 0, &ErrFunctionNotFound{funcName}
|
||||
}
|
||||
|
||||
if firstLine {
|
||||
return FirstPCAfterPrologue(p, origfn, false)
|
||||
} else if lineOffset > 0 {
|
||||
filename, lineno := origfn.cu.lineInfo.PCToLine(origfn.Entry, origfn.Entry)
|
||||
breakAddr, _, err := bi.LineToPC(filename, lineno+lineOffset)
|
||||
return breakAddr, err
|
||||
}
|
||||
|
||||
return origfn.Entry, nil
|
||||
}
|
||||
|
||||
// FunctionReturnLocations will return a list of addresses corresponding
|
||||
// to 'ret' or 'call runtime.deferreturn'.
|
||||
func FunctionReturnLocations(p Process, funcName string) ([]uint64, error) {
|
||||
const deferReturn = "runtime.deferreturn"
|
||||
|
||||
g := p.SelectedGoroutine()
|
||||
fn, ok := p.BinInfo().LookupFunc[funcName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find function %s", funcName)
|
||||
}
|
||||
|
||||
instructions, err := Disassemble(p, g, fn.Entry, fn.End)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var addrs []uint64
|
||||
for _, instruction := range instructions {
|
||||
if instruction.IsRet() {
|
||||
addrs = append(addrs, instruction.Loc.PC)
|
||||
}
|
||||
}
|
||||
addrs = append(addrs, findDeferReturnCalls(instructions)...)
|
||||
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// Next continues execution until the next source line.
|
||||
func Next(dbp Process) (err error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
if err = next(dbp, false, false); err != nil {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// Continue continues execution of the debugged
|
||||
// process. It will continue until it hits a breakpoint
|
||||
// or is otherwise stopped.
|
||||
func Continue(dbp Process) error {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, thread := range dbp.ThreadList() {
|
||||
thread.Common().returnValues = nil
|
||||
}
|
||||
dbp.CheckAndClearManualStopRequest()
|
||||
defer func() {
|
||||
// Make sure we clear internal breakpoints if we simultaneously receive a
|
||||
// manual stop request and hit a breakpoint.
|
||||
if dbp.CheckAndClearManualStopRequest() {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
if dbp.CheckAndClearManualStopRequest() {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return nil
|
||||
}
|
||||
trapthread, err := dbp.ContinueOnce()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
threads := dbp.ThreadList()
|
||||
|
||||
if err := pickCurrentThread(dbp, trapthread, threads); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
curthread := dbp.CurrentThread()
|
||||
curbp := curthread.Breakpoint()
|
||||
|
||||
switch {
|
||||
case curbp.Breakpoint == nil:
|
||||
// runtime.Breakpoint, manual stop or debugCallV1-related stop
|
||||
recorded, _ := dbp.Recorded()
|
||||
if recorded {
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
|
||||
loc, err := curthread.Location()
|
||||
if err != nil || loc.Fn == nil {
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
|
||||
switch {
|
||||
case loc.Fn.Name == "runtime.breakpoint":
|
||||
// Single-step current thread until we exit runtime.breakpoint and
|
||||
// runtime.Breakpoint.
|
||||
// On go < 1.8 it was sufficient to single-step twice on go1.8 a change
|
||||
// to the compiler requires 4 steps.
|
||||
if err := stepInstructionOut(dbp, curthread, "runtime.breakpoint", "runtime.Breakpoint"); err != nil {
|
||||
return err
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
case strings.HasPrefix(loc.Fn.Name, debugCallFunctionNamePrefix1) || strings.HasPrefix(loc.Fn.Name, debugCallFunctionNamePrefix2):
|
||||
fncall := &dbp.Common().fncallState
|
||||
if !fncall.inProgress {
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
fncall.step(dbp)
|
||||
// only stop execution if the function call finished
|
||||
if fncall.finished {
|
||||
fncall.inProgress = false
|
||||
if fncall.err != nil {
|
||||
return fncall.err
|
||||
}
|
||||
curthread.Common().returnValues = fncall.returnValues()
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
default:
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
case curbp.Active && curbp.Internal:
|
||||
switch curbp.Kind {
|
||||
case StepBreakpoint:
|
||||
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
||||
if err := conditionErrors(threads); err != nil {
|
||||
return err
|
||||
}
|
||||
regs, err := curthread.Registers(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pc := regs.PC()
|
||||
text, err := disassemble(curthread, regs, dbp.Breakpoints(), dbp.BinInfo(), 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 = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(curthread)
|
||||
if err := dbp.ClearInternalBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
}
|
||||
case curbp.Active:
|
||||
onNextGoroutine, err := onNextGoroutine(curthread, dbp.Breakpoints())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if onNextGoroutine {
|
||||
err := dbp.ClearInternalBreakpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if curbp.Name == UnrecoveredPanic {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
return conditionErrors(threads)
|
||||
default:
|
||||
// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func conditionErrors(threads []Thread) error {
|
||||
var condErr error
|
||||
for _, th := range threads {
|
||||
if bp := th.Breakpoint(); bp.Breakpoint != nil && bp.CondError != nil {
|
||||
if condErr == nil {
|
||||
condErr = bp.CondError
|
||||
} 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 pickCurrentThread(dbp Process, trapthread Thread, threads []Thread) error {
|
||||
for _, th := range threads {
|
||||
if bp := th.Breakpoint(); bp.Active && bp.Internal {
|
||||
return dbp.SwitchThread(th.ThreadID())
|
||||
}
|
||||
}
|
||||
if bp := trapthread.Breakpoint(); bp.Active {
|
||||
return dbp.SwitchThread(trapthread.ThreadID())
|
||||
}
|
||||
for _, th := range threads {
|
||||
if bp := th.Breakpoint(); bp.Active {
|
||||
return dbp.SwitchThread(th.ThreadID())
|
||||
}
|
||||
}
|
||||
return dbp.SwitchThread(trapthread.ThreadID())
|
||||
}
|
||||
|
||||
// stepInstructionOut repeatedly calls StepInstruction until the current
|
||||
// function is neither fnname1 or fnname2.
|
||||
// This function is used to step out of runtime.Breakpoint as well as
|
||||
// runtime.debugCallV1.
|
||||
func stepInstructionOut(dbp Process, curthread Thread, fnname1, fnname2 string) error {
|
||||
for {
|
||||
if err := curthread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
loc, err := curthread.Location()
|
||||
if err != nil || loc.Fn == nil || (loc.Fn.Name != fnname1 && loc.Fn.Name != fnname2) {
|
||||
if g := dbp.SelectedGoroutine(); g != nil {
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
return curthread.SetCurrentBreakpoint()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step will continue until another source line is reached.
|
||||
// Will step into functions.
|
||||
func Step(dbp Process) (err error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
if err = next(dbp, true, false); err != nil {
|
||||
switch err.(type) {
|
||||
case ErrThreadBlocked: // Noop
|
||||
default:
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// SameGoroutineCondition 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)},
|
||||
}
|
||||
}
|
||||
|
||||
func frameoffCondition(frameoff int64) ast.Expr {
|
||||
return &ast.BinaryExpr{
|
||||
Op: token.EQL,
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "runtime"},
|
||||
Sel: &ast.Ident{Name: "frameoff"},
|
||||
},
|
||||
Y: &ast.BasicLit{Kind: token.INT, Value: strconv.FormatInt(frameoff, 10)},
|
||||
}
|
||||
}
|
||||
|
||||
func andFrameoffCondition(cond ast.Expr, frameoff int64) ast.Expr {
|
||||
if cond == nil {
|
||||
return nil
|
||||
}
|
||||
return &ast.BinaryExpr{
|
||||
Op: token.LAND,
|
||||
X: cond,
|
||||
Y: frameoffCondition(frameoff),
|
||||
}
|
||||
}
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
// function currently being executed or a deferred function is executed
|
||||
func StepOut(dbp Process) error {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return err
|
||||
}
|
||||
if dbp.Breakpoints().HasInternalBreakpoints() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
|
||||
selg := dbp.SelectedGoroutine()
|
||||
curthread := dbp.CurrentThread()
|
||||
|
||||
topframe, retframe, err := topframe(selg, curthread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
}()
|
||||
|
||||
if topframe.Inlined {
|
||||
if err := next(dbp, false, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success = true
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
sameGCond := SameGoroutineCondition(selg)
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.FrameOffset())
|
||||
|
||||
var deferpc uint64
|
||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||
if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
|
||||
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond)
|
||||
if err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
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 && deferpc == 0 {
|
||||
return errors.New("nothing to stepout to")
|
||||
}
|
||||
|
||||
if topframe.Ret != 0 {
|
||||
bp, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond)
|
||||
if err != nil {
|
||||
if _, isexists := err.(BreakpointExistsError); !isexists {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil {
|
||||
configureReturnBreakpoint(dbp.BinInfo(), bp, &topframe, retFrameCond)
|
||||
}
|
||||
}
|
||||
|
||||
if bp := curthread.Breakpoint(); bp.Breakpoint == nil {
|
||||
curthread.SetCurrentBreakpoint()
|
||||
}
|
||||
|
||||
success = true
|
||||
return Continue(dbp)
|
||||
}
|
||||
|
||||
// GoroutinesInfo searches for goroutines starting at index 'start', and
|
||||
// returns an array of up to 'count' (or all found elements, if 'count' is 0)
|
||||
// G structures representing the information Delve care about from the internal
|
||||
// runtime G structure.
|
||||
// GoroutinesInfo also returns the next index to be used as 'start' argument
|
||||
// while scanning for all available goroutines, or -1 if there was an error
|
||||
// or if the index already reached the last possible value.
|
||||
func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
if dbp.Common().allGCache != nil {
|
||||
// We can't use the cached array to fulfill a subrange request
|
||||
if start == 0 && (count == 0 || count >= len(dbp.Common().allGCache)) {
|
||||
return dbp.Common().allGCache, -1, nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
threadg = map[int]*G{}
|
||||
allg []*G
|
||||
rdr = dbp.BinInfo().DwarfReader()
|
||||
)
|
||||
|
||||
threads := dbp.ThreadList()
|
||||
for _, th := range threads {
|
||||
if th.Blocked() {
|
||||
continue
|
||||
}
|
||||
g, _ := GetG(th)
|
||||
if g != nil {
|
||||
threadg[g.ID] = g
|
||||
}
|
||||
}
|
||||
|
||||
addr, err := rdr.AddrFor("runtime.allglen", dbp.BinInfo().staticBase)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
allglenBytes := make([]byte, 8)
|
||||
_, err = dbp.CurrentThread().ReadMemory(allglenBytes, uintptr(addr))
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
||||
|
||||
rdr.Seek(0)
|
||||
allgentryaddr, err := rdr.AddrFor("runtime.allgs", dbp.BinInfo().staticBase)
|
||||
if err != nil {
|
||||
// try old name (pre Go 1.6)
|
||||
allgentryaddr, err = rdr.AddrFor("runtime.allg", dbp.BinInfo().staticBase)
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
}
|
||||
faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
|
||||
_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||
|
||||
for i := uint64(start); i < allglen; i++ {
|
||||
if count != 0 && len(allg) >= count {
|
||||
return allg, int(i), nil
|
||||
}
|
||||
gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true)
|
||||
if err != nil {
|
||||
allg = append(allg, &G{Unreadable: err})
|
||||
continue
|
||||
}
|
||||
g, err := gvar.parseG()
|
||||
if err != nil {
|
||||
allg = append(allg, &G{Unreadable: err})
|
||||
continue
|
||||
}
|
||||
if thg, allocated := threadg[g.ID]; allocated {
|
||||
loc, err := thg.Thread.Location()
|
||||
if err != nil {
|
||||
return nil, -1, err
|
||||
}
|
||||
g.Thread = thg.Thread
|
||||
// Prefer actual thread location information.
|
||||
g.CurrentLoc = *loc
|
||||
g.SystemStack = thg.SystemStack
|
||||
}
|
||||
if g.Status != Gdead {
|
||||
allg = append(allg, g)
|
||||
}
|
||||
}
|
||||
if start == 0 {
|
||||
dbp.Common().allGCache = allg
|
||||
}
|
||||
|
||||
return allg, -1, nil
|
||||
}
|
||||
|
||||
// FindGoroutine returns a G struct representing the goroutine
|
||||
// specified by `gid`.
|
||||
func FindGoroutine(dbp Process, gid int) (*G, error) {
|
||||
if selg := dbp.SelectedGoroutine(); (gid == -1) || (selg != nil && selg.ID == gid) || (selg == nil && gid == 0) {
|
||||
// Return the currently selected goroutine in the following circumstances:
|
||||
//
|
||||
// 1. if the caller asks for gid == -1 (because that's what a goroutine ID of -1 means in our API).
|
||||
// 2. if gid == selg.ID.
|
||||
// this serves two purposes: (a) it's an optimizations that allows us
|
||||
// to avoid reading any other goroutine and, more importantly, (b) we
|
||||
// could be reading an incorrect value for the goroutine ID of a thread.
|
||||
// This condition usually happens when a goroutine calls runtime.clone
|
||||
// and for a short period of time two threads will appear to be running
|
||||
// the same goroutine.
|
||||
// 3. if the caller asks for gid == 0 and the selected goroutine is
|
||||
// either 0 or nil.
|
||||
// Goroutine 0 is special, it either means we have no current goroutine
|
||||
// (for example, running C code), or that we are running on a speical
|
||||
// stack (system stack, signal handling stack) and we didn't properly
|
||||
// detect it.
|
||||
// Since there could be multiple goroutines '0' running simultaneously
|
||||
// if the user requests it return the one that's already selected or
|
||||
// nil if there isn't a selected goroutine.
|
||||
return selg, nil
|
||||
}
|
||||
|
||||
if gid == 0 {
|
||||
return nil, fmt.Errorf("Unknown goroutine %d", gid)
|
||||
}
|
||||
|
||||
// Calling GoroutinesInfo could be slow if there are many goroutines
|
||||
// running, check if a running goroutine has been requested first.
|
||||
for _, thread := range dbp.ThreadList() {
|
||||
g, _ := GetG(thread)
|
||||
if g != nil && g.ID == gid {
|
||||
return g, nil
|
||||
}
|
||||
}
|
||||
|
||||
const goroutinesInfoLimit = 10
|
||||
nextg := 0
|
||||
for nextg >= 0 {
|
||||
var gs []*G
|
||||
var err error
|
||||
gs, nextg, err = GoroutinesInfo(dbp, nextg, goroutinesInfoLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range gs {
|
||||
if gs[i].ID == gid {
|
||||
if gs[i].Unreadable != nil {
|
||||
return nil, gs[i].Unreadable
|
||||
}
|
||||
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.
|
||||
// If deferCall is > 0 the eval scope will be relative to the specified deferred call.
|
||||
func ConvertEvalScope(dbp Process, gid, frame, deferCall int) (*EvalScope, error) {
|
||||
if _, err := dbp.Valid(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ct := dbp.CurrentThread()
|
||||
g, err := FindGoroutine(dbp, gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g == nil {
|
||||
return ThreadScope(ct)
|
||||
}
|
||||
|
||||
var thread MemoryReadWriter
|
||||
if g.Thread == nil {
|
||||
thread = ct
|
||||
} else {
|
||||
thread = g.Thread
|
||||
}
|
||||
|
||||
locs, err := g.Stacktrace(frame+1, deferCall > 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if frame >= len(locs) {
|
||||
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
||||
}
|
||||
|
||||
if deferCall > 0 {
|
||||
if deferCall-1 >= len(locs[frame].Defers) {
|
||||
return nil, fmt.Errorf("Frame %d only has %d deferred calls", frame, len(locs[frame].Defers))
|
||||
}
|
||||
|
||||
d := locs[frame].Defers[deferCall-1]
|
||||
if d.Unreadable != nil {
|
||||
return nil, d.Unreadable
|
||||
}
|
||||
|
||||
return d.EvalScope(ct)
|
||||
}
|
||||
|
||||
return FrameToScope(dbp.BinInfo(), thread, g, locs[frame:]...), nil
|
||||
}
|
||||
|
||||
// FrameToScope returns a new EvalScope for frames[0].
|
||||
// If frames has at least two elements all memory between
|
||||
// frames[0].Regs.SP() and frames[1].Regs.CFA will be cached.
|
||||
// Otherwise all memory between frames[0].Regs.SP() and frames[0].Regs.CFA
|
||||
// will be cached.
|
||||
func FrameToScope(bi *BinaryInfo, thread MemoryReadWriter, g *G, frames ...Stackframe) *EvalScope {
|
||||
var gvar *Variable
|
||||
if g != nil {
|
||||
gvar = g.variable
|
||||
}
|
||||
|
||||
// Creates a cacheMem that will preload the entire stack frame the first
|
||||
// time any local variable is read.
|
||||
// Remember that the stack grows downward in memory.
|
||||
minaddr := frames[0].Regs.SP()
|
||||
var maxaddr uint64
|
||||
if len(frames) > 1 && frames[0].SystemStack == frames[1].SystemStack {
|
||||
maxaddr = uint64(frames[1].Regs.CFA)
|
||||
} else {
|
||||
maxaddr = uint64(frames[0].Regs.CFA)
|
||||
}
|
||||
if maxaddr > minaddr && maxaddr-minaddr < maxFramePrefetchSize {
|
||||
thread = cacheMemory(thread, uintptr(minaddr), int(maxaddr-minaddr))
|
||||
}
|
||||
|
||||
s := &EvalScope{Location: frames[0].Call, Regs: frames[0].Regs, Mem: thread, Gvar: gvar, BinInfo: bi, frameOffset: frames[0].FrameOffset()}
|
||||
s.PC = frames[0].lastpc
|
||||
return s
|
||||
}
|
||||
|
||||
// createUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint.
|
||||
// This function is meant to be called by implementations of the Process interface.
|
||||
func createUnrecoveredPanicBreakpoint(p Process, writeBreakpoint WriteBreakpointFn) {
|
||||
panicpc, err := FindFunctionLocation(p, "runtime.startpanic", true, 0)
|
||||
if _, isFnNotFound := err.(*ErrFunctionNotFound); isFnNotFound {
|
||||
panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
|
||||
}
|
||||
if err == nil {
|
||||
bp, err := p.Breakpoints().SetWithID(unrecoveredPanicID, panicpc, writeBreakpoint)
|
||||
if err == nil {
|
||||
bp.Name = UnrecoveredPanic
|
||||
bp.Variables = []string{"runtime.curg._panic.arg"}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createFatalThrowBreakpoint(p Process, writeBreakpoint WriteBreakpointFn) {
|
||||
fatalpc, err := FindFunctionLocation(p, "runtime.fatalthrow", true, 0)
|
||||
if err == nil {
|
||||
bp, err := p.Breakpoints().SetWithID(fatalThrowID, fatalpc, writeBreakpoint)
|
||||
if err == nil {
|
||||
bp.Name = FatalThrow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error) {
|
||||
pc, _, line, ok := fn.cu.lineInfo.PrologueEndPC(fn.Entry, fn.End)
|
||||
if ok {
|
||||
if !sameline {
|
||||
return pc, nil
|
||||
}
|
||||
_, entryLine := fn.cu.lineInfo.PCToLine(fn.Entry, fn.Entry)
|
||||
if entryLine == line {
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
|
||||
pc, err := firstPCAfterPrologueDisassembly(p, fn, sameline)
|
||||
if err != nil {
|
||||
return fn.Entry, err
|
||||
}
|
||||
|
||||
if pc == fn.Entry {
|
||||
// Look for the first instruction with the stmt flag set, so that setting a
|
||||
// breakpoint with file:line and with the function name always result on
|
||||
// the same instruction being selected.
|
||||
entryFile, entryLine := fn.cu.lineInfo.PCToLine(fn.Entry, fn.Entry)
|
||||
if pc, _, err := p.BinInfo().LineToPC(entryFile, entryLine); err == nil && pc >= fn.Entry && pc < fn.End {
|
||||
return pc, nil
|
||||
}
|
||||
}
|
||||
|
||||
return pc, nil
|
||||
}
|
||||
252
vendor/github.com/go-delve/delve/pkg/proc/registers.go
generated
vendored
Normal file
252
vendor/github.com/go-delve/delve/pkg/proc/registers.go
generated
vendored
Normal file
@@ -0,0 +1,252 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Registers is an interface for a generic register type. The
|
||||
// interface encapsulates the generic values / actions
|
||||
// we need independent of arch. The concrete register types
|
||||
// will be different depending on OS/Arch.
|
||||
type Registers interface {
|
||||
PC() uint64
|
||||
SP() uint64
|
||||
BP() uint64
|
||||
CX() uint64
|
||||
TLS() uint64
|
||||
// GAddr returns the address of the G variable if it is known, 0 and false otherwise
|
||||
GAddr() (uint64, bool)
|
||||
Get(int) (uint64, error)
|
||||
Slice(floatingPoint bool) []Register
|
||||
// Copy returns a copy of the registers that is guaranteed not to change
|
||||
// when the registers of the associated thread change.
|
||||
Copy() Registers
|
||||
}
|
||||
|
||||
// Register represents a CPU register.
|
||||
type Register struct {
|
||||
Name string
|
||||
Bytes []byte
|
||||
Value string
|
||||
}
|
||||
|
||||
// AppendWordReg appends a word (16 bit) register to regs.
|
||||
func AppendWordReg(regs []Register, name string, value uint16) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#04x", value)})
|
||||
}
|
||||
|
||||
// AppendDwordReg appends a double word (32 bit) register to regs.
|
||||
func AppendDwordReg(regs []Register, name string, value uint32) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#08x", value)})
|
||||
}
|
||||
|
||||
// AppendQwordReg appends a quad word (64 bit) register to regs.
|
||||
func AppendQwordReg(regs []Register, name string, value uint64) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#016x", value)})
|
||||
}
|
||||
|
||||
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, value)
|
||||
return append(regs, Register{name, buf.Bytes()[:size], descr.Describe(value, size)})
|
||||
}
|
||||
|
||||
// AppendEflagReg appends EFLAG register to regs.
|
||||
func AppendEflagReg(regs []Register, name string, value uint64) []Register {
|
||||
return appendFlagReg(regs, name, value, eflagsDescription, 64)
|
||||
}
|
||||
|
||||
// AppendMxcsrReg appends MXCSR register to regs.
|
||||
func AppendMxcsrReg(regs []Register, name string, value uint64) []Register {
|
||||
return appendFlagReg(regs, name, value, mxcsrDescription, 32)
|
||||
}
|
||||
|
||||
// AppendX87Reg appends a 80 bit float register to regs.
|
||||
func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register {
|
||||
var f float64
|
||||
fset := false
|
||||
|
||||
const (
|
||||
_SIGNBIT = 1 << 15
|
||||
_EXP_BIAS = (1 << 14) - 1 // 2^(n-1) - 1 = 16383
|
||||
_SPECIALEXP = (1 << 15) - 1 // all bits set
|
||||
_HIGHBIT = 1 << 63
|
||||
_QUIETBIT = 1 << 62
|
||||
)
|
||||
|
||||
sign := 1.0
|
||||
if exponent&_SIGNBIT != 0 {
|
||||
sign = -1.0
|
||||
}
|
||||
exponent &= ^uint16(_SIGNBIT)
|
||||
|
||||
NaN := math.NaN()
|
||||
Inf := math.Inf(+1)
|
||||
|
||||
switch exponent {
|
||||
case 0:
|
||||
switch {
|
||||
case mantissa == 0:
|
||||
f = sign * 0.0
|
||||
fset = true
|
||||
case mantissa&_HIGHBIT != 0:
|
||||
f = NaN
|
||||
fset = true
|
||||
}
|
||||
case _SPECIALEXP:
|
||||
switch {
|
||||
case mantissa&_HIGHBIT == 0:
|
||||
f = sign * Inf
|
||||
fset = true
|
||||
default:
|
||||
f = NaN // signaling NaN
|
||||
fset = true
|
||||
}
|
||||
default:
|
||||
if mantissa&_HIGHBIT == 0 {
|
||||
f = NaN
|
||||
fset = true
|
||||
}
|
||||
}
|
||||
|
||||
if !fset {
|
||||
significand := float64(mantissa) / (1 << 63)
|
||||
f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS))
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, exponent)
|
||||
binary.Write(&buf, binary.LittleEndian, mantissa)
|
||||
|
||||
return append(regs, Register{fmt.Sprintf("ST(%d)", index), buf.Bytes(), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
|
||||
}
|
||||
|
||||
// AppendSSEReg appends a 256 bit SSE register to regs.
|
||||
func AppendSSEReg(regs []Register, name string, xmm []byte) []Register {
|
||||
buf := bytes.NewReader(xmm)
|
||||
|
||||
var out bytes.Buffer
|
||||
var vi [16]uint8
|
||||
for i := range vi {
|
||||
binary.Read(buf, binary.LittleEndian, &vi[i])
|
||||
}
|
||||
|
||||
fmt.Fprintf(&out, "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8], vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0])
|
||||
|
||||
fmt.Fprintf(&out, "\tv2_int={ %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x }", vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0], vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8])
|
||||
|
||||
fmt.Fprintf(&out, "\tv4_int={ %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x }", vi[3], vi[2], vi[1], vi[0], vi[7], vi[6], vi[5], vi[4], vi[11], vi[10], vi[9], vi[8], vi[15], vi[14], vi[13], vi[12])
|
||||
|
||||
fmt.Fprintf(&out, "\tv8_int={ %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x }", vi[1], vi[0], vi[3], vi[2], vi[5], vi[4], vi[7], vi[6], vi[9], vi[8], vi[11], vi[10], vi[13], vi[12], vi[15], vi[14])
|
||||
|
||||
fmt.Fprintf(&out, "\tv16_int={ %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x }", vi[0], vi[1], vi[2], vi[3], vi[4], vi[5], vi[6], vi[7], vi[8], vi[9], vi[10], vi[11], vi[12], vi[13], vi[14], vi[15])
|
||||
|
||||
buf.Seek(0, os.SEEK_SET)
|
||||
var v2 [2]float64
|
||||
for i := range v2 {
|
||||
binary.Read(buf, binary.LittleEndian, &v2[i])
|
||||
}
|
||||
fmt.Fprintf(&out, "\tv2_float={ %g %g }", v2[0], v2[1])
|
||||
|
||||
buf.Seek(0, os.SEEK_SET)
|
||||
var v4 [4]float32
|
||||
for i := range v4 {
|
||||
binary.Read(buf, binary.LittleEndian, &v4[i])
|
||||
}
|
||||
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
||||
|
||||
return append(regs, Register{name, xmm, out.String()})
|
||||
}
|
||||
|
||||
// ErrUnknownRegister is returned when the value of an unknown
|
||||
// register is requested.
|
||||
var ErrUnknownRegister = errors.New("unknown register")
|
||||
|
||||
type flagRegisterDescr []flagDescr
|
||||
type flagDescr struct {
|
||||
name string
|
||||
mask uint64
|
||||
}
|
||||
|
||||
var mxcsrDescription flagRegisterDescr = []flagDescr{
|
||||
{"FZ", 1 << 15},
|
||||
{"RZ/RN", 1<<14 | 1<<13},
|
||||
{"PM", 1 << 12},
|
||||
{"UM", 1 << 11},
|
||||
{"OM", 1 << 10},
|
||||
{"ZM", 1 << 9},
|
||||
{"DM", 1 << 8},
|
||||
{"IM", 1 << 7},
|
||||
{"DAZ", 1 << 6},
|
||||
{"PE", 1 << 5},
|
||||
{"UE", 1 << 4},
|
||||
{"OE", 1 << 3},
|
||||
{"ZE", 1 << 2},
|
||||
{"DE", 1 << 1},
|
||||
{"IE", 1 << 0},
|
||||
}
|
||||
|
||||
var eflagsDescription flagRegisterDescr = []flagDescr{
|
||||
{"CF", 1 << 0},
|
||||
{"", 1 << 1},
|
||||
{"PF", 1 << 2},
|
||||
{"AF", 1 << 4},
|
||||
{"ZF", 1 << 6},
|
||||
{"SF", 1 << 7},
|
||||
{"TF", 1 << 8},
|
||||
{"IF", 1 << 9},
|
||||
{"DF", 1 << 10},
|
||||
{"OF", 1 << 11},
|
||||
{"IOPL", 1<<12 | 1<<13},
|
||||
{"NT", 1 << 14},
|
||||
{"RF", 1 << 16},
|
||||
{"VM", 1 << 17},
|
||||
{"AC", 1 << 18},
|
||||
{"VIF", 1 << 19},
|
||||
{"VIP", 1 << 20},
|
||||
{"ID", 1 << 21},
|
||||
}
|
||||
|
||||
func (descr flagRegisterDescr) Mask() uint64 {
|
||||
var r uint64
|
||||
for _, f := range descr {
|
||||
r = r | f.mask
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
|
||||
var r []string
|
||||
for _, f := range descr {
|
||||
if f.name == "" {
|
||||
continue
|
||||
}
|
||||
// rbm is f.mask with only the right-most bit set:
|
||||
// 0001 1100 -> 0000 0100
|
||||
rbm := f.mask & -f.mask
|
||||
if rbm == f.mask {
|
||||
if reg&f.mask != 0 {
|
||||
r = append(r, f.name)
|
||||
}
|
||||
} else {
|
||||
x := (reg & f.mask) >> uint64(math.Log2(float64(rbm)))
|
||||
r = append(r, fmt.Sprintf("%s=%x", f.name, x))
|
||||
}
|
||||
}
|
||||
if reg & ^descr.Mask() != 0 {
|
||||
r = append(r, fmt.Sprintf("unknown_flags=%x", reg&^descr.Mask()))
|
||||
}
|
||||
return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
|
||||
}
|
||||
90
vendor/github.com/go-delve/delve/pkg/proc/registers_amd64.go
generated
vendored
Normal file
90
vendor/github.com/go-delve/delve/pkg/proc/registers_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
)
|
||||
|
||||
var dwarfToAsm = map[int]x86asm.Reg{
|
||||
0: x86asm.RAX,
|
||||
1: x86asm.RDX,
|
||||
2: x86asm.RCX,
|
||||
3: x86asm.RBX,
|
||||
4: x86asm.RSI,
|
||||
5: x86asm.RDI,
|
||||
6: x86asm.RBP,
|
||||
7: x86asm.RSP,
|
||||
8: x86asm.R8,
|
||||
9: x86asm.R9,
|
||||
10: x86asm.R10,
|
||||
11: x86asm.R11,
|
||||
12: x86asm.R12,
|
||||
13: x86asm.R13,
|
||||
14: x86asm.R14,
|
||||
15: x86asm.R15,
|
||||
16: x86asm.RIP,
|
||||
}
|
||||
|
||||
var dwarfToName = 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",
|
||||
}
|
||||
|
||||
// GetDwarfRegister maps between DWARF register numbers and architecture
|
||||
// registers.
|
||||
// The mapping 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 GetDwarfRegister(regs Registers, i int) []byte {
|
||||
if asmreg, ok := dwarfToAsm[i]; ok {
|
||||
x, _ := regs.Get(int(asmreg))
|
||||
var buf bytes.Buffer
|
||||
binary.Write(&buf, binary.LittleEndian, x)
|
||||
return buf.Bytes()
|
||||
}
|
||||
if regname, ok := dwarfToName[i]; ok {
|
||||
regslice := regs.Slice(true)
|
||||
for _, reg := range regslice {
|
||||
if reg.Name == regname {
|
||||
return reg.Bytes
|
||||
}
|
||||
}
|
||||
}
|
||||
return []byte{}
|
||||
}
|
||||
711
vendor/github.com/go-delve/delve/pkg/proc/stack.go
generated
vendored
Normal file
711
vendor/github.com/go-delve/delve/pkg/proc/stack.go
generated
vendored
Normal file
@@ -0,0 +1,711 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/frame"
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
"github.com/go-delve/delve/pkg/dwarf/reader"
|
||||
)
|
||||
|
||||
// This code is partly adapted from runtime.gentraceback in
|
||||
// $GOROOT/src/runtime/traceback.go
|
||||
|
||||
// Stackframe represents a frame in a system stack.
|
||||
//
|
||||
// Each stack frame has two locations Current and Call.
|
||||
//
|
||||
// For the topmost stackframe Current and Call are the same location.
|
||||
//
|
||||
// For stackframes after the first Current is the location corresponding to
|
||||
// the return address and Call is the location of the CALL instruction that
|
||||
// was last executed on the frame. Note however that Call.PC is always equal
|
||||
// to Current.PC, because finding the correct value for Call.PC would
|
||||
// require disassembling each function in the stacktrace.
|
||||
//
|
||||
// For synthetic stackframes generated for inlined function calls Current.Fn
|
||||
// is the function containing the inlining and Call.Fn in the inlined
|
||||
// function.
|
||||
type Stackframe struct {
|
||||
Current, Call Location
|
||||
|
||||
// Frame registers.
|
||||
Regs op.DwarfRegisters
|
||||
// High address of the stack.
|
||||
stackHi uint64
|
||||
// Return address for this stack frame (as read from the stack frame itself).
|
||||
Ret uint64
|
||||
// Address to the memory location containing the return address
|
||||
addrret uint64
|
||||
// Err is set if an error occurred during stacktrace
|
||||
Err error
|
||||
// SystemStack is true if this frame belongs to a system stack.
|
||||
SystemStack bool
|
||||
// Inlined is true if this frame is actually an inlined call.
|
||||
Inlined bool
|
||||
// Bottom is true if this is the bottom of the stack
|
||||
Bottom bool
|
||||
|
||||
// lastpc is a memory address guaranteed to belong to the last instruction
|
||||
// executed in this stack frame.
|
||||
// For the topmost stack frame this will be the same as Current.PC and
|
||||
// Call.PC, for other stack frames it will usually be Current.PC-1, but
|
||||
// could be different when inlined calls are involved in the stacktrace.
|
||||
// Note that this address isn't guaranteed to belong to the start of an
|
||||
// instruction and, for this reason, should not be propagated outside of
|
||||
// pkg/proc.
|
||||
// Use this value to determine active lexical scopes for the stackframe.
|
||||
lastpc uint64
|
||||
|
||||
// TopmostDefer is the defer that would be at the top of the stack when a
|
||||
// panic unwind would get to this call frame, in other words it's the first
|
||||
// deferred function that will be called if the runtime unwinds past this
|
||||
// call frame.
|
||||
TopmostDefer *Defer
|
||||
|
||||
// Defers is the list of functions deferred by this stack frame (so far).
|
||||
Defers []*Defer
|
||||
}
|
||||
|
||||
// FrameOffset returns the address of the stack frame, absolute for system
|
||||
// stack frames or as an offset from stackhi for goroutine stacks (a
|
||||
// negative value).
|
||||
func (frame *Stackframe) FrameOffset() int64 {
|
||||
if frame.SystemStack {
|
||||
return frame.Regs.CFA
|
||||
}
|
||||
return frame.Regs.CFA - int64(frame.stackHi)
|
||||
}
|
||||
|
||||
// FramePointerOffset returns the value of the frame pointer, absolute for
|
||||
// system stack frames or as an offset from stackhi for goroutine stacks (a
|
||||
// negative value).
|
||||
func (frame *Stackframe) FramePointerOffset() int64 {
|
||||
if frame.SystemStack {
|
||||
return int64(frame.Regs.BP())
|
||||
}
|
||||
return int64(frame.Regs.BP()) - int64(frame.stackHi)
|
||||
}
|
||||
|
||||
// ThreadStacktrace returns the stack trace for thread.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
||||
g, _ := GetG(thread)
|
||||
if g == nil {
|
||||
regs, err := thread.Registers(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(regs, thread.BinInfo().staticBase), 0, nil, -1, nil)
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
return g.Stacktrace(depth, false)
|
||||
}
|
||||
|
||||
func (g *G) stackIterator() (*stackIterator, error) {
|
||||
stkbar, err := g.stkbar()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.Thread != nil {
|
||||
regs, err := g.Thread.Registers(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(regs, g.variable.bi.staticBase), g.stackhi, stkbar, g.stkbarPos, g), nil
|
||||
}
|
||||
return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, stkbar, g.stkbarPos, g), 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, readDefers bool) ([]Stackframe, error) {
|
||||
it, err := g.stackIterator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frames, err := it.stacktrace(depth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if readDefers {
|
||||
g.readDefers(frames)
|
||||
}
|
||||
return frames, nil
|
||||
}
|
||||
|
||||
// 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 uint64
|
||||
top bool
|
||||
atend bool
|
||||
frame Stackframe
|
||||
bi *BinaryInfo
|
||||
mem MemoryReadWriter
|
||||
err error
|
||||
|
||||
stackhi uint64
|
||||
systemstack bool
|
||||
stackBarrierPC uint64
|
||||
stkbar []savedLR
|
||||
|
||||
// regs is the register set for the current frame
|
||||
regs op.DwarfRegisters
|
||||
|
||||
g *G // the goroutine being stacktraced, nil if we are stacktracing a goroutine-less thread
|
||||
g0_sched_sp uint64 // value of g0.sched.sp (see comments around its use)
|
||||
|
||||
dwarfReader *dwarf.Reader
|
||||
}
|
||||
|
||||
type savedLR struct {
|
||||
ptr uint64
|
||||
val uint64
|
||||
}
|
||||
|
||||
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int, g *G) *stackIterator {
|
||||
stackBarrierFunc := bi.LookupFunc["runtime.stackBarrier"] // stack barriers were removed in Go 1.9
|
||||
var stackBarrierPC uint64
|
||||
if stackBarrierFunc != nil && stkbar != nil {
|
||||
stackBarrierPC = stackBarrierFunc.Entry
|
||||
fn := bi.PCToFunc(regs.PC())
|
||||
if fn != nil && fn.Name == "runtime.stackBarrier" {
|
||||
// We caught the goroutine as it's executing the stack barrier, we must
|
||||
// determine whether or not g.stackPos has already been incremented or not.
|
||||
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < regs.SP() {
|
||||
// runtime.stackBarrier has not incremented stkbarPos.
|
||||
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < regs.SP() {
|
||||
// runtime.stackBarrier has incremented stkbarPos.
|
||||
stkbarPos--
|
||||
} else {
|
||||
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", regs.SP())}
|
||||
}
|
||||
}
|
||||
stkbar = stkbar[stkbarPos:]
|
||||
}
|
||||
var g0_sched_sp uint64
|
||||
systemstack := true
|
||||
if g != nil {
|
||||
systemstack = g.SystemStack
|
||||
g0var, _ := g.variable.fieldVariable("m").structMember("g0")
|
||||
if g0var != nil {
|
||||
g0, _ := g0var.parseG()
|
||||
if g0 != nil {
|
||||
g0_sched_sp = g0.SP
|
||||
}
|
||||
}
|
||||
}
|
||||
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar, systemstack: systemstack, g: g, g0_sched_sp: g0_sched_sp, dwarfReader: bi.dwarf.Reader()}
|
||||
}
|
||||
|
||||
// Next points the iterator to the next stack frame.
|
||||
func (it *stackIterator) Next() bool {
|
||||
if it.err != nil || it.atend {
|
||||
return false
|
||||
}
|
||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||
it.frame = it.newStackframe(ret, retaddr)
|
||||
|
||||
if it.stkbar != nil && it.frame.Ret == it.stackBarrierPC && it.frame.addrret == it.stkbar[0].ptr {
|
||||
// Skip stack barrier frames
|
||||
it.frame.Ret = it.stkbar[0].val
|
||||
it.stkbar = it.stkbar[1:]
|
||||
}
|
||||
|
||||
if it.switchStack() {
|
||||
return true
|
||||
}
|
||||
|
||||
if it.frame.Ret <= 0 {
|
||||
it.atend = true
|
||||
return true
|
||||
}
|
||||
|
||||
it.top = false
|
||||
it.pc = it.frame.Ret
|
||||
it.regs = callFrameRegs
|
||||
return true
|
||||
}
|
||||
|
||||
// asmcgocallSPOffsetSaveSlot is the offset from systemstack.SP where
|
||||
// (goroutine.SP - StackHi) is saved in runtime.asmcgocall after the stack
|
||||
// switch happens.
|
||||
const asmcgocallSPOffsetSaveSlot = 0x28
|
||||
|
||||
// switchStack will use the current frame to determine if it's time to
|
||||
// switch between the system stack and the goroutine stack or vice versa.
|
||||
// Sets it.atend when the top of the stack is reached.
|
||||
func (it *stackIterator) switchStack() bool {
|
||||
if it.frame.Current.Fn == nil {
|
||||
return false
|
||||
}
|
||||
switch it.frame.Current.Fn.Name {
|
||||
case "runtime.asmcgocall":
|
||||
if it.top || !it.systemstack {
|
||||
return false
|
||||
}
|
||||
|
||||
// This function is called by a goroutine to execute a C function and
|
||||
// switches from the goroutine stack to the system stack.
|
||||
// Since we are unwinding the stack from callee to caller we have switch
|
||||
// from the system stack to the goroutine stack.
|
||||
|
||||
off, _ := readIntRaw(it.mem, uintptr(it.regs.SP()+asmcgocallSPOffsetSaveSlot), int64(it.bi.Arch.PtrSize())) // reads "offset of SP from StackHi" from where runtime.asmcgocall saved it
|
||||
oldsp := it.regs.SP()
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = uint64(int64(it.stackhi) - off)
|
||||
|
||||
// runtime.asmcgocall can also be called from inside the system stack,
|
||||
// in that case no stack switch actually happens
|
||||
if it.regs.SP() == oldsp {
|
||||
return false
|
||||
}
|
||||
it.systemstack = false
|
||||
|
||||
// advances to the next frame in the call stack
|
||||
it.frame.addrret = uint64(int64(it.regs.SP()) + int64(it.bi.Arch.PtrSize()))
|
||||
it.frame.Ret, _ = readUintRaw(it.mem, uintptr(it.frame.addrret), int64(it.bi.Arch.PtrSize()))
|
||||
it.pc = it.frame.Ret
|
||||
|
||||
it.top = false
|
||||
return true
|
||||
|
||||
case "runtime.cgocallback_gofunc":
|
||||
// For a detailed description of how this works read the long comment at
|
||||
// the start of $GOROOT/src/runtime/cgocall.go and the source code of
|
||||
// runtime.cgocallback_gofunc in $GOROOT/src/runtime/asm_amd64.s
|
||||
//
|
||||
// When a C functions calls back into go it will eventually call into
|
||||
// runtime.cgocallback_gofunc which is the function that does the stack
|
||||
// switch from the system stack back into the goroutine stack
|
||||
// Since we are going backwards on the stack here we see the transition
|
||||
// as goroutine stack -> system stack.
|
||||
|
||||
if it.top || it.systemstack {
|
||||
return false
|
||||
}
|
||||
|
||||
if it.g0_sched_sp <= 0 {
|
||||
return false
|
||||
}
|
||||
// entering the system stack
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g0_sched_sp
|
||||
// reads the previous value of g0.sched.sp that runtime.cgocallback_gofunc saved on the stack
|
||||
it.g0_sched_sp, _ = readUintRaw(it.mem, uintptr(it.regs.SP()), int64(it.bi.Arch.PtrSize()))
|
||||
it.top = false
|
||||
callFrameRegs, ret, retaddr := it.advanceRegs()
|
||||
frameOnSystemStack := it.newStackframe(ret, retaddr)
|
||||
it.pc = frameOnSystemStack.Ret
|
||||
it.regs = callFrameRegs
|
||||
it.systemstack = true
|
||||
return true
|
||||
|
||||
case "runtime.goexit", "runtime.rt0_go", "runtime.mcall":
|
||||
// Look for "top of stack" functions.
|
||||
it.atend = true
|
||||
return true
|
||||
|
||||
default:
|
||||
if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") {
|
||||
// The runtime switches to the system stack in multiple places.
|
||||
// This usually happens through a call to runtime.systemstack but there
|
||||
// are functions that switch to the system stack manually (for example
|
||||
// runtime.morestack).
|
||||
// Since we are only interested in printing the system stack for cgo
|
||||
// calls we switch directly to the goroutine stack if we detect that the
|
||||
// function at the top of the stack is a runtime function.
|
||||
it.systemstack = false
|
||||
it.top = false
|
||||
it.pc = it.g.PC
|
||||
it.regs.Reg(it.regs.SPRegNum).Uint64Val = it.g.SP
|
||||
it.regs.Reg(it.regs.BPRegNum).Uint64Val = it.g.BP
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Frame returns the frame the iterator is pointing at.
|
||||
func (it *stackIterator) Frame() Stackframe {
|
||||
it.frame.Bottom = it.atend
|
||||
return it.frame
|
||||
}
|
||||
|
||||
// Err returns the error encountered during stack iteration.
|
||||
func (it *stackIterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
// frameBase calculates the frame base pseudo-register for DWARF for fn and
|
||||
// the current frame.
|
||||
func (it *stackIterator) frameBase(fn *Function) int64 {
|
||||
it.dwarfReader.Seek(fn.offset)
|
||||
e, err := it.dwarfReader.Next()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
fb, _, _, _ := it.bi.Location(e, dwarf.AttrFrameBase, it.pc, it.regs)
|
||||
return fb
|
||||
}
|
||||
|
||||
func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
|
||||
if retaddr == 0 {
|
||||
it.err = NullAddrError{}
|
||||
return Stackframe{}
|
||||
}
|
||||
f, l, fn := it.bi.PCToLine(it.pc)
|
||||
if fn == nil {
|
||||
f = "?"
|
||||
l = -1
|
||||
} else {
|
||||
it.regs.FrameBase = it.frameBase(fn)
|
||||
}
|
||||
r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, stackHi: it.stackhi, SystemStack: it.systemstack, lastpc: it.pc}
|
||||
r.Call = r.Current
|
||||
if !it.top && r.Current.Fn != nil && it.pc != r.Current.Fn.Entry {
|
||||
// if the return address is the entry point of the function that
|
||||
// contains it then this is some kind of fake return frame (for example
|
||||
// runtime.sigreturn) that didn't actually call the current frame,
|
||||
// attempting to get the location of the CALL instruction would just
|
||||
// obfuscate what's going on, since there is no CALL instruction.
|
||||
switch r.Current.Fn.Name {
|
||||
case "runtime.mstart", "runtime.systemstack_switch":
|
||||
// these frames are inserted by runtime.systemstack and there is no CALL
|
||||
// instruction to look for at pc - 1
|
||||
default:
|
||||
r.lastpc = it.pc - 1
|
||||
r.Call.File, r.Call.Line = r.Current.Fn.cu.lineInfo.PCToLine(r.Current.Fn.Entry, it.pc-1)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
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 = it.appendInlineCalls(frames, it.Frame())
|
||||
if len(frames) >= depth+1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := it.Err(); err != nil {
|
||||
if len(frames) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
frames = append(frames, Stackframe{Err: err})
|
||||
}
|
||||
return frames, nil
|
||||
}
|
||||
|
||||
func (it *stackIterator) appendInlineCalls(frames []Stackframe, frame Stackframe) []Stackframe {
|
||||
if frame.Call.Fn == nil {
|
||||
return append(frames, frame)
|
||||
}
|
||||
if frame.Call.Fn.cu.lineInfo == nil {
|
||||
return append(frames, frame)
|
||||
}
|
||||
|
||||
callpc := frame.Call.PC
|
||||
if len(frames) > 0 {
|
||||
callpc--
|
||||
}
|
||||
|
||||
irdr := reader.InlineStack(it.bi.dwarf, frame.Call.Fn.offset, reader.ToRelAddr(callpc, it.bi.staticBase))
|
||||
for irdr.Next() {
|
||||
entry, offset := reader.LoadAbstractOrigin(irdr.Entry(), it.dwarfReader)
|
||||
|
||||
fnname, okname := entry.Val(dwarf.AttrName).(string)
|
||||
fileidx, okfileidx := entry.Val(dwarf.AttrCallFile).(int64)
|
||||
line, okline := entry.Val(dwarf.AttrCallLine).(int64)
|
||||
|
||||
if !okname || !okfileidx || !okline {
|
||||
break
|
||||
}
|
||||
if fileidx-1 < 0 || fileidx-1 >= int64(len(frame.Current.Fn.cu.lineInfo.FileNames)) {
|
||||
break
|
||||
}
|
||||
|
||||
inlfn := &Function{Name: fnname, Entry: frame.Call.Fn.Entry, End: frame.Call.Fn.End, offset: offset, cu: frame.Call.Fn.cu}
|
||||
frames = append(frames, Stackframe{
|
||||
Current: frame.Current,
|
||||
Call: Location{
|
||||
frame.Call.PC,
|
||||
frame.Call.File,
|
||||
frame.Call.Line,
|
||||
inlfn,
|
||||
},
|
||||
Regs: frame.Regs,
|
||||
stackHi: frame.stackHi,
|
||||
Ret: frame.Ret,
|
||||
addrret: frame.addrret,
|
||||
Err: frame.Err,
|
||||
SystemStack: frame.SystemStack,
|
||||
Inlined: true,
|
||||
lastpc: frame.lastpc,
|
||||
})
|
||||
|
||||
frame.Call.File = frame.Current.Fn.cu.lineInfo.FileNames[fileidx-1].Path
|
||||
frame.Call.Line = int(line)
|
||||
}
|
||||
|
||||
return append(frames, frame)
|
||||
}
|
||||
|
||||
// advanceRegs calculates it.callFrameRegs using it.regs and the frame
|
||||
// descriptor entry for the current stack frame.
|
||||
// it.regs.CallFrameCFA is updated.
|
||||
func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uint64, retaddr uint64) {
|
||||
fde, err := it.bi.frameEntries.FDEForPC(it.pc)
|
||||
var framectx *frame.FrameContext
|
||||
if _, nofde := err.(*frame.ErrNoFDEForPC); nofde {
|
||||
framectx = it.bi.Arch.FixFrameUnwindContext(nil, it.pc, it.bi)
|
||||
} else {
|
||||
framectx = it.bi.Arch.FixFrameUnwindContext(fde.EstablishFrame(it.pc), it.pc, it.bi)
|
||||
}
|
||||
|
||||
cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0)
|
||||
if cfareg == nil {
|
||||
it.err = fmt.Errorf("CFA becomes undefined at PC %#x", it.pc)
|
||||
return op.DwarfRegisters{StaticBase: it.bi.staticBase}, 0, 0
|
||||
}
|
||||
it.regs.CFA = int64(cfareg.Uint64Val)
|
||||
|
||||
callFrameRegs = op.DwarfRegisters{StaticBase: it.bi.staticBase, ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum, BPRegNum: it.regs.BPRegNum}
|
||||
|
||||
// According to the standard the compiler should be responsible for emitting
|
||||
// rules for the RSP register so that it can then be used to calculate CFA,
|
||||
// however neither Go nor GCC do this.
|
||||
// In the following line we copy GDB's behaviour by assuming this is
|
||||
// implicit.
|
||||
// See also the comment in dwarf2_frame_default_init in
|
||||
// $GDB_SOURCE/dwarf2-frame.c
|
||||
callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg)
|
||||
|
||||
for i, regRule := range framectx.Regs {
|
||||
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
||||
callFrameRegs.AddReg(i, reg)
|
||||
if i == framectx.RetAddrReg {
|
||||
if reg == nil {
|
||||
if err == nil {
|
||||
err = fmt.Errorf("Undefined return address at %#x", it.pc)
|
||||
}
|
||||
it.err = err
|
||||
} else {
|
||||
ret = reg.Uint64Val
|
||||
}
|
||||
retaddr = uint64(it.regs.CFA + regRule.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
return callFrameRegs, ret, retaddr
|
||||
}
|
||||
|
||||
func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, cfa int64) (*op.DwarfRegister, error) {
|
||||
switch rule.Rule {
|
||||
default:
|
||||
fallthrough
|
||||
case frame.RuleUndefined:
|
||||
return nil, nil
|
||||
case frame.RuleSameVal:
|
||||
reg := *it.regs.Reg(regnum)
|
||||
return ®, nil
|
||||
case frame.RuleOffset:
|
||||
return it.readRegisterAt(regnum, uint64(cfa+rule.Offset))
|
||||
case frame.RuleValOffset:
|
||||
return op.DwarfRegisterFromUint64(uint64(cfa + rule.Offset)), nil
|
||||
case frame.RuleRegister:
|
||||
return it.regs.Reg(rule.Reg), nil
|
||||
case frame.RuleExpression:
|
||||
v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return it.readRegisterAt(regnum, uint64(v))
|
||||
case frame.RuleValExpression:
|
||||
v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return op.DwarfRegisterFromUint64(uint64(v)), nil
|
||||
case frame.RuleArchitectural:
|
||||
return nil, errors.New("architectural frame rules are unsupported")
|
||||
case frame.RuleCFA:
|
||||
if it.regs.Reg(rule.Reg) == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return op.DwarfRegisterFromUint64(uint64(int64(it.regs.Uint64Val(rule.Reg)) + rule.Offset)), nil
|
||||
case frame.RuleFramePointer:
|
||||
curReg := it.regs.Reg(rule.Reg)
|
||||
if curReg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if curReg.Uint64Val <= uint64(cfa) {
|
||||
return it.readRegisterAt(regnum, curReg.Uint64Val)
|
||||
}
|
||||
newReg := *curReg
|
||||
return &newReg, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (it *stackIterator) readRegisterAt(regnum uint64, addr uint64) (*op.DwarfRegister, error) {
|
||||
buf := make([]byte, it.bi.Arch.RegSize(regnum))
|
||||
_, err := it.mem.ReadMemory(buf, uintptr(addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return op.DwarfRegisterFromBytes(buf), nil
|
||||
}
|
||||
|
||||
// Defer represents one deferred call
|
||||
type Defer struct {
|
||||
DeferredPC uint64 // Value of field _defer.fn.fn, the deferred function
|
||||
DeferPC uint64 // PC address of instruction that added this defer
|
||||
SP uint64 // Value of SP register when this function was deferred (this field gets adjusted when the stack is moved to match the new stack space)
|
||||
link *Defer // Next deferred function
|
||||
argSz int64
|
||||
|
||||
variable *Variable
|
||||
Unreadable error
|
||||
}
|
||||
|
||||
// readDefers decorates the frames with the function deferred at each stack frame.
|
||||
func (g *G) readDefers(frames []Stackframe) {
|
||||
curdefer := g.Defer()
|
||||
i := 0
|
||||
|
||||
// scan simultaneously frames and the curdefer linked list, assigning
|
||||
// defers to their associated frames.
|
||||
for {
|
||||
if curdefer == nil || i >= len(frames) {
|
||||
return
|
||||
}
|
||||
if curdefer.Unreadable != nil {
|
||||
// Current defer is unreadable, stick it into the first available frame
|
||||
// (so that it can be reported to the user) and exit
|
||||
frames[i].Defers = append(frames[i].Defers, curdefer)
|
||||
return
|
||||
}
|
||||
if frames[i].Err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if frames[i].TopmostDefer == nil {
|
||||
frames[i].TopmostDefer = curdefer
|
||||
}
|
||||
|
||||
if frames[i].SystemStack || curdefer.SP >= uint64(frames[i].Regs.CFA) {
|
||||
// frames[i].Regs.CFA is the value that SP had before the function of
|
||||
// frames[i] was called.
|
||||
// This means that when curdefer.SP == frames[i].Regs.CFA then curdefer
|
||||
// was added by the previous frame.
|
||||
//
|
||||
// curdefer.SP < frames[i].Regs.CFA means curdefer was added by a
|
||||
// function further down the stack.
|
||||
//
|
||||
// SystemStack frames live on a different physical stack and can't be
|
||||
// compared with deferred frames.
|
||||
i++
|
||||
} else {
|
||||
frames[i].Defers = append(frames[i].Defers, curdefer)
|
||||
curdefer = curdefer.Next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Defer) load() {
|
||||
d.variable.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
|
||||
if d.variable.Unreadable != nil {
|
||||
d.Unreadable = d.variable.Unreadable
|
||||
return
|
||||
}
|
||||
|
||||
fnvar := d.variable.fieldVariable("fn").maybeDereference()
|
||||
if fnvar.Addr != 0 {
|
||||
fnvar = fnvar.loadFieldNamed("fn")
|
||||
if fnvar.Unreadable == nil {
|
||||
d.DeferredPC, _ = constant.Uint64Val(fnvar.Value)
|
||||
}
|
||||
}
|
||||
|
||||
d.DeferPC, _ = constant.Uint64Val(d.variable.fieldVariable("pc").Value)
|
||||
d.SP, _ = constant.Uint64Val(d.variable.fieldVariable("sp").Value)
|
||||
d.argSz, _ = constant.Int64Val(d.variable.fieldVariable("siz").Value)
|
||||
|
||||
linkvar := d.variable.fieldVariable("link").maybeDereference()
|
||||
if linkvar.Addr != 0 {
|
||||
d.link = &Defer{variable: linkvar}
|
||||
}
|
||||
}
|
||||
|
||||
// errSPDecreased is used when (*Defer).Next detects a corrupted linked
|
||||
// list, specifically when after followin a link pointer the value of SP
|
||||
// decreases rather than increasing or staying the same (the defer list is a
|
||||
// FIFO list, nodes further down the list have been added by function calls
|
||||
// further down the call stack and therefore the SP should always increase).
|
||||
var errSPDecreased = errors.New("corrupted defer list: SP decreased")
|
||||
|
||||
// Next returns the next defer in the linked list
|
||||
func (d *Defer) Next() *Defer {
|
||||
if d.link == nil {
|
||||
return nil
|
||||
}
|
||||
d.link.load()
|
||||
if d.link.SP < d.SP {
|
||||
d.link.Unreadable = errSPDecreased
|
||||
}
|
||||
return d.link
|
||||
}
|
||||
|
||||
// EvalScope returns an EvalScope relative to the argument frame of this deferred call.
|
||||
// The argument frame of a deferred call is stored in memory immediately
|
||||
// after the deferred header.
|
||||
func (d *Defer) EvalScope(thread Thread) (*EvalScope, error) {
|
||||
scope, err := GoroutineScope(thread)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get scope: %v", err)
|
||||
}
|
||||
|
||||
bi := thread.BinInfo()
|
||||
scope.PC = d.DeferredPC
|
||||
scope.File, scope.Line, scope.Fn = bi.PCToLine(d.DeferredPC)
|
||||
|
||||
if scope.Fn == nil {
|
||||
return nil, fmt.Errorf("could not find function at %#x", d.DeferredPC)
|
||||
}
|
||||
|
||||
// The arguments are stored immediately after the defer header struct, i.e.
|
||||
// addr+sizeof(_defer). Since CFA in go is always the address of the first
|
||||
// argument, that's what we use for the value of CFA.
|
||||
// For SP we use CFA minus the size of one pointer because that would be
|
||||
// the space occupied by pushing the return address on the stack during the
|
||||
// CALL.
|
||||
scope.Regs.CFA = (int64(d.variable.Addr) + d.variable.RealType.Common().ByteSize)
|
||||
scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(scope.Regs.CFA - int64(bi.Arch.PtrSize()))
|
||||
|
||||
bi.dwarfReader.Seek(scope.Fn.offset)
|
||||
e, err := bi.dwarfReader.Next()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read DWARF function entry: %v", err)
|
||||
}
|
||||
scope.Regs.FrameBase, _, _, _ = bi.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs)
|
||||
scope.Mem = cacheMemory(scope.Mem, uintptr(scope.Regs.CFA), int(d.argSz))
|
||||
|
||||
return scope, nil
|
||||
}
|
||||
584
vendor/github.com/go-delve/delve/pkg/proc/threads.go
generated
vendored
Normal file
584
vendor/github.com/go-delve/delve/pkg/proc/threads.go
generated
vendored
Normal file
@@ -0,0 +1,584 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
||||
"github.com/go-delve/delve/pkg/dwarf/reader"
|
||||
)
|
||||
|
||||
// Thread represents a thread.
|
||||
type Thread interface {
|
||||
MemoryReadWriter
|
||||
Location() (*Location, error)
|
||||
// Breakpoint will return the breakpoint that this thread is stopped at or
|
||||
// nil if the thread is not stopped at any breakpoint.
|
||||
Breakpoint() BreakpointState
|
||||
ThreadID() int
|
||||
|
||||
// Registers returns the CPU registers of this thread. The contents of the
|
||||
// variable returned may or may not change to reflect the new CPU status
|
||||
// when the thread is resumed or the registers are changed by calling
|
||||
// SetPC/SetSP/etc.
|
||||
// To insure that the the returned variable won't change call the Copy
|
||||
// method of Registers.
|
||||
Registers(floatingPoint bool) (Registers, error)
|
||||
|
||||
// RestoreRegisters restores saved registers
|
||||
RestoreRegisters(Registers) error
|
||||
Arch() Arch
|
||||
BinInfo() *BinaryInfo
|
||||
StepInstruction() error
|
||||
// Blocked returns true if the thread is blocked
|
||||
Blocked() bool
|
||||
// SetCurrentBreakpoint updates the current breakpoint of this thread
|
||||
SetCurrentBreakpoint() error
|
||||
// Common returns the CommonThread structure for this thread
|
||||
Common() *CommonThread
|
||||
|
||||
SetPC(uint64) error
|
||||
SetSP(uint64) error
|
||||
SetDX(uint64) error
|
||||
}
|
||||
|
||||
// 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 *Function
|
||||
}
|
||||
|
||||
// ErrThreadBlocked is returned when the thread
|
||||
// is blocked in the scheduler.
|
||||
type ErrThreadBlocked struct{}
|
||||
|
||||
func (tbe ErrThreadBlocked) Error() string {
|
||||
return "thread blocked"
|
||||
}
|
||||
|
||||
// CommonThread contains fields used by this package, common to all
|
||||
// implementations of the Thread interface.
|
||||
type CommonThread struct {
|
||||
returnValues []*Variable
|
||||
}
|
||||
|
||||
// ReturnValues reads the return values from the function executing on
|
||||
// this thread using the provided LoadConfig.
|
||||
func (t *CommonThread) ReturnValues(cfg LoadConfig) []*Variable {
|
||||
loadValues(t.returnValues, cfg)
|
||||
return t.returnValues
|
||||
}
|
||||
|
||||
// topframe returns the two topmost frames of g, or thread if g is nil.
|
||||
func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
|
||||
var frames []Stackframe
|
||||
var err error
|
||||
|
||||
if g == nil {
|
||||
if thread.Blocked() {
|
||||
return Stackframe{}, Stackframe{}, ErrThreadBlocked{}
|
||||
}
|
||||
frames, err = ThreadStacktrace(thread, 1)
|
||||
} else {
|
||||
frames, err = g.Stacktrace(1, true)
|
||||
}
|
||||
if err != nil {
|
||||
return Stackframe{}, Stackframe{}, err
|
||||
}
|
||||
switch len(frames) {
|
||||
case 0:
|
||||
return Stackframe{}, Stackframe{}, errors.New("empty stack trace")
|
||||
case 1:
|
||||
return frames[0], Stackframe{}, nil
|
||||
default:
|
||||
return frames[0], frames[1], nil
|
||||
}
|
||||
}
|
||||
|
||||
// ErrNoSourceForPC is returned when the given address
|
||||
// does not correspond with a source file location.
|
||||
type ErrNoSourceForPC struct {
|
||||
pc uint64
|
||||
}
|
||||
|
||||
func (err *ErrNoSourceForPC) Error() string {
|
||||
return fmt.Sprintf("no source for PC %#x", err.pc)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Regardless of stepInto the following breakpoints will be set:
|
||||
// - a breakpoint on the first deferred function with NextDeferBreakpoint
|
||||
// kind, the list of all the addresses to deferreturn calls in this function
|
||||
// and condition checking that we remain on the same goroutine
|
||||
// - a breakpoint on each line of the function, with a condition checking
|
||||
// that we stay on the same stack frame and goroutine.
|
||||
// - a breakpoint on the return address of the function, with a condition
|
||||
// checking that we move to the previous stack frame and stay on the same
|
||||
// goroutine.
|
||||
//
|
||||
// The breakpoint on the return address is *not* set if the current frame is
|
||||
// an inlined call. For inlined calls topframe.Current.Fn is the function
|
||||
// where the inlining happened and the second set of breakpoints will also
|
||||
// cover the "return address".
|
||||
//
|
||||
// If inlinedStepOut is true this function implements the StepOut operation
|
||||
// for an inlined function call. Everything works the same as normal except
|
||||
// when removing instructions belonging to inlined calls we also remove all
|
||||
// instructions belonging to the current inlined call.
|
||||
func next(dbp Process, stepInto, inlinedStepOut bool) error {
|
||||
selg := dbp.SelectedGoroutine()
|
||||
curthread := dbp.CurrentThread()
|
||||
topframe, retframe, err := topframe(selg, curthread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if topframe.Current.Fn == nil {
|
||||
return &ErrNoSourceForPC{topframe.Current.PC}
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if inlinedStepOut && !topframe.Inlined {
|
||||
panic("next called with inlinedStepOut but topframe was not inlined")
|
||||
}
|
||||
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
}()
|
||||
|
||||
ext := filepath.Ext(topframe.Current.File)
|
||||
csource := ext != ".go" && ext != ".s"
|
||||
var thread MemoryReadWriter = curthread
|
||||
var regs Registers
|
||||
if selg != nil && selg.Thread != nil {
|
||||
thread = selg.Thread
|
||||
regs, err = selg.Thread.Registers(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
text, err := disassemble(thread, regs, dbp.Breakpoints(), dbp.BinInfo(), topframe.Current.Fn.Entry, topframe.Current.Fn.End, false)
|
||||
if err != nil && stepInto {
|
||||
return err
|
||||
}
|
||||
|
||||
sameGCond := SameGoroutineCondition(selg)
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.FrameOffset())
|
||||
sameFrameCond := andFrameoffCondition(sameGCond, topframe.FrameOffset())
|
||||
var sameOrRetFrameCond ast.Expr
|
||||
if sameGCond != nil {
|
||||
if topframe.Inlined {
|
||||
sameOrRetFrameCond = sameFrameCond
|
||||
} else {
|
||||
sameOrRetFrameCond = &ast.BinaryExpr{
|
||||
Op: token.LAND,
|
||||
X: sameGCond,
|
||||
Y: &ast.BinaryExpr{
|
||||
Op: token.LOR,
|
||||
X: frameoffCondition(topframe.FrameOffset()),
|
||||
Y: frameoffCondition(retframe.FrameOffset()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 := setStepIntoBreakpoint(dbp, []AsmInstruction{instr}, sameGCond); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Non-absolute call instruction, set a StepBreakpoint here
|
||||
if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, sameGCond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !csource {
|
||||
deferreturns := findDeferReturnCalls(text)
|
||||
|
||||
// Set breakpoint on the most recently deferred function (if any)
|
||||
var deferpc uint64
|
||||
if topframe.TopmostDefer != nil && topframe.TopmostDefer.DeferredPC != 0 {
|
||||
deferfn := dbp.BinInfo().PCToFunc(topframe.TopmostDefer.DeferredPC)
|
||||
var err error
|
||||
deferpc, err = FirstPCAfterPrologue(dbp, deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, sameGCond)
|
||||
if err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil && stepInto {
|
||||
bp.DeferReturns = deferreturns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add breakpoints on all the lines in the current function
|
||||
pcs, err := topframe.Current.Fn.cu.lineInfo.AllPCsBetween(topframe.Current.Fn.Entry, topframe.Current.Fn.End-1, topframe.Current.File, topframe.Current.Line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !stepInto {
|
||||
// Removing any PC range belonging to an inlined call
|
||||
frame := topframe
|
||||
if inlinedStepOut {
|
||||
frame = retframe
|
||||
}
|
||||
pcs, err = removeInlinedCalls(dbp, pcs, frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !csource {
|
||||
var covered bool
|
||||
for i := range pcs {
|
||||
if topframe.Current.Fn.Entry <= pcs[i] && pcs[i] < topframe.Current.Fn.End {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !covered {
|
||||
fn := dbp.BinInfo().PCToFunc(topframe.Ret)
|
||||
if selg != nil && fn != nil && fn.Name == "runtime.goexit" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, pc := range pcs {
|
||||
if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, sameFrameCond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if !topframe.Inlined {
|
||||
// Add a breakpoint on the return address for the current frame.
|
||||
// For inlined functions there is no need to do this, the set of PCs
|
||||
// returned by the AllPCsBetween call above already cover all instructions
|
||||
// of the containing function.
|
||||
bp, err := dbp.SetBreakpoint(topframe.Ret, NextBreakpoint, retFrameCond)
|
||||
if err != nil {
|
||||
if _, isexists := err.(BreakpointExistsError); isexists {
|
||||
if bp.Kind == NextBreakpoint {
|
||||
// If the return address shares the same address with one of the lines
|
||||
// of the function (because we are stepping through a recursive
|
||||
// function) then the corresponding breakpoint should be active both on
|
||||
// this frame and on the return frame.
|
||||
bp.Cond = sameOrRetFrameCond
|
||||
}
|
||||
}
|
||||
// Return address could be wrong, if we are unable to set a breakpoint
|
||||
// there it's ok.
|
||||
}
|
||||
if bp != nil {
|
||||
configureReturnBreakpoint(dbp.BinInfo(), bp, &topframe, retFrameCond)
|
||||
}
|
||||
}
|
||||
|
||||
if bp := curthread.Breakpoint(); bp.Breakpoint == nil {
|
||||
curthread.SetCurrentBreakpoint()
|
||||
}
|
||||
success = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func findDeferReturnCalls(text []AsmInstruction) []uint64 {
|
||||
const deferreturn = "runtime.deferreturn"
|
||||
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 == deferreturn {
|
||||
deferreturns = append(deferreturns, instr.Loc.PC)
|
||||
}
|
||||
}
|
||||
return deferreturns
|
||||
}
|
||||
|
||||
// Removes instructions belonging to inlined calls of topframe from pcs.
|
||||
// If includeCurrentFn is true it will also remove all instructions
|
||||
// belonging to the current function.
|
||||
func removeInlinedCalls(dbp Process, pcs []uint64, topframe Stackframe) ([]uint64, error) {
|
||||
bi := dbp.BinInfo()
|
||||
irdr := reader.InlineStack(bi.dwarf, topframe.Call.Fn.offset, 0)
|
||||
for irdr.Next() {
|
||||
e := irdr.Entry()
|
||||
if e.Offset == topframe.Call.Fn.offset {
|
||||
continue
|
||||
}
|
||||
ranges, err := bi.dwarf.Ranges(e)
|
||||
if err != nil {
|
||||
return pcs, err
|
||||
}
|
||||
for _, rng := range ranges {
|
||||
pcs = removePCsBetween(pcs, rng[0], rng[1], bi.staticBase)
|
||||
}
|
||||
irdr.SkipChildren()
|
||||
}
|
||||
return pcs, irdr.Err()
|
||||
}
|
||||
|
||||
func removePCsBetween(pcs []uint64, start, end, staticBase uint64) []uint64 {
|
||||
out := pcs[:0]
|
||||
for _, pc := range pcs {
|
||||
if pc < start+staticBase || pc >= end+staticBase {
|
||||
out = append(out, pc)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) error {
|
||||
if len(text) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
instr := text[0]
|
||||
|
||||
if instr.DestLoc == nil {
|
||||
// Call destination couldn't be resolved because this was not the
|
||||
// current instruction, therefore the step-into breakpoint can not be set.
|
||||
return nil
|
||||
}
|
||||
|
||||
fn := instr.DestLoc.Fn
|
||||
|
||||
// Skip unexported runtime functions
|
||||
if fn != nil && 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.
|
||||
|
||||
pc := instr.DestLoc.PC
|
||||
|
||||
// We want to skip the function prologue but we should only do it if the
|
||||
// destination address of the CALL instruction is the entry point of the
|
||||
// function.
|
||||
// Calls to runtime.duffzero and duffcopy inserted by the compiler can
|
||||
// sometimes point inside the body of those functions, well after the
|
||||
// prologue.
|
||||
if fn != nil && fn.Entry == instr.DestLoc.PC {
|
||||
pc, _ = FirstPCAfterPrologue(dbp, fn, false)
|
||||
}
|
||||
|
||||
// Set a breakpoint after the function's prologue
|
||||
if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getGVariable(thread Thread) (*Variable, error) {
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gaddr, hasgaddr := regs.GAddr()
|
||||
if !hasgaddr {
|
||||
gaddrbs := make([]byte, thread.Arch().PtrSize())
|
||||
_, err := thread.ReadMemory(gaddrbs, uintptr(regs.TLS()+thread.BinInfo().GStructOffset()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gaddr = binary.LittleEndian.Uint64(gaddrbs)
|
||||
}
|
||||
|
||||
return newGVariable(thread, uintptr(gaddr), thread.Arch().DerefTLS())
|
||||
}
|
||||
|
||||
func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) {
|
||||
typ, err := thread.BinInfo().findType("runtime.g")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := ""
|
||||
|
||||
if deref {
|
||||
typ = &godwarf.PtrType{
|
||||
CommonType: godwarf.CommonType{
|
||||
ByteSize: int64(thread.Arch().PtrSize()),
|
||||
Name: "",
|
||||
ReflectKind: reflect.Ptr,
|
||||
Offset: 0,
|
||||
},
|
||||
Type: typ,
|
||||
}
|
||||
} else {
|
||||
name = "runtime.curg"
|
||||
}
|
||||
|
||||
return newVariableFromThread(thread, 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 GetG(thread Thread) (*G, error) {
|
||||
if loc, _ := thread.Location(); loc.Fn != nil && loc.Fn.Name == "runtime.clone" {
|
||||
// When threads are executing runtime.clone the value of TLS is unreliable.
|
||||
return nil, nil
|
||||
}
|
||||
gaddr, err := getGVariable(thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err := gaddr.parseG()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g.ID == 0 {
|
||||
// The runtime uses a special goroutine with ID == 0 to mark that the
|
||||
// current goroutine is executing on the system stack (sometimes also
|
||||
// referred to as the g0 stack or scheduler stack, I'm not sure if there's
|
||||
// actually any difference between those).
|
||||
// For our purposes it's better if we always return the real goroutine
|
||||
// since the rest of the code assumes the goroutine ID is univocal.
|
||||
// The real 'current goroutine' is stored in g0.m.curg
|
||||
curgvar, err := g.variable.fieldVariable("m").structMember("curg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, err = curgvar.parseG()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.SystemStack = true
|
||||
}
|
||||
g.Thread = thread
|
||||
if loc, err := thread.Location(); err == nil {
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// ThreadScope returns an EvalScope for this thread.
|
||||
func ThreadScope(thread Thread) (*EvalScope, error) {
|
||||
locations, err := ThreadStacktrace(thread, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(locations) < 1 {
|
||||
return nil, errors.New("could not decode first frame")
|
||||
}
|
||||
return FrameToScope(thread.BinInfo(), thread, nil, locations...), nil
|
||||
}
|
||||
|
||||
// GoroutineScope returns an EvalScope for the goroutine running on this thread.
|
||||
func GoroutineScope(thread Thread) (*EvalScope, error) {
|
||||
locations, err := ThreadStacktrace(thread, 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(locations) < 1 {
|
||||
return nil, errors.New("could not decode first frame")
|
||||
}
|
||||
g, err := GetG(thread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return FrameToScope(thread.BinInfo(), thread, g, locations...), nil
|
||||
}
|
||||
|
||||
// onNextGoroutine returns true if this thread is on the goroutine requested by the current 'next' command
|
||||
func onNextGoroutine(thread Thread, breakpoints *BreakpointMap) (bool, error) {
|
||||
var bp *Breakpoint
|
||||
for i := range breakpoints.M {
|
||||
if breakpoints.M[i].Kind != UserBreakpoint && breakpoints.M[i].internalCond != nil {
|
||||
bp = breakpoints.M[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if bp == nil {
|
||||
return false, nil
|
||||
}
|
||||
// Internal breakpoint conditions can take multiple different forms:
|
||||
// Step into breakpoints:
|
||||
// runtime.curg.goid == X
|
||||
// Next or StepOut breakpoints:
|
||||
// runtime.curg.goid == X && runtime.frameoff == Y
|
||||
// Breakpoints that can be hit either by stepping on a line in the same
|
||||
// function or by returning from the function:
|
||||
// runtime.curg.goid == X && (runtime.frameoff == Y || runtime.frameoff == Z)
|
||||
// Here we are only interested in testing the runtime.curg.goid clause.
|
||||
w := onNextGoroutineWalker{thread: thread}
|
||||
ast.Walk(&w, bp.internalCond)
|
||||
return w.ret, w.err
|
||||
}
|
||||
|
||||
type onNextGoroutineWalker struct {
|
||||
thread Thread
|
||||
ret bool
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *onNextGoroutineWalker) Visit(n ast.Node) ast.Visitor {
|
||||
if binx, isbin := n.(*ast.BinaryExpr); isbin && binx.Op == token.EQL && exprToString(binx.X) == "runtime.curg.goid" {
|
||||
w.ret, w.err = evalBreakpointCondition(w.thread, n.(ast.Expr))
|
||||
return nil
|
||||
}
|
||||
return w
|
||||
}
|
||||
1257
vendor/github.com/go-delve/delve/pkg/proc/types.go
generated
vendored
Normal file
1257
vendor/github.com/go-delve/delve/pkg/proc/types.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2267
vendor/github.com/go-delve/delve/pkg/proc/variables.go
generated
vendored
Normal file
2267
vendor/github.com/go-delve/delve/pkg/proc/variables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
430
vendor/github.com/go-delve/delve/pkg/proc/winutil/regs.go
generated
vendored
Normal file
430
vendor/github.com/go-delve/delve/pkg/proc/winutil/regs.go
generated
vendored
Normal file
@@ -0,0 +1,430 @@
|
||||
package winutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/arch/x86/x86asm"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
)
|
||||
|
||||
// AMD64Registers represents CPU registers on an AMD64 processor.
|
||||
type AMD64Registers struct {
|
||||
rax uint64
|
||||
rbx uint64
|
||||
rcx uint64
|
||||
rdx uint64
|
||||
rdi uint64
|
||||
rsi uint64
|
||||
rbp uint64
|
||||
rsp uint64
|
||||
r8 uint64
|
||||
r9 uint64
|
||||
r10 uint64
|
||||
r11 uint64
|
||||
r12 uint64
|
||||
r13 uint64
|
||||
r14 uint64
|
||||
r15 uint64
|
||||
rip uint64
|
||||
eflags uint64
|
||||
cs uint64
|
||||
fs uint64
|
||||
gs uint64
|
||||
tls uint64
|
||||
Context *CONTEXT
|
||||
fltSave *XMM_SAVE_AREA32
|
||||
}
|
||||
|
||||
// NewAMD64Registers creates a new AMD64Registers struct from a CONTEXT
|
||||
// struct and the TEB base address of the thread.
|
||||
func NewAMD64Registers(context *CONTEXT, TebBaseAddress uint64, floatingPoint bool) *AMD64Registers {
|
||||
regs := &AMD64Registers{
|
||||
rax: uint64(context.Rax),
|
||||
rbx: uint64(context.Rbx),
|
||||
rcx: uint64(context.Rcx),
|
||||
rdx: uint64(context.Rdx),
|
||||
rdi: uint64(context.Rdi),
|
||||
rsi: uint64(context.Rsi),
|
||||
rbp: uint64(context.Rbp),
|
||||
rsp: uint64(context.Rsp),
|
||||
r8: uint64(context.R8),
|
||||
r9: uint64(context.R9),
|
||||
r10: uint64(context.R10),
|
||||
r11: uint64(context.R11),
|
||||
r12: uint64(context.R12),
|
||||
r13: uint64(context.R13),
|
||||
r14: uint64(context.R14),
|
||||
r15: uint64(context.R15),
|
||||
rip: uint64(context.Rip),
|
||||
eflags: uint64(context.EFlags),
|
||||
cs: uint64(context.SegCs),
|
||||
fs: uint64(context.SegFs),
|
||||
gs: uint64(context.SegGs),
|
||||
tls: TebBaseAddress,
|
||||
}
|
||||
|
||||
if floatingPoint {
|
||||
regs.fltSave = &context.FltSave
|
||||
}
|
||||
regs.Context = context
|
||||
|
||||
return regs
|
||||
}
|
||||
|
||||
// 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.rip},
|
||||
{"Rsp", r.rsp},
|
||||
{"Rax", r.rax},
|
||||
{"Rbx", r.rbx},
|
||||
{"Rcx", r.rcx},
|
||||
{"Rdx", r.rdx},
|
||||
{"Rdi", r.rdi},
|
||||
{"Rsi", r.rsi},
|
||||
{"Rbp", r.rbp},
|
||||
{"R8", r.r8},
|
||||
{"R9", r.r9},
|
||||
{"R10", r.r10},
|
||||
{"R11", r.r11},
|
||||
{"R12", r.r12},
|
||||
{"R13", r.r13},
|
||||
{"R14", r.r14},
|
||||
{"R15", r.r15},
|
||||
{"Eflags", r.eflags},
|
||||
{"Cs", r.cs},
|
||||
{"Fs", r.fs},
|
||||
{"Gs", r.gs},
|
||||
{"TLS", r.tls},
|
||||
}
|
||||
outlen := len(regs)
|
||||
if r.fltSave != nil && floatingPoint {
|
||||
outlen += 6 + 8 + 2 + 16
|
||||
}
|
||||
out := make([]proc.Register, 0, outlen)
|
||||
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 r.fltSave != nil && floatingPoint {
|
||||
out = proc.AppendWordReg(out, "CW", r.fltSave.ControlWord)
|
||||
out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
|
||||
out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord))
|
||||
out = proc.AppendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
|
||||
out = proc.AppendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
|
||||
out = proc.AppendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
|
||||
|
||||
for i := range r.fltSave.FloatRegisters {
|
||||
out = proc.AppendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
|
||||
}
|
||||
|
||||
out = proc.AppendMxcsrReg(out, "MXCSR", uint64(r.fltSave.MxCsr))
|
||||
out = proc.AppendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
|
||||
|
||||
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 {
|
||||
out = proc.AppendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the current program counter
|
||||
// i.e. the RIP CPU register.
|
||||
func (r *AMD64Registers) PC() uint64 {
|
||||
return r.rip
|
||||
}
|
||||
|
||||
// SP returns the stack pointer location,
|
||||
// i.e. the RSP register.
|
||||
func (r *AMD64Registers) SP() uint64 {
|
||||
return r.rsp
|
||||
}
|
||||
|
||||
func (r *AMD64Registers) BP() uint64 {
|
||||
return r.rbp
|
||||
}
|
||||
|
||||
// CX returns the value of the RCX register.
|
||||
func (r *AMD64Registers) CX() uint64 {
|
||||
return r.rcx
|
||||
}
|
||||
|
||||
// TLS returns the value of the register
|
||||
// that contains the location of the thread
|
||||
// local storage segment.
|
||||
func (r *AMD64Registers) TLS() uint64 {
|
||||
return r.tls
|
||||
}
|
||||
|
||||
// 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.rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.r8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.r9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.r10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.r11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.r12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.r13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.r14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.r15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.r8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.r9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.r10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.r11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.r12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.r13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.r14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.r15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.r8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.r9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.r10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.r11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.r12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.r13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.r14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.r15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.r8, nil
|
||||
case x86asm.R9:
|
||||
return r.r9, nil
|
||||
case x86asm.R10:
|
||||
return r.r10, nil
|
||||
case x86asm.R11:
|
||||
return r.r11, nil
|
||||
case x86asm.R12:
|
||||
return r.r12, nil
|
||||
case x86asm.R13:
|
||||
return r.r13, nil
|
||||
case x86asm.R14:
|
||||
return r.r14, nil
|
||||
case x86asm.R15:
|
||||
return r.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 = *r
|
||||
rr.Context = NewCONTEXT()
|
||||
*(rr.Context) = *(r.Context)
|
||||
rr.fltSave = &rr.Context.FltSave
|
||||
return &rr
|
||||
}
|
||||
|
||||
// M128A tracks the _M128A windows struct.
|
||||
type M128A struct {
|
||||
Low uint64
|
||||
High int64
|
||||
}
|
||||
|
||||
// XMM_SAVE_AREA32 tracks the _XMM_SAVE_AREA32 windows struct.
|
||||
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
|
||||
}
|
||||
|
||||
// CONTEXT tracks the _CONTEXT of windows.
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user