FpDebug: refactor stack unwinding. Introduce TDbgStackUnwinder and new classes for X86

This commit is contained in:
Martin 2023-11-27 21:34:27 +01:00
parent b5db1bf52f
commit 476cfd4484
8 changed files with 625 additions and 269 deletions

View File

@ -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;

View 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.

View File

@ -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;

View File

@ -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} );

View File

@ -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}

View File

@ -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;

View File

@ -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"/>

View File

@ -16,7 +16,7 @@ uses
FpDbgDwarfDataClasses, FpDbgDwarfFreePascal, fpDbgSymTableContext,
fpDbgSymTable, FpDbgAvrClasses, FpDbgDisasAvr, FpDbgRsp, FpDbgCommon,
FpImgReaderWinPETypes, FpDbgHardcodedFreepascalInfo, FpDbgCallContextInfo,
FpWatchResultData, FpDbgDwarfCFI;
FpWatchResultData, FpDbgDwarfCFI, FpDbgCpuX86;
implementation