lazarus/components/fpdebug/fpdbgdarwinclasses.pas

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.