diff --git a/FastMM5.pas b/FastMM5.pas index a947d8f..5e62d27 100644 --- a/FastMM5.pas +++ b/FastMM5.pas @@ -147,7 +147,10 @@ interface uses - Winapi.Windows; + {$IFDEF MACOS} + FastMM5_OSXUtil, Posix.Pthread, Posix.Systypes + {$ELSE} + Winapi.Windows{$ENDIF}; {$RangeChecks Off} {$BoolEval Off} @@ -218,6 +221,10 @@ interface {$endif} {$endif} +{$ifdef MACOS} + {$define PurePascal} +{$endif} + {Optionally disable debug info in this unit, so the debugger does not step into it.} {$ifdef FastMM_NoDebugInfo} {$DEBUGINFO OFF} @@ -292,7 +299,8 @@ interface {$endif} {The default name of debug support library.} - CFastMM_DefaultDebugSupportLibraryName = {$ifndef 64Bit}'FastMM_FullDebugMode.dll'{$else}'FastMM_FullDebugMode64.dll'{$endif}; + CFastMM_DefaultDebugSupportLibraryName = {$IFDEF MACOS} {$IFDEF CPUARM64} 'libFastMM_FullDebugModeARM64.dylib' {$ELSE} 'libFastMM_FullDebugMode64.dylib' {$ENDIF} {$ELSE} + {$ifndef 64Bit}'FastMM_FullDebugMode.dll'{$else}'FastMM_FullDebugMode64.dll'{$endif} {$ENDIF}; type @@ -2399,7 +2407,7 @@ function CharCount(APFirstFreeChar, APBufferStart: PWideChar): Integer; forward; {Releases a block of memory back to the operating system. Returns 0 on success, -1 on failure.} function OS_FreeVirtualMemory(APointer: Pointer; ABlockSize: NativeInt): Integer; begin - if Winapi.Windows.VirtualFree(APointer, 0, MEM_RELEASE) then + if {$IFDEF MSWINDOWS}Winapi.Windows.{$ENDIF}VirtualFree(APointer, {$IFDEF MSWINDOWS}0{$ELSE}ABlockSize{$ENDIF}, MEM_RELEASE) then begin AtomicDecrement(MemoryUsageCurrent, NativeUInt(ABlockSize)); Result := 0; @@ -2416,7 +2424,7 @@ function OS_AllocateVirtualMemory(ABlockSize: NativeInt; AReserveOnlyNoReadWrite begin if AReserveOnlyNoReadWriteAccess then begin - Result := Winapi.Windows.VirtualAlloc(nil, NativeUInt(ABlockSize), MEM_RESERVE, PAGE_NOACCESS); + Result := {$ifdef MSWINDOWS}Winapi.Windows.{$ENDIF}VirtualAlloc(nil, NativeUInt(ABlockSize), MEM_RESERVE, PAGE_NOACCESS); end else begin @@ -2424,7 +2432,7 @@ function OS_AllocateVirtualMemory(ABlockSize: NativeInt; AReserveOnlyNoReadWrite LAllocationFlags := MEM_COMMIT or MEM_TOP_DOWN else LAllocationFlags := MEM_COMMIT; - Result := Winapi.Windows.VirtualAlloc(nil, NativeUInt(ABlockSize), LAllocationFlags, PAGE_READWRITE); + Result := {$ifdef MSWINDOWS}Winapi.Windows.{$ENDIF}VirtualAlloc(nil, NativeUInt(ABlockSize), LAllocationFlags, PAGE_READWRITE); {The emergency address space reserve is released when address space runs out for the first time. This allows some subsequent memory allocation requests to succeed in order to allow the application to allocate some memory for error handling, etc. in response to the EOutOfMemory exception. This only applies to 32-bit applications.} @@ -2454,12 +2462,12 @@ function OS_AllocateVirtualMemoryAtAddress(APAddress: Pointer; ABlockSize: Nativ begin if AReserveOnlyNoReadWriteAccess then begin - Result := Winapi.Windows.VirtualAlloc(APAddress, NativeUInt(ABlockSize), MEM_RESERVE, PAGE_NOACCESS) <> nil; + Result := {$ifdef MSWINDOWS}Winapi.Windows.{$endif}VirtualAlloc(APAddress, NativeUInt(ABlockSize), MEM_RESERVE, PAGE_NOACCESS) <> nil; end else begin - Result := (Winapi.Windows.VirtualAlloc(APAddress, NativeUInt(ABlockSize), MEM_RESERVE, PAGE_READWRITE) <> nil) - and (Winapi.Windows.VirtualAlloc(APAddress, NativeUInt(ABlockSize), MEM_COMMIT, PAGE_READWRITE) <> nil); + Result := ({$ifdef MSWINDOWS}Winapi.Windows.{$endif}VirtualAlloc(APAddress, NativeUInt(ABlockSize), MEM_RESERVE, PAGE_READWRITE) <> nil) + and ({$ifdef MSWINDOWS}Winapi.Windows.{$endif}VirtualAlloc(APAddress, NativeUInt(ABlockSize), MEM_COMMIT, PAGE_READWRITE) <> nil); end; if Result then @@ -2480,10 +2488,13 @@ function OS_AllocateVirtualMemoryAtAddress(APAddress: Pointer; ABlockSize: Nativ {Determines the size and state of the virtual memory region starting at APRegionStart.} procedure OS_GetVirtualMemoryRegionInfo(APRegionStart: Pointer; var AMemoryRegionInfo: TMemoryRegionInfo); +{$ifdef MSWINDOWS} var LMemInfo: TMemoryBasicInformation; +{$endif} begin - if Winapi.Windows.VirtualQuery(APRegionStart, LMemInfo, SizeOf(LMemInfo)) > 0 then + {$ifdef MSWINDOWS} + if Winapi.Windows.VirtualQuery(APRegionStart, LMemInfo, SizeOf(LMemInfo)) > 0 then //SZ: I don't know how to do this on MacOS begin AMemoryRegionInfo.RegionStartAddress := LMemInfo.BaseAddress; AMemoryRegionInfo.RegionSize := LMemInfo.RegionSize; @@ -2508,6 +2519,7 @@ procedure OS_GetVirtualMemoryRegionInfo(APRegionStart: Pointer; var AMemoryRegio end else + {$endif} begin {VirtualQuery fails for addresses above the highest memory address accessible to the process. (Experimentally determined as addresses >= $ffff0000 under 32-bit, and addresses >= $7fffffff0000 under 64-bit.)} @@ -2519,13 +2531,21 @@ procedure OS_GetVirtualMemoryRegionInfo(APRegionStart: Pointer; var AMemoryRegio current thread is unable to make any progress, because it is waiting for locked resources.} procedure OS_AllowOtherThreadToRun; inline; begin + {$ifdef MACOS} + YieldProcessor; // sched_yield; + {$else} Winapi.Windows.SwitchToThread; + {$endif} end; {Returns the thread ID for the calling thread.} function OS_GetCurrentThreadID: Cardinal; inline; begin + {$ifdef MACOS} + Result := Cardinal(pthread_self); + {$else} Result := Winapi.Windows.GetCurrentThreadID; + {$endif} end; {Returns the current system date and time. The time is in 24 hour format.} @@ -2533,7 +2553,7 @@ procedure OS_GetCurrentDateTime(var AYear, AMonth, ADay, AHour, AMinute, ASecond var LSystemTime: TSystemTime; begin - Winapi.Windows.GetLocalTime(LSystemTime); + {$IFDEF MSWINDOWS}Winapi.Windows.{$ENDIF}GetLocalTime(LSystemTime); AYear := LSystemTime.wYear; AMonth := LSystemTime.wMonth; ADay := LSystemTime.wDay; @@ -2547,9 +2567,14 @@ procedure OS_GetCurrentDateTime(var AYear, AMonth, ADay, AHour, AMinute, ASecond after 49.7 days.} function OS_GetMillisecondsSinceStartup: Cardinal; begin + {$ifdef MACOS} + Result := AbsoluteToNanoseconds(UpTime) div 1000000; + {$else} Result := Winapi.Windows.GetTickCount; + {$endif} end; +{$IFDEF MSWINDOWS} procedure OS_MillisecondsSinceStartupToDateTime(AMillisecondsSinceStartup: Cardinal; var AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliseconds: Word); var @@ -2575,6 +2600,33 @@ procedure OS_MillisecondsSinceStartupToDateTime(AMillisecondsSinceStartup: Cardi ASecond := LSystemTime.wSecond; AMilliseconds := LSystemTime.wMilliseconds; end; +{$ENDIF} +{$IFDEF MACOS} +procedure OS_MillisecondsSinceStartupToDateTime(AMillisecondsSinceStartup: Cardinal; + var AYear, AMonth, ADay, AHour, AMinute, ASecond, AMilliseconds: Word); +var + LSystemTime: TSystemTime; + CurTime: Int64; + LTimeDelta: Cardinal; +begin + {Get the current time, as well as the delta between the current time and the required timestamp.} + GetLocalTime(LSystemTime); + LTimeDelta := OS_GetMillisecondsSinceStartup - AMillisecondsSinceStartup; + + {Get the current time in milliseconds, so the delta can be subtracted easily, then convert it} + CurTime := GetTimeMilliSeconds; + Dec(CurTime, LTimeDelta); + LSystemTime := MilliSecondsToSystemTime(CurTime); + + AYear := LSystemTime.wYear; + AMonth := LSystemTime.wMonth; + ADay := LSystemTime.wDay; + AHour := LSystemTime.wHour; + AMinute := LSystemTime.wMinute; + ASecond := LSystemTime.wSecond; + AMilliseconds := LSystemTime.wMilliseconds; +end; +{$ENDIF} {Fills a buffer with the full path and filename of the application. If AReturnLibraryFilename = True and this is a library then the full path and filename of the library is returned instead.} @@ -2588,9 +2640,14 @@ function OS_GetApplicationFilename(APFilenameBuffer, APBufferEnd: PWideChar; ARe LModuleHandle := 0; if AReturnLibraryFilename and IsLibrary then LModuleHandle := HInstance; - + {$ifdef MACOS} + var LBufferSize := (NativeInt(APBufferEnd) - NativeInt(Result)) div SizeOf(WideChar); + StrLCopy(Result, PChar(ParamStr(0)), LBufferSize); + Inc(Result, Length(ParamStr(0))); + {$else} LNumChars := Winapi.Windows.GetModuleFileNameW(LModuleHandle, Result, Cardinal(CharCount(APBufferEnd, APFilenameBuffer))); Inc(Result, LNumChars); + {$endif} end; function OS_GetEnvironmentVariableValue(APEnvironmentVariableName, APValueBuffer, APBufferEnd: PWideChar): PWideChar; @@ -2603,23 +2660,51 @@ function OS_GetEnvironmentVariableValue(APEnvironmentVariableName, APValueBuffer Exit; LBufferSize := Cardinal((NativeUInt(APBufferEnd) - NativeUInt(Result)) div SizeOf(WideChar)); - LNumChars := Winapi.Windows.GetEnvironmentVariableW(APEnvironmentVariableName, Result, LBufferSize); + LNumChars := {$ifdef MSWINDOWS}Winapi.Windows.{$ENDIF}GetEnvironmentVariableW(APEnvironmentVariableName, Result, LBufferSize); if LNumChars < LBufferSize then Inc(Result, LNumChars); end; +{$IFDEF MACOS} +function AllocateUTF8String(APWideText: PWideChar; APBufferSize: PInteger): PAnsiChar; forward; +{$ENDIF} + {Returns True if the given file exists. APFileName must be a #0 terminated string.} function OS_FileExists(APFileName: PWideChar): Boolean; begin {This will return True for folders and False for files that are locked by another process, but is "good enough" for the purpose for which it will be used.} + {$ifdef MACOS} + var Stat: _stat; + var LFileNameUTF8: PAnsiChar; + var BufSize: Integer; + LFileNameUTF8 := AllocateUTF8String(APFileName, @BufSize); + try + Result := lstat(PAnsiChar(UTF8Encode(APFileName)), Stat) = 0; + finally + OS_FreeVirtualMemory(LFileNameUTF8, BufSize); + end; + {$else} Result := Winapi.Windows.GetFileAttributesW(APFileName) <> INVALID_FILE_ATTRIBUTES; + {$endif} end; {Attempts to delete the file. Returns True if it was successfully deleted.} function OS_DeleteFile(APFileName: PWideChar): Boolean; begin + {$ifdef MACOS} + var LFileNameUTF8: PAnsiChar; + var BufSize: Integer; + LFileNameUTF8 := AllocateUTF8String(APFileName, @BufSize); + try + Result := remove(PAnsiChar(LFileNameUTF8)) >= 0; + finally + OS_FreeVirtualMemory(LFileNameUTF8, BufSize); + end; + + {$else} Result := Winapi.Windows.DeleteFileW(APFileName); + {$endif} end; {Opens the given file for writing, returning the file handle. If the file does not exist it will be created. The file @@ -2627,8 +2712,20 @@ function OS_DeleteFile(APFileName: PWideChar): Boolean; function OS_OpenOrCreateFile(APFileName: PWideChar; var AFileHandle: THandle): Boolean; begin {Try to open/create the file in read/write mode.} + {$IFDEF MACOS} + var LFileNameUTF8: PAnsiChar; + var BufSize: Integer; + LFileNameUTF8 := AllocateUTF8String(APFileName, @BufSize); + try + AFileHandle := CreateFileUTF8(LFileNameUTF8, GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, 0); + finally + OS_FreeVirtualMemory(LFileNameUTF8, BufSize); + end; + {$ELSE} AFileHandle := Winapi.Windows.CreateFileW(APFileName, GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ, nil, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + {$ENDIF} if AFileHandle = INVALID_HANDLE_VALUE then Exit(False); @@ -2643,7 +2740,7 @@ function OS_WriteFile(AFileHandle: THandle; APData: Pointer; ADataSizeInBytes: I var LBytesWritten: Cardinal; begin - Winapi.Windows.WriteFile(AFileHandle, APData^, Cardinal(ADataSizeInBytes), LBytesWritten, nil); + {$IFDEF MSWINDOWS}Winapi.Windows.{$ENDIF}WriteFile(AFileHandle, APData^, Cardinal(ADataSizeInBytes), LBytesWritten, nil); Result := LBytesWritten = Cardinal(ADataSizeInBytes); end; @@ -2655,13 +2752,22 @@ procedure OS_CloseFile(AFileHandle: THandle); procedure OS_OutputDebugString(APDebugMessage: PWideChar); inline; begin + {$ifdef MACOS} + Writeln(APDebugMessage); + {$else} Winapi.Windows.OutputDebugString(APDebugMessage); + {$endif} end; {Shows a message box if the program is not showing one already.} procedure OS_ShowMessageBox(APText, APCaption: PWideChar); begin + {$ifdef MACOS} + WriteLn(APText); + WriteLn(APCaption); + {$else} Winapi.Windows.MessageBoxW(0, APText, APCaption, MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_DEFAULT_DESKTOP_ONLY); + {$endif} end; @@ -2748,6 +2854,31 @@ function ConvertUTF16toUTF8(APWideText: PWideChar; ANumWideChars: Integer; APOut Dec(Result); end; +{$IFDEF MACOS} +{Converts the UTF-16 text pointed to by APWideText to UTF-8 and allocates a buffer for the output. +Returns a buffer to the first byte of the string. Returns the size of the buffer in LPBufferSize. (SZ) +the buffer must be freed using OS_FreeVirtualMemory } +function AllocateUTF8String(APWideText: PWideChar; APBufferSize: PInteger): PAnsiChar; +var + LPBufferStart, LPBufferPos: PByte; + AWideCharCount: Integer; +begin + {We need to add either a BOM or a couple of line breaks before the text, so a larger buffer is needed than the + maximum text size. If converting to UTF-8 it is also possible for the resulting text to be bigger than the UTF-16 + encoded text.} + AWideCharCount := Length(APWideText); + APBufferSize^ := (AWideCharCount + 4) * 3; + + LPBufferStart := OS_AllocateVirtualMemory(APBufferSize^, False); + if LPBufferStart = nil then + Exit(nil); + + LPBufferPos := LPBufferStart; + LPBufferPos := ConvertUTF16toUTF8(APWideText, AWideCharCount, LPBufferPos); + Result := PAnsiChar(LPBufferStart); +end; +{$ENDIF} + function OpenOrCreateTextFile(APFileName: PWideChar; AAddLineBreakToExistingFile: Boolean; var AFileHandle: THandle): Boolean; const @@ -4661,6 +4792,8 @@ function FastMM_FreeMem_FreeLargeBlock(APLargeBlock: Pointer): Integer; {Try to lock the large block manager so that the block may be freed.} if AtomicCmpExchange(LPLargeBlockManager.LargeBlockManagerLocked, 1, 0) = 0 then begin + MemoryBarrier; // SZ: needed here? + {Unlink the large block from the circular queue for the manager.} UnlinkLargeBlock(LPLargeBlockHeader); @@ -4995,6 +5128,8 @@ procedure BinMediumSequentialFeedRemainder(APMediumBlockManager: PMediumBlockMan if AtomicCmpExchange(APMediumBlockManager.LastMediumBlockSequentialFeedOffset.IntegerValue, 0, LPreviousLastSequentialFeedBlockOffset) = LPreviousLastSequentialFeedBlockOffset then begin + MemoryBarrier; // SZ: needed here? + LSequentialFeedFreeSize := LPreviousLastSequentialFeedBlockOffset - CMediumBlockSpanHeaderSize; {Get the block for the remaining space} @@ -5147,6 +5282,7 @@ function FastMM_FreeMem_FreeMediumBlock(APMediumBlock: Pointer): Integer; begin {Memory fence required for ARM here} + MemoryBarrier; if LPMediumBlockManager.PendingFreeList = nil then begin @@ -5221,6 +5357,7 @@ function FastMM_GetMem_GetMediumBlock_AllocateNewSequentialFeedSpan(APMediumBloc APMediumBlockManager.SequentialFeedMediumBlockSpan := LPNewSpan; {May need a memory fence here for ARM.} + MemoryBarrier; APMediumBlockManager.LastMediumBlockSequentialFeedOffset.IntegerValue := Integer(NativeInt(Result) - NativeInt(LPNewSpan)); end @@ -5331,6 +5468,8 @@ function FastMM_GetMem_GetMediumBlock_TryGetBlockFromSequentialFeedSpan(APMedium LNewLastSequentialFeedBlockOffset.IntegerAndABACounter, LPreviousLastSequentialFeedBlockOffset.IntegerAndABACounter) = LPreviousLastSequentialFeedBlockOffset.IntegerAndABACounter then begin + MemoryBarrier; // SZ: needed here? + Result := Pointer(PByte(LPSequentialFeedSpan) + LNewLastSequentialFeedBlockOffset.IntegerValue); {Set the header for the block.} @@ -6105,6 +6244,7 @@ function FastMM_GetMem_GetMediumBlock(AMinimumBlockSize, AOptimalBlockSize, AMax try to lock the manager, and allocate a new sequential feed span if necessary.} if AtomicCmpExchange(LPMediumBlockManager.MediumBlockManagerLocked, 1, 0) = 0 then begin + MemoryBarrier; // SZ: needed here? {3.1) Try to allocate a free block. Another thread may have freed a block before this arena could be locked.} if ((LPMediumBlockManager.MediumBlockBinGroupBitmap and LLargerBinGroupsMask) <> 0) @@ -6596,6 +6736,7 @@ function FastMM_FreeMem_FreeSmallBlock(APSmallBlock: Pointer): Integer; inline; begin {ARM requires a memory fence here.} + MemoryBarrier; if LPSmallBlockManager.PendingFreeList = nil then begin @@ -6734,6 +6875,7 @@ function FastMM_GetMem_GetSmallBlock_AllocateNewSequentialFeedSpanAndUnlockArena LPSmallBlockSpan.BlocksInUse := LTotalBlocksInSpan; {Memory fence required for ARM here.} + MemoryBarrier; {Set it up for sequential block serving} LLastBlockOffset := CSmallBlockSpanHeaderSize + APSmallBlockManager.BlockSize * (LTotalBlocksInSpan - 1); @@ -6862,6 +7004,7 @@ function FastMM_GetMem_GetSmallBlock_TryGetBlockFromSequentialFeedSpan(APSmallBl LNewLastSequentialFeedBlockOffset.IntegerAndABACounter, LPreviousLastSequentialFeedBlockOffset.IntegerAndABACounter) = LPreviousLastSequentialFeedBlockOffset.IntegerAndABACounter then begin + MemoryBarrier; // SZ: needed here? Result := @PByte(LPSequentialFeedSpan)[LNewLastSequentialFeedBlockOffset.IntegerValue]; SetSmallBlockHeader(Result, LPSequentialFeedSpan, False, False); @@ -6992,6 +7135,7 @@ function FastMM_GetMem_GetSmallBlock_AllocateFreeBlockAndUnlockArena(APSmallBloc @BlockHasNoDebugInfo: pop esi +end; {$else} var LPFirstPartiallyFreeSpan, LPNewFirstPartiallyFreeSpan: PSmallBlockSpanHeader; @@ -7022,6 +7166,7 @@ function FastMM_GetMem_GetSmallBlock_AllocateFreeBlockAndUnlockArena(APSmallBloc end; {ARM requires a data memory barrier here to ensure that all prior writes have completed before the arena is unlocked.} + MemoryBarrier; APSmallBlockManager.SmallBlockManagerLocked := 0; @@ -7039,8 +7184,8 @@ function FastMM_GetMem_GetSmallBlock_AllocateFreeBlockAndUnlockArena(APSmallBloc SetBlockHasDebugInfo(Result, False); end; -{$endif} end; +{$endif} {Tries to allocate a small block through the given small block manager. If the manager has no available blocks, or it is locked, then the corresponding managers in other arenas are also tried.} @@ -7172,6 +7317,7 @@ function FastMM_GetMem_GetSmallBlock(APSmallBlockManager: PSmallBlockManager): P call LogSmallBlockThreadContentionAndYieldToOtherThread pop eax jmp @Attempt1Loop +end; {$else} begin while True do @@ -7250,6 +7396,7 @@ function FastMM_GetMem_GetSmallBlock(APSmallBlockManager: PSmallBlockManager): P if AtomicCmpExchange(APSmallBlockManager.SmallBlockManagerLocked, 1, 0) = 0 then begin + MemoryBarrier; // SZ: needed here? {Check if there is a pending free list. If so the first pending free block is returned and the rest are freed.} if APSmallBlockManager.PendingFreeList <> nil then @@ -7290,8 +7437,8 @@ function FastMM_GetMem_GetSmallBlock(APSmallBlockManager: PSmallBlockManager): P LogSmallBlockThreadContentionAndYieldToOtherThread; end; -{$endif} end; +{$endif} function FastMM_ReallocMem_ReallocSmallBlock(APointer: Pointer; ANewUserSize: NativeInt): Pointer; {$ifdef X86ASM} @@ -8162,6 +8309,8 @@ function FastMM_ProcessAllPendingFrees: Boolean; begin if AtomicCmpExchange(LPSmallBlockManager.SmallBlockManagerLocked, 1, 0) = 0 then begin + MemoryBarrier; // SZ: needed here? + {Process the pending frees list.} LPPendingFreeBlock := AtomicExchange(LPSmallBlockManager.PendingFreeList, nil); if LPPendingFreeBlock <> nil then @@ -8189,6 +8338,8 @@ function FastMM_ProcessAllPendingFrees: Boolean; begin if AtomicCmpExchange(LPMediumBlockManager.MediumBlockManagerLocked, 1, 0) = 0 then begin + MemoryBarrier; // SZ: needed here? + {Process the pending frees list.} LPPendingFreeBlock := AtomicExchange(LPMediumBlockManager.PendingFreeList, nil); if LPPendingFreeBlock <> nil then @@ -8215,6 +8366,8 @@ function FastMM_ProcessAllPendingFrees: Boolean; begin if AtomicCmpExchange(LPLargeBlockManager.LargeBlockManagerLocked, 1, 0) = 0 then begin + MemoryBarrier; // SZ: needed here? + if ProcessLargeBlockPendingFrees_ArenaAlreadyLocked(LPLargeBlockManager) <> 0 then System.Error(reInvalidPtr); end @@ -8340,6 +8493,8 @@ function FastMM_WalkBlocks(ACallBack: TFastMM_WalkBlocksCallback; AWalkBlockType Continue; end; + MemoryBarrier; // SZ: needed here? + LPLargeBlockHeader := LPLargeBlockManager.FirstLargeBlockHeader; while NativeUInt(LPLargeBlockHeader) <> NativeUInt(LPLargeBlockManager) do begin @@ -8381,6 +8536,8 @@ function FastMM_WalkBlocks(ACallBack: TFastMM_WalkBlocksCallback; AWalkBlockType Continue; end; + MemoryBarrier; // SZ: needed here? + LPMediumBlockSpan := LPMediumBlockManager.FirstMediumBlockSpanHeader; while NativeUInt(LPMediumBlockSpan) <> NativeUInt(LPMediumBlockManager) do begin @@ -8402,6 +8559,8 @@ function FastMM_WalkBlocks(ACallBack: TFastMM_WalkBlocksCallback; AWalkBlockType OS_AllowOtherThreadToRun; end; + MemoryBarrier; // SZ: needed here? + {Has the other thread completed the allocation, or is this perhaps a memory pool corruption?} if GetMediumBlockSize(LPMediumBlock) = 0 then begin @@ -8476,6 +8635,7 @@ function FastMM_WalkBlocks(ACallBack: TFastMM_WalkBlocksCallback; AWalkBlockType begin {Memory fence required for ARM} + MemoryBarrier; {The last block may have been released before the manager was locked, so we need to check whether it is still a small block span.} @@ -8719,6 +8879,8 @@ function FastMM_GetUsageSummary(ALockTimeoutMilliseconds: Cardinal): TFastMM_Usa Result.EfficiencyPercentage := 100; end; + +//@@@ check macos begin procedure FastMM_GetMemoryManagerState_CallBack(const ABlockInfo: TFastMM_WalkAllocatedBlocks_BlockInfo); var LPMemoryManagerState: ^TFastMM_MemoryManagerState; @@ -8878,6 +9040,8 @@ procedure FastMM_GetMemoryMap(var AMemoryMap: TMemoryMap; ALockTimeoutMillisecon end; +// @@@ check macos end + {Returns True if there are live pointers using this memory manager.} function FastMM_HasLivePointers: Boolean; var @@ -9301,12 +9465,14 @@ procedure FastMM_BuildFileMappingObjectName; var i, LProcessID: Cardinal; begin + {$ifdef MSWINDOWS} LProcessID := GetCurrentProcessId; for i := 0 to 7 do begin SharingFileMappingObjectName[(High(SharingFileMappingObjectName) - 1) - i] := AnsiChar(CHexDigits[((LProcessID shr (i * 4)) and $F)]); end; + {$endif} end; {Searches the current process for a shared memory manager} @@ -9315,6 +9481,7 @@ function FastMM_FindSharedMemoryManager: PMemoryManagerEx; LPMapAddress: Pointer; LLocalMappingObjectHandle: NativeUInt; begin + {$ifdef mswindows} {Try to open the shared memory manager file mapping} LLocalMappingObjectHandle := OpenFileMappingA(FILE_MAP_READ, False, SharingFileMappingObjectName); {Is a memory manager in this process sharing its memory manager?} @@ -9331,6 +9498,7 @@ function FastMM_FindSharedMemoryManager: PMemoryManagerEx; UnmapViewOfFile(LPMapAddress); CloseHandle(LLocalMappingObjectHandle); end; + {$endif} end; {Searches the current process for a shared memory manager. If no memory has been allocated using this memory manager @@ -9342,6 +9510,7 @@ function FastMM_AttemptToUseSharedMemoryManager: Boolean; LTokenValueBuffer: array[0..CTokenBufferMaxWideChars - 1] of WideChar; LPMemoryManagerEx: PMemoryManagerEx; begin + {$ifdef mswindows} if CurrentInstallationState = mmisInstalled then begin {Is this MM being shared? If so, switching to another MM is not allowed} @@ -9391,6 +9560,9 @@ function FastMM_AttemptToUseSharedMemoryManager: Boolean; {Another memory manager has already been installed.} Result := False; end; + {$else} + Result := False; + {$endif} end; {Starts sharing this memory manager with other modules in the current process. Only one memory manager may be shared @@ -9399,6 +9571,7 @@ function FastMM_ShareMemoryManager: Boolean; var LPMapAddress: Pointer; begin + {$ifdef MSWINDOWS} if (CurrentInstallationState = mmisInstalled) and (not FastMM_InstalledMemoryManagerChangedExternally) and (SharingFileMappingObjectHandle = 0) then @@ -9429,6 +9602,9 @@ function FastMM_ShareMemoryManager: Boolean; {Either another memory manager has been set or this memory manager is already being shared} Result := False; end; + {$else} + Result := False; + {$endif} end; @@ -10361,11 +10537,13 @@ procedure FastMM_FinalizeMemoryManager; FastMM_FreeDebugSupportLibrary; + {$ifdef MSWINDOWS} if SharingFileMappingObjectHandle <> 0 then begin CloseHandle(SharingFileMappingObjectHandle); SharingFileMappingObjectHandle := 0; end; + {$endif} end; @@ -10383,6 +10561,8 @@ function FastMM_SetNormalOrDebugMemoryManager: Boolean; while AtomicCmpExchange(SettingMemoryManager, 1, 0) <> 0 do OS_AllowOtherThreadToRun; + MemoryBarrier; // SZ: needed here? + {Check that the memory manager has not been changed since the last time it was set.} if FastMM_InstalledMemoryManagerChangedExternally then begin @@ -10437,6 +10617,7 @@ procedure FastMM_InstallMemoryManager; Exit; end; + {$ifdef MSWINDOWS} if System.GetHeapStatus.TotalAllocated <> 0 then begin LTokenValues := Default(TEventLogTokenValues); @@ -10445,6 +10626,7 @@ procedure FastMM_InstallMemoryManager; Exit; end; + {$endif} if FastMM_SetNormalOrDebugMemoryManager then begin diff --git a/FastMM5_OSXUtil.pas b/FastMM5_OSXUtil.pas new file mode 100644 index 0000000..8b20fb9 --- /dev/null +++ b/FastMM5_OSXUtil.pas @@ -0,0 +1,728 @@ +unit FastMM5_OSXUtil; + +interface + +type + LPCSTR = PAnsiChar; + LPSTR = PAnsiChar; + DWORD = Cardinal; + BOOL = Boolean; + + PSystemTime = ^TSystemTime; + _SYSTEMTIME = record + wYear: Word; + wMonth: Word; + wDayOfWeek: Word; + wDay: Word; + wHour: Word; + wMinute: Word; + wSecond: Word; + wMilliseconds: Word; + end; + TSystemTime = _SYSTEMTIME; + SYSTEMTIME = _SYSTEMTIME; + SIZE_T = NativeUInt; + PUINT_PTR = ^UIntPtr; + +const + PAGE_NOACCESS = 1; + PAGE_READONLY = 2; + PAGE_READWRITE = 4; + PAGE_WRITECOPY = 8; + PAGE_EXECUTE = $10; + PAGE_EXECUTE_READ = $20; + PAGE_EXECUTE_READWRITE = $40; + PAGE_GUARD = $100; + PAGE_NOCACHE = $200; + MEM_COMMIT = $1000; + MEM_RESERVE = $2000; + MEM_DECOMMIT = $4000; + MEM_RELEASE = $8000; + MEM_FREE = $10000; + MEM_PRIVATE = $20000; + MEM_MAPPED = $40000; + MEM_RESET = $80000; + MEM_TOP_DOWN = $100000; + + EXCEPTION_ACCESS_VIOLATION = DWORD($C0000005); + + +//function GetModuleHandleA(lpModuleName: LPCSTR): HMODULE; stdcall; +function GetEnvironmentVariableW(lpName: PWideChar; lpBuffer: PWideChar; nSize: DWORD): DWORD; stdcall; overload; +function DeleteFileA(lpFileName: LPCSTR): BOOL; stdcall; +function VirtualAlloc(lpvAddress: Pointer; dwSize: SIZE_T; flAllocationType, flProtect: DWORD): Pointer; stdcall; +function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: Cardinal): LongBool; stdcall; + +//procedure RaiseException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; +// lpArguments: PUINT_PTR); stdcall; + +type + PSecurityAttributes = ^TSecurityAttributes; + _SECURITY_ATTRIBUTES = record + nLength: DWORD; + lpSecurityDescriptor: Pointer; + bInheritHandle: BOOL; + end; + TSecurityAttributes = _SECURITY_ATTRIBUTES; + SECURITY_ATTRIBUTES = _SECURITY_ATTRIBUTES; + +const + GENERIC_READ = DWORD($80000000); + GENERIC_WRITE = $40000000; + OPEN_ALWAYS = 4; + FILE_ATTRIBUTE_NORMAL = $00000080; + FILE_BEGIN = 0; + FILE_CURRENT = 1; + FILE_END = 2; + INVALID_SET_FILE_POINTER = DWORD(-1); + FILE_SHARE_READ = $00000001; + + +procedure GetLocalTime(var lpSystemTime: TSystemTime); stdcall; +function GetTimeMilliSeconds: Int64; +function MilliSecondsToSystemTime(MSecs: Int64): TSystemTime; + +function CreateFileUTF8(lpFileName: PAnsiChar; dwDesiredAccess, dwShareMode: DWORD; + lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; + hTemplateFile: THandle): THandle; stdcall; + +function SetFilePointer(hFile: THandle; lDistanceToMove: Longint; + lpDistanceToMoveHigh: PLongInt; dwMoveMethod: DWORD): DWORD; stdcall; + +function CloseHandle(hObject: THandle): BOOL; stdcall; +function WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite: Cardinal; + var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Boolean; stdcall; + +function StrLCopy(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar; overload; + + +const + libc = '/usr/lib/libc.dylib'; + libdl = '/usr/lib/libdl.dylib'; + libpthread = '/usr/lib/libpthread.dylib'; + CarbonCoreLib = '/System/Library/Frameworks/CoreServices.framework/CoreServices'; + INVALID_HANDLE_VALUE = Cardinal(-1); + + _PU = ''; +{$IF Defined(OSX) and (Defined(CPUX86) or Defined(CPUX64))} + _INODE_SUFFIX = '$INODE64'; +{$ELSE} + _INODE_SUFFIX = ''; +{$ENDIF} + +const + PROT_READ = 1; + {$EXTERNALSYM PROT_READ} + PROT_WRITE = 2; + {$EXTERNALSYM PROT_WRITE} + PROT_EXEC = 4; + {$EXTERNALSYM PROT_EXEC} + PROT_NONE = 0; + {$EXTERNALSYM PROT_NONE} + + MAP_FIXED = $10; + {$EXTERNALSYM MAP_FIXED} + MAP_PRIVATE = 2; + {$EXTERNALSYM MAP_PRIVATE} + MAP_SHARED = 1; + {$EXTERNALSYM MAP_SHARED} + + MAP_FILE = $0 platform; + {$EXTERNALSYM MAP_FILE} + MAP_ANON = $1000 platform; + {$EXTERNALSYM MAP_ANON} + + MAP_FAILED = Pointer(-1); + {$EXTERNALSYM MAP_FAILED} + + RTLD_LAZY = 1; { Lazy function call binding. } + +type + off_t = Int64; + pthread_t = Pointer; + UnsignedWide = UInt64; + AbsoluteTime = UnsignedWide; + Nanoseconds = UnsignedWide; + + dev_t = Int32; + mode_t = UInt16; + nlink_t = UInt16; + uid_t = UInt32; + gid_t = UInt32; + time_t = LongInt; + blkcnt_t = Int64; + blksize_t = Int32; + + _stat = record + st_dev: dev_t; // device + st_mode: mode_t; // protection + st_nlink: nlink_t; // number of hard links + st_ino: UInt64; // inode ino64_t + st_uid: uid_t; // user ID of owner + st_gid: gid_t; // group ID of owner + st_rdev: dev_t; // device type (if inode device) + st_atime: time_t; // time of last access + st_atimensec: LongInt; + st_mtime: time_t; // time of last modification + st_mtimensec: LongInt; + st_ctime: time_t; // time of last change + st_ctimensec: LongInt; + st_birthtime: time_t; // file creation time + st_birthtimensec: LongInt; + + st_size: off_t; // total size, in bytes + st_blocks: blkcnt_t; // number of blocks allocated + st_blksize: blksize_t; // blocksize for filesystem I/O + + st_flags: UInt32; // user defined flags for file + st_gen: UInt32; // file generation number + + __unused1: Int32; + __unused2: Int64; + __unused3: Int64; + end; + {$EXTERNALSYM _stat} + P_stat = ^_stat; + + +(*function malloc(size: size_t): Pointer; cdecl; + external libc name _PU + 'malloc'; +{$EXTERNALSYM malloc} *) + +function aligned_alloc(alignment, size: size_t): Pointer; cdecl; + external libc name _PU + 'aligned_alloc'; +{$EXTERNALSYM aligned_alloc} + +(*function __malloc(size: size_t): Pointer; cdecl; + external libc name _PU + 'malloc'; +{$EXTERNALSYM __malloc} + +function calloc(nelem: size_t; eltsize: size_t): Pointer; cdecl; + external libc name _PU + 'calloc'; + +function realloc(P: Pointer; NewSize: size_t): Pointer; cdecl; + external libc name _PU + 'realloc'; *) + +procedure free(p: Pointer); cdecl; + external libc name _PU + 'free'; + +function mprotect(Addr: Pointer; Len: size_t; Prot: Integer): Integer; cdecl; + external libc name _PU + 'mprotect'; + +function mmap(Addr: Pointer; Len: size_t; Prot: Integer; Flags: Integer; + FileDes: Integer; Off: off_t): Pointer; cdecl; + external libc name _PU + 'mmap'; + +function sched_yield: Integer; cdecl; external libc name _PU + 'sched_yield'; + +function pthread_self: pthread_t; cdecl; external libpthread name _PU + 'pthread_self'; + +function UpTime: AbsoluteTime; cdecl; external CarbonCoreLib name _PU + 'UpTime'; + +function AbsoluteToNanoseconds(absoluteTime: AbsoluteTime): Nanoseconds; cdecl; external CarbonCoreLib name _PU + 'AbsoluteToNanoseconds'; + +function getenv(Name: MarshaledAString): MarshaledAString; cdecl; + external libc name _PU + 'getenv'; + +function remove(Path: MarshaledAString): Integer; cdecl; + external libc name _PU + 'remove'; + +function lstat(FileName: MarshaledAString; var StatBuffer: _stat): Integer; cdecl; + external libc name _PU + 'lstat' + _INODE_SUFFIX; + +function dlsym(Handle: NativeUInt; Symbol: MarshaledAString): Pointer; cdecl; + external libdl name _PU + 'dlsym'; + +function dlerror: MarshaledAString; cdecl; + external libdl name _PU + 'dlerror'; + +function LoadLibrary(ModuleName: PChar): HMODULE; +function FreeLibrary(Module: HMODULE): LongBool; +function GetProcAddress(Module: HMODULE; Proc: PAnsiChar): Pointer; + +implementation + +uses + Posix.Stdlib, Posix.Unistd, Posix.SysMman, Posix.Fcntl, Posix.SysStat, Posix.SysTime, Posix.Time, Posix.Errno, Posix.Signal, + Macapi.Mach; + +function CreateFileUTF8(lpFileName: PAnsiChar; dwDesiredAccess, dwShareMode: DWORD; + lpSecurityAttributes: PSecurityAttributes; dwCreationDisposition, dwFlagsAndAttributes: DWORD; + hTemplateFile: THandle): THandle; stdcall; +var + Flags: Integer; + FileAccessRights: Integer; +begin +// O_RDONLY open for reading only +// O_WRONLY open for writing only +// O_RDWR open for reading and writing +// O_NONBLOCK do not block on open or for data to become available +// O_APPEND append on each write +// O_CREAT create file if it does not exist +// O_TRUNC truncate size to 0 +// O_EXCL error if O_CREAT and the file exists +// O_SHLOCK atomically obtain a shared lock +// O_EXLOCK atomically obtain an exclusive lock +// O_NOFOLLOW do not follow symlinks +// O_SYMLINK allow open of symlinks +// O_EVTONLY descriptor requested for event notifications only +// O_CLOEXEC mark as close-on-exec + + Flags := 0; + FileAccessRights := S_IRUSR or S_IWUSR or S_IRGRP or S_IWGRP or S_IROTH or S_IWOTH; + + case dwDesiredAccess and (GENERIC_READ or GENERIC_WRITE) of //= (GENERIC_READ or GENERIC_WRITE) then + GENERIC_READ or GENERIC_WRITE: Flags := Flags or O_RDWR; + GENERIC_READ: Flags := Flags or O_RDONLY; + GENERIC_WRITE: Flags := Flags or O_WRONLY; + else + Exit(THandle(-1)); + end; + + case dwCreationDisposition of +// CREATE_NEW: +// CREATE_ALWAYS: +// OPEN_EXISTING: + OPEN_ALWAYS: Flags := Flags or O_CREAT; +// TRUNCATE_EXISTING: + end; + + Result := THandle(__open(lpFileName, Flags, FileAccessRights)); + + // ShareMode + +// smode := Mode and $F0 shr 4; +// if ShareMode[smode] <> 0 then +// begin +// LockVar.l_whence := SEEK_SET; +// LockVar.l_start := 0; +// LockVar.l_len := 0; +// LockVar.l_type := ShareMode[smode]; +// Tvar := fcntl(FileHandle, F_SETLK, LockVar); +// Code := errno; +// if (Tvar = -1) and (Code <> EINVAL) and (Code <> ENOTSUP) then +// EINVAL/ENOTSUP - file doesn't support locking +// begin +// __close(FileHandle); +// Exit; +// end; +end; + +type + _LARGE_INTEGER = record + case Integer of + 0: ( + LowPart: DWORD; + HighPart: Longint); + 1: ( + QuadPart: Int64); + end; + +function WriteFile(hFile: THandle; const Buffer; nNumberOfBytesToWrite: Cardinal; + var lpNumberOfBytesWritten: Cardinal; lpOverlapped: Pointer): Boolean; stdcall; +begin + lpNumberOfBytesWritten := __write(hFile, @Buffer, nNumberOfBytesToWrite); + if lpNumberOfBytesWritten = Cardinal(-1) then + begin + lpNumberOfBytesWritten := 0; + Result := False; + end + else + Result := True; +end; + +function SetFilePointer(hFile: THandle; lDistanceToMove: Longint; + lpDistanceToMoveHigh: PLongInt; dwMoveMethod: DWORD): DWORD; stdcall; +var + dist: _LARGE_INTEGER; +begin + dist.LowPart := lDistanceToMove; + if Assigned(lpDistanceToMoveHigh) then + dist.HighPart := lpDistanceToMoveHigh^ + else + dist.HighPart := 0; + + dist.QuadPart := lseek(hFile, dist.QuadPart, dwMoveMethod); // dwMoveMethod = same as in windows + if dist.QuadPart = -1 then + Result := DWORD(-1) + else + begin + Result := dist.LowPart; + if Assigned(lpDistanceToMoveHigh) then + lpDistanceToMoveHigh^ := dist.HighPart; + end; +end; + +procedure GetLocalTime(var lpSystemTime: TSystemTime); stdcall; +var + T: time_t; + TV: timeval; + UT: tm; +begin + gettimeofday(TV, nil); + T := TV.tv_sec; + localtime_r(T, UT); + + lpSystemTime.wYear := UT.tm_year + 1900; + lpSystemTime.wMonth := UT.tm_mon + 1; + lpSystemTime.wDayOfWeek := UT.tm_wday; + lpSystemTime.wDay := UT.tm_mday; + lpSystemTime.wHour := UT.tm_hour; + lpSystemTime.wMinute := UT.tm_min; + lpSystemTime.wSecond := UT.tm_sec; + lpSystemTime.wMilliseconds := TV.tv_usec div 1000; +end; + +function GetTimeMilliSeconds: Int64; +var + TV: timeval; +begin + gettimeofday(TV, nil); + Result := Int64(tv.tv_sec) * 1000000 + int64(tv.tv_usec); +end; + +function MilliSecondsToSystemTime(MSecs: Int64): TSystemTime; +var + T: time_t; + TV: timeval; + UT: tm; +begin + TV.tv_usec := MSecs mod 1000000; + TV.tv_sec := MSecs div 1000000; + T := TV.tv_sec; + localtime_r(T, UT); + + Result.wYear := UT.tm_year + 1900; + Result.wMonth := UT.tm_mon + 1; + Result.wDayOfWeek := UT.tm_wday; + Result.wDay := UT.tm_mday; + Result.wHour := UT.tm_hour; + Result.wMinute := UT.tm_min; + Result.wSecond := UT.tm_sec; + Result.wMilliseconds := TV.tv_usec div 1000; +end; + +{function GetLocalTimeNanoSeconds: Int64; +var + t: mach_timespec_t; +const + CLOCK_REALTIME = 0; +begin + clock_get_time(CLOCK_REALTIME, t); + Result := Int64(t.tv_sec) * 1000000000 + int64(t.tv_nsec); +end; } + +function CloseHandle(hObject: THandle): BOOL; stdcall; +begin + Result := __close(hObject) = 0; +end; + +function StrLen(const Str: PWideChar): Cardinal; overload; +begin + Result := Length(Str); +end; + +function StrLen(const Str: PAnsiChar): Cardinal; overload; +begin + Result := Length(Str); +end; + +function StrLCopy(Dest: PWideChar; const Source: PWideChar; MaxLen: Cardinal): PWideChar; overload; +var + Len: Cardinal; +begin + Result := Dest; + Len := StrLen(Source); + if Len > MaxLen then + Len := MaxLen; + Move(Source^, Dest^, Len * SizeOf(WideChar)); + Dest[Len] := #0; +end; + +function StrLCopy(Dest: MarshaledAString; const Source: MarshaledAString; MaxLen: Cardinal): MarshaledAString; overload; +var + Len: Cardinal; +begin + Result := Dest; + Len := StrLen(Source); + if Len > MaxLen then + Len := MaxLen; + Move(Source^, Dest^, Len * SizeOf(Byte)); + Dest[Len] := #0; +end; + + +function StrPLCopy(Dest: PWideChar; const Source: UnicodeString; MaxLen: Cardinal): PWideChar; +begin + Result := StrLCopy(Dest, PWideChar(Source), MaxLen); +end; + +function GetModuleHandle(lpModuleName: PWideChar): HMODULE; +begin + Result := 0; + if lpModuleName = 'kernel32' then + Result := 1; +end; + +function GetModuleHandleA(lpModuleName: LPCSTR): HMODULE; stdcall; +begin + Result := GetModuleHandle(PChar(string(lpModuleName))); +end; + +function GetEnvironmentVariableW(lpName: PWideChar; lpBuffer: PWideChar; nSize: DWORD): DWORD; stdcall; overload; +var + Len: Integer; + Env: string; +begin + env := string(getenv(PAnsiChar(UTF8Encode(lpName)))); + + Len := Length(env) + 1; + Result := Len; + if nSize < Result then + Result := nSize; + + StrPLCopy(lpBuffer, env, Result); + if Len > nSize then + SetLastError(122) //ERROR_INSUFFICIENT_BUFFER) + else + begin + SetLastError(0); + Dec(Result); // should not include terminating #0 + end; +end; + +function DeleteFileA(lpFileName: LPCSTR): BOOL; stdcall; +begin + Result := unlink(lpFileName) <> -1; +end; + +// ReservedBlock := VirtualAlloc(Pointer(DebugReservedAddress), 65536, MEM_RESERVE, PAGE_NOACCESS); + +var + PageSize: LongInt = 0; + +function VirtualAlloc(lpvAddress: Pointer; dwSize: SIZE_T; flAllocationType, flProtect: DWORD): Pointer; stdcall; +var +// PageSize: LongInt; + AllocSize: LongInt; + Flags: Integer; + Prot: Integer; +begin + if flAllocationType and (MEM_RESERVE or MEM_COMMIT) = 0 then + Exit(0); + + Flags := MAP_PRIVATE or MAP_ANON; + Prot := PROT_NONE; + + if flProtect and PAGE_READONLY <> 0 then + Prot := Prot or PROT_READ; + if flProtect and PAGE_READWRITE <> 0 then + Prot := Prot or PROT_READ or PROT_WRITE; + if flProtect and PAGE_EXECUTE <> 0 then + Prot := Prot or PROT_EXEC; + if flProtect and PAGE_EXECUTE_READ <> 0 then + Prot := Prot or PROT_EXEC or PROT_READ; + if flProtect and PAGE_EXECUTE_READWRITE <> 0 then + Prot := Prot or PROT_EXEC or PROT_READ or PROT_WRITE; + + if PageSize = 0 then + PageSize := sysconf(_SC_PAGESIZE); + + if lpvAddress <> nil then + Flags := Flags or MAP_FIXED; + + AllocSize := dwSize - (dwSize mod PageSize) + PageSize; + + Result := mmap(lpvAddress, AllocSize, Prot, Flags, -1, 0); + + FillChar(Result^, dwSize, 0); +end; + +function VirtualFree(lpAddress: Pointer; dwSize, dwFreeType: Cardinal): LongBool; stdcall; +var + Err: Integer; +begin + {if dwFreetype = MEM_RELEASE then + begin + if lpAddress = Pointer($80800000) then + munmap(lpAddress, dwSize) + else + free(lpAddress); + end; } + if dwFreeType = MEM_RELEASE then + begin + Err := munmap(lpAddress, dwSize); + Result := Err = 0; + + if Err <> 0 then // for debugging + System.Error(reInvalidOp); + end + else // if dwFreeType = MEM_DECOMMIT then + begin + Result := False; + System.Error(reInvalidOp); // not supported + end; +end; + +//procedure RaiseException(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD; +// lpArguments: PUINT_PTR); stdcall; +//begin +// WriteLN('ACCESS VIOLATION (set breakpoint in FastMM_OSXUtil: RaiseException for easier debugging)'); +// kill(getppid, SIGSEGV); +//// asm int 3; end; +//end; + +function dlopen(Filename: MarshaledAString; Flag: Integer): NativeUInt; cdecl; + external libdl name _PU + 'dlopen'; + +function dlclose(Handle: NativeUInt): Integer; cdecl; + external libdl name _PU + 'dlclose'; + + +function LoadLibrary(ModuleName: PChar): HMODULE; +begin + Result := HMODULE(dlopen(PAnsiChar(UTF8Encode(ModuleName)), RTLD_LAZY)); +end; + +function FreeLibrary(Module: HMODULE): LongBool; +begin + Result := False; + if Module <> 0 then + Result := LongBool(dlclose(Module)); +end; + +function GetProcAddress(Module: HMODULE; Proc: PAnsiChar): Pointer; +var + Error: MarshaledAString; +begin + // dlsym doesn't clear the error state when the function succeeds + dlerror; + Result := dlsym(Module, Proc); + Error := dlerror; + if Error <> nil then + Result := nil +end; + +// *************************** query memory access ************************ +// doesn't work; always returns KERN_INVALID_ARGUMENT +(*const + libSystem = '/usr/lib/libSystem.dylib'; + +type + mach_port_t = Pointer; // Delphi equivalent for mach_port_t + boolean_t = Integer; + + vm_prot_t = UInt32; + vm_inherit_t = UInt32; + natural_t = UInt32; + + vm_map_t = mach_port_t; + vm_map_read_t = mach_port_t; + vm_map_inspect_t = mach_port_t; + + vm_offset_t = Pointer; + vm_address_t = Pointer; + Pvm_address_t = ^vm_address_t; + vm_region_flavor_t = Integer; + vm_size_t = UIntPtr; + Pvm_size_t = ^vm_size_t; + mach_msg_type_number_t = natural_t; + Pmach_msg_type_number_t = ^mach_msg_type_number_t; + Pmach_port_t = ^mach_port_t; + kern_return_t = integer; // AI + +type + {$ALIGN 4} + vm_region_basic_info_data_64_t = record + protection: vm_prot_t; + max_protection: vm_prot_t; + inheritance: vm_inherit_t; + shared: boolean_t; + reserved: boolean_t; + offset: UInt32; + behavior: UInt32; + user_wired_count: natural_t; + end; + vm_region_info_t = ^vm_region_basic_info_data_64_t; + + +function vm_region_64( + target_task: vm_map_read_t; + address: Pvm_address_t; + size: Pvm_size_t; + flavor: vm_region_flavor_t; + info: vm_region_info_t; + infoCnt: Pmach_msg_type_number_t; + object_name: Pmach_port_t +): kern_return_t; cdecl; external libSystem; + + +//type +// vm_info_region_64_t = record +// vir_start: natural_t; // start of region */ +// vir_end: natural_t; // end of region */ +// vir_object: natural_t; // the mapped object */ +// vir_offset: memory_object_offset_t; // offset into object */ +// vir_needs_copy: boolean_t; // does object need to be copied? */ +// vir_protection: vm_prot_t; // protection code */ +// vir_max_protection: vm_prot_t; // maximum protection */ +// vir_inheritance: vm_inherit_t; // inheritance */ +// vir_wired_count: natural_t; // number of times wired */ +// vir_user_wired_count: natural_t; // number of times user has wired */ +// end; +// Pvm_info_region_64_t = ^vm_info_region_64_t; + +// +//function mach_vm_region_info_64( +// task: vm_map_read_t; +// address: vm_address_t; +// region: Pvm_info_region_64_t; +// objects: Pvm_info_object_array_t; +// objectsCnt: Pmach_msg_type_number_t +//): kern_return_t; cdecl; external libSystem; + +function mach_task_self: mach_port_t; cdecl + external libc name _PU + 'mach_task_self'; + +const + VM_REGION_BASIC_INFO_64 = 9; + VM_REGION_BASIC_INFO_COUNT_64 = SizeOf(vm_region_basic_info_data_64_t) div SizeOf(integer); + VM_REGION_BASIC_INFO = 10; + KERN_SUCCESS = 0; + VM_PROT_NONE = 0; + +procedure test; // from AI +begin + var size: vm_size_t := 4096; + var address: vm_address_t := Pointer($10000000); // Replace with your desired virtual address + + var info: vm_region_basic_info_data_64_t; + FillChar(info, SizeOf(info), 0); + var info_count: mach_msg_type_number_t := VM_REGION_BASIC_INFO_COUNT_64; + var object_name: mach_port_t := nil; + + var result: kern_return_t := vm_region_64( + mach_task_self, @address, @size, VM_REGION_BASIC_INFO_64, + @info, @info_count, @object_name); + + if (result = KERN_SUCCESS) then + begin + if (info.protection = VM_PROT_NONE) then + writeln('The virtual memory region is free.') + else + writeln('The virtual memory region is not free.'); + end + else if Result = KERN_INVALID_ARGUMENT then + WriteLn('Invalid argument.') + else + writeln('Error querying virtual memory region.'); +end; + + + +begin +test; +*) + + +end. diff --git a/FullDebugMode DLL/Precompiled/libFastMM_FullDebugMode64.dylib b/FullDebugMode DLL/Precompiled/libFastMM_FullDebugMode64.dylib new file mode 100644 index 0000000..e00a7c2 Binary files /dev/null and b/FullDebugMode DLL/Precompiled/libFastMM_FullDebugMode64.dylib differ diff --git a/FullDebugMode DLL/Precompiled/libFastMM_FullDebugModeARM64.dylib b/FullDebugMode DLL/Precompiled/libFastMM_FullDebugModeARM64.dylib new file mode 100644 index 0000000..5110fe9 Binary files /dev/null and b/FullDebugMode DLL/Precompiled/libFastMM_FullDebugModeARM64.dylib differ