FpDebug: Handle hitting breakpoints, that have been removed while the event was waiting. Still need to re-execute the asm instruction that was hidden by int3

git-svn-id: trunk@61839 -
This commit is contained in:
martin 2019-09-08 19:03:09 +00:00
parent 2f812db331
commit 876682fc9d
4 changed files with 82 additions and 10 deletions

View File

@ -139,7 +139,10 @@ type
FProcess: TDbgProcess;
FID: Integer;
FHandle: THandle;
FIsAtBreakInstruction: Boolean;
FPausedAtRemovedBreakPointState: (rbUnknown, rbNone, rbFound{, rbFoundAndDec});
FPausedAtRemovedBreakPointAddress: TDBGPtr;
function GetRegisterValueList: TDbgRegisterValueList;
protected
FCallStackEntryList: TDbgCallstackEntryList;
@ -152,8 +155,11 @@ type
procedure LoadRegisterValues; virtual;
property Process: TDbgProcess read FProcess;
function ResetInstructionPointerAfterBreakpoint: boolean; virtual; abstract;
procedure DoBeforeBreakLocationMapChange; // A new location added / or a location removed => memory will change
procedure ValidateRemovedBreakPointInfo;
public
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual;
function HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean; // include removed breakpoints that (may have) already triggered
procedure CheckAndResetInstructionPointerAfterBreakpoint;
procedure BeforeContinue; virtual;
function AddWatchpoint(AnAddr: TDBGPtr): integer; virtual;
@ -175,7 +181,6 @@ type
property NextIsSingleStep: boolean read FNextIsSingleStep write FNextIsSingleStep;
property RegisterValueList: TDbgRegisterValueList read GetRegisterValueList;
property CallStackEntryList: TDbgCallstackEntryList read FCallStackEntryList;
property IsAtBreakInstruction: boolean read FIsAtBreakInstruction; // Is stopped at Int3 code. Needs to exec orig instruction instead
end;
TDbgThreadClass = class of TDbgThread;
@ -241,7 +246,7 @@ type
procedure RemoveLocotion(const ALocation: TDBGPtr; const AInternalBreak: TFpInternalBreakpoint);
function GetInternalBreaksAtLocation(const ALocation: TDBGPtr): TFpInternalBreakpointArray;
function GetOrigValueAtLocation(const ALocation: TDBGPtr): Byte; // returns Int3, if there is no break at this location
function HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean; // returns Int3, if there is no break at this location
function HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean;
function GetEnumerator: TBreakLocationMapEnumerator;
end;
@ -376,6 +381,7 @@ type
function GetLib(const AHandle: THandle; out ALib: TDbgLibrary): Boolean;
function GetThread(const AID: Integer; out AThread: TDbgThread): Boolean;
procedure RemoveBreak(const ABreakPoint: TFpInternalBreakpoint);
procedure DoBeforeBreakLocationMapChange;
function HasBreak(const ALocation: TDbgPtr): Boolean; // TODO: remove, once an address can have many breakpoints
procedure RemoveThread(const AID: DWord);
function FormatAddress(const AAddress): String;
@ -617,6 +623,7 @@ begin
exit;
end;
FProcess.DoBeforeBreakLocationMapChange; // Only if a new breakpoint is set => memory changed
new(LocData);
LocData^.ErrorSetting := not FProcess.InsertBreakInstructionCode(ALocation, LocData^.OrigValue);
LocData^.IsBreakList := False;
@ -662,6 +669,7 @@ begin
exit;
end;
FProcess.DoBeforeBreakLocationMapChange; // Only if a breakpoint is removed => memory changed
if not LocData^.ErrorSetting then
FProcess.RemoveBreakInstructionCode(ALocation, LocData^.OrigValue);
Delete(ALocation);
@ -1237,6 +1245,7 @@ function TDbgProcess.ResolveDebugEvent(AThread: TDbgThread): TFPDEvent;
var
CurrentAddr: TDBGPtr;
begin
AThread.ValidateRemovedBreakPointInfo;
result := AnalyseDebugEvent(AThread);
if result = deBreakpoint then
@ -1365,6 +1374,14 @@ begin
FCurrentBreakpoint := nil;
end;
procedure TDbgProcess.DoBeforeBreakLocationMapChange;
var
t: TDbgThread;
begin
for t in FThreadMap do
t.DoBeforeBreakLocationMapChange;
end;
function TDbgProcess.HasBreak(const ALocation: TDbgPtr): Boolean;
begin
if FBreakMap = nil then
@ -1654,6 +1671,41 @@ begin
// Do nothing
end;
procedure TDbgThread.DoBeforeBreakLocationMapChange;
var
t: TDBGPtr;
begin
debugln(FPausedAtRemovedBreakPointState <> rbUnknown, ['@@@@@@@ MAP tid ', ID, ' ', ord(FPausedAtRemovedBreakPointState), ' ',dbghex(FPausedAtRemovedBreakPointAddress)]);
if (FPausedAtRemovedBreakPointState <> rbUnknown) and
(FPausedAtRemovedBreakPointAddress = GetInstructionPointerRegisterValue) then
exit;
t := GetInstructionPointerRegisterValue;
if Process.HasInsertedBreakInstructionAtLocation(t - 1) then begin
(* There is a chance, that the code jumped to this Addr, instead of executing the breakpoint.
But if the next signal for this thread is a breakpoint at this address, then
it must be handled (even if the breakpoint has been removed since)
*)
FPausedAtRemovedBreakPointAddress := t;
FPausedAtRemovedBreakPointState := rbFound;
debugln(['####### STORE ',dbghex( t), ' for id ', ID]);
// Most likely the debugger should see the previous address (unless we got here
// by jump.
// Call something like ResetInstructionPointerAfterBreakpointForPendingSignal; virtual;
////ResetInstructionPointerAfterBreakpoint;
end
else
FPausedAtRemovedBreakPointState := rbNone;
end;
procedure TDbgThread.ValidateRemovedBreakPointInfo;
begin
if (FPausedAtRemovedBreakPointState <> rbUnknown) and
(FPausedAtRemovedBreakPointAddress <> GetInstructionPointerRegisterValue)
then
FPausedAtRemovedBreakPointState := rbUnknown;
end;
constructor TDbgThread.Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle);
begin
FID := AID;
@ -1663,18 +1715,32 @@ begin
inherited Create;
end;
function TDbgThread.HasInsertedBreakInstructionAtLocation(const ALocation: TDBGPtr): Boolean;
var
t: TDBGPtr;
begin
t := GetInstructionPointerRegisterValue;
Result := ( (FPausedAtRemovedBreakPointState = rbFound) and
(FPausedAtRemovedBreakPointAddress = t) ) or
Process.HasInsertedBreakInstructionAtLocation(t - 1);
debugln(['####### CHECK ',result, ' for id ', ID, ' stored ', FPausedAtRemovedBreakPointState=rbFound, ' ',FPausedAtRemovedBreakPointAddress=t, ' ',dbghex(t), ' ', dbghex(FPausedAtRemovedBreakPointAddress)]);
end;
procedure TDbgThread.CheckAndResetInstructionPointerAfterBreakpoint;
begin
// todo: check that the breakpoint is NOT in the temp removed list
if not Process.HasInsertedBreakInstructionAtLocation(GetInstructionPointerRegisterValue - 1) then
exit;
FIsAtBreakInstruction := True;
ResetInstructionPointerAfterBreakpoint;
if HasInsertedBreakInstructionAtLocation(GetInstructionPointerRegisterValue - 1)
then begin
FPausedAtRemovedBreakPointState := rbFound;
ResetInstructionPointerAfterBreakpoint;
end;
end;
procedure TDbgThread.BeforeContinue;
begin
// Do nothing
// On Windows this is only called, if this was the signalled thread
FPausedAtRemovedBreakPointState := rbUnknown;
FPausedAtRemovedBreakPointAddress := 0;
end;
function TDbgThread.AddWatchpoint(AnAddr: TDBGPtr): integer;

