Package frysk.proc.live

This is a frysk implementation package that provides the implementation of frysk.proc for ptrace based (GNU/Linux) systems.

See:
          Description

Interface Summary
Isa Instruction Set Architecture.
 

Class Summary
AddressSpaceByteBuffer  
Breakpoint Internal proc class that represents a Breakpoint at a certain address in a Proc.
BreakpointAddresses Keeps track of address breakpoints for a Proc (all Tasks of a Proc share the same breakpoints).
BreakpointAddresses.CodeObserver Small package private class holding Task and TaskObserver.Code interested in a particular breakpoint.
IA32InstructionParser  
IA32InstructionParser.Jump  
Instruction An architecture independent way of representing an assembly level instruction which is used for stepping and breakpoint insertion.
IsaFactory  
IsaPowerPC  
LinuxIA32  
LinuxPPC32  
LinuxPPC64  
LinuxPtraceHost A Linux Host tracked using PTRACE.
LinuxPtraceProc A Linux Proc tracked using PTRACE.
LinuxPtraceProc.InstructionAction Class describing the action to take on the suspended Task before adding or deleting an Instruction observer.
LinuxPtraceProcState A UNIX Process State
LinuxPtraceProcState.Attaching A process is being attached, this is broken down into sub-states.
LinuxPtraceProcState.Attaching.ToMainTask In the process of attaching, the main task has been sent an attach request.
LinuxPtraceProcState.Attaching.ToOtherTasks In the process of attaching, the main task is attached, now waiting for the remaining tasks to indicate they, too, have attached (or at least processed the attach request).
LinuxPtraceProcState.Detaching In the process of detaching; waiting for all tasks to report back that they have successfully detached.
LinuxPtraceTask A Linux Task tracked using PTRACE.
LinuxPtraceTaskState A Linux Task's State tracked using PTRACE.
LinuxPtraceTaskState.Attached The task is attached, and waiting to be either continued, or unblocked.
LinuxPtraceTaskState.Attached.WaitForContinueOrUnblock The blocked task has stopped, possibly with a pending signal, waiting on either a continue or an unblock.
LinuxPtraceTaskState.Attached.WaitForUnblock Got continue, just need to clear the blocks.
LinuxPtraceTaskState.Attaching The task is in the process of being attached.
LinuxPtraceTaskState.BlockedSignal The LinuxPtraceTask is blocked by a set of observers, remain in this state until all the observers have unblocked themselves.
LinuxPtraceTaskState.Running Keep the task running.
LinuxPtraceTaskState.StartClonedTask A cloned task just starting out, wait for it to stop, and for it to be unblocked.
LinuxPtraceTaskState.StartMainTask A new main Task, created via fork, just starting.
LinuxPtraceTaskState.Stepping  
LinuxPtraceTaskState.SyscallBlockedInSyscall A task blocked after SyscallEnter notification or while runningInSyscall by an event other than syscalledEvent
LinuxWaitBuilder Handles wait events generated by the wait builder.
LinuxX8664  
LiveHost A live Host/Proc/Task is characterised by its stateful nature; i.e., an ability to respond to stateful requests such as add/remove observers.
LiveProc A live Host/Proc/Task is characterised by its stateful nature; i.e., an ability to respond to stateful requests such as add/remove observers.
LiveTask A live Host/Proc/Task is characterised by its stateful nature; i.e., an ability to respond to stateful requests such as add/remove observers.
LogicalMemoryBuffer MemorySpaceByteBuffer that filters out anything the frysk core might have done to the underlying memory.
PtraceRegisterBanksFactory The target has registers scattered across one or more register banks.
RegisterSetByteBuffer  
State  
TaskObservable Observable element of Task.
TaskObservation The binding between an Observer and its Observable.
TestByteBuffer  
TestProcStopped  
TestRefresh Check the host's refresh mechanism.
TestRegs Check all register values.
TestRuntimeIsa  
TestRuntimeIsa.AttachedObserver  
TestTaskObserverBlocked Check the behavior of an observer that blocks a Task's progress.
TestTaskObserverCode  
TestTaskObserverCode.AttachedObserver  
TestTaskObserverCode.CodeObserver  
TestTaskObserverCode.CountingCodeObserver  
TestTaskObserverCode.RemovingCodeObserver  
TestTaskObserverCode.SignaledObserver  
TestTaskObserverCode.Symbol  
Watchpoint Internal proc class that represents a Breakpoint at a certain address in a Proc.
WatchpointAddresses Keeps track of address watchppoints for a Proc (all Tasks of a Proc share the same breakpoints).
X8664InstructionParser  
 

Package frysk.proc.live Description

This is a frysk implementation package that provides the implementation of frysk.proc for ptrace based (GNU/Linux) systems. Normally none of these implementation details are needed for normal use of frysk-core and the public classes of frysk.proc should be used.

Implementation notes on Instruction and Code observers

Instruction observers are what implement instruction stepping, Code observers are what implement low level instruction breakpoints.

Lets assume that we installed an InstructionObserver, the Task stopped notified the observer which decided that the Task needed to be blocked which turned the TaskState into BlockedSignal. If you are interested in what would happen while stepping and the Task not being blocked already, start reading from (*) and assume we are in the Running state and just want to continue running the task after some event happened.

So we start at Task.unblock(InstructionObserver) which will notify the BlockedSignal state class in LinuxTaskState. The handleUnblock() method will be triggered. Depending on which observers are actually installed (and whether all blocking observer have been cleared now) a Running TaskState is determined and sendContinue(task, sig) is called on that Running state (the Running state class is also defined inside the LinuxTaskState).

