mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-30 21:43:38 +02:00
2577 lines
108 KiB
ObjectPascal
2577 lines
108 KiB
ObjectPascal
{ $Id: fpdbgwinclasses.pp 43410 2013-11-09 20:34:31Z martin $ }
|
|
{
|
|
---------------------------------------------------------------------------
|
|
fpdbgwinclasses.pp - Native freepascal debugger
|
|
---------------------------------------------------------------------------
|
|
|
|
This unit contains debugger classes for a native freepascal debugger
|
|
|
|
---------------------------------------------------------------------------
|
|
|
|
@created(Sun Feb 9th WET 2014)
|
|
@lastmod($Date: 2013-11-09 21:34:31 +0100 (za, 09 nov 2013) $)
|
|
@author(Joost van der Sluis <joost@@cnoc.nl>)
|
|
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
|
|
(* About Windows debug events and breakpoints
|
|
|
|
In a multi-threaded app, several threads can all reach breakpoints (the same
|
|
or different breakpoints) at the same time.
|
|
Windows will report each such breakpoint in an event on its own.
|
|
|
|
When the first Breakpoint event is received, it is not possible to tell which
|
|
other threads have also hit breakpoints.
|
|
- A thread that has hit a breakpoint will have its Instruction-Pointer exactly
|
|
one after the int3 break instruction.
|
|
- But a thread could also be in that location as a result of a jump. (If the
|
|
int3 replaced another 1 byte instruction)
|
|
As a consequence: While all threads are stopped due to the first thread having
|
|
hit a breakpoint, the Instruction pointer for the other threads may be
|
|
wrong/unusable. It may need correction by -1, if that other thread also already
|
|
hit a breakpoint. [1]
|
|
|
|
If the debugger resumes after a breakpoint, it must temporarily remove the
|
|
breakpoint, so the original instruction can be executed. (There is an option
|
|
to do "out of place execution", but that is not implemented, and may not always
|
|
be available)
|
|
In order to execute the original instruction (while the int3 is removed):
|
|
- The thread must do a single-step. This ensures it can not loop back and
|
|
execute the instruction again, when it should hit the breakpoint again (after
|
|
looping back)
|
|
- Other threads must be suspended, so they can not run to/through the location
|
|
of the breakpoint. Otherwise they would miss the breakpoint, as the int3 is
|
|
removed,
|
|
Other threads may/should execute, if they previously started a single step.
|
|
|
|
The debugger may also skip a breakpoint (for the current thread) that is next
|
|
to be hit, even if it had no event yet.
|
|
The controller should have seen that the thread was at the breakpoint location,
|
|
and should have triggered the actions for the breakpoint.
|
|
|
|
If several events (such a breakpoints) have been raised at the same time (e.g.
|
|
several breakpoints hit), then those events will be reported.
|
|
=> They will be reported, even if their thread got suspended in the meantime.
|
|
(Since the event had already happened, no code execution happens in such a
|
|
suspended thread.)
|
|
However that means, if the debugger want thread A to do a single step over a
|
|
(temp removed) breakpoint, then the next event for the debugger could be an
|
|
already pending signal (other breakpoint or other event).
|
|
In that case, the single step, may not yet have been executed, and will only
|
|
happen if the debugger calls ContinueDebugEvent for the current event.
|
|
But the debugger is not allowed to run the current thread, because the int3
|
|
for thread A is still temporary removed.
|
|
The debugger can run the thread, if it single steps it. Otherwise it can
|
|
suspend it before calling ContinueDebugEvent (TODO if that does not work, it
|
|
must revert to single step).
|
|
|
|
The pending single step thread will remember its single step flag. So it just
|
|
needs to be kept un-suspended for the next ContinueDebugEvent.
|
|
|
|
[1] TODO (may or may not work):
|
|
It may be possible to get the other events using Win10 DBG_REPLY_LATER
|
|
(or setting the IP back to the breakpoint, and hit it again).
|
|
Then while *all* threads are suspended, events can be collected.
|
|
If no more events are coming in, the original thread can be resumed, triggering
|
|
its breakpoint event again.
|
|
All the event, would need to be collected, and each would need to be answered
|
|
with a ContinueDebugEvent to windows.
|
|
And only when all events are known AND the debugger has not yet called
|
|
ContinueDebugEvent for the last event (so the target app is paused), then they
|
|
would be reported (one by one) to the user.
|
|
|
|
*)
|
|
unit FpDbgWinClasses;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$IFDEF INLINE_OFF}{$INLINE OFF}{$ENDIF}
|
|
{off $DEFINE DebuglnWinDebugEvents}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
SysUtils,
|
|
Windows,
|
|
{$IF FPC_Fullversion>30202}
|
|
{$ifNdef cpui386} ufloatx80, sfpux80, {$endif}
|
|
{$ENDIF}
|
|
Math,
|
|
LazLinkedList,
|
|
FpDbgUtil,
|
|
FpDbgClasses,
|
|
DbgIntfProcess,
|
|
FpDbgWinExtra,
|
|
strutils,
|
|
FpDbgInfo,
|
|
FpDbgLoader, FpDbgDisasX86,
|
|
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
|
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, UTF8Process,
|
|
FpDbgCommon, FpdMemoryTools, FpErrorMessages, FpDbgCpuX86;
|
|
|
|
type
|
|
|
|
TFpWinCtxFlags = (cfSkip, cfControl, cfFull);
|
|
TFpContextChangeFlag = (ccfControl, ccfInteger);
|
|
TFpContextChangeFlags = set of TFpContextChangeFlag;
|
|
PPWSTR = ^PWSTR;
|
|
|
|
{ TDbgWinThread }
|
|
|
|
TDbgWinThread = class(TDbgx86Thread)
|
|
private type
|
|
TBreakPointState = (bsNone, bsInSingleStep);
|
|
private
|
|
FHasExceptionCleared: boolean;
|
|
FIsSuspended: Boolean;
|
|
FBreakPointState: TBreakPointState;
|
|
FDoNotPollName: Boolean;
|
|
FIgnoreNextInt3: Boolean;
|
|
FName: String;
|
|
FUnwinder: TDbgStackUnwinderX86MultiMethod;
|
|
FFailed_CONTEXT_EXTENDED_REGISTERS: boolean;
|
|
protected
|
|
FThreadContextChanged: boolean;
|
|
FThreadContextChangeFlags: TFpContextChangeFlags;
|
|
FCurrentContext: PFpContext; // FCurrentContext := Pointer((PtrUInt(@_UnAligendContext) + 15) and not PtrUInt($F));
|
|
_UnAligendContext: TFpContext;
|
|
_StoredContext: TFpContext;
|
|
procedure LoadRegisterValues; override;
|
|
function GetFpThreadContext(var AStorage: TFpContext; out ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags): Boolean;
|
|
function SetFpThreadContext(ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags = cfSkip): Boolean;
|
|
function GetName: String; override;
|
|
function GetStackUnwinder: TDbgStackUnwinder; override;
|
|
public
|
|
destructor Destroy; override;
|
|
procedure Suspend;
|
|
procedure SuspendForStepOverBreakPoint;
|
|
procedure Resume;
|
|
procedure EndSingleStepOverBreakPoint;
|
|
procedure SetSingleStep;
|
|
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
|
function DetectHardwareWatchpoint: Pointer; override;
|
|
procedure BeforeContinue; override;
|
|
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
|
function ReadThreadState: boolean;
|
|
procedure ClearExceptionSignal; override;
|
|
property HasExceptionCleared: boolean read FHasExceptionCleared;
|
|
|
|
procedure SetRegisterValue(AName: string; AValue: QWord); override;
|
|
procedure StoreRegisters; override;
|
|
procedure RestoreRegisters; override;
|
|
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
|
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
|
procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
|
|
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
|
function GetStackPointerRegisterValue: TDbgPtr; override;
|
|
property Process;
|
|
end;
|
|
|
|
TDbgWinThreadNameInternal = class(TLinkListItem)
|
|
Fid: TThreadID;
|
|
Fthreadname: shortstring;
|
|
procedure SetInfo(id:TThreadID;const threadname:string);
|
|
end;
|
|
|
|
TDbgWinThreadNameList = class(TLinkList)
|
|
private
|
|
const
|
|
FMaxCounter = 10000;
|
|
var
|
|
FNumCounter: integer;
|
|
protected
|
|
function CreateItem: TLinkListItem; override;
|
|
public
|
|
procedure ClearThread;
|
|
function FindById(id:TThreadID):TDbgWinThreadNameInternal;
|
|
function FetchThread(id:TThreadID):string;
|
|
procedure AddThread(id:TThreadID;const threadname:string);
|
|
end;
|
|
|
|
{ TDbgWinProcess }
|
|
|
|
TDbgWinProcess = class(TDbgx86Process)
|
|
private
|
|
FInfo: TCreateProcessDebugInfo;
|
|
FProcProcess: TProcessWithRedirect;
|
|
FJustStarted, FTerminated: boolean;
|
|
FDbgUiRemoteBreakin: TDBGPtr;
|
|
FBitness: TBitness;
|
|
FThreadNameList: TDbgWinThreadNameList;
|
|
function GetFullProcessImageName(AProcessHandle: THandle): string;
|
|
function GetModuleFileName(AModuleHandle: THandle): string;
|
|
function GetProcFilename(AProcess: TDbgProcess; lpImageName: LPVOID; fUnicode: word; hFile: handle): string;
|
|
procedure LogLastError(AMsg: String = '');
|
|
protected
|
|
procedure AfterChangingInstructionCode(const ALocation: TDBGPtr; ACount: Integer); override;
|
|
function GetHandle: THandle; override;
|
|
function GetLastEventProcessIdentifier: THandle; override;
|
|
procedure InitializeLoaders; override;
|
|
function CreateWatchPointData: TFpWatchPointData; override;
|
|
public
|
|
constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses;
|
|
AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig = nil); override;
|
|
destructor Destroy; override;
|
|
|
|
function ReadData(const AAdress: TDbgPtr; const ASize: Cardinal; out AData): Boolean; override;
|
|
function WriteData(const AAdress: TDbgPtr; const ASize: Cardinal; const AData): Boolean; override;
|
|
function ReadString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: String): Boolean; override;
|
|
function ReadWString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: WideString): Boolean; override;
|
|
function CallParamDefaultLocation(AParamIdx: Integer): TFpDbgMemLocation; override;
|
|
|
|
procedure Interrupt; // required by app/fpd
|
|
function HandleDebugEvent(const ADebugEvent: TDebugEvent): Boolean;
|
|
|
|
function StartInstance(AParams, AnEnvironment: TStrings; AWorkingDirectory, AConsoleTty: string;
|
|
AFlags: TStartInstanceFlags; out AnError: TFpError): boolean; override;
|
|
function AttachToInstance(APid: Integer; out AnError: TFpError): boolean; override;
|
|
|
|
class function isSupported(ATargetInfo: TTargetDescriptor): boolean; override;
|
|
|
|
function CanContinueForWatchEval(ACurrentThread: TDbgThread): boolean; override;
|
|
function Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean; override;
|
|
function Detach(AProcess: TDbgProcess; AThread: TDbgThread): boolean; override;
|
|
function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override;
|
|
function AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; override;
|
|
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
|
|
|
|
procedure StartProcess(const AThreadID: DWORD; const AInfo: TCreateProcessDebugInfo);
|
|
|
|
function Pause: boolean; override;
|
|
|
|
procedure TerminateProcess; override;
|
|
|
|
function AddLib(const AInfo: TLoadDLLDebugInfo): TDbgLibrary;
|
|
procedure RemoveLib(const AInfo: TUnloadDLLDebugInfo);
|
|
end;
|
|
TDbgWinProcessClass = class of TDbgWinProcess;
|
|
|
|
{ tDbgWinLibrary }
|
|
|
|
tDbgWinLibrary = class(TDbgLibrary)
|
|
private
|
|
FInfo: TLoadDLLDebugInfo;
|
|
protected
|
|
procedure InitializeLoaders; override;
|
|
public
|
|
constructor Create(const AProcess: TDbgProcess; const ADefaultName: String;
|
|
const AModuleHandle: THandle; AInfo: TLoadDLLDebugInfo);
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
var
|
|
DBG_VERBOSE, DBG_WARNINGS, FPDBG_WINDOWS: PLazLoggerLogGroup;
|
|
|
|
{$ifdef cpux86_64}
|
|
const
|
|
FLAG_TRACE_BIT = $100;
|
|
{$endif}
|
|
|
|
function dbgs(ABrkPointState: TDbgWinThread.TBreakPointState): String;
|
|
begin
|
|
WriteStr(Result, ABrkPointState);
|
|
end;
|
|
|
|
function dbgs(AnDbgEvent: DEBUG_EVENT): String; overload;
|
|
begin
|
|
case AnDbgEvent.dwDebugEventCode of
|
|
CREATE_PROCESS_DEBUG_EVENT: result := '>> CREATE_PROCESS_DEBUG_EVENT'
|
|
+ ' htproc:' + IntToStr(AnDbgEvent.CreateProcessInfo.hProcess);
|
|
CREATE_THREAD_DEBUG_EVENT: result := '>> CREATE_THREAD_DEBUG_EVENT'
|
|
+ ' hthread:' + IntToStr(AnDbgEvent.CreateThread.hThread)
|
|
+ ' start:' + dbghex(PtrUInt(AnDbgEvent.CreateThread.lpStartAddress));
|
|
EXCEPTION_DEBUG_EVENT: begin
|
|
result := 'EXCEPTION_DEBUG_EVENT'
|
|
+ ' Code:' + dbghex(AnDbgEvent.Exception.ExceptionRecord.ExceptionCode)
|
|
+ ' Flags:' + dbghex(AnDbgEvent.Exception.ExceptionRecord.ExceptionFlags)
|
|
+ ' NumParam:' + IntToStr(AnDbgEvent.Exception.ExceptionRecord.NumberParameters);
|
|
case AnDbgEvent.Exception.ExceptionRecord.ExceptionCode of
|
|
EXCEPTION_ACCESS_VIOLATION: Result := Result + ' EXCEPTION_ACCESS_VIOLATION';
|
|
EXCEPTION_BREAKPOINT: Result := Result + ' EXCEPTION_BREAKPOINT';
|
|
STATUS_WX86_BREAKPOINT: Result := Result + ' STATUS_WX86_BREAKPOINT';
|
|
EXCEPTION_DATATYPE_MISALIGNMENT: Result := Result + ' EXCEPTION_DATATYPE_MISALIGNMENT';
|
|
EXCEPTION_SINGLE_STEP: Result := Result + ' EXCEPTION_SINGLE_STEP';
|
|
STATUS_WX86_SINGLE_STEP: Result := Result + ' STATUS_WX86_SINGLE_STEP';
|
|
EXCEPTION_ARRAY_BOUNDS_EXCEEDED: Result := Result + ' EXCEPTION_ARRAY_BOUNDS_EXCEEDED';
|
|
EXCEPTION_FLT_DENORMAL_OPERAND: Result := Result + ' EXCEPTION_FLT_DENORMAL_OPERAND';
|
|
EXCEPTION_FLT_DIVIDE_BY_ZERO: Result := Result + ' EXCEPTION_FLT_DIVIDE_BY_ZERO';
|
|
EXCEPTION_FLT_INEXACT_RESULT: Result := Result + ' EXCEPTION_FLT_INEXACT_RESULT';
|
|
EXCEPTION_FLT_INVALID_OPERATION: Result := Result + ' EXCEPTION_FLT_INVALID_OPERATION';
|
|
EXCEPTION_FLT_OVERFLOW: Result := Result + ' EXCEPTION_FLT_OVERFLOW';
|
|
EXCEPTION_FLT_STACK_CHECK: Result := Result + ' EXCEPTION_FLT_STACK_CHECK';
|
|
EXCEPTION_FLT_UNDERFLOW: Result := Result + ' EXCEPTION_FLT_UNDERFLOW';
|
|
EXCEPTION_INT_DIVIDE_BY_ZERO: Result := Result + ' EXCEPTION_INT_DIVIDE_BY_ZERO';
|
|
EXCEPTION_INT_OVERFLOW: Result := Result + ' EXCEPTION_INT_OVERFLOW';
|
|
EXCEPTION_INVALID_HANDLE: Result := Result + ' EXCEPTION_INVALID_HANDLE';
|
|
EXCEPTION_PRIV_INSTRUCTION: Result := Result + ' EXCEPTION_PRIV_INSTRUCTION';
|
|
EXCEPTION_NONCONTINUABLE_EXCEPTION: Result := Result + ' EXCEPTION_NONCONTINUABLE_EXCEPTION';
|
|
EXCEPTION_NONCONTINUABLE: Result := Result + ' EXCEPTION_NONCONTINUABLE';
|
|
EXCEPTION_STACK_OVERFLOW: Result := Result + ' EXCEPTION_STACK_OVERFLOW';
|
|
EXCEPTION_INVALID_DISPOSITION: Result := Result + ' EXCEPTION_INVALID_DISPOSITION';
|
|
EXCEPTION_IN_PAGE_ERROR: Result := Result + ' EXCEPTION_IN_PAGE_ERROR';
|
|
EXCEPTION_ILLEGAL_INSTRUCTION: Result := Result + ' EXCEPTION_ILLEGAL_INSTRUCTION';
|
|
EXCEPTION_POSSIBLE_DEADLOCK: Result := Result + ' EXCEPTION_POSSIBLE_DEADLOCK';
|
|
end;
|
|
end;
|
|
EXIT_PROCESS_DEBUG_EVENT: result := '<< EXIT_PROCESS_DEBUG_EVENT'
|
|
+ ' exitcode:' + IntToStr(AnDbgEvent.ExitProcess.dwExitCode);
|
|
EXIT_THREAD_DEBUG_EVENT: result := '<< EXIT_THREAD_DEBUG_EVENT'
|
|
+ ' exitcode:' + IntToStr(AnDbgEvent.ExitThread.dwExitCode);
|
|
LOAD_DLL_DEBUG_EVENT: result := '> LOAD_DLL_DEBUG_EVENT';
|
|
OUTPUT_DEBUG_STRING_EVENT: result := 'OUTPUT_DEBUG_STRING_EVENT';
|
|
UNLOAD_DLL_DEBUG_EVENT: result := '< UNLOAD_DLL_DEBUG_EVENT';
|
|
RIP_EVENT: result := 'RIP_EVENT'
|
|
+ ' type:' + IntToStr(AnDbgEvent.RipInfo.dwType)
|
|
+ ' err:' + IntToStr(AnDbgEvent.RipInfo.dwError);
|
|
else result := 'Code='+inttostr(AnDbgEvent.dwDebugEventCode);
|
|
end;
|
|
Result := format('EVENT for Process %d Thread %d: %s', [AnDbgEvent.dwProcessId, AnDbgEvent.dwThreadId, Result]);
|
|
end;
|
|
|
|
|
|
const
|
|
{$ifdef cpux86_64}
|
|
CONTEXT_XSTATE = $00100040; // 64bit // Early Win-7-SP1 needs $00100020
|
|
{$else}
|
|
CONTEXT_XSTATE = $00010040; // 32 bit
|
|
{$endif}
|
|
|
|
XSTATE_LEGACY_FLOATING_POINT = 0;
|
|
XSTATE_LEGACY_SSE = 1;
|
|
XSTATE_GSSE = 2;
|
|
XSTATE_AVX = XSTATE_GSSE;
|
|
XSTATE_MPX_BNDREGS = 3;
|
|
XSTATE_MPX_BNDCSR = 4;
|
|
XSTATE_AVX512_KMASK = 5;
|
|
XSTATE_AVX512_ZMM_H = 6;
|
|
XSTATE_AVX512_ZMM = 7;
|
|
XSTATE_IPT = 8;
|
|
XSTATE_CET_U = 11;
|
|
XSTATE_LWP = 62;
|
|
MAXIMUM_XSTATE_FEATURES = 64;
|
|
|
|
XSTATE_MASK_LEGACY_FLOATING_POINT = DWORD64(1 << XSTATE_LEGACY_FLOATING_POINT);
|
|
XSTATE_MASK_LEGACY_SSE = DWORD64(1 << XSTATE_LEGACY_SSE);
|
|
XSTATE_MASK_LEGACY = (XSTATE_MASK_LEGACY_FLOATING_POINT or XSTATE_MASK_LEGACY_SSE);
|
|
XSTATE_MASK_GSSE = DWORD64(1 << XSTATE_GSSE);
|
|
XSTATE_MASK_AVX = XSTATE_MASK_GSSE;
|
|
type
|
|
PPCONTEXT = ^PCONTEXT;
|
|
|
|
var
|
|
DebugBreakAddr: Pointer = nil;
|
|
_CreateRemoteThread: function(hProcess: THandle; lpThreadAttributes: Pointer; dwStackSize: DWORD; lpStartAddress: TFNThreadStartRoutine; lpParameter: Pointer; dwCreationFlags: DWORD; var lpThreadId: DWORD): THandle; stdcall = nil;
|
|
_GetFinalPathNameByHandle: function(hFile: HANDLE; lpFilename:LPWSTR; cchFilePath, dwFlags: DWORD):DWORD; stdcall = nil;
|
|
_QueryFullProcessImageName: function (hProcess:HANDLE; dwFlags: DWord; lpExeName:LPWSTR; var lpdwSize:DWORD):BOOL; stdcall = nil;
|
|
_DebugActiveProcessStop: function (ProcessId:DWORD):BOOL; stdcall = nil;
|
|
_DebugActiveProcess: function (ProcessId:DWORD):BOOL; stdcall = nil;
|
|
_IsWow64Process: function (hProcess:HANDLE; WoW64Process: PBOOL):BOOL; stdcall = nil;
|
|
_Wow64GetThreadContext: function (hThread: THandle; var lpContext: WOW64_CONTEXT): BOOL; stdcall = nil;
|
|
_Wow64SetThreadContext: function (hThread: THandle; const lpContext: WOW64_CONTEXT): BOOL; stdcall = nil;
|
|
_Wow64SuspendThread: function (hThread:HANDLE):DWORD; stdcall = nil;
|
|
_DebugBreakProcess: function(Process:HANDLE): WINBOOL; stdcall = nil;
|
|
_GetThreadDescription: function(hThread: THandle; ppszThreadDescription: PPWSTR): HResult; stdcall = nil;
|
|
_WaitForDebugEventEx: function(var lpDebugEvent: TDebugEvent; dwMilliseconds: DWORD): BOOL; stdcall = nil;
|
|
// XState
|
|
_GetEnabledXStateFeatures: function(): DWORD64; stdcall = nil;
|
|
_InitializeContext: function(Buffer: Pointer; ContextFlags: DWORD; Context: PPCONTEXT; ContextLength: PDWORD): BOOL; stdcall = nil;
|
|
_GetXStateFeaturesMask: function(Context: PCONTEXT; FeatureMask: PDWORD64): BOOL; stdcall = nil;
|
|
_LocateXStateFeature: function(Context: PCONTEXT; FeatureId: DWORD; Length: PDWORD): PM128A; stdcall = nil;
|
|
_SetXStateFeaturesMask: function(Context: PCONTEXT; FeatureMask: DWORD64): BOOL; stdcall = nil;
|
|
_xstate_FeatureMask: DWORD64;
|
|
|
|
procedure LoadKernelEntryPoints;
|
|
var
|
|
hMod: THandle;
|
|
begin
|
|
hMod := GetModuleHandle(kernel32);
|
|
DebugLn(DBG_WARNINGS and (hMod = 0), ['ERROR: Failed to get kernel32 handle']);
|
|
if hMod = 0 then
|
|
exit; //????
|
|
|
|
DebugBreakAddr := GetProcAddress(hMod, 'DebugBreak');
|
|
Pointer(_CreateRemoteThread) := GetProcAddress(hMod, 'CreateRemoteThread');
|
|
Pointer(_QueryFullProcessImageName) := GetProcAddress(hMod, 'QueryFullProcessImageNameW'); // requires Vista
|
|
Pointer(_DebugActiveProcessStop) := GetProcAddress(hMod, 'DebugActiveProcessStop');
|
|
Pointer(_DebugActiveProcess) := GetProcAddress(hMod, 'DebugActiveProcess');
|
|
Pointer(_GetFinalPathNameByHandle) := GetProcAddress(hMod, 'GetFinalPathNameByHandleW');
|
|
Pointer(_DebugBreakProcess) := GetProcAddress(hMod, 'DebugBreakProcess');
|
|
Pointer(_GetThreadDescription) := GetProcAddress(hMod, 'GetThreadDescription');
|
|
{$ifdef cpux86_64}
|
|
Pointer(_IsWow64Process) := GetProcAddress(hMod, 'IsWow64Process');
|
|
Pointer(_Wow64GetThreadContext) := GetProcAddress(hMod, 'Wow64GetThreadContext');
|
|
Pointer(_Wow64SetThreadContext) := GetProcAddress(hMod, 'Wow64SetThreadContext');
|
|
Pointer(_Wow64SuspendThread) := GetProcAddress(hMod, 'Wow64SuspendThread');
|
|
{$endif}
|
|
Pointer(_WaitForDebugEventEx) := GetProcAddress(hMod, 'WaitForDebugEventEx');
|
|
// xstate
|
|
Pointer(_GetEnabledXStateFeatures) := GetProcAddress(hMod, 'GetEnabledXStateFeatures');
|
|
Pointer(_InitializeContext) := GetProcAddress(hMod, 'InitializeContext');
|
|
Pointer(_GetXStateFeaturesMask) := GetProcAddress(hMod, 'GetXStateFeaturesMask');
|
|
Pointer(_LocateXStateFeature) := GetProcAddress(hMod, 'LocateXStateFeature');
|
|
Pointer(_SetXStateFeaturesMask) := GetProcAddress(hMod, 'SetXStateFeaturesMask');
|
|
if (_GetEnabledXStateFeatures=nil) or (_InitializeContext=nil) or (_GetXStateFeaturesMask=nil) or
|
|
(_LocateXStateFeature=nil) or (_SetXStateFeaturesMask=nil)
|
|
then begin
|
|
_GetEnabledXStateFeatures := nil;
|
|
end
|
|
else begin
|
|
_xstate_FeatureMask := _GetEnabledXStateFeatures();
|
|
if (_xstate_FeatureMask and XSTATE_MASK_GSSE) = 0 then
|
|
_GetEnabledXStateFeatures := nil;
|
|
end;
|
|
|
|
DebugLn(DBG_WARNINGS and (DebugBreakAddr = nil), ['WARNING: Failed to get DebugBreakAddr']);
|
|
DebugLn(DBG_WARNINGS and (_CreateRemoteThread = nil), ['WARNING: Failed to get CreateRemoteThread']);
|
|
DebugLn(DBG_WARNINGS and (_QueryFullProcessImageName = nil), ['WARNING: Failed to get QueryFullProcessImageName']);
|
|
DebugLn(DBG_WARNINGS and (_DebugActiveProcessStop = nil), ['WARNING: Failed to get DebugActiveProcessStop']);
|
|
DebugLn(DBG_WARNINGS and (_DebugActiveProcess = nil), ['WARNING: Failed to get DebugActiveProcess']);
|
|
DebugLn(DBG_WARNINGS and (_GetFinalPathNameByHandle = nil), ['WARNING: Failed to get GetFinalPathNameByHandle']);
|
|
DebugLn(DBG_WARNINGS and (_DebugBreakProcess = nil), ['WARNING: Failed to get DebugBreakProcess']);
|
|
DebugLn(DBG_WARNINGS and (_GetThreadDescription = nil), ['WARNING: Failed to get GetThreadDescription']);
|
|
{$ifdef cpux86_64}
|
|
DebugLn(DBG_WARNINGS and (_IsWow64Process = nil), ['WARNING: Failed to get IsWow64Process']);
|
|
DebugLn(DBG_WARNINGS and (_Wow64GetThreadContext = nil), ['WARNING: Failed to get Wow64GetThreadContext']);
|
|
DebugLn(DBG_WARNINGS and (_Wow64SetThreadContext = nil), ['WARNING: Failed to get Wow64SetThreadContext']);
|
|
DebugLn(DBG_WARNINGS and (_Wow64SuspendThread = nil), ['WARNING: Failed to get _Wow64SuspendThread']);
|
|
{$endif}
|
|
end;
|
|
|
|
//TDbgWinThreadName
|
|
|
|
procedure TDbgWinThreadNameInternal.SetInfo(id:TThreadID;const threadname:string);
|
|
begin
|
|
Fid:=id;
|
|
Fthreadname:=threadname;
|
|
end;
|
|
|
|
//
|
|
|
|
function TDbgWinThreadNameList.CreateItem: TLinkListItem;
|
|
begin
|
|
Result:=TLinkListItem(TDbgWinThreadNameInternal.Create);
|
|
end;
|
|
|
|
procedure TDbgWinThreadNameList.ClearThread;
|
|
begin
|
|
Clear;
|
|
FNumCounter:=0;
|
|
end;
|
|
|
|
function TDbgWinThreadNameList.FindById(id:TThreadID):TDbgWinThreadNameInternal;
|
|
var
|
|
node:TDbgWinThreadNameInternal;
|
|
begin
|
|
Result:=nil;
|
|
node:=TDbgWinThreadNameInternal(First);
|
|
while (node<>nil) do
|
|
begin
|
|
if (node.Fid=id) then
|
|
begin
|
|
Exit(node);
|
|
end;
|
|
node:=TDbgWinThreadNameInternal(node.Next);
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinThreadNameList.FetchThread(id:TThreadID):string;
|
|
var
|
|
node:TDbgWinThreadNameInternal;
|
|
begin
|
|
Result:='';
|
|
node:=FindById(id);
|
|
if (node<>nil) then
|
|
begin
|
|
Result:=node.Fthreadname;
|
|
Delete(TLinkListItem(node));
|
|
Dec(FNumCounter);
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgWinThreadNameList.AddThread(id:TThreadID;const threadname:string);
|
|
var
|
|
node:TDbgWinThreadNameInternal;
|
|
begin
|
|
node:=FindById(id);
|
|
if (node<>nil) then
|
|
begin
|
|
node.SetInfo(id,threadname);
|
|
end else
|
|
begin
|
|
|
|
if (FNumCounter>=FMaxCounter) then
|
|
begin
|
|
//limit
|
|
node:=TDbgWinThreadNameInternal(First);
|
|
if (node=nil) then Exit;
|
|
node.SetInfo(id,threadname);
|
|
MoveToLast(TLinkListItem(node));
|
|
end else
|
|
begin
|
|
node:=TDbgWinThreadNameInternal(GetNewItem);
|
|
node.SetInfo(id,threadname);
|
|
AddAsLast(TLinkListItem(node));
|
|
Inc(FNumCounter);
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
//TThreadNameMap
|
|
|
|
procedure TDbgWinProcess.LogLastError(AMsg: String);
|
|
begin
|
|
if not GotExitProcess then
|
|
DebugLn(DBG_WARNINGS, 'FpDbg-ERROR: %s -> %s', [AMsg, GetLastErrorText]);
|
|
end;
|
|
|
|
procedure TDbgWinProcess.AfterChangingInstructionCode(const ALocation: TDBGPtr;
|
|
ACount: Integer);
|
|
begin
|
|
inherited AfterChangingInstructionCode(ALocation, ACount);
|
|
FlushInstructionCache(Handle, Pointer(PtrUInt(ALocation)), 1);
|
|
//FlushInstructionCache(Handle, nil, 0);
|
|
end;
|
|
|
|
function TDbgWinProcess.GetFullProcessImageName(AProcessHandle: THandle): string;
|
|
var
|
|
u: UnicodeString;
|
|
len: DWORD;
|
|
begin
|
|
Result := '';
|
|
if _QueryFullProcessImageName = nil then
|
|
exit;
|
|
len := MAX_PATH;
|
|
SetLength(u, len);
|
|
if _QueryFullProcessImageName(AProcessHandle, 0, @u[1], len)
|
|
then begin
|
|
SetLength(u, len);
|
|
Result:=UTF8Encode(u);
|
|
end
|
|
else begin
|
|
LogLastError;
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinProcess.GetModuleFileName(AModuleHandle: THandle): string;
|
|
var
|
|
u: UnicodeString;
|
|
s: string;
|
|
len: Integer;
|
|
begin
|
|
result := '';
|
|
|
|
// GetFinalPathNameByHandle is only available on Windows Vista / Server 2008
|
|
if assigned(_GetFinalPathNameByHandle) then begin
|
|
SetLength(u, MAX_PATH+1);
|
|
|
|
len := _GetFinalPathNameByHandle(AModuleHandle, @u[1], MAX_PATH, 0);
|
|
s:='';
|
|
if len > 0
|
|
then begin
|
|
// On some older Windows versions there's a bug in GetFinalPathNameByHandleW,
|
|
// which leads to a trailing #0.
|
|
if (u[len]=#0) then
|
|
dec(len);
|
|
SetLength(u, len);
|
|
s:=UTF8Encode(u);
|
|
end else begin
|
|
u := '';
|
|
LogLastError;
|
|
end;
|
|
result := S;
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinProcess.GetProcFilename(AProcess: TDbgProcess; lpImageName: LPVOID; fUnicode: word; hFile: handle): string;
|
|
var
|
|
NamePtr: TDbgPtr;
|
|
S: String;
|
|
W: WideString;
|
|
begin
|
|
S := '';
|
|
if (lpImageName<>nil) and AProcess.ReadOrdinal(TDbgPtr(lpImageName), NamePtr)
|
|
then begin
|
|
if fUnicode <> 0
|
|
then begin
|
|
if AProcess.ReadWString(NamePtr, MAX_PATH, W)
|
|
then S := W;
|
|
end
|
|
else begin
|
|
AProcess.ReadString(NamePtr, MAX_PATH, S);
|
|
end;
|
|
end;
|
|
|
|
if S = ''
|
|
then begin
|
|
if hFile=0 then
|
|
S := GetFullProcessImageName(AProcess.Handle)
|
|
else
|
|
S := GetModuleFileName(hFile);
|
|
end;
|
|
result := S;
|
|
end;
|
|
|
|
{ tDbgWinLibrary }
|
|
|
|
procedure tDbgWinLibrary.InitializeLoaders;
|
|
var
|
|
FileInformation: TByHandleFileInformation;
|
|
Loader: TDbgImageLoader;
|
|
begin
|
|
Loader := nil;
|
|
if GetFileInformationByHandle(FInfo.hFile, FileInformation) then
|
|
Loader := TDbgImageLoaderLibrary.Create(FInfo.hFile, nil, TDBGPtr(FInfo.lpBaseOfDll))
|
|
else if Name <> '' then
|
|
begin
|
|
// There are situations in which the provided handle is not a file-handle. In
|
|
// those cases, use the filename as fallback.
|
|
// (Happened in a Windows-docker (Azure, AKS) on the kernel32.dll. No idea
|
|
// why, though)
|
|
if FileExists(Name) then
|
|
Loader := TDbgImageLoaderLibrary.Create(Name, nil, TDBGPtr(FInfo.lpBaseOfDll))
|
|
else
|
|
DebugLn(DBG_WARNINGS, 'File [%s] related to library does not exist', [Name]);
|
|
end;
|
|
if Assigned(Loader) and Loader.IsValid then
|
|
Loader.AddToLoaderList(LoaderList)
|
|
else
|
|
Loader.Free;
|
|
end;
|
|
|
|
constructor tDbgWinLibrary.Create(const AProcess: TDbgProcess;
|
|
const ADefaultName: String; const AModuleHandle: THandle;
|
|
AInfo: TLoadDLLDebugInfo);
|
|
var
|
|
S: String;
|
|
begin
|
|
inherited Create(AProcess, ADefaultName, AModuleHandle);
|
|
FInfo := AInfo;
|
|
|
|
s := TDbgWinProcess(AProcess).GetProcFilename(AProcess, AInfo.lpImageName, AInfo.fUnicode, AInfo.hFile);
|
|
if s <> ''
|
|
then SetFileName(s);
|
|
|
|
LoadInfo;
|
|
end;
|
|
|
|
{ TDbgWinProcess }
|
|
|
|
function TDbgWinProcess.GetHandle: THandle;
|
|
begin
|
|
Result:=FInfo.hProcess;
|
|
end;
|
|
|
|
function TDbgWinProcess.GetLastEventProcessIdentifier: THandle;
|
|
begin
|
|
Result:= MDebugEvent.LoadDll.hFile;
|
|
end;
|
|
|
|
procedure TDbgWinProcess.InitializeLoaders;
|
|
var
|
|
FileInformation: TByHandleFileInformation;
|
|
Loader: TDbgImageLoader;
|
|
begin
|
|
Loader := nil;
|
|
if GetFileInformationByHandle(FInfo.hFile, FileInformation) then
|
|
Loader := TDbgImageLoader.Create(FInfo.hFile, nil, TDbgPtr(FInfo.lpBaseOfImage))
|
|
else if Name <> '' then
|
|
begin
|
|
// There are situations in which the provided handle is not a file-handle. In
|
|
// those cases, use the filename as fallback.
|
|
// (Happened in a Windows-docker (Azure, AKS) on the kernel32.dll. No idea
|
|
// why, though)
|
|
if FileExists(Name) then
|
|
Loader := TDbgImageLoader.Create(Name, nil, TDBGPtr(FInfo.lpBaseOfImage))
|
|
else
|
|
DebugLn(DBG_WARNINGS, 'File [%s] related to the process does not exist', [Name]);
|
|
end;
|
|
if Assigned(Loader) and Loader.IsValid then
|
|
Loader.AddToLoaderList(LoaderList)
|
|
else
|
|
Loader.Free;
|
|
end;
|
|
|
|
function TDbgWinProcess.CreateWatchPointData: TFpWatchPointData;
|
|
begin
|
|
Result := TFpIntelWatchPointData.Create;
|
|
end;
|
|
|
|
constructor TDbgWinProcess.Create(const AFileName: string;
|
|
AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager;
|
|
AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig);
|
|
begin
|
|
{$ifdef cpui386}
|
|
FBitness := b32;
|
|
{$else}
|
|
FBitness := b64;
|
|
{$endif}
|
|
FThreadNameList := TDbgWinThreadNameList.Create;
|
|
inherited Create(AFileName, AnOsClasses, AMemManager, AMemModel, AProcessConfig);
|
|
end;
|
|
|
|
destructor TDbgWinProcess.Destroy;
|
|
begin
|
|
FInfo.hProcess:=0;
|
|
FProcProcess.Free;
|
|
FThreadNameList.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TDbgWinProcess.ReadData(const AAdress: TDbgPtr; const ASize: Cardinal; out AData): Boolean;
|
|
var
|
|
BytesRead: PtrUInt;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinProcess.ReadData');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinProcess.ReadData: MDebugEvent.dwProcessId <> 0');
|
|
Result := ReadProcessMemory(Handle, Pointer(PtrUInt(AAdress)), @AData, ASize, BytesRead) and (BytesRead = ASize);
|
|
|
|
if Result then
|
|
MaskBreakpointsInReadData(AAdress, ASize, AData)
|
|
else
|
|
LogLastError('ReadData '+dbghex(int64(AAdress))+' / '+dbgs(ASize) + '(done: '+dbgs(BytesRead)+' )');
|
|
end;
|
|
|
|
function TDbgWinProcess.WriteData(const AAdress: TDbgPtr; const ASize: Cardinal; const AData): Boolean;
|
|
var
|
|
BytesWritten: PtrUInt;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinProcess.WriteData');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinProcess.WriteData: MDebugEvent.dwProcessId <> 0');
|
|
Result := WriteProcessMemory(Handle, Pointer(PtrUInt(AAdress)), @AData, ASize, BytesWritten) and (BytesWritten = ASize);
|
|
|
|
if not Result then
|
|
LogLastError('WriteData '+dbghex(int64(AAdress))+' / '+dbgs(ASize) + '(done: '+dbgs(BytesWritten)+' )');
|
|
end;
|
|
|
|
function TDbgWinProcess.ReadString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: String): Boolean;
|
|
var
|
|
BytesRead: PtrUInt;
|
|
buf: array of Char;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinProcess.ReadString');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinProcess.ReadString: MDebugEvent.dwProcessId <> 0');
|
|
AData := '';
|
|
SetLength(buf, AMaxSize + 1);
|
|
Result := ReadProcessMemory(Handle, Pointer(PtrUInt(AAdress)), @Buf[0], AMaxSize, BytesRead);
|
|
if not Result then Exit;
|
|
if BytesRead < AMaxSize
|
|
then Buf[BytesRead] := #0
|
|
else Buf[AMaxSize] := #0;
|
|
AData := PChar(@Buf[0]);
|
|
end;
|
|
|
|
function TDbgWinProcess.ReadWString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: WideString): Boolean;
|
|
var
|
|
BytesRead: PtrUInt;
|
|
buf: array of WChar;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinProcess.ReadWString');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinProcess.ReadWString: MDebugEvent.dwProcessId <> 0');
|
|
AData := '';
|
|
SetLength(buf, AMaxSize + 1);
|
|
Result := ReadProcessMemory(Handle, Pointer(PtrUInt(AAdress)), @Buf[0], SizeOf(WChar) * AMaxSize, BytesRead);
|
|
if not Result then Exit;
|
|
BytesRead := BytesRead div SizeOf(WChar);
|
|
if BytesRead < AMaxSize
|
|
then Buf[BytesRead] := #0
|
|
else Buf[AMaxSize] := #0;
|
|
AData := PWChar(@Buf[0]);
|
|
end;
|
|
|
|
function TDbgWinProcess.CallParamDefaultLocation(AParamIdx: Integer
|
|
): TFpDbgMemLocation;
|
|
begin
|
|
Result := InvalidLoc;
|
|
case Mode of
|
|
dm32: case AParamIdx of
|
|
-1: Result := RegisterLoc(0); // EAX
|
|
0: Result := RegisterLoc(0); // EAX
|
|
1: Result := RegisterLoc(2); // EDX
|
|
2: Result := RegisterLoc(1); // ECX
|
|
else
|
|
Result := UnInitializedLoc;
|
|
end;
|
|
dm64: case AParamIdx of
|
|
-1: Result := RegisterLoc(0); // RAX
|
|
0: Result := RegisterLoc(2); // RCX
|
|
1: Result := RegisterLoc(1); // RDX
|
|
2: Result := RegisterLoc(8); // R8
|
|
3: Result := RegisterLoc(9); // R9
|
|
else
|
|
Result := UnInitializedLoc;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgWinProcess.Interrupt;
|
|
var
|
|
_UC: record
|
|
C: TContext;
|
|
D: array[1..16] of Byte;
|
|
end;
|
|
Context: PContext;
|
|
begin
|
|
// Interrupting is implemented by suspending the thread and set DB0 to the
|
|
// (to be) executed EIP. When the thread is resumed, it will generate a break
|
|
// Single stepping doesn't work in all cases.
|
|
|
|
// A context needs to be aligned to 16 bytes. Unfortunately, the compiler has
|
|
// no directive for this, so align it somewhere in our "reserved" memory
|
|
Context := AlignPtr(@_UC, $10);
|
|
SuspendThread(FInfo.hThread);
|
|
try
|
|
Context^.ContextFlags := CONTEXT_CONTROL or CONTEXT_DEBUG_REGISTERS;
|
|
if not GetThreadContext(FInfo.hThread, Context^)
|
|
then begin
|
|
DebugLn(DBG_WARNINGS, 'Proces %u interrupt: Unable to get context', [ProcessID]);
|
|
Exit;
|
|
end;
|
|
|
|
Context^.ContextFlags := CONTEXT_DEBUG_REGISTERS;
|
|
{$ifdef cpui386}
|
|
Context^.Dr0 := Context^.Eip;
|
|
{$else}
|
|
Context^.Dr0 := Context^.Rip;
|
|
{$endif}
|
|
Context^.Dr7 := (Context^.Dr7 and $FFF0FFFF) or $1;
|
|
|
|
if not SetThreadContext(FInfo.hThread, Context^)
|
|
then begin
|
|
DebugLn(DBG_WARNINGS, 'Proces %u interrupt: Unable to set context', [ProcessID]);
|
|
Exit;
|
|
end;
|
|
finally
|
|
ResumeTHread(FInfo.hThread);
|
|
end;
|
|
end;
|
|
|
|
{ ------------------------------------------------------------------
|
|
HandleDebugEvent
|
|
|
|
Result: True if the event was triggered internally
|
|
The callee should continue the process
|
|
------------------------------------------------------------------ }
|
|
function TDbgWinProcess.HandleDebugEvent(const ADebugEvent: TDebugEvent): Boolean;
|
|
begin
|
|
Result := False;
|
|
case ADebugEvent.dwDebugEventCode of
|
|
EXIT_THREAD_DEBUG_EVENT: begin
|
|
// The thread event will be freed later, may still be used
|
|
// will be freed, in "TDbgWinProcess.Continue"
|
|
// This relies on the thread being removed, to be the same as FCurrentThread in FPDbgController
|
|
RemoveThread(ADebugEvent.dwThreadId);
|
|
end;
|
|
LOAD_DLL_DEBUG_EVENT: begin
|
|
AddLib(ADebugEvent.LoadDll);
|
|
end;
|
|
UNLOAD_DLL_DEBUG_EVENT: begin
|
|
RemoveLib(ADebugEvent.UnloadDll);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinProcess.StartInstance(AParams, AnEnvironment: TStrings;
|
|
AWorkingDirectory, AConsoleTty: string; AFlags: TStartInstanceFlags; out
|
|
AnError: TFpError): boolean;
|
|
var
|
|
LastErr: Integer;
|
|
begin
|
|
result := false;
|
|
FProcProcess := TProcessWithRedirect.Create(nil);
|
|
try
|
|
// To debug sub-processes, this needs to be poDebugProcess
|
|
FProcProcess.Options:=[poDebugProcess, poDebugOnlyThisProcess, poNewProcessGroup];
|
|
if siForceNewConsole in AFlags then
|
|
FProcProcess.Options:=FProcProcess.Options+[poNewConsole];
|
|
FProcProcess.Executable:=Name;
|
|
FProcProcess.Parameters:=AParams;
|
|
FProcProcess.Environment:=AnEnvironment;
|
|
FProcProcess.CurrentDirectory:=AWorkingDirectory;
|
|
if Config.UseConsoleWinPos then begin
|
|
FProcProcess.StartupOptions := FProcProcess.StartupOptions + [suoUsePosition];
|
|
FProcProcess.WindowLeft := Cardinal(Config.ConsoleWinPos.X);
|
|
FProcProcess.WindowTop := Cardinal(Config.ConsoleWinPos.Y);
|
|
end;
|
|
if Config.UseConsoleWinSize then begin
|
|
FProcProcess.StartupOptions := FProcProcess.StartupOptions + [suoUseSize];
|
|
FProcProcess.WindowWidth := Cardinal(Config.ConsoleWinSize.X);
|
|
FProcProcess.WindowHeight := Cardinal(Config.ConsoleWinSize.Y);
|
|
end;
|
|
if Config.UseConsoleWinBuffer then begin
|
|
FProcProcess.StartupOptions := FProcProcess.StartupOptions + [suoUseCountChars];
|
|
FProcProcess.WindowColumns := Cardinal(Config.ConsoleWinBuffer.X);
|
|
FProcProcess.WindowRows := Cardinal(Config.ConsoleWinBuffer.Y);
|
|
end;
|
|
|
|
if DBG_PROCESS_HAS_REDIRECT then begin
|
|
FProcProcess.SetRedirection(dtStdIn, Config.StdInRedirFile, Config.FileOverwriteStdIn);
|
|
if (Config.StdOutRedirFile = Config.StdErrRedirFile) then begin
|
|
if Config.StdOutRedirFile <> '' then begin
|
|
FProcProcess.SetRedirection(dtStdOut, Config.StdOutRedirFile, Config.FileOverwriteStdOut or Config.FileOverwriteStdErr);
|
|
FProcProcess.Options := FProcProcess.Options + [poStdErrToOutPut];
|
|
end;
|
|
end
|
|
else begin
|
|
FProcProcess.SetRedirection(dtStdOut, Config.StdOutRedirFile, Config.FileOverwriteStdOut);
|
|
FProcProcess.SetRedirection(dtStdErr, Config.StdErrRedirFile, Config.FileOverwriteStdErr);
|
|
end;
|
|
|
|
if (Win32MajorVersion < 6) or
|
|
( (Win32MajorVersion = 6) and (Win32MinorVersion <= 1) )
|
|
then
|
|
FProcProcess.ApplyWin7Fix;
|
|
end;
|
|
|
|
FProcProcess.Execute;
|
|
|
|
Init(FProcProcess.ProcessID, 0);
|
|
|
|
FThreadNameList.ClearThread;
|
|
|
|
Result:=true;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
LastErr := Integer(GetLastError);
|
|
DebugLn(DBG_WARNINGS, 'Failed to start process "%s". Errormessage: "%s %d".',[Name, E.Message, LastErr]);
|
|
{$ifdef cpui386}
|
|
if (E is EProcess) and (GetLastError=50) then
|
|
begin
|
|
AnError := CreateError(fpErrCreateProcess, [Name, LastErr, E.Message, 'Note that on Windows it is not possible to debug a 64-bit application with a 32-bit debugger.'])
|
|
end
|
|
else
|
|
{$endif i386}
|
|
AnError := CreateError(fpErrCreateProcess, [Name, LastErr, E.Message, '']);
|
|
FreeAndNil(FProcProcess);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinProcess.AttachToInstance(APid: Integer; out AnError: TFpError
|
|
): boolean;
|
|
var
|
|
LastErr: Integer;
|
|
begin
|
|
Result := false;
|
|
if _DebugActiveProcess = nil then begin
|
|
AnError := CreateError(fpErrAttachProcess, [Name, 0, 'API unavailable', '']);
|
|
exit;
|
|
end;
|
|
if not _DebugActiveProcess(APid) then begin
|
|
LastErr := Integer(GetLastError);
|
|
AnError := CreateError(fpErrAttachProcess, [Name, LastErr, GetLastErrorText(LastErr), '']);
|
|
exit;
|
|
end;
|
|
|
|
Init(APid, 0);
|
|
|
|
FThreadNameList.ClearThread;
|
|
|
|
Result := true;
|
|
// TODO: change the filename to the actual exe-filename. Load the correct dwarf info
|
|
end;
|
|
|
|
class function TDbgWinProcess.isSupported(ATargetInfo: TTargetDescriptor
|
|
): boolean;
|
|
begin
|
|
result := (ATargetInfo.OS = osWindows) and
|
|
(ATargetInfo.machineType in [mt386, mtX86_64]);
|
|
end;
|
|
|
|
function TDbgWinProcess.CanContinueForWatchEval(ACurrentThread: TDbgThread
|
|
): boolean;
|
|
begin
|
|
Result := inherited CanContinueForWatchEval(ACurrentThread);
|
|
Result := Result and
|
|
( (TDbgWinThread(ACurrentThread).FHasExceptionCleared) or
|
|
(MDebugEvent.dwDebugEventCode <> EXCEPTION_DEBUG_EVENT) or
|
|
(MDebugEvent.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT) or
|
|
(MDebugEvent.Exception.ExceptionRecord.ExceptionCode = STATUS_WX86_BREAKPOINT) or
|
|
(MDebugEvent.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_SINGLE_STEP) or
|
|
(MDebugEvent.Exception.ExceptionRecord.ExceptionCode = STATUS_WX86_SINGLE_STEP)
|
|
);
|
|
end;
|
|
|
|
function TDbgWinProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread;
|
|
SingleStep: boolean): boolean;
|
|
|
|
function HasThreadInSkippingBreak: Boolean;
|
|
var
|
|
t: TDbgThread;
|
|
begin
|
|
Result := False;
|
|
for t in FThreadMap do
|
|
if TDbgWinThread(t).FBreakPointState = bsInSingleStep then begin
|
|
Result := True;
|
|
break;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
EventThread, t: TDbgThread;
|
|
WinEventThread: TDbgWinThread absolute EventThread;
|
|
WinAThread: TDbgWinThread absolute AThread;
|
|
HasExceptionCleared, EventThreadNeedsTempBrkRemove: Boolean;
|
|
begin
|
|
debugln(FPDBG_WINDOWS, ['TDbgWinProcess.Continue ',SingleStep, ' # ', ' # ',DbgSTime]);
|
|
HasExceptionCleared := (WinAThread <> nil) and WinAThread.FHasExceptionCleared;
|
|
|
|
if assigned(AThread) and not FThreadMap.HasId(AThread.ID) then begin
|
|
AThread := nil;
|
|
end;
|
|
|
|
(* In case a thread needs to single-step over a (temp-removed) breakpoint,
|
|
other events (from suspended threads, if the event is already triggered)
|
|
can be received. THe single step must be continued until finished.
|
|
This may mean suspending the current thread.
|
|
*)
|
|
|
|
(* AThread versus EventThread
|
|
|
|
* AThread:
|
|
- AThread is ONLY passed for the "SingleStep" parameter.
|
|
|
|
- If AThread is at breakpoint, and AThread is *not* the event-thread, then
|
|
AThread must still hit that breakpoint.
|
|
Only the event-thread has been checked for being at a breakpoint.
|
|
|
|
* EventThread
|
|
- The event-thread will have been checked for being at a breakpoint.
|
|
It therefore must always step-over, if it is at a breakpoint
|
|
|
|
- Except, if the event-thread is at a hardcoded breakpoint.
|
|
In that case:
|
|
~ The controller has handled, the hardcoded breakpoint.
|
|
~ The IP was *not* reset.
|
|
So the event-thread may already be at the *next* breakpoint.
|
|
*)
|
|
|
|
EventThreadNeedsTempBrkRemove := False;
|
|
if AProcess.GetThread(MDebugEvent.dwThreadId, EventThread) then begin
|
|
EventThreadNeedsTempBrkRemove :=
|
|
(not EventThread.PausedAtHardcodeBreakPoint) and
|
|
Process.HasInsertedBreakInstructionAtLocation(EventThread.GetInstructionPointerRegisterValue);
|
|
|
|
if EventThreadNeedsTempBrkRemove then
|
|
WinEventThread.FBreakPointState := bsInSingleStep;
|
|
|
|
if ( (EventThread = AThread) and SingleStep ) or
|
|
( EventThreadNeedsTempBrkRemove )
|
|
then
|
|
WinEventThread.SetSingleStep;
|
|
assert((WinEventThread.FBreakPointState=bsNone) or WinEventThread.NextIsSingleStep, 'TDbgWinProcess.Continue: (WinEventThread.FBreakPointState=bsNone) or WinEventThread.NextIsSingleStep');
|
|
end;
|
|
|
|
if (AThread <> nil) and (AThread <> EventThread) and SingleStep then
|
|
WinAThread.SetSingleStep;
|
|
|
|
if EventThreadNeedsTempBrkRemove or HasThreadInSkippingBreak then begin
|
|
debugln(FPDBG_WINDOWS or DBG_VERBOSE, '## Skip BrkPoint: EvntThread Nil=%s ISS=%s TmpRmBreak=%s / Thread Nil=%s ISS=%s ',
|
|
[ dbgs(EventThread <> nil), dbgs((EventThread<>nil) and EventThread.NextIsSingleStep), dbgs(EventThreadNeedsTempBrkRemove),
|
|
dbgs(AThread <> nil), dbgs((AThread<>nil) and AThread.NextIsSingleStep) ]);
|
|
for t in FThreadMap do
|
|
TDbgWinThread(t).SuspendForStepOverBreakPoint;
|
|
end;
|
|
|
|
for t in FThreadMap do
|
|
if (t <> AThread) and (t.SuspendCount > 0) then
|
|
TDbgWinThread(t).Suspend;
|
|
|
|
AProcess.ThreadsBeforeContinue;
|
|
if AThread<>nil then debugln(FPDBG_WINDOWS, ['## ath.iss ',AThread.NextIsSingleStep]);
|
|
|
|
if HasExceptionCleared then
|
|
result := Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE)
|
|
else
|
|
if MDebugEvent.dwDebugEventCode = EXCEPTION_DEBUG_EVENT then
|
|
case MDebugEvent.Exception.ExceptionRecord.ExceptionCode of
|
|
EXCEPTION_BREAKPOINT, STATUS_WX86_BREAKPOINT,
|
|
EXCEPTION_SINGLE_STEP, STATUS_WX86_SINGLE_STEP: begin
|
|
result := Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
end
|
|
else
|
|
result := Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_EXCEPTION_NOT_HANDLED);
|
|
end
|
|
else
|
|
result := Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
DebugLn((FPDBG_WINDOWS or DBG_WARNINGS) and (not Result), 'ContinueDebugEvent failed: %d', [Windows.GetLastError]);
|
|
result := true;
|
|
MDebugEvent.dwProcessId := 0; // Flag as running // for assert in ReadThreadState
|
|
end;
|
|
|
|
function TDbgWinProcess.Detach(AProcess: TDbgProcess; AThread: TDbgThread
|
|
): boolean;
|
|
var
|
|
t: TDbgWinThread;
|
|
PendingDebugEvent: TDebugEvent;
|
|
begin
|
|
Result := _DebugActiveProcessStop <> nil;
|
|
if not Result then
|
|
exit;
|
|
|
|
RemoveAllBreakPoints;
|
|
|
|
// Collect all pending events // Deal with any breakpoint/int3 hit
|
|
if not GetThread(MDebugEvent.dwThreadId, TDbgThread(AThread)) then begin
|
|
assert(False, 'TDbgWinProcess.Detach: Missing thread');
|
|
TDbgThread(AThread) := AddThread(MDebugEvent.dwThreadId);
|
|
end;
|
|
|
|
for TDbgThread(t) in FThreadMap do
|
|
if not t.ID = MDebugEvent.dwThreadId then
|
|
t.Suspend;
|
|
|
|
TDbgWinThread(AThread).SetSingleStep;
|
|
Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
while Windows.WaitForDebugEvent(PendingDebugEvent, 1) do begin
|
|
if PendingDebugEvent.dwThreadId = MDebugEvent.dwThreadId then
|
|
break;
|
|
case PendingDebugEvent.dwDebugEventCode of
|
|
CREATE_PROCESS_DEBUG_EVENT: begin
|
|
if PendingDebugEvent.CreateProcessInfo.hFile <> 0 then
|
|
CloseHandle(PendingDebugEvent.CreateProcessInfo.hFile);
|
|
_DebugActiveProcessStop(PendingDebugEvent.dwProcessId);
|
|
end;
|
|
EXCEPTION_DEBUG_EVENT:
|
|
case PendingDebugEvent.Exception.ExceptionRecord.ExceptionCode of
|
|
EXCEPTION_BREAKPOINT, STATUS_WX86_BREAKPOINT: begin
|
|
if not GetThread(PendingDebugEvent.dwThreadId, TDbgThread(t)) then
|
|
TDbgThread(t) := AddThread(PendingDebugEvent.dwThreadId);
|
|
t.CheckAndResetInstructionPointerAfterBreakpoint;
|
|
end;
|
|
end;
|
|
end;
|
|
Windows.ContinueDebugEvent(PendingDebugEvent.dwProcessId, PendingDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
end;
|
|
|
|
for TDbgThread(t) in FThreadMap do
|
|
t.Resume;
|
|
|
|
Result := _DebugActiveProcessStop(ProcessID);
|
|
// Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
end;
|
|
|
|
function TDbgWinProcess.WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean;
|
|
var
|
|
t: TDbgWinThread;
|
|
Done: Boolean;
|
|
begin
|
|
repeat
|
|
Done := True;
|
|
if _WaitForDebugEventEx <> nil then
|
|
result := _WaitForDebugEventEx(MDebugEvent, INFINITE)
|
|
else
|
|
result := Windows.WaitForDebugEvent(MDebugEvent, INFINITE);
|
|
DebugLn(FPDBG_WINDOWS and (not Result), 'WaitForDebugEvent failed: %d', [Windows.GetLastError]);
|
|
|
|
if Result and FTerminated and (MDebugEvent.dwDebugEventCode <> EXIT_PROCESS_DEBUG_EVENT)
|
|
and (MDebugEvent.dwDebugEventCode <> EXIT_THREAD_DEBUG_EVENT)
|
|
then begin
|
|
// Wait for the terminate event // Do not report any queued breakpoints
|
|
DebugLn(FPDBG_WINDOWS, ['Terminating... Skipping event: ', dbgs(MDebugEvent)]);
|
|
for TDbgThread(t) in FThreadMap do
|
|
t.Suspend;
|
|
Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
Done := False;
|
|
end
|
|
|
|
else
|
|
if Result and (MDebugEvent.dwProcessId <> Self.ProcessID) then begin
|
|
(* Some events are not processed yet anyway.
|
|
They never reach AnalyseDebugEvent, so deal with them here
|
|
*)
|
|
case MDebugEvent.dwDebugEventCode of
|
|
CREATE_PROCESS_DEBUG_EVENT: begin
|
|
//child process: ignore
|
|
// we currently do not use the file handle => close it
|
|
if MDebugEvent.CreateProcessInfo.hFile <> 0 then
|
|
if not CloseHandle(MDebugEvent.CreateProcessInfo.hFile) then
|
|
debugln(DBG_WARNINGS, ['Failed to close new process file handle: ',GetLastErrorText]);
|
|
if _DebugActiveProcessStop <> nil then
|
|
if not _DebugActiveProcessStop(MDebugEvent.dwProcessId) then
|
|
debugln(DBG_WARNINGS, ['Failed to detach: ',GetLastErrorText]);
|
|
|
|
Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
Done := False;
|
|
end;
|
|
EXIT_PROCESS_DEBUG_EVENT: begin
|
|
// Should never be here, since it detached
|
|
FThreadNameList.ClearThread;
|
|
Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
Done := False;
|
|
end;
|
|
end;
|
|
end;
|
|
until Done;
|
|
|
|
ProcessIdentifier:=MDebugEvent.dwProcessId;
|
|
ThreadIdentifier:=MDebugEvent.dwThreadId;
|
|
{$IFDEF DebuglnWinDebugEvents}
|
|
DebugLn(FPDBG_WINDOWS, [dbgs(MDebugEvent), ' ', Result, ' # ',DbgSTime]);
|
|
for TDbgThread(t) in FThreadMap do begin
|
|
if t.ReadThreadState then
|
|
DebugLn(FPDBG_WINDOWS,
|
|
'Thr.Id:%d %x SSTep %s EF %s DR6:%x DR7:%x WP:%x RegAcc: %d, SStep: %d Task: %d, ExcBrk: %d Susp: %s, ISS: %s BS:%s',
|
|
[t.ID, t.GetInstructionPointerRegisterValue, dbgs(t.FCurrentContext^.def.EFlags and FLAG_TRACE_BIT), dbghex(t.FCurrentContext^.def.EFlags), t.FCurrentContext^.def.Dr6, t.FCurrentContext^.def.Dr7, t.FCurrentContext^.def.Dr6 and 15, t.FCurrentContext^.def.Dr6 and (1<< 13), t.FCurrentContext^.def.Dr6 and (1<< 14), t.FCurrentContext^.def.Dr6 and (1<< 15), t.FCurrentContext^.def.Dr6 and (1<< 16), dbgs(t.FIsSuspended), dbgs(t.NextIsSingleStep), dbgs(t.FBreakPointState) ]);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
RestoreTempBreakInstructionCodes;
|
|
if not FTerminated then
|
|
for TDbgThread(t) in FThreadMap do
|
|
t.Resume;
|
|
|
|
// Should be done in AnalyseDebugEvent, but that is not called for forked processes
|
|
if (MDebugEvent.dwDebugEventCode = CREATE_PROCESS_DEBUG_EVENT) and
|
|
(MDebugEvent.dwProcessId <> ProcessID) and
|
|
(MDebugEvent.CreateProcessInfo.hFile <> 0)
|
|
then begin
|
|
CloseHandle(MDebugEvent.CreateProcessInfo.hFile);
|
|
MDebugEvent.CreateProcessInfo.hFile := 0;
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
|
|
|
|
procedure HandleException(const AEvent: TDebugEvent; out InterceptAtFirstChance: Boolean);
|
|
const
|
|
PARAMCOLS = 12 - SizeOf(Pointer);
|
|
var
|
|
Info0: QWORD;
|
|
Info1: QWORD;
|
|
Info1Str: String;
|
|
ExInfo32: TExceptionDebugInfo32 absolute AEvent.Exception;
|
|
ExInfo64: TExceptionDebugInfo64 absolute AEvent.Exception;
|
|
begin
|
|
InterceptAtFirstChance := True;
|
|
// Kept the debug-output as comments, since they provide deeper information
|
|
// on how to interprete the exception-information.
|
|
{
|
|
if AEvent.Exception.dwFirstChance = 0
|
|
then DebugLn(DBG_VERBOSE, 'Exception: ')
|
|
else DebugLn(DBG_VERBOSE, 'First chance exception: ');
|
|
}
|
|
// in both 32 and 64 case is the exceptioncode the first, so no difference
|
|
case AEvent.Exception.ExceptionRecord.ExceptionCode of
|
|
EXCEPTION_ACCESS_VIOLATION : ExceptionClass:='ACCESS VIOLATION';
|
|
EXCEPTION_ARRAY_BOUNDS_EXCEEDED : ExceptionClass:='ARRAY BOUNDS EXCEEDED';
|
|
EXCEPTION_BREAKPOINT : ExceptionClass:='BREAKPOINT'; // should never be here
|
|
EXCEPTION_DATATYPE_MISALIGNMENT : ExceptionClass:='DATATYPE MISALIGNMENT';
|
|
EXCEPTION_FLT_DENORMAL_OPERAND : ExceptionClass:='FLT DENORMAL OPERAND';
|
|
EXCEPTION_FLT_DIVIDE_BY_ZERO : ExceptionClass:='FLT DIVIDE BY ZERO';
|
|
EXCEPTION_FLT_INEXACT_RESULT : ExceptionClass:='FLT INEXACT RESULT';
|
|
EXCEPTION_FLT_INVALID_OPERATION : ExceptionClass:='FLT INVALID OPERATION';
|
|
EXCEPTION_FLT_OVERFLOW : ExceptionClass:='FLT OVERFLOW';
|
|
EXCEPTION_FLT_STACK_CHECK : ExceptionClass:='FLT STACK CHECK';
|
|
EXCEPTION_FLT_UNDERFLOW : ExceptionClass:='FLT UNDERFLOW';
|
|
EXCEPTION_ILLEGAL_INSTRUCTION : ExceptionClass:='ILLEGAL INSTRUCTION';
|
|
EXCEPTION_IN_PAGE_ERROR : ExceptionClass:='IN PAGE ERROR';
|
|
EXCEPTION_INT_DIVIDE_BY_ZERO : ExceptionClass:='INT DIVIDE BY ZERO';
|
|
EXCEPTION_INT_OVERFLOW : ExceptionClass:='INT OVERFLOW';
|
|
EXCEPTION_INVALID_DISPOSITION : ExceptionClass:='INVALID DISPOSITION';
|
|
EXCEPTION_INVALID_HANDLE : ExceptionClass:='INVALID HANDLE';
|
|
EXCEPTION_NONCONTINUABLE_EXCEPTION : ExceptionClass:='NONCONTINUABLE EXCEPTION';
|
|
EXCEPTION_POSSIBLE_DEADLOCK : ExceptionClass:='POSSIBLE DEADLOCK';
|
|
EXCEPTION_PRIV_INSTRUCTION : ExceptionClass:='PRIV INSTRUCTION';
|
|
EXCEPTION_SINGLE_STEP : ExceptionClass:='SINGLE STEP'; // should never be here
|
|
EXCEPTION_STACK_OVERFLOW : ExceptionClass:='STACK OVERFLOW';
|
|
|
|
// add some status - don't know if we can get them here
|
|
{
|
|
DBG_EXCEPTION_NOT_HANDLED : DebugLn(DBG_VERBOSE, 'DBG_EXCEPTION_NOT_HANDLED');
|
|
STATUS_GUARD_PAGE_VIOLATION : DebugLn(DBG_VERBOSE, 'STATUS_GUARD_PAGE_VIOLATION');
|
|
STATUS_NO_MEMORY : DebugLn(DBG_VERBOSE, 'STATUS_NO_MEMORY');
|
|
STATUS_CONTROL_C_EXIT : DebugLn(DBG_VERBOSE, 'STATUS_CONTROL_C_EXIT');
|
|
STATUS_FLOAT_MULTIPLE_FAULTS : DebugLn(DBG_VERBOSE, 'STATUS_FLOAT_MULTIPLE_FAULTS');
|
|
STATUS_FLOAT_MULTIPLE_TRAPS : DebugLn(DBG_VERBOSE, 'STATUS_FLOAT_MULTIPLE_TRAPS');
|
|
STATUS_REG_NAT_CONSUMPTION : DebugLn(DBG_VERBOSE, 'STATUS_REG_NAT_CONSUMPTION');
|
|
STATUS_SXS_EARLY_DEACTIVATION : DebugLn(DBG_VERBOSE, 'STATUS_SXS_EARLY_DEACTIVATION');
|
|
STATUS_SXS_INVALID_DEACTIVATION : DebugLn(DBG_VERBOSE, 'STATUS_SXS_INVALID_DEACTIVATION');
|
|
}
|
|
else
|
|
InterceptAtFirstChance := False;
|
|
ExceptionClass := 'Unknown exception code $' + IntToHex(ExInfo32.ExceptionRecord.ExceptionCode, 8);
|
|
{
|
|
DebugLn(DBG_VERBOSE, ' [');
|
|
case ExInfo32.ExceptionRecord.ExceptionCode and $C0000000 of
|
|
STATUS_SEVERITY_SUCCESS : DebugLn(DBG_VERBOSE, 'SEVERITY_ERROR');
|
|
STATUS_SEVERITY_INFORMATIONAL : DebugLn(DBG_VERBOSE, 'SEVERITY_ERROR');
|
|
STATUS_SEVERITY_WARNING : DebugLn(DBG_VERBOSE, 'SEVERITY_WARNING');
|
|
STATUS_SEVERITY_ERROR : DebugLn(DBG_VERBOSE, 'SEVERITY_ERROR');
|
|
end;
|
|
if ExInfo32.ExceptionRecord.ExceptionCode and $20000000 <> 0
|
|
then DebugLn (DBG_VERBOSE, ' Customer');
|
|
if ExInfo32.ExceptionRecord.ExceptionCode and $10000000 <> 0
|
|
then DebugLn (DBG_VERBOSE, ' Reserved');
|
|
case (ExInfo32.ExceptionRecord.ExceptionCode and $0FFF0000) shr 16 of
|
|
FACILITY_DEBUGGER : DebugLn(DBG_VERBOSE, 'FACILITY_DEBUGGER');
|
|
FACILITY_RPC_RUNTIME : DebugLn(DBG_VERBOSE, 'FACILITY_RPC_RUNTIME');
|
|
FACILITY_RPC_STUBS : DebugLn(DBG_VERBOSE, 'FACILITY_RPC_STUBS');
|
|
FACILITY_IO_ERROR_CODE : DebugLn(DBG_VERBOSE, 'FACILITY_IO_ERROR_CODE');
|
|
FACILITY_TERMINAL_SERVER : DebugLn(DBG_VERBOSE, 'FACILITY_TERMINAL_SERVER');
|
|
FACILITY_USB_ERROR_CODE : DebugLn(DBG_VERBOSE, 'FACILITY_USB_ERROR_CODE');
|
|
FACILITY_HID_ERROR_CODE : DebugLn(DBG_VERBOSE, 'FACILITY_HID_ERROR_CODE');
|
|
FACILITY_FIREWIRE_ERROR_CODE : DebugLn(DBG_VERBOSE, 'FACILITY_FIREWIRE_ERROR_CODE');
|
|
FACILITY_CLUSTER_ERROR_CODE : DebugLn(DBG_VERBOSE, 'FACILITY_CLUSTER_ERROR_CODE');
|
|
FACILITY_ACPI_ERROR_CODE : DebugLn(DBG_VERBOSE, 'FACILITY_ACPI_ERROR_CODE');
|
|
FACILITY_SXS_ERROR_CODE : DebugLn(DBG_VERBOSE, 'FACILITY_SXS_ERROR_CODE');
|
|
else
|
|
DebugLn(DBG_VERBOSE, ' Facility: $', IntToHex((ExInfo32.ExceptionRecord.ExceptionCode and $0FFF0000) shr 16, 3));
|
|
end;
|
|
DebugLn(DBG_VERBOSE, ' Code: $', IntToHex((ExInfo32.ExceptionRecord.ExceptionCode and $0000FFFF), 4));
|
|
}
|
|
end;
|
|
ExceptionClass:='External: '+ExceptionClass;
|
|
ExceptionMessage:='';
|
|
{
|
|
if GMode = dm32
|
|
then Info0 := PtrUInt(ExInfo32.ExceptionRecord.ExceptionAddress)
|
|
else Info0 := PtrUInt(ExInfo64.ExceptionRecord.ExceptionAddress);
|
|
DebugLn(DBG_VERBOSE, ' at: ', FormatAddress(Info0));
|
|
DebugLn(DBG_VERBOSE, ' Flags:', Format('%x', [AEvent.Exception.ExceptionRecord.ExceptionFlags]), ' [');
|
|
|
|
if AEvent.Exception.ExceptionRecord.ExceptionFlags = 0
|
|
then DebugLn(DBG_VERBOSE, 'Continuable')
|
|
else DebugLn(DBG_VERBOSE, 'Not continuable');
|
|
DebugLn(DBG_VERBOSE, ']');
|
|
if GMode = dm32
|
|
then DebugLn(DBG_VERBOSE, ' ParamCount:', IntToStr(ExInfo32.ExceptionRecord.NumberParameters))
|
|
else DebugLn(DBG_VERBOSE, ' ParamCount:', IntToStr(ExInfo64.ExceptionRecord.NumberParameters));
|
|
}
|
|
case AEvent.Exception.ExceptionRecord.ExceptionCode of
|
|
EXCEPTION_ACCESS_VIOLATION: begin
|
|
if GMode = dm32
|
|
then begin
|
|
Info0 := ExInfo32.ExceptionRecord.ExceptionInformation[0];
|
|
Info1 := ExInfo32.ExceptionRecord.ExceptionInformation[1];
|
|
end
|
|
else begin
|
|
Info0 := ExInfo64.ExceptionRecord.ExceptionInformation[0];
|
|
Info1 := ExInfo64.ExceptionRecord.ExceptionInformation[1];
|
|
end;
|
|
Info1Str := FormatAddress(Info1);
|
|
|
|
case Info0 of
|
|
EXCEPTION_READ_FAULT: ExceptionMessage := 'Access violation reading from address ' + Info1Str +'.';
|
|
EXCEPTION_WRITE_FAULT: ExceptionMessage := 'Access violation writing to address ' + Info1Str +'.';
|
|
EXCEPTION_EXECUTE_FAULT: ExceptionMessage := 'Access violation executing address ' + Info1Str +'.';
|
|
end;
|
|
end;
|
|
end;
|
|
{
|
|
DebugLn(DBG_VERBOSE, ' Info: ');
|
|
for n := 0 to EXCEPTION_MAXIMUM_PARAMETERS - 1 do
|
|
begin
|
|
if GMode = dm32
|
|
then Info0 := ExInfo32.ExceptionRecord.ExceptionInformation[n]
|
|
else Info0 := ExInfo64.ExceptionRecord.ExceptionInformation[n];
|
|
DebugLn(DBG_VERBOSE, IntToHex(Info0, DBGPTRSIZE[GMode] * 2), ' ');
|
|
if n and (PARAMCOLS - 1) = (PARAMCOLS - 1)
|
|
then begin
|
|
DebugLn(DBG_VERBOSE, '');
|
|
DebugLn(DBG_VERBOSE, ' ');
|
|
end;
|
|
end;
|
|
DebugLn(DBG_VERBOSE, '');
|
|
}
|
|
end;
|
|
|
|
procedure DumpEvent(const AEvent: String);
|
|
var
|
|
f: Cardinal;
|
|
n: integer;
|
|
begin
|
|
if (DBG_VERBOSE = nil) or (not DBG_VERBOSE^.Enabled) then
|
|
exit;
|
|
DebugLn('===');
|
|
DebugLn(AEvent);
|
|
DebugLn('---');
|
|
DebugLn('Process ID: '+ IntToSTr(MDebugEvent.dwProcessId));
|
|
DebugLn('Thread ID: '+ IntToStr(MDebugEvent.dwThreadId));
|
|
|
|
if AThread = nil then Exit;
|
|
if TDbgWinThread(AThread).FCurrentContext = nil then Exit;
|
|
|
|
{$PUSH}{$R-}
|
|
{$ifdef cpui386}
|
|
with TDbgWinThread(AThread).FCurrentContext^.def do DebugLn(Format('DS: 0x%x, ES: 0x%x, FS: 0x%x, GS: 0x%x', [SegDs, SegEs, SegFs, SegGs]));
|
|
with TDbgWinThread(AThread).FCurrentContext^.def do DebugLn(Format('EAX: 0x%x, EBX: 0x%x, ECX: 0x%x, EDX: 0x%x, EDI: 0x%x, ESI: 0x%x', [Eax, Ebx, Ecx, Edx, Edi, Esi]));
|
|
with TDbgWinThread(AThread).FCurrentContext^.def do DebugLn(Format('CS: 0x%x, SS: 0x%x, EBP: 0x%x, EIP: 0x%x, ESP: 0x%x, EFlags: 0x%x [', [SegCs, SegSs, Ebp, Eip, Esp, EFlags]));
|
|
{$else}
|
|
// TODO: if bitness
|
|
with TDbgWinThread(AThread).FCurrentContext^.def do DebugLn(Format('SegDS: 0x%4.4x, SegES: 0x%4.4x, SegFS: 0x%4.4x, SegGS: 0x%4.4x', [SegDs, SegEs, SegFs, SegGs]));
|
|
with TDbgWinThread(AThread).FCurrentContext^.def do DebugLn(Format('RAX: 0x%16.16x, RBX: 0x%16.16x, RCX: 0x%16.16x, RDX: 0x%16.16x, RDI: 0x%16.16x, RSI: 0x%16.16x, R9: 0x%16.16x, R10: 0x%16.16x, R11: 0x%16.16x, R12: 0x%16.16x, R13: 0x%16.16x, R14: 0x%16.16x, R15: 0x%16.16x', [Rax, Rbx, Rcx, Rdx, Rdi, Rsi, R9, R10, R11, R12, R13, R14, R15]));
|
|
with TDbgWinThread(AThread).FCurrentContext^.def do DebugLn(Format('SegCS: 0x%4.4x, SegSS: 0x%4.4x, RBP: 0x%16.16x, RIP: 0x%16.16x, RSP: 0x%16.16x, EFlags: 0x%8.8x [', [SegCs, SegSs, Rbp, Rip, Rsp, EFlags]));
|
|
{$endif}
|
|
// luckely flag and debug registers are named the same
|
|
with TDbgWinThread(AThread).FCurrentContext^.def do
|
|
begin
|
|
if EFlags and (1 shl 0) <> 0 then DebugLn('CF ');
|
|
if EFlags and (1 shl 2) <> 0 then DebugLn('PF ');
|
|
if EFlags and (1 shl 4) <> 0 then DebugLn('AF ');
|
|
if EFlags and (1 shl 6) <> 0 then DebugLn('ZF ');
|
|
if EFlags and (1 shl 7) <> 0 then DebugLn('SF ');
|
|
if EFlags and (1 shl 8) <> 0 then DebugLn('TF ');
|
|
if EFlags and (1 shl 9) <> 0 then DebugLn('IF ');
|
|
if EFlags and (1 shl 10) <> 0 then DebugLn('DF ');
|
|
if EFlags and (1 shl 11) <> 0 then DebugLn('OF ');
|
|
if (EFlags shr 12) and 3 <> 0 then DebugLn('IOPL=', IntToSTr((EFlags shr 12) and 3));
|
|
if EFlags and (1 shl 14) <> 0 then DebugLn('NT ');
|
|
if EFlags and (1 shl 16) <> 0 then DebugLn('RF ');
|
|
if EFlags and (1 shl 17) <> 0 then DebugLn('VM ');
|
|
if EFlags and (1 shl 18) <> 0 then DebugLn('AC ');
|
|
if EFlags and (1 shl 19) <> 0 then DebugLn('VIF ');
|
|
if EFlags and (1 shl 20) <> 0 then DebugLn('VIP ');
|
|
if EFlags and (1 shl 21) <> 0 then DebugLn('ID ');
|
|
DebugLn(']');
|
|
|
|
DebugLn(Format('DR0: 0x%x, DR1: 0x%x, DR2: 0x%x, DR3: 0x%x', [Dr0, Dr1, Dr2, Dr3]));
|
|
DebugLn(' DR6: 0x', IntToHex(Dr6, SizeOf(Pointer) * 2), ' [');
|
|
if Dr6 and $0001 <> 0 then DebugLn('B0 ');
|
|
if Dr6 and $0002 <> 0 then DebugLn('B1 ');
|
|
if Dr6 and $0004 <> 0 then DebugLn('B2 ');
|
|
if Dr6 and $0008 <> 0 then DebugLn('B3 ');
|
|
if Dr6 and $2000 <> 0 then DebugLn('BD ');
|
|
if Dr6 and $4000 <> 0 then DebugLn('BS ');
|
|
if Dr6 and $8000 <> 0 then DebugLn('BT ');
|
|
DebugLn('] DR7: 0x', IntToHex(Dr7, SizeOf(Pointer) * 2), ' [');
|
|
if Dr7 and $01 <> 0 then DebugLn('L0 ');
|
|
if Dr7 and $02 <> 0 then DebugLn('G0 ');
|
|
if Dr7 and $04 <> 0 then DebugLn('L1 ');
|
|
if Dr7 and $08 <> 0 then DebugLn('G1 ');
|
|
if Dr7 and $10 <> 0 then DebugLn('L2 ');
|
|
if Dr7 and $20 <> 0 then DebugLn('G2 ');
|
|
if Dr7 and $40 <> 0 then DebugLn('L3 ');
|
|
if Dr7 and $80 <> 0 then DebugLn('G3 ');
|
|
if Dr7 and $100 <> 0 then DebugLn('LE ');
|
|
if Dr7 and $200 <> 0 then DebugLn('GE ');
|
|
if Dr7 and $2000 <> 0 then DebugLn('GD ');
|
|
f := Dr7 shr 16;
|
|
for n := 0 to 3 do
|
|
begin
|
|
DebugLn('R/W', IntToSTr(n),':');
|
|
case f and 3 of
|
|
0: DebugLn('ex');
|
|
1: DebugLn('wo');
|
|
2: DebugLn('IO');
|
|
3: DebugLn('rw');
|
|
end;
|
|
f := f shr 2;
|
|
DebugLn(' LEN', IntToSTr(n),':', IntToSTr(f and 3 + 1), ' ');
|
|
f := f shr 2;
|
|
end;
|
|
DebugLn(']');
|
|
end;
|
|
DebugLn('---');
|
|
{$POP}
|
|
end;
|
|
|
|
procedure HandleOutputDebug(const AEvent: TDebugEvent);
|
|
var
|
|
S: String;
|
|
W: WideString;
|
|
begin
|
|
if AEvent.DebugString.fUnicode <> 0
|
|
then begin
|
|
if not ReadWString(TDbgPtr(AEvent.DebugString.lpDebugStringData), AEvent.DebugString.nDebugStringLength, W)
|
|
then Exit;
|
|
S := W;
|
|
end
|
|
else begin
|
|
if not ReadString(TDbgPtr(AEvent.DebugString.lpDebugStringData), AEvent.DebugString.nDebugStringLength, S)
|
|
then Exit;
|
|
end;
|
|
DebugLn(DBG_VERBOSE, '[%d:%d]: %s', [AEvent.dwProcessId, AEvent.dwThreadId, S]);
|
|
if OnDebugOutputEvent <> nil then
|
|
OnDebugOutputEvent(Self, AEvent.dwProcessId, AEvent.dwThreadId, S);
|
|
end;
|
|
|
|
const
|
|
EXCEPTION_SET_THREADNAME = $406D1388;
|
|
var
|
|
InterceptAtFirst: Boolean;
|
|
threadname: String;
|
|
t: TDbgThread;
|
|
Lib: TDbgLibrary;
|
|
FpProc: TFpSymbol;
|
|
begin
|
|
if AThread <> nil then
|
|
TDbgWinThread(AThread).EndSingleStepOverBreakPoint;
|
|
|
|
if HandleDebugEvent(MDebugEvent)
|
|
then result := deBreakpoint // unreachable
|
|
else begin
|
|
|
|
case MDebugEvent.dwDebugEventCode of
|
|
EXCEPTION_DEBUG_EVENT: begin
|
|
//DumpEvent('EXCEPTION_DEBUG_EVENT');
|
|
case MDebugEvent.Exception.ExceptionRecord.ExceptionCode of
|
|
EXCEPTION_BREAKPOINT, STATUS_WX86_BREAKPOINT: begin
|
|
if FJustStarted and (MDebugEvent.Exception.dwFirstChance <> 0) and (MDebugEvent.Exception.ExceptionRecord.ExceptionFlags = 0) then
|
|
begin
|
|
FJustStarted:=false;
|
|
result := deInternalContinue;
|
|
end
|
|
else
|
|
if (AThread <> nil) and (TDbgWinThread(AThread).FIgnoreNextInt3) then begin
|
|
result := deInternalContinue; // pause request
|
|
TDbgWinThread(AThread).FIgnoreNextInt3 := False;
|
|
end
|
|
else begin
|
|
result := deBreakpoint;
|
|
if AThread <> nil then
|
|
TDbgWinThread(AThread).ResetInstructionPointerAfterBreakpoint; // This is always an int3 breakpoint
|
|
end;
|
|
end;
|
|
EXCEPTION_SINGLE_STEP, STATUS_WX86_SINGLE_STEP: begin
|
|
// includes WatchPoints
|
|
result := deBreakpoint;
|
|
end;
|
|
EXCEPTION_SET_THREADNAME: begin
|
|
if AThread <> nil then begin
|
|
if not ReadString(TDbgPtr(MDebugEvent.Exception.ExceptionRecord.ExceptionInformation[1]), 200, threadname) then
|
|
threadname := 'error getting threadname';
|
|
t := AThread;
|
|
with MDebugEvent.Exception.ExceptionRecord do begin
|
|
if (NumberParameters >= 3) and
|
|
((ExceptionInformation[0] and $ffffffff) = $1000) and
|
|
(TThreadID(ExceptionInformation[2]) <> 0) and
|
|
(TThreadID(ExceptionInformation[2]) <> TThreadID(-1))
|
|
then begin
|
|
if not GetThread(Integer(ExceptionInformation[2]), t) then
|
|
t := nil;
|
|
end;
|
|
end;
|
|
if t <> nil then begin
|
|
with TDbgWinThread(t) do begin
|
|
FName := threadname;
|
|
FDoNotPollName := True;
|
|
end;
|
|
end else
|
|
with MDebugEvent.Exception.ExceptionRecord do
|
|
begin
|
|
FThreadNameList.AddThread(TThreadID(ExceptionInformation[2]),threadname);
|
|
end;
|
|
end;
|
|
result := deInternalContinue;
|
|
end
|
|
else begin
|
|
HandleException(MDebugEvent, InterceptAtFirst);
|
|
if (MDebugEvent.Exception.dwFirstChance = 1) and (not InterceptAtFirst) then
|
|
result := deInternalContinue // might be an SEH exception
|
|
else
|
|
result := deException;
|
|
end;
|
|
end;
|
|
end;
|
|
CREATE_THREAD_DEBUG_EVENT: begin
|
|
//DumpEvent('CREATE_THREAD_DEBUG_EVENT');
|
|
result := deInternalContinue;
|
|
if PauseRequested then begin
|
|
if FDbgUiRemoteBreakin = 0 then begin
|
|
FDbgUiRemoteBreakin := TDBGPtr(-1);
|
|
for Lib in LibMap do
|
|
if (lowercase(Lib.Name) = 'ntdll.dll') or
|
|
(lowercase(copy(Lib.Name, length(Lib.Name)-9, 10)) = '\ntdll.dll')
|
|
then begin
|
|
FpProc := Lib.SymbolTableInfo.FindProcSymbol('DbgUiRemoteBreakin', True);
|
|
if (FpProc <> nil) and (FpProc.Address.Address <> 0) then begin
|
|
FDbgUiRemoteBreakin := FpProc.Address.Address;
|
|
FpProc.ReleaseReference;
|
|
end;
|
|
break;
|
|
end;
|
|
end;
|
|
if (FDbgUiRemoteBreakin <> TDBGPtr(-1)) and (TDBGPtr(MDebugEvent.CreateThread.lpStartAddress) = FDbgUiRemoteBreakin) and (AThread <> nil) then
|
|
TDbgWinThread(AThread).FIgnoreNextInt3 := True;
|
|
end;
|
|
end;
|
|
CREATE_PROCESS_DEBUG_EVENT: begin
|
|
//DumpEvent('CREATE_PROCESS_DEBUG_EVENT');
|
|
if MDebugEvent.dwProcessId = TDbgWinThread(AThread).Process.ProcessID then begin
|
|
//main process
|
|
StartProcess(MDebugEvent.dwThreadId, MDebugEvent.CreateProcessInfo); // hfile will be closed by TDbgImageLoader
|
|
FJustStarted := true;
|
|
result := deCreateProcess;
|
|
end
|
|
else begin
|
|
//child process: ignore
|
|
// we currently do not use the file handle => close it
|
|
if MDebugEvent.CreateProcessInfo.hFile <> 0 then
|
|
CloseHandle(MDebugEvent.CreateProcessInfo.hFile);
|
|
result := deInternalContinue;
|
|
end;
|
|
end;
|
|
EXIT_THREAD_DEBUG_EVENT: begin
|
|
//DumpEvent('EXIT_THREAD_DEBUG_EVENT');
|
|
result := deInternalContinue;
|
|
end;
|
|
EXIT_PROCESS_DEBUG_EVENT: begin
|
|
//DumpEvent('EXIT_PROCESS_DEBUG_EVENT');
|
|
SetExitCode(MDebugEvent.ExitProcess.dwExitCode);
|
|
// Let the kernel close all debug-handles and close-up the
|
|
// debuggee.
|
|
FThreadNameList.ClearThread;
|
|
Windows.ContinueDebugEvent(MDebugEvent.dwProcessId, MDebugEvent.dwThreadId, DBG_CONTINUE);
|
|
result := deExitProcess;
|
|
end;
|
|
LOAD_DLL_DEBUG_EVENT: begin
|
|
//DumpEvent('LOAD_DLL_DEBUG_EVENT');
|
|
result := deLoadLibrary;
|
|
end;
|
|
UNLOAD_DLL_DEBUG_EVENT: begin
|
|
//DumpEvent('UNLOAD_DLL_DEBUG_EVENT');
|
|
result := deUnloadLibrary;
|
|
end;
|
|
OUTPUT_DEBUG_STRING_EVENT: begin
|
|
//DumpEvent('OUTPUT_DEBUG_STRING_EVENT');
|
|
HandleOutputDebug(MDebugEvent);
|
|
result := deInternalContinue;
|
|
end;
|
|
RIP_EVENT: begin
|
|
//DumpEvent('RIP_EVENT');
|
|
result := deInternalContinue;
|
|
end
|
|
else begin
|
|
raise Exception.CreateFmt('Unknown dwDebugEventCode value %d',[MDebugEvent.dwDebugEventCode]);
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
|
|
var
|
|
threadname: string;
|
|
begin
|
|
case MDebugEvent.dwDebugEventCode of
|
|
CREATE_THREAD_DEBUG_EVENT :
|
|
begin
|
|
result := OSDbgClasses.DbgThreadClass.Create(Self, AThreadIdentifier, MDebugEvent.CreateThread.hThread);
|
|
IsMainThread := false;
|
|
end;
|
|
CREATE_PROCESS_DEBUG_EVENT :
|
|
begin
|
|
result := OSDbgClasses.DbgThreadClass.Create(Self, AThreadIdentifier, MDebugEvent.CreateProcessInfo.hThread);
|
|
IsMainThread := true;
|
|
end
|
|
else
|
|
result := nil;
|
|
end; {case}
|
|
|
|
if (result<>nil) then
|
|
begin
|
|
threadname:=FThreadNameList.FetchThread(result.ID);
|
|
if (threadname<>'') then
|
|
with TDbgWinThread(result) do
|
|
begin
|
|
FName := threadname;
|
|
FDoNotPollName := True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgWinProcess.StartProcess(const AThreadID: DWORD;const AInfo: TCreateProcessDebugInfo);
|
|
var
|
|
s: string;
|
|
{$ifNdef cpui386}
|
|
b: BOOL;
|
|
{$endif}
|
|
begin
|
|
FInfo := AInfo;
|
|
if ThreadID = 0 then
|
|
SetThreadId(AThreadID);
|
|
{$ifdef cpui386}
|
|
FBitness := b32; // only 32 bit supported
|
|
{$else}
|
|
if (_IsWow64Process <> nil) and _IsWow64Process(GetHandle, @b) then begin
|
|
if b then
|
|
FBitness := b32
|
|
else
|
|
FBitness := b64;
|
|
end
|
|
else
|
|
FBitness := b64;
|
|
{$endif}
|
|
|
|
s := GetProcFilename(Self, AInfo.lpImageName, AInfo.fUnicode, 0);
|
|
if s <> ''
|
|
then SetFileName(s);
|
|
end;
|
|
|
|
function TDbgWinProcess.Pause: boolean;
|
|
var
|
|
hndl: Handle;
|
|
hThread: THandle;
|
|
NewThreadId: Cardinal;
|
|
begin
|
|
//hndl := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or PROCESS_VM_WRITE or PROCESS_VM_READ, False, TargetPID);
|
|
hndl := OpenProcess(PROCESS_ALL_ACCESS, false, ProcessID);
|
|
PauseRequested:=true;
|
|
Result := False;
|
|
if _DebugBreakProcess <> nil then
|
|
Result := _DebugBreakProcess(hndl);
|
|
if not Result then begin
|
|
DebugLn(DBG_WARNINGS, ['pause failed(1) ', GetLastError]);
|
|
if (_CreateRemoteThread <> nil) and (DebugBreakAddr <> nil) then begin
|
|
hThread := _CreateRemoteThread(hndl, nil, 0, DebugBreakAddr, nil, 0, NewThreadId);
|
|
if hThread = 0 then begin
|
|
DebugLn(DBG_WARNINGS, ['pause failed(2) ', GetLastError]);
|
|
end
|
|
else begin
|
|
Result := True;
|
|
CloseHandle(hThread);
|
|
end;
|
|
end;
|
|
end;
|
|
CloseHandle(hndl);
|
|
end;
|
|
|
|
procedure TDbgWinProcess.TerminateProcess;
|
|
begin
|
|
Windows.TerminateProcess(Handle, 0);
|
|
FTerminated := True;
|
|
FThreadNameList.ClearThread;
|
|
end;
|
|
|
|
function TDbgWinProcess.AddLib(const AInfo: TLoadDLLDebugInfo): TDbgLibrary;
|
|
begin
|
|
Result := TDbgWinLibrary.Create(Self, HexValue(AInfo.lpBaseOfDll, SizeOf(Pointer), [hvfIncludeHexchar]), AInfo.hFile, AInfo);
|
|
AddLibrary(Result, TDbgPtr(AInfo.lpBaseOfDll));
|
|
end;
|
|
|
|
procedure TDbgWinProcess.RemoveLib(const AInfo: TUnloadDLLDebugInfo);
|
|
var
|
|
Lib: TDbgLibrary;
|
|
ID: TDbgPtr;
|
|
begin
|
|
if FLibMap = nil then Exit;
|
|
ID := TDbgPtr(AInfo.lpBaseOfDll);
|
|
if not FLibMap.GetData(ID, Lib) then Exit;
|
|
FSymInstances.Remove(Lib);
|
|
FLibMap.Delete(ID);
|
|
end;
|
|
|
|
{ TDbgWinThread }
|
|
|
|
procedure TDbgWinThread.LoadRegisterValues;
|
|
{$IF FPC_Fullversion>30202}{$ifNdef cpui386}
|
|
type
|
|
PExtended = ^floatx80;
|
|
{$endif}{$ENDIF}
|
|
const
|
|
M128A_NULL: M128A = (Low: 0; High: 0; );
|
|
var
|
|
Context: PCONTEXT;
|
|
ContextSize: DWord;
|
|
Buffer, Buffer2: Pointer;
|
|
FeatureMask: DWORD64;
|
|
Xmm, Ymm: PM128A;
|
|
FeatureLength, FeatureLength2: DWORD;
|
|
i: Integer;
|
|
EM, SEM: TFPUExceptionMask;
|
|
r: TDbgRegisterValue;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinThread.LoadRegisterValues');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinThread.LoadRegisterValues: MDebugEvent.dwProcessId <> 0');
|
|
|
|
if FCurrentContext = nil then
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
EM := GetExceptionMask;
|
|
{$IF FPC_Fullversion>30202}{$ifNdef cpui386}
|
|
SEM := softfloat_exception_mask;
|
|
{$endif}{$ENDIF}
|
|
try
|
|
SetExceptionMask(EM + [exInvalidOp, exDenormalized, exZeroDivide, exOverflow, exUnderflow, exPrecision]);
|
|
{$IF FPC_Fullversion>30202}{$ifNdef cpui386}
|
|
softfloat_exception_mask := SEM + [exInvalidOp, exDenormalized, exZeroDivide,exOverflow, exUnderflow, exPrecision];
|
|
{$endif}{$ENDIF}
|
|
|
|
{$ifdef cpui386}
|
|
with FCurrentContext^.def do
|
|
begin
|
|
FRegisterValueList.DbgRegisterAutoCreate['eax'].SetValue(Eax, IntToStr(Eax),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ecx'].SetValue(Ecx, IntToStr(Ecx),4,1);
|
|
FRegisterValueList.DbgRegisterAutoCreate['edx'].SetValue(Edx, IntToStr(Edx),4,2);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ebx'].SetValue(Ebx, IntToStr(Ebx),4,3);
|
|
FRegisterValueList.DbgRegisterAutoCreate['esp'].SetValue(Esp, IntToStr(Esp),4,4);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ebp'].SetValue(Ebp, IntToStr(Ebp),4,5);
|
|
FRegisterValueList.DbgRegisterAutoCreate['esi'].SetValue(Esi, IntToStr(Esi),4,6);
|
|
FRegisterValueList.DbgRegisterAutoCreate['edi'].SetValue(Edi, IntToStr(Edi),4,7);
|
|
FRegisterValueList.DbgRegisterAutoCreate['eip'].SetValue(Eip, IntToStr(Eip),4,8);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['eflags'].Setx86EFlagsValue(EFlags);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['cs'].SetValue(SegCs, IntToStr(SegCs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ss'].SetValue(SegSs, IntToStr(SegSs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ds'].SetValue(SegDs, IntToStr(SegDs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['es'].SetValue(SegEs, IntToStr(SegEs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fs'].SetValue(SegFs, IntToStr(SegFs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['gs'].SetValue(SegGs, IntToStr(SegGs),4,0);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['st0'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[ 0])^),10,500);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st1'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[10])^),10,501);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st2'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[20])^),10,502);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st3'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[30])^),10,503);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st4'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[40])^),10,504);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st5'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[50])^),10,505);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st6'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[60])^),10,506);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st7'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[70])^),10,507);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['fctrl' ].SetValue(FloatSave.ControlWord, IntToStr(FloatSave.ControlWord),2,510);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fstat' ].SetValue(FloatSave.StatusWord, IntToStr(FloatSave.StatusWord),2,511);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ftag' ].SetValue(FloatSave.TagWord, IntToStr(FloatSave.TagWord),1,512);
|
|
//FRegisterValueList.DbgRegisterAutoCreate['fErrOp' ].SetValue(FloatSave.ErrorOpcode, IntToStr(FloatSave.ErrorOpcode),2,513);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fErrOff'].SetValue(FloatSave.ErrorOffset, IntToStr(FloatSave.ErrorOffset),4,514);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fErrSel'].SetValue(FloatSave.ErrorSelector, IntToStr(FloatSave.ErrorSelector),2,515);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fDatOff'].SetValue(FloatSave.DataOffset, IntToStr(FloatSave.DataOffset),4,516);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fDatSel'].SetValue(FloatSave.DataSelector, IntToStr(FloatSave.DataSelector),2,517);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fCr0NpxSt'].SetValue(FloatSave.Cr0NpxState, IntToStr(FloatSave.Cr0NpxState),4,518);
|
|
|
|
if not FFailed_CONTEXT_EXTENDED_REGISTERS then begin
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm0'].SetValue(@ExtendedRegisters[10*16],16,600, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm1'].SetValue(@ExtendedRegisters[11*16],16,601, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm2'].SetValue(@ExtendedRegisters[12*16],16,602, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm3'].SetValue(@ExtendedRegisters[13*16],16,603, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm4'].SetValue(@ExtendedRegisters[14*16],16,604, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm5'].SetValue(@ExtendedRegisters[15*16],16,605, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm6'].SetValue(@ExtendedRegisters[16*16],16,606, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm7'].SetValue(@ExtendedRegisters[17*16],16,607, @XmmToFormat);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['MxCsr'].SetValue(PDWORD(@ExtendedRegisters[24])^, IntToStr(PDWORD(@ExtendedRegisters[24])^),4,620);
|
|
end;
|
|
end;
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
|
with FCurrentContext^.WOW do
|
|
begin
|
|
FRegisterValueList.DbgRegisterAutoCreate['eax'].SetValue(Eax, IntToStr(Eax),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ecx'].SetValue(Ecx, IntToStr(Ecx),4,1);
|
|
FRegisterValueList.DbgRegisterAutoCreate['edx'].SetValue(Edx, IntToStr(Edx),4,2);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ebx'].SetValue(Ebx, IntToStr(Ebx),4,3);
|
|
FRegisterValueList.DbgRegisterAutoCreate['esp'].SetValue(Esp, IntToStr(Esp),4,4);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ebp'].SetValue(Ebp, IntToStr(Ebp),4,5);
|
|
FRegisterValueList.DbgRegisterAutoCreate['esi'].SetValue(Esi, IntToStr(Esi),4,6);
|
|
FRegisterValueList.DbgRegisterAutoCreate['edi'].SetValue(Edi, IntToStr(Edi),4,7);
|
|
FRegisterValueList.DbgRegisterAutoCreate['eip'].SetValue(Eip, IntToStr(Eip),4,8);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['eflags'].Setx86EFlagsValue(EFlags);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['cs'].SetValue(SegCs, IntToStr(SegCs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ss'].SetValue(SegSs, IntToStr(SegSs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ds'].SetValue(SegDs, IntToStr(SegDs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['es'].SetValue(SegEs, IntToStr(SegEs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fs'].SetValue(SegFs, IntToStr(SegFs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['gs'].SetValue(SegGs, IntToStr(SegGs),4,0);
|
|
|
|
// TODO: 64bit extended is not 10 byte // currently downgrading to double
|
|
{$IF FPC_Fullversion>30202}
|
|
FRegisterValueList.DbgRegisterAutoCreate['st0'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[ 0])^),10,500);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st1'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[10])^),10,501);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st2'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[20])^),10,502);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st3'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[30])^),10,503);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st4'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[40])^),10,504);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st5'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[50])^),10,505);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st6'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[60])^),10,506);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st7'].SetValue(0, FloatToStr(PExtended(@FloatSave.RegisterArea[70])^),10,507);
|
|
{$ENDIF}
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['fctrl' ].SetValue(FloatSave.ControlWord, IntToStr(FloatSave.ControlWord),2,510);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fstat' ].SetValue(FloatSave.StatusWord, IntToStr(FloatSave.StatusWord),2,511);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ftag' ].SetValue(FloatSave.TagWord, IntToStr(FloatSave.TagWord),1,512);
|
|
//FRegisterValueList.DbgRegisterAutoCreate['fErrOp' ].SetValue(FloatSave.ErrorOpcode, IntToStr(FloatSave.ErrorOpcode),2,513);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fErrOff'].SetValue(FloatSave.ErrorOffset, IntToStr(FloatSave.ErrorOffset),4,514);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fErrSel'].SetValue(FloatSave.ErrorSelector, IntToStr(FloatSave.ErrorSelector),2,515);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fDatOff'].SetValue(FloatSave.DataOffset, IntToStr(FloatSave.DataOffset),4,516);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fDatSel'].SetValue(FloatSave.DataSelector, IntToStr(FloatSave.DataSelector),2,517);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fCr0NpxSt'].SetValue(FloatSave.Cr0NpxState, IntToStr(FloatSave.Cr0NpxState),4,518);
|
|
|
|
if not FFailed_CONTEXT_EXTENDED_REGISTERS then begin
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm0'].SetValue(@ExtendedRegisters[10*16],16,600, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm1'].SetValue(@ExtendedRegisters[11*16],16,601, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm2'].SetValue(@ExtendedRegisters[12*16],16,602, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm3'].SetValue(@ExtendedRegisters[13*16],16,603, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm4'].SetValue(@ExtendedRegisters[14*16],16,604, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm5'].SetValue(@ExtendedRegisters[15*16],16,605, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm6'].SetValue(@ExtendedRegisters[16*16],16,606, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm7'].SetValue(@ExtendedRegisters[17*16],16,607, @XmmToFormat);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['MxCsr'].SetValue(PDWORD(@ExtendedRegisters[24])^, IntToStr(PDWORD(@ExtendedRegisters[24])^),4,620);
|
|
end;
|
|
end
|
|
else
|
|
with FCurrentContext^.def do
|
|
begin
|
|
FRegisterValueList.DbgRegisterAutoCreate['rax'].SetValue(rax, IntToStr(rax),8,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rbx'].SetValue(rbx, IntToStr(rbx),8,3);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rcx'].SetValue(rcx, IntToStr(rcx),8,2);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rdx'].SetValue(rdx, IntToStr(rdx),8,1);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rsi'].SetValue(rsi, IntToStr(rsi),8,4);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rdi'].SetValue(rdi, IntToStr(rdi),8,5);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rbp'].SetValue(rbp, IntToStr(rbp),8,6);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rsp'].SetValue(rsp, IntToStr(rsp),8,7);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['r8'].SetValue(r8, IntToStr(r8),8,8);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r9'].SetValue(r9, IntToStr(r9),8,9);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r10'].SetValue(r10, IntToStr(r10),8,10);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r11'].SetValue(r11, IntToStr(r11),8,11);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r12'].SetValue(r12, IntToStr(r12),8,12);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r13'].SetValue(r13, IntToStr(r13),8,13);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r14'].SetValue(r14, IntToStr(r14),8,14);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r15'].SetValue(r15, IntToStr(r15),8,15);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['rip'].SetValue(rip, IntToStr(rip),8,16);
|
|
FRegisterValueList.DbgRegisterAutoCreate['eflags'].Setx86EFlagsValue(EFlags);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['cs'].SetValue(SegCs, IntToStr(SegCs),8,43);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ss'].SetValue(SegSs, IntToStr(SegSs),8,44);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ds'].SetValue(SegDs, IntToStr(SegDs),8,45);
|
|
FRegisterValueList.DbgRegisterAutoCreate['es'].SetValue(SegEs, IntToStr(SegEs),8,42);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fs'].SetValue(SegFs, IntToStr(SegFs),8,46);
|
|
FRegisterValueList.DbgRegisterAutoCreate['gs'].SetValue(SegGs, IntToStr(SegGs),8,47);
|
|
|
|
// TODO: 64bit extended is not 10 byte // currently downgrading to double
|
|
{$IF FPC_Fullversion>30202}
|
|
FRegisterValueList.DbgRegisterAutoCreate['st0'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[0])^),10,500);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st1'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[1])^),10,501);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st2'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[2])^),10,502);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st3'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[3])^),10,503);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st4'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[4])^),10,504);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st5'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[5])^),10,505);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st6'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[6])^),10,506);
|
|
FRegisterValueList.DbgRegisterAutoCreate['st7'].SetValue(0, FloatToStr(PExtended(@FltSave.FloatRegisters[7])^),10,507);
|
|
{$ENDIF}
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['fctrl' ].SetValue(FltSave.ControlWord, IntToStr(FltSave.ControlWord),2,510);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fstat' ].SetValue(FltSave.StatusWord, IntToStr(FltSave.StatusWord),2,511);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ftag' ].SetValue(FltSave.TagWord, IntToStr(FltSave.TagWord),1,512);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fErrOp' ].SetValue(FltSave.ErrorOpcode, IntToStr(FltSave.ErrorOpcode),2,513);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fErrOff'].SetValue(FltSave.ErrorOffset, IntToStr(FltSave.ErrorOffset),4,514);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fErrSel'].SetValue(FltSave.ErrorSelector, IntToStr(FltSave.ErrorSelector),2,515);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fDatOff'].SetValue(FltSave.DataOffset, IntToStr(FltSave.DataOffset),4,516);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fDatSel'].SetValue(FltSave.DataSelector, IntToStr(FltSave.DataSelector),2,517);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm0' ].SetValue(@FltSave.XmmRegisters[ 0],16,600, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm1' ].SetValue(@FltSave.XmmRegisters[ 1],16,601, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm2' ].SetValue(@FltSave.XmmRegisters[ 2],16,602, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm3' ].SetValue(@FltSave.XmmRegisters[ 3],16,603, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm4' ].SetValue(@FltSave.XmmRegisters[ 4],16,604, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm5' ].SetValue(@FltSave.XmmRegisters[ 5],16,605, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm6' ].SetValue(@FltSave.XmmRegisters[ 6],16,606, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm7' ].SetValue(@FltSave.XmmRegisters[ 7],16,607, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm8' ].SetValue(@FltSave.XmmRegisters[ 8],16,608, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm9' ].SetValue(@FltSave.XmmRegisters[ 9],16,609, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm10'].SetValue(@FltSave.XmmRegisters[10],16,610, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm11'].SetValue(@FltSave.XmmRegisters[11],16,611, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm12'].SetValue(@FltSave.XmmRegisters[12],16,612, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm13'].SetValue(@FltSave.XmmRegisters[13],16,613, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm14'].SetValue(@FltSave.XmmRegisters[14],16,614, @XmmToFormat);
|
|
FRegisterValueList.DbgRegisterAutoCreate['Xmm15'].SetValue(@FltSave.XmmRegisters[15],16,615, @XmmToFormat);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['MxCsr'].SetValue(FltSave.MxCsr, IntToStr(FltSave.MxCsr),4,620);
|
|
FRegisterValueList.DbgRegisterAutoCreate['MxCsrM'].SetValue(FltSave.MxCsr_Mask, IntToStr(FltSave.MxCsr_Mask),4,621);
|
|
end;
|
|
{$endif} // 64bit
|
|
|
|
if _GetEnabledXStateFeatures <> nil then begin
|
|
ContextSize := 0;
|
|
|
|
if _InitializeContext(nil, CONTEXT_ALL or CONTEXT_XSTATE, nil, @ContextSize) or
|
|
(GetLastError <> ERROR_INSUFFICIENT_BUFFER)
|
|
then
|
|
exit;
|
|
|
|
Buffer := AllocMem(ContextSize+$40);
|
|
if Buffer = nil then
|
|
exit;
|
|
Buffer2 := AlignPtr(Buffer, $40);
|
|
|
|
try
|
|
if not _InitializeContext(Buffer2, CONTEXT_ALL or CONTEXT_XSTATE, @Context, @ContextSize) then
|
|
exit;
|
|
if not _SetXStateFeaturesMask(Context, XSTATE_MASK_AVX) then
|
|
exit;
|
|
if not GetThreadContext(Handle, Context^) then // context is VAR PARAM
|
|
exit;
|
|
|
|
Xmm := _LocateXStateFeature(Context, XSTATE_LEGACY_SSE, @FeatureLength);
|
|
Ymm := _LocateXStateFeature(Context, XSTATE_AVX, @FeatureLength2);
|
|
if (Xmm = nil) or (Ymm = nil) or (FeatureLength2 = 0) then
|
|
exit;
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) and (FeatureLength > 8 * SizeOf(M128A)) then
|
|
FeatureLength := 8 * SizeOf(M128A);
|
|
{$endif}
|
|
|
|
if (_GetXStateFeaturesMask(Context, @FeatureMask)) and
|
|
((FeatureMask and XSTATE_MASK_AVX) = 0)
|
|
then begin
|
|
// AVX not init yet // upper half must be 0
|
|
for i := 0 to FeatureLength div SizeOf(M128A) - 1 do begin
|
|
r := FRegisterValueList.DbgRegisterAutoCreate['Ymm'+IntToStr(i)];
|
|
r.SetValue(@Xmm[i],32,700+i, @YmmToFormat);
|
|
FillByte(PByte(r.Data+16)^, 16, 0);
|
|
end;
|
|
end
|
|
else begin
|
|
for i := 0 to FeatureLength div SizeOf(M128A) - 1 do begin
|
|
r := FRegisterValueList.DbgRegisterAutoCreate['Ymm'+IntToStr(i)];
|
|
r.SetValue(@Xmm[i],32,700+i, @YmmToFormat);
|
|
move(Ymm[i], PByte(r.Data+16)^, 16);
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
Freemem(Buffer);
|
|
end;
|
|
end;
|
|
|
|
finally
|
|
FRegisterValueListValid:=true;
|
|
SetExceptionMask(EM);
|
|
{$IF FPC_Fullversion>30202}{$ifNdef cpui386}
|
|
softfloat_exception_mask := SEM;
|
|
{$endif}{$ENDIF}
|
|
end;
|
|
end;
|
|
|
|
function TDbgWinThread.GetFpThreadContext(var AStorage: TFpContext; out
|
|
ACtxPtr: PFpContext; ACtxFlags: TFpWinCtxFlags): Boolean;
|
|
begin
|
|
ACtxPtr := AlignPtr(@AStorage, $10);
|
|
|
|
if not FFailed_CONTEXT_EXTENDED_REGISTERS then begin
|
|
SetLastError(0);
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
case ACtxFlags of
|
|
cfControl: ACtxPtr^.WOW.ContextFlags := WOW64_CONTEXT_CONTROL;
|
|
cfFull: ACtxPtr^.WOW.ContextFlags := WOW64_CONTEXT_SEGMENTS or WOW64_CONTEXT_INTEGER or WOW64_CONTEXT_CONTROL or WOW64_CONTEXT_DEBUG_REGISTERS or WOW64_CONTEXT_FLOATING_POINT or WOW64_CONTEXT_EXTENDED_REGISTERS;
|
|
end;
|
|
Result := (_Wow64GetThreadContext <> nil) and _Wow64GetThreadContext(Handle, ACtxPtr^.WOW);
|
|
end
|
|
else begin
|
|
{$endif}
|
|
case ACtxFlags of
|
|
cfControl: ACtxPtr^.def.ContextFlags := CONTEXT_CONTROL;
|
|
{$ifdef cpui386}
|
|
cfFull: ACtxPtr^.def.ContextFlags := CONTEXT_SEGMENTS or CONTEXT_INTEGER or CONTEXT_CONTROL or CONTEXT_DEBUG_REGISTERS or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS;
|
|
{$else}
|
|
cfFull: ACtxPtr^.def.ContextFlags := CONTEXT_SEGMENTS or CONTEXT_INTEGER or CONTEXT_CONTROL or CONTEXT_DEBUG_REGISTERS or CONTEXT_FLOATING_POINT;
|
|
{$endif}
|
|
end;
|
|
(* or CONTEXT_FLOATING_POINT or CONTEXT_EXTENDED_REGISTERS *)
|
|
Result := GetThreadContext(Handle, ACtxPtr^.def_w);
|
|
{$ifdef cpux86_64}
|
|
end;
|
|
{$endif}
|
|
if GetLastError <> 0 then
|
|
FFailed_CONTEXT_EXTENDED_REGISTERS := True;
|
|
DebugLn(DBG_WARNINGS and (not Result), ['Unable to get Context for ', ID, ': ', GetLastErrorText, ' ', FFailed_CONTEXT_EXTENDED_REGISTERS]);
|
|
end;
|
|
|
|
if FFailed_CONTEXT_EXTENDED_REGISTERS then begin
|
|
|
|
SetLastError(0);
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
case ACtxFlags of
|
|
cfControl: ACtxPtr^.WOW.ContextFlags := WOW64_CONTEXT_CONTROL;
|
|
cfFull: ACtxPtr^.WOW.ContextFlags := WOW64_CONTEXT_SEGMENTS or WOW64_CONTEXT_INTEGER or WOW64_CONTEXT_CONTROL or WOW64_CONTEXT_DEBUG_REGISTERS or WOW64_CONTEXT_FLOATING_POINT;
|
|
end;
|
|
Result := (_Wow64GetThreadContext <> nil) and _Wow64GetThreadContext(Handle, ACtxPtr^.WOW);
|
|
end
|
|
else begin
|
|
{$endif}
|
|
case ACtxFlags of
|
|
cfControl: ACtxPtr^.def.ContextFlags := CONTEXT_CONTROL;
|
|
cfFull: ACtxPtr^.def.ContextFlags := CONTEXT_SEGMENTS or CONTEXT_INTEGER or CONTEXT_CONTROL or CONTEXT_DEBUG_REGISTERS or CONTEXT_FLOATING_POINT;
|
|
end;
|
|
Result := GetThreadContext(Handle, ACtxPtr^.def_w);
|
|
{$ifdef cpux86_64}
|
|
end;
|
|
{$endif}
|
|
DebugLn(DBG_WARNINGS and (not Result), ['Unable to get Context for ', ID, ': ', GetLastErrorText]);
|
|
end;
|
|
|
|
end;
|
|
|
|
function TDbgWinThread.SetFpThreadContext(ACtxPtr: PFpContext;
|
|
ACtxFlags: TFpWinCtxFlags): Boolean;
|
|
begin
|
|
SetLastError(0);
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
case ACtxFlags of
|
|
cfControl: ACtxPtr^.WOW.ContextFlags := WOW64_CONTEXT_CONTROL;
|
|
cfFull: ACtxPtr^.WOW.ContextFlags := WOW64_CONTEXT_SEGMENTS or WOW64_CONTEXT_INTEGER or WOW64_CONTEXT_CONTROL or WOW64_CONTEXT_DEBUG_REGISTERS;
|
|
end;
|
|
if ccfControl in FThreadContextChangeFlags then
|
|
ACtxPtr^.def.ContextFlags := ACtxPtr^.def.ContextFlags or WOW64_CONTEXT_CONTROL;
|
|
if ccfInteger in FThreadContextChangeFlags then
|
|
ACtxPtr^.def.ContextFlags := ACtxPtr^.def.ContextFlags or WOW64_CONTEXT_INTEGER;
|
|
Result := (_Wow64SetThreadContext <> nil) and _Wow64SetThreadContext(Handle, ACtxPtr^.WOW);
|
|
end
|
|
else begin
|
|
{$endif}
|
|
case ACtxFlags of
|
|
cfControl: ACtxPtr^.def.ContextFlags := CONTEXT_CONTROL;
|
|
cfFull: ACtxPtr^.def.ContextFlags := CONTEXT_SEGMENTS or CONTEXT_INTEGER or CONTEXT_CONTROL or CONTEXT_DEBUG_REGISTERS;
|
|
end;
|
|
if ccfControl in FThreadContextChangeFlags then
|
|
ACtxPtr^.def.ContextFlags := ACtxPtr^.def.ContextFlags or CONTEXT_CONTROL;
|
|
if ccfInteger in FThreadContextChangeFlags then
|
|
ACtxPtr^.def.ContextFlags := ACtxPtr^.def.ContextFlags or CONTEXT_INTEGER;
|
|
Result := SetThreadContext(Handle, ACtxPtr^.def_w);
|
|
{$ifdef cpux86_64}
|
|
end;
|
|
{$endif}
|
|
DebugLn(DBG_WARNINGS and (not Result), ['Unable to set Context for ', ID, ': ', GetLastErrorText]);
|
|
end;
|
|
|
|
function TDbgWinThread.GetName: String;
|
|
var
|
|
n: PWSTR;
|
|
begin
|
|
Result := '';
|
|
if FDoNotPollName then begin
|
|
Result := FName;
|
|
end else begin
|
|
if _GetThreadDescription <> nil then
|
|
if Succeeded(_GetThreadDescription(Handle, @n)) then begin
|
|
Result := WideCharToString(n);
|
|
LocalFree(HLOCAL(n));
|
|
end;
|
|
end;
|
|
if Result = '' then
|
|
Result := inherited GetName;
|
|
end;
|
|
|
|
function TDbgWinThread.GetStackUnwinder: TDbgStackUnwinder;
|
|
begin
|
|
if FUnwinder = nil then
|
|
FUnwinder := TDbgStackUnwinderX86MultiMethod.Create(Process);
|
|
Result := FUnwinder;
|
|
end;
|
|
|
|
destructor TDbgWinThread.Destroy;
|
|
begin
|
|
FUnwinder.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TDbgWinThread.Suspend;
|
|
var
|
|
r: DWORD;
|
|
begin
|
|
if FIsSuspended then
|
|
exit;
|
|
{$ifdef cpux86_64}
|
|
if (Process.Mode = dm32) and (_Wow64SuspendThread <> nil) then
|
|
r := _Wow64SuspendThread(Handle)
|
|
else
|
|
{$endif}
|
|
r := SuspendThread(Handle);
|
|
FIsSuspended := r <> DWORD(-1);
|
|
debugln(DBG_WARNINGS and (r = DWORD(-1)), 'Failed to suspend Thread %d (handle: %d). Error: %s', [Id, Handle, GetLastErrorText]);
|
|
end;
|
|
|
|
procedure TDbgWinThread.SuspendForStepOverBreakPoint;
|
|
var
|
|
t: TDBGPtr;
|
|
begin
|
|
t := GetInstructionPointerRegisterValue;
|
|
if (FBreakPointState = bsInSingleStep)
|
|
// or (NextIsSingleStep)
|
|
then begin
|
|
Process.TempRemoveBreakInstructionCode(t);
|
|
end
|
|
else
|
|
if NextIsSingleStep and (not Process.HasInsertedBreakInstructionAtLocation(t)) then begin
|
|
// nothing / do the single step
|
|
end
|
|
else
|
|
Suspend;
|
|
end;
|
|
|
|
procedure TDbgWinThread.Resume;
|
|
var
|
|
r: DWORD;
|
|
begin
|
|
if not FIsSuspended then
|
|
exit;
|
|
r := ResumeThread(Handle);
|
|
FIsSuspended := not(r <> DWORD(-1));
|
|
debugln(DBG_WARNINGS and (r = DWORD(-1)), 'Failed to resume Thread %d (handle: %d). Error: %s', [Id, Handle, GetLastErrorText]);
|
|
end;
|
|
|
|
procedure TDbgWinThread.EndSingleStepOverBreakPoint;
|
|
begin
|
|
FBreakPointState := bsNone;
|
|
end;
|
|
|
|
procedure TDbgWinThread.SetSingleStep;
|
|
begin
|
|
NextIsSingleStep := True;
|
|
|
|
if FCurrentContext = nil then
|
|
if not ReadThreadState then
|
|
exit;
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
|
FCurrentContext^.WOW.EFlags := FCurrentContext^.WOW.EFlags or FLAG_TRACE_BIT // TODO WOW_FLAG....
|
|
else
|
|
{$endif}
|
|
FCurrentContext^.def.EFlags := FCurrentContext^.def.EFlags or FLAG_TRACE_BIT;
|
|
FThreadContextChanged:=true;
|
|
end;
|
|
|
|
procedure TDbgWinThread.ApplyWatchPoints(AWatchPointData: TFpWatchPointData);
|
|
begin
|
|
if FCurrentContext = nil then
|
|
if not ReadThreadState then
|
|
exit;
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
with FCurrentContext^.WOW do begin
|
|
Dr0 := DWORD(TFpIntelWatchPointData(AWatchPointData).Dr03[0]);
|
|
Dr1 := DWORD(TFpIntelWatchPointData(AWatchPointData).Dr03[1]);
|
|
Dr2 := DWORD(TFpIntelWatchPointData(AWatchPointData).Dr03[2]);
|
|
Dr3 := DWORD(TFpIntelWatchPointData(AWatchPointData).Dr03[3]);
|
|
Dr7 := (Dr7 and $0000FF00) or DWORD(TFpIntelWatchPointData(AWatchPointData).Dr7);
|
|
DebugLn(DBG_VERBOSE, '### WATCH ADDED dr0 %x dr1 %x dr2 %x dr3 %x dr7 %x', [ dr0,dr1,dr2,dr3, dr7]);
|
|
end;
|
|
end
|
|
else begin
|
|
{$endif}
|
|
with FCurrentContext^.def do begin
|
|
Dr0 := TFpIntelWatchPointData(AWatchPointData).Dr03[0];
|
|
Dr1 := TFpIntelWatchPointData(AWatchPointData).Dr03[1];
|
|
Dr2 := TFpIntelWatchPointData(AWatchPointData).Dr03[2];
|
|
Dr3 := TFpIntelWatchPointData(AWatchPointData).Dr03[3];
|
|
Dr7 := (Dr7 and $0000FF00) or TFpIntelWatchPointData(AWatchPointData).Dr7;
|
|
DebugLn(DBG_VERBOSE, '### WATCH ADDED dr0 %x dr1 %x dr2 %x dr3 %x dr7 %x', [ dr0,dr1,dr2,dr3, dr7]);
|
|
end;
|
|
{$ifdef cpux86_64}
|
|
end;
|
|
{$endif}
|
|
FThreadContextChanged:=true;
|
|
end;
|
|
|
|
function TDbgWinThread.DetectHardwareWatchpoint: Pointer;
|
|
var
|
|
Dr6: DWORD64;
|
|
wd: TFpIntelWatchPointData;
|
|
begin
|
|
result := nil;
|
|
if FCurrentContext = nil then
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
Dr6 := DWORD64(FCurrentContext^.WOW.Dr6);
|
|
end
|
|
else begin
|
|
{$endif}
|
|
Dr6 := FCurrentContext^.def.Dr6;
|
|
{$ifdef cpux86_64}
|
|
end;
|
|
{$endif}
|
|
|
|
wd := TFpIntelWatchPointData(Process.WatchPointData);
|
|
if dr6 and 1 = 1 then result := wd.Owner[0]
|
|
else if dr6 and 2 = 2 then result := wd.Owner[1]
|
|
else if dr6 and 4 = 4 then result := wd.Owner[2]
|
|
else if dr6 and 8 = 8 then result := wd.Owner[3];
|
|
if (Result = nil) and ((dr6 and 15) <> 0) then
|
|
Result := Pointer(-1); // not owned watchpoint
|
|
end;
|
|
|
|
procedure TDbgWinThread.BeforeContinue;
|
|
begin
|
|
inherited;
|
|
if ID = MDebugEvent.dwThreadId then begin
|
|
FHasExceptionCleared := False;
|
|
|
|
{$ifdef cpux86_64}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
if (FCurrentContext <> nil) and
|
|
(FCurrentContext^.WOW.Dr6 <> $ffff0ff0) then
|
|
begin
|
|
FCurrentContext^.WOW.Dr6:=$ffff0ff0;
|
|
FThreadContextChanged:=true;
|
|
end;
|
|
end
|
|
else begin
|
|
{$endif}
|
|
if (FCurrentContext <> nil) and
|
|
(FCurrentContext^.def.Dr6 <> $ffff0ff0) then
|
|
begin
|
|
FCurrentContext^.def.Dr6:=$ffff0ff0;
|
|
FThreadContextChanged:=true;
|
|
end;
|
|
{$ifdef cpux86_64}
|
|
end;
|
|
{$endif}
|
|
end;
|
|
|
|
if FThreadContextChanged then
|
|
begin
|
|
Assert(FCurrentContext <> nil, 'TDbgWinThread.BeforeContinue: none existing context was changed');
|
|
if not SetFpThreadContext(FCurrentContext) then
|
|
debugln(FPDBG_WINDOWS or DBG_WARNINGS, ['Failed to SetFpThreadContext()']);
|
|
end;
|
|
FThreadContextChanged := False;
|
|
FThreadContextChangeFlags := [];
|
|
FCurrentContext := nil;
|
|
FHasResetInstructionPointerAfterBreakpoint := False;
|
|
end;
|
|
|
|
function TDbgWinThread.ResetInstructionPointerAfterBreakpoint: boolean;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinThread.ResetInstructionPointerAfterBreakpoint');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinThread.ResetInstructionPointerAfterBreakpoint: MDebugEvent.dwProcessId <> 0');
|
|
assert((MDebugEvent.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT) or (MDebugEvent.Exception.ExceptionRecord.ExceptionCode = STATUS_WX86_BREAKPOINT), 'TDbgWinThread.ResetInstructionPointerAfterBreakpoint: (MDebugEvent.Exception.ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT) or (MDebugEvent.Exception.ExceptionRecord.ExceptionCode = STATUS_WX86_BREAKPOINT)');
|
|
|
|
Result := False;
|
|
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
assert(not FHasResetInstructionPointerAfterBreakpoint, 'TDbgWinThread.ResetInstructionPointerAfterBreakpoint: not FHasResetInstructionPointerAfterBreakpoint');
|
|
{$ifdef cpui386}
|
|
if not CheckForHardcodeBreakPoint(FCurrentContext^.def.Eip - 1) then
|
|
dec(FCurrentContext^.def.Eip);
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
if not CheckForHardcodeBreakPoint(FCurrentContext^.WOW.Eip - 1) then
|
|
dec(FCurrentContext^.WOW.Eip);
|
|
end
|
|
else begin
|
|
if not CheckForHardcodeBreakPoint(FCurrentContext^.def.Rip - 1) then
|
|
dec(FCurrentContext^.def.Rip);
|
|
end;
|
|
{$endif}
|
|
|
|
FThreadContextChanged := True;
|
|
FHasResetInstructionPointerAfterBreakpoint := True;
|
|
Result := True;
|
|
end;
|
|
|
|
function TDbgWinThread.ReadThreadState: boolean;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinThread.ReadThreadState');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinThread.ReadThreadState: MDebugEvent.dwProcessId <> 0');
|
|
|
|
if Process.ProcessID <> MDebugEvent.dwProcessId then begin
|
|
DebugLn(DBG_WARNINGS, 'ERROR: attempt to read threadstate, for wrong process. Thread: %u Thread-Process: %u Event-Process %u', [Id, Process.ProcessID, MDebugEvent.dwProcessId]);
|
|
exit(False);
|
|
end;
|
|
|
|
Result := True;
|
|
if FCurrentContext <> nil then
|
|
exit;
|
|
|
|
Result := GetFpThreadContext(_UnAligendContext, FCurrentContext, cfFull);
|
|
DebugLn((DBG_WARNINGS or DBG_VERBOSE) and (not Result), ['Failed to read thread-state for ', ID]);
|
|
//FThreadContextChanged := False; TODO: why was that not here?
|
|
FThreadContextChangeFlags := [];
|
|
FRegisterValueListValid:=False;
|
|
FHasResetInstructionPointerAfterBreakpoint := False;
|
|
end;
|
|
|
|
procedure TDbgWinThread.ClearExceptionSignal;
|
|
begin
|
|
inherited ClearExceptionSignal;
|
|
FHasExceptionCleared := True;
|
|
end;
|
|
|
|
procedure TDbgWinThread.SetRegisterValue(AName: string; AValue: QWord);
|
|
begin
|
|
//{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinThread.SetRegisterValue');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinThread.SetRegisterValue: MDebugEvent.dwProcessId <> 0');
|
|
|
|
if not ReadThreadState then
|
|
exit;
|
|
|
|
{$ifdef cpui386}
|
|
assert((AValue and QWord($ffffffff00000000) = 0) or (AValue and QWord($ffffffff00000000) = QWord($ffffffff00000000)), 'TDbgWinThread.SetRegisterValue: ((AValue and QWord($ffffffff00000000) = 0) or ((AValue and QWord($ffffffff00000000) = QWord($ffffffff00000000)');
|
|
case AName of
|
|
'eip': FCurrentContext^.def.Eip := DWORD(AValue);
|
|
'eax': FCurrentContext^.def.Eax := DWORD(AValue);
|
|
'ecx': FCurrentContext^.def.Ecx := DWORD(AValue);
|
|
'edx': FCurrentContext^.def.Edx := DWORD(AValue);
|
|
else
|
|
raise Exception.CreateFmt('Setting the [%s] register is not supported', [AName]);
|
|
end;
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then begin
|
|
assert((AValue and QWord($ffffffff00000000) = 0) or (AValue and QWord($ffffffff00000000) = QWord($ffffffff00000000)), 'TDbgWinThread.SetRegisterValue: ((AValue and QWord($ffffffff00000000) = 0) or ((AValue and QWord($ffffffff00000000) = QWord($ffffffff00000000)');
|
|
case AName of
|
|
'eip': FCurrentContext^.WOW.Eip := DWORD(AValue);
|
|
'eax': FCurrentContext^.WOW.Eax := DWORD(AValue);
|
|
'ecx': FCurrentContext^.WOW.Ecx := DWORD(AValue);
|
|
'edx': FCurrentContext^.WOW.Edx := DWORD(AValue);
|
|
else
|
|
raise Exception.CreateFmt('Setting the [%s] register is not supported', [AName]);
|
|
end;
|
|
end
|
|
else begin
|
|
case AName of
|
|
'rip': FCurrentContext^.def.Rip := AValue;
|
|
'rax': FCurrentContext^.def.Rax := AValue;
|
|
'rcx': FCurrentContext^.def.Rcx := AValue;
|
|
'rdx': FCurrentContext^.def.Rdx := AValue;
|
|
'r8': FCurrentContext^.def.R8 := AValue;
|
|
'r9': FCurrentContext^.def.R9 := AValue;
|
|
else
|
|
raise Exception.CreateFmt('Setting the [%s] register is not supported', [AName]);
|
|
end;
|
|
end;
|
|
{$endif}
|
|
FThreadContextChanged:=True;
|
|
case AName of
|
|
'eip', 'rip': Include(FThreadContextChangeFlags, ccfControl);
|
|
else Include(FThreadContextChangeFlags, ccfInteger);
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgWinThread.StoreRegisters;
|
|
begin
|
|
_StoredContext := _UnAligendContext;
|
|
end;
|
|
|
|
procedure TDbgWinThread.RestoreRegisters;
|
|
begin
|
|
_UnAligendContext := _StoredContext;
|
|
FThreadContextChanged := True;
|
|
FRegisterValueListValid := False;
|
|
end;
|
|
|
|
function TDbgWinThread.GetInstructionPointerRegisterValue: TDbgPtr;
|
|
begin
|
|
//{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinThread.GetInstructionPointerRegisterValue');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinThread.GetInstructionPointerRegisterValue: MDebugEvent.dwProcessId <> 0');
|
|
|
|
Result := 0;
|
|
if FCurrentContext = nil then
|
|
if not ReadThreadState then
|
|
exit;
|
|
{$ifdef cpui386}
|
|
Result := FCurrentContext^.def.Eip;
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
|
Result := FCurrentContext^.WOW.Eip
|
|
else
|
|
Result := FCurrentContext^.def.Rip;
|
|
{$endif}
|
|
end;
|
|
|
|
function TDbgWinThread.GetStackBasePointerRegisterValue: TDbgPtr;
|
|
begin
|
|
//{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinThread.GetStackBasePointerRegisterValue');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinThread.GetStackBasePointerRegisterValue: MDebugEvent.dwProcessId <> 0');
|
|
|
|
Result := 0;
|
|
if FCurrentContext = nil then
|
|
if not ReadThreadState then
|
|
exit;
|
|
{$ifdef cpui386}
|
|
Result := FCurrentContext^.def.Ebp;
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
|
Result := FCurrentContext^.WOW.Ebp
|
|
else
|
|
Result := FCurrentContext^.def.Rbp;
|
|
{$endif}
|
|
end;
|
|
|
|
procedure TDbgWinThread.SetInstructionPointerRegisterValue(AValue: TDbgPtr);
|
|
begin
|
|
if FCurrentContext = nil then
|
|
exit;
|
|
{$ifdef cpui386}
|
|
FCurrentContext^.def.Eip := AValue;
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
|
FCurrentContext^.WOW.Eip := AValue
|
|
else
|
|
FCurrentContext^.def.Rip := AValue;
|
|
{$endif}
|
|
FThreadContextChanged:=True;
|
|
end;
|
|
|
|
procedure TDbgWinThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
|
begin
|
|
if FCurrentContext = nil then
|
|
exit;
|
|
{$ifdef cpui386}
|
|
FCurrentContext^.def.Esp := AValue;
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
|
FCurrentContext^.WOW.Esp := AValue
|
|
else
|
|
FCurrentContext^.def.Rsp := AValue;
|
|
{$endif}
|
|
FThreadContextChanged:=True;
|
|
end;
|
|
|
|
function TDbgWinThread.GetStackPointerRegisterValue: TDbgPtr;
|
|
begin
|
|
//{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgWinThread.GetStackPointerRegisterValue');{$ENDIF}
|
|
assert(MDebugEvent.dwProcessId <> 0, 'TDbgWinThread.GetStackPointerRegisterValue: MDebugEvent.dwProcessId <> 0');
|
|
|
|
Result := 0;
|
|
if FCurrentContext = nil then
|
|
if not ReadThreadState then
|
|
exit;
|
|
{$ifdef cpui386}
|
|
Result := FCurrentContext^.def.Esp;
|
|
{$else}
|
|
if (TDbgWinProcess(Process).FBitness = b32) then
|
|
Result := FCurrentContext^.WOW.Esp
|
|
else
|
|
Result := FCurrentContext^.def.Rsp;
|
|
{$endif}
|
|
end;
|
|
|
|
initialization
|
|
LoadKernelEntryPoints;
|
|
|
|
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
|
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
|
FPDBG_WINDOWS := DebugLogger.FindOrRegisterLogGroup('FPDBG_WINDOWS' {$IFDEF FPDBG_WINDOWS} , True {$ENDIF} );
|
|
|
|
RegisterDbgOsClasses(TOSDbgClasses.Create(
|
|
TDbgWinProcess,
|
|
TDbgWinThread,
|
|
TX86AsmDecoder
|
|
));
|
|
|
|
end.
|
|
|