View File

@ -525,6 +525,7 @@ var
aKernResult: kern_return_t;
old_StateCnt: mach_msg_Type_number_t;
begin
inherited;
if Process.CurrentWatchpoint>-1 then
begin
if Process.Mode=dm32 then
@ -868,7 +869,7 @@ begin
{$endif linux}
{$ifdef darwin}
AThread.NextIsSingleStep:=SingleStep;
AThread.BeforeContinue;
AThread.BeforeContinue; // TODO: All threads
if HasInsertedBreakInstructionAtLocation(AThread.GetInstructionPointerRegisterValue) then begin
TempRemoveBreakInstructionCode(AThread.GetInstructionPointerRegisterValue);
fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal));

View File

@ -631,6 +631,7 @@ begin
if not FIsPaused then
exit;
inherited;
if Process.CurrentWatchpoint>-1 then
WriteDebugReg(6, 0);

View File

@ -1376,9 +1376,12 @@ end;
procedure TDbgWinThread.BeforeContinue;
begin
if Process.ProcessID <> MDebugEvent.dwProcessId then
if ID <> MDebugEvent.dwThreadId then
exit;
inherited;
if (FCurrentContext <> nil) and
(FCurrentContext^.Dr6 <> $ffff0ff0) then
begin
@ -1427,6 +1430,7 @@ begin
{$else}
Dec(Context^.Rip);
dec(FCurrentContext^.Rip);
debugln(['TDbgWinThread.ResetInstructionPointerAfterBreakpoint ',ID, ' before ', dbghex(FCurrentContext^.Rip), ' / ',Context^.Rip]);
{$endif}
if not SetThreadContext(Handle, Context^)