sendContinue() is an instance method of the Running TaskState class which will return the appropriate Running subclass/instance depending on how the Task continues. If an Instruction observer (or the task is currently at a breakpoint, see below) then LinuxTask.sendStepInstruction code is called and sendContinue will return Stepping (which is a subclass of Running) as the new state of the Task.

sendStepInstruction() will do some bookkeeping to remember which signal number was requested and whether or not the Task is currently at a signal return instruction. We need to do that here so we know what we were doing when we get a callback from the kernel after the step. Then the appropriate ptrace command is invoked so the Task is doing the actual step. Some, but not all (signal number and sigret in particular) this is contained as state in the Stepping task state instance.

If the Task made its step then we will get a TrappedEvent. Here things get a little messy since ptrace/wait uses trap events for signaling almost everything. Luckily the Isa should be able to tell us if the current Task just did an instruction step. If it did (and it isn't a breakpoint address in which case we need to do some extra things) then we inform all Instruction observers and if any of them tells us to Block then we move into the BlockedSignal state, otherwise we keep in the Stepping state and continue from (*).

When a Code observer is installed then the ptrace task states will all call setupSteppingBreakpoint() after a breakpoint hit has been detected, which makes sure the PC is setup correctly (which can be off by one on some architectures after a breakpoint) and then mark the Task as being at that particular breakpoint (this is still kept as Task field for now). We need to always do the adjustment immediately in case the user decides to move to a Blocked state instead of of continuing the Task. When Running.sendContinue is later called it will depend on this fact.

All the logic of how to breakpoint step is contained in the Breakpoint class that checks the properties of the Instruction class object which is created by the Isa through an instruction parser before the breakpoint is inserted at a given location. An Instruction knows how long the instruction is, which bytes it represents, whether it can be single stepped out of line and how to set that up given the original pc location and an alternative address, plus any fixups that are needed to the Task afterwards (and it has a notion of whether or not the Instruction can be simulated, but that isn't currently used, see below). A Breakpoint ties an Instruction to a particular address and Proc (and Tasks can have zero or more Breakpoints, they share the same Breakpoint on the same address with other Tasks of a Proc and when no Tasks of a Proc has an Breakpoint at a particular address anymore the Breakpoint is removed).

For stepping the Breakpoint the Running.sendContinue() method first calls Breakpoint.prepareStep(), then signals ptrace to do a single step of the Task, putting the Task in Stepping state and then in handleTrapEvent() calls Breakpoint.stepDone(). prepareStep() queries the capabilities of the Instruction at the pc address and depending on that either sets things up for doing a step out of line, simulate the instruction (but none of the current Instructions have been setup to do simulation yet, and look at the comment in prepareStep() to see what is needed to fully enable this option) or reset the current Instruction (removing the breakpoint instruction temporarily). Accordingly a Breakpoint can be in the state NOT_STEPPING, OUT_OF_LINE_STEPPING, SIMULATE_STEPPING or RESET_STEPPING.

In the case of RESET_STEPPING other Tasks might miss and just past the Breakpoint during the brief period between the reset, step and reinstall. Breakpoint prepareStep() just takes the Instruction bytes and puts them at the current pc address, and doneStep() reinstates the breakpoint instruction. The right solution here would be to stop all other Tasks first, step and then continue them all.

When the Instruction supports single step out of line then the Breakpoint requests an address in the single step out of line area of the Proc, instructs the Instruction to install itself there for the current Task calling Instruction.setupExecuteOutOfLine(). The default action of setupExecuteOutOfLine() is to set the pc to the given address and place a copy the instruction bytes there (although this can be overridden if an Instruction wants to do something more fancy). When the task signals the Breakpoint that a step was taken by calling ,code>stepDone(), the Breakpoint calls Instruction.fixupExecuteOutOfLine() with the original pc and replacement address so any adjustments can be done to the Task registers. The default action is to just set the pc to the original pc plus the length of the Instruction just stepped. But Instructions can override that if more is needed. As an example the RET instruction doesn't do any fixup (since the only action is setting the pc to the right location in the first place) and the JMP instruction sets the pc to original pc plus/minus the difference of the alternate address and the pc after the single step. Afterward the Breakpoint returns the used address to the Proc so it can be used by other Tasks needing to do a single step out of line.

The Proc maintains a single step out of line area pool of addresses that point to locations that are at least as big is the largest instruction of the instruction set. The Proc gets this list from the Isa the first time an address is requested through getOutOfLineAddress(). Currently this is just the address of the main function entry point (see below). The address is taken out of the pool and the Breakpoint is responsible for putting it back through doneOutOfLine (see above). If no address is currently available the call blocks till one is available (this was way easier than inventing yet another TaskState and getting the communication between Proc and Task about this right, and contention is very low and at the longest it takes for an address to become available is one instruction single step).

Deficiencies of the Instruction Parser. The framework is in place and works for the few Instructions that are known to the instruction parser, but there are all hand coded (see IA32InstructionParser which just handles NOP, INT3, RETQ and one JMP variant, the X8664Instruction just delegates to the IA32 for now). There don't seem to be libraries available to easily plugin that would give us the fixup instructions needed. The best available is the kprobes examples from the linux kernel which have as drawback that they are coded to be intimately tied to the kernel/C way of doing things and only seem handles instructions found in kernel space (no robust instruction parsing, just a instruction bits/lookup table).appreciated. So we need to sit down with the various instruction manuals and just code it up by hand if we cannot find an existing library. (Bonus points for finding something that would not just give us ssol fixups but also simulation of instructions when hooked to the registers and memory of a Task.) This is Frysk bug #4762. See there for updates. This means that in most cases we currently fall back to reset instruction stepping which has its own deficiency of not stopping other Tasks which means breakpoints can and will be missed.