mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-22 01:39:42 +02:00
FpDebug: refactor stack unwinding. Introduce TDbgStackUnwinder and new classes for X86
This commit is contained in:
parent
b5db1bf52f
commit
476cfd4484
@ -192,6 +192,50 @@ type
|
||||
property StoredStackFrame: TDBGPtr read FStoredStackFrame;
|
||||
end;
|
||||
|
||||
TTDbgStackUnwindResult = (suSuccess, suFailed,
|
||||
suFailedAtEOS, // this is the End Of Stack
|
||||
suGuessed // Got a frame, but may be wrong
|
||||
);
|
||||
|
||||
TDbgStackUnwinder = class
|
||||
public
|
||||
procedure InitForThread(AThread: TDbgThread); virtual; abstract;
|
||||
// FrameBasePointer is optional
|
||||
procedure GetTopFrame(out CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||
out ANewFrame: TDbgCallstackEntry); virtual; abstract;
|
||||
procedure InitForFrame(ACurrentFrame: TDbgCallstackEntry;
|
||||
out CodePointer, StackPointer, FrameBasePointer: TDBGPtr); virtual; abstract;
|
||||
// AFrameIndex: The frame-index to be read. Starts at 1 (since 0 is top-lever, and handled by GetTopFrame)
|
||||
function Unwind(AFrameIndex: integer;
|
||||
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||
ACurrentFrame: TDbgCallstackEntry; // nil for top frame
|
||||
out ANewFrame: TDbgCallstackEntry
|
||||
): TTDbgStackUnwindResult; virtual; abstract;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderX86Base }
|
||||
// Avoid circular unit refs
|
||||
|
||||
TDbgStackUnwinderX86Base = class(TDbgStackUnwinder)
|
||||
private
|
||||
FThread: TDbgThread;
|
||||
FProcess: TDbgProcess;
|
||||
FAddressSize: Integer;
|
||||
protected
|
||||
FDwarfNumIP, FDwarfNumBP, FDwarfNumSP: integer;
|
||||
FNameIP, FNameBP, FNameSP: String;
|
||||
property Process: TDbgProcess read FProcess;
|
||||
property Thread: TDbgThread read FThread;
|
||||
property AddressSize: Integer read FAddressSize;
|
||||
public
|
||||
constructor Create(AProcess: TDbgProcess);
|
||||
procedure InitForThread(AThread: TDbgThread); override;
|
||||
procedure InitForFrame(ACurrentFrame: TDbgCallstackEntry; out CodePointer,
|
||||
StackPointer, FrameBasePointer: TDBGPtr); override;
|
||||
procedure GetTopFrame(out CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||
out ANewFrame: TDbgCallstackEntry); override;
|
||||
end;
|
||||
|
||||
{ TDbgThread }
|
||||
TFpInternalBreakpoint = class;
|
||||
|
||||
@ -223,6 +267,7 @@ type
|
||||
procedure DoBeforeBreakLocationMapChange; // A new location added / or a location removed => memory will change
|
||||
procedure ValidateRemovedBreakPointInfo;
|
||||
function GetName: String; virtual;
|
||||
function GetStackUnwinder: TDbgStackUnwinder; virtual; abstract;
|
||||
|
||||
public
|
||||
constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle); virtual;
|
||||
@ -3051,6 +3096,74 @@ begin
|
||||
FHasSteppedOut := True;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderX86Base }
|
||||
|
||||
constructor TDbgStackUnwinderX86Base.Create(AProcess: TDbgProcess);
|
||||
begin
|
||||
FProcess := AProcess;
|
||||
case AProcess.Mode of
|
||||
dm32: begin
|
||||
FAddressSize := 4;
|
||||
FDwarfNumIP := 8; // Dwarf Reg Num EIP
|
||||
FDwarfNumBP := 5; // EBP
|
||||
FDwarfNumSP := 4; // ESP
|
||||
FNameIP := 'eip';
|
||||
FNameBP := 'ebp';
|
||||
FNameSP := 'esp';
|
||||
end;
|
||||
dm64: begin
|
||||
FAddressSize := 8;
|
||||
FDwarfNumIP := 16; // Dwarf Reg Num RIP
|
||||
FDwarfNumBP := 6; // RBP
|
||||
FDwarfNumSP := 7; // RSP
|
||||
FNameIP := 'rip';
|
||||
FNameBP := 'rbp';
|
||||
FNameSP := 'rsp';
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDbgStackUnwinderX86Base.InitForThread(AThread: TDbgThread);
|
||||
begin
|
||||
FThread := AThread;
|
||||
end;
|
||||
|
||||
procedure TDbgStackUnwinderX86Base.InitForFrame(
|
||||
ACurrentFrame: TDbgCallstackEntry; out CodePointer, StackPointer,
|
||||
FrameBasePointer: TDBGPtr);
|
||||
var
|
||||
R: TDbgRegisterValue;
|
||||
begin
|
||||
CodePointer := ACurrentFrame.AnAddress;
|
||||
FrameBasePointer := ACurrentFrame.FrameAdress;
|
||||
R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumBP);
|
||||
if R <> nil then
|
||||
FrameBasePointer := R.NumValue;
|
||||
StackPointer := 0;
|
||||
R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumSP);
|
||||
if R = nil then exit;
|
||||
StackPointer := R.NumValue;
|
||||
end;
|
||||
|
||||
procedure TDbgStackUnwinderX86Base.GetTopFrame(out CodePointer, StackPointer,
|
||||
FrameBasePointer: TDBGPtr; out ANewFrame: TDbgCallstackEntry);
|
||||
var
|
||||
i: Integer;
|
||||
R: TDbgRegisterValue;
|
||||
begin
|
||||
CodePointer := Thread.GetInstructionPointerRegisterValue;
|
||||
StackPointer := Thread.GetStackPointerRegisterValue;
|
||||
FrameBasePointer := Thread.GetStackBasePointerRegisterValue;
|
||||
ANewFrame := TDbgCallstackEntry.create(Thread, 0, FrameBasePointer, CodePointer);
|
||||
|
||||
i := Thread.RegisterValueList.Count;
|
||||
while i > 0 do begin
|
||||
dec(i);
|
||||
R := Thread.RegisterValueList[i];
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[R.Name].SetValue(R.NumValue, R.StrValue, R.Size, R.DwarfIdx);
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TDbgThread }
|
||||
|
||||
function TDbgThread.GetRegisterValueList: TDbgRegisterValueList;
|
||||
@ -3315,254 +3428,50 @@ procedure TDbgThread.PrepareCallStackEntryList(AFrameRequired: Integer);
|
||||
const
|
||||
MAX_FRAMES = 150000; // safety net
|
||||
var
|
||||
Address, FrameBase, LastFrameBase, Dummy: QWord;
|
||||
PrevAddress, PrevFrameBase, PrevStackPtr: QWord;
|
||||
Size, CountNeeded, IP, BP, CodeReadErrCnt, SP, i,
|
||||
PrevStmtAddressOffs: integer;
|
||||
AnEntry, NewEntry: TDbgCallstackEntry;
|
||||
R, StackReg, FrameReg: TDbgRegisterValue;
|
||||
nIP, nBP, nSP: String;
|
||||
Address, FrameBase, StackPtr: TDBGPtr;
|
||||
CountNeeded, i: integer;
|
||||
AnEntry: TDbgCallstackEntry;
|
||||
NextIdx: LongInt;
|
||||
OutSideFrame: Boolean;
|
||||
StackPtr: TDBGPtr;
|
||||
Row: TDwarfCallFrameInformationRow;
|
||||
CIE: TDwarfCIE;
|
||||
CU: TDwarfCompilationUnit;
|
||||
|
||||
procedure CheckFrame(var NextEntry: TDbgCallstackEntry; AForce: boolean = False);
|
||||
begin
|
||||
if (not AForce) and (NextEntry <> nil) and Process.Disassembler.IsAfterCallInstruction(Address) then
|
||||
exit;
|
||||
|
||||
if not Process.Disassembler.UnwindFrame(PrevAddress, PrevStackPtr, PrevFrameBase, False) then
|
||||
exit;
|
||||
|
||||
if (NextEntry <> nil) and
|
||||
(Address = PrevAddress) and (FrameBase = PrevFrameBase) and (StackPtr = PrevStackPtr)
|
||||
then
|
||||
exit;
|
||||
if not Process.Disassembler.IsAfterCallInstruction(PrevAddress) then
|
||||
exit;
|
||||
|
||||
Address := PrevAddress;
|
||||
FrameBase := PrevFrameBase;
|
||||
StackPtr := PrevStackPtr;
|
||||
NextEntry.Free;
|
||||
NextEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
||||
NextEntry.RegisterValueList.DbgRegisterAutoCreate[nIP].SetValue(Address, IntToStr(Address),Size, IP);
|
||||
NextEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
||||
NextEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
||||
LastFrameBase := FrameBase;
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
if LastFrameBase > 0 then
|
||||
LastFrameBase := LastFrameBase - 1;
|
||||
{$POP}
|
||||
end;
|
||||
|
||||
Unwinder: TDbgStackUnwinder;
|
||||
Res: TTDbgStackUnwindResult;
|
||||
begin
|
||||
// TODO: use AFrameRequired // check if already partly done
|
||||
if FCallStackEntryList = nil then
|
||||
if FCallStackEntryList = nil then begin
|
||||
FCallStackEntryList := TDbgCallstackEntryList.Create;
|
||||
FCallStackEntryList.FreeObjects:=true;
|
||||
end;
|
||||
if AFrameRequired = -2 then
|
||||
exit;
|
||||
|
||||
if (AFrameRequired >= 0) and (AFrameRequired < FCallStackEntryList.Count) then
|
||||
exit;
|
||||
|
||||
case FProcess.Mode of
|
||||
dm32: begin
|
||||
Size := 4;
|
||||
IP := 8; // Dwarf Reg Num EIP
|
||||
BP := 5; // EBP
|
||||
SP := 4; // ESP
|
||||
nIP := 'eip';
|
||||
nBP := 'ebp';
|
||||
nSP := 'esp';
|
||||
end;
|
||||
dm64: begin
|
||||
Size := 8;
|
||||
IP := 16; // Dwarf Reg Num RIP
|
||||
BP := 6; // RBP
|
||||
SP := 7; // RSP
|
||||
nIP := 'rip';
|
||||
nBP := 'rbp';
|
||||
nSP := 'rsp';
|
||||
end;
|
||||
else begin
|
||||
assert(False, 'unknown address size for stack');
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
Unwinder := GetStackUnwinder;
|
||||
Unwinder.InitForThread(Self);
|
||||
|
||||
FCallStackEntryList.FreeObjects:=true;
|
||||
|
||||
PrevStmtAddressOffs := 1;
|
||||
if FCallStackEntryList.Count > 0 then begin
|
||||
AnEntry := FCallStackEntryList[FCallStackEntryList.Count - 1];
|
||||
Address:=AnEntry.AnAddress;
|
||||
FrameBase:=AnEntry.FrameAdress;
|
||||
R := AnEntry.RegisterValueList.FindRegisterByDwarfIndex(SP);
|
||||
if R = nil then exit;
|
||||
StackPtr := R.NumValue;
|
||||
if FCallStackEntryList.Count = 0 then begin
|
||||
Unwinder.GetTopFrame(Address, StackPtr, FrameBase, AnEntry);
|
||||
FCallStackEntryList.Add(AnEntry);
|
||||
end
|
||||
else begin
|
||||
PrevStmtAddressOffs := 0;
|
||||
Address := GetInstructionPointerRegisterValue;
|
||||
FrameBase := GetStackBasePointerRegisterValue;
|
||||
StackPtr := GetStackPointerRegisterValue;
|
||||
AnEntry := TDbgCallstackEntry.create(Self, 0, FrameBase, Address);
|
||||
|
||||
// Initialize register values
|
||||
// Top level could do without entry in registerlist, but this way the
|
||||
// same code can handle both cases.
|
||||
i := 0;
|
||||
R := RegisterValueList.FindRegisterByDwarfIndex(i);
|
||||
while Assigned(R) do
|
||||
begin
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[R.Name].SetValue(R.NumValue, R.StrValue, R.Size, R.DwarfIdx);
|
||||
inc(i);
|
||||
R := RegisterValueList.FindRegisterByDwarfIndex(i);
|
||||
end;
|
||||
FCallStackEntryList.Add(AnEntry);
|
||||
AnEntry := FCallStackEntryList[FCallStackEntryList.Count - 1];
|
||||
Unwinder.InitForFrame(AnEntry, Address, StackPtr, FrameBase);
|
||||
end;
|
||||
|
||||
NextIdx := FCallStackEntryList.Count;
|
||||
|
||||
if AFrameRequired < 0 then
|
||||
AFrameRequired := MaxInt;
|
||||
CountNeeded := AFrameRequired - FCallStackEntryList.Count;
|
||||
LastFrameBase := 0;
|
||||
CodeReadErrCnt := 0;
|
||||
while (CountNeeded > 0) do
|
||||
begin
|
||||
PrevAddress := Address;
|
||||
PrevFrameBase := FrameBase;
|
||||
PrevStackPtr := StackPtr;
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
if (Process.DbgInfo as TFpDwarfInfo).FindCallFrameInfo(Address - PrevStmtAddressOffs, CIE, Row) and
|
||||
TDwarfCallFrameInformation.TryObtainNextCallFrame(AnEntry, CIE, Size, NextIdx, Self, Row, Process, NewEntry)
|
||||
{$POP}
|
||||
then begin
|
||||
PrevStmtAddressOffs := 1;
|
||||
if not Assigned(NewEntry) then begin
|
||||
CU := (Process.DbgInfo as TFpDwarfInfo).CompilationUnitForAddr(Address);
|
||||
if (CU = nil) or (CU.DwarfSymbolClassMap = nil) or (not CU.DwarfSymbolClassMap.IgnoreCfiStackEnd) then
|
||||
// Done.
|
||||
Break;
|
||||
end
|
||||
else begin
|
||||
Address := NewEntry.AnAddress;
|
||||
StackReg := NewEntry.RegisterValueList.FindRegisterByDwarfIndex(SP);
|
||||
FrameReg := NewEntry.RegisterValueList.FindRegisterByDwarfIndex(BP);
|
||||
StackPtr := 0;
|
||||
if (StackReg <> nil) and (FrameReg <> nil) then begin
|
||||
StackPtr := StackReg.FNumValue;
|
||||
FrameBase := FrameReg.FNumValue;
|
||||
end;
|
||||
AnEntry := NewEntry;
|
||||
CheckFrame(NewEntry);
|
||||
FCallStackEntryList.Add(NewEntry);
|
||||
Dec(CountNeeded);
|
||||
inc(NextIdx);
|
||||
If (NextIdx > MAX_FRAMES) then
|
||||
Break;
|
||||
Res := Unwinder.Unwind(NextIdx, Address, StackPtr, FrameBase, AnEntry, AnEntry);
|
||||
if not (Res in [suSuccess, suGuessed]) then
|
||||
break;
|
||||
|
||||
Continue;
|
||||
end;
|
||||
end;
|
||||
PrevStmtAddressOffs := 1;
|
||||
|
||||
if Process.Disassembler.UnwindFrame(PrevAddress, PrevStackPtr, PrevFrameBase, True) and
|
||||
Process.Disassembler.IsAfterCallInstruction(PrevAddress)
|
||||
then begin
|
||||
Address := PrevAddress;
|
||||
FrameBase := PrevFrameBase;
|
||||
StackPtr := PrevStackPtr;
|
||||
AnEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nIP].SetValue(Address, IntToStr(Address),Size, IP);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
||||
FCallStackEntryList.Add(AnEntry);
|
||||
Dec(CountNeeded);
|
||||
inc(NextIdx);
|
||||
CodeReadErrCnt := 0;
|
||||
If (NextIdx > MAX_FRAMES) then
|
||||
break;
|
||||
LastFrameBase := FrameBase;
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
if LastFrameBase > 0 then
|
||||
LastFrameBase := LastFrameBase - 1;
|
||||
{$POP}
|
||||
continue;
|
||||
end;
|
||||
Address := PrevAddress;
|
||||
FrameBase := PrevFrameBase;
|
||||
StackPtr := PrevStackPtr;
|
||||
|
||||
if (FrameBase <> 0) and (FrameBase > LastFrameBase)
|
||||
then begin
|
||||
if StackPtr = 0 then
|
||||
break;
|
||||
// CFI not available or contains unsupported structures. Fallback to
|
||||
// old fashioned stack-tracing.
|
||||
OutSideFrame := False;
|
||||
if not Process.Disassembler.GetFunctionFrameInfo(Address, OutSideFrame) then begin
|
||||
if Process.Disassembler.LastErrorWasMemReadErr then begin
|
||||
inc(CodeReadErrCnt);
|
||||
if CodeReadErrCnt > 5 then break; // If the code cannot be read the stack pointer is wrong.
|
||||
if NextIdx <= 1 then
|
||||
OutSideFrame := True; // Maybe after "TProc(nil)();" call, then no frame could have been set up
|
||||
end;
|
||||
end;
|
||||
LastFrameBase := FrameBase;
|
||||
|
||||
if (not OutSideFrame) and (NextIdx = 1) and (AnEntry.ProcSymbol <> nil) then begin
|
||||
OutSideFrame := Address = LocToAddrOrNil(AnEntry.ProcSymbol.Address); // the top frame must be outside frame, if it is at entrypoint / needed for exceptions
|
||||
end;
|
||||
|
||||
if OutSideFrame then begin
|
||||
if not Process.ReadData(StackPtr, Size, Address) or (Address = 0) then Break;
|
||||
|
||||
if (not Process.ReadData(Address, 1, Dummy) or (Address = 0)) then begin
|
||||
OutSideFrame := False;
|
||||
end
|
||||
else begin
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
StackPtr := StackPtr + 1 * Size; // After popping return-addr from "StackPtr"
|
||||
if LastFrameBase > 0 then
|
||||
LastFrameBase := LastFrameBase - 1; // Make the loop think thas LastFrameBase was smaller
|
||||
{$POP}
|
||||
// last stack has no frame
|
||||
//AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(0, '0',Size, BP);
|
||||
end;
|
||||
end;
|
||||
if not OutSideFrame then begin
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
StackPtr := FrameBase + 2 * Size; // After popping return-addr from "FrameBase + Size"
|
||||
{$POP}
|
||||
if not Process.ReadData(FrameBase + Size, Size, Address) or (Address = 0) then Break;
|
||||
if not Process.ReadData(FrameBase, Size, FrameBase) then Break;
|
||||
end;
|
||||
AnEntry := TDbgCallstackEntry.create(Self, NextIdx, FrameBase, Address);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nIP].SetValue(Address, IntToStr(Address),Size, IP);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(FrameBase, IntToStr(FrameBase),Size, BP);
|
||||
AnEntry.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPtr, IntToStr(StackPtr),Size, SP);
|
||||
CheckFrame(AnEntry, FrameBase < StackPtr);
|
||||
FCallStackEntryList.Add(AnEntry);
|
||||
Dec(CountNeeded);
|
||||
inc(NextIdx);
|
||||
CodeReadErrCnt := 0;
|
||||
If (NextIdx > MAX_FRAMES) then
|
||||
break;
|
||||
end
|
||||
else
|
||||
begin
|
||||
AnEntry := nil;
|
||||
CheckFrame(AnEntry);
|
||||
if AnEntry <> nil then
|
||||
FCallStackEntryList.Add(AnEntry)
|
||||
else
|
||||
Break;
|
||||
end;
|
||||
FCallStackEntryList.Add(AnEntry);
|
||||
dec(CountNeeded);
|
||||
inc(NextIdx);
|
||||
end;
|
||||
if CountNeeded > 0 then // there was an error / not possible to read more frames
|
||||
FCallStackEntryList.SetHasReadAllAvailableFrames;
|
||||
|
307
components/fpdebug/fpdbgcpux86.pas
Normal file
307
components/fpdebug/fpdbgcpux86.pas
Normal file
@ -0,0 +1,307 @@
|
||||
unit FpDbgCpuX86;
|
||||
|
||||
{$mode objfpc}{$H+}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes, SysUtils, DbgIntfBaseTypes, FpDbgClasses, FpdMemoryTools,
|
||||
FpDbgDwarfCFI, FpDbgDwarfDataClasses, FpDbgDisasX86;
|
||||
|
||||
type
|
||||
|
||||
{ TDbgStackUnwinderX86FramePointer }
|
||||
|
||||
TDbgStackUnwinderX86FramePointer = class(TDbgStackUnwinderX86Base)
|
||||
private
|
||||
FLastFrameBaseIncreased: Boolean;
|
||||
FCodeReadErrCnt: integer;
|
||||
public
|
||||
procedure InitForThread(AThread: TDbgThread); override;
|
||||
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderX86DwarfCfi }
|
||||
|
||||
TDbgStackUnwinderX86DwarfCfi = class(TDbgStackUnwinderX86Base)
|
||||
// Maybe have TDbgStackUnwinderX86Base as delegate instead of base
|
||||
private
|
||||
public
|
||||
//procedure InitForThread(AThread: TDbgThread); override;
|
||||
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderX86MultiMethod }
|
||||
|
||||
TDbgStackUnwinderX86MultiMethod = class(TDbgStackUnwinderX86Base)
|
||||
// Base is for GetTopFrame // Alternatively forward to any of the sub-unwinder
|
||||
private
|
||||
FDwarfUnwinder: TDbgStackUnwinderX86DwarfCfi;
|
||||
FFrameUnwinder: TDbgStackUnwinderX86FramePointer;
|
||||
FAsmUnwinder: TDbgStackUnwinderIntelDisAssembler;
|
||||
public
|
||||
constructor Create(AProcess: TDbgProcess);
|
||||
destructor Destroy; override;
|
||||
procedure InitForThread(AThread: TDbgThread); override;
|
||||
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TDbgStackUnwinderX86FramePointer }
|
||||
|
||||
procedure TDbgStackUnwinderX86FramePointer.InitForThread(AThread: TDbgThread);
|
||||
begin
|
||||
inherited InitForThread(AThread);
|
||||
FLastFrameBaseIncreased := True;
|
||||
FCodeReadErrCnt := 0;
|
||||
end;
|
||||
|
||||
function TDbgStackUnwinderX86FramePointer.Unwind(AFrameIndex: integer;
|
||||
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||
): TTDbgStackUnwindResult;
|
||||
var
|
||||
OutSideFrame: Boolean;
|
||||
Dummy: QWord;
|
||||
LastFrameBase: TDBGPtr;
|
||||
NewRes: TTDbgStackUnwindResult;
|
||||
begin
|
||||
ANewFrame := nil;
|
||||
Result := suFailed;
|
||||
NewRes := suSuccess;
|
||||
|
||||
if StackPointer = 0 then
|
||||
exit;
|
||||
if not FLastFrameBaseIncreased then
|
||||
exit;
|
||||
|
||||
OutSideFrame := False;
|
||||
LastFrameBase := FrameBasePointer;
|
||||
|
||||
if not Process.Disassembler.GetFunctionFrameInfo(CodePointer, OutSideFrame) then begin
|
||||
if Process.Disassembler.LastErrorWasMemReadErr then begin
|
||||
inc(FCodeReadErrCnt);
|
||||
if FCodeReadErrCnt > 5 then // If the code cannot be read the stack pointer is wrong.
|
||||
exit;
|
||||
if AFrameIndex <= 1 then
|
||||
OutSideFrame := True; // Maybe after "TProc(nil)();" call, then no frame could have been set up
|
||||
end;
|
||||
end;
|
||||
if (not OutSideFrame) {and (AFrameIndex = 1)} and (ACurrentFrame.ProcSymbol <> nil) then begin
|
||||
// the frame must be outside frame, if it is at entrypoint / needed for exceptions
|
||||
OutSideFrame := CodePointer = LocToAddrOrNil(ACurrentFrame.ProcSymbol.Address);
|
||||
end;
|
||||
|
||||
if OutSideFrame then begin
|
||||
if not Process.ReadData(StackPointer, AddressSize, CodePointer) or (CodePointer = 0) then
|
||||
exit;
|
||||
|
||||
if (not Process.ReadData(CodePointer, 1, Dummy) or (CodePointer = 0)) then begin
|
||||
OutSideFrame := False;
|
||||
NewRes := suGuessed;
|
||||
end
|
||||
else begin
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
StackPointer := StackPointer + 1 * AddressSize; // After popping return-addr from "StackPointer"
|
||||
if LastFrameBase > 0 then
|
||||
LastFrameBase := LastFrameBase - 1; // Make the loop think thas LastFrameBase was smaller
|
||||
{$POP}
|
||||
// last stack has no frame
|
||||
//ACurrentFrame.RegisterValueList.DbgRegisterAutoCreate[nBP].SetValue(0, '0',AddressSize, BP);
|
||||
end;
|
||||
end;
|
||||
|
||||
if not OutSideFrame then begin
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
StackPointer := FrameBasePointer + 2 * AddressSize; // After popping return-addr from "FrameBasePointer + AddressSize"
|
||||
{$POP}
|
||||
if not Process.ReadData(FrameBasePointer + AddressSize, AddressSize, CodePointer) or (CodePointer = 0) then
|
||||
exit;
|
||||
if not Process.ReadData(FrameBasePointer, AddressSize, FrameBasePointer) then
|
||||
exit;
|
||||
end;
|
||||
|
||||
FLastFrameBaseIncreased := (FrameBasePointer <> 0) and (FrameBasePointer > LastFrameBase);
|
||||
if not FLastFrameBaseIncreased then
|
||||
NewRes := suGuessed;
|
||||
|
||||
|
||||
ANewFrame := TDbgCallstackEntry.create(Thread, AFrameIndex, FrameBasePointer, CodePointer);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameIP].SetValue(CodePointer, IntToStr(CodePointer),AddressSize, FDwarfNumIP);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameBP].SetValue(FrameBasePointer, IntToStr(FrameBasePointer),AddressSize, FDwarfNumBP);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameSP].SetValue(StackPointer, IntToStr(StackPointer),AddressSize, FDwarfNumSP);
|
||||
FCodeReadErrCnt := 0;
|
||||
Result := NewRes;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderX86DwarfCfi }
|
||||
|
||||
function TDbgStackUnwinderX86DwarfCfi.Unwind(AFrameIndex: integer;
|
||||
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||
): TTDbgStackUnwindResult;
|
||||
var
|
||||
PrevStmtAddressOffs: Integer;
|
||||
Row: TDwarfCallFrameInformationRow;
|
||||
CIE: TDwarfCIE;
|
||||
CU: TDwarfCompilationUnit;
|
||||
StackReg, FrameReg: TDbgRegisterValue;
|
||||
begin
|
||||
ANewFrame := nil;
|
||||
Result := suFailed;
|
||||
|
||||
PrevStmtAddressOffs := 1;
|
||||
if AFrameIndex <= 1 then
|
||||
PrevStmtAddressOffs := 0;
|
||||
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
if (Process.DbgInfo as TFpDwarfInfo).FindCallFrameInfo(CodePointer - PrevStmtAddressOffs, CIE, Row) and
|
||||
TDwarfCallFrameInformation.TryObtainNextCallFrame(
|
||||
ACurrentFrame, CIE, AddressSize, AFrameIndex, Thread, Row, Process, ANewFrame
|
||||
)
|
||||
{$POP}
|
||||
then begin
|
||||
if not Assigned(ANewFrame) then begin
|
||||
CU := (Process.DbgInfo as TFpDwarfInfo).CompilationUnitForAddr(CodePointer);
|
||||
if (CU = nil) or (CU.DwarfSymbolClassMap = nil) or (not CU.DwarfSymbolClassMap.IgnoreCfiStackEnd) then begin
|
||||
// Done.
|
||||
Result := suFailedAtEOS;
|
||||
exit;
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
Result := suSuccess;
|
||||
CodePointer := ANewFrame.AnAddress;
|
||||
StackReg := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumSP);
|
||||
FrameReg := ANewFrame.RegisterValueList.FindRegisterByDwarfIndex(FDwarfNumBP);
|
||||
StackPointer := 0;
|
||||
FrameBasePointer := 0;
|
||||
if (StackReg <> nil) and (FrameReg <> nil) then begin
|
||||
StackPointer := StackReg.NumValue;
|
||||
FrameBasePointer := FrameReg.NumValue;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderX86MultiMethod }
|
||||
|
||||
constructor TDbgStackUnwinderX86MultiMethod.Create(AProcess: TDbgProcess);
|
||||
begin
|
||||
FDwarfUnwinder := TDbgStackUnwinderX86DwarfCfi.Create(AProcess);
|
||||
FFrameUnwinder := TDbgStackUnwinderX86FramePointer.Create(AProcess);
|
||||
FAsmUnwinder := TDbgStackUnwinderIntelDisAssembler.Create(AProcess);
|
||||
inherited Create(AProcess);
|
||||
end;
|
||||
|
||||
destructor TDbgStackUnwinderX86MultiMethod.Destroy;
|
||||
begin
|
||||
inherited Destroy;
|
||||
FDwarfUnwinder.Free;
|
||||
FFrameUnwinder.Free;
|
||||
FAsmUnwinder.Free;
|
||||
end;
|
||||
|
||||
procedure TDbgStackUnwinderX86MultiMethod.InitForThread(AThread: TDbgThread);
|
||||
begin
|
||||
inherited InitForThread(AThread);
|
||||
FDwarfUnwinder.InitForThread(AThread);
|
||||
FFrameUnwinder.InitForThread(AThread);
|
||||
FAsmUnwinder.InitForThread(AThread);
|
||||
end;
|
||||
|
||||
function TDbgStackUnwinderX86MultiMethod.Unwind(AFrameIndex: integer;
|
||||
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||
): TTDbgStackUnwindResult;
|
||||
var
|
||||
OrigCodePointer, OrigFrameBasePointer, OrigStackPointer: TDBGPtr;
|
||||
CodePointer2, FrameBasePointer2, StackPointer2: TDBGPtr;
|
||||
ANewFrame2: TDbgCallstackEntry;
|
||||
ResAsm: TTDbgStackUnwindResult;
|
||||
begin
|
||||
OrigCodePointer := CodePointer;
|
||||
OrigFrameBasePointer := FrameBasePointer;
|
||||
OrigStackPointer := StackPointer;
|
||||
|
||||
Result := FDwarfUnwinder.Unwind(AFrameIndex,
|
||||
CodePointer, StackPointer, FrameBasePointer, ACurrentFrame, ANewFrame);
|
||||
|
||||
if (Result = suSuccess) then begin
|
||||
FFrameUnwinder.FLastFrameBaseIncreased := True;
|
||||
if Process.Disassembler.IsAfterCallInstruction(CodePointer) then
|
||||
exit;
|
||||
end;
|
||||
if Result = suFailedAtEOS then
|
||||
exit;
|
||||
|
||||
// Get Asm unwind
|
||||
CodePointer2 := OrigCodePointer;
|
||||
FrameBasePointer2 := OrigFrameBasePointer;
|
||||
StackPointer2 := OrigStackPointer;
|
||||
ResAsm := FAsmUnwinder.Unwind(AFrameIndex,
|
||||
CodePointer2, StackPointer2, FrameBasePointer2, ACurrentFrame, ANewFrame2);
|
||||
|
||||
if (ResAsm = suSuccess) then begin
|
||||
// prefer Asm result over DwarfCfi
|
||||
FFrameUnwinder.FLastFrameBaseIncreased := True;
|
||||
if Process.Disassembler.IsAfterCallInstruction(CodePointer2) then begin
|
||||
ANewFrame.Free;
|
||||
CodePointer := CodePointer2;
|
||||
FrameBasePointer := FrameBasePointer2;
|
||||
StackPointer := StackPointer2;
|
||||
ANewFrame := ANewFrame2;
|
||||
Result := suSuccess;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
if (ResAsm in [suSuccess, suGuessed]) and
|
||||
(Result in [suSuccess, suGuessed]) and
|
||||
(CodePointer = CodePointer2) and (StackPointer = StackPointer2) and (FrameBasePointer = FrameBasePointer2)
|
||||
then begin
|
||||
// Both results where unsure => but both equal
|
||||
FFrameUnwinder.FLastFrameBaseIncreased := True;
|
||||
ANewFrame2.Free;
|
||||
Result := suSuccess;
|
||||
exit;
|
||||
end;
|
||||
|
||||
|
||||
ANewFrame.Free;
|
||||
// get frame-unwind
|
||||
CodePointer := OrigCodePointer;
|
||||
FrameBasePointer := OrigFrameBasePointer;
|
||||
StackPointer := OrigStackPointer;
|
||||
Result := FFrameUnwinder.Unwind(AFrameIndex,
|
||||
CodePointer, StackPointer, FrameBasePointer, ACurrentFrame, ANewFrame);
|
||||
|
||||
if (Result = suSuccess) then begin
|
||||
if Process.Disassembler.IsAfterCallInstruction(CodePointer2) then
|
||||
exit;
|
||||
end;
|
||||
|
||||
if (ResAsm in [suSuccess, suGuessed]) then begin
|
||||
ANewFrame.Free;
|
||||
CodePointer := CodePointer2;
|
||||
FrameBasePointer := FrameBasePointer2;
|
||||
StackPointer := StackPointer2;
|
||||
ANewFrame := ANewFrame2;
|
||||
Result := suSuccess;
|
||||
exit;
|
||||
end;
|
||||
|
||||
|
||||
end;
|
||||
|
||||
end.
|
||||
|
@ -114,6 +114,7 @@ type
|
||||
protected
|
||||
function ReadThreadState: boolean;
|
||||
function ReadDebugState: boolean;
|
||||
// function GetStackUnwinder: TDbgStackUnwinder; override;
|
||||
public
|
||||
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
||||
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
||||
|
@ -287,21 +287,23 @@ type
|
||||
TOperandFlag = (ofMemory);
|
||||
TOperandFlags = set of TOperandFlag;
|
||||
|
||||
TInstructionOperand = record
|
||||
CodeIndex: integer;
|
||||
Value: String;
|
||||
Size: TOperandSize;
|
||||
ByteCount: Word;
|
||||
ByteCount2: Byte;
|
||||
FormatFlags: THexValueFormatFlags;
|
||||
Flags: TOperandFlags;
|
||||
end;
|
||||
|
||||
TInstruction = record
|
||||
OpCode: TFullOpcode;
|
||||
Flags: set of TInstructionFlag;
|
||||
Segment: String;
|
||||
MaskIndex: Byte;
|
||||
|
||||
Operand: array[1..4] of record
|
||||
CodeIndex: integer;
|
||||
Value: String;
|
||||
Size: TOperandSize;
|
||||
ByteCount: Word;
|
||||
ByteCount2: Byte;
|
||||
FormatFlags: THexValueFormatFlags;
|
||||
Flags: TOperandFlags;
|
||||
end;
|
||||
Operand: array[1..4] of TInstructionOperand;
|
||||
OperCnt: Integer;
|
||||
|
||||
ParseFlags: TFlags;
|
||||
@ -575,6 +577,18 @@ type
|
||||
function UnwindFrame(var AnAddress, AStackPtr, AFramePtr: TDBGPtr; AQuick: boolean): boolean; override;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderIntelDisAssembler }
|
||||
|
||||
TDbgStackUnwinderIntelDisAssembler = class(TDbgStackUnwinderX86Base)
|
||||
private
|
||||
//FCodeReadErrCnt: integer;
|
||||
public
|
||||
//procedure InitForThread(AThread: TDbgThread); override;
|
||||
function Unwind(AFrameIndex: integer; var CodePointer, StackPointer,
|
||||
FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out
|
||||
ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
var
|
||||
@ -5272,17 +5286,74 @@ function TX86AsmDecoder.UnwindFrame(var AnAddress, AStackPtr,
|
||||
Result := 8;
|
||||
end;
|
||||
|
||||
var
|
||||
NewAddr, NewStack, NewFrame: TDBGPtr;
|
||||
CurAddr: PByte;
|
||||
|
||||
function ValueFromOperand(constref Oper: TInstructionOperand; out AVal: TDBGPtr): boolean;
|
||||
var
|
||||
OpVal: Int64;
|
||||
Src: TDBGPtr;
|
||||
RSize: Cardinal;
|
||||
begin
|
||||
AVal := 0;
|
||||
Result := True;
|
||||
if (Oper.ByteCount2 <> 0) then
|
||||
exit(False);
|
||||
|
||||
if (Oper.ByteCount = 0)
|
||||
then begin
|
||||
if (IsRegister(Oper.Value, 'bp')) then
|
||||
AVal := NewFrame
|
||||
else
|
||||
if (IsRegister(Oper.Value, 'sp')) then
|
||||
AVal := NewStack
|
||||
else
|
||||
exit(False);
|
||||
end
|
||||
else
|
||||
if (Oper.ByteCount <> 0)
|
||||
then begin
|
||||
OpVal := ValueFromMem(CurAddr[Oper.CodeIndex], Oper.ByteCount, Oper.FormatFlags);
|
||||
|
||||
if (IsRegister(Oper.Value, 'bp%s')) then
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
AVal := NewFrame + OpVal
|
||||
{$POP}
|
||||
else
|
||||
if (IsRegister(Oper.Value, 'sp%s')) then
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
AVal := NewStack + OpVal
|
||||
{$POP}
|
||||
else
|
||||
if (Oper.Value = '%s') and (not(ofMemory in Oper.Flags))
|
||||
then
|
||||
AVal := TDBGPtr(OpVal) // constant
|
||||
else
|
||||
exit(False);
|
||||
end
|
||||
else
|
||||
exit(False);
|
||||
|
||||
if (ofMemory in Oper.Flags) then begin
|
||||
Src := AVal;
|
||||
AVal := 0;
|
||||
RSize := RegisterSize(Oper.Value);
|
||||
if not FProcess.ReadData(Src, RSize, AVal, RSize) then
|
||||
exit(False);
|
||||
end;
|
||||
end;
|
||||
|
||||
const
|
||||
MAX_SEARCH_ADDR = 8000;
|
||||
MAX_SEARCH_CNT = 400;
|
||||
var
|
||||
NewAddr, NewStack, NewFrame, MaxAddr, MaxAddr2, StartAddr, StartStack, Tmp: TDBGPtr;
|
||||
MaxAddr, MaxAddr2, StartAddr, StartStack, Tmp: TDBGPtr;
|
||||
ConditionalForwardAddr, BackwardJumpAddress: TDBGPtr;
|
||||
Cnt: Integer;
|
||||
instr: TX86AsmInstruction;
|
||||
RSize: Cardinal;
|
||||
Val: Int64;
|
||||
CurAddr: PByte;
|
||||
begin
|
||||
Result := False;
|
||||
NewAddr := AnAddress;
|
||||
@ -5324,8 +5395,9 @@ begin
|
||||
case instr.X86OpCode of
|
||||
OPret:
|
||||
begin
|
||||
if instr.X86Instruction.OperCnt > 1 then
|
||||
if instr.X86Instruction.OperCnt > 1 then begin
|
||||
exit;
|
||||
end;
|
||||
|
||||
Val := 0;
|
||||
if instr.X86Instruction.OperCnt = 1 then
|
||||
@ -5415,20 +5487,19 @@ begin
|
||||
exit;
|
||||
|
||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp') and
|
||||
not(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
||||
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||
then begin
|
||||
if (not IsRegister(instr.X86Instruction.Operand[2].Value, 'bp')) or
|
||||
(Instr.X86Instruction.Operand[2].ByteCount <> 0) or
|
||||
(Instr.X86Instruction.Operand[2].ByteCount2 <> 0) or
|
||||
(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
||||
then
|
||||
exit; // TODO: check if source is known part of stack
|
||||
NewStack := NewFrame;
|
||||
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||
exit;
|
||||
NewStack := Tmp;
|
||||
end;
|
||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp') and
|
||||
not(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
||||
then
|
||||
exit; // TODO: check if source is SP or known part of stack
|
||||
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||
then begin
|
||||
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||
exit;
|
||||
NewFrame := Tmp;
|
||||
end;
|
||||
end;
|
||||
OPlea:
|
||||
begin
|
||||
@ -5436,17 +5507,24 @@ begin
|
||||
exit;
|
||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp')
|
||||
then begin
|
||||
if (not IsRegister(instr.X86Instruction.Operand[2].Value, 'bp%s')) or
|
||||
(Instr.X86Instruction.Operand[2].ByteCount = 0) or
|
||||
if (Instr.X86Instruction.Operand[2].ByteCount = 0) or
|
||||
(Instr.X86Instruction.Operand[2].ByteCount2 <> 0) or
|
||||
not(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
||||
then
|
||||
exit;
|
||||
|
||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
NewFrame := NewFrame + Val;
|
||||
{$POP}
|
||||
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'sp%s')) then begin
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
NewFrame := NewStack + Val;
|
||||
{$POP}
|
||||
end
|
||||
else
|
||||
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'bp%s')) then begin
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
NewFrame := NewFrame + Val;
|
||||
{$POP}
|
||||
end;
|
||||
end;
|
||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp')
|
||||
then begin
|
||||
@ -5456,15 +5534,14 @@ begin
|
||||
then
|
||||
exit;
|
||||
|
||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
||||
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'sp%s')) then begin
|
||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
NewStack := NewStack + Val;
|
||||
{$POP}
|
||||
end
|
||||
else
|
||||
if (IsRegister(instr.X86Instruction.Operand[2].Value, 'bp%s')) then begin
|
||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
NewStack := NewFrame + Val;
|
||||
{$POP}
|
||||
@ -5477,18 +5554,23 @@ begin
|
||||
begin
|
||||
if instr.X86Instruction.OperCnt <> 2 then
|
||||
exit;
|
||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp')
|
||||
then begin
|
||||
if (instr.X86Instruction.Operand[2].Value <> '%s') or
|
||||
(Instr.X86Instruction.Operand[2].ByteCount = 0) or
|
||||
(Instr.X86Instruction.Operand[2].ByteCount2 <> 0) or
|
||||
(ofMemory in Instr.X86Instruction.Operand[2].Flags)
|
||||
then
|
||||
exit;
|
||||
|
||||
Val := ValueFromMem(CurAddr[Instr.X86Instruction.Operand[2].CodeIndex], Instr.X86Instruction.Operand[2].ByteCount, Instr.X86Instruction.Operand[2].FormatFlags);
|
||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'sp') and
|
||||
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||
then begin
|
||||
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||
exit;
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
NewStack := NewStack + Val;
|
||||
NewStack := NewStack + int64(Tmp);
|
||||
{$POP}
|
||||
end;
|
||||
if IsRegister(instr.X86Instruction.Operand[1].Value, 'bp') and
|
||||
not(ofMemory in Instr.X86Instruction.Operand[1].Flags)
|
||||
then begin
|
||||
if not ValueFromOperand(instr.X86Instruction.Operand[2], Tmp) then
|
||||
exit;
|
||||
{$PUSH}{$R-}{$Q-}
|
||||
NewFrame := NewFrame + int64(Tmp);
|
||||
{$POP}
|
||||
end;
|
||||
end;
|
||||
@ -5557,6 +5639,26 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TDbgStackUnwinderIntelDisAssembler }
|
||||
|
||||
function TDbgStackUnwinderIntelDisAssembler.Unwind(AFrameIndex: integer;
|
||||
var CodePointer, StackPointer, FrameBasePointer: TDBGPtr;
|
||||
ACurrentFrame: TDbgCallstackEntry; out ANewFrame: TDbgCallstackEntry
|
||||
): TTDbgStackUnwindResult;
|
||||
begin
|
||||
ANewFrame := nil;
|
||||
Result := suFailed;
|
||||
|
||||
if Process.Disassembler.UnwindFrame(CodePointer, StackPointer, FrameBasePointer, False)
|
||||
then begin
|
||||
ANewFrame := TDbgCallstackEntry.Create(Thread, AFrameIndex, FrameBasePointer, CodePointer);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameIP].SetValue(CodePointer, IntToStr(CodePointer),AddressSize, FDwarfNumIP);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameBP].SetValue(FrameBasePointer, IntToStr(FrameBasePointer),AddressSize, FDwarfNumBP);
|
||||
ANewFrame.RegisterValueList.DbgRegisterAutoCreate[FNameSP].SetValue(StackPointer, IntToStr(StackPointer),AddressSize, FDwarfNumSP);
|
||||
Result := suSuccess;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
initialization
|
||||
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
||||
|
@ -27,7 +27,8 @@ uses
|
||||
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, Maps,
|
||||
FpDbgCommon, FpdMemoryTools,
|
||||
FpErrorMessages,
|
||||
FpImgReaderBase;
|
||||
FpImgReaderBase,
|
||||
FpDbgCpuX86;
|
||||
|
||||
type
|
||||
user_regs_struct64 = record
|
||||
@ -274,6 +275,7 @@ type
|
||||
FIsSteppingBreakPoint: boolean;
|
||||
FDidResetInstructionPointer: Boolean;
|
||||
FHasThreadState: boolean;
|
||||
FUnwinder: TDbgStackUnwinderX86MultiMethod;
|
||||
function GetDebugRegOffset(ind: byte): pointer;
|
||||
function ReadDebugReg(ind: byte; out AVal: PtrUInt): boolean;
|
||||
function WriteDebugReg(ind: byte; AVal: PtrUInt): boolean;
|
||||
@ -284,7 +286,9 @@ type
|
||||
function RequestInternalPause: Boolean;
|
||||
function CheckSignalForPostponing(AWaitedStatus: cint): Boolean;
|
||||
procedure ResetPauseStates;
|
||||
function GetStackUnwinder: TDbgStackUnwinder; override;
|
||||
public
|
||||
destructor Destroy; override;
|
||||
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
||||
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
||||
function DetectHardwareWatchpoint: Pointer; override;
|
||||
@ -666,6 +670,19 @@ begin
|
||||
FDidResetInstructionPointer := False;
|
||||
end;
|
||||
|
||||
function TDbgLinuxThread.GetStackUnwinder: TDbgStackUnwinder;
|
||||
begin
|
||||
if FUnwinder = nil then
|
||||
FUnwinder := TDbgStackUnwinderX86MultiMethod.Create(Process);
|
||||
Result := FUnwinder;
|
||||
end;
|
||||
|
||||
destructor TDbgLinuxThread.Destroy;
|
||||
begin
|
||||
FUnwinder.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TDbgLinuxThread.ResetInstructionPointerAfterBreakpoint: boolean;
|
||||
begin
|
||||
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgLinuxThread.ResetInstructionPointerAfterBreakpoint');{$ENDIF}
|
||||
|
@ -120,7 +120,7 @@ uses
|
||||
FpDbgLoader, FpDbgDisasX86,
|
||||
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
||||
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, UTF8Process,
|
||||
FpDbgCommon, FpdMemoryTools, FpErrorMessages;
|
||||
FpDbgCommon, FpdMemoryTools, FpErrorMessages, FpDbgCpuX86;
|
||||
|
||||
type
|
||||
|
||||
@ -140,6 +140,7 @@ type
|
||||
FBreakPointState: TBreakPointState;
|
||||
FDoNotPollName: Boolean;
|
||||
FName: String;
|
||||
FUnwinder: TDbgStackUnwinderX86MultiMethod;
|
||||
protected
|
||||
FThreadContextChanged: boolean;
|
||||
FThreadContextChangeFlags: TFpContextChangeFlags;
|
||||
@ -150,7 +151,9 @@ type
|
||||
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;
|
||||
@ -1673,6 +1676,19 @@ begin
|
||||
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;
|
||||
|
@ -229,6 +229,10 @@ File(s) with other licenses (see also header in file(s):
|
||||
<Filename Value="fpdbgdwarfcfi.pas"/>
|
||||
<UnitName Value="FpDbgDwarfCFI"/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Filename Value="fpdbgcpux86.pas"/>
|
||||
<UnitName Value="fpdbgcpux86"/>
|
||||
</Item>
|
||||
</Files>
|
||||
<i18n>
|
||||
<EnableI18N Value="True"/>
|
||||
|
@ -16,7 +16,7 @@ uses
|
||||
FpDbgDwarfDataClasses, FpDbgDwarfFreePascal, fpDbgSymTableContext,
|
||||
fpDbgSymTable, FpDbgAvrClasses, FpDbgDisasAvr, FpDbgRsp, FpDbgCommon,
|
||||
FpImgReaderWinPETypes, FpDbgHardcodedFreepascalInfo, FpDbgCallContextInfo,
|
||||
FpWatchResultData, FpDbgDwarfCFI;
|
||||
FpWatchResultData, FpDbgDwarfCFI, FpDbgCpuX86;
|
||||
|
||||
implementation
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user