1
0
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:
Faissal Elamraoui
2017-03-26 16:55:28 +02:00
parent abf3c41032
commit 4dd6983d3c
67 changed files with 448 additions and 925 deletions

79
vendor/github.com/derekparker/delve/proc/arch.go generated vendored Normal file
View 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
View 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
View 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
}

View 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
View 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

File diff suppressed because it is too large Load Diff

283
vendor/github.com/derekparker/delve/proc/exc.h generated vendored Normal file
View 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_ */

View 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
View 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
View 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
View 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
View 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
View 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_ */

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}

View 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()
}

View 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
}

View 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 Developers 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(&regset.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
}

View 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
View 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, " "))
}

View 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 threads 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
}

View 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, &regs) })
if err != nil {
return nil, err
}
r := &Regs{&regs, 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
}

View 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
View 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
}

View 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

View 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
View 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)
}

View 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)&regs, &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)&regs, 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)&regs, &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)&regs, 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;
}

View 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
}

View 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);

View 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
}

View 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
View 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

File diff suppressed because it is too large Load Diff

View 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
}