From 16559906402f563acbc519afe4d15252902315be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Pejovi=C4=87?= Date: Fri, 23 Jul 2021 12:02:17 +0200 Subject: [PATCH 1/6] Add support for memory mapping to reserved address range on Windows --- .../oracle/svm/core/windows/WindowsUtils.java | 55 +++- .../windows/WindowsVirtualMemoryProvider.java | 241 ++++++++++++++++-- .../svm/core/windows/headers/MemoryAPI.java | 11 + 3 files changed, 289 insertions(+), 18 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java index dab7acd92e7f..bb8527c2a713 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java @@ -30,12 +30,13 @@ import java.io.IOException; import org.graalvm.nativeimage.PinnedObject; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.nativeimage.c.struct.CPointerTo; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.CLongPointer; +import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; @@ -45,13 +46,15 @@ import com.oracle.svm.core.annotate.RecomputeFieldValue.CustomFieldValueComputer; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.c.function.CEntryPointActions; import com.oracle.svm.core.windows.headers.FileAPI; +import com.oracle.svm.core.windows.headers.LibLoaderAPI; import com.oracle.svm.core.windows.headers.WinBase; +import com.oracle.svm.core.windows.headers.WinBase.HMODULE; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaField; -@Platforms(Platform.WINDOWS.class) public class WindowsUtils { @TargetClass(className = "java.lang.ProcessImpl") @@ -182,4 +185,50 @@ public static long getNanoCounter() { double freq = performanceFrequency; return (long) ((current / freq) * NANOSECS_PER_SEC); } + + @CPointerTo(nameOfCType = "void*") + interface CFunctionPointerPointer extends PointerBase { + T read(); + + void write(T value); + } + + /** Sentinel value denoting the uninitialized pointer. */ + static final PointerBase UNINITIALIZED_POINTER = WordFactory.pointer(0xBAD); + + /** + * Retrieves and caches the address of an exported function from an already loaded DLL if the + * cached function pointer is {@linkplain #UNINITIALIZED_POINTER uninitialized}, otherwise it + * returns the cached value. + */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + static T getAndCacheFunctionPointer(CFunctionPointerPointer cachedFunctionPointer, + CCharPointer dllName, CCharPointer functionName) { + T functionPointer = cachedFunctionPointer.read(); + if (functionPointer.equal(UNINITIALIZED_POINTER)) { + functionPointer = getFunctionPointer(dllName, functionName, false); + cachedFunctionPointer.write(functionPointer); + } + return functionPointer; + } + + /** Retrieves the address of an exported function from an already loaded DLL. */ + @SuppressWarnings("unchecked") + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + static T getFunctionPointer(CCharPointer dllName, CCharPointer functionName, boolean failOnError) { + PointerBase functionPointer = LibLoaderAPI.GetProcAddress(getDLLHandle(dllName), functionName); + if (functionPointer.isNull() && failOnError) { + CEntryPointActions.failFatally(WinBase.GetLastError(), functionName); + } + return (T) functionPointer; + } + + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static HMODULE getDLLHandle(CCharPointer dllName) { + HMODULE dllHandle = LibLoaderAPI.GetModuleHandleA(dllName); + if (dllHandle.isNull()) { + CEntryPointActions.failFatally(WinBase.GetLastError(), dllName); + } + return dllHandle; + } } diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVirtualMemoryProvider.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVirtualMemoryProvider.java index 2df1d32e19be..09f42ea8fe68 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVirtualMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsVirtualMemoryProvider.java @@ -24,9 +24,16 @@ */ package com.oracle.svm.core.windows; +import static org.graalvm.nativeimage.c.function.CFunction.Transition.NO_TRANSITION; + import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; +import org.graalvm.nativeimage.c.struct.RawField; +import org.graalvm.nativeimage.c.struct.RawStructure; import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.nativeimage.hosted.Feature; @@ -40,11 +47,15 @@ import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.c.function.CEntryPointActions; import com.oracle.svm.core.os.VirtualMemoryProvider; import com.oracle.svm.core.util.PointerUtils; import com.oracle.svm.core.util.UnsignedUtils; +import com.oracle.svm.core.windows.WindowsUtils.CFunctionPointerPointer; import com.oracle.svm.core.windows.headers.MemoryAPI; import com.oracle.svm.core.windows.headers.SysinfoAPI; +import com.oracle.svm.core.windows.headers.WinBase; +import com.oracle.svm.core.windows.headers.WinBase.HANDLE; @AutomaticFeature class WindowsVirtualMemoryProviderFeature implements Feature { @@ -132,29 +143,214 @@ private static int accessAsProt(int access) { } } + /** Sentinel value indicating that no special alignment is required. */ + private static final UnsignedWord UNALIGNED = WordFactory.zero(); + @Override @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) public Pointer reserve(UnsignedWord nbytes, UnsignedWord alignment) { - if (UnsignedUtils.isAMultiple(getAllocationGranularity(), alignment)) { - return reserve(nbytes); + UnsignedWord requiredAlignment = alignment; + if (UnsignedUtils.isAMultiple(getAllocationGranularity(), requiredAlignment)) { + requiredAlignment = UNALIGNED; + } + + /* + * For memory mapping to an already reserved address range to work, the address range must + * be reserved as a placeholder. So we first try to reserve a placeholder and ... + */ + Pointer reservedPlaceholder = reservePlaceholder(nbytes, requiredAlignment); + if (reservedPlaceholder.isNonNull()) { + /* + * ... replace it with a normal allocation as the rest of the system is unaware of + * placeholders. This effectively makes the use of placeholders transparent. + */ + replacePlaceholder(reservedPlaceholder, nbytes); + return reservedPlaceholder; } + + /* + * If that fails, it's most likely because the OS doesn't support placeholders, so we + * continue without support for memory mapping to a reserved address range. + */ + assert reservedPlaceholder.isNull(); + /* Reserve a container that is large enough for the requested size *and* the alignment. */ - Pointer reservedStart = reserve(nbytes.add(alignment)); - if (reservedStart.isNull()) { + Pointer reserved = reserve(nbytes.add(requiredAlignment)); + if (reserved.isNull()) { return WordFactory.nullPointer(); } - return PointerUtils.roundUp(reservedStart, alignment); + return requiredAlignment.equal(UNALIGNED) ? reserved : PointerUtils.roundUp(reserved, requiredAlignment); } - @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) - private static Pointer reserve(UnsignedWord nbytes) { - return MemoryAPI.VirtualAlloc(WordFactory.nullPointer(), nbytes, MemoryAPI.MEM_RESERVE(), MemoryAPI.PAGE_NOACCESS()); + /** Reserves a normal private allocation. */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static Pointer reserve(UnsignedWord size) { + return MemoryAPI.VirtualAlloc(WordFactory.nullPointer(), size, MemoryAPI.MEM_RESERVE(), MemoryAPI.PAGE_NOACCESS()); + } + + private static final int MEM_RESERVE_PLACEHOLDER = 0x00040000; + + /** Reserves a placeholder, which is a type of reserved memory region. */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static Pointer reservePlaceholder(UnsignedWord size, UnsignedWord alignment) { + int allocationType = MemoryAPI.MEM_RESERVE() | MEM_RESERVE_PLACEHOLDER; + return invokeVirtualAlloc2(WordFactory.nullPointer(), size, allocationType, MemoryAPI.PAGE_NOACCESS(), alignment); + } + + private static final int MEM_REPLACE_PLACEHOLDER = 0x00004000; + private static final CGlobalData REPLACE_PLACEHOLDER_ERROR_MESSAGE = CGlobalDataFactory.createCString("Failed to replace a placeholder."); + + /** Replaces a placeholder with a normal private allocation. */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static void replacePlaceholder(PointerBase placeholder, UnsignedWord size) { + int allocationType = MemoryAPI.MEM_RESERVE() | MEM_REPLACE_PLACEHOLDER; + if (invokeVirtualAlloc2(placeholder, size, allocationType, MemoryAPI.PAGE_NOACCESS(), WordFactory.zero()).isNull()) { + CEntryPointActions.failFatally(WinBase.GetLastError(), REPLACE_PLACEHOLDER_ERROR_MESSAGE.get()); + } + } + + private static final CGlobalData KERNELBASE_DLL = CGlobalDataFactory.createCString("kernelbase.dll"); + + private static final CGlobalData VIRTUAL_ALLOC_2 = CGlobalDataFactory.createCString("VirtualAlloc2"); + private static final CGlobalData> VIRTUAL_ALLOC_2_POINTER = CGlobalDataFactory.createWord(WindowsUtils.UNINITIALIZED_POINTER); + + /** + * Like VirtualAlloc, but with additional support for placeholders and alignment specification. + * + * If the OS does not provide VirtualAlloc2, the null pointer is returned. + */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static Pointer invokeVirtualAlloc2(PointerBase baseAddress, UnsignedWord size, int allocationType, int pageProtection, UnsignedWord alignment) { + VirtualAlloc2 virtualAlloc2 = WindowsUtils.getAndCacheFunctionPointer(VIRTUAL_ALLOC_2_POINTER.get(), KERNELBASE_DLL.get(), VIRTUAL_ALLOC_2.get()); + if (virtualAlloc2.isNull()) { + /* The OS does not provide VirtualAlloc2 (nor placeholders). */ + return WordFactory.nullPointer(); + } + MEM_EXTENDED_PARAMETER extendedParameter = StackValue.get(MEM_EXTENDED_PARAMETER.class); + specifyAlignment(extendedParameter, StackValue.get(MEM_ADDRESS_REQUIREMENTS.class), alignment); + return virtualAlloc2.invoke(WordFactory.nullPointer(), baseAddress, size, allocationType, pageProtection, extendedParameter, 1); + } + + private interface VirtualAlloc2 extends CFunctionPointer { + @InvokeCFunctionPointer(transition = NO_TRANSITION) + Pointer invoke(HANDLE process, PointerBase baseAddress, UnsignedWord size, int allocationType, int pageProtection, + PointerBase extendedParameters, int parameterCount); + } + + /** Specifies the alignment for the new memory allocation. */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static void specifyAlignment(MEM_EXTENDED_PARAMETER extendedParameter, MEM_ADDRESS_REQUIREMENTS addressRequirements, UnsignedWord alignment) { + /* + * Alignment is specified using two structures: MEM_EXTENDED_PARAMETER and + * MEM_ADDRESS_REQUIREMENTS. Normally, we would import them using @CStruct, but not all + * versions of MSVC have them, so we define them below using @RawStructure instead. + * + * Note that it is generally not safe to pass @RawStructure arguments to native code due to + * a lack of control over their memory layout. However, in this particular case this is not + * a problem, as both structures contain only fields of the same size. Therefore, to achieve + * the memory layout that the native code expects, it is sufficient to ensure the correct + * order of the fields, which is easily done by prefixing the field names. + */ + extendedParameter.f1Type(MemExtendedParameterAddressRequirements); + extendedParameter.f2Pointer(addressRequirements.rawValue()); + addressRequirements.f1LowestStartingAddress(WordFactory.nullPointer()); + addressRequirements.f2HighestEndingAddress(WordFactory.nullPointer()); + addressRequirements.f3Alignment(alignment); + } + + /** Represents an extended parameter for a function that manages virtual memory. */ + @RawStructure + private interface MEM_EXTENDED_PARAMETER extends PointerBase { + /* This structure must exactly match the memory layout expected by the native code. */ + @RawField + void f1Type(long value); + + @RawField + void f2Pointer(long value); + } + + /** MEM_EXTENDED_PARAMETER_TYPE enumeration Constants. */ + private static final int MemExtendedParameterAddressRequirements = 1; + + /** + * Specifies a lowest and highest base address and alignment as part of an extended parameter to + * a function that manages virtual memory. + */ + @RawStructure + private interface MEM_ADDRESS_REQUIREMENTS extends PointerBase { + /* This structure must exactly match the memory layout expected by the native code. */ + @RawField + void f1LowestStartingAddress(PointerBase value); + + @RawField + void f2HighestEndingAddress(PointerBase value); + + @RawField + void f3Alignment(UnsignedWord value); } @Override @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) public Pointer mapFile(PointerBase start, UnsignedWord nbytes, WordBase fileHandle, UnsignedWord offset, int access) { - return WordFactory.nullPointer(); + /* + * On Windows, we map views of file mappings into the address range, so we assume that the + * `fileHandle` is actually a handle returned by the `MemoryAPI.CreateFileMappingW`. + */ + if (start.isNull()) { + /* + * Memory mapping to an unreserved address range imposes an additional restriction on + * the alignment of the `offset`, which we do not currently support. + */ + return WordFactory.nullPointer(); + } + + /* First split off a placeholder from the reserved address range ... */ + if (!splitPlaceholder(start, nbytes)) { + /* The OS does not support placeholders. */ + return WordFactory.nullPointer(); + } + + /* ... and then map a view into the placeholder. */ + int pageProtection = (access & Access.WRITE) != 0 ? MemoryAPI.PAGE_WRITECOPY() : MemoryAPI.PAGE_READONLY(); + Pointer fileView = invokeMapViewOfFile3((HANDLE) fileHandle, start, offset.rawValue(), nbytes, pageProtection); + if (fileView.isNull()) { + /* Restore a normal allocation as the caller is unaware of placeholders. */ + replacePlaceholder(start, nbytes); + } + return fileView; + } + + private static final int MEM_PRESERVE_PLACEHOLDER = 0x00000002; + + /** Splits off a placeholder from the existing one. */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static boolean splitPlaceholder(PointerBase start, UnsignedWord size) { + return MemoryAPI.VirtualFree(start, size, MemoryAPI.MEM_RELEASE() | MEM_PRESERVE_PLACEHOLDER) != 0; + } + + private static final CGlobalData MAP_VIEW_OF_FILE_3 = CGlobalDataFactory.createCString("MapViewOfFile3"); + private static final CGlobalData> MAP_VIEW_OF_FILE_3_POINTER = CGlobalDataFactory.createWord(WindowsUtils.UNINITIALIZED_POINTER); + + /** + * Like MapViewOfFile, but with additional support for placeholders and alignment specification. + * + * If the OS does not provide MapViewOfFile3, the null pointer is returned. + */ + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static Pointer invokeMapViewOfFile3(HANDLE fileMapping, PointerBase baseAddress, long offset, UnsignedWord viewSize, int pageProtection) { + MapViewOfFile3 mapViewOfFile3 = WindowsUtils.getAndCacheFunctionPointer(MAP_VIEW_OF_FILE_3_POINTER.get(), KERNELBASE_DLL.get(), MAP_VIEW_OF_FILE_3.get()); + if (mapViewOfFile3.isNull()) { + /* The OS does not provide MapViewOfFile3 (nor placeholders). */ + return WordFactory.nullPointer(); + } + return mapViewOfFile3.invoke(fileMapping, WordFactory.nullPointer(), baseAddress, offset, viewSize, MEM_REPLACE_PLACEHOLDER, + pageProtection, WordFactory.nullPointer(), 0); + } + + private interface MapViewOfFile3 extends CFunctionPointer { + @InvokeCFunctionPointer(transition = NO_TRANSITION) + Pointer invoke(HANDLE fileMapping, HANDLE process, PointerBase baseAddress, long offset, UnsignedWord viewSize, + int allocationType, int pageProtection, PointerBase extendedParameters, int parameterCount); } @Override @@ -181,13 +377,28 @@ public int uncommit(PointerBase start, UnsignedWord nbytes) { @Override @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) public int free(PointerBase start, UnsignedWord nbytes) { - /* Retrieve the start of the enclosing container that was originally reserved. */ + /* + * The reserved address range may have been split into multiple placeholders (some of which + * are memory mapped), so we start from the end of the range and work our way backwards. + */ MemoryAPI.MEMORY_BASIC_INFORMATION memoryInfo = StackValue.get(MemoryAPI.MEMORY_BASIC_INFORMATION.class); - if (MemoryAPI.VirtualQuery(start, memoryInfo, SizeOf.unsigned(MemoryAPI.MEMORY_BASIC_INFORMATION.class)).equal(0)) { - return -1; + Pointer end = ((Pointer) start).add(nbytes).subtract(1); + while (end.aboveThan((Pointer) start)) { + MemoryAPI.VirtualQuery(end, memoryInfo, SizeOf.unsigned(MemoryAPI.MEMORY_BASIC_INFORMATION.class)); + if (!free(memoryInfo.AllocationBase(), memoryInfo.Type() == MemoryAPI.MEM_MAPPED())) { + return -1; + } + end = ((Pointer) memoryInfo.AllocationBase()).subtract(1); + } + return 0; + } + + @Uninterruptible(reason = "May be called from uninterruptible code.", mayBeInlined = true) + private static boolean free(PointerBase allocationBase, boolean isMemoryMapped) { + if (isMemoryMapped) { + return MemoryAPI.UnmapViewOfFile(allocationBase) != 0; + } else { + return MemoryAPI.VirtualFree(allocationBase, WordFactory.zero(), MemoryAPI.MEM_RELEASE()) != 0; } - assert start.equal(memoryInfo.BaseAddress()) : "Invalid memory block start"; - int result = MemoryAPI.VirtualFree(memoryInfo.AllocationBase(), WordFactory.zero(), MemoryAPI.MEM_RELEASE()); - return (result != 0) ? 0 : -1; } } diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java index fbbca4dc0650..dd4372142533 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java @@ -63,6 +63,13 @@ public class MemoryAPI { @CConstant public static native int PAGE_READWRITE(); + @CConstant + public static native int PAGE_WRITECOPY(); + + /** Unmaps a mapped view of a file from the calling process's address space. */ + @CFunction(transition = NO_TRANSITION) + public static native int UnmapViewOfFile(PointerBase lpBaseAddress); + /** Reserves, commits, or changes the state of a region of pages. */ @CFunction(transition = NO_TRANSITION) public static native Pointer VirtualAlloc(PointerBase lpAddress, UnsignedWord dwSize, int flAllocationType, int flProtect); @@ -117,4 +124,8 @@ public interface MEMORY_BASIC_INFORMATION extends PointerBase { @CField int Type(); } + + /** MEMORY_BASIC_INFORMATION - Type Constants */ + @CConstant + public static native int MEM_MAPPED(); } From f4aabc4720c38b3945fc88897dba828cda17f6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Pejovi=C4=87?= Date: Wed, 28 Jul 2021 11:00:38 +0200 Subject: [PATCH 2/6] Ensure image heap size is a multiple of page size --- .../core/genscavenge/AbstractImageHeapLayouter.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java index 35b94b4b91ce..f2d7ceb6f7f6 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractImageHeapLayouter.java @@ -77,6 +77,10 @@ public T[] getPartitions() { return partitions; } + private T getLastPartition() { + return getPartitions()[PARTITION_COUNT - 1]; + } + public AbstractImageHeapLayouter() { this.partitions = createPartitionsArray(PARTITION_COUNT); this.partitions[READ_ONLY_PRIMITIVE] = createPartition("readOnlyPrimitive", false, false, false); @@ -111,6 +115,12 @@ public ImageHeapLayoutInfo layout(ImageHeap imageHeap, int pageSize) { } else if (partition == getWritableHuge()) { endAlignment = pageSize; } + + /* Make sure the image heap size is a multiple of the page size. */ + if (partition == getLastPartition()) { + endAlignment = pageSize; + } + partition.setStartAlignment(startAlignment); partition.setEndAlignment(endAlignment); } From 974d38cb6d560db9587c795269c084b8e6957282 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Pejovi=C4=87?= Date: Thu, 19 Aug 2021 11:02:21 +0200 Subject: [PATCH 3/6] Refactor AbstractCopyingImageHeapProvider --- .../os/AbstractCopyingImageHeapProvider.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java index 57f16c904356..7f5e94ab1f46 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/os/AbstractCopyingImageHeapProvider.java @@ -71,18 +71,11 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W // Copy the memory to the reserved address space. Word imageHeapBegin = IMAGE_HEAP_BEGIN.get(); UnsignedWord imageHeapSizeInFile = getImageHeapSizeInFile(); - Pointer imageHeap = heapBase.add(Heap.getHeap().getImageHeapOffsetInAddressSpace()); - imageHeap = VirtualMemoryProvider.get().commit(imageHeap, imageHeapSizeInFile, VirtualMemoryProvider.Access.READ | VirtualMemoryProvider.Access.WRITE); - if (imageHeap.isNull()) { - freeImageHeap(allocatedMemory); - return CEntryPointErrors.RESERVE_ADDRESS_SPACE_FAILED; - } - - int copyResult = copyMemory(imageHeapBegin, imageHeapSizeInFile, imageHeap); - if (copyResult != CEntryPointErrors.NO_ERROR) { + int result = commitAndCopyMemory(imageHeapBegin, imageHeapSizeInFile, imageHeap); + if (result != CEntryPointErrors.NO_ERROR) { freeImageHeap(allocatedMemory); - return copyResult; + return result; } // Protect the read-only parts at the start of the image heap. @@ -91,7 +84,7 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W Pointer firstPartOfReadOnlyImageHeap = imageHeap.add(nullRegionSize); UnsignedWord writableBeginPageOffset = UnsignedUtils.roundDown(IMAGE_HEAP_WRITABLE_BEGIN.get().subtract(imageHeapBegin.add(nullRegionSize)), pageSize); if (writableBeginPageOffset.aboveThan(0)) { - if (VirtualMemoryProvider.get().protect(firstPartOfReadOnlyImageHeap, writableBeginPageOffset, VirtualMemoryProvider.Access.READ) != 0) { + if (VirtualMemoryProvider.get().protect(firstPartOfReadOnlyImageHeap, writableBeginPageOffset, Access.READ) != 0) { freeImageHeap(allocatedMemory); return CEntryPointErrors.PROTECT_HEAP_FAILED; } @@ -102,7 +95,7 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W if (writableEndPageOffset.belowThan(imageHeapSizeInFile)) { Pointer afterWritableBoundary = imageHeap.add(writableEndPageOffset); UnsignedWord afterWritableSize = imageHeapSizeInFile.subtract(writableEndPageOffset); - if (VirtualMemoryProvider.get().protect(afterWritableBoundary, afterWritableSize, VirtualMemoryProvider.Access.READ) != 0) { + if (VirtualMemoryProvider.get().protect(afterWritableBoundary, afterWritableSize, Access.READ) != 0) { freeImageHeap(allocatedMemory); return CEntryPointErrors.PROTECT_HEAP_FAILED; } @@ -123,6 +116,14 @@ public int initialize(Pointer reservedAddressSpace, UnsignedWord reservedSize, W return CEntryPointErrors.NO_ERROR; } + @Uninterruptible(reason = "Called during isolate initialization.") + protected int commitAndCopyMemory(Pointer loadedImageHeap, UnsignedWord imageHeapSize, Pointer newImageHeap) { + if (VirtualMemoryProvider.get().commit(newImageHeap, imageHeapSize, Access.READ | Access.WRITE).isNull()) { + return CEntryPointErrors.RESERVE_ADDRESS_SPACE_FAILED; + } + return copyMemory(loadedImageHeap, imageHeapSize, newImageHeap); + } + @Uninterruptible(reason = "Called during isolate initialization.") protected abstract int copyMemory(Pointer loadedImageHeap, UnsignedWord imageHeapSize, Pointer newImageHeap); From dde224520a79cae2aa6169d7e551e924fbadcbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Pejovi=C4=87?= Date: Thu, 19 Aug 2021 11:38:25 +0200 Subject: [PATCH 4/6] Add copy-on-write image heap provider for Windows --- .../windows/WindowsImageHeapProvider.java | 230 ++++++++++++++++++ .../oracle/svm/core/windows/WindowsUtils.java | 3 + .../svm/core/windows/headers/FileAPI.java | 29 ++- .../oracle/svm/core/windows/headers/LibC.java | 9 +- .../svm/core/windows/headers/MemoryAPI.java | 8 + .../svm/core/windows/headers/WinBase.java | 3 + 6 files changed, 273 insertions(+), 9 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java new file mode 100644 index 000000000000..e99781765fcf --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.windows; + +import static com.oracle.svm.core.Isolates.IMAGE_HEAP_BEGIN; +import static com.oracle.svm.core.Isolates.IMAGE_HEAP_RELOCATABLE_BEGIN; +import static com.oracle.svm.core.Isolates.IMAGE_HEAP_RELOCATABLE_END; +import static org.graalvm.nativeimage.c.function.CFunction.Transition.NO_TRANSITION; + +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.function.CFunctionPointer; +import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.WordPointer; +import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.word.LocationIdentity; +import org.graalvm.word.Pointer; +import org.graalvm.word.PointerBase; +import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.c.CGlobalData; +import com.oracle.svm.core.c.CGlobalDataFactory; +import com.oracle.svm.core.c.function.CEntryPointActions; +import com.oracle.svm.core.c.function.CEntryPointErrors; +import com.oracle.svm.core.os.CopyingImageHeapProvider; +import com.oracle.svm.core.os.ImageHeapProvider; +import com.oracle.svm.core.os.VirtualMemoryProvider; +import com.oracle.svm.core.os.VirtualMemoryProvider.Access; +import com.oracle.svm.core.windows.headers.FileAPI; +import com.oracle.svm.core.windows.headers.LibC; +import com.oracle.svm.core.windows.headers.LibC.WCharPointer; +import com.oracle.svm.core.windows.headers.LibLoaderAPI; +import com.oracle.svm.core.windows.headers.MemoryAPI; +import com.oracle.svm.core.windows.headers.WinBase; +import com.oracle.svm.core.windows.headers.WinBase.HANDLE; +import com.oracle.svm.core.windows.headers.WinBase.HMODULE; + +@AutomaticFeature +class WindowsImageHeapProviderFeature implements Feature { + @Override + public void duringSetup(DuringSetupAccess access) { + if (!ImageSingletons.contains(ImageHeapProvider.class)) { + ImageSingletons.add(ImageHeapProvider.class, new WindowsImageHeapProvider()); + } + } +} + +/** + * An image heap provider for Windows that creates image heaps that are copy-on-write clones of the + * loaded image heap. + */ +public class WindowsImageHeapProvider extends CopyingImageHeapProvider { + @Override + @Uninterruptible(reason = "Called during isolate initialization.") + protected int commitAndCopyMemory(Pointer loadedImageHeap, UnsignedWord imageHeapSize, Pointer newImageHeap) { + HANDLE imageHeapFileMapping = getImageHeapFileMapping(); + if (imageHeapFileMapping.isNull()) { + /* Fall back to copying from memory. */ + return super.commitAndCopyMemory(loadedImageHeap, imageHeapSize, newImageHeap); + } + + /* Map a copy-on-write view of the image heap. */ + if (VirtualMemoryProvider.get().mapFile(newImageHeap, imageHeapSize, imageHeapFileMapping, getImageHeapFileOffset(), + Access.READ | Access.WRITE).isNull()) { + /* Fall back to copying from memory. */ + return super.commitAndCopyMemory(loadedImageHeap, imageHeapSize, newImageHeap); + } + + /* Copy relocatable pages. */ + return copyMemory(IMAGE_HEAP_RELOCATABLE_BEGIN.get(), IMAGE_HEAP_RELOCATABLE_END.get().subtract(IMAGE_HEAP_RELOCATABLE_BEGIN.get()), + newImageHeap.add(IMAGE_HEAP_RELOCATABLE_BEGIN.get().subtract(IMAGE_HEAP_BEGIN.get()))); + } + + @Override + @Uninterruptible(reason = "Called during isolate initialization.") + protected int copyMemory(Pointer loadedImageHeap, UnsignedWord imageHeapSize, Pointer newImageHeap) { + LibC.memcpy(newImageHeap, loadedImageHeap, imageHeapSize); + return CEntryPointErrors.NO_ERROR; + } + + /** + * The pseudovariable __ImageBase provided by the MSVC linker represents the DOS header of the + * module, which is what a Win32 module begins with. In other words, it is the base address of + * the module and is the same as its HMODULE. + */ + private static final CGlobalData IMAGE_BASE = CGlobalDataFactory.forSymbol("__ImageBase"); + + /** The cached handle of the image heap file mapping that closes when the process exits. */ + private static final CGlobalData IMAGE_HEAP_FILE_MAPPING = CGlobalDataFactory.createWord(WindowsUtils.UNINITIALIZED_HANDLE); + + @Uninterruptible(reason = "Called during isolate initialization.", mayBeInlined = true) + private static HANDLE getImageHeapFileMapping() { + HANDLE value = IMAGE_HEAP_FILE_MAPPING.get().read(); + if (value.equal(WindowsUtils.UNINITIALIZED_HANDLE)) { + HANDLE fileMapping = createImageHeapFileMapping(); + HANDLE existingMapping = (HANDLE) ((Pointer) IMAGE_HEAP_FILE_MAPPING.get()).compareAndSwapWord(0, + WindowsUtils.UNINITIALIZED_HANDLE, fileMapping, LocationIdentity.ANY_LOCATION); + + if (existingMapping.equal(WindowsUtils.UNINITIALIZED_HANDLE)) { + value = fileMapping; + } else { + /* Another thread has already created the mapping, so use that. */ + value = existingMapping; + WinBase.CloseHandle(fileMapping); + } + } + return value; + } + + /** Returns a handle to the image heap file mapping or the null pointer in case of an error. */ + @Uninterruptible(reason = "Called during isolate initialization.", mayBeInlined = true) + private static HANDLE createImageHeapFileMapping() { + /* Get the path of the file that contains the image heap. */ + WCharPointer filePath = StackValue.get(WinBase.MAX_PATH, WCharPointer.class); + int length = LibLoaderAPI.GetModuleFileNameW((HMODULE) IMAGE_BASE.get(), filePath, WinBase.MAX_PATH); + if (length == 0 || length == WinBase.MAX_PATH) { + return WordFactory.nullPointer(); + } + + /* Open the file for mapping. */ + HANDLE fileHandle = FileAPI.CreateFileW(filePath, FileAPI.GENERIC_READ(), FileAPI.FILE_SHARE_READ() | FileAPI.FILE_SHARE_DELETE(), + WordFactory.nullPointer(), FileAPI.OPEN_EXISTING(), 0, WordFactory.nullPointer()); + if (fileHandle.equal(WinBase.INVALID_HANDLE_VALUE())) { + return WordFactory.nullPointer(); + } + + /* Create the mapping and close the file. */ + HANDLE fileMapping = MemoryAPI.CreateFileMappingW(fileHandle, WordFactory.nullPointer(), MemoryAPI.PAGE_READONLY(), + 0, 0, WordFactory.nullPointer()); + WinBase.CloseHandle(fileHandle); + return fileMapping; + } + + private static final CGlobalData IMAGE_HEAP_FILE_OFFSET = CGlobalDataFactory.createWord(); + + @Uninterruptible(reason = "Called during isolate initialization.", mayBeInlined = true) + private static UnsignedWord getImageHeapFileOffset() { + /* + * The file offset of a relative virtual address (RVA) in a PE image can be determined by + * inspecting the PE image headers. + * + * We do this using two helper functions from ntdll.dll: RtlImageNtHeader and + * RtlAddressInSectionTable. Unfortunately, they are not publicly documented, but I think it + * is unlikely that this will pose a problem (and it would definitely not go unnoticed). + * + * Alternatively, we could use equivalent functions from dbghelp.dll (ImageNtHeader and + * ImageRvaToVa), but this would introduce an additional DLL dependency (which we would like + * to avoid). Or we could even do it manually. The tricky part would be finding the header + * of the section containing the RVA. + */ + UnsignedWord value = IMAGE_HEAP_FILE_OFFSET.get().read(); + if (value.equal(0)) { + /* Get the NT header of the module that contains the image heap. */ + PointerBase ntHeader = invokeRtlImageNtHeader(IMAGE_BASE.get()); + /* Compute the RVA of the image heap. */ + int rva = (int) (IMAGE_HEAP_BEGIN.get().rawValue() - IMAGE_BASE.get().rawValue()); + /* Get the file offset of the image heap. */ + value = invokeRtlAddressInSectionTable(ntHeader, rva); + IMAGE_HEAP_FILE_OFFSET.get().write(value); + } + return value; + } + + private static final CGlobalData NTDLL_DLL = CGlobalDataFactory.createCString("ntdll.dll"); + private static final CGlobalData RTL_IMAGE_NT_HEADER = CGlobalDataFactory.createCString("RtlImageNtHeader"); + private static final CGlobalData RTL_ADDRESS_IN_SECTION_TABLE = CGlobalDataFactory.createCString("RtlAddressInSectionTable"); + + private static final int ERROR_BAD_EXE_FORMAT = 0xC1; + + /** Locates the IMAGE_NT_HEADERS structure in a PE image and returns a pointer to the data. */ + @Uninterruptible(reason = "Called during isolate initialization.", mayBeInlined = true) + private static PointerBase invokeRtlImageNtHeader(PointerBase imageBase) { + RtlImageNtHeader rtlImageNtHeader = WindowsUtils.getFunctionPointer(NTDLL_DLL.get(), RTL_IMAGE_NT_HEADER.get(), true); + PointerBase ntHeader = rtlImageNtHeader.invoke(imageBase); + if (ntHeader.isNull()) { + CEntryPointActions.failFatally(ERROR_BAD_EXE_FORMAT, RTL_IMAGE_NT_HEADER.get()); + } + return ntHeader; + } + + private interface RtlImageNtHeader extends CFunctionPointer { + @InvokeCFunctionPointer(transition = NO_TRANSITION) + PointerBase invoke(PointerBase imageBase); + } + + /** + * Locates a relative virtual address (RVA) within the image header of a file that is mapped as + * a file and returns the offset of the corresponding byte in the file. + */ + @Uninterruptible(reason = "Called during isolate initialization.", mayBeInlined = true) + private static UnsignedWord invokeRtlAddressInSectionTable(PointerBase ntHeader, int rva) { + RtlAddressInSectionTable rtlAddressInSectionTable = WindowsUtils.getFunctionPointer(NTDLL_DLL.get(), RTL_ADDRESS_IN_SECTION_TABLE.get(), true); + UnsignedWord offset = (UnsignedWord) rtlAddressInSectionTable.invoke(ntHeader, WordFactory.nullPointer(), rva); + if (offset.equal(0)) { + CEntryPointActions.failFatally(ERROR_BAD_EXE_FORMAT, RTL_ADDRESS_IN_SECTION_TABLE.get()); + } + return offset; + } + + private interface RtlAddressInSectionTable extends CFunctionPointer { + @InvokeCFunctionPointer(transition = NO_TRANSITION) + PointerBase invoke(PointerBase ntHeader, PointerBase base, int rva); + } +} diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java index bb8527c2a713..91ef665334f2 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsUtils.java @@ -186,6 +186,9 @@ public static long getNanoCounter() { return (long) ((current / freq) * NANOSECS_PER_SEC); } + /** Sentinel value denoting the uninitialized kernel handle. */ + public static final PointerBase UNINITIALIZED_HANDLE = WordFactory.pointer(1); + @CPointerTo(nameOfCType = "void*") interface CFunctionPointerPointer extends PointerBase { T read(); diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/FileAPI.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/FileAPI.java index 3d92e3b00ae5..467078a89c40 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/FileAPI.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/FileAPI.java @@ -26,8 +26,6 @@ import static org.graalvm.nativeimage.c.function.CFunction.Transition.NO_TRANSITION; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.CContext; import org.graalvm.nativeimage.c.constant.CConstant; import org.graalvm.nativeimage.c.function.CFunction; @@ -37,16 +35,37 @@ import org.graalvm.word.UnsignedWord; import com.oracle.svm.core.windows.headers.LibC.WCharPointer; +import com.oracle.svm.core.windows.headers.WinBase.HANDLE; -//Checkstyle: stop +// Checkstyle: stop /** - * Definitions manually translated from the Windows header file fileapi.h. + * Definitions for Windows fileapi.h */ @CContext(WindowsDirectives.class) -@Platforms(Platform.WINDOWS.class) public class FileAPI { + /** Generic Access Rights */ + @CConstant + public static native int GENERIC_READ(); + + /** Creates or opens a file or I/O device. */ + @CFunction(transition = NO_TRANSITION) + public static native HANDLE CreateFileW(WCharPointer lpFileName, int dwDesiredAccess, int dwShareMode, + PointerBase lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, + HANDLE hTemplateFile); + + /** CreateFile - dwShareMode Constants */ + @CConstant + public static native int FILE_SHARE_READ(); + + @CConstant + public static native int FILE_SHARE_DELETE(); + + /** CreateFile - dwCreationDisposition Constants */ + @CConstant + public static native int OPEN_EXISTING(); + @CFunction public static native int WriteFile(int hFile, CCharPointer lpBuffer, UnsignedWord nNumberOfBytesToWrite, CIntPointer lpNumberOfBytesWritten, PointerBase lpOverlapped); diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/LibC.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/LibC.java index 4d89104a2be1..6773afdee5cf 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/LibC.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/LibC.java @@ -24,8 +24,6 @@ */ package com.oracle.svm.core.windows.headers; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.CContext; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.struct.CPointerTo; @@ -35,14 +33,17 @@ import com.oracle.svm.core.annotate.Uninterruptible; -//Checkstyle: stop +// Checkstyle: stop /** * Basic functions from the standard Visual Studio C Run-Time library */ @CContext(WindowsDirectives.class) -@Platforms(Platform.WINDOWS.class) public class LibC { + + @CFunction(transition = CFunction.Transition.NO_TRANSITION) + public static native T memcpy(T dest, PointerBase src, UnsignedWord n); + @CFunction(transition = CFunction.Transition.NO_TRANSITION) public static native T malloc(UnsignedWord size); diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java index dd4372142533..7f275821f126 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/MemoryAPI.java @@ -36,6 +36,9 @@ import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; +import com.oracle.svm.core.windows.headers.LibC.WCharPointer; +import com.oracle.svm.core.windows.headers.WinBase.HANDLE; + // Checkstyle: stop /** @@ -66,6 +69,11 @@ public class MemoryAPI { @CConstant public static native int PAGE_WRITECOPY(); + /** Creates or opens a named or unnamed file mapping object for a specified file. */ + @CFunction(transition = NO_TRANSITION) + public static native HANDLE CreateFileMappingW(HANDLE hFile, PointerBase lpFileMappingAttributes, int flProtect, + int dwMaximumSizeHigh, int dwMaximumSizeLow, WCharPointer lpName); + /** Unmaps a mapped view of a file from the calling process's address space. */ @CFunction(transition = NO_TRANSITION) public static native int UnmapViewOfFile(PointerBase lpBaseAddress); diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/WinBase.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/WinBase.java index a0206b5712b9..47f97da1de8b 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/WinBase.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/WinBase.java @@ -49,6 +49,9 @@ public class WinBase { public interface HANDLE extends PointerBase { } + @CConstant + public static native HANDLE INVALID_HANDLE_VALUE(); + @CPointerTo(nameOfCType = "HANDLE") public interface LPHANDLE extends PointerBase { HANDLE read(); From 0ac5470e26069d5fe8f92a2439d1db4a639d52f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Pejovi=C4=87?= Date: Thu, 2 Sep 2021 13:11:52 +0200 Subject: [PATCH 5/6] Ensure image heap offset in file is page size aligned --- .../src/com/oracle/svm/hosted/image/NativeImageViaCC.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java index daebe68b7de2..577e4e06f338 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageViaCC.java @@ -283,6 +283,9 @@ public List getCommand() { cmd.add("/INCREMENTAL:NO"); cmd.add("/NODEFAULTLIB:LIBCMT"); + /* Use page size alignment to support memory mapping of the image heap. */ + cmd.add("/FILEALIGN:4096"); + /* Put .lib and .exp files in a temp dir as we don't usually need them. */ cmd.add("/IMPLIB:" + getTempDirectory().resolve(imageName + ".lib")); From fb89576d96a9c573dd86241d0f8b1d6a672d82f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandar=20Pejovi=C4=87?= Date: Wed, 15 Sep 2021 17:51:25 +0200 Subject: [PATCH 6/6] Fix superclass of WindowsImageHeapProvider --- .../com/oracle/svm/core/windows/WindowsImageHeapProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java index e99781765fcf..6db754847a90 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsImageHeapProvider.java @@ -48,7 +48,7 @@ import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.c.function.CEntryPointActions; import com.oracle.svm.core.c.function.CEntryPointErrors; -import com.oracle.svm.core.os.CopyingImageHeapProvider; +import com.oracle.svm.core.os.AbstractCopyingImageHeapProvider; import com.oracle.svm.core.os.ImageHeapProvider; import com.oracle.svm.core.os.VirtualMemoryProvider; import com.oracle.svm.core.os.VirtualMemoryProvider.Access; @@ -75,7 +75,7 @@ public void duringSetup(DuringSetupAccess access) { * An image heap provider for Windows that creates image heaps that are copy-on-write clones of the * loaded image heap. */ -public class WindowsImageHeapProvider extends CopyingImageHeapProvider { +public class WindowsImageHeapProvider extends AbstractCopyingImageHeapProvider { @Override @Uninterruptible(reason = "Called during isolate initialization.") protected int commitAndCopyMemory(Pointer loadedImageHeap, UnsignedWord imageHeapSize, Pointer newImageHeap) {