{ $Id$ } { --------------------------------------------------------------------------- fpdbgclasses.pp - Native freepascal debugger --------------------------------------------------------------------------- This unit contains debugger classes for a native freepascal debugger --------------------------------------------------------------------------- @created(Mon Apr 10th WET 2006) @lastmod($Date$) @author(Marc Weustink ) *************************************************************************** * * * 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 . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * * *************************************************************************** } unit FpDbgClasses; {$mode objfpc}{$H+} interface uses {$ifdef windows} Windows, {$endif} Classes, SysUtils, Maps, FpDbgUtil, FpDbgWinExtra, FpDbgLoader, LazLoggerBase, LazClasses; type TDbgPtr = QWord; // PtrUInt; {$ifdef windows} TDbgProcess = class; {$endif} {$ifdef windows} TDbgThread = class(TObject) private FProcess: TDbgProcess; FID: Integer; FHandle: THandle; FBaseAddr: Pointer; FStartAddr: Pointer; FSingleStepping: Boolean; protected public constructor Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle; const ABase, AStart: Pointer); destructor Destroy; override; function SingleStep: Boolean; property ID: Integer read FID; property Handle: THandle read FHandle; property SingleStepping: boolean read FSingleStepping; end; {$endif} TDbgSymbolType = ( stNone, stValue, // The symbol has a value (var, field, function, procedure (value is address of func/proc, so it can be called) stType // The Symbol is a type (including proc/func declaration / without DW_AT_low_pc) ); TDbgSymbolKind = ( skNone, // undefined type // skUser, // userdefined type, this sym refers to another sym defined elswhere skInstance, // the main exe/dll, containing all other syms skUnit, // contains syms defined in this unit //-------------------------------------------------------------------------- skRecord, // the address member is the relative location within the skObject, // structure skClass, skInterface, skProcedure, skFunction, //-------------------------------------------------------------------------- skArray, //-------------------------------------------------------------------------- skPointer, skInteger, // Basic types, these cannot have references or children skCardinal, // only size matters ( char(1) = Char, char(2) = WideChar skBoolean, // cardinal(1) = Byte etc. skChar, skFloat, skString, skAnsiString, skCurrency, skVariant, skWideString, skEnum, skSet, //-------------------------------------------------------------------------- skRegister // the Address member is the register number //-------------------------------------------------------------------------- ); TDbgSymbolFlag =( //sfPointer, // The sym is a pointer to the reference sfConst, // The sym is a constant and cannot be modified sfVar, sfOut, sfpropGet, sfPropSet, sfPropStored ); TDbgSymbolFlags = set of TDbgSymbolFlag; TDbgSymbolField = ( sfiName, sfiKind, sfiSymType, sfiAddress, sfiSize ); TDbgSymbolFields = set of TDbgSymbolField; { TDbgSymbol } TDbgSymbol = class(TRefCountedObject) private FEvaluatedFields: TDbgSymbolFields; // Cached fields FName: String; FKind: TDbgSymbolKind; FSymbolType: TDbgSymbolType; FAddress: TDbgPtr; FSize: Integer; function GetSymbolType: TDbgSymbolType; inline; function GetKind: TDbgSymbolKind; inline; function GetName: String; function GetSize: Integer; function GetAddress: TDbgPtr; protected // NOT cached fields function GetPointedToType: TDbgSymbol; virtual; function GetChild(AIndex: Integer): TDbgSymbol; virtual; function GetColumn: Cardinal; virtual; function GetCount: Integer; virtual; function GetFile: String; virtual; function GetFlags: TDbgSymbolFlags; virtual; function GetLine: Cardinal; virtual; function GetParent: TDbgSymbol; virtual; function GetReference: TDbgSymbol; virtual; protected // Cached fields procedure SetName(AValue: String); procedure SetKind(AValue: TDbgSymbolKind); procedure SetSymbolType(AValue: TDbgSymbolType); procedure SetAddress(AValue: TDbgPtr); procedure SetSize(AValue: Integer); procedure KindNeeded; virtual; procedure NameNeeded; virtual; procedure SymbolTypeNeeded; virtual; procedure AddressNeeded; virtual; procedure SizeNeeded; virtual; //procedure Needed; virtual; public constructor Create(const AName: String); constructor Create(const AName: String; AKind: TDbgSymbolKind; AAddress: TDbgPtr); destructor Destroy; override; // Basic info property Name: String read GetName; property SymbolType: TDbgSymbolType read GetSymbolType; property Kind: TDbgSymbolKind read GetKind; // Memory; Size is also part of type (byte vs word vs ...) property Address: TDbgPtr read GetAddress; property Size: Integer read GetSize; // In Bytes // Location property FileName: String read GetFile; property Line: Cardinal read GetLine; property Column: Cardinal read GetColumn; // property Flags: TDbgSymbolFlags read GetFlags; deprecated; property Count: Integer read GetCount; deprecated; property Reference: TDbgSymbol read GetReference; deprecated; property Parent: TDbgSymbol read GetParent; deprecated; //property Children[AIndex: Integer]: TDbgSymbol read GetChild; // For pointers only property PointedToType: TDbgSymbol read GetPointedToType; end; { TDbgInfo } TDbgInfo = class(TObject) private FHasInfo: Boolean; protected procedure SetHasInfo; public constructor Create(ALoader: TDbgImageLoader); virtual; function FindSymbol(const AName: String): TDbgSymbol; virtual; function FindSymbol(AAddress: TDbgPtr): TDbgSymbol; virtual; property HasInfo: Boolean read FHasInfo; function GetLineAddress(const AFileName: String; ALine: Cardinal): TDbgPtr; virtual; end; {$ifdef windows} TDbgBreakpoint = class; TDbgBreakpointEvent = procedure(const ASender: TDbgBreakpoint; const AContext: TContext) of object; TDbgBreakpoint = class(TObject) private FProcess: TDbgProcess; FLocation: TDbgPtr; FOrgValue: Byte; procedure SetBreak; procedure ResetBreak; protected public constructor Create(const AProcess: TDbgProcess; const ALocation: TDbgPtr); destructor Destroy; override; function Hit(const AThreadID: Integer): Boolean; property Location: TDbgPtr read FLocation; end; {$endif} { TDbgInstance } {$ifdef windows} TDbgInstance = class(TObject) private FName: String; FProcess: TDbgProcess; FModuleHandle: THandle; FBaseAddr: TDbgPtr; FBreakList: TList; FDbgInfo: TDbgInfo; FLoader: TDbgImageLoader; procedure LoadInfo; procedure CheckName; procedure SetName(const AValue: String); public constructor Create(const AProcess: TDbgProcess; const ADefaultName: String; const AModuleHandle: THandle; const ABaseAddr, ANameAddr: TDbgPtr; const AUnicode: Boolean); destructor Destroy; override; function AddBreak(const AFileName: String; ALine: Cardinal): TDbgBreakpoint; function AddrOffset: Int64; // gives the offset between the loaded addresses and the compiled addresses function FindSymbol(AAdress: TDbgPtr): TDbgSymbol; function RemoveBreak(const AFileName: String; ALine: Cardinal): Boolean; property Process: TDbgProcess read FProcess; property ModuleHandle: THandle read FModuleHandle; property BaseAddr: TDbgPtr read FBaseAddr; end; {$endif} {$ifdef windows} TDbgLibrary = class(TDbgInstance) private public constructor Create(const AProcess: TDbgProcess; const ADefaultName: String; const AInfo: TLoadDLLDebugInfo); property Name: String read FName; end; {$endif} { TDbgProcess } {$ifdef windows} TDbgProcess = class(TDbgInstance) private FProcessID: Integer; FThreadID: Integer; FInfo: TCreateProcessDebugInfo; FThreadMap: TMap; // map ThreadID -> ThreadObject FLibMap: TMap; // map LibAddr -> LibObject FBreakMap: TMap; // map BreakAddr -> BreakObject FSymInstances: TList; // list of dbgInstances with debug info FMainThread: TDbgThread; FCurrentBreakpoint: TDbgBreakpoint; // set if we are executing the code at the break // if the singlestep is done, set the break again FReEnableBreakStep: Boolean; // Set when we are reenabling a breakpoint // We need a single step, so the IP is after the break to set procedure SetName(const AValue: String); procedure ThreadDestroyed(const AThread: TDbgThread); protected public constructor Create(const ADefaultName: String; const AProcessID, AThreadID: Integer; const AInfo: TCreateProcessDebugInfo); destructor Destroy; override; function AddBreak(const ALocation: TDbgPtr): TDbgBreakpoint; function AddLib(const AInfo: TLoadDLLDebugInfo): TDbgLibrary; procedure AddThread(const AID: Integer; const AInfo: TCreateThreadDebugInfo); function FindSymbol(const AName: String): TDbgSymbol; function FindSymbol(AAdress: TDbgPtr): TDbgSymbol; function GetLib(const AHandle: THandle; out ALib: TDbgLibrary): Boolean; function GetThread(const AID: Integer; out AThread: TDbgThread): Boolean; procedure Interrupt; procedure ContinueDebugEvent(const AThread: TDbgThread; const ADebugEvent: TDebugEvent); function HandleDebugEvent(const ADebugEvent: TDebugEvent): Boolean; function RemoveBreak(const ALocation: TDbgPtr): Boolean; procedure RemoveLib(const AInfo: TUnloadDLLDebugInfo); procedure RemoveThread(const AID: DWord); function ReadData(const AAdress: TDbgPtr; const ASize: Cardinal; out AData): Boolean; function ReadOrdinal(const AAdress: TDbgPtr; out AData): Boolean; function ReadString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: String): Boolean; function ReadWString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: WideString): Boolean; function WriteData(const AAdress: TDbgPtr; const ASize: Cardinal; const AData): Boolean; property Handle: THandle read FInfo.hProcess; property Name: String read FName write SetName; end; {$endif} function dbgs(ADbgSymbolKind: TDbgSymbolKind): String; overload; implementation //uses // FpDbgDwarf; procedure LogLastError; begin DebugLn('FpDbg-ERROR: ', GetLastErrorText); end; function dbgs(ADbgSymbolKind: TDbgSymbolKind): String; begin Result := ''; WriteStr(Result, ADbgSymbolKind); end; {$ifdef windows} { TDbgInstance } function TDbgInstance.AddBreak(const AFileName: String; ALine: Cardinal): TDbgBreakpoint; var addr: TDbgPtr; begin Result := nil; if not FDbgInfo.HasInfo then Exit; addr := FDbgInfo.GetLineAddress(AFileName, ALine); if addr = 0 then Exit; Result := FProcess.AddBreak(addr - AddrOffset); end; function TDbgInstance.AddrOffset: Int64; begin Result := FLoader.ImageBase - BaseAddr; end; procedure TDbgInstance.CheckName; begin if FName = '' then FName := Format('@%p', [Pointer(PtrUInt(FBaseAddr))]); end; constructor TDbgInstance.Create(const AProcess: TDbgProcess; const ADefaultName: String; const AModuleHandle: THandle; const ABaseAddr, ANameAddr: TDbgPtr; const AUnicode: Boolean); var NamePtr: TDbgPtr; S: String; W: WideString; len: Integer; begin FBaseAddr := ABaseAddr; FModuleHandle := AModuleHandle; FBreakList := TList.Create; FProcess := AProcess; inherited Create; W := ''; if AProcess.ReadOrdinal(ANameAddr, NamePtr) then begin if AUnicode then begin AProcess.ReadWString(NamePtr, MAX_PATH, W); end else begin if AProcess.ReadString(NamePtr, MAX_PATH, S) then W := S; end; end; if W = '' then begin SetLength(S, MAX_PATH); len := GetModuleFileName(FModuleHandle, @S[1], MAX_PATH); if len > 0 then SetLength(S, len - 1) else begin S := ''; LogLastError; end; W := S; end; if W = '' then W := ADefaultName; SetName(W); LoadInfo; end; destructor TDbgInstance.Destroy; var n: integer; begin for n := 0 to FBreakList.Count - 1 do begin Process.RemoveBreak(TDbgBreakpoint(FBreakList[n]).FLocation); end; FBreakList.Clear; FreeAndNil(FBreakList); FreeAndNil(FDbgInfo); FreeAndNil(FLoader); inherited; end; function TDbgInstance.FindSymbol(AAdress: TDbgPtr): TDbgSymbol; begin Result := FDbgInfo.FindSymbol(AAdress + AddrOffset); end; procedure TDbgInstance.LoadInfo; begin FLoader := TDbgImageLoader.Create(FModuleHandle); assert(false, 'fpc will not compile this'); //FDbgInfo := TDbgDwarf.Create(FLoader); //TDbgDwarf(FDbgInfo).LoadCompilationUnits; end; function TDbgInstance.RemoveBreak(const AFileName: String; ALine: Cardinal): Boolean; var addr: TDbgPtr; begin Result := False; if not FDbgInfo.HasInfo then Exit; addr := FDbgInfo.GetLineAddress(AFileName, ALine); if addr = 0 then Exit; Result := FProcess.RemoveBreak(addr - AddrOffset); end; procedure TDbgInstance.SetName(const AValue: String); begin FName := AValue; CheckName; end; {$endif} {$ifdef windows} { TDbgLibrary } constructor TDbgLibrary.Create(const AProcess: TDbgProcess; const ADefaultName: String; const AInfo: TLoadDLLDebugInfo); begin inherited Create(AProcess, ADefaultName, AInfo.hFile, TDbgPtr(AInfo.lpBaseOfDll), TDbgPtr(AInfo.lpImageName), AInfo.fUnicode <> 0); end; {$endif} {$ifdef windows} { TDbgProcess } function TDbgProcess.AddBreak(const ALocation: TDbgPtr): TDbgBreakpoint; begin Result := TDbgBreakpoint.Create(Self, ALocation); FBreakMap.Add(ALocation, Result); end; function TDbgProcess.AddLib(const AInfo: TLoadDLLDebugInfo): TDbgLibrary; var ID: TDbgPtr; begin Result := TDbgLibrary.Create(Self, HexValue(AInfo.lpBaseOfDll, SizeOf(Pointer), [hvfIncludeHexchar]), AInfo); ID := TDbgPtr(AInfo.lpBaseOfDll); FLibMap.Add(ID, Result); if Result.FDbgInfo.HasInfo then FSymInstances.Add(Result); end; procedure TDbgProcess.AddThread(const AID: Integer; const AInfo: TCreateThreadDebugInfo); var Thread: TDbgThread; begin Thread := TDbgThread.Create(Self, AID, AInfo.hThread, AInfo.lpThreadLocalBase, AInfo.lpStartAddress); FThreadMap.Add(AID, Thread); end; procedure TDbgProcess.ContinueDebugEvent(const AThread: TDbgThread; const ADebugEvent: TDebugEvent); begin case ADebugEvent.dwDebugEventCode of EXCEPTION_DEBUG_EVENT: begin case ADebugEvent.Exception.ExceptionRecord.ExceptionCode of EXCEPTION_BREAKPOINT: begin if AThread = nil then Exit; if FCurrentBreakpoint = nil then Exit; if AThread.SingleStepping then Exit; AThread.SingleStep; FReEnableBreakStep := True; end; end; end; end; end; constructor TDbgProcess.Create(const ADefaultName: String; const AProcessID, AThreadID: Integer; const AInfo: TCreateProcessDebugInfo); const {.$IFDEF CPU64} MAP_ID_SIZE = itu8; {.$ELSE} // MAP_ID_SIZE = itu4; {.$ENDIF} begin FProcessID := AProcessID; FThreadID := AThreadID; FInfo := AInfo; FThreadMap := TMap.Create(itu4, SizeOf(TDbgThread)); FLibMap := TMap.Create(MAP_ID_SIZE, SizeOf(TDbgLibrary)); FBreakMap := TMap.Create(MAP_ID_SIZE, SizeOf(TDbgBreakpoint)); FCurrentBreakpoint := nil; FSymInstances := TList.Create; inherited Create(Self, ADefaultName, AInfo.hFile, TDbgPtr(AInfo.lpBaseOfImage), TDbgPtr(AInfo.lpImageName), AInfo.fUnicode <> 0); FMainThread := TDbgThread.Create(Self, AThreadID, AInfo.hThread, AInfo.lpThreadLocalBase, AInfo.lpStartAddress); FThreadMap.Add(AThreadID, FMainThread); if FDbgInfo.HasInfo then FSymInstances.Add(Self); end; destructor TDbgProcess.Destroy; begin // CloseHandle(FInfo.hThread); CloseHandle(FInfo.hProcess); FreeAndNil(FBreakMap); FreeAndNil(FThreadMap); FreeAndNil(FLibMap); FreeAndNil(FSymInstances); inherited; end; function TDbgProcess.FindSymbol(const AName: String): TDbgSymbol; begin Result := FDbgInfo.FindSymbol(AName); end; function TDbgProcess.FindSymbol(AAdress: TDbgPtr): TDbgSymbol; var n: Integer; Inst: TDbgInstance; begin for n := 0 to FSymInstances.Count - 1 do begin Inst := TDbgInstance(FSymInstances[n]); Result := Inst.FindSymbol(AAdress); if Result <> nil then Exit; end; Result := nil; end; function TDbgProcess.GetLib(const AHandle: THandle; out ALib: TDbgLibrary): Boolean; var Iterator: TMapIterator; Lib: TDbgLibrary; begin Result := False; Iterator := TMapIterator.Create(FLibMap); while not Iterator.EOM do begin Iterator.GetData(Lib); Result := Lib.ModuleHandle = AHandle; if Result then begin ALib := Lib; Break; end; Iterator.Next; end; Iterator.Free; end; function TDbgProcess.GetThread(const AID: Integer; out AThread: TDbgThread): Boolean; var Thread: TDbgThread; begin Result := FThreadMap.GetData(AID, Thread) and (Thread <> nil); if Result then AThread := Thread else Log('Unknown thread ID %u for process %u', [AID, FProcessID]); end; { ------------------------------------------------------------------ HandleDebugEvent Result: True if the event was triggered internally The callee should continue the process ------------------------------------------------------------------ } function TDbgProcess.HandleDebugEvent(const ADebugEvent: TDebugEvent): Boolean; function DoBreak: Boolean; var ID: TDbgPtr; begin Result := False; ID := TDbgPtr(ADebugEvent.Exception.ExceptionRecord.ExceptionAddress); if not FBreakMap.GetData(ID, FCurrentBreakpoint) then Exit; if FCurrentBreakpoint = nil then Exit; Result := True; if not FCurrentBreakpoint.Hit(ADebugEvent.dwThreadId) then FCurrentBreakpoint := nil; // no need for a singlestep if we continue end; function DoSingleStep: Boolean; var _UC: record C: TContext; D: array[1..16] of Byte; end; Context: PContext; begin Result := False; // check if we are interupting Context := AlignPtr(@_UC, $10); Context^.ContextFlags := CONTEXT_DEBUG_REGISTERS; if GetThreadContext(FInfo.hThread, Context^) then begin if Context^.Dr6 and 1 <> 0 then begin // interrupt ! // disable break. Context^.Dr7 := Context^.Dr7 and not $1; Context^.Dr0 := 0; if not SetThreadContext(FInfo.hThread, Context^) then begin // Heeellppp!! Log('Thread %u: Unable to reset BR0', [ADebugEvent.dwThreadId]); end; // check if we are also singlestepping // if not, then exit, else proceed to next check if Context^.Dr6 and $40 = 0 then Exit; end; end else begin // if we can not get the context, we probable weren't able to set it either Log('Thread %u: Unable to get context', [ADebugEvent.dwThreadId]); end; // check if we are single stepping ourself if FCurrentBreakpoint = nil then Exit; FCurrentBreakpoint.SetBreak; FCurrentBreakpoint := nil; Result := FReEnableBreakStep; FReEnableBreakStep := False; end; begin Result := False; case ADebugEvent.dwDebugEventCode of EXCEPTION_DEBUG_EVENT: begin case ADebugEvent.Exception.ExceptionRecord.ExceptionCode of EXCEPTION_BREAKPOINT: {Result :=} DoBreak; // we never set a break ourself, let the callee pause! EXCEPTION_SINGLE_STEP: Result := DoSingleStep; end; end; CREATE_THREAD_DEBUG_EVENT: begin AddThread(ADebugEvent.dwThreadId, ADebugEvent.CreateThread) end; EXIT_THREAD_DEBUG_EVENT: begin RemoveThread(ADebugEvent.dwThreadId); end; LOAD_DLL_DEBUG_EVENT: begin AddLib(ADebugEvent.LoadDll); end; UNLOAD_DLL_DEBUG_EVENT: begin RemoveLib(ADebugEvent.UnloadDll); end; end; end; procedure TDbgProcess.Interrupt; var _UC: record C: TContext; D: array[1..16] of Byte; end; Context: PContext; r: DWORD; 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); r := SuspendThread(FInfo.hThread); try Context^.ContextFlags := CONTEXT_CONTROL or CONTEXT_DEBUG_REGISTERS; if not GetThreadContext(FInfo.hThread, Context^) then begin Log('Proces %u interrupt: Unable to get context', [FProcessID]); 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 Log('Proces %u interrupt: Unable to set context', [FProcessID]); Exit; end; finally r := ResumeTHread(FInfo.hThread); end; end; function TDbgProcess.ReadData(const AAdress: TDbgPtr; const ASize: Cardinal; out AData): Boolean; var BytesRead: Cardinal; begin Result := ReadProcessMemory(Handle, Pointer(PtrUInt(AAdress)), @AData, ASize, BytesRead) and (BytesRead = ASize); if not Result then LogLastError; end; function TDbgProcess.ReadOrdinal(const AAdress: TDbgPtr; out AData): Boolean; begin Result := ReadData(AAdress, 4, AData); end; function TDbgProcess.ReadString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: String): Boolean; var BytesRead: Cardinal; buf: array of Char; begin 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 TDbgProcess.ReadWString(const AAdress: TDbgPtr; const AMaxSize: Cardinal; out AData: WideString): Boolean; var BytesRead: Cardinal; buf: array of WChar; begin 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 TDbgProcess.RemoveBreak(const ALocation: TDbgPtr): Boolean; begin if FBreakMap = nil then Result := False else Result := FBreakMap.Delete(ALocation); end; procedure TDbgProcess.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; if Lib.FDbgInfo.HasInfo then FSymInstances.Remove(Lib); FLibMap.Delete(ID); // TODO: Free lib ??? end; procedure TDbgProcess.RemoveThread(const AID: DWord); begin if FThreadMap = nil then Exit; FThreadMap.Delete(AID); end; procedure TDbgProcess.SetName(const AValue: String); begin FName := AValue; end; procedure TDbgProcess.ThreadDestroyed(const AThread: TDbgThread); begin if AThread = FMainThread then FMainThread := nil; end; function TDbgProcess.WriteData(const AAdress: TDbgPtr; const ASize: Cardinal; const AData): Boolean; var BytesWritten: Cardinal; begin Result := WriteProcessMemory(Handle, Pointer(PtrUInt(AAdress)), @AData, ASize, BytesWritten) and (BytesWritten = ASize); if not Result then LogLastError; end; {$endif} {$ifdef windows} { TDbgThread } constructor TDbgThread.Create(const AProcess: TDbgProcess; const AID: Integer; const AHandle: THandle; const ABase, AStart: Pointer); begin FID := AID; FHandle := AHandle; FBaseAddr := ABase; FStartAddr := AStart; FProcess := AProcess; inherited Create; end; destructor TDbgThread.Destroy; begin FProcess.ThreadDestroyed(Self); inherited; end; function TDbgThread.SingleStep: Boolean; var _UC: record C: TContext; D: array[1..16] of Byte; end; Context: PContext; begin Context := AlignPtr(@_UC, $10); Context^.ContextFlags := CONTEXT_CONTROL; if not GetThreadContext(FHandle, Context^) then begin Log('Thread %u: Unable to get context', [FID]); Exit; end; Context^.ContextFlags := CONTEXT_CONTROL; Context^.EFlags := Context^.EFlags or FLAG_TRACE_BIT; if not SetThreadContext(FHandle, Context^) then begin Log('Thread %u: Unable to set context', [FID]); Exit; end; FSingleStepping := True; end; {$endif} { TDbgInfo } constructor TDbgInfo.Create(ALoader: TDbgImageLoader); begin inherited Create; end; function TDbgInfo.FindSymbol(const AName: String): TDbgSymbol; begin Result := nil; end; function TDbgInfo.FindSymbol(AAddress: TDbgPtr): TDbgSymbol; begin Result := nil; end; function TDbgInfo.GetLineAddress(const AFileName: String; ALine: Cardinal): TDbgPtr; begin Result := 0; end; procedure TDbgInfo.SetHasInfo; begin FHasInfo := True; end; { TDbgSymbol } constructor TDbgSymbol.Create(const AName: String); begin inherited Create; AddReference; if AName <> '' then SetName(AName); end; constructor TDbgSymbol.Create(const AName: String; AKind: TDbgSymbolKind; AAddress: TDbgPtr); begin Create(AName); SetKind(AKind); FAddress := AAddress; end; destructor TDbgSymbol.Destroy; begin inherited Destroy; end; function TDbgSymbol.GetAddress: TDbgPtr; begin if not(sfiAddress in FEvaluatedFields) then AddressNeeded; Result := FAddress; end; function TDbgSymbol.GetKind: TDbgSymbolKind; begin if not(sfiKind in FEvaluatedFields) then KindNeeded; Result := FKind; end; function TDbgSymbol.GetName: String; begin if not(sfiName in FEvaluatedFields) then NameNeeded; Result := FName; end; function TDbgSymbol.GetSize: Integer; begin if not(sfiSize in FEvaluatedFields) then SizeNeeded; Result := FSize; end; function TDbgSymbol.GetSymbolType: TDbgSymbolType; begin if not(sfiSymType in FEvaluatedFields) then SymbolTypeNeeded; Result := FSymbolType; end; function TDbgSymbol.GetPointedToType: TDbgSymbol; begin Result := nil; end; procedure TDbgSymbol.SetAddress(AValue: TDbgPtr); begin FAddress := AValue; Include(FEvaluatedFields, sfiAddress); end; procedure TDbgSymbol.SetKind(AValue: TDbgSymbolKind); begin FKind := AValue; Include(FEvaluatedFields, sfiKind); end; procedure TDbgSymbol.SetSymbolType(AValue: TDbgSymbolType); begin FSymbolType := AValue; Include(FEvaluatedFields, sfiSymType); end; procedure TDbgSymbol.SetSize(AValue: Integer); begin FSize := AValue; Include(FEvaluatedFields, sfiSize); end; procedure TDbgSymbol.SetName(AValue: String); begin FName := AValue; Include(FEvaluatedFields, sfiName); end; function TDbgSymbol.GetChild(AIndex: Integer): TDbgSymbol; begin result := nil; end; function TDbgSymbol.GetColumn: Cardinal; begin Result := 0; end; function TDbgSymbol.GetCount: Integer; begin Result := 0; end; function TDbgSymbol.GetFile: String; begin Result := ''; end; function TDbgSymbol.GetFlags: TDbgSymbolFlags; begin Result := []; end; function TDbgSymbol.GetLine: Cardinal; begin Result := 0; end; function TDbgSymbol.GetParent: TDbgSymbol; begin Result := nil; end; function TDbgSymbol.GetReference: TDbgSymbol; begin Result := nil; end; procedure TDbgSymbol.KindNeeded; begin SetKind(skNone); end; procedure TDbgSymbol.NameNeeded; begin SetName(''); end; procedure TDbgSymbol.SymbolTypeNeeded; begin SetSymbolType(stNone); end; procedure TDbgSymbol.AddressNeeded; begin SetAddress(0); end; procedure TDbgSymbol.SizeNeeded; begin SetSize(0); end; {$ifdef windows} { TDbgBreak } constructor TDbgBreakpoint.Create(const AProcess: TDbgProcess; const ALocation: TDbgPtr); begin FProcess := AProcess; FLocation := ALocation; inherited Create; SetBreak; end; destructor TDbgBreakpoint.Destroy; begin ResetBreak; inherited; end; function TDbgBreakpoint.Hit(const AThreadID: Integer): Boolean; var Thread: TDbgThread; _UC: record C: TContext; D: array[1..16] of Byte; end; Context: PContext; begin Result := False; if FOrgValue = $CC then Exit; // breakpoint on a hardcoded breakpoint // no need to jum back and restore instruction ResetBreak; if not FProcess.GetThread(AThreadId, Thread) then Exit; Context := AlignPtr(@_UC, $10); Context^.ContextFlags := CONTEXT_CONTROL; if not GetThreadContext(Thread.Handle, Context^) then begin Log('Break $s: Unable to get context', [HexValue(FLocation, SizeOf(Pointer), [hvfIncludeHexchar])]); Exit; end; Context^.ContextFlags := CONTEXT_CONTROL; {$ifdef cpui386} Dec(Context^.Eip); {$else} Dec(Context^.Rip); {$endif} if not SetThreadContext(Thread.Handle, Context^) then begin Log('Break %s: Unable to set context', [HexValue(FLocation, SizeOf(Pointer), [hvfIncludeHexchar])]); Exit; end; Result := True; end; procedure TDbgBreakpoint.ResetBreak; begin if FOrgValue = $CC then Exit; // breakpoint on a hardcoded breakpoint if not FProcess.WriteData(FLocation, 1, FOrgValue) then begin Log('Unable to reset breakpoint at $%p', [FLocation]); Exit; end; FlushInstructionCache(FProcess.FInfo.hProcess, Pointer(PtrUInt(FLocation)), 1); end; procedure TDbgBreakpoint.SetBreak; const Int3: Byte = $CC; begin if not FProcess.ReadData(FLocation, 1, FOrgValue) then begin Log('Unable to read breakpoint at $%p', [FLocation]); Exit; end; if FOrgValue = $CC then Exit; // breakpoint on a hardcoded breakpoint if not FProcess.WriteData(FLocation, 1, Int3) then begin Log('Unable to set breakpoint at $%p', [FLocation]); Exit; end; FlushInstructionCache(FProcess.FInfo.hProcess, Pointer(PtrUInt(FLocation)), 1); end; {$endif} end.