mirror of
https://github.com/beego/bee.git
synced 2025-06-21 10:00:18 +00:00
Use Delve v0.12.1 instead of master
This commit is contained in:
79
vendor/github.com/derekparker/delve/proc/arch.go
generated
vendored
Normal file
79
vendor/github.com/derekparker/delve/proc/arch.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
package proc
|
||||
|
||||
import "runtime"
|
||||
|
||||
// Arch defines an interface for representing a
|
||||
// CPU architecture.
|
||||
type Arch interface {
|
||||
SetGStructOffset(ver GoVersion, iscgo bool)
|
||||
PtrSize() int
|
||||
BreakpointInstruction() []byte
|
||||
BreakpointSize() int
|
||||
GStructOffset() uint64
|
||||
}
|
||||
|
||||
// AMD64 represents the AMD64 CPU architecture.
|
||||
type AMD64 struct {
|
||||
ptrSize int
|
||||
breakInstruction []byte
|
||||
breakInstructionLen int
|
||||
gStructOffset uint64
|
||||
hardwareBreakpointUsage []bool
|
||||
}
|
||||
|
||||
// AMD64Arch returns an initialized AMD64
|
||||
// struct.
|
||||
func AMD64Arch() *AMD64 {
|
||||
var breakInstr = []byte{0xCC}
|
||||
|
||||
return &AMD64{
|
||||
ptrSize: 8,
|
||||
breakInstruction: breakInstr,
|
||||
breakInstructionLen: len(breakInstr),
|
||||
hardwareBreakpointUsage: make([]bool, 4),
|
||||
}
|
||||
}
|
||||
|
||||
// SetGStructOffset sets the offset of the G struct on the AMD64
|
||||
// arch struct. The offset is dependent on the Go compiler Version
|
||||
// and whether or not the target program was externally linked.
|
||||
func (a *AMD64) SetGStructOffset(ver GoVersion, isextld bool) {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
a.gStructOffset = 0x8a0
|
||||
case "linux":
|
||||
a.gStructOffset = 0xfffffffffffffff0
|
||||
if isextld || ver.AfterOrEqual(GoVersion{1, 5, -1, 2, 0}) || ver.IsDevel() {
|
||||
a.gStructOffset += 8
|
||||
}
|
||||
case "windows":
|
||||
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
||||
// to G struct per:
|
||||
// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
|
||||
a.gStructOffset = 0x28
|
||||
}
|
||||
}
|
||||
|
||||
// PtrSize returns the size of a pointer
|
||||
// on this architecture.
|
||||
func (a *AMD64) PtrSize() int {
|
||||
return a.ptrSize
|
||||
}
|
||||
|
||||
// BreakpointInstruction returns the Breakpoint
|
||||
// instruction for this architecture.
|
||||
func (a *AMD64) BreakpointInstruction() []byte {
|
||||
return a.breakInstruction
|
||||
}
|
||||
|
||||
// BreakpointSize returns the size of the
|
||||
// breakpoint instruction on this architecture.
|
||||
func (a *AMD64) BreakpointSize() int {
|
||||
return a.breakInstructionLen
|
||||
}
|
||||
|
||||
// GStructOffset returns the offset of the G
|
||||
// struct in thread local storage.
|
||||
func (a *AMD64) GStructOffset() uint64 {
|
||||
return a.gStructOffset
|
||||
}
|
163
vendor/github.com/derekparker/delve/proc/breakpoints.go
generated
vendored
Normal file
163
vendor/github.com/derekparker/delve/proc/breakpoints.go
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Breakpoint represents a breakpoint. Stores information on the break
|
||||
// point including the byte of data that originally was stored at that
|
||||
// address.
|
||||
type Breakpoint struct {
|
||||
// File & line information for printing.
|
||||
FunctionName string
|
||||
File string
|
||||
Line int
|
||||
|
||||
Addr uint64 // Address breakpoint is set for.
|
||||
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
|
||||
Name string // User defined name of the breakpoint
|
||||
ID int // Monotonically increasing ID.
|
||||
Kind BreakpointKind // Whether this is an internal breakpoint (for next'ing or stepping).
|
||||
|
||||
// Breakpoint information
|
||||
Tracepoint bool // Tracepoint flag
|
||||
Goroutine bool // Retrieve goroutine information
|
||||
Stacktrace int // Number of stack frames to retrieve
|
||||
Variables []string // Variables to evaluate
|
||||
LoadArgs *LoadConfig
|
||||
LoadLocals *LoadConfig
|
||||
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
|
||||
TotalHitCount uint64 // Number of times a breakpoint has been reached
|
||||
|
||||
// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
|
||||
// will also check if the caller is runtime.gopanic or if the return
|
||||
// address is in the DeferReturns array.
|
||||
// Next uses NextDeferBreakpoints for the breakpoint it sets on the
|
||||
// deferred function, DeferReturns is populated with the
|
||||
// addresses of calls to runtime.deferreturn in the current
|
||||
// function. This insures that the breakpoint on the deferred
|
||||
// function only triggers on panic or on the defer call to
|
||||
// the function, not when the function is called directly
|
||||
DeferReturns []uint64
|
||||
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
|
||||
Cond ast.Expr
|
||||
}
|
||||
|
||||
// Breakpoint Kind determines the behavior of delve when the
|
||||
// breakpoint is reached.
|
||||
type BreakpointKind int
|
||||
|
||||
const (
|
||||
// UserBreakpoint is a user set breakpoint
|
||||
UserBreakpoint BreakpointKind = iota
|
||||
// NextBreakpoint is a breakpoint set by Next, Continue
|
||||
// will stop on it and delete it
|
||||
NextBreakpoint
|
||||
// NextDeferBreakpoint is a breakpoint set by Next on the
|
||||
// first deferred function. In addition to checking their condition
|
||||
// breakpoints of this kind will also check that the function has been
|
||||
// called by runtime.gopanic or through runtime.deferreturn.
|
||||
NextDeferBreakpoint
|
||||
// StepBreakpoint is a breakpoint set by Step on a CALL instruction,
|
||||
// Continue will set a new breakpoint (of NextBreakpoint kind) on the
|
||||
// destination of CALL, delete this breakpoint and then continue again
|
||||
StepBreakpoint
|
||||
)
|
||||
|
||||
func (bp *Breakpoint) String() string {
|
||||
return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
|
||||
}
|
||||
|
||||
// Clear this breakpoint appropriately depending on whether it is a
|
||||
// hardware or software breakpoint.
|
||||
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
|
||||
if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
|
||||
return nil, fmt.Errorf("could not clear breakpoint %s", err)
|
||||
}
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// BreakpointExistsError is returned when trying to set a breakpoint at
|
||||
// an address that already has a breakpoint set for it.
|
||||
type BreakpointExistsError struct {
|
||||
file string
|
||||
line int
|
||||
addr uint64
|
||||
}
|
||||
|
||||
func (bpe BreakpointExistsError) Error() string {
|
||||
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
|
||||
}
|
||||
|
||||
// InvalidAddressError represents the result of
|
||||
// attempting to set a breakpoint at an invalid address.
|
||||
type InvalidAddressError struct {
|
||||
address uint64
|
||||
}
|
||||
|
||||
func (iae InvalidAddressError) Error() string {
|
||||
return fmt.Sprintf("Invalid address %#v\n", iae.address)
|
||||
}
|
||||
|
||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
||||
_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
|
||||
return err
|
||||
}
|
||||
|
||||
func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
|
||||
if bp.Cond == nil {
|
||||
return true, nil
|
||||
}
|
||||
if bp.Kind == NextDeferBreakpoint {
|
||||
frames, err := thread.Stacktrace(2)
|
||||
if err == nil {
|
||||
ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
|
||||
isdeferreturn := false
|
||||
if len(frames) >= 1 {
|
||||
for _, pc := range bp.DeferReturns {
|
||||
if frames[0].Ret == pc {
|
||||
isdeferreturn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ispanic && !isdeferreturn {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
scope, err := thread.Scope()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
v, err := scope.evalAST(bp.Cond)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("error evaluating expression: %v", err)
|
||||
}
|
||||
if v.Unreadable != nil {
|
||||
return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
|
||||
}
|
||||
if v.Kind != reflect.Bool {
|
||||
return true, errors.New("condition expression not boolean")
|
||||
}
|
||||
return constant.BoolVal(v.Value), nil
|
||||
}
|
||||
|
||||
// Internal returns true for breakpoints not set directly by the user.
|
||||
func (bp *Breakpoint) Internal() bool {
|
||||
return bp.Kind != UserBreakpoint
|
||||
}
|
||||
|
||||
// NoBreakpointError is returned when trying to
|
||||
// clear a breakpoint that does not exist.
|
||||
type NoBreakpointError struct {
|
||||
addr uint64
|
||||
}
|
||||
|
||||
func (nbp NoBreakpointError) Error() string {
|
||||
return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
|
||||
}
|
67
vendor/github.com/derekparker/delve/proc/disasm.go
generated
vendored
Normal file
67
vendor/github.com/derekparker/delve/proc/disasm.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package proc
|
||||
|
||||
type AsmInstruction struct {
|
||||
Loc Location
|
||||
DestLoc *Location
|
||||
Bytes []byte
|
||||
Breakpoint bool
|
||||
AtPC bool
|
||||
Inst *ArchInst
|
||||
}
|
||||
|
||||
type AssemblyFlavour int
|
||||
|
||||
const (
|
||||
GNUFlavour = AssemblyFlavour(iota)
|
||||
IntelFlavour
|
||||
)
|
||||
|
||||
// Disassemble disassembles target memory between startPC and endPC
|
||||
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
|
||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
|
||||
func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool) ([]AsmInstruction, error) {
|
||||
if thread.dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
mem, err := thread.readMemory(uintptr(startPC), int(endPC-startPC))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := make([]AsmInstruction, 0, len(mem)/15)
|
||||
pc := startPC
|
||||
|
||||
var curpc uint64
|
||||
var regs Registers
|
||||
if currentGoroutine {
|
||||
regs, _ = thread.Registers(false)
|
||||
if regs != nil {
|
||||
curpc = regs.PC()
|
||||
}
|
||||
}
|
||||
|
||||
for len(mem) > 0 {
|
||||
bp, atbp := thread.dbp.Breakpoints[pc]
|
||||
if atbp {
|
||||
for i := range bp.OriginalData {
|
||||
mem[i] = bp.OriginalData[i]
|
||||
}
|
||||
}
|
||||
file, line, fn := thread.dbp.PCToLine(pc)
|
||||
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
|
||||
inst, err := asmDecode(mem, pc)
|
||||
if err == nil {
|
||||
atpc := currentGoroutine && (curpc == pc)
|
||||
destloc := thread.resolveCallArg(inst, atpc, regs)
|
||||
r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
|
||||
|
||||
pc += uint64(inst.Size())
|
||||
mem = mem[inst.Size():]
|
||||
} else {
|
||||
r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
|
||||
pc++
|
||||
mem = mem[1:]
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
183
vendor/github.com/derekparker/delve/proc/disasm_amd64.go
generated
vendored
Normal file
183
vendor/github.com/derekparker/delve/proc/disasm_amd64.go
generated
vendored
Normal file
@ -0,0 +1,183 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"rsc.io/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))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
|
||||
if inst.Inst == nil {
|
||||
return "?"
|
||||
}
|
||||
|
||||
var text string
|
||||
|
||||
switch flavour {
|
||||
case GNUFlavour:
|
||||
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
|
||||
case IntelFlavour:
|
||||
fallthrough
|
||||
default:
|
||||
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
|
||||
}
|
||||
|
||||
if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
|
||||
text += " " + inst.DestLoc.Fn.Name
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func (inst *AsmInstruction) IsCall() bool {
|
||||
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
|
||||
}
|
||||
|
||||
func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers) *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
|
||||
}
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
base, err1 := regs.Get(int(arg.Base))
|
||||
index, err2 := regs.Get(int(arg.Index))
|
||||
if err1 != nil || err2 != nil {
|
||||
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, err := thread.readMemory(addr, inst.MemBytes)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pc = binary.LittleEndian.Uint64(pcbytes)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
file, line, fn := thread.dbp.PCToLine(pc)
|
||||
if fn == nil {
|
||||
return nil
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
|
||||
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
|
||||
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
|
||||
text, err := dbp.CurrentThread.Disassemble(fn.Entry, fn.End, false)
|
||||
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/derekparker/delve/proc/doc.go
generated
vendored
Normal file
9
vendor/github.com/derekparker/delve/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
|
1121
vendor/github.com/derekparker/delve/proc/eval.go
generated
vendored
Normal file
1121
vendor/github.com/derekparker/delve/proc/eval.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
283
vendor/github.com/derekparker/delve/proc/exc.h
generated
vendored
Normal file
283
vendor/github.com/derekparker/delve/proc/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_ */
|
768
vendor/github.com/derekparker/delve/proc/exc_user_darwin.c
generated
vendored
Normal file
768
vendor/github.com/derekparker/delve/proc/exc_user_darwin.c
generated
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
112
vendor/github.com/derekparker/delve/proc/exec_darwin.c
generated
vendored
Normal file
112
vendor/github.com/derekparker/delve/proc/exec_darwin.c
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
#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);
|
||||
}
|
10
vendor/github.com/derekparker/delve/proc/exec_darwin.h
generated
vendored
Normal file
10
vendor/github.com/derekparker/delve/proc/exec_darwin.h
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
#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*);
|
113
vendor/github.com/derekparker/delve/proc/go_version.go
generated
vendored
Normal file
113
vendor/github.com/derekparker/delve/proc/go_version.go
generated
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GoVersion represents the Go version of
|
||||
// the Go compiler version used to compile
|
||||
// the target binary.
|
||||
type GoVersion struct {
|
||||
Major int
|
||||
Minor int
|
||||
Rev int
|
||||
Beta int
|
||||
RC int
|
||||
}
|
||||
|
||||
func ParseVersionString(ver string) (GoVersion, bool) {
|
||||
var r GoVersion
|
||||
var err1, err2, err3 error
|
||||
|
||||
if strings.HasPrefix(ver, "devel") {
|
||||
return GoVersion{-1, 0, 0, 0, 0}, true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(ver, "go") {
|
||||
ver := strings.Split(ver, " ")[0]
|
||||
v := strings.SplitN(ver[2:], ".", 3)
|
||||
switch len(v) {
|
||||
case 2:
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
vr := strings.SplitN(v[1], "beta", 2)
|
||||
if len(vr) == 2 {
|
||||
r.Beta, err3 = strconv.Atoi(vr[1])
|
||||
} else {
|
||||
vr = strings.SplitN(v[1], "rc", 2)
|
||||
if len(vr) == 2 {
|
||||
r.RC, err3 = strconv.Atoi(vr[1])
|
||||
} else {
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
if err2 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
|
||||
r.Minor, err2 = strconv.Atoi(vr[0])
|
||||
r.Rev = -1
|
||||
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return r, true
|
||||
|
||||
case 3:
|
||||
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
r.Rev, err3 = strconv.Atoi(v[2])
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return r, true
|
||||
|
||||
default:
|
||||
return GoVersion{}, false
|
||||
}
|
||||
}
|
||||
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
// AfterOrEqual returns whether one GoVersion is after or
|
||||
// equal to the other.
|
||||
func (v *GoVersion) AfterOrEqual(b GoVersion) bool {
|
||||
if v.Major < b.Major {
|
||||
return false
|
||||
} else if v.Major > b.Major {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Minor < b.Minor {
|
||||
return false
|
||||
} else if v.Minor > b.Minor {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Rev < b.Rev {
|
||||
return false
|
||||
} else if v.Rev > b.Rev {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Beta < b.Beta {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.RC < b.RC {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsDevel returns whether the GoVersion
|
||||
// is a development version.
|
||||
func (v *GoVersion) IsDevel() bool {
|
||||
return v.Major < 0
|
||||
}
|
119
vendor/github.com/derekparker/delve/proc/mach_exc.defs
generated
vendored
Normal file
119
vendor/github.com/derekparker/delve/proc/mach_exc.defs
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. The rights granted to you under the License
|
||||
* may not be used to create, or enable the creation or redistribution of,
|
||||
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||
* circumvent, violate, or enable the circumvention or violation of, any
|
||||
* terms of an Apple operating system software license agreement.
|
||||
*
|
||||
* Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/*
|
||||
* @OSF_COPYRIGHT@
|
||||
*/
|
||||
/*
|
||||
* Mach Operating System
|
||||
* Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission to use, copy, modify and distribute this software and its
|
||||
* documentation is hereby granted, provided that both the copyright
|
||||
* notice and this permission notice appear in all copies of the
|
||||
* software, derivative works or modified versions, and any portions
|
||||
* thereof, and that both notices appear in supporting documentation.
|
||||
*
|
||||
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
|
||||
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
|
||||
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
||||
*
|
||||
* Carnegie Mellon requests users of this software to return to
|
||||
*
|
||||
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
|
||||
* School of Computer Science
|
||||
* Carnegie Mellon University
|
||||
* Pittsburgh PA 15213-3890
|
||||
*
|
||||
* any improvements or extensions that they make and grant Carnegie Mellon
|
||||
* the rights to redistribute these changes.
|
||||
*/
|
||||
/*
|
||||
*/
|
||||
/*
|
||||
* Abstract:
|
||||
* MiG definitions file for Mach exception interface.
|
||||
*/
|
||||
|
||||
subsystem
|
||||
#if KERNEL_USER
|
||||
KernelUser
|
||||
#endif
|
||||
mach_exc 2405;
|
||||
|
||||
#include <mach/std_types.defs>
|
||||
#include <mach/mach_types.defs>
|
||||
|
||||
ServerPrefix catch_;
|
||||
|
||||
type mach_exception_data_t = array[*:2] of int64_t;
|
||||
type exception_type_t = int;
|
||||
|
||||
routine mach_exception_raise(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
thread : mach_port_move_send_t;
|
||||
task : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
thread : mach_port_t;
|
||||
task : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t
|
||||
);
|
||||
|
||||
routine mach_exception_raise_state(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t, const;
|
||||
inout flavor : int;
|
||||
old_state : thread_state_t, const;
|
||||
out new_state : thread_state_t);
|
||||
|
||||
routine mach_exception_raise_state_identity(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
thread : mach_port_move_send_t;
|
||||
task : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
thread : mach_port_t;
|
||||
task : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t;
|
||||
inout flavor : int;
|
||||
old_state : thread_state_t;
|
||||
out new_state : thread_state_t);
|
||||
|
||||
/* vim: set ft=c : */
|
283
vendor/github.com/derekparker/delve/proc/mach_exc.h
generated
vendored
Normal file
283
vendor/github.com/derekparker/delve/proc/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_ */
|
768
vendor/github.com/derekparker/delve/proc/mach_exc_user_darwin.c
generated
vendored
Normal file
768
vendor/github.com/derekparker/delve/proc/mach_exc_user_darwin.c
generated
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
57
vendor/github.com/derekparker/delve/proc/mem.go
generated
vendored
Normal file
57
vendor/github.com/derekparker/delve/proc/mem.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package proc
|
||||
|
||||
const cacheEnabled = true
|
||||
|
||||
type memoryReadWriter interface {
|
||||
readMemory(addr uintptr, size int) (data []byte, err error)
|
||||
writeMemory(addr uintptr, data []byte) (written int, err error)
|
||||
}
|
||||
|
||||
type memCache struct {
|
||||
cacheAddr uintptr
|
||||
cache []byte
|
||||
mem memoryReadWriter
|
||||
}
|
||||
|
||||
func (m *memCache) contains(addr uintptr, size int) bool {
|
||||
return addr >= m.cacheAddr && addr <= (m.cacheAddr+uintptr(len(m.cache)-size))
|
||||
}
|
||||
|
||||
func (m *memCache) readMemory(addr uintptr, size int) (data []byte, err error) {
|
||||
if m.contains(addr, size) {
|
||||
d := make([]byte, size)
|
||||
copy(d, m.cache[addr-m.cacheAddr:])
|
||||
return d, nil
|
||||
}
|
||||
|
||||
return m.mem.readMemory(addr, size)
|
||||
}
|
||||
|
||||
func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
return m.mem.writeMemory(addr, data)
|
||||
}
|
||||
|
||||
func cacheMemory(mem memoryReadWriter, addr uintptr, size int) memoryReadWriter {
|
||||
if !cacheEnabled {
|
||||
return mem
|
||||
}
|
||||
if size <= 0 {
|
||||
return mem
|
||||
}
|
||||
if cacheMem, isCache := mem.(*memCache); isCache {
|
||||
if cacheMem.contains(addr, size) {
|
||||
return mem
|
||||
} else {
|
||||
cache, err := cacheMem.mem.readMemory(addr, size)
|
||||
if err != nil {
|
||||
return mem
|
||||
}
|
||||
return &memCache{addr, cache, mem}
|
||||
}
|
||||
}
|
||||
cache, err := mem.readMemory(addr, size)
|
||||
if err != nil {
|
||||
return mem
|
||||
}
|
||||
return &memCache{addr, cache, mem}
|
||||
}
|
189
vendor/github.com/derekparker/delve/proc/moduledata.go
generated
vendored
Normal file
189
vendor/github.com/derekparker/delve/proc/moduledata.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// delve counterpart to runtime.moduledata
|
||||
type moduleData struct {
|
||||
types, etypes uintptr
|
||||
typemapVar *Variable
|
||||
}
|
||||
|
||||
func (dbp *Process) loadModuleData() (err error) {
|
||||
dbp.loadModuleDataOnce.Do(func() {
|
||||
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||
var md *Variable
|
||||
md, err = scope.packageVarAddr("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
|
||||
}
|
||||
|
||||
dbp.moduleData = append(dbp.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
|
||||
|
||||
md = nextVar.maybeDereference()
|
||||
if md.Unreadable != nil {
|
||||
err = md.Unreadable
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, error) {
|
||||
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
|
||||
if err := dbp.loadModuleData(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var md *moduleData
|
||||
for i := range dbp.moduleData {
|
||||
if typeAddr >= dbp.moduleData[i].types && typeAddr < dbp.moduleData[i].etypes {
|
||||
md = &dbp.moduleData[i]
|
||||
}
|
||||
}
|
||||
|
||||
rtyp, err := dbp.findType("runtime._type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if md == nil {
|
||||
v, err := dbp.reflectOffsMapAccess(off)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue(LoadConfig{false, 1, 0, 0, -1})
|
||||
addr, _ := constant.Int64Val(v.Value)
|
||||
return v.newVariable(v.Name, uintptr(addr), rtyp), nil
|
||||
}
|
||||
|
||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.CurrentThread)); t != nil {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
res := md.types + uintptr(off)
|
||||
|
||||
return dbp.CurrentThread.newVariable("", res, rtyp), nil
|
||||
}
|
||||
|
||||
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
|
||||
if err = dbp.loadModuleData(); err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
for _, md := range dbp.moduleData {
|
||||
if typeAddr >= md.types && typeAddr < md.etypes {
|
||||
return dbp.loadName(md.types + off)
|
||||
}
|
||||
}
|
||||
|
||||
v, err := dbp.reflectOffsMapAccess(off)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
resv := v.maybeDereference()
|
||||
if resv.Unreadable != nil {
|
||||
return "", "", 0, resv.Unreadable
|
||||
}
|
||||
|
||||
return dbp.loadName(resv.Addr)
|
||||
}
|
||||
|
||||
func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
|
||||
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||
reflectOffs, err := scope.packageVarAddr("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)), dbp.CurrentThread))
|
||||
}
|
||||
|
||||
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 (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||
off := addr
|
||||
namedata, err := dbp.CurrentThread.readMemory(off, 3)
|
||||
off += 3
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
namelen := uint16(namedata[1]<<8) | uint16(namedata[2])
|
||||
|
||||
rawstr, err := dbp.CurrentThread.readMemory(off, int(namelen))
|
||||
off += uintptr(namelen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
name = string(rawstr)
|
||||
|
||||
if namedata[0]&nameflagHasTag != 0 {
|
||||
taglendata, err := dbp.CurrentThread.readMemory(off, 2)
|
||||
off += 2
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1])
|
||||
|
||||
rawstr, err := dbp.CurrentThread.readMemory(off, int(taglen))
|
||||
off += uintptr(taglen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
tag = string(rawstr)
|
||||
}
|
||||
|
||||
if namedata[0]&nameflagHasPkg != 0 {
|
||||
pkgdata, err := dbp.CurrentThread.readMemory(off, 4)
|
||||
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
|
||||
}
|
942
vendor/github.com/derekparker/delve/proc/proc.go
generated
vendored
Normal file
942
vendor/github.com/derekparker/delve/proc/proc.go
generated
vendored
Normal file
@ -0,0 +1,942 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/dwarf/frame"
|
||||
"github.com/derekparker/delve/dwarf/line"
|
||||
"github.com/derekparker/delve/dwarf/reader"
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// Process represents all of the information the debugger
|
||||
// is holding onto regarding the process we are debugging.
|
||||
type Process struct {
|
||||
Pid int // Process Pid
|
||||
Process *os.Process // Pointer to process struct for the actual process we are debugging
|
||||
LastModified time.Time // Time the executable of this process was last modified
|
||||
|
||||
// Breakpoint table, holds information on breakpoints.
|
||||
// Maps instruction address to Breakpoint struct.
|
||||
Breakpoints map[uint64]*Breakpoint
|
||||
|
||||
// List of threads mapped as such: pid -> *Thread
|
||||
Threads map[int]*Thread
|
||||
|
||||
// Active thread
|
||||
CurrentThread *Thread
|
||||
|
||||
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
|
||||
// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
||||
SelectedGoroutine *G
|
||||
|
||||
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||
packageMap map[string]string
|
||||
|
||||
allGCache []*G
|
||||
dwarf *dwarf.Data
|
||||
goSymTable *gosym.Table
|
||||
frameEntries frame.FrameDescriptionEntries
|
||||
lineInfo line.DebugLines
|
||||
os *OSProcessDetails
|
||||
arch Arch
|
||||
breakpointIDCounter int
|
||||
internalBreakpointIDCounter int
|
||||
firstStart bool
|
||||
halt bool
|
||||
exited bool
|
||||
ptraceChan chan func()
|
||||
ptraceDoneChan chan interface{}
|
||||
types map[string]dwarf.Offset
|
||||
|
||||
loadModuleDataOnce sync.Once
|
||||
moduleData []moduleData
|
||||
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
||||
}
|
||||
|
||||
var NotExecutableErr = errors.New("not an executable file")
|
||||
|
||||
// New returns an initialized Process struct. Before returning,
|
||||
// it will also launch a goroutine in order to handle ptrace(2)
|
||||
// functions. For more information, see the documentation on
|
||||
// `handlePtraceFuncs`.
|
||||
func New(pid int) *Process {
|
||||
dbp := &Process{
|
||||
Pid: pid,
|
||||
Threads: make(map[int]*Thread),
|
||||
Breakpoints: make(map[uint64]*Breakpoint),
|
||||
firstStart: true,
|
||||
os: new(OSProcessDetails),
|
||||
ptraceChan: make(chan func()),
|
||||
ptraceDoneChan: make(chan interface{}),
|
||||
nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry),
|
||||
}
|
||||
// TODO: find better way to determine proc arch (perhaps use executable file info)
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
dbp.arch = AMD64Arch()
|
||||
}
|
||||
go dbp.handlePtraceFuncs()
|
||||
return dbp
|
||||
}
|
||||
|
||||
// ProcessExitedError indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
type ProcessExitedError struct {
|
||||
Pid int
|
||||
Status int
|
||||
}
|
||||
|
||||
func (pe ProcessExitedError) Error() string {
|
||||
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
// Detach from the process being debugged, optionally killing it.
|
||||
func (dbp *Process) Detach(kill bool) (err error) {
|
||||
if dbp.Running() {
|
||||
if err = dbp.Halt(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !kill {
|
||||
// Clean up any breakpoints we've set.
|
||||
for _, bp := range dbp.Breakpoints {
|
||||
if bp != nil {
|
||||
_, err := dbp.ClearBreakpoint(bp.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = PtraceDetach(dbp.Pid, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if kill {
|
||||
err = killProcess(dbp.Pid)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Exited returns whether the debugged
|
||||
// process has exited.
|
||||
func (dbp *Process) Exited() bool {
|
||||
return dbp.exited
|
||||
}
|
||||
|
||||
// Running returns whether the debugged
|
||||
// process is currently executing.
|
||||
func (dbp *Process) Running() bool {
|
||||
for _, th := range dbp.Threads {
|
||||
if th.running {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// LoadInformation finds the executable and then uses it
|
||||
// to parse the following information:
|
||||
// * Dwarf .debug_frame section
|
||||
// * Dwarf .debug_line section
|
||||
// * Go symbol table.
|
||||
func (dbp *Process) LoadInformation(path string) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
exe, path, err := dbp.findExecutable(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, err := os.Stat(path)
|
||||
if err == nil {
|
||||
dbp.LastModified = fi.ModTime()
|
||||
}
|
||||
|
||||
wg.Add(5)
|
||||
go dbp.loadProcessInformation(&wg)
|
||||
go dbp.parseDebugFrame(exe, &wg)
|
||||
go dbp.obtainGoSymbols(exe, &wg)
|
||||
go dbp.parseDebugLineInfo(exe, &wg)
|
||||
go dbp.loadTypeMap(&wg)
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindFileLocation returns the PC for a given file:line.
|
||||
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
||||
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
|
||||
pc, fn, err := dbp.goSymTable.LineToPC(fileName, lineno)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if fn.Entry == pc {
|
||||
pc, _ = dbp.FirstPCAfterPrologue(fn, true)
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// FindFunctionLocation finds address of a function's line
|
||||
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
|
||||
// If lineOffset is passed FindFunctionLocation will return the address of that line
|
||||
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
|
||||
// Note that setting breakpoints at that address will cause surprising behavior:
|
||||
// https://github.com/derekparker/delve/issues/170
|
||||
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
origfn := dbp.goSymTable.LookupFunc(funcName)
|
||||
if origfn == nil {
|
||||
return 0, fmt.Errorf("Could not find function %s\n", funcName)
|
||||
}
|
||||
|
||||
if firstLine {
|
||||
return dbp.FirstPCAfterPrologue(origfn, false)
|
||||
} else if lineOffset > 0 {
|
||||
filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry)
|
||||
breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset)
|
||||
return breakAddr, err
|
||||
}
|
||||
|
||||
return origfn.Entry, nil
|
||||
}
|
||||
|
||||
// CurrentLocation returns the location of the current thread.
|
||||
func (dbp *Process) CurrentLocation() (*Location, error) {
|
||||
return dbp.CurrentThread.Location()
|
||||
}
|
||||
|
||||
// RequestManualStop sets the `halt` flag and
|
||||
// sends SIGSTOP to all threads.
|
||||
func (dbp *Process) RequestManualStop() error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
dbp.halt = true
|
||||
return dbp.requestManualStop()
|
||||
}
|
||||
|
||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
||||
// break point table. Setting a break point must be thread specific due to
|
||||
// ptrace actions needing the thread to be in a signal-delivery-stop.
|
||||
func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
|
||||
tid := dbp.CurrentThread.ID
|
||||
|
||||
if bp, ok := dbp.FindBreakpoint(addr); ok {
|
||||
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||
}
|
||||
|
||||
f, l, fn := dbp.goSymTable.PCToLine(uint64(addr))
|
||||
if fn == nil {
|
||||
return nil, InvalidAddressError{address: addr}
|
||||
}
|
||||
|
||||
newBreakpoint := &Breakpoint{
|
||||
FunctionName: fn.Name,
|
||||
File: f,
|
||||
Line: l,
|
||||
Addr: addr,
|
||||
Kind: kind,
|
||||
Cond: cond,
|
||||
HitCount: map[int]uint64{},
|
||||
}
|
||||
|
||||
if kind != UserBreakpoint {
|
||||
dbp.internalBreakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.internalBreakpointIDCounter
|
||||
} else {
|
||||
dbp.breakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.breakpointIDCounter
|
||||
}
|
||||
|
||||
thread := dbp.Threads[tid]
|
||||
originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newBreakpoint.OriginalData = originalData
|
||||
dbp.Breakpoints[addr] = newBreakpoint
|
||||
|
||||
return newBreakpoint, nil
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the breakpoint at addr.
|
||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
bp, ok := dbp.FindBreakpoint(addr)
|
||||
if !ok {
|
||||
return nil, NoBreakpointError{addr: addr}
|
||||
}
|
||||
|
||||
if _, err := bp.Clear(dbp.CurrentThread); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(dbp.Breakpoints, addr)
|
||||
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// Status returns the status of the current main thread context.
|
||||
func (dbp *Process) Status() *WaitStatus {
|
||||
return dbp.CurrentThread.Status
|
||||
}
|
||||
|
||||
// Next continues execution until the next source line.
|
||||
func (dbp *Process) Next() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for i := range dbp.Breakpoints {
|
||||
if dbp.Breakpoints[i].Internal() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
}
|
||||
|
||||
if err = dbp.next(false); err != nil {
|
||||
switch err.(type) {
|
||||
case ThreadBlockedError, NoReturnAddr: // Noop
|
||||
default:
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
}
|
||||
|
||||
// Continue continues execution of the debugged
|
||||
// process. It will continue until it hits a breakpoint
|
||||
// or is otherwise stopped.
|
||||
func (dbp *Process) Continue() error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for {
|
||||
if err := dbp.resume(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbp.allGCache = nil
|
||||
for _, th := range dbp.Threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dbp.Halt(); err != nil {
|
||||
return dbp.exitGuard(err)
|
||||
}
|
||||
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dbp.pickCurrentThread(trapthread); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case dbp.CurrentThread.CurrentBreakpoint == nil:
|
||||
// runtime.Breakpoint or manual stop
|
||||
if dbp.CurrentThread.onRuntimeBreakpoint() {
|
||||
for i := 0; i < 2; i++ {
|
||||
if err = dbp.CurrentThread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return dbp.conditionErrors()
|
||||
case dbp.CurrentThread.onTriggeredInternalBreakpoint():
|
||||
if dbp.CurrentThread.CurrentBreakpoint.Kind == StepBreakpoint {
|
||||
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
||||
if err := dbp.conditionErrors(); err != nil {
|
||||
return err
|
||||
}
|
||||
pc, err := dbp.CurrentThread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
text, err := dbp.CurrentThread.Disassemble(pc, pc+maxInstructionLength, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// here we either set a breakpoint into the destination of the CALL
|
||||
// instruction or we determined that the called function is hidden,
|
||||
// either way we need to resume execution
|
||||
if err = dbp.setStepIntoBreakpoint(text, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := dbp.ClearInternalBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.conditionErrors()
|
||||
}
|
||||
case dbp.CurrentThread.onTriggeredBreakpoint():
|
||||
onNextGoroutine, err := dbp.CurrentThread.onNextGoroutine()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if onNextGoroutine {
|
||||
err := dbp.ClearInternalBreakpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return dbp.conditionErrors()
|
||||
default:
|
||||
// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) conditionErrors() error {
|
||||
var condErr error
|
||||
for _, th := range dbp.Threads {
|
||||
if th.CurrentBreakpoint != nil && th.BreakpointConditionError != nil {
|
||||
if condErr == nil {
|
||||
condErr = th.BreakpointConditionError
|
||||
} else {
|
||||
return fmt.Errorf("multiple errors evaluating conditions")
|
||||
}
|
||||
}
|
||||
}
|
||||
return condErr
|
||||
}
|
||||
|
||||
// pick a new dbp.CurrentThread, with the following priority:
|
||||
// - a thread with onTriggeredInternalBreakpoint() == true
|
||||
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
|
||||
// - trapthread
|
||||
func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
|
||||
for _, th := range dbp.Threads {
|
||||
if th.onTriggeredInternalBreakpoint() {
|
||||
return dbp.SwitchThread(th.ID)
|
||||
}
|
||||
}
|
||||
if trapthread.onTriggeredBreakpoint() {
|
||||
return dbp.SwitchThread(trapthread.ID)
|
||||
}
|
||||
for _, th := range dbp.Threads {
|
||||
if th.onTriggeredBreakpoint() {
|
||||
return dbp.SwitchThread(th.ID)
|
||||
}
|
||||
}
|
||||
return dbp.SwitchThread(trapthread.ID)
|
||||
}
|
||||
|
||||
// Step will continue until another source line is reached.
|
||||
// Will step into functions.
|
||||
func (dbp *Process) Step() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for i := range dbp.Breakpoints {
|
||||
if dbp.Breakpoints[i].Internal() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
}
|
||||
|
||||
if err = dbp.next(true); err != nil {
|
||||
switch err.(type) {
|
||||
case ThreadBlockedError, NoReturnAddr: // Noop
|
||||
default:
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
}
|
||||
|
||||
// Returns an expression that evaluates to true when the current goroutine is g
|
||||
func sameGoroutineCondition(g *G) ast.Expr {
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return &ast.BinaryExpr{
|
||||
Op: token.EQL,
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "runtime"},
|
||||
Sel: &ast.Ident{Name: "curg"},
|
||||
},
|
||||
Sel: &ast.Ident{Name: "goid"},
|
||||
},
|
||||
Y: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(g.ID)},
|
||||
}
|
||||
}
|
||||
|
||||
// StepInstruction will continue the current thread for exactly
|
||||
// one instruction. This method affects only the thread
|
||||
// asssociated with the selected goroutine. All other
|
||||
// threads will remain stopped.
|
||||
func (dbp *Process) StepInstruction() (err error) {
|
||||
if dbp.SelectedGoroutine == nil {
|
||||
return errors.New("cannot single step: no selected goroutine")
|
||||
}
|
||||
if dbp.SelectedGoroutine.thread == nil {
|
||||
// Step called on parked goroutine
|
||||
if _, err := dbp.SetBreakpoint(dbp.SelectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.Continue()
|
||||
}
|
||||
dbp.allGCache = nil
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
dbp.SelectedGoroutine.thread.clearBreakpointState()
|
||||
err = dbp.SelectedGoroutine.thread.StepInstruction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.SelectedGoroutine.thread.SetCurrentBreakpoint()
|
||||
}
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
// function currently being executed or a deferred function is executed
|
||||
func (dbp *Process) StepOut() error {
|
||||
cond := sameGoroutineCondition(dbp.SelectedGoroutine)
|
||||
|
||||
topframe, err := topframe(dbp.SelectedGoroutine, dbp.CurrentThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pcs := []uint64{}
|
||||
|
||||
var deferpc uint64 = 0
|
||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.DeferPC != 0 {
|
||||
_, _, deferfn := dbp.goSymTable.PCToLine(dbp.SelectedGoroutine.DeferPC)
|
||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pcs = append(pcs, deferpc)
|
||||
}
|
||||
}
|
||||
|
||||
if topframe.Ret == 0 && deferpc == 0 {
|
||||
return errors.New("nothing to stepout to")
|
||||
}
|
||||
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
|
||||
if err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil {
|
||||
// For StepOut we do not want to step into the deferred function
|
||||
// when it's called by runtime.deferreturn so we do not populate
|
||||
// DeferReturns.
|
||||
bp.DeferReturns = []uint64{}
|
||||
}
|
||||
}
|
||||
|
||||
if topframe.Ret != 0 {
|
||||
if err := dbp.setInternalBreakpoints(topframe.Current.PC, []uint64{topframe.Ret}, NextBreakpoint, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
}
|
||||
|
||||
// SwitchThread changes from current thread to the thread specified by `tid`.
|
||||
func (dbp *Process) SwitchThread(tid int) error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
if th, ok := dbp.Threads[tid]; ok {
|
||||
dbp.CurrentThread = th
|
||||
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// SwitchGoroutine changes from current thread to the thread
|
||||
// running the specified goroutine.
|
||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
g, err := dbp.FindGoroutine(gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and SelectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.thread != nil {
|
||||
return dbp.SwitchThread(g.thread.ID)
|
||||
}
|
||||
dbp.SelectedGoroutine = g
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoroutinesInfo returns an array of G structures representing the information
|
||||
// Delve cares about from the internal runtime G structure.
|
||||
func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
if dbp.allGCache != nil {
|
||||
return dbp.allGCache, nil
|
||||
}
|
||||
|
||||
var (
|
||||
threadg = map[int]*Thread{}
|
||||
allg []*G
|
||||
rdr = dbp.DwarfReader()
|
||||
)
|
||||
|
||||
for i := range dbp.Threads {
|
||||
if dbp.Threads[i].blocked() {
|
||||
continue
|
||||
}
|
||||
g, _ := dbp.Threads[i].GetG()
|
||||
if g != nil {
|
||||
threadg[g.ID] = dbp.Threads[i]
|
||||
}
|
||||
}
|
||||
|
||||
addr, err := rdr.AddrFor("runtime.allglen")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allglenBytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
||||
|
||||
rdr.Seek(0)
|
||||
allgentryaddr, err := rdr.AddrFor("runtime.allgs")
|
||||
if err != nil {
|
||||
// try old name (pre Go 1.6)
|
||||
allgentryaddr, err = rdr.AddrFor("runtime.allg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
|
||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||
|
||||
for i := uint64(0); i < allglen; i++ {
|
||||
gvar, err := dbp.CurrentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, err := gvar.parseG()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if thread, allocated := threadg[g.ID]; allocated {
|
||||
loc, err := thread.Location()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.thread = thread
|
||||
// Prefer actual thread location information.
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
if g.Status != Gdead {
|
||||
allg = append(allg, g)
|
||||
}
|
||||
}
|
||||
dbp.allGCache = allg
|
||||
return allg, nil
|
||||
}
|
||||
|
||||
func (g *G) Thread() *Thread {
|
||||
return g.thread
|
||||
}
|
||||
|
||||
// Halt stops all threads.
|
||||
func (dbp *Process) Halt() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for _, th := range dbp.Threads {
|
||||
if err := th.Halt(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Registers obtains register values from the
|
||||
// "current" thread of the traced process.
|
||||
func (dbp *Process) Registers() (Registers, error) {
|
||||
return dbp.CurrentThread.Registers(false)
|
||||
}
|
||||
|
||||
// PC returns the PC of the current thread.
|
||||
func (dbp *Process) PC() (uint64, error) {
|
||||
return dbp.CurrentThread.PC()
|
||||
}
|
||||
|
||||
// CurrentBreakpoint returns the breakpoint the current thread
|
||||
// is stopped at.
|
||||
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
|
||||
return dbp.CurrentThread.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// DwarfReader returns a reader for the dwarf data
|
||||
func (dbp *Process) DwarfReader() *reader.Reader {
|
||||
return reader.New(dbp.dwarf)
|
||||
}
|
||||
|
||||
// Sources returns list of source files that comprise the debugged binary.
|
||||
func (dbp *Process) Sources() map[string]*gosym.Obj {
|
||||
return dbp.goSymTable.Files
|
||||
}
|
||||
|
||||
// Funcs returns list of functions present in the debugged program.
|
||||
func (dbp *Process) Funcs() []gosym.Func {
|
||||
return dbp.goSymTable.Funcs
|
||||
}
|
||||
|
||||
// Types returns list of types present in the debugged program.
|
||||
func (dbp *Process) Types() ([]string, error) {
|
||||
types := make([]string, 0, len(dbp.types))
|
||||
for k := range dbp.types {
|
||||
types = append(types, k)
|
||||
}
|
||||
return types, nil
|
||||
}
|
||||
|
||||
// PCToLine converts an instruction address to a file/line/function.
|
||||
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
||||
return dbp.goSymTable.PCToLine(pc)
|
||||
}
|
||||
|
||||
// FindBreakpointByID finds the breakpoint for the given ID.
|
||||
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
||||
for _, bp := range dbp.Breakpoints {
|
||||
if bp.ID == id {
|
||||
return bp, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindBreakpoint finds the breakpoint for the given pc.
|
||||
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
||||
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
||||
if bp, ok := dbp.Breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok {
|
||||
return bp, true
|
||||
}
|
||||
// Directly use addr to lookup breakpoint.
|
||||
if bp, ok := dbp.Breakpoints[pc]; ok {
|
||||
return bp, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Returns a new Process struct.
|
||||
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
|
||||
if attach {
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.Pid) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _, err = dbp.wait(dbp.Pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
proc, err := os.FindProcess(dbp.Pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.Process = proc
|
||||
err = dbp.LoadInformation(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dbp.updateThreadList(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ver, isextld, err := dbp.getGoInformation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.arch.SetGStructOffset(ver, isextld)
|
||||
// SelectedGoroutine can not be set correctly by the call to updateThreadList
|
||||
// because without calling SetGStructOffset we can not read the G struct of CurrentThread
|
||||
// but without calling updateThreadList we can not examine memory to determine
|
||||
// the offset of g struct inside TLS
|
||||
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
|
||||
|
||||
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
||||
if err == nil {
|
||||
bp, err := dbp.SetBreakpoint(panicpc, UserBreakpoint, nil)
|
||||
if err == nil {
|
||||
bp.Name = "unrecovered-panic"
|
||||
bp.ID = -1
|
||||
dbp.breakpointIDCounter--
|
||||
}
|
||||
}
|
||||
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) ClearInternalBreakpoints() error {
|
||||
for _, bp := range dbp.Breakpoints {
|
||||
if !bp.Internal() {
|
||||
continue
|
||||
}
|
||||
if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range dbp.Threads {
|
||||
if dbp.Threads[i].CurrentBreakpoint != nil && dbp.Threads[i].CurrentBreakpoint.Internal() {
|
||||
dbp.Threads[i].CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) handlePtraceFuncs() {
|
||||
// We must ensure here that we are running on the same thread during
|
||||
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
|
||||
// all commands after PTRACE_ATTACH to come from the same thread.
|
||||
runtime.LockOSThread()
|
||||
|
||||
for fn := range dbp.ptraceChan {
|
||||
fn()
|
||||
dbp.ptraceDoneChan <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) execPtraceFunc(fn func()) {
|
||||
dbp.ptraceChan <- fn
|
||||
<-dbp.ptraceDoneChan
|
||||
}
|
||||
|
||||
func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) {
|
||||
vv, err := dbp.EvalPackageVariable("runtime.buildVersion", LoadConfig{true, 0, 64, 0, 0})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Could not determine version number: %v\n", err)
|
||||
return
|
||||
}
|
||||
if vv.Unreadable != nil {
|
||||
err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable)
|
||||
return
|
||||
}
|
||||
|
||||
ver, ok := ParseVersionString(constant.StringVal(vv.Value))
|
||||
if !ok {
|
||||
err = fmt.Errorf("Could not parse version number: %v\n", vv.Value)
|
||||
return
|
||||
}
|
||||
|
||||
rdr := dbp.DwarfReader()
|
||||
rdr.Seek(0)
|
||||
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
|
||||
if err != nil {
|
||||
return ver, isextld, err
|
||||
}
|
||||
if prod, ok := entry.Val(dwarf.AttrProducer).(string); ok && (strings.HasPrefix(prod, "GNU AS")) {
|
||||
isextld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindGoroutine returns a G struct representing the goroutine
|
||||
// specified by `gid`.
|
||||
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
|
||||
if gid == -1 {
|
||||
return dbp.SelectedGoroutine, nil
|
||||
}
|
||||
|
||||
gs, err := dbp.GoroutinesInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range gs {
|
||||
if gs[i].ID == gid {
|
||||
return gs[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown goroutine %d", gid)
|
||||
}
|
||||
|
||||
// ConvertEvalScope returns a new EvalScope in the context of the
|
||||
// specified goroutine ID and stack frame.
|
||||
func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
g, err := dbp.FindGoroutine(gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g == nil {
|
||||
return dbp.CurrentThread.Scope()
|
||||
}
|
||||
|
||||
var out EvalScope
|
||||
|
||||
if g.thread == nil {
|
||||
out.Thread = dbp.CurrentThread
|
||||
} else {
|
||||
out.Thread = g.thread
|
||||
}
|
||||
|
||||
locs, err := g.Stacktrace(frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if frame >= len(locs) {
|
||||
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
||||
}
|
||||
|
||||
out.PC, out.CFA = locs[frame].Current.PC, locs[frame].CFA
|
||||
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) postExit() {
|
||||
dbp.exited = true
|
||||
close(dbp.ptraceChan)
|
||||
close(dbp.ptraceDoneChan)
|
||||
}
|
231
vendor/github.com/derekparker/delve/proc/proc_darwin.c
generated
vendored
Normal file
231
vendor/github.com/derekparker/delve/proc/proc_darwin.c
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
#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;
|
||||
}
|
511
vendor/github.com/derekparker/delve/proc/proc_darwin.go
generated
vendored
Normal file
511
vendor/github.com/derekparker/delve/proc/proc_darwin.go
generated
vendored
Normal file
@ -0,0 +1,511 @@
|
||||
package proc
|
||||
|
||||
// #include "proc_darwin.h"
|
||||
// #include "threads_darwin.h"
|
||||
// #include "exec_darwin.h"
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/debug/macho"
|
||||
|
||||
"github.com/derekparker/delve/dwarf/frame"
|
||||
"github.com/derekparker/delve/dwarf/line"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// 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) (*Process, error) {
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, NotExecutableErr
|
||||
}
|
||||
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
|
||||
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 {
|
||||
err = dbp.updateThreadListForTask(C.get_task_for_pid(C.int(dbp.Pid)))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err != couldNotGetThreadCount && err != couldNotGetThreadList {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.allGCache = nil
|
||||
for _, th := range dbp.Threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.Halt(); err != nil {
|
||||
return nil, dbp.exitGuard(err)
|
||||
}
|
||||
|
||||
_, err = dbp.waitForStop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.os.initialized = true
|
||||
dbp, err = initializeDebugProcess(dbp, argv0Go, false)
|
||||
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) (*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
|
||||
|
||||
return initializeDebugProcess(dbp, "", true)
|
||||
}
|
||||
|
||||
// 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)
|
||||
)
|
||||
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 (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section("__debug_frame")
|
||||
debugInfoSec := exe.Section("__debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section("__debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find __debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section("__gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section("__gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section("__debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section("__debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find __debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*macho.File, string, error) {
|
||||
if path == "" {
|
||||
path = C.GoString(C.find_executable(C.int(dbp.Pid)))
|
||||
}
|
||||
exe, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if exe.Cpu != macho.CpuAmd64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = exe.DWARF()
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return exe, path, nil
|
||||
}
|
||||
|
||||
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, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
||||
|
||||
case C.MACH_RCV_INTERRUPTED:
|
||||
if !dbp.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 {
|
||||
if dbp.halt {
|
||||
dbp.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) setCurrentBreakpoints(trapthread *Thread) error {
|
||||
ports, err := dbp.waitForStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trapthread.SetCurrentBreakpoint()
|
||||
for _, port := range ports {
|
||||
if th, ok := dbp.Threads[port]; ok {
|
||||
err := th.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
var status sys.WaitStatus
|
||||
wpid, err := sys.Wait4(pid, &status, options, nil)
|
||||
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 ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
// all threads stopped over a breakpoint are made to step over it
|
||||
for _, thread := range dbp.Threads {
|
||||
if thread.CurrentBreakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
for _, thread := range dbp.Threads {
|
||||
if err := thread.resume(); err != nil {
|
||||
return dbp.exitGuard(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
54
vendor/github.com/derekparker/delve/proc/proc_darwin.h
generated
vendored
Normal file
54
vendor/github.com/derekparker/delve/proc/proc_darwin.h
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
#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);
|
482
vendor/github.com/derekparker/delve/proc/proc_linux.go
generated
vendored
Normal file
482
vendor/github.com/derekparker/delve/proc/proc_linux.go
generated
vendored
Normal file
@ -0,0 +1,482 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/dwarf/frame"
|
||||
"github.com/derekparker/delve/dwarf/line"
|
||||
"golang.org/x/debug/elf"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
var (
|
||||
proc *exec.Cmd
|
||||
err error
|
||||
)
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, NotExecutableErr
|
||||
}
|
||||
dbp := New(0)
|
||||
dbp.execPtraceFunc(func() {
|
||||
proc = exec.Command(cmd[0])
|
||||
proc.Args = cmd
|
||||
proc.Stdout = os.Stdout
|
||||
proc.Stderr = os.Stderr
|
||||
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
|
||||
if wd != "" {
|
||||
proc.Dir = wd
|
||||
}
|
||||
err = proc.Start()
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp.Pid = proc.Process.Pid
|
||||
_, _, err = dbp.wait(proc.Process.Pid, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||
}
|
||||
return initializeDebugProcess(dbp, proc.Path, false)
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
return initializeDebugProcess(New(pid), "", true)
|
||||
}
|
||||
|
||||
// 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.wait(tid, 0)
|
||||
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.wait(tid, 0); 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 nil
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*elf.File, string, error) {
|
||||
if path == "" {
|
||||
path = fmt.Sprintf("/proc/%d/exe", dbp.Pid)
|
||||
}
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
elfFile, err := elf.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if elfFile.Machine != elf.EM_X86_64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = elfFile.DWARF()
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return elfFile, path, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section(".debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section(".gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section(".gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section(".debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*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, ProcessExitedError{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
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
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 status.StopSignal() == sys.SIGTRAP && dbp.halt {
|
||||
th.running = false
|
||||
dbp.halt = false
|
||||
return th, nil
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP {
|
||||
th.running = false
|
||||
return th, nil
|
||||
}
|
||||
if th != nil {
|
||||
// TODO(dp) alert user about unexpected signals here.
|
||||
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
||||
if err == sys.ESRCH {
|
||||
return nil, ProcessExitedError{Pid: dbp.Pid}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.Pid))
|
||||
if err == nil {
|
||||
// removes newline character
|
||||
comm = bytes.TrimSuffix(comm, []byte("\n"))
|
||||
}
|
||||
|
||||
if comm == nil || len(comm) <= 0 {
|
||||
stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.Pid))
|
||||
if err != nil {
|
||||
fmt.Printf("Could not read proc stat: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.Pid)
|
||||
rexp, err := regexp.Compile(expr)
|
||||
if err != nil {
|
||||
fmt.Printf("Regexp compile error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
match := rexp.FindSubmatch(stat)
|
||||
if match == nil {
|
||||
fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.Pid)
|
||||
os.Exit(1)
|
||||
}
|
||||
comm = match[1]
|
||||
}
|
||||
dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
|
||||
}
|
||||
|
||||
func 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
|
||||
}
|
||||
|
||||
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) setCurrentBreakpoints(trapthread *Thread) error {
|
||||
for _, th := range dbp.Threads {
|
||||
if th.CurrentBreakpoint == nil {
|
||||
err := th.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
if err != sys.ESRCH {
|
||||
return err
|
||||
}
|
||||
if status(dbp.Pid, dbp.os.comm) == StatusZombie {
|
||||
_, err := dbp.trapWait(-1)
|
||||
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 != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
for _, thread := range dbp.Threads {
|
||||
if err := thread.resume(); err != nil && err != sys.ESRCH {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGINT)
|
||||
}
|
643
vendor/github.com/derekparker/delve/proc/proc_windows.go
generated
vendored
Normal file
643
vendor/github.com/derekparker/delve/proc/proc_windows.go
generated
vendored
Normal file
@ -0,0 +1,643 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/derekparker/delve/dwarf/frame"
|
||||
"github.com/derekparker/delve/dwarf/line"
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Windows specific information.
|
||||
type OSProcessDetails struct {
|
||||
hProcess syscall.Handle
|
||||
breakThread int
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the binary exists and is an executable file
|
||||
if filepath.Base(cmd[0]) == cmd[0] {
|
||||
if _, err := exec.LookPath(cmd[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
peFile, err := openExecutablePath(argv0Go)
|
||||
if err != nil {
|
||||
return nil, NotExecutableErr
|
||||
}
|
||||
peFile.Close()
|
||||
|
||||
// Duplicate the stdin/stdout/stderr handles
|
||||
files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)}
|
||||
p, _ := syscall.GetCurrentProcess()
|
||||
fd := make([]syscall.Handle, len(files))
|
||||
for i := range files {
|
||||
err := syscall.DuplicateHandle(p, syscall.Handle(files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CloseHandle(syscall.Handle(fd[i]))
|
||||
}
|
||||
|
||||
argv0, err := syscall.UTF16PtrFromString(argv0Go)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create suitable command line for CreateProcess
|
||||
// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L326
|
||||
// adapted from standard library makeCmdLine
|
||||
// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L86
|
||||
var cmdLineGo string
|
||||
if len(cmd) >= 1 {
|
||||
for _, v := range cmd {
|
||||
if cmdLineGo != "" {
|
||||
cmdLineGo += " "
|
||||
}
|
||||
cmdLineGo += syscall.EscapeArg(v)
|
||||
}
|
||||
}
|
||||
|
||||
var cmdLine *uint16
|
||||
if cmdLineGo != "" {
|
||||
if cmdLine, err = syscall.UTF16PtrFromString(cmdLineGo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var workingDir *uint16
|
||||
if wd != "" {
|
||||
if workingDir, err = syscall.UTF16PtrFromString(wd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the startup info and create process
|
||||
si := new(sys.StartupInfo)
|
||||
si.Cb = uint32(unsafe.Sizeof(*si))
|
||||
si.Flags = syscall.STARTF_USESTDHANDLES
|
||||
si.StdInput = sys.Handle(fd[0])
|
||||
si.StdOutput = sys.Handle(fd[1])
|
||||
si.StdErr = sys.Handle(fd[2])
|
||||
pi := new(sys.ProcessInformation)
|
||||
|
||||
dbp := New(0)
|
||||
dbp.execPtraceFunc(func() {
|
||||
if wd == "" {
|
||||
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, nil, si, pi)
|
||||
} else {
|
||||
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, workingDir, si, pi)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sys.CloseHandle(sys.Handle(pi.Process))
|
||||
sys.CloseHandle(sys.Handle(pi.Thread))
|
||||
|
||||
dbp.Pid = int(pi.ProcessId)
|
||||
|
||||
return newDebugProcess(dbp, argv0Go)
|
||||
}
|
||||
|
||||
// newDebugProcess prepares process pid for debugging.
|
||||
func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
||||
// It should not actually be possible for the
|
||||
// call to waitForDebugEvent to fail, since Windows
|
||||
// will always fire a CREATE_PROCESS_DEBUG_EVENT event
|
||||
// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
|
||||
// Attaching with DebugActiveProcess has similar effect.
|
||||
var err error
|
||||
var tid, exitCode int
|
||||
dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
|
||||
}
|
||||
// Suspend all threads so that the call to _ContinueDebugEvent will
|
||||
// not resume the target.
|
||||
for _, thread := range dbp.Threads {
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(dbp.Pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return initializeDebugProcess(dbp, exepath, false)
|
||||
}
|
||||
|
||||
// findExePath searches for process pid, and returns its executable path.
|
||||
func findExePath(pid int) (string, error) {
|
||||
// Original code suggested different approach (see below).
|
||||
// Maybe it could be useful in the future.
|
||||
//
|
||||
// Find executable path from PID/handle on Windows:
|
||||
// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
|
||||
|
||||
p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer syscall.CloseHandle(p)
|
||||
|
||||
n := uint32(128)
|
||||
for {
|
||||
buf := make([]uint16, int(n))
|
||||
err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
|
||||
switch err {
|
||||
case syscall.ERROR_INSUFFICIENT_BUFFER:
|
||||
// try bigger buffer
|
||||
n *= 2
|
||||
// but stop if it gets too big
|
||||
if n > 10000 {
|
||||
return "", err
|
||||
}
|
||||
case nil:
|
||||
return syscall.UTF16ToString(buf[:n]), nil
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
// TODO: Probably should have SeDebugPrivilege before starting here.
|
||||
err := _DebugActiveProcess(uint32(pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exepath, err := findExePath(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newDebugProcess(New(pid), exepath)
|
||||
}
|
||||
|
||||
// Kill kills the process.
|
||||
func (dbp *Process) Kill() error {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if !dbp.Threads[dbp.Pid].Stopped() {
|
||||
return errors.New("process must be stopped in order to kill it")
|
||||
}
|
||||
// TODO: Should not have to ignore failures here,
|
||||
// but some tests appear to Kill twice causing
|
||||
// this to fail on second attempt.
|
||||
_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
|
||||
dbp.exited = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() error {
|
||||
return _DebugBreakProcess(dbp.os.hProcess)
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
// We ignore this request since threads are being
|
||||
// tracked as they are created/killed in waitForDebugEvent.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*Thread, error) {
|
||||
if thread, ok := dbp.Threads[threadID]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
thread := &Thread{
|
||||
ID: threadID,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
thread.os.hThread = hThread
|
||||
dbp.Threads[threadID] = thread
|
||||
if dbp.CurrentThread == nil {
|
||||
dbp.SwitchThread(thread.ID)
|
||||
}
|
||||
if suspendNewThreads {
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := debugFrameSec.Data()
|
||||
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
|
||||
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||
for _, s := range f.Symbols {
|
||||
if s.Name != name {
|
||||
continue
|
||||
}
|
||||
if s.SectionNumber <= 0 {
|
||||
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
||||
}
|
||||
if len(f.Sections) < int(s.SectionNumber) {
|
||||
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no %s symbol found", name)
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
|
||||
ssym, err := findPESymbol(f, sname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
esym, err := findPESymbol(f, ename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ssym.SectionNumber != esym.SectionNumber {
|
||||
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
|
||||
}
|
||||
sect := f.Sections[ssym.SectionNumber-1]
|
||||
data, err := sect.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[ssym.Value:esym.Value], nil
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func pcln(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
|
||||
var imageBase uint64
|
||||
switch oh := exe.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
imageBase = uint64(oh.ImageBase)
|
||||
case *pe.OptionalHeader64:
|
||||
imageBase = oh.ImageBase
|
||||
default:
|
||||
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
|
||||
}
|
||||
if sect := exe.Section(".text"); sect != nil {
|
||||
textStart = imageBase + uint64(sect.VirtualAddress)
|
||||
}
|
||||
if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
|
||||
// We didn't find the symbols, so look for the names used in 1.3 and earlier.
|
||||
// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
|
||||
var err2 error
|
||||
if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
|
||||
// Same as above.
|
||||
var err2 error
|
||||
if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
return textStart, symtab, pclntab, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
_, symdat, pclndat, err := pcln(exe)
|
||||
if err != nil {
|
||||
fmt.Println("could not get Go symbols", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := sec.Data()
|
||||
if err != nil && uint32(len(debugLine)) < sec.Size {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
||||
debugLine = debugLine[:sec.VirtualSize]
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*pe.File, string, error) {
|
||||
peFile, err := openExecutablePath(path)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = dwarfFromPE(peFile)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return peFile, path, nil
|
||||
}
|
||||
|
||||
func openExecutablePath(path string) (*pe.File, error) {
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pe.NewFile(f)
|
||||
}
|
||||
|
||||
// Adapted from src/debug/pe/file.go: pe.(*File).DWARF()
|
||||
func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
|
||||
// There are many other DWARF sections, but these
|
||||
// are the ones the debug/dwarf package uses.
|
||||
// Don't bother loading others.
|
||||
var names = [...]string{"abbrev", "info", "line", "str"}
|
||||
var dat [len(names)][]byte
|
||||
for i, name := range names {
|
||||
name = ".debug_" + name
|
||||
s := f.Section(name)
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
b, err := s.Data()
|
||||
if err != nil && uint32(len(b)) < s.Size {
|
||||
return nil, err
|
||||
}
|
||||
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
|
||||
b = b[:s.VirtualSize]
|
||||
}
|
||||
dat[i] = b
|
||||
}
|
||||
|
||||
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
|
||||
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
|
||||
}
|
||||
|
||||
type waitForDebugEventFlags int
|
||||
|
||||
const (
|
||||
waitBlocking waitForDebugEventFlags = 1 << iota
|
||||
waitSuspendNewThreads
|
||||
)
|
||||
|
||||
func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
|
||||
var debugEvent _DEBUG_EVENT
|
||||
shouldExit := false
|
||||
for {
|
||||
continueStatus := uint32(_DBG_CONTINUE)
|
||||
var milliseconds uint32 = 0
|
||||
if flags&waitBlocking != 0 {
|
||||
milliseconds = syscall.INFINITE
|
||||
}
|
||||
// Wait for a debug event...
|
||||
err := _WaitForDebugEvent(&debugEvent, milliseconds)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// ... handle each event kind ...
|
||||
unionPtr := unsafe.Pointer(&debugEvent.U[0])
|
||||
switch debugEvent.DebugEventCode {
|
||||
case _CREATE_PROCESS_DEBUG_EVENT:
|
||||
debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
|
||||
hFile := debugInfo.File
|
||||
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||
err = syscall.CloseHandle(hFile)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
dbp.os.hProcess = debugInfo.Process
|
||||
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
break
|
||||
case _CREATE_THREAD_DEBUG_EVENT:
|
||||
debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
|
||||
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
break
|
||||
case _EXIT_THREAD_DEBUG_EVENT:
|
||||
delete(dbp.Threads, int(debugEvent.ThreadId))
|
||||
break
|
||||
case _OUTPUT_DEBUG_STRING_EVENT:
|
||||
//TODO: Handle debug output strings
|
||||
break
|
||||
case _LOAD_DLL_DEBUG_EVENT:
|
||||
debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
|
||||
hFile := debugInfo.File
|
||||
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||
err = syscall.CloseHandle(hFile)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
break
|
||||
case _UNLOAD_DLL_DEBUG_EVENT:
|
||||
break
|
||||
case _RIP_EVENT:
|
||||
break
|
||||
case _EXCEPTION_DEBUG_EVENT:
|
||||
exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
|
||||
if code := exception.ExceptionRecord.ExceptionCode; code == _EXCEPTION_BREAKPOINT || code == _EXCEPTION_SINGLE_STEP {
|
||||
tid := int(debugEvent.ThreadId)
|
||||
dbp.os.breakThread = tid
|
||||
return tid, 0, nil
|
||||
} else {
|
||||
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
|
||||
}
|
||||
case _EXIT_PROCESS_DEBUG_EVENT:
|
||||
debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
|
||||
exitCode = int(debugInfo.ExitCode)
|
||||
shouldExit = true
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
|
||||
}
|
||||
|
||||
// .. and then continue unless we received an event that indicated we should break into debugger.
|
||||
err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if shouldExit {
|
||||
return 0, exitCode, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
var err error
|
||||
var tid, exitCode int
|
||||
dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
|
||||
}
|
||||
th := dbp.Threads[tid]
|
||||
return th, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
return 0, nil, fmt.Errorf("not implemented: wait")
|
||||
}
|
||||
|
||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
||||
// While the debug event that stopped the target was being propagated
|
||||
// other target threads could generate other debug events.
|
||||
// After this function we need to know about all the threads
|
||||
// stopped on a breakpoint. To do that we first suspend all target
|
||||
// threads and then repeatedly call _ContinueDebugEvent followed by
|
||||
// waitForDebugEvent in non-blocking mode.
|
||||
// We need to explicitly call SuspendThread because otherwise the
|
||||
// call to _ContinueDebugEvent will resume execution of some of the
|
||||
// target threads.
|
||||
|
||||
err := trapthread.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, thread := range dbp.Threads {
|
||||
thread.running = false
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
var err error
|
||||
var tid int
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(dbp.Pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||
if err == nil {
|
||||
tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tid == 0 {
|
||||
break
|
||||
}
|
||||
err = dbp.Threads[tid].SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
for _, thread := range dbp.Threads {
|
||||
if thread.CurrentBreakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, thread := range dbp.Threads {
|
||||
thread.running = true
|
||||
_, err := _ResumeThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
p, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.Kill()
|
||||
}
|
28
vendor/github.com/derekparker/delve/proc/ptrace_darwin.go
generated
vendored
Normal file
28
vendor/github.com/derekparker/delve/proc/ptrace_darwin.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package proc
|
||||
|
||||
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
|
||||
}
|
96
vendor/github.com/derekparker/delve/proc/ptrace_linux.go
generated
vendored
Normal file
96
vendor/github.com/derekparker/delve/proc/ptrace_linux.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// 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 PtraceXsave, err error) {
|
||||
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.PtraceFpRegs)), 0, 0)
|
||||
if err == syscall.Errno(0) {
|
||||
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) {
|
||||
return
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= iov.Len {
|
||||
return
|
||||
}
|
||||
xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
|
||||
xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
|
||||
xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
|
||||
|
||||
if xcomp_bv&(1<<63) != 0 {
|
||||
// compact format not supported
|
||||
return
|
||||
}
|
||||
|
||||
if xstate_bv&(1<<2) == 0 {
|
||||
// AVX state not present
|
||||
return
|
||||
}
|
||||
|
||||
avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:iov.Len]
|
||||
regset.AvxState = true
|
||||
copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
|
||||
|
||||
return
|
||||
}
|
13
vendor/github.com/derekparker/delve/proc/ptrace_windows.go
generated
vendored
Normal file
13
vendor/github.com/derekparker/delve/proc/ptrace_windows.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func PtraceAttach(pid int) error {
|
||||
return fmt.Errorf("not implemented: PtraceAttach")
|
||||
}
|
||||
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
return _DebugActiveProcessStop(uint32(tid))
|
||||
}
|
230
vendor/github.com/derekparker/delve/proc/registers.go
generated
vendored
Normal file
230
vendor/github.com/derekparker/delve/proc/registers.go
generated
vendored
Normal file
@ -0,0 +1,230 @@
|
||||
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
|
||||
CX() uint64
|
||||
TLS() uint64
|
||||
Get(int) (uint64, error)
|
||||
SetPC(*Thread, uint64) error
|
||||
Slice() []Register
|
||||
}
|
||||
|
||||
type Register struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func appendWordReg(regs []Register, name string, value uint16) []Register {
|
||||
return append(regs, Register{name, fmt.Sprintf("%#04x", value)})
|
||||
}
|
||||
|
||||
func appendDwordReg(regs []Register, name string, value uint32) []Register {
|
||||
return append(regs, Register{name, fmt.Sprintf("%#08x", value)})
|
||||
}
|
||||
|
||||
func appendQwordReg(regs []Register, name string, value uint64) []Register {
|
||||
return append(regs, Register{name, fmt.Sprintf("%#016x", value)})
|
||||
}
|
||||
|
||||
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
|
||||
return append(regs, Register{name, descr.Describe(value, size)})
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
return append(regs, Register{fmt.Sprintf("ST(%d)", index), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
|
||||
}
|
||||
|
||||
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, out.String()})
|
||||
}
|
||||
|
||||
var UnknownRegisterError = errors.New("unknown register")
|
||||
|
||||
// Registers obtains register values from the debugged process.
|
||||
func (t *Thread) Registers(floatingPoint bool) (Registers, error) {
|
||||
return registers(t, floatingPoint)
|
||||
}
|
||||
|
||||
// PC returns the current PC for this thread.
|
||||
func (t *Thread) PC() (uint64, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return regs.PC(), nil
|
||||
}
|
||||
|
||||
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, " "))
|
||||
}
|
363
vendor/github.com/derekparker/delve/proc/registers_darwin_amd64.go
generated
vendored
Normal file
363
vendor/github.com/derekparker/delve/proc/registers_darwin_amd64.go
generated
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
package proc
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"rsc.io/x86/x86asm"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// 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 []Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []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([]Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Rflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the 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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(thread *Thread, 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
|
||||
}
|
||||
|
||||
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.rax >> 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, UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (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 = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
|
||||
regs.fpregs = appendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
|
||||
regs.fpregs = appendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
|
||||
|
||||
for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} {
|
||||
stb := C.GoBytes(unsafe.Pointer(st), 10)
|
||||
mantissa := binary.LittleEndian.Uint64(stb[:8])
|
||||
exponent := binary.LittleEndian.Uint16(stb[8:])
|
||||
regs.fpregs = appendX87Reg(regs.fpregs, i, exponent, mantissa)
|
||||
}
|
||||
|
||||
regs.fpregs = appendFlagReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr), mxcsrDescription, 32)
|
||||
regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
|
||||
|
||||
for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} {
|
||||
regs.fpregs = appendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
|
||||
}
|
||||
}
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) saveRegisters() (Registers, error) {
|
||||
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not save register contents")
|
||||
}
|
||||
return &Regs{rip: uint64(thread.os.registers.__rip), rsp: uint64(thread.os.registers.__rsp)}, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) restoreRegisters() error {
|
||||
kret := C.set_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not save register contents")
|
||||
}
|
||||
return nil
|
||||
}
|
325
vendor/github.com/derekparker/delve/proc/registers_linux_amd64.go
generated
vendored
Normal file
325
vendor/github.com/derekparker/delve/proc/registers_linux_amd64.go
generated
vendored
Normal file
@ -0,0 +1,325 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"rsc.io/x86/x86asm"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Regs is a wrapper for sys.PtraceRegs.
|
||||
type Regs struct {
|
||||
regs *sys.PtraceRegs
|
||||
fpregs []Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.regs.Rip},
|
||||
{"Rsp", r.regs.Rsp},
|
||||
{"Rax", r.regs.Rax},
|
||||
{"Rbx", r.regs.Rbx},
|
||||
{"Rcx", r.regs.Rcx},
|
||||
{"Rdx", r.regs.Rdx},
|
||||
{"Rdi", r.regs.Rdi},
|
||||
{"Rsi", r.regs.Rsi},
|
||||
{"Rbp", r.regs.Rbp},
|
||||
{"R8", r.regs.R8},
|
||||
{"R9", r.regs.R9},
|
||||
{"R10", r.regs.R10},
|
||||
{"R11", r.regs.R11},
|
||||
{"R12", r.regs.R12},
|
||||
{"R13", r.regs.R13},
|
||||
{"R14", r.regs.R14},
|
||||
{"R15", r.regs.R15},
|
||||
{"Orig_rax", r.regs.Orig_rax},
|
||||
{"Cs", r.regs.Cs},
|
||||
{"Eflags", r.regs.Eflags},
|
||||
{"Ss", r.regs.Ss},
|
||||
{"Fs_base", r.regs.Fs_base},
|
||||
{"Gs_base", r.regs.Gs_base},
|
||||
{"Ds", r.regs.Ds},
|
||||
{"Es", r.regs.Es},
|
||||
{"Fs", r.regs.Fs},
|
||||
{"Gs", r.regs.Gs},
|
||||
}
|
||||
out := make([]Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the value of RIP register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.regs.PC()
|
||||
}
|
||||
|
||||
// SP returns the value of RSP register.
|
||||
func (r *Regs) SP() uint64 {
|
||||
return r.regs.Rsp
|
||||
}
|
||||
|
||||
// CX returns the value of RCX register.
|
||||
func (r *Regs) CX() uint64 {
|
||||
return r.regs.Rcx
|
||||
}
|
||||
|
||||
// TLS returns the address of the thread
|
||||
// local storage memory segment.
|
||||
func (r *Regs) TLS() uint64 {
|
||||
return r.regs.Fs_base
|
||||
}
|
||||
|
||||
// SetPC sets RIP to the value specified by 'pc'.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) (err error) {
|
||||
r.regs.SetPC(pc)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Regs) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.regs.Rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.regs.Rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.regs.Rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.regs.Rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.regs.Rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.regs.Rax >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.regs.Rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.regs.Rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.regs.Rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.regs.Rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.regs.Rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.regs.Rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.regs.R8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.regs.R9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.regs.R10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.regs.R11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.regs.R12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.regs.R13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.regs.R14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.regs.R15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.regs.Rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.regs.Rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.regs.Rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.regs.Rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.regs.Rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.regs.Rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.regs.Rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.regs.Rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.regs.R8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.regs.R9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.regs.R10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.regs.R11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.regs.R12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.regs.R13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.regs.R14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.regs.R15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.regs.Rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.regs.Rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.regs.Rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.regs.Rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.regs.Rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.regs.Rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.regs.Rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.regs.Rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.regs.R8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.regs.R9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.regs.R10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.regs.R11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.regs.R12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.regs.R13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.regs.R14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.regs.R15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.regs.Rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.regs.Rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.regs.Rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.regs.Rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.regs.Rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.regs.Rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.regs.Rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.regs.Rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.regs.R8, nil
|
||||
case x86asm.R9:
|
||||
return r.regs.R9, nil
|
||||
case x86asm.R10:
|
||||
return r.regs.R10, nil
|
||||
case x86asm.R11:
|
||||
return r.regs.R11, nil
|
||||
case x86asm.R12:
|
||||
return r.regs.R12, nil
|
||||
case x86asm.R13:
|
||||
return r.regs.R13, nil
|
||||
case x86asm.R14:
|
||||
return r.regs.R14, nil
|
||||
case x86asm.R15:
|
||||
return r.regs.R15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
var (
|
||||
regs sys.PtraceRegs
|
||||
err error
|
||||
)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, ®s) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &Regs{®s, nil}
|
||||
if floatingPoint {
|
||||
r.fpregs, err = thread.fpRegisters()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
|
||||
type PtraceFpRegs struct {
|
||||
Cwd uint16
|
||||
Swd uint16
|
||||
Ftw uint16
|
||||
Fop uint16
|
||||
Rip uint64
|
||||
Rdp uint64
|
||||
Mxcsr uint32
|
||||
MxcrMask uint32
|
||||
StSpace [32]uint32
|
||||
XmmSpace [256]byte
|
||||
padding [24]uint32
|
||||
}
|
||||
|
||||
type PtraceXsave struct {
|
||||
PtraceFpRegs
|
||||
AvxState bool // contains AVX state
|
||||
YmmSpace [256]byte
|
||||
}
|
||||
|
||||
const (
|
||||
_X86_XSTATE_MAX_SIZE = 2688
|
||||
_NT_X86_XSTATE = 0x202
|
||||
|
||||
_XSAVE_HEADER_START = 512
|
||||
_XSAVE_HEADER_LEN = 64
|
||||
_XSAVE_EXTENDED_REGION_START = 576
|
||||
_XSAVE_SSE_REGION_LEN = 416
|
||||
)
|
||||
|
||||
func (thread *Thread) fpRegisters() (regs []Register, err error) {
|
||||
var fpregs PtraceXsave
|
||||
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
|
||||
|
||||
// x87 registers
|
||||
regs = appendWordReg(regs, "CW", fpregs.Cwd)
|
||||
regs = appendWordReg(regs, "SW", fpregs.Swd)
|
||||
regs = appendWordReg(regs, "TW", fpregs.Ftw)
|
||||
regs = appendWordReg(regs, "FOP", fpregs.Fop)
|
||||
regs = appendQwordReg(regs, "FIP", fpregs.Rip)
|
||||
regs = appendQwordReg(regs, "FDP", fpregs.Rdp)
|
||||
|
||||
for i := 0; i < len(fpregs.StSpace); i += 4 {
|
||||
regs = appendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
|
||||
}
|
||||
|
||||
// SSE registers
|
||||
regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32)
|
||||
regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
|
||||
|
||||
for i := 0; i < len(fpregs.XmmSpace); i += 16 {
|
||||
regs = appendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
|
||||
if fpregs.AvxState {
|
||||
regs = appendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
347
vendor/github.com/derekparker/delve/proc/registers_windows_amd64.go
generated
vendored
Normal file
347
vendor/github.com/derekparker/delve/proc/registers_windows_amd64.go
generated
vendored
Normal file
@ -0,0 +1,347 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rsc.io/x86/x86asm"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// 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
|
||||
eflags uint64
|
||||
cs uint64
|
||||
fs uint64
|
||||
gs uint64
|
||||
tls uint64
|
||||
fltSave *_XMM_SAVE_AREA32
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []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 {
|
||||
outlen += 6 + 8 + 2 + 16
|
||||
}
|
||||
out := make([]Register, 0, outlen)
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = append(out, Register{reg.k, eflagsDescription.Describe(reg.v, 64)})
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
if r.fltSave != nil {
|
||||
out = appendWordReg(out, "CW", r.fltSave.ControlWord)
|
||||
out = appendWordReg(out, "SW", r.fltSave.StatusWord)
|
||||
out = appendWordReg(out, "TW", uint16(r.fltSave.TagWord))
|
||||
out = appendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
|
||||
out = appendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
|
||||
out = appendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
|
||||
|
||||
for i := range r.fltSave.FloatRegisters {
|
||||
out = appendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
|
||||
}
|
||||
|
||||
out = appendFlagReg(out, "MXCSR", uint64(r.fltSave.MxCsr), mxcsrDescription, 32)
|
||||
out = appendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
|
||||
|
||||
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 {
|
||||
out = 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 *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
|
||||
}
|
||||
|
||||
// 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.tls
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
|
||||
context := 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)
|
||||
}
|
||||
|
||||
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.rax >> 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, UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
context := 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)
|
||||
}
|
||||
|
||||
regs := &Regs{
|
||||
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: uint64(threadInfo.TebBaseAddress),
|
||||
}
|
||||
|
||||
if floatingPoint {
|
||||
regs.fltSave = &context.FltSave
|
||||
}
|
||||
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) saveRegisters() (Registers, error) {
|
||||
return nil, fmt.Errorf("not implemented: saveRegisters")
|
||||
}
|
||||
|
||||
func (thread *Thread) restoreRegisters() error {
|
||||
return fmt.Errorf("not implemented: restoreRegisters")
|
||||
}
|
211
vendor/github.com/derekparker/delve/proc/stack.go
generated
vendored
Normal file
211
vendor/github.com/derekparker/delve/proc/stack.go
generated
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/derekparker/delve/dwarf/frame"
|
||||
)
|
||||
|
||||
// NoReturnAddr is returned when return address
|
||||
// could not be found during stack trace.
|
||||
type NoReturnAddr struct {
|
||||
fn string
|
||||
}
|
||||
|
||||
func (nra NoReturnAddr) Error() string {
|
||||
return fmt.Sprintf("could not find return address for %s", nra.fn)
|
||||
}
|
||||
|
||||
// Stackframe represents a frame in a system stack.
|
||||
type Stackframe struct {
|
||||
// Address the function above this one on the call stack will return to.
|
||||
Current Location
|
||||
// Address of the call instruction for the function above on the call stack.
|
||||
Call Location
|
||||
// Start address of the stack frame.
|
||||
CFA int64
|
||||
// Description of the stack frame.
|
||||
FDE *frame.FrameDescriptionEntry
|
||||
// Return address for this stack frame (as read from the stack frame itself).
|
||||
Ret uint64
|
||||
}
|
||||
|
||||
// Scope returns a new EvalScope using this frame.
|
||||
func (frame *Stackframe) Scope(thread *Thread) *EvalScope {
|
||||
return &EvalScope{Thread: thread, PC: frame.Current.PC, CFA: frame.CFA}
|
||||
}
|
||||
|
||||
// ReturnAddress returns the return address of the function
|
||||
// this thread is executing.
|
||||
func (t *Thread) ReturnAddress() (uint64, error) {
|
||||
locations, err := t.Stacktrace(2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(locations) < 2 {
|
||||
return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
|
||||
}
|
||||
return locations[1].Current.PC, nil
|
||||
}
|
||||
|
||||
func (t *Thread) stackIterator() (*stackIterator, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newStackIterator(t.dbp, regs.PC(), regs.SP()), nil
|
||||
}
|
||||
|
||||
// Stacktrace returns the stack trace for thread.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
it, err := t.stackIterator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
|
||||
func (g *G) stackIterator() (*stackIterator, error) {
|
||||
if g.thread != nil {
|
||||
return g.thread.stackIterator()
|
||||
}
|
||||
return newStackIterator(g.dbp, g.PC, g.SP), nil
|
||||
}
|
||||
|
||||
// Stacktrace returns the stack trace for a goroutine.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (g *G) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
it, err := g.stackIterator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
|
||||
// GoroutineLocation returns the location of the given
|
||||
// goroutine.
|
||||
func (dbp *Process) GoroutineLocation(g *G) *Location {
|
||||
f, l, fn := dbp.PCToLine(g.PC)
|
||||
return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
|
||||
}
|
||||
|
||||
// NullAddrError is an error for a null address.
|
||||
type NullAddrError struct{}
|
||||
|
||||
func (n NullAddrError) Error() string {
|
||||
return "NULL address"
|
||||
}
|
||||
|
||||
// stackIterator holds information
|
||||
// required to iterate and walk the program
|
||||
// stack.
|
||||
type stackIterator struct {
|
||||
pc, sp uint64
|
||||
top bool
|
||||
atend bool
|
||||
frame Stackframe
|
||||
dbp *Process
|
||||
err error
|
||||
}
|
||||
|
||||
func newStackIterator(dbp *Process, pc, sp uint64) *stackIterator {
|
||||
return &stackIterator{pc: pc, sp: sp, top: true, dbp: dbp, err: nil, atend: false}
|
||||
}
|
||||
|
||||
// Next points the iterator to the next stack frame.
|
||||
func (it *stackIterator) Next() bool {
|
||||
if it.err != nil || it.atend {
|
||||
return false
|
||||
}
|
||||
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top)
|
||||
if it.err != nil {
|
||||
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
|
||||
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
|
||||
it.atend = true
|
||||
it.err = nil
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if it.frame.Current.Fn == nil {
|
||||
if it.top {
|
||||
it.err = fmt.Errorf("PC not associated to any function")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if it.frame.Ret <= 0 {
|
||||
it.atend = true
|
||||
return true
|
||||
}
|
||||
// Look for "top of stack" functions.
|
||||
if it.frame.Current.Fn.Name == "runtime.goexit" || it.frame.Current.Fn.Name == "runtime.rt0_go" || it.frame.Current.Fn.Name == "runtime.mcall" {
|
||||
it.atend = true
|
||||
return true
|
||||
}
|
||||
|
||||
it.top = false
|
||||
it.pc = it.frame.Ret
|
||||
it.sp = uint64(it.frame.CFA)
|
||||
return true
|
||||
}
|
||||
|
||||
// Frame returns the frame the iterator is pointing at.
|
||||
func (it *stackIterator) Frame() Stackframe {
|
||||
if it.err != nil {
|
||||
panic(it.err)
|
||||
}
|
||||
return it.frame
|
||||
}
|
||||
|
||||
// Err returns the error encountered during stack iteration.
|
||||
func (it *stackIterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
func (dbp *Process) frameInfo(pc, sp uint64, top bool) (Stackframe, error) {
|
||||
f, l, fn := dbp.PCToLine(pc)
|
||||
fde, err := dbp.frameEntries.FDEForPC(pc)
|
||||
if err != nil {
|
||||
return Stackframe{}, err
|
||||
}
|
||||
spoffset, retoffset := fde.ReturnAddressOffset(pc)
|
||||
cfa := int64(sp) + spoffset
|
||||
|
||||
retaddr := uintptr(cfa + retoffset)
|
||||
if retaddr == 0 {
|
||||
return Stackframe{}, NullAddrError{}
|
||||
}
|
||||
data, err := dbp.CurrentThread.readMemory(retaddr, dbp.arch.PtrSize())
|
||||
if err != nil {
|
||||
return Stackframe{}, err
|
||||
}
|
||||
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: binary.LittleEndian.Uint64(data)}
|
||||
if !top {
|
||||
r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1)
|
||||
r.Call.PC, _, _ = dbp.goSymTable.LineToPC(r.Call.File, r.Call.Line)
|
||||
} else {
|
||||
r.Call = r.Current
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
||||
if depth < 0 {
|
||||
return nil, errors.New("negative maximum stack depth")
|
||||
}
|
||||
frames := make([]Stackframe, 0, depth+1)
|
||||
for it.Next() {
|
||||
frames = append(frames, it.Frame())
|
||||
if len(frames) >= depth+1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := it.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return frames, nil
|
||||
}
|
112
vendor/github.com/derekparker/delve/proc/syscall_windows.go
generated
vendored
Normal file
112
vendor/github.com/derekparker/delve/proc/syscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
package proc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
//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
|
114
vendor/github.com/derekparker/delve/proc/syscall_windows_amd64.go
generated
vendored
Normal file
114
vendor/github.com/derekparker/delve/proc/syscall_windows_amd64.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
package proc
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
_CONTEXT_AMD64 = 0x100000
|
||||
_CONTEXT_CONTROL = (_CONTEXT_AMD64 | 0x1)
|
||||
_CONTEXT_INTEGER = (_CONTEXT_AMD64 | 0x2)
|
||||
_CONTEXT_SEGMENTS = (_CONTEXT_AMD64 | 0x4)
|
||||
_CONTEXT_FLOATING_POINT = (_CONTEXT_AMD64 | 0x8)
|
||||
_CONTEXT_DEBUG_REGISTERS = (_CONTEXT_AMD64 | 0x10)
|
||||
_CONTEXT_FULL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT)
|
||||
_CONTEXT_ALL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_SEGMENTS | _CONTEXT_FLOATING_POINT | _CONTEXT_DEBUG_REGISTERS)
|
||||
_CONTEXT_EXCEPTION_ACTIVE = 0x8000000
|
||||
_CONTEXT_SERVICE_ACTIVE = 0x10000000
|
||||
_CONTEXT_EXCEPTION_REQUEST = 0x40000000
|
||||
_CONTEXT_EXCEPTION_REPORTING = 0x80000000
|
||||
)
|
||||
|
||||
type _M128A struct {
|
||||
Low uint64
|
||||
High int64
|
||||
}
|
||||
|
||||
type _XMM_SAVE_AREA32 struct {
|
||||
ControlWord uint16
|
||||
StatusWord uint16
|
||||
TagWord byte
|
||||
Reserved1 byte
|
||||
ErrorOpcode uint16
|
||||
ErrorOffset uint32
|
||||
ErrorSelector uint16
|
||||
Reserved2 uint16
|
||||
DataOffset uint32
|
||||
DataSelector uint16
|
||||
Reserved3 uint16
|
||||
MxCsr uint32
|
||||
MxCsr_Mask uint32
|
||||
FloatRegisters [8]_M128A
|
||||
XmmRegisters [256]byte
|
||||
Reserved4 [96]byte
|
||||
}
|
||||
|
||||
type _CONTEXT struct {
|
||||
P1Home uint64
|
||||
P2Home uint64
|
||||
P3Home uint64
|
||||
P4Home uint64
|
||||
P5Home uint64
|
||||
P6Home uint64
|
||||
|
||||
ContextFlags uint32
|
||||
MxCsr uint32
|
||||
|
||||
SegCs uint16
|
||||
SegDs uint16
|
||||
SegEs uint16
|
||||
SegFs uint16
|
||||
SegGs uint16
|
||||
SegSs uint16
|
||||
EFlags uint32
|
||||
|
||||
Dr0 uint64
|
||||
Dr1 uint64
|
||||
Dr2 uint64
|
||||
Dr3 uint64
|
||||
Dr6 uint64
|
||||
Dr7 uint64
|
||||
|
||||
Rax uint64
|
||||
Rcx uint64
|
||||
Rdx uint64
|
||||
Rbx uint64
|
||||
Rsp uint64
|
||||
Rbp uint64
|
||||
Rsi uint64
|
||||
Rdi uint64
|
||||
R8 uint64
|
||||
R9 uint64
|
||||
R10 uint64
|
||||
R11 uint64
|
||||
R12 uint64
|
||||
R13 uint64
|
||||
R14 uint64
|
||||
R15 uint64
|
||||
|
||||
Rip uint64
|
||||
|
||||
FltSave _XMM_SAVE_AREA32
|
||||
|
||||
VectorRegister [26]_M128A
|
||||
VectorControl uint64
|
||||
|
||||
DebugControl uint64
|
||||
LastBranchToRip uint64
|
||||
LastBranchFromRip uint64
|
||||
LastExceptionToRip uint64
|
||||
LastExceptionFromRip uint64
|
||||
}
|
||||
|
||||
// newCONTEXT allocates Windows CONTEXT structure aligned to 16 bytes.
|
||||
func newCONTEXT() *_CONTEXT {
|
||||
var c *_CONTEXT
|
||||
buf := make([]byte, unsafe.Sizeof(*c)+15)
|
||||
return (*_CONTEXT)(unsafe.Pointer((uintptr(unsafe.Pointer(&buf[15]))) &^ 15))
|
||||
}
|
||||
|
||||
type _DEBUG_EVENT struct {
|
||||
DebugEventCode uint32
|
||||
ProcessId uint32
|
||||
ThreadId uint32
|
||||
_ uint32 // to align Union properly
|
||||
U [160]byte
|
||||
}
|
504
vendor/github.com/derekparker/delve/proc/threads.go
generated
vendored
Normal file
504
vendor/github.com/derekparker/delve/proc/threads.go
generated
vendored
Normal file
@ -0,0 +1,504 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// Thread represents a single thread in the traced process
|
||||
// ID represents the thread id or port, Process holds a reference to the
|
||||
// Process struct that contains info on the process as
|
||||
// a whole, and Status represents the last result of a `wait` call
|
||||
// on this thread.
|
||||
type Thread struct {
|
||||
ID int // Thread ID or mach port
|
||||
Status *WaitStatus // Status returned from last wait call
|
||||
CurrentBreakpoint *Breakpoint // Breakpoint thread is currently stopped at
|
||||
BreakpointConditionMet bool // Output of evaluating the breakpoint's condition
|
||||
BreakpointConditionError error // Error evaluating the breakpoint's condition
|
||||
|
||||
dbp *Process
|
||||
singleStepping bool
|
||||
running bool
|
||||
os *OSSpecificDetails
|
||||
}
|
||||
|
||||
// Location represents the location of a thread.
|
||||
// Holds information on the current instruction
|
||||
// address, the source file:line, and the function.
|
||||
type Location struct {
|
||||
PC uint64
|
||||
File string
|
||||
Line int
|
||||
Fn *gosym.Func
|
||||
}
|
||||
|
||||
// Continue the execution of this thread.
|
||||
//
|
||||
// If we are currently at a breakpoint, we'll clear it
|
||||
// first and then resume execution. Thread will continue until
|
||||
// it hits a breakpoint or is signaled.
|
||||
func (thread *Thread) Continue() error {
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check whether we are stopped at a breakpoint, and
|
||||
// if so, single step over it before continuing.
|
||||
if _, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return thread.resume()
|
||||
}
|
||||
|
||||
// StepInstruction steps a single instruction.
|
||||
//
|
||||
// Executes exactly one instruction and then returns.
|
||||
// If the thread is at a breakpoint, we first clear it,
|
||||
// execute the instruction, and then replace the breakpoint.
|
||||
// Otherwise we simply execute the next instruction.
|
||||
func (thread *Thread) StepInstruction() (err error) {
|
||||
thread.running = true
|
||||
thread.singleStepping = true
|
||||
defer func() {
|
||||
thread.singleStepping = false
|
||||
thread.running = false
|
||||
}()
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bp, ok := thread.dbp.FindBreakpoint(pc)
|
||||
if ok {
|
||||
// Clear the breakpoint so that we can continue execution.
|
||||
_, err = bp.Clear(thread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Restore breakpoint now that we have passed it.
|
||||
defer func() {
|
||||
err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr)
|
||||
}()
|
||||
}
|
||||
|
||||
err = thread.singleStep()
|
||||
if err != nil {
|
||||
if _, exited := err.(ProcessExitedError); exited {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("step failed: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location returns the threads location, including the file:line
|
||||
// of the corresponding source code, the function we're in
|
||||
// and the current instruction address.
|
||||
func (thread *Thread) Location() (*Location, error) {
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, l, fn := thread.dbp.PCToLine(pc)
|
||||
return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
// ThreadBlockedError is returned when the thread
|
||||
// is blocked in the scheduler.
|
||||
type ThreadBlockedError struct{}
|
||||
|
||||
func (tbe ThreadBlockedError) Error() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// returns topmost frame of g or thread if g is nil
|
||||
func topframe(g *G, thread *Thread) (Stackframe, error) {
|
||||
var frames []Stackframe
|
||||
var err error
|
||||
|
||||
if g == nil {
|
||||
if thread.blocked() {
|
||||
return Stackframe{}, ThreadBlockedError{}
|
||||
}
|
||||
frames, err = thread.Stacktrace(0)
|
||||
} else {
|
||||
frames, err = g.Stacktrace(0)
|
||||
}
|
||||
if err != nil {
|
||||
return Stackframe{}, err
|
||||
}
|
||||
if len(frames) < 1 {
|
||||
return Stackframe{}, errors.New("empty stack trace")
|
||||
}
|
||||
return frames[0], nil
|
||||
}
|
||||
|
||||
// Set breakpoints at every line, and the return address. Also look for
|
||||
// a deferred function and set a breakpoint there too.
|
||||
// If stepInto is true it will also set breakpoints inside all
|
||||
// functions called on the current source line, for non-absolute CALLs
|
||||
// a breakpoint of kind StepBreakpoint is set on the CALL instruction,
|
||||
// Continue will take care of setting a breakpoint to the destination
|
||||
// once the CALL is reached.
|
||||
func (dbp *Process) next(stepInto bool) error {
|
||||
topframe, err := topframe(dbp.SelectedGoroutine, dbp.CurrentThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
}()
|
||||
|
||||
csource := filepath.Ext(topframe.Current.File) != ".go"
|
||||
thread := dbp.CurrentThread
|
||||
currentGoroutine := false
|
||||
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.thread != nil {
|
||||
thread = dbp.SelectedGoroutine.thread
|
||||
currentGoroutine = true
|
||||
}
|
||||
|
||||
text, err := thread.Disassemble(topframe.FDE.Begin(), topframe.FDE.End(), currentGoroutine)
|
||||
if err != nil && stepInto {
|
||||
return err
|
||||
}
|
||||
|
||||
cond := sameGoroutineCondition(dbp.SelectedGoroutine)
|
||||
|
||||
if stepInto {
|
||||
for _, instr := range text {
|
||||
if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() {
|
||||
continue
|
||||
}
|
||||
|
||||
if instr.DestLoc != nil && instr.DestLoc.Fn != nil {
|
||||
if err := dbp.setStepIntoBreakpoint([]AsmInstruction{instr}, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Non-absolute call instruction, set a StepBreakpoint here
|
||||
if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !csource {
|
||||
deferreturns := []uint64{}
|
||||
|
||||
// Find all runtime.deferreturn locations in the function
|
||||
// See documentation of Breakpoint.DeferCond for why this is necessary
|
||||
for _, instr := range text {
|
||||
if instr.IsCall() && instr.DestLoc != nil && instr.DestLoc.Fn != nil && instr.DestLoc.Fn.Name == "runtime.deferreturn" {
|
||||
deferreturns = append(deferreturns, instr.Loc.PC)
|
||||
}
|
||||
}
|
||||
|
||||
// Set breakpoint on the most recently deferred function (if any)
|
||||
var deferpc uint64 = 0
|
||||
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.DeferPC != 0 {
|
||||
_, _, deferfn := dbp.goSymTable.PCToLine(dbp.SelectedGoroutine.DeferPC)
|
||||
var err error
|
||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
|
||||
if err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil {
|
||||
bp.DeferReturns = deferreturns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add breakpoints on all the lines in the current function
|
||||
pcs, err := dbp.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !csource {
|
||||
var covered bool
|
||||
for i := range pcs {
|
||||
if topframe.FDE.Cover(pcs[i]) {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !covered {
|
||||
fn := dbp.goSymTable.PCToFunc(topframe.Ret)
|
||||
if dbp.SelectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a breakpoint on the return address for the current frame
|
||||
pcs = append(pcs, topframe.Ret)
|
||||
success = true
|
||||
return dbp.setInternalBreakpoints(topframe.Current.PC, pcs, NextBreakpoint, cond)
|
||||
}
|
||||
|
||||
func (dbp *Process) setStepIntoBreakpoint(text []AsmInstruction, cond ast.Expr) error {
|
||||
if len(text) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
instr := text[0]
|
||||
|
||||
if instr.DestLoc == nil || instr.DestLoc.Fn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fn := instr.DestLoc.Fn
|
||||
|
||||
// Ensure PC and Entry match, otherwise StepInto is likely to set
|
||||
// its breakpoint before DestLoc.PC and hence run too far ahead.
|
||||
// Calls to runtime.duffzero and duffcopy have this problem.
|
||||
if fn.Entry != instr.DestLoc.PC {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip unexported runtime functions
|
||||
if strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO(aarzilli): if we want to let users hide functions
|
||||
// or entire packages from being stepped into with 'step'
|
||||
// those extra checks should be done here.
|
||||
|
||||
// Set a breakpoint after the function's prologue
|
||||
pc, _ := dbp.FirstPCAfterPrologue(fn, false)
|
||||
if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setInternalBreakpoints sets a breakpoint to all addresses specified in pcs
|
||||
// skipping over curpc and curpc-1
|
||||
func (dbp *Process) setInternalBreakpoints(curpc uint64, pcs []uint64, kind BreakpointKind, cond ast.Expr) error {
|
||||
for i := range pcs {
|
||||
if pcs[i] == curpc || pcs[i] == curpc-1 {
|
||||
continue
|
||||
}
|
||||
if _, err := dbp.SetBreakpoint(pcs[i], kind, cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPC sets the PC for this thread.
|
||||
func (thread *Thread) SetPC(pc uint64) error {
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return regs.SetPC(thread, pc)
|
||||
}
|
||||
|
||||
func (thread *Thread) getGVariable() (*Variable, error) {
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if thread.dbp.arch.GStructOffset() == 0 {
|
||||
// GetG was called through SwitchThread / updateThreadList during initialization
|
||||
// thread.dbp.arch isn't setup yet (it needs a CurrentThread to read global variables from)
|
||||
return nil, fmt.Errorf("g struct offset not initialized")
|
||||
}
|
||||
|
||||
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), thread.dbp.arch.PtrSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gaddr := uintptr(binary.LittleEndian.Uint64(gaddrbs))
|
||||
|
||||
// On Windows, the value at TLS()+GStructOffset() is a
|
||||
// pointer to the G struct.
|
||||
needsDeref := runtime.GOOS == "windows"
|
||||
|
||||
return thread.newGVariable(gaddr, needsDeref)
|
||||
}
|
||||
|
||||
func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) {
|
||||
typ, err := thread.dbp.findType("runtime.g")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := ""
|
||||
|
||||
if deref {
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
|
||||
} else {
|
||||
name = "runtime.curg"
|
||||
}
|
||||
|
||||
return thread.newVariable(name, gaddr, typ), nil
|
||||
}
|
||||
|
||||
// GetG returns information on the G (goroutine) that is executing on this thread.
|
||||
//
|
||||
// The G structure for a thread is stored in thread local storage. Here we simply
|
||||
// calculate the address and read and parse the G struct.
|
||||
//
|
||||
// We cannot simply use the allg linked list in order to find the M that represents
|
||||
// the given OS thread and follow its G pointer because on Darwin mach ports are not
|
||||
// universal, so our port for this thread would not map to the `id` attribute of the M
|
||||
// structure. Also, when linked against libc, Go prefers the libc version of clone as
|
||||
// opposed to the runtime version. This has the consequence of not setting M.id for
|
||||
// any thread, regardless of OS.
|
||||
//
|
||||
// In order to get around all this craziness, we read the address of the G structure for
|
||||
// the current thread from the thread local storage area.
|
||||
func (thread *Thread) GetG() (g *G, err error) {
|
||||
gaddr, err := thread.getGVariable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err = gaddr.parseG()
|
||||
if err == nil {
|
||||
g.thread = thread
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at
|
||||
// the operating system level. Actual implementation
|
||||
// is OS dependant, look in OS thread file.
|
||||
func (thread *Thread) Stopped() bool {
|
||||
return thread.stopped()
|
||||
}
|
||||
|
||||
// Halt stops this thread from executing. Actual
|
||||
// implementation is OS dependant. Look in OS
|
||||
// thread file.
|
||||
func (thread *Thread) Halt() (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
thread.running = false
|
||||
}
|
||||
}()
|
||||
if thread.Stopped() {
|
||||
return
|
||||
}
|
||||
err = thread.halt()
|
||||
return
|
||||
}
|
||||
|
||||
// Scope returns the current EvalScope for this thread.
|
||||
func (thread *Thread) Scope() (*EvalScope, error) {
|
||||
locations, err := thread.Stacktrace(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(locations) < 1 {
|
||||
return nil, errors.New("could not decode first frame")
|
||||
}
|
||||
return locations[0].Scope(thread), nil
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint sets the current breakpoint that this
|
||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
|
||||
func (thread *Thread) SetCurrentBreakpoint() error {
|
||||
thread.CurrentBreakpoint = nil
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
thread.CurrentBreakpoint = bp
|
||||
if err = thread.SetPC(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread)
|
||||
if thread.onTriggeredBreakpoint() {
|
||||
if g, err := thread.GetG(); err == nil {
|
||||
thread.CurrentBreakpoint.HitCount[g.ID]++
|
||||
}
|
||||
thread.CurrentBreakpoint.TotalHitCount++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (thread *Thread) clearBreakpointState() {
|
||||
thread.CurrentBreakpoint = nil
|
||||
thread.BreakpointConditionMet = false
|
||||
thread.BreakpointConditionError = nil
|
||||
}
|
||||
|
||||
func (thread *Thread) onTriggeredBreakpoint() bool {
|
||||
return (thread.CurrentBreakpoint != nil) && thread.BreakpointConditionMet
|
||||
}
|
||||
|
||||
func (thread *Thread) onTriggeredInternalBreakpoint() bool {
|
||||
return thread.onTriggeredBreakpoint() && thread.CurrentBreakpoint.Internal()
|
||||
}
|
||||
|
||||
func (thread *Thread) onRuntimeBreakpoint() bool {
|
||||
loc, err := thread.Location()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return loc.Fn != nil && loc.Fn.Name == "runtime.breakpoint"
|
||||
}
|
||||
|
||||
// onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command
|
||||
func (thread *Thread) onNextGoroutine() (bool, error) {
|
||||
var bp *Breakpoint
|
||||
for i := range thread.dbp.Breakpoints {
|
||||
if thread.dbp.Breakpoints[i].Internal() {
|
||||
bp = thread.dbp.Breakpoints[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if bp == nil {
|
||||
return false, nil
|
||||
}
|
||||
if bp.Kind == NextDeferBreakpoint {
|
||||
// we just want to check the condition on the goroutine id here
|
||||
bp.Kind = NextBreakpoint
|
||||
defer func() {
|
||||
bp.Kind = NextDeferBreakpoint
|
||||
}()
|
||||
}
|
||||
return bp.checkCondition(thread)
|
||||
}
|
177
vendor/github.com/derekparker/delve/proc/threads_darwin.c
generated
vendored
Normal file
177
vendor/github.com/derekparker/delve/proc/threads_darwin.c
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
#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;
|
||||
}
|
136
vendor/github.com/derekparker/delve/proc/threads_darwin.go
generated
vendored
Normal file
136
vendor/github.com/derekparker/delve/proc/threads_darwin.go
generated
vendored
Normal file
@ -0,0 +1,136 @@
|
||||
package proc
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
// #include "proc_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
sys "golang.org/x/sys/unix"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// 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) halt() (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 {
|
||||
t.running = true
|
||||
// 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
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.kevent", "runtime.mach_semaphore_wait", "runtime.usleep":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
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 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(addr uintptr, size int) ([]byte, error) {
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var (
|
||||
buf = make([]byte, size)
|
||||
vmData = unsafe.Pointer(&buf[0])
|
||||
vmAddr = C.mach_vm_address_t(addr)
|
||||
length = C.mach_msg_type_number_t(size)
|
||||
)
|
||||
|
||||
ret := C.read_memory(t.dbp.os.task, vmAddr, vmData, length)
|
||||
if ret < 0 {
|
||||
return nil, fmt.Errorf("could not read memory")
|
||||
}
|
||||
return buf, nil
|
||||
}
|
41
vendor/github.com/derekparker/delve/proc/threads_darwin.h
generated
vendored
Normal file
41
vendor/github.com/derekparker/delve/proc/threads_darwin.h
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
#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);
|
108
vendor/github.com/derekparker/delve/proc/threads_linux.go
generated
vendored
Normal file
108
vendor/github.com/derekparker/delve/proc/threads_linux.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails hold Linux specific
|
||||
// process details.
|
||||
type OSSpecificDetails struct {
|
||||
registers sys.PtraceRegs
|
||||
}
|
||||
|
||||
func (t *Thread) halt() (err error) {
|
||||
err = sys.Tgkill(t.dbp.Pid, t.ID, sys.SIGSTOP)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("halt err %s on thread %d", err, t.ID)
|
||||
return
|
||||
}
|
||||
_, _, err = t.dbp.wait(t.ID, 0)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("wait err %s on thread %d", err, t.ID)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) stopped() bool {
|
||||
state := status(t.ID, t.dbp.os.comm)
|
||||
return state == StatusTraceStop || state == StatusTraceStopT
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
return t.resumeWithSig(0)
|
||||
}
|
||||
|
||||
func (t *Thread) resumeWithSig(sig int) (err error) {
|
||||
t.running = true
|
||||
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.ID, sig) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() (err error) {
|
||||
for {
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.ID) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wpid, status, err := t.dbp.wait(t.ID, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (status == nil || status.Exited()) && wpid == t.dbp.Pid {
|
||||
t.dbp.postExit()
|
||||
rs := 0
|
||||
if status != nil {
|
||||
rs = status.ExitStatus()
|
||||
}
|
||||
return ProcessExitedError{Pid: t.dbp.Pid, Status: rs}
|
||||
}
|
||||
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Thread) blocked() bool {
|
||||
pc, _ := t.PC()
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Thread) saveRegisters() (Registers, error) {
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not save register contents")
|
||||
}
|
||||
return &Regs{&t.os.registers, nil}, nil
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters() (err error) {
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(t.ID, &t.os.registers) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, addr, data) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) readMemory(addr uintptr, size int) (data []byte, err error) {
|
||||
if size == 0 {
|
||||
return
|
||||
}
|
||||
data = make([]byte, size)
|
||||
t.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(t.ID, addr, data) })
|
||||
return
|
||||
}
|
151
vendor/github.com/derekparker/delve/proc/threads_windows.go
generated
vendored
Normal file
151
vendor/github.com/derekparker/delve/proc/threads_windows.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// 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) halt() (err error) {
|
||||
// Ignore the request to halt. On Windows, all threads are halted
|
||||
// on return from WaitForDebugEvent.
|
||||
return nil
|
||||
|
||||
// TODO - This may not be correct in all usages of dbp.Halt. There
|
||||
// are some callers who use dbp.Halt() to stop the process when it is not
|
||||
// already broken on a debug event.
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
context := 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 ProcessExitedError{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 {
|
||||
t.running = true
|
||||
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?
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.kevent", "runtime.usleep":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Thread) stopped() bool {
|
||||
// TODO: We are assuming that threads are always stopped
|
||||
// during command execution.
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||
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
|
||||
}
|
||||
|
||||
func (t *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var count uintptr
|
||||
buf := make([]byte, size)
|
||||
err := _ReadProcessMemory(t.dbp.os.hProcess, addr, &buf[0], uintptr(size), &count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf[:count], nil
|
||||
}
|
715
vendor/github.com/derekparker/delve/proc/types.go
generated
vendored
Normal file
715
vendor/github.com/derekparker/delve/proc/types.go
generated
vendored
Normal file
@ -0,0 +1,715 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/derekparker/delve/dwarf/reader"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// The kind field in runtime._type is a reflect.Kind value plus
|
||||
// some extra flags defined here.
|
||||
// See equivalent declaration in $GOROOT/src/reflect/type.go
|
||||
const (
|
||||
kindDirectIface = 1 << 5
|
||||
kindGCProg = 1 << 6 // Type.gc points to GC program
|
||||
kindNoPointers = 1 << 7
|
||||
kindMask = (1 << 5) - 1
|
||||
)
|
||||
|
||||
// Value of tflag field in runtime._type.
|
||||
// See $GOROOT/reflect/type.go for a description of these flags.
|
||||
const (
|
||||
tflagUncommon = 1 << 0
|
||||
tflagExtraStar = 1 << 1
|
||||
tflagNamed = 1 << 2
|
||||
)
|
||||
|
||||
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||
func (dbp *Process) findType(name string) (dwarf.Type, error) {
|
||||
off, found := dbp.types[name]
|
||||
if !found {
|
||||
return nil, reader.TypeNotFoundErr
|
||||
}
|
||||
return dbp.dwarf.Type(off)
|
||||
}
|
||||
|
||||
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
|
||||
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
|
||||
}
|
||||
|
||||
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
|
||||
dbp.loadPackageMap()
|
||||
if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
|
||||
// Allow users to specify type names verbatim as quoted
|
||||
// string. Useful as a catch-all workaround for cases where we don't
|
||||
// parse/serialize types correctly or can not resolve package paths.
|
||||
typn, _ := strconv.Unquote(lit.Value)
|
||||
return dbp.findType(typn)
|
||||
}
|
||||
dbp.expandPackagesInType(expr)
|
||||
if snode, ok := expr.(*ast.StarExpr); ok {
|
||||
// Pointer types only appear in the dwarf informations when
|
||||
// a pointer to the type is used in the target program, here
|
||||
// we create a pointer type on the fly so that the user can
|
||||
// specify a pointer to any variable used in the target program
|
||||
ptyp, err := dbp.findTypeExpr(snode.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbp.pointerTo(ptyp), nil
|
||||
}
|
||||
return dbp.findType(exprToString(expr))
|
||||
}
|
||||
|
||||
func complexType(typename string) bool {
|
||||
for _, ch := range typename {
|
||||
switch ch {
|
||||
case '*', '[', '<', '{', '(', ' ':
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dbp *Process) loadPackageMap() error {
|
||||
if dbp.packageMap != nil {
|
||||
return nil
|
||||
}
|
||||
dbp.packageMap = map[string]string{}
|
||||
reader := dbp.DwarfReader()
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType {
|
||||
continue
|
||||
}
|
||||
|
||||
typename, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok || complexType(typename) {
|
||||
continue
|
||||
}
|
||||
|
||||
dot := strings.LastIndex(typename, ".")
|
||||
if dot < 0 {
|
||||
continue
|
||||
}
|
||||
path := typename[:dot]
|
||||
slash := strings.LastIndex(path, "/")
|
||||
if slash < 0 || slash+1 >= len(path) {
|
||||
continue
|
||||
}
|
||||
name := path[slash+1:]
|
||||
dbp.packageMap[name] = path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) loadTypeMap(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
dbp.types = make(map[string]dwarf.Offset)
|
||||
reader := dbp.DwarfReader()
|
||||
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
name, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, exists := dbp.types[name]; !exists {
|
||||
dbp.types[name] = entry.Offset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) expandPackagesInType(expr ast.Expr) {
|
||||
switch e := expr.(type) {
|
||||
case *ast.ArrayType:
|
||||
dbp.expandPackagesInType(e.Elt)
|
||||
case *ast.ChanType:
|
||||
dbp.expandPackagesInType(e.Value)
|
||||
case *ast.FuncType:
|
||||
for i := range e.Params.List {
|
||||
dbp.expandPackagesInType(e.Params.List[i].Type)
|
||||
}
|
||||
if e.Results != nil {
|
||||
for i := range e.Results.List {
|
||||
dbp.expandPackagesInType(e.Results.List[i].Type)
|
||||
}
|
||||
}
|
||||
case *ast.MapType:
|
||||
dbp.expandPackagesInType(e.Key)
|
||||
dbp.expandPackagesInType(e.Value)
|
||||
case *ast.ParenExpr:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
case *ast.SelectorExpr:
|
||||
switch x := e.X.(type) {
|
||||
case *ast.Ident:
|
||||
if path, ok := dbp.packageMap[x.Name]; ok {
|
||||
x.Name = path
|
||||
}
|
||||
default:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
default:
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
type nameOfRuntimeTypeEntry struct {
|
||||
typename string
|
||||
kind int64
|
||||
}
|
||||
|
||||
// Returns the type name of the type described in _type.
|
||||
// _type is a non-loaded Variable pointing to runtime._type struct in the target.
|
||||
// The returned string is in the format that's used in DWARF data
|
||||
func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) {
|
||||
if e, ok := _type.dbp.nameOfRuntimeType[_type.Addr]; ok {
|
||||
return e.typename, e.kind, nil
|
||||
}
|
||||
|
||||
var tflag int64
|
||||
|
||||
if tflagField := _type.toFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
||||
}
|
||||
if kindField := _type.toFieldNamed("kind"); kindField != nil && kindField.Value != nil {
|
||||
kind, _ = constant.Int64Val(kindField.Value)
|
||||
}
|
||||
|
||||
// Named types are defined by a 'type' expression, everything else
|
||||
// (for example pointers to named types) are not considered named.
|
||||
if tflag&tflagNamed != 0 {
|
||||
typename, err = nameOfNamedRuntimeType(_type, kind, tflag)
|
||||
return typename, kind, err
|
||||
} else {
|
||||
typename, err = nameOfUnnamedRuntimeType(_type, kind, tflag)
|
||||
return typename, kind, err
|
||||
}
|
||||
|
||||
_type.dbp.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
|
||||
|
||||
return typename, kind, nil
|
||||
}
|
||||
|
||||
// The layout of a runtime._type struct is as follows:
|
||||
//
|
||||
// <runtime._type><kind specific struct fields><runtime.uncommontype>
|
||||
//
|
||||
// with the 'uncommon type struct' being optional
|
||||
//
|
||||
// For named types first we extract the type name from the 'str'
|
||||
// field in the runtime._type struct.
|
||||
// Then we prepend the package path from the runtime.uncommontype
|
||||
// struct, when it exists.
|
||||
//
|
||||
// To find out the memory address of the runtime.uncommontype struct
|
||||
// we first cast the Variable pointing to the runtime._type struct
|
||||
// to a struct specific to the type's kind (for example, if the type
|
||||
// being described is a slice type the variable will be specialized
|
||||
// to a runtime.slicetype).
|
||||
func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string, err error) {
|
||||
var strOff int64
|
||||
if strField := _type.toFieldNamed("str"); strField != nil && strField.Value != nil {
|
||||
strOff, _ = constant.Int64Val(strField.Value)
|
||||
} else {
|
||||
return "", errors.New("could not find str field")
|
||||
}
|
||||
|
||||
// The following code is adapted from reflect.(*rtype).Name.
|
||||
// For a description of how memory is organized for type names read
|
||||
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
|
||||
|
||||
typename, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(strOff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if tflag&tflagExtraStar != 0 {
|
||||
typename = typename[1:]
|
||||
}
|
||||
|
||||
if i := strings.Index(typename, "."); i >= 0 {
|
||||
typename = typename[i+1:]
|
||||
} else {
|
||||
return typename, nil
|
||||
}
|
||||
|
||||
// The following code is adapted from reflect.(*rtype).PkgPath in
|
||||
// $GOROOT/src/reflect/type.go
|
||||
|
||||
_type, err = specificRuntimeType(_type, kind)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if ut := uncommon(_type, tflag); ut != nil {
|
||||
if pkgPathField := ut.toFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
||||
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
||||
pkgPath, _, _, err := _type.dbp.resolveNameOff(_type.Addr, uintptr(pkgPathOff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
typename = pkgPath + "." + typename
|
||||
}
|
||||
}
|
||||
|
||||
return typename, nil
|
||||
}
|
||||
|
||||
func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
||||
_type, err := specificRuntimeType(_type, kind)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// The types referred to here are defined in $GOROOT/src/runtime/type.go
|
||||
switch reflect.Kind(kind & kindMask) {
|
||||
case reflect.Array:
|
||||
var len int64
|
||||
if lenField := _type.toFieldNamed("len"); lenField != nil && lenField.Value != nil {
|
||||
len, _ = constant.Int64Val(lenField.Value)
|
||||
}
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("[%d]%s", len, elemname), nil
|
||||
case reflect.Chan:
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "chan " + elemname, nil
|
||||
case reflect.Func:
|
||||
return nameOfFuncRuntimeType(_type, tflag, true)
|
||||
case reflect.Interface:
|
||||
return nameOfInterfaceRuntimeType(_type, kind, tflag)
|
||||
case reflect.Map:
|
||||
keyname, err := fieldToType(_type, "key")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "map[" + keyname + "]" + elemname, nil
|
||||
case reflect.Ptr:
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "*" + elemname, nil
|
||||
case reflect.Slice:
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "[]" + elemname, nil
|
||||
case reflect.Struct:
|
||||
return nameOfStructRuntimeType(_type, kind, tflag)
|
||||
default:
|
||||
return nameOfNamedRuntimeType(_type, kind, tflag)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the expression describing an anonymous function type.
|
||||
// A runtime.functype is followed by a runtime.uncommontype
|
||||
// (optional) and then by an array of pointers to runtime._type,
|
||||
// one for each input and output argument.
|
||||
func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) {
|
||||
rtyp, err := _type.dbp.findType("runtime._type")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prtyp := _type.dbp.pointerTo(rtyp)
|
||||
|
||||
uadd := _type.RealType.Common().ByteSize
|
||||
if ut := uncommon(_type, tflag); ut != nil {
|
||||
uadd += ut.RealType.Common().ByteSize
|
||||
}
|
||||
|
||||
var inCount, outCount int64
|
||||
if inCountField := _type.toFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil {
|
||||
inCount, _ = constant.Int64Val(inCountField.Value)
|
||||
}
|
||||
if outCountField := _type.toFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil {
|
||||
outCount, _ = constant.Int64Val(outCountField.Value)
|
||||
// only the lowest 15 bits of outCount are used, rest are flags
|
||||
outCount = outCount & (1<<15 - 1)
|
||||
}
|
||||
|
||||
cursortyp := _type.newVariable("", _type.Addr+uintptr(uadd), prtyp)
|
||||
var buf bytes.Buffer
|
||||
if anonymous {
|
||||
buf.WriteString("func(")
|
||||
} else {
|
||||
buf.WriteString("(")
|
||||
}
|
||||
|
||||
for i := int64(0); i < inCount; i++ {
|
||||
argtype := cursortyp.maybeDereference()
|
||||
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteString(argtypename)
|
||||
if i != inCount-1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteString(")")
|
||||
|
||||
switch outCount {
|
||||
case 0:
|
||||
// nothing to do
|
||||
case 1:
|
||||
buf.WriteString(" ")
|
||||
argtype := cursortyp.maybeDereference()
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteString(argtypename)
|
||||
default:
|
||||
buf.WriteString(" (")
|
||||
for i := int64(0); i < outCount; i++ {
|
||||
argtype := cursortyp.maybeDereference()
|
||||
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteString(argtypename)
|
||||
if i != inCount-1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteString(")")
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("interface {")
|
||||
|
||||
methods, _ := _type.structMember("methods")
|
||||
methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1})
|
||||
if methods.Unreadable != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(methods.Children) == 0 {
|
||||
buf.WriteString("}")
|
||||
return buf.String(), nil
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
|
||||
for i, im := range methods.Children {
|
||||
var methodname, methodtype string
|
||||
for i := range im.Children {
|
||||
switch im.Children[i].Name {
|
||||
case "name":
|
||||
nameoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||
var err error
|
||||
methodname, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(nameoff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
case "typ":
|
||||
typeoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||
typ, err := _type.dbp.resolveTypeOff(_type.Addr, uintptr(typeoff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
typ, err = specificRuntimeType(typ, int64(reflect.Func))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var tflag int64
|
||||
if tflagField := typ.toFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
||||
}
|
||||
methodtype, err = nameOfFuncRuntimeType(typ, tflag, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(methodname)
|
||||
buf.WriteString(methodtype)
|
||||
|
||||
if i != len(methods.Children)-1 {
|
||||
buf.WriteString("; ")
|
||||
} else {
|
||||
buf.WriteString(" }")
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("struct {")
|
||||
|
||||
fields, _ := _type.structMember("fields")
|
||||
fields.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1})
|
||||
if fields.Unreadable != nil {
|
||||
return "", fields.Unreadable
|
||||
}
|
||||
|
||||
if len(fields.Children) == 0 {
|
||||
buf.WriteString("}")
|
||||
return buf.String(), nil
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
|
||||
for i, field := range fields.Children {
|
||||
var fieldname, fieldtypename string
|
||||
var typeField *Variable
|
||||
for i := range field.Children {
|
||||
switch field.Children[i].Name {
|
||||
case "name":
|
||||
nameoff, _ := constant.Int64Val(field.Children[i].Value)
|
||||
var err error
|
||||
fieldname, _, _, err = _type.dbp.loadName(uintptr(nameoff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
case "typ":
|
||||
typeField = field.Children[i].maybeDereference()
|
||||
var err error
|
||||
fieldtypename, _, err = nameOfRuntimeType(typeField)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fieldname will be the empty string for anonymous fields
|
||||
if fieldname != "" {
|
||||
buf.WriteString(fieldname)
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
buf.WriteString(fieldtypename)
|
||||
if i != len(fields.Children)-1 {
|
||||
buf.WriteString("; ")
|
||||
} else {
|
||||
buf.WriteString(" }")
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func fieldToType(_type *Variable, fieldName string) (string, error) {
|
||||
typeField, err := _type.structMember(fieldName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
typeField = typeField.maybeDereference()
|
||||
typename, _, err := nameOfRuntimeType(typeField)
|
||||
return typename, err
|
||||
}
|
||||
|
||||
func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
|
||||
rtyp, err := _type.dbp.findType("runtime._type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prtyp := _type.dbp.pointerTo(rtyp)
|
||||
|
||||
uintptrtyp, err := _type.dbp.findType("uintptr")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uint32typ := &dwarf.UintType{dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 4, Name: "uint32"}}}
|
||||
uint16typ := &dwarf.UintType{dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 2, Name: "uint16"}}}
|
||||
|
||||
newStructType := func(name string, sz uintptr) *dwarf.StructType {
|
||||
return &dwarf.StructType{dwarf.CommonType{Name: name, ByteSize: int64(sz)}, name, "struct", nil, false}
|
||||
}
|
||||
|
||||
appendField := func(typ *dwarf.StructType, name string, fieldtype dwarf.Type, off uintptr) {
|
||||
typ.Field = append(typ.Field, &dwarf.StructField{Name: name, ByteOffset: int64(off), Type: fieldtype})
|
||||
}
|
||||
|
||||
newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
|
||||
r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size()))
|
||||
appendField(r, "array", _type.dbp.pointerTo(elemtype), 0)
|
||||
appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
|
||||
appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
|
||||
return &dwarf.SliceType{StructType: *r, ElemType: elemtype}
|
||||
}
|
||||
|
||||
var typ *dwarf.StructType
|
||||
|
||||
type rtype struct {
|
||||
size uintptr
|
||||
ptrdata uintptr
|
||||
hash uint32 // hash of type; avoids computation in hash tables
|
||||
tflag uint8 // extra type information flags
|
||||
align uint8 // alignment of variable with this type
|
||||
fieldAlign uint8 // alignment of struct field with this type
|
||||
kind uint8 // enumeration for C
|
||||
alg *byte // algorithm table
|
||||
gcdata *byte // garbage collection data
|
||||
str int32 // string form
|
||||
ptrToThis int32 // type for pointer to this type, may be zero
|
||||
}
|
||||
|
||||
switch reflect.Kind(kind & kindMask) {
|
||||
case reflect.Array:
|
||||
// runtime.arraytype
|
||||
var a struct {
|
||||
rtype
|
||||
elem *rtype // array element type
|
||||
slice *rtype // slice type
|
||||
len uintptr
|
||||
}
|
||||
typ = newStructType("runtime.arraytype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
appendField(typ, "len", uintptrtyp, unsafe.Offsetof(a.len))
|
||||
case reflect.Chan:
|
||||
// runtime.chantype
|
||||
var a struct {
|
||||
rtype
|
||||
elem *rtype // channel element type
|
||||
dir uintptr // channel direction (ChanDir)
|
||||
}
|
||||
typ = newStructType("runtime.chantype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Func:
|
||||
// runtime.functype
|
||||
var a struct {
|
||||
rtype `reflect:"func"`
|
||||
inCount uint16
|
||||
outCount uint16 // top bit is set if last input parameter is ...
|
||||
}
|
||||
typ = newStructType("runtime.functype", unsafe.Sizeof(a))
|
||||
appendField(typ, "inCount", uint16typ, unsafe.Offsetof(a.inCount))
|
||||
appendField(typ, "outCount", uint16typ, unsafe.Offsetof(a.outCount))
|
||||
case reflect.Interface:
|
||||
// runtime.imethod
|
||||
type imethod struct {
|
||||
name uint32 // name of method
|
||||
typ uint32 // .(*FuncType) underneath
|
||||
}
|
||||
|
||||
var im imethod
|
||||
|
||||
// runtime.interfacetype
|
||||
var a struct {
|
||||
rtype `reflect:"interface"`
|
||||
pkgPath *byte // import path
|
||||
methods []imethod // sorted by hash
|
||||
}
|
||||
|
||||
imethodtype := newStructType("runtime.imethod", unsafe.Sizeof(im))
|
||||
appendField(imethodtype, "name", uint32typ, unsafe.Offsetof(im.name))
|
||||
appendField(imethodtype, "typ", uint32typ, unsafe.Offsetof(im.typ))
|
||||
typ = newStructType("runtime.interfacetype", unsafe.Sizeof(a))
|
||||
appendField(typ, "methods", newSliceType(imethodtype), unsafe.Offsetof(a.methods))
|
||||
case reflect.Map:
|
||||
// runtime.maptype
|
||||
var a struct {
|
||||
rtype `reflect:"map"`
|
||||
key *rtype // map key type
|
||||
elem *rtype // map element (value) type
|
||||
bucket *rtype // internal bucket structure
|
||||
hmap *rtype // internal map header
|
||||
keysize uint8 // size of key slot
|
||||
indirectkey uint8 // store ptr to key instead of key itself
|
||||
valuesize uint8 // size of value slot
|
||||
indirectvalue uint8 // store ptr to value instead of value itself
|
||||
bucketsize uint16 // size of bucket
|
||||
reflexivekey bool // true if k==k for all keys
|
||||
needkeyupdate bool // true if we need to update key on an overwrite
|
||||
}
|
||||
typ = newStructType("runtime.maptype", unsafe.Sizeof(a))
|
||||
appendField(typ, "key", prtyp, unsafe.Offsetof(a.key))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Ptr:
|
||||
// runtime.ptrtype
|
||||
var a struct {
|
||||
rtype `reflect:"ptr"`
|
||||
elem *rtype // pointer element (pointed at) type
|
||||
}
|
||||
typ = newStructType("runtime.ptrtype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Slice:
|
||||
// runtime.slicetype
|
||||
var a struct {
|
||||
rtype `reflect:"slice"`
|
||||
elem *rtype // slice element type
|
||||
}
|
||||
|
||||
typ = newStructType("runtime.slicetype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Struct:
|
||||
// runtime.structtype
|
||||
type structField struct {
|
||||
name *byte // name is empty for embedded fields
|
||||
typ *rtype // type of field
|
||||
offset uintptr // byte offset of field within struct
|
||||
}
|
||||
|
||||
var sf structField
|
||||
|
||||
var a struct {
|
||||
rtype `reflect:"struct"`
|
||||
pkgPath *byte
|
||||
fields []structField // sorted by offset
|
||||
}
|
||||
|
||||
fieldtype := newStructType("runtime.structtype", unsafe.Sizeof(sf))
|
||||
appendField(fieldtype, "name", uintptrtyp, unsafe.Offsetof(sf.name))
|
||||
appendField(fieldtype, "typ", prtyp, unsafe.Offsetof(sf.typ))
|
||||
typ = newStructType("runtime.structtype", unsafe.Sizeof(a))
|
||||
appendField(typ, "fields", newSliceType(fieldtype), unsafe.Offsetof(a.fields))
|
||||
default:
|
||||
return _type, nil
|
||||
}
|
||||
|
||||
return _type.newVariable(_type.Name, _type.Addr, typ), nil
|
||||
}
|
||||
|
||||
// See reflect.(*rtype).uncommon in $GOROOT/src/reflect/type.go
|
||||
func uncommon(_type *Variable, tflag int64) *Variable {
|
||||
if tflag&tflagUncommon == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
typ, err := _type.dbp.findType("runtime.uncommontype")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return _type.newVariable(_type.Name, _type.Addr+uintptr(_type.RealType.Size()), typ)
|
||||
}
|
1572
vendor/github.com/derekparker/delve/proc/variables.go
generated
vendored
Normal file
1572
vendor/github.com/derekparker/delve/proc/variables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
181
vendor/github.com/derekparker/delve/proc/zsyscall_windows.go
generated
vendored
Normal file
181
vendor/github.com/derekparker/delve/proc/zsyscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package proc
|
||||
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user