2020-01-21 20:41:24 +00:00
package proc
2020-03-10 19:27:38 +00:00
import (
2020-03-23 17:57:01 +00:00
"errors"
2020-03-10 19:27:38 +00:00
"fmt"
2020-03-23 17:57:01 +00:00
"go/constant"
"os"
2021-07-02 16:37:55 +00:00
"sort"
2020-03-23 17:57:01 +00:00
"strings"
2021-07-02 16:37:55 +00:00
"github.com/go-delve/delve/pkg/dwarf/op"
2020-03-23 17:57:01 +00:00
"github.com/go-delve/delve/pkg/goversion"
2022-10-04 15:07:05 +00:00
"github.com/go-delve/delve/pkg/logflags"
2021-10-25 19:37:36 +00:00
"github.com/go-delve/delve/pkg/proc/internal/ebpf"
2020-03-23 17:57:01 +00:00
)
var (
// ErrNotRecorded is returned when an action is requested that is
// only possible on recorded (traced) programs.
ErrNotRecorded = errors . New ( "not a recording" )
// ErrNoRuntimeAllG is returned when the runtime.allg list could
// not be found.
ErrNoRuntimeAllG = errors . New ( "could not find goroutine array" )
// ErrProcessDetached indicates that we detached from the target process.
ErrProcessDetached = errors . New ( "detached from the process" )
2020-03-10 19:27:38 +00:00
)
2020-10-21 19:50:52 +00:00
type LaunchFlags uint8
const (
LaunchForeground LaunchFlags = 1 << iota
LaunchDisableASLR
)
2020-01-21 20:41:24 +00:00
// Target represents the process being debugged.
type Target struct {
Process
2023-02-22 17:26:28 +00:00
proc ProcessInternal
recman RecordingManipulationInternal
2021-11-26 16:06:23 +00:00
2023-05-09 18:40:00 +00:00
pid int
CmdLine string
2020-03-10 19:27:38 +00:00
// StopReason describes the reason why the target process is stopped.
// A process could be stopped for multiple simultaneous reasons, in which
// case only one will be reported.
StopReason StopReason
2020-11-09 19:28:40 +00:00
// currentThread is the thread that will be used by next/step/stepout and to evaluate variables if no goroutine is selected.
currentThread Thread
2020-03-10 19:27:38 +00:00
// 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
2020-01-21 20:41:24 +00:00
// fncallForG stores a mapping of current active function calls.
2022-08-16 16:31:11 +00:00
fncallForG map [ int64 ] * callInjection
2020-01-21 20:41:24 +00:00
2020-02-11 01:31:54 +00:00
asyncPreemptChanged bool // runtime/debug.asyncpreemptoff was changed
asyncPreemptOff int64 // cached value of runtime/debug.asyncpreemptoff
2020-01-21 20:41:24 +00:00
// gcache is a cache for Goroutines that we
// have read and parsed from the targets memory.
// This must be cleared whenever the target is resumed.
gcache goroutineCache
2021-02-23 20:38:52 +00:00
iscgo * bool
2021-06-22 11:35:13 +00:00
// exitStatus is the exit status of the process we are debugging.
// Saved here to relay to any future commands.
exitStatus int
2021-07-02 16:37:55 +00:00
// fakeMemoryRegistry contains the list of all compositeMemory objects
// created since the last restart, it exists so that registerized variables
// can be given a unique address.
fakeMemoryRegistry [ ] * compositeMemory
fakeMemoryRegistryMap map [ string ] * compositeMemory
2022-03-21 19:42:37 +00:00
2022-07-14 21:14:45 +00:00
partOfGroup bool
2020-01-21 20:41:24 +00:00
}
2021-08-09 17:56:20 +00:00
type KeepSteppingBreakpoints uint8
const (
HaltKeepsSteppingBreakpoints KeepSteppingBreakpoints = 1 << iota
TracepointKeepsSteppingBreakpoints
)
2020-03-23 17:57:01 +00:00
// ErrProcessExited indicates that the process has exited and contains both
// process id and exit status.
type ErrProcessExited struct {
Pid int
Status int
}
func ( pe ErrProcessExited ) Error ( ) string {
return fmt . Sprintf ( "Process %d has exited with status %d" , pe . Pid , pe . Status )
}
2020-03-10 19:27:38 +00:00
// StopReason describes the reason why the target process is stopped.
// A process could be stopped for multiple simultaneous reasons, in which
// case only one will be reported.
type StopReason uint8
2020-11-12 23:24:31 +00:00
// String maps StopReason to string representation.
func ( sr StopReason ) String ( ) string {
switch sr {
case StopUnknown :
2021-05-10 18:36:03 +00:00
return "unknown"
2020-11-12 23:24:31 +00:00
case StopLaunched :
return "launched"
case StopAttached :
return "attached"
case StopExited :
return "exited"
case StopBreakpoint :
return "breakpoint"
case StopHardcodedBreakpoint :
return "hardcoded breakpoint"
case StopManual :
return "manual"
case StopNextFinished :
return "next finished"
case StopCallReturned :
return "call returned"
2021-05-06 17:33:56 +00:00
case StopWatchpoint :
return "watchpoint"
2020-11-12 23:24:31 +00:00
default :
return ""
}
}
2020-03-10 19:27:38 +00:00
const (
StopUnknown StopReason = iota
StopLaunched // The process was just launched
StopAttached // The debugger stopped the process after attaching
2020-03-27 21:30:27 +00:00
StopExited // The target process terminated
2020-03-10 19:27:38 +00:00
StopBreakpoint // The target process hit one or more software breakpoints
StopHardcodedBreakpoint // The target process hit a hardcoded breakpoint (for example runtime.Breakpoint())
StopManual // A manual stop was requested
2022-01-06 17:01:47 +00:00
StopNextFinished // The next/step/stepout/stepInstruction command terminated
2020-03-27 21:30:27 +00:00
StopCallReturned // An injected call completed
2021-05-06 17:33:56 +00:00
StopWatchpoint // The target process hit one or more watchpoints
2020-03-10 19:27:38 +00:00
)
2020-03-23 17:57:01 +00:00
// DisableAsyncPreemptEnv returns a process environment (like os.Environ)
// where asyncpreemptoff is set to 1.
func DisableAsyncPreemptEnv ( ) [ ] string {
env := os . Environ ( )
for i := range env {
if strings . HasPrefix ( env [ i ] , "GODEBUG=" ) {
// Go 1.14 asynchronous preemption mechanism is incompatible with
// debuggers, see: https://github.com/golang/go/issues/36494
env [ i ] += ",asyncpreemptoff=1"
}
}
return env
}
2023-02-22 17:26:28 +00:00
// newTarget returns an initialized Target object.
2021-11-26 16:06:23 +00:00
// The p argument can optionally implement the RecordingManipulation interface.
2023-05-09 18:40:00 +00:00
func ( grp * TargetGroup ) newTarget ( p ProcessInternal , pid int , currentThread Thread , path , cmdline string ) ( * Target , error ) {
2020-03-10 19:27:38 +00:00
entryPoint , err := p . EntryPoint ( )
if err != nil {
return nil , err
}
2023-02-22 17:26:28 +00:00
err = p . BinInfo ( ) . LoadBinaryInfo ( path , entryPoint , grp . cfg . DebugInfoDirs )
2020-03-10 19:27:38 +00:00
if err != nil {
return nil , err
}
for _ , image := range p . BinInfo ( ) . Images {
if image . loadErr != nil {
return nil , image . loadErr
}
}
2020-01-21 20:41:24 +00:00
t := & Target {
2020-11-09 19:28:40 +00:00
Process : p ,
2021-11-26 16:06:23 +00:00
proc : p ,
2022-08-16 16:31:11 +00:00
fncallForG : make ( map [ int64 ] * callInjection ) ,
2020-11-09 19:28:40 +00:00
currentThread : currentThread ,
2021-11-26 16:06:23 +00:00
pid : pid ,
2023-05-09 18:40:00 +00:00
CmdLine : cmdline ,
2020-01-21 20:41:24 +00:00
}
2020-03-10 19:27:38 +00:00
2021-11-26 16:06:23 +00:00
if recman , ok := p . ( RecordingManipulationInternal ) ; ok {
t . recman = recman
} else {
t . recman = & dummyRecordingManipulation { }
}
2020-11-09 19:28:40 +00:00
g , _ := GetG ( currentThread )
2020-03-10 19:27:38 +00:00
t . selectedGoroutine = g
2023-03-14 21:23:25 +00:00
t . Breakpoints ( ) . Logical = grp . LogicalBreakpoints
2020-06-03 18:14:14 +00:00
t . createUnrecoveredPanicBreakpoint ( )
t . createFatalThrowBreakpoint ( )
2022-10-04 15:07:05 +00:00
t . createPluginOpenBreakpoint ( )
2020-03-10 19:27:38 +00:00
2020-01-21 20:41:24 +00:00
t . gcache . init ( p . BinInfo ( ) )
2021-07-02 16:37:55 +00:00
t . fakeMemoryRegistryMap = make ( map [ string ] * compositeMemory )
2020-02-11 01:31:54 +00:00
2023-02-22 17:26:28 +00:00
if grp . cfg . DisableAsyncPreempt {
2020-02-11 01:31:54 +00:00
setAsyncPreemptOff ( t , 1 )
}
2020-03-10 19:27:38 +00:00
return t , nil
2020-01-21 20:41:24 +00:00
}
2021-11-26 16:06:23 +00:00
// Pid returns the pid of the target process.
func ( t * Target ) Pid ( ) int {
return t . pid
}
2021-02-23 20:38:52 +00:00
// IsCgo returns the value of runtime.iscgo
func ( t * Target ) IsCgo ( ) bool {
if t . iscgo != nil {
return * t . iscgo
}
2021-08-09 17:16:24 +00:00
scope := globalScope ( t , t . BinInfo ( ) , t . BinInfo ( ) . Images [ 0 ] , t . Memory ( ) )
2021-02-23 20:38:52 +00:00
iscgov , err := scope . findGlobal ( "runtime" , "iscgo" )
if err == nil {
iscgov . loadValue ( loadFullValue )
if iscgov . Unreadable == nil {
t . iscgo = new ( bool )
* t . iscgo = constant . BoolVal ( iscgov . Value )
return constant . BoolVal ( iscgov . Value )
}
}
return false
}
2021-06-22 11:35:13 +00:00
// Valid returns true if this Process can be used. When it returns false it
// also returns an error describing why the Process is invalid (either
// ErrProcessExited or ErrProcessDetached).
func ( t * Target ) Valid ( ) ( bool , error ) {
ok , err := t . proc . Valid ( )
if ! ok && err != nil {
if pe , ok := err . ( ErrProcessExited ) ; ok {
pe . Status = t . exitStatus
err = pe
}
}
return ok , err
}
2020-01-21 20:41:24 +00:00
// SupportsFunctionCalls returns whether or not the backend supports
// calling functions during a debug session.
// Currently only non-recorded processes running on AMD64 support
// function calls.
func ( t * Target ) SupportsFunctionCalls ( ) bool {
2023-09-21 17:39:57 +00:00
return t . Process . BinInfo ( ) . Arch . Name == "amd64" || ( t . Process . BinInfo ( ) . Arch . Name == "arm64" && t . Process . BinInfo ( ) . GOOS != "windows" ) || t . Process . BinInfo ( ) . Arch . Name == "ppc64le"
2020-01-21 20:41:24 +00:00
}
2021-07-02 16:37:55 +00:00
// ClearCaches clears internal caches that should not survive a restart.
2020-01-21 20:41:24 +00:00
// This should be called anytime the target process executes instructions.
2021-07-02 16:37:55 +00:00
func ( t * Target ) ClearCaches ( ) {
t . clearFakeMemory ( )
2020-01-21 20:41:24 +00:00
t . gcache . Clear ( )
2024-09-18 21:17:07 +00:00
t . BinInfo ( ) . moduleDataCache = nil
2020-03-10 19:48:46 +00:00
for _ , thread := range t . ThreadList ( ) {
thread . Common ( ) . g = nil
}
2020-01-21 20:41:24 +00:00
}
2022-07-14 21:14:45 +00:00
// Restart will start the process group over from the location specified by the "from" locspec.
2020-03-04 18:21:29 +00:00
// This is only useful for recorded targets.
// Restarting of a normal process happens at a higher level (debugger.Restart).
2022-07-14 21:14:45 +00:00
func ( grp * TargetGroup ) Restart ( from string ) error {
if len ( grp . targets ) != 1 {
panic ( "multiple targets not implemented" )
}
for _ , t := range grp . targets {
t . ClearCaches ( )
}
t := grp . Selected
currentThread , err := t . recman . Restart ( grp . cctx , from )
2020-03-10 19:27:38 +00:00
if err != nil {
return err
}
2020-11-09 19:28:40 +00:00
t . currentThread = currentThread
2020-03-10 19:27:38 +00:00
t . selectedGoroutine , _ = GetG ( t . CurrentThread ( ) )
if from != "" {
t . StopReason = StopManual
} else {
t . StopReason = StopLaunched
}
return nil
}
// SelectedGoroutine returns the currently selected goroutine.
func ( t * Target ) SelectedGoroutine ( ) * G {
return t . selectedGoroutine
}
// SwitchGoroutine will change the selected and active goroutine.
2023-08-17 21:24:43 +00:00
func ( t * Target ) SwitchGoroutine ( g * G ) error {
if ok , err := t . Valid ( ) ; ! ok {
2020-03-10 19:27:38 +00:00
return err
}
if g == nil {
return nil
}
if g . Thread != nil {
2023-08-17 21:24:43 +00:00
return t . SwitchThread ( g . Thread . ThreadID ( ) )
2020-03-10 19:27:38 +00:00
}
2023-08-17 21:24:43 +00:00
t . selectedGoroutine = g
2020-03-10 19:27:38 +00:00
return nil
}
// SwitchThread will change the selected and active thread.
2023-08-17 21:24:43 +00:00
func ( t * Target ) SwitchThread ( tid int ) error {
if ok , err := t . Valid ( ) ; ! ok {
2020-03-10 19:27:38 +00:00
return err
}
2023-08-17 21:24:43 +00:00
if th , ok := t . FindThread ( tid ) ; ok {
t . currentThread = th
t . selectedGoroutine , _ = GetG ( t . CurrentThread ( ) )
2020-03-10 19:27:38 +00:00
return nil
}
return fmt . Errorf ( "thread %d does not exist" , tid )
2020-01-21 20:41:24 +00:00
}
2020-02-11 01:31:54 +00:00
2020-03-23 17:57:01 +00:00
// setAsyncPreemptOff enables or disables async goroutine preemption by
// writing the value 'v' to runtime.debug.asyncpreemptoff.
// A value of '1' means off, a value of '0' means on.
func setAsyncPreemptOff ( p * Target , v int64 ) {
if producer := p . BinInfo ( ) . Producer ( ) ; producer == "" || ! goversion . ProducerAfterOrEqual ( producer , 1 , 14 ) {
return
}
logger := p . BinInfo ( ) . logger
2021-08-09 17:16:24 +00:00
scope := globalScope ( p , p . BinInfo ( ) , p . BinInfo ( ) . Images [ 0 ] , p . Memory ( ) )
2021-08-23 18:32:02 +00:00
// +rtype -var debug anytype
2020-03-23 17:57:01 +00:00
debugv , err := scope . findGlobal ( "runtime" , "debug" )
2023-08-16 13:17:03 +00:00
if err != nil {
logger . Warnf ( "could not find runtime/debug variable (or unreadable): %v" , err )
return
}
if debugv . Unreadable != nil {
logger . Warnf ( "runtime/debug variable unreadable: %v" , err , debugv . Unreadable )
2020-03-23 17:57:01 +00:00
return
}
2021-08-23 18:32:02 +00:00
asyncpreemptoffv , err := debugv . structMember ( "asyncpreemptoff" ) // +rtype int32
2020-03-23 17:57:01 +00:00
if err != nil {
logger . Warnf ( "could not find asyncpreemptoff field: %v" , err )
return
}
asyncpreemptoffv . loadValue ( loadFullValue )
if asyncpreemptoffv . Unreadable != nil {
logger . Warnf ( "asyncpreemptoff field unreadable: %v" , asyncpreemptoffv . Unreadable )
return
}
p . asyncPreemptChanged = true
p . asyncPreemptOff , _ = constant . Int64Val ( asyncpreemptoffv . Value )
err = scope . setValue ( asyncpreemptoffv , newConstant ( constant . MakeInt64 ( v ) , scope . Mem ) , "" )
2021-03-09 10:35:24 +00:00
if err != nil {
logger . Warnf ( "could not set asyncpreemptoff %v" , err )
}
2020-03-23 17:57:01 +00:00
}
// createUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint.
2020-06-03 18:14:14 +00:00
func ( t * Target ) createUnrecoveredPanicBreakpoint ( ) {
panicpcs , err := FindFunctionLocation ( t . Process , "runtime.startpanic" , 0 )
2020-03-23 17:57:01 +00:00
if _ , isFnNotFound := err . ( * ErrFunctionNotFound ) ; isFnNotFound {
2020-06-03 18:14:14 +00:00
panicpcs , err = FindFunctionLocation ( t . Process , "runtime.fatalpanic" , 0 )
2020-03-23 17:57:01 +00:00
}
if err == nil {
2022-02-25 09:09:53 +00:00
bp , err := t . SetBreakpoint ( unrecoveredPanicID , panicpcs [ 0 ] , UserBreakpoint , nil )
2020-03-23 17:57:01 +00:00
if err == nil {
2022-05-25 20:58:26 +00:00
bp . Logical . Name = UnrecoveredPanic
bp . Logical . Variables = [ ] string { "runtime.curg._panic.arg" }
2020-03-23 17:57:01 +00:00
}
}
}
// createFatalThrowBreakpoint creates the a breakpoint as runtime.fatalthrow.
2020-06-03 18:14:14 +00:00
func ( t * Target ) createFatalThrowBreakpoint ( ) {
2023-06-27 16:33:07 +00:00
setFatalThrow := func ( pcs [ ] uint64 , err error ) {
2022-05-18 16:16:26 +00:00
if err == nil {
2023-06-27 16:33:07 +00:00
bp , err := t . SetBreakpoint ( fatalThrowID , pcs [ 0 ] , UserBreakpoint , nil )
if err == nil {
bp . Logical . Name = FatalThrow
}
2022-05-18 16:16:26 +00:00
}
}
2023-06-27 16:33:07 +00:00
setFatalThrow ( FindFunctionLocation ( t . Process , "runtime.throw" , 0 ) )
setFatalThrow ( FindFunctionLocation ( t . Process , "runtime.fatal" , 0 ) )
setFatalThrow ( FindFunctionLocation ( t . Process , "runtime.winthrow" , 0 ) )
setFatalThrow ( FindFunctionLocation ( t . Process , "runtime.fatalsignal" , 0 ) )
2020-03-23 17:57:01 +00:00
}
2020-11-09 19:28:40 +00:00
2022-10-04 15:07:05 +00:00
// createPluginOpenBreakpoint creates a breakpoint at the return instruction
// of plugin.Open (if it exists) that will try to enable suspended
// breakpoints.
func ( t * Target ) createPluginOpenBreakpoint ( ) {
retpcs , _ := findRetPC ( t , "plugin.Open" )
for _ , retpc := range retpcs {
bp , err := t . SetBreakpoint ( 0 , retpc , PluginOpenBreakpoint , nil )
if err != nil {
t . BinInfo ( ) . logger . Errorf ( "could not set plugin.Open breakpoint: %v" , err )
} else {
bp . Breaklets [ len ( bp . Breaklets ) - 1 ] . callback = t . pluginOpenCallback
}
}
}
2020-11-09 19:28:40 +00:00
// CurrentThread returns the currently selected thread which will be used
// for next/step/stepout and for reading variables, unless a goroutine is
// selected.
func ( t * Target ) CurrentThread ( ) Thread {
return t . currentThread
}
2021-04-12 21:59:43 +00:00
2021-07-31 15:16:26 +00:00
type UProbeTraceResult struct {
2021-10-25 19:37:36 +00:00
FnAddr int
GoroutineID int
2022-07-29 10:00:32 +00:00
IsRet bool
2021-10-25 19:37:36 +00:00
InputParams [ ] * Variable
ReturnParams [ ] * Variable
2021-07-31 15:16:26 +00:00
}
func ( t * Target ) GetBufferedTracepoints ( ) [ ] * UProbeTraceResult {
var results [ ] * UProbeTraceResult
tracepoints := t . proc . GetBufferedTracepoints ( )
2021-10-25 19:37:36 +00:00
convertInputParamToVariable := func ( ip * ebpf . RawUProbeParam ) * Variable {
v := & Variable { }
v . RealType = ip . RealType
v . Len = ip . Len
v . Base = ip . Base
v . Addr = ip . Addr
v . Kind = ip . Kind
2023-07-25 23:27:37 +00:00
if v . RealType == nil {
v . Unreadable = errors . New ( "type not supported by ebpf" )
return v
}
2021-10-25 19:37:36 +00:00
cachedMem := CreateLoadedCachedMemory ( ip . Data )
2023-05-15 21:46:33 +00:00
compMem , _ := CreateCompositeMemory ( cachedMem , t . BinInfo ( ) . Arch , op . DwarfRegisters { } , ip . Pieces , ip . RealType . Common ( ) . ByteSize )
2021-10-25 19:37:36 +00:00
v . mem = compMem
// Load the value here so that we don't have to export
// loadValue outside of proc.
v . loadValue ( loadFullValue )
return v
}
2021-07-31 15:16:26 +00:00
for _ , tp := range tracepoints {
r := & UProbeTraceResult { }
r . FnAddr = tp . FnAddr
2021-08-24 12:53:27 +00:00
r . GoroutineID = tp . GoroutineID
2022-07-29 10:00:32 +00:00
r . IsRet = tp . IsRet
2021-07-31 15:16:26 +00:00
for _ , ip := range tp . InputParams {
2021-10-25 19:37:36 +00:00
v := convertInputParamToVariable ( ip )
2021-07-31 15:16:26 +00:00
r . InputParams = append ( r . InputParams , v )
}
2021-10-25 19:37:36 +00:00
for _ , ip := range tp . ReturnParams {
v := convertInputParamToVariable ( ip )
r . ReturnParams = append ( r . ReturnParams , v )
}
2021-07-31 15:16:26 +00:00
results = append ( results , r )
}
return results
}
2022-03-21 19:42:37 +00:00
// ResumeNotify specifies a channel that will be closed the next time
2022-07-14 21:14:45 +00:00
// Continue finishes resuming the targets.
2023-08-17 21:24:43 +00:00
func ( grp * TargetGroup ) ResumeNotify ( ch chan <- struct { } ) {
grp . cctx . ResumeChan = ch
2022-03-21 19:42:37 +00:00
}
2022-07-14 21:14:45 +00:00
// RequestManualStop attempts to stop all the processes' threads.
2023-08-17 21:24:43 +00:00
func ( grp * TargetGroup ) RequestManualStop ( ) error {
grp . cctx . StopMu . Lock ( )
defer grp . cctx . StopMu . Unlock ( )
grp . cctx . manualStopRequested = true
return grp . Selected . proc . RequestManualStop ( grp . cctx )
2022-03-21 19:42:37 +00:00
}
2021-07-02 16:37:55 +00:00
const (
2021-07-31 15:16:26 +00:00
FakeAddressBase = 0xbeef000000000000
2022-03-23 15:10:00 +00:00
fakeAddressUnresolv = 0xbeed000000000000 // this address never resolves to memory
2021-07-02 16:37:55 +00:00
)
// newCompositeMemory creates a new compositeMemory object and registers it.
// If the same composite memory has been created before it will return a
// cached object.
// This caching is primarily done so that registerized variables don't get a
// different address every time they are evaluated, which would be confusing
// and leak memory.
2023-05-15 21:46:33 +00:00
func ( t * Target ) newCompositeMemory ( mem MemoryReadWriter , regs op . DwarfRegisters , pieces [ ] op . Piece , descr * locationExpr , size int64 ) ( int64 , * compositeMemory , error ) {
2021-07-02 16:37:55 +00:00
var key string
if regs . CFA != 0 && len ( pieces ) > 0 {
// key is created by concatenating the location expression with the CFA,
// this combination is guaranteed to be unique between resumes.
buf := new ( strings . Builder )
fmt . Fprintf ( buf , "%#x " , regs . CFA )
2022-07-15 12:56:00 +00:00
op . PrettyPrint ( buf , descr . instr , t . BinInfo ( ) . Arch . RegnumToString )
2021-07-02 16:37:55 +00:00
key = buf . String ( )
if cmem := t . fakeMemoryRegistryMap [ key ] ; cmem != nil {
return int64 ( cmem . base ) , cmem , nil
}
}
2023-05-15 21:46:33 +00:00
cmem , err := newCompositeMemory ( mem , t . BinInfo ( ) . Arch , regs , pieces , size )
2021-07-02 16:37:55 +00:00
if err != nil {
return 0 , cmem , err
}
t . registerFakeMemory ( cmem )
if key != "" {
t . fakeMemoryRegistryMap [ key ] = cmem
}
return int64 ( cmem . base ) , cmem , nil
}
func ( t * Target ) registerFakeMemory ( mem * compositeMemory ) ( addr uint64 ) {
t . fakeMemoryRegistry = append ( t . fakeMemoryRegistry , mem )
2021-07-31 15:16:26 +00:00
addr = FakeAddressBase
2021-07-02 16:37:55 +00:00
if len ( t . fakeMemoryRegistry ) > 1 {
prevMem := t . fakeMemoryRegistry [ len ( t . fakeMemoryRegistry ) - 2 ]
addr = uint64 ( alignAddr ( int64 ( prevMem . base + uint64 ( len ( prevMem . data ) ) ) , 0x100 ) ) // the call to alignAddr just makes the address look nicer, it is not necessary
}
mem . base = addr
return addr
}
func ( t * Target ) findFakeMemory ( addr uint64 ) * compositeMemory {
i := sort . Search ( len ( t . fakeMemoryRegistry ) , func ( i int ) bool {
mem := t . fakeMemoryRegistry [ i ]
return addr <= mem . base || ( mem . base <= addr && addr < ( mem . base + uint64 ( len ( mem . data ) ) ) )
} )
if i != len ( t . fakeMemoryRegistry ) {
mem := t . fakeMemoryRegistry [ i ]
if mem . base <= addr && addr < ( mem . base + uint64 ( len ( mem . data ) ) ) {
return mem
}
}
return nil
}
func ( t * Target ) clearFakeMemory ( ) {
for i := range t . fakeMemoryRegistry {
t . fakeMemoryRegistry [ i ] = nil
}
t . fakeMemoryRegistry = t . fakeMemoryRegistry [ : 0 ]
t . fakeMemoryRegistryMap = make ( map [ string ] * compositeMemory )
}
2021-07-08 15:47:53 +00:00
// dwrapUnwrap checks if fn is a dwrap wrapper function and unwraps it if it is.
func ( t * Target ) dwrapUnwrap ( fn * Function ) * Function {
if fn == nil {
return nil
}
2021-09-16 13:38:15 +00:00
if ! strings . Contains ( fn . Name , "·dwrap·" ) && ! fn . trampoline {
2021-07-08 15:47:53 +00:00
return fn
}
if unwrap := t . BinInfo ( ) . dwrapUnwrapCache [ fn . Entry ] ; unwrap != nil {
return unwrap
}
text , err := disassemble ( t . Memory ( ) , nil , t . Breakpoints ( ) , t . BinInfo ( ) , fn . Entry , fn . End , false )
if err != nil {
return fn
}
for _ , instr := range text {
if instr . IsCall ( ) && instr . DestLoc != nil && instr . DestLoc . Fn != nil && ! instr . DestLoc . Fn . privateRuntime ( ) {
t . BinInfo ( ) . dwrapUnwrapCache [ fn . Entry ] = instr . DestLoc . Fn
return instr . DestLoc . Fn
}
}
return fn
}
2021-11-26 16:06:23 +00:00
2023-04-24 19:12:54 +00:00
func ( t * Target ) pluginOpenCallback ( Thread , * Target ) ( bool , error ) {
2022-10-04 15:07:05 +00:00
logger := logflags . DebuggerLogger ( )
for _ , lbp := range t . Breakpoints ( ) . Logical {
if isSuspended ( t , lbp ) {
err := enableBreakpointOnTarget ( t , lbp )
if err != nil {
logger . Debugf ( "could not enable breakpoint %d: %v" , lbp . LogicalID , err )
} else {
logger . Debugf ( "suspended breakpoint %d enabled" , lbp . LogicalID )
}
}
}
2023-04-24 19:12:54 +00:00
return false , nil
2022-10-04 15:07:05 +00:00
}
func isSuspended ( t * Target , lbp * LogicalBreakpoint ) bool {
for _ , bp := range t . Breakpoints ( ) . M {
if bp . LogicalID ( ) == lbp . LogicalID {
return false
}
}
return true
}
2021-11-26 16:06:23 +00:00
type dummyRecordingManipulation struct {
}
// Recorded always returns false for the native proc backend.
func ( * dummyRecordingManipulation ) Recorded ( ) ( bool , string ) { return false , "" }
// ChangeDirection will always return an error in the native proc backend, only for
// recorded traces.
func ( * dummyRecordingManipulation ) ChangeDirection ( dir Direction ) error {
if dir != Forward {
return ErrNotRecorded
}
return nil
}
// GetDirection will always return Forward.
func ( * dummyRecordingManipulation ) GetDirection ( ) Direction { return Forward }
// When will always return an empty string and nil, not supported on native proc backend.
func ( * dummyRecordingManipulation ) When ( ) ( string , error ) { return "" , nil }
// Checkpoint will always return an error on the native proc backend,
// only supported for recorded traces.
func ( * dummyRecordingManipulation ) Checkpoint ( string ) ( int , error ) { return - 1 , ErrNotRecorded }
// Checkpoints will always return an error on the native proc backend,
// only supported for recorded traces.
func ( * dummyRecordingManipulation ) Checkpoints ( ) ( [ ] Checkpoint , error ) { return nil , ErrNotRecorded }
// ClearCheckpoint will always return an error on the native proc backend,
// only supported in recorded traces.
func ( * dummyRecordingManipulation ) ClearCheckpoint ( int ) error { return ErrNotRecorded }
// Restart will always return an error in the native proc backend, only for
// recorded traces.
2022-03-21 19:42:37 +00:00
func ( * dummyRecordingManipulation ) Restart ( * ContinueOnceContext , string ) ( Thread , error ) {
return nil , ErrNotRecorded
}
2023-08-09 17:30:22 +00:00
var ErrWaitForNotImplemented = errors . New ( "waitfor not implemented" )
func ( waitFor * WaitFor ) Valid ( ) bool {
return waitFor != nil && waitFor . Name != ""
}