mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-30 23:43:40 +02:00
1008 lines
32 KiB
ObjectPascal
1008 lines
32 KiB
ObjectPascal
unit FpDbgDarwinClasses;
|
|
|
|
{$mode objfpc}{$H+}
|
|
{$linkframework Security}
|
|
{$IFDEF INLINE_OFF}{$INLINE OFF}{$ENDIF}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes,
|
|
SysUtils,
|
|
BaseUnix,
|
|
termio,
|
|
process,
|
|
FpDbgClasses,
|
|
FpDbgLoader, FpDbgDisasX86,
|
|
DbgIntfBaseTypes, DbgIntfDebuggerBase,
|
|
FpDbgLinuxExtra,
|
|
FpDbgDwarfDataClasses,
|
|
FpImgReaderMacho,
|
|
FpDbgInfo,
|
|
MacOSAll,
|
|
FpDbgUtil,
|
|
UTF8Process,
|
|
{$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif},
|
|
FpDbgCommon, FpdMemoryTools,
|
|
FpErrorMessages, FpDbgCpuX86;
|
|
|
|
type
|
|
x86_thread_state32_t = record
|
|
__eax: cuint;
|
|
__ebx: cuint;
|
|
__ecx: cuint;
|
|
__edx: cuint;
|
|
__edi: cuint;
|
|
__esi: cuint;
|
|
__ebp: cuint;
|
|
__esp: cuint;
|
|
__ss: cuint;
|
|
__eflags: cuint;
|
|
__eip: cuint;
|
|
__cs: cuint;
|
|
__ds: cuint;
|
|
__es: cuint;
|
|
__fs: cuint;
|
|
__gs: cuint;
|
|
end;
|
|
|
|
x86_thread_state64_t = record
|
|
__rax: cuint64;
|
|
__rbx: cuint64;
|
|
__rcx: cuint64;
|
|
__rdx: cuint64;
|
|
__rdi: cuint64;
|
|
__rsi: cuint64;
|
|
__rbp: cuint64;
|
|
__rsp: cuint64;
|
|
__r8: cuint64;
|
|
__r9: cuint64;
|
|
__r10: cuint64;
|
|
__r11: cuint64;
|
|
__r12: cuint64;
|
|
__r13: cuint64;
|
|
__r14: cuint64;
|
|
__r15: cuint64;
|
|
__rip: cuint64;
|
|
__rflags: cuint64;
|
|
__cs: cuint64;
|
|
__fs: cuint64;
|
|
__gs: cuint64;
|
|
end;
|
|
|
|
x86_debug_state32_t = record
|
|
__dr0: cuint32;
|
|
__dr1: cuint32;
|
|
__dr2: cuint32;
|
|
__dr3: cuint32;
|
|
__dr4: cuint32;
|
|
__dr5: cuint32;
|
|
__dr6: cuint32;
|
|
__dr7: cuint32;
|
|
end;
|
|
|
|
x86_debug_state64_t = record
|
|
__dr0: cuint64;
|
|
__dr1: cuint64;
|
|
__dr2: cuint64;
|
|
__dr3: cuint64;
|
|
__dr4: cuint64;
|
|
__dr5: cuint64;
|
|
__dr6: cuint64;
|
|
__dr7: cuint64;
|
|
end;
|
|
|
|
x86_debug_state = record
|
|
case a: byte of
|
|
1: (ds32: x86_debug_state32_t);
|
|
2: (ds64: x86_debug_state64_t);
|
|
end;
|
|
|
|
type
|
|
|
|
{ TDbgDarwinThread }
|
|
|
|
TDbgDarwinThread = class(TDbgx86Thread)
|
|
private
|
|
FThreadState32: x86_thread_state32_t;
|
|
FThreadState64: x86_thread_state64_t;
|
|
FDebugState32: x86_debug_state32_t;
|
|
FDebugState64: x86_debug_state64_t;
|
|
FDebugStateRead: boolean;
|
|
FDebugStateChanged: boolean;
|
|
FIsSteppingBreakPoint: boolean;
|
|
protected
|
|
function ReadThreadState: boolean;
|
|
function ReadDebugState: boolean;
|
|
// function GetStackUnwinder: TDbgStackUnwinder; override;
|
|
public
|
|
function ResetInstructionPointerAfterBreakpoint: boolean; override;
|
|
procedure ApplyWatchPoints(AWatchPointData: TFpWatchPointData); override;
|
|
function DetectHardwareWatchpoint: Pointer; override;
|
|
procedure BeforeContinue; override;
|
|
procedure LoadRegisterValues; override;
|
|
|
|
function GetInstructionPointerRegisterValue: TDbgPtr; override;
|
|
function GetStackPointerRegisterValue: TDbgPtr; override;
|
|
procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override;
|
|
procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override;
|
|
function GetStackBasePointerRegisterValue: TDbgPtr; override;
|
|
end;
|
|
|
|
{ TDbgDarwinProcess }
|
|
|
|
TDbgDarwinProcess = class(TDbgx86Process)
|
|
private
|
|
FStatus: cint;
|
|
FProcessStarted: boolean;
|
|
FTaskPort: mach_port_name_t;
|
|
FProcProcess: TProcessUTF8;
|
|
FIsTerminating: boolean;
|
|
FExceptionSignal: PtrUInt;
|
|
FMasterPtyFd: cint;
|
|
FExecutableFilename: string;
|
|
function GetDebugAccessRights: boolean;
|
|
{$ifndef VER2_6}
|
|
procedure OnForkEvent(Sender : TObject);
|
|
{$endif}
|
|
protected
|
|
procedure InitializeLoaders; override;
|
|
function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override;
|
|
function AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent; override;
|
|
function CreateWatchPointData: TFpWatchPointData; override;
|
|
public
|
|
function StartInstance(AParams, AnEnvironment: TStrings;
|
|
AWorkingDirectory, AConsoleTty: string; AFlags: TStartInstanceFlags;
|
|
out AnError: TFpError): boolean; override;
|
|
class function isSupported(ATargetInfo: TTargetDescriptor): boolean; override;
|
|
constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig = nil); override;
|
|
destructor Destroy; override;
|
|
|
|
function ReadData(const AAdress: TDbgPtr; const ASize: Cardinal; out AData): Boolean; override;
|
|
function WriteData(const AAdress: TDbgPtr; const ASize: Cardinal; const AData): Boolean; override;
|
|
function CallParamDefaultLocation(AParamIdx: Integer): TFpDbgMemLocation; override;
|
|
|
|
function CheckForConsoleOutput(ATimeOutMs: integer): integer; override;
|
|
function GetConsoleOutput: string; override;
|
|
procedure SendConsoleInput(AString: string); override;
|
|
|
|
procedure TerminateProcess; override;
|
|
|
|
function Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean; override;
|
|
function WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean; override;
|
|
function Pause: boolean; override;
|
|
end;
|
|
TDbgDarwinProcessClass = class of TDbgDarwinProcess;
|
|
|
|
implementation
|
|
|
|
var
|
|
DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup;
|
|
GConsoleTty: string;
|
|
|
|
type
|
|
vm_map_t = mach_port_t;
|
|
vm_offset_t = UIntPtr;
|
|
vm_address_t = vm_offset_t;
|
|
vm_size_t = UIntPtr;
|
|
vm_prot_t = cint;
|
|
mach_vm_address_t = uint64;
|
|
mach_msg_Type_number_t = natural_t;
|
|
mach_vm_size_t = uint64;
|
|
task_t = mach_port_t;
|
|
thread_act_t = mach_port_t;
|
|
thread_act_array = array[0..255] of thread_act_t;
|
|
thread_act_array_t = ^thread_act_array;
|
|
thread_state_flavor_t = cint;
|
|
thread_state_t = ^natural_t;
|
|
|
|
const
|
|
x86_THREAD_STATE32 = 1;
|
|
x86_FLOAT_STATE32 = 2;
|
|
x86_EXCEPTION_STATE32 = 3;
|
|
x86_THREAD_STATE64 = 4;
|
|
x86_FLOAT_STATE64 = 5;
|
|
x86_EXCEPTION_STATE64 = 6;
|
|
x86_THREAD_STATE = 7;
|
|
x86_FLOAT_STATE = 8;
|
|
x86_EXCEPTION_STATE = 9;
|
|
x86_DEBUG_STATE32 = 10;
|
|
x86_DEBUG_STATE64 = 11;
|
|
//x86_DEBUG_STATE = 12;
|
|
THREAD_STATE_NONE = 13;
|
|
x86_AVX_STATE32 = 16;
|
|
x86_AVX_STATE64 = 17;
|
|
x86_AVX_STATE = 18;
|
|
|
|
x86_THREAD_STATE32_COUNT: mach_msg_Type_number_t = sizeof(x86_thread_state32_t) div sizeof(cint);
|
|
x86_THREAD_STATE64_COUNT: mach_msg_Type_number_t = sizeof(x86_thread_state64_t) div sizeof(cint);
|
|
x86_DEBUG_STATE32_COUNT: mach_msg_Type_number_t = sizeof(x86_debug_state32_t) div sizeof(cint);
|
|
x86_DEBUG_STATE64_COUNT: mach_msg_Type_number_t = sizeof(x86_debug_state64_t) div sizeof(cint);
|
|
|
|
function task_for_pid(target_tport: mach_port_name_t; pid: integer; var t: mach_port_name_t): kern_return_t; cdecl external name 'task_for_pid';
|
|
function mach_task_self: mach_port_name_t; cdecl external name 'mach_task_self';
|
|
function mach_error_string(error_value: mach_error_t): pchar; cdecl; external name 'mach_error_string';
|
|
function mach_vm_protect(target_task: vm_map_t; adress: mach_vm_address_t; size: mach_vm_size_t; set_maximum: boolean_t; new_protection: vm_prot_t): kern_return_t; cdecl external name 'mach_vm_protect';
|
|
function mach_vm_write(target_task: vm_map_t; address: mach_vm_address_t; data: vm_offset_t; dataCnt: mach_msg_Type_number_t): kern_return_t; cdecl external name 'mach_vm_write';
|
|
function mach_vm_read(target_task: vm_map_t; address: mach_vm_address_t; size: mach_vm_size_t; var data: vm_offset_t; var dataCnt: mach_msg_Type_number_t): kern_return_t; cdecl external name 'mach_vm_read';
|
|
|
|
function task_threads(target_task: task_t; var act_list: thread_act_array_t; var act_listCnt: mach_msg_type_number_t): kern_return_t; cdecl external name 'task_threads';
|
|
function thread_get_state(target_act: thread_act_t; flavor: thread_state_flavor_t; old_state: thread_state_t; var old_stateCnt: mach_msg_Type_number_t): kern_return_t; cdecl external name 'thread_get_state';
|
|
function thread_set_state(target_act: thread_act_t; flavor: thread_state_flavor_t; new_state: thread_state_t; old_stateCnt: mach_msg_Type_number_t): kern_return_t; cdecl external name 'thread_set_state';
|
|
|
|
function posix_openpt(oflag: cint): cint;cdecl;external 'c' name 'posix_openpt';
|
|
function ptsname(__fd:longint):Pchar;cdecl;external 'c' name 'ptsname';
|
|
function grantpt(__fd:longint):longint;cdecl;external 'c' name 'grantpt';
|
|
function unlockpt(__fd:longint):longint;cdecl;external 'c' name 'unlockpt';
|
|
|
|
Function WIFSTOPPED(Status: Integer): Boolean;
|
|
begin
|
|
WIFSTOPPED:=((Status and $FF)=$7F);
|
|
end;
|
|
|
|
{ TDbgDarwinThread }
|
|
|
|
Function safefpdup2(fildes, fildes2 : cInt): cInt;
|
|
begin
|
|
repeat
|
|
safefpdup2:=fpdup2(fildes,fildes2);
|
|
until (safefpdup2<>-1) or (fpgeterrno<>ESysEINTR);
|
|
end;
|
|
|
|
{$ifndef VER2_6}
|
|
procedure TDbgDarwinProcess.OnForkEvent(Sender: TObject);
|
|
{$else}
|
|
procedure OnForkEvent;
|
|
{$endif VER2_6}
|
|
var
|
|
ConsoleTtyFd: cint;
|
|
begin
|
|
if FpSetsid<>0 then
|
|
begin
|
|
// For some reason, FpSetsid always fails.
|
|
// writeln('Failed to set sid. '+inttostr(fpgeterrno));
|
|
end;
|
|
if GConsoleTty<>'' then
|
|
begin
|
|
ConsoleTtyFd:=FpOpen(GConsoleTty, O_RDWR + O_NOCTTY);
|
|
if ConsoleTtyFd>-1 then
|
|
begin
|
|
if (FpIOCtl(ConsoleTtyFd, TIOCSCTTY, nil) = -1) then
|
|
begin
|
|
// This call always fails for some reason. That's also why login_tty can not be used. (login_tty
|
|
// also calls TIOCSCTTY, but when it fails it aborts) The failure is ignored.
|
|
// writeln('Failed to set tty '+inttostr(fpgeterrno));
|
|
end;
|
|
|
|
safefpdup2(ConsoleTtyFd,0);
|
|
safefpdup2(ConsoleTtyFd,1);
|
|
safefpdup2(ConsoleTtyFd,2);
|
|
end
|
|
else
|
|
writeln('Failed to open tty '+GConsoleTty+'. Errno: '+inttostr(fpgeterrno));
|
|
end;
|
|
|
|
fpPTrace(PTRACE_TRACEME, 0, nil, nil);
|
|
end;
|
|
|
|
function TDbgDarwinThread.ReadThreadState: boolean;
|
|
var
|
|
aKernResult: kern_return_t;
|
|
old_StateCnt: mach_msg_Type_number_t;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgDarwinThread.ReadThreadState');{$ENDIF}
|
|
if ID<0 then
|
|
begin
|
|
// The ID is set to -1 when the debugger does not have sufficient rights.
|
|
// In that case just return zero's, so that the debuggee wil just run without
|
|
// any problems/exceptions in the debugger.
|
|
FillByte(FThreadState32, SizeOf(FThreadState32),0);
|
|
FillByte(FThreadState64, SizeOf(FThreadState64),0);
|
|
result := true;
|
|
exit;
|
|
end;
|
|
if Process.Mode=dm32 then
|
|
begin
|
|
old_StateCnt:=x86_THREAD_STATE32_COUNT;
|
|
aKernResult:=thread_get_state(Id,x86_THREAD_STATE32, @FThreadState32,old_StateCnt);
|
|
end
|
|
else
|
|
begin
|
|
old_StateCnt:=x86_THREAD_STATE64_COUNT;
|
|
aKernResult:=thread_get_state(Id,x86_THREAD_STATE64, @FThreadState64,old_StateCnt);
|
|
end;
|
|
result := aKernResult = KERN_SUCCESS;
|
|
if not result then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to call thread_get_state for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]);
|
|
end;
|
|
FRegisterValueListValid:=false;
|
|
FDebugStateRead:=false;
|
|
end;
|
|
|
|
function TDbgDarwinThread.ReadDebugState: boolean;
|
|
var
|
|
aKernResult: kern_return_t;
|
|
old_StateCnt: mach_msg_Type_number_t;
|
|
begin
|
|
if FDebugStateRead then
|
|
begin
|
|
result := true;
|
|
exit;
|
|
end;
|
|
|
|
if Process.Mode=dm32 then
|
|
begin
|
|
old_StateCnt:=x86_DEBUG_STATE32_COUNT;
|
|
aKernResult:=thread_get_state(ID, x86_DEBUG_STATE32, @FDebugState32, old_StateCnt);
|
|
end
|
|
else
|
|
begin
|
|
old_StateCnt:=x86_DEBUG_STATE64_COUNT;
|
|
aKernResult:=thread_get_state(ID, x86_DEBUG_STATE64, @FDebugState64, old_StateCnt);
|
|
end;
|
|
if aKernResult <> KERN_SUCCESS then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to call thread_get_state to ge debug-info for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]);
|
|
result := false;
|
|
end
|
|
else
|
|
begin
|
|
result := true;
|
|
FDebugStateRead:=true;
|
|
end;
|
|
end;
|
|
|
|
function TDbgDarwinThread.ResetInstructionPointerAfterBreakpoint: boolean;
|
|
var
|
|
aKernResult: kern_return_t;
|
|
new_StateCnt: mach_msg_Type_number_t;
|
|
begin
|
|
result := true;
|
|
if ID<0 then
|
|
Exit;
|
|
|
|
if Process.Mode=dm32 then
|
|
begin
|
|
Dec(FThreadState32.__eip);
|
|
new_StateCnt := x86_THREAD_STATE32_COUNT;
|
|
aKernResult:=thread_set_state(ID,x86_THREAD_STATE32, @FThreadState32, new_StateCnt);
|
|
end
|
|
else
|
|
begin
|
|
Dec(FThreadState64.__rip);
|
|
new_StateCnt := x86_THREAD_STATE64_COUNT;
|
|
aKernResult:=thread_set_state(ID,x86_THREAD_STATE64, @FThreadState64, new_StateCnt);
|
|
end;
|
|
|
|
if aKernResult <> KERN_SUCCESS then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to call thread_set_state for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]);
|
|
result := false;
|
|
end;
|
|
end;
|
|
|
|
type
|
|
TDr32bitArr = array[0..4] of cuint32;
|
|
TDr64bitArr = array[0..4] of cuint64;
|
|
|
|
procedure TDbgDarwinThread.ApplyWatchPoints(AWatchPointData: TFpWatchPointData);
|
|
procedure UpdateWatches32;
|
|
var
|
|
drArr: ^TDr32bitArr;
|
|
i: Integer;
|
|
r: boolean;
|
|
addr: cuint32;
|
|
begin
|
|
drArr := @FDebugState32.__dr0;
|
|
|
|
r := True;
|
|
for i := 0 to 3 do begin
|
|
addr := cuint32(TFpIntelWatchPointData(AWatchPointData).Dr03[i]);
|
|
drArr^[i]:=addr;
|
|
end;
|
|
FDebugState32.__dr7 := (FDebugState32.__dr7 and $0000FF00);
|
|
if r then
|
|
FDebugState32.__dr7 := FDebugState32.__dr7 or cuint32(TFpIntelWatchPointData(AWatchPointData).Dr7);
|
|
end;
|
|
|
|
procedure UpdateWatches64;
|
|
var
|
|
drArr: ^TDr64bitArr;
|
|
i: Integer;
|
|
r: boolean;
|
|
addr: cuint64;
|
|
begin
|
|
drArr := @FDebugState64.__dr0;
|
|
|
|
r := True;
|
|
for i := 0 to 3 do begin
|
|
addr := cuint64(TFpIntelWatchPointData(AWatchPointData).Dr03[i]);
|
|
drArr^[i]:=addr;
|
|
end;
|
|
FDebugState32.__dr7 := (FDebugState32.__dr7 and $0000FF00);
|
|
if r then
|
|
FDebugState32.__dr7 := FDebugState32.__dr7 or cuint64(TFpIntelWatchPointData(AWatchPointData).Dr7);
|
|
end;
|
|
|
|
begin
|
|
if ID<0 then
|
|
Exit;
|
|
if not ReadDebugState then
|
|
exit;
|
|
|
|
if Process.Mode=dm32 then
|
|
UpdateWatches32
|
|
else
|
|
UpdateWatches64;
|
|
FDebugStateChanged:=true;
|
|
end;
|
|
|
|
function TDbgDarwinThread.DetectHardwareWatchpoint: Pointer;
|
|
var
|
|
dr6: DWord;
|
|
wd: TFpIntelWatchPointData;
|
|
begin
|
|
result := nil;
|
|
if ID<0 then
|
|
Exit;
|
|
if ReadDebugState then
|
|
begin
|
|
if Process.Mode=dm32 then
|
|
dr6 := FDebugState32.__dr6
|
|
else
|
|
dr6 := lo(FDebugState64.__dr6);
|
|
|
|
wd := TFpIntelWatchPointData(Process.WatchPointData);
|
|
if dr6 and 1 = 1 then result := wd.Owner[0]
|
|
else if dr6 and 2 = 2 then result := wd.Owner[1]
|
|
else if dr6 and 4 = 4 then result := wd.Owner[2]
|
|
else if dr6 and 8 = 8 then result := wd.Owner[3];
|
|
if (Result = nil) and ((dr6 and 15) <> 0) then
|
|
Result := Pointer(-1); // not owned watchpoint
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgDarwinThread.BeforeContinue;
|
|
var
|
|
aKernResult: kern_return_t;
|
|
old_StateCnt: mach_msg_Type_number_t;
|
|
begin
|
|
inherited;
|
|
if Process.CurrentWatchpoint <> nil then
|
|
begin
|
|
if Process.Mode=dm32 then
|
|
FDebugState32.__dr6:=0
|
|
else
|
|
FDebugState64.__dr6:=0;
|
|
FDebugStateChanged:=true;
|
|
end;
|
|
|
|
if FDebugStateRead and FDebugStateChanged then
|
|
begin
|
|
if Process.Mode=dm32 then
|
|
begin
|
|
old_StateCnt:=x86_DEBUG_STATE32_COUNT;
|
|
aKernResult:=thread_set_state(Id, x86_DEBUG_STATE32, @FDebugState32, old_StateCnt);
|
|
end
|
|
else
|
|
begin
|
|
old_StateCnt:=x86_DEBUG_STATE64_COUNT;
|
|
aKernResult:=thread_set_state(Id, x86_DEBUG_STATE64, @FDebugState64, old_StateCnt);
|
|
end;
|
|
|
|
if aKernResult <> KERN_SUCCESS then
|
|
debugln(DBG_WARNINGS, 'Failed to call thread_set_state for thread %d. Mach error: '+mach_error_string(aKernResult),[Id]);
|
|
end;
|
|
end;
|
|
|
|
procedure TDbgDarwinThread.LoadRegisterValues;
|
|
begin
|
|
if Process.Mode=dm32 then with FThreadState32 do
|
|
begin
|
|
FRegisterValueList.DbgRegisterAutoCreate['eax'].SetValue(__eax, IntToStr(__eax),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ecx'].SetValue(__ecx, IntToStr(__ecx),4,1);
|
|
FRegisterValueList.DbgRegisterAutoCreate['edx'].SetValue(__edx, IntToStr(__edx),4,2);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ebx'].SetValue(__ebx, IntToStr(__ebx),4,3);
|
|
FRegisterValueList.DbgRegisterAutoCreate['esp'].SetValue(__esp, IntToStr(__esp),4,4);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ebp'].SetValue(__ebp, IntToStr(__ebp),4,5);
|
|
FRegisterValueList.DbgRegisterAutoCreate['esi'].SetValue(__esi, IntToStr(__esi),4,6);
|
|
FRegisterValueList.DbgRegisterAutoCreate['edi'].SetValue(__edi, IntToStr(__edi),4,7);
|
|
FRegisterValueList.DbgRegisterAutoCreate['eip'].SetValue(__eip, IntToStr(__eip),4,8);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['eflags'].Setx86EFlagsValue(__eflags);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['cs'].SetValue(__cs, IntToStr(__cs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ss'].SetValue(__ss, IntToStr(__ss),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['ds'].SetValue(__ds, IntToStr(__ds),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['es'].SetValue(__es, IntToStr(__es),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fs'].SetValue(__fs, IntToStr(__fs),4,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['gs'].SetValue(__gs, IntToStr(__gs),4,0);
|
|
end else with FThreadState64 do
|
|
begin
|
|
FRegisterValueList.DbgRegisterAutoCreate['rax'].SetValue(__rax, IntToStr(__rax),8,0);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rbx'].SetValue(__rbx, IntToStr(__rbx),8,3);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rcx'].SetValue(__rcx, IntToStr(__rcx),8,2);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rdx'].SetValue(__rdx, IntToStr(__rdx),8,1);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rsi'].SetValue(__rsi, IntToStr(__rsi),8,4);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rdi'].SetValue(__rdi, IntToStr(__rdi),8,5);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rbp'].SetValue(__rbp, IntToStr(__rbp),8,6);
|
|
FRegisterValueList.DbgRegisterAutoCreate['rsp'].SetValue(__rsp, IntToStr(__rsp),8,7);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['r8'].SetValue(__r8, IntToStr(__r8),8,8);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r9'].SetValue(__r9, IntToStr(__r9),8,9);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r10'].SetValue(__r10, IntToStr(__r10),8,10);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r11'].SetValue(__r11, IntToStr(__r11),8,11);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r12'].SetValue(__r12, IntToStr(__r12),8,12);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r13'].SetValue(__r13, IntToStr(__r13),8,13);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r14'].SetValue(__r14, IntToStr(__r14),8,14);
|
|
FRegisterValueList.DbgRegisterAutoCreate['r15'].SetValue(__r15, IntToStr(__r15),8,15);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['rip'].SetValue(__rip, IntToStr(__rip),8,16);
|
|
FRegisterValueList.DbgRegisterAutoCreate['eflags'].Setx86EFlagsValue(__rflags);
|
|
|
|
FRegisterValueList.DbgRegisterAutoCreate['cs'].SetValue(__cs, IntToStr(__cs),8,43);
|
|
FRegisterValueList.DbgRegisterAutoCreate['fs'].SetValue(__fs, IntToStr(__fs),8,46);
|
|
FRegisterValueList.DbgRegisterAutoCreate['gs'].SetValue(__gs, IntToStr(__gs),8,47);
|
|
end;
|
|
FRegisterValueListValid:=true;
|
|
end;
|
|
|
|
function TDbgDarwinThread.GetInstructionPointerRegisterValue: TDbgPtr;
|
|
begin
|
|
if Process.Mode=dm32 then
|
|
result := FThreadState32.__eip
|
|
else
|
|
result := FThreadState64.__rip;
|
|
end;
|
|
|
|
function TDbgDarwinThread.GetStackPointerRegisterValue: TDbgPtr;
|
|
begin
|
|
if Process.Mode=dm32 then
|
|
result := FThreadState32.__esp
|
|
else
|
|
result := FThreadState64.__rsp;
|
|
end;
|
|
|
|
procedure TDbgDarwinThread.SetInstructionPointerRegisterValue(AValue: TDbgPtr);
|
|
begin
|
|
end;
|
|
|
|
procedure TDbgDarwinThread.SetStackPointerRegisterValue(AValue: TDbgPtr);
|
|
begin
|
|
end;
|
|
|
|
function TDbgDarwinThread.GetStackBasePointerRegisterValue: TDbgPtr;
|
|
begin
|
|
if Process.Mode=dm32 then
|
|
result := FThreadState32.__ebp
|
|
else
|
|
result := FThreadState64.__rbp;
|
|
end;
|
|
|
|
{ TDbgDarwinProcess }
|
|
|
|
function TDbgDarwinProcess.GetDebugAccessRights: boolean;
|
|
var
|
|
authFlags: AuthorizationFlags;
|
|
stat: OSStatus;
|
|
author: AuthorizationRef;
|
|
authItem: AuthorizationItem;
|
|
authRights: AuthorizationRights;
|
|
begin
|
|
result := false;
|
|
authFlags := kAuthorizationFlagExtendRights or kAuthorizationFlagPreAuthorize or kAuthorizationFlagInteractionAllowed or ( 1 << 5);
|
|
|
|
stat := AuthorizationCreate(nil, kAuthorizationEmptyEnvironment, authFlags, author);
|
|
if stat <> errAuthorizationSuccess then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to create authorization. Authorization error: ' + inttostr(stat));
|
|
exit;
|
|
end;
|
|
|
|
authItem.name:='system.privilege.taskport';
|
|
authItem.flags:=0;
|
|
authItem.value:=nil;
|
|
authItem.valueLength:=0;
|
|
|
|
authRights.count:=1;
|
|
authRights.items:=@authItem;
|
|
|
|
stat := AuthorizationCopyRights(author, authRights, kAuthorizationEmptyEnvironment, authFlags, nil);
|
|
if stat <> errAuthorizationSuccess then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to get debug-(taskport)-privilege. Authorization error: ' + inttostr(stat));
|
|
exit;
|
|
end;
|
|
result := true;
|
|
end;
|
|
|
|
procedure TDbgDarwinProcess.InitializeLoaders;
|
|
var
|
|
PrimaryLoader: TDbgImageLoader;
|
|
begin
|
|
PrimaryLoader := TDbgImageLoader.Create(FExecutableFilename);
|
|
PrimaryLoader.AddToLoaderList(LoaderList);
|
|
end;
|
|
|
|
function TDbgDarwinProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread;
|
|
begin
|
|
IsMainThread:=true;
|
|
result := TDbgDarwinThread.Create(Self, AthreadIdentifier, AthreadIdentifier)
|
|
end;
|
|
|
|
function TDbgDarwinProcess.CreateWatchPointData: TFpWatchPointData;
|
|
begin
|
|
Result := TFpIntelWatchPointData.Create;
|
|
end;
|
|
|
|
constructor TDbgDarwinProcess.Create(const AFileName: string;
|
|
AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel;
|
|
AProcessConfig: TDbgProcessConfig);
|
|
begin
|
|
inherited Create(AFileName, AnOsClasses, AMemManager, AMemModel, AProcessConfig);
|
|
|
|
GetDebugAccessRights;
|
|
end;
|
|
|
|
destructor TDbgDarwinProcess.Destroy;
|
|
begin
|
|
FProcProcess.Free;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TDbgDarwinProcess.StartInstance(AParams, AnEnvironment: TStrings;
|
|
AWorkingDirectory, AConsoleTty: string; AFlags: TStartInstanceFlags; out
|
|
AnError: TFpError): boolean;
|
|
var
|
|
AProcess: TProcessUTF8;
|
|
AnExecutabeFilename: string;
|
|
AMasterPtyFd: cint;
|
|
aKernResult: kern_return_t;
|
|
begin
|
|
result := false;
|
|
|
|
AnExecutabeFilename:=ExcludeTrailingPathDelimiter(Name);
|
|
if DirectoryExists(AnExecutabeFilename) then
|
|
begin
|
|
if not (ExtractFileExt(AnExecutabeFilename)='.app') then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, format('Can not debug %s, because it''s a directory',[AnExecutabeFilename]));
|
|
Exit;
|
|
end;
|
|
|
|
AnExecutabeFilename := AnExecutabeFilename + '/Contents/MacOS/' + ChangeFileExt(ExtractFileName(AnExecutabeFilename),'');
|
|
if not FileExists(AnExecutabeFilename) then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, format('Can not find %s.',[AnExecutabeFilename]));
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
AMasterPtyFd:=-1;
|
|
if siRediretOutput in AFlags then
|
|
begin
|
|
if AConsoleTty<>'' then
|
|
debugln(DBG_VERBOSE, 'It is of no use to provide a console-tty when the console output is being redirected.');
|
|
AMasterPtyFd := posix_openpt(O_RDWR + O_NOCTTY);
|
|
if AMasterPtyFd<0 then
|
|
DebugLn(DBG_WARNINGS, 'Failed to open pseudo-tty. Errno: ' + IntToStr(fpgeterrno))
|
|
else
|
|
begin
|
|
if grantpt(AMasterPtyFd)<>0 then
|
|
DebugLn(DBG_WARNINGS, 'Failed to set pseudo-tty slave permissions. Errno: ' + IntToStr(fpgeterrno));
|
|
if unlockpt(AMasterPtyFd)<>0 then
|
|
DebugLn(DBG_WARNINGS, 'Failed to unlock pseudo-tty slave. Errno: ' + IntToStr(fpgeterrno));
|
|
AConsoleTty := strpas(ptsname(AMasterPtyFd));
|
|
end;
|
|
end;
|
|
|
|
AProcess := TProcessUTF8.Create(nil);
|
|
try
|
|
AProcess.OnForkEvent:=@OnForkEvent;
|
|
AProcess.Executable:=AnExecutabeFilename;
|
|
AProcess.Parameters:=AParams;
|
|
AProcess.Environment:=AnEnvironment;
|
|
AProcess.CurrentDirectory:=AWorkingDirectory;
|
|
GConsoleTty := AConsoleTty;
|
|
|
|
AProcess.Execute;
|
|
Init(AProcess.ProcessID, 0);
|
|
FExecutableFilename:=AnExecutabeFilename;
|
|
FMasterPtyFd := AMasterPtyFd;
|
|
FProcProcess := AProcess;
|
|
sleep(100);
|
|
Result:=ProcessID > 0;
|
|
|
|
if Result then begin
|
|
aKernResult:=task_for_pid(mach_task_self, ProcessID, FTaskPort);
|
|
if aKernResult <> KERN_SUCCESS then
|
|
begin
|
|
Result := False;
|
|
debugln(DBG_WARNINGS, 'Failed to get task for process '+IntToStr(ProcessID)+'. Probably insufficient rights to debug applications. Mach error: '+mach_error_string(aKernResult));
|
|
end;
|
|
end;
|
|
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
DebugLn(DBG_WARNINGS, Format('Failed to start process "%s". Errormessage: "%s".',[AnExecutabeFilename, E.Message]));
|
|
AProcess.Free;
|
|
|
|
if AMasterPtyFd>-1 then
|
|
FpClose(AMasterPtyFd);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
class function TDbgDarwinProcess.isSupported(ATargetInfo: TTargetDescriptor
|
|
): boolean;
|
|
begin
|
|
result := (ATargetInfo.OS = osDarwin) and
|
|
(ATargetInfo.machineType = mtX86_64);
|
|
end;
|
|
|
|
function TDbgDarwinProcess.ReadData(const AAdress: TDbgPtr;
|
|
const ASize: Cardinal; out AData): Boolean;
|
|
var
|
|
aKernResult: kern_return_t;
|
|
cnt: mach_msg_Type_number_t;
|
|
b: pointer;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgDarwinProcess.ReadData');{$ENDIF}
|
|
result := false;
|
|
|
|
aKernResult := mach_vm_read(FTaskPort, AAdress, ASize, PtrUInt(b), cnt);
|
|
if aKernResult <> KERN_SUCCESS then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'Failed to read data at address '+FormatAddress(AAdress)+'. Mach error: '+mach_error_string(aKernResult));
|
|
Exit;
|
|
end;
|
|
System.Move(b^, AData, Cnt);
|
|
MaskBreakpointsInReadData(AAdress, ASize, AData);
|
|
result := true;
|
|
end;
|
|
|
|
function TDbgDarwinProcess.WriteData(const AAdress: TDbgPtr;
|
|
const ASize: Cardinal; const AData): Boolean;
|
|
var
|
|
aKernResult: kern_return_t;
|
|
begin
|
|
{$IFDEF FPDEBUG_THREAD_CHECK}AssertFpDebugThreadId('TDbgDarwinProcess.WriteData');{$ENDIF}
|
|
result := false;
|
|
aKernResult:=mach_vm_protect(FTaskPort, AAdress, ASize, boolean_t(false), 7 {VM_PROT_READ + VM_PROT_WRITE + VM_PROT_COPY});
|
|
if aKernResult <> KERN_SUCCESS then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'Failed to call vm_protect for address '+FormatAddress(AAdress)+'. Mach error: '+mach_error_string(aKernResult));
|
|
Exit;
|
|
end;
|
|
|
|
aKernResult := mach_vm_write(FTaskPort, AAdress, vm_offset_t(@AData), ASize);
|
|
if aKernResult <> KERN_SUCCESS then
|
|
begin
|
|
DebugLn(DBG_WARNINGS, 'Failed to write data at address '+FormatAddress(AAdress)+'. Mach error: '+mach_error_string(aKernResult));
|
|
Exit;
|
|
end;
|
|
|
|
result := true;
|
|
end;
|
|
|
|
function TDbgDarwinProcess.CallParamDefaultLocation(AParamIdx: Integer
|
|
): TFpDbgMemLocation;
|
|
begin
|
|
case Mode of
|
|
dm32: case AParamIdx of
|
|
0: Result := RegisterLoc(0); // EAX
|
|
1: Result := RegisterLoc(2); // EDX
|
|
2: Result := RegisterLoc(1); // ECX
|
|
end;
|
|
dm64: case AParamIdx of
|
|
0: Result := RegisterLoc(5); // RDI
|
|
1: Result := RegisterLoc(4); // RSI
|
|
2: Result := RegisterLoc(1); // RDX
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDbgDarwinProcess.CheckForConsoleOutput(ATimeOutMs: integer): integer;
|
|
Var
|
|
f: TfdSet;
|
|
sleepytime: ttimeval;
|
|
begin
|
|
sleepytime.tv_sec := ATimeOutMs div 1000;
|
|
sleepytime.tv_usec := (ATimeOutMs mod 1000)*1000;
|
|
FpFD_ZERO(f);
|
|
fpFD_SET(FMasterPtyFd,f);
|
|
result := fpselect(FMasterPtyFd+1,@f,nil,nil,@sleepytime);
|
|
end;
|
|
|
|
function TDbgDarwinProcess.GetConsoleOutput: string;
|
|
var
|
|
ABytesRead: cint;
|
|
ABuf: array[0..1023] of char;
|
|
begin
|
|
ABytesRead := fpRead(FMasterPtyFd, ABuf[0], SizeOf(ABuf));
|
|
if ABytesRead>0 then
|
|
result := Copy(ABuf, 0, ABytesRead)
|
|
else
|
|
result := '';
|
|
end;
|
|
|
|
procedure TDbgDarwinProcess.SendConsoleInput(AString: string);
|
|
begin
|
|
if FpWrite(FMasterPtyFd, AString[1], length(AString)) <> Length(AString) then
|
|
debugln(DBG_WARNINGS, 'Failed to send input to console.');
|
|
end;
|
|
|
|
procedure TDbgDarwinProcess.TerminateProcess;
|
|
begin
|
|
FIsTerminating:=true;
|
|
if fpkill(ProcessID,SIGKILL)<>0 then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to send SIGKILL to process %d. Errno: %d',[ProcessID, errno]);
|
|
FIsTerminating:=false;
|
|
end;
|
|
end;
|
|
|
|
function TDbgDarwinProcess.Continue(AProcess: TDbgProcess; AThread: TDbgThread; SingleStep: boolean): boolean;
|
|
var
|
|
e: integer;
|
|
begin
|
|
fpseterrno(0);
|
|
{$ifdef linux}
|
|
fpPTrace(PTRACE_CONT, ProcessID, nil, nil);
|
|
{$endif linux}
|
|
{$ifdef darwin}
|
|
AThread.NextIsSingleStep:=SingleStep;
|
|
AThread.BeforeContinue; // TODO: All threads
|
|
if HasInsertedBreakInstructionAtLocation(AThread.GetInstructionPointerRegisterValue) then begin
|
|
TempRemoveBreakInstructionCode(AThread.GetInstructionPointerRegisterValue);
|
|
fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal));
|
|
TDbgDarwinThread(AThread).FIsSteppingBreakPoint := True;
|
|
end
|
|
else
|
|
if SingleStep then begin
|
|
fpPTrace(PTRACE_SINGLESTEP, ProcessID, pointer(1), pointer(FExceptionSignal));
|
|
TDbgDarwinThread(AThread).FIsSteppingBreakPoint := True;
|
|
end
|
|
else if FIsTerminating then
|
|
fpPTrace(PTRACE_KILL, ProcessID, pointer(1), nil)
|
|
else
|
|
fpPTrace(PTRACE_CONT, ProcessID, pointer(1), pointer(FExceptionSignal));
|
|
{$endif darwin}
|
|
e := fpgeterrno;
|
|
if e <> 0 then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to continue process. Errcode: '+inttostr(e));
|
|
result := false;
|
|
end
|
|
else
|
|
result := true;
|
|
end;
|
|
|
|
function TDbgDarwinProcess.WaitForDebugEvent(out ProcessIdentifier, ThreadIdentifier: THandle): boolean;
|
|
var
|
|
aKernResult: kern_return_t;
|
|
act_list: thread_act_array_t;
|
|
act_listCtn: mach_msg_type_number_t;
|
|
begin
|
|
ThreadIdentifier:=-1;
|
|
|
|
ProcessIdentifier:=FpWaitPid(-1, FStatus, 0);
|
|
RestoreTempBreakInstructionCodes;
|
|
|
|
result := ProcessIdentifier<>-1;
|
|
if not result then
|
|
debugln(DBG_WARNINGS, 'Failed to wait for debug event. Errcode: %d', [fpgeterrno])
|
|
else if (WIFSTOPPED(FStatus)) then
|
|
begin
|
|
aKernResult := task_threads(FTaskPort, act_list, act_listCtn);
|
|
if aKernResult <> KERN_SUCCESS then
|
|
begin
|
|
debugln(DBG_WARNINGS, 'Failed to call task_threads. Mach error: '+mach_error_string(aKernResult));
|
|
end
|
|
else if act_listCtn>0 then
|
|
ThreadIdentifier := act_list^[0];
|
|
end
|
|
end;
|
|
|
|
function TDbgDarwinProcess.Pause: boolean;
|
|
begin
|
|
result := FpKill(ProcessID, SIGTRAP)=0;
|
|
PauseRequested:=true;
|
|
end;
|
|
|
|
function TDbgDarwinProcess.AnalyseDebugEvent(AThread: TDbgThread): TFPDEvent;
|
|
|
|
begin
|
|
FExceptionSignal:=0;
|
|
if wifexited(FStatus) or wifsignaled(FStatus) then
|
|
begin
|
|
SetExitCode(wexitStatus(FStatus));
|
|
// Clear all pending signals
|
|
repeat
|
|
until FpWaitPid(-1, FStatus, WNOHANG)<1;
|
|
|
|
result := deExitProcess
|
|
end
|
|
else if WIFSTOPPED(FStatus) then
|
|
begin
|
|
//debugln(DBG_WARNINGS, 'Stopped ',FStatus, ' signal: ',wstopsig(FStatus));
|
|
TDbgDarwinThread(AThread).ReadThreadState;
|
|
case wstopsig(FStatus) of
|
|
SIGTRAP:
|
|
begin
|
|
if not FProcessStarted then
|
|
begin
|
|
result := deCreateProcess;
|
|
FProcessStarted:=true;
|
|
end
|
|
else
|
|
begin
|
|
result := deBreakpoint;
|
|
if not TDbgDarwinThread(AThread).FIsSteppingBreakPoint then
|
|
AThread.CheckAndResetInstructionPointerAfterBreakpoint;
|
|
end
|
|
end;
|
|
SIGBUS:
|
|
begin
|
|
ExceptionClass:='SIGBUS';
|
|
FExceptionSignal:=SIGBUS;
|
|
result := deException;
|
|
end;
|
|
SIGINT:
|
|
begin
|
|
ExceptionClass:='SIGINT';
|
|
FExceptionSignal:=SIGINT;
|
|
result := deException;
|
|
end;
|
|
SIGSEGV:
|
|
begin
|
|
ExceptionClass:='SIGSEGV';
|
|
FExceptionSignal:=SIGSEGV;
|
|
result := deException;
|
|
end;
|
|
SIGKILL:
|
|
begin
|
|
if FIsTerminating then
|
|
result := deInternalContinue
|
|
else
|
|
begin
|
|
ExceptionClass:='SIGKILL';
|
|
FExceptionSignal:=SIGKILL;
|
|
result := deException;
|
|
end;
|
|
end;
|
|
SIGCHLD:
|
|
begin
|
|
FExceptionSignal:=SIGCHLD;
|
|
result := deInternalContinue;
|
|
end
|
|
else
|
|
begin
|
|
ExceptionClass:='Unknown exception code '+inttostr(wstopsig(FStatus));
|
|
FExceptionSignal:=wstopsig(FStatus);
|
|
result := deException;
|
|
end;
|
|
end; {case}
|
|
if result=deException then
|
|
ExceptionClass:='External: '+ExceptionClass;
|
|
end
|
|
else
|
|
raise exception.CreateFmt('Received unknown status %d from process with pid=%d',[FStatus, ProcessID]);
|
|
|
|
TDbgDarwinThread(AThread).FIsSteppingBreakPoint := False;
|
|
end;
|
|
|
|
initialization
|
|
DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} );
|
|
DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} );
|
|
|
|
RegisterDbgOsClasses(TOSDbgClasses.Create(
|
|
TDbgDarwinProcess,
|
|
TDbgDarwinThread,
|
|
TX86AsmDecoder
|
|
));
|
|
|
|
end.
|