mirror of
				https://github.com/jart/cosmopolitan.git
				synced 2025-10-26 11:10:58 +00:00 
			
		
		
		
	Fix madvise() on Windows
This commit is contained in:
		
							parent
							
								
									f51fd97644
								
							
						
					
					
						commit
						ce0143e2a1
					
				
					 5 changed files with 121 additions and 35 deletions
				
			
		|  | @ -17,46 +17,43 @@ | ||||||
| │ PERFORMANCE OF THIS SOFTWARE.                                                │ | │ PERFORMANCE OF THIS SOFTWARE.                                                │ | ||||||
| ╚─────────────────────────────────────────────────────────────────────────────*/ | ╚─────────────────────────────────────────────────────────────────────────────*/ | ||||||
| #include "libc/calls/syscall_support-nt.internal.h" | #include "libc/calls/syscall_support-nt.internal.h" | ||||||
| #include "libc/macros.internal.h" |  | ||||||
| #include "libc/nt/enum/offerpriority.h" | #include "libc/nt/enum/offerpriority.h" | ||||||
| #include "libc/nt/memory.h" |  | ||||||
| #include "libc/nt/runtime.h" | #include "libc/nt/runtime.h" | ||||||
| #include "libc/nt/struct/memoryrangeentry.h" | #include "libc/nt/struct/memoryrangeentry.h" | ||||||
| #include "libc/sysv/consts/madv.h" | #include "libc/sysv/consts/madv.h" | ||||||
| #include "libc/sysv/errfuns.h" | #include "libc/sysv/errfuns.h" | ||||||
| 
 | 
 | ||||||
| forceinline typeof(PrefetchVirtualMemory) *GetPrefetchVirtualMemory(void) { | typedef bool32 (*__msabi PrefetchVirtualMemoryPtr)( | ||||||
|   static bool once; |     int64_t hProcess, uintptr_t NumberOfEntries, | ||||||
|   static typeof(PrefetchVirtualMemory) *PrefetchVirtualMemory_; |     struct NtMemoryRangeEntry *VirtualAddresses, uint32_t reserved_Flags); | ||||||
|   if (!once) { | 
 | ||||||
|  | textwindows static PrefetchVirtualMemoryPtr GetPrefetchVirtualMemory(void) { | ||||||
|  |   static PrefetchVirtualMemoryPtr PrefetchVirtualMemory_; | ||||||
|  |   if (!PrefetchVirtualMemory_) { | ||||||
|     PrefetchVirtualMemory_ = /* win8.1+ */ |     PrefetchVirtualMemory_ = /* win8.1+ */ | ||||||
|         GetProcAddressModule("Kernel32.dll", "PrefetchVirtualMemory"); |         GetProcAddressModule("Kernel32.dll", "PrefetchVirtualMemory"); | ||||||
|     once = true; |  | ||||||
|   } |   } | ||||||
|   return PrefetchVirtualMemory_; |   return PrefetchVirtualMemory_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| forceinline typeof(OfferVirtualMemory) *GetOfferVirtualMemory(void) { | typedef bool32 (*__msabi OfferVirtualMemoryPtr)(void *inout_VirtualAddress, | ||||||
|   static bool once; |                                                 size_t Size, int Priority); | ||||||
|   static typeof(OfferVirtualMemory) *OfferVirtualMemory_; | 
 | ||||||
|   if (!once) { | textwindows static OfferVirtualMemoryPtr GetOfferVirtualMemory(void) { | ||||||
|  |   static OfferVirtualMemoryPtr OfferVirtualMemory_; | ||||||
|  |   if (!OfferVirtualMemory_) { | ||||||
|     OfferVirtualMemory_ = /* win8.1+ */ |     OfferVirtualMemory_ = /* win8.1+ */ | ||||||
|         GetProcAddressModule("Kernel32.dll", "OfferVirtualMemory"); |         GetProcAddressModule("Kernel32.dll", "OfferVirtualMemory"); | ||||||
|     once = true; |  | ||||||
|   } |   } | ||||||
|   return OfferVirtualMemory_; |   return OfferVirtualMemory_; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| textwindows int sys_madvise_nt(void *addr, size_t length, int advice) { | textwindows int sys_madvise_nt(void *addr, size_t length, int advice) { | ||||||
|   uint32_t rangecount; |  | ||||||
|   struct NtMemoryRangeEntry ranges[1]; |  | ||||||
|   if (advice == MADV_WILLNEED || advice == MADV_SEQUENTIAL) { |   if (advice == MADV_WILLNEED || advice == MADV_SEQUENTIAL) { | ||||||
|     typeof(PrefetchVirtualMemory) *fn = GetPrefetchVirtualMemory(); |     PrefetchVirtualMemoryPtr fn = GetPrefetchVirtualMemory(); | ||||||
|     if (fn) { |     if (fn) { | ||||||
|       ranges[0].VirtualAddress = addr; |       if (fn(GetCurrentProcess(), 1, &(struct NtMemoryRangeEntry){addr, length}, | ||||||
|       ranges[0].NumberOfBytes = length; |              0)) { | ||||||
|       rangecount = ARRAYLEN(ranges); |  | ||||||
|       if (fn(GetCurrentProcess(), &rangecount, ranges, 0)) { |  | ||||||
|         return 0; |         return 0; | ||||||
|       } else { |       } else { | ||||||
|         return __winerr(); |         return __winerr(); | ||||||
|  | @ -65,7 +62,7 @@ textwindows int sys_madvise_nt(void *addr, size_t length, int advice) { | ||||||
|       return enosys(); |       return enosys(); | ||||||
|     } |     } | ||||||
|   } else if (advice == MADV_FREE) { |   } else if (advice == MADV_FREE) { | ||||||
|     typeof(OfferVirtualMemory) *fn = GetOfferVirtualMemory(); |     OfferVirtualMemoryPtr fn = GetOfferVirtualMemory(); | ||||||
|     if (fn) { |     if (fn) { | ||||||
|       if (fn(addr, length, kNtVmOfferPriorityNormal)) { |       if (fn(addr, length, kNtVmOfferPriorityNormal)) { | ||||||
|         return 0; |         return 0; | ||||||
|  |  | ||||||
|  | @ -29,21 +29,21 @@ | ||||||
|  * |  * | ||||||
|  * @param advice can be MADV_WILLNEED, MADV_SEQUENTIAL, MADV_FREE, etc. |  * @param advice can be MADV_WILLNEED, MADV_SEQUENTIAL, MADV_FREE, etc. | ||||||
|  * @return 0 on success, or -1 w/ errno |  * @return 0 on success, or -1 w/ errno | ||||||
|  |  * @raise EINVAL if `advice` isn't valid or supported by system | ||||||
|  |  * @raise EINVAL on Linux if addr/length isn't page size aligned with | ||||||
|  |  *     respect to `getauxval(AT_PAGESZ)` | ||||||
|  |  * @raise ENOMEM on Linux if addr/length overlaps unmapped regions | ||||||
|  * @see libc/sysv/consts.sh |  * @see libc/sysv/consts.sh | ||||||
|  * @see fadvise() |  * @see fadvise() | ||||||
|  */ |  */ | ||||||
| int madvise(void *addr, size_t length, int advice) { | int madvise(void *addr, size_t length, int advice) { | ||||||
|   int rc; |   int rc; | ||||||
|   if (advice != 127 /* see consts.sh */) { |   if (IsAsan() && !__asan_is_valid(addr, length)) { | ||||||
|     if (IsAsan() && !__asan_is_valid(addr, length)) { |     rc = efault(); | ||||||
|       rc = efault(); |   } else if (!IsWindows()) { | ||||||
|     } else if (!IsWindows()) { |     rc = sys_madvise(addr, length, advice); | ||||||
|       rc = sys_madvise(addr, length, advice); |  | ||||||
|     } else { |  | ||||||
|       rc = sys_madvise_nt(addr, length, advice); |  | ||||||
|     } |  | ||||||
|   } else { |   } else { | ||||||
|     rc = einval(); |     rc = sys_madvise_nt(addr, length, advice); | ||||||
|   } |   } | ||||||
|   STRACE("madvise(%p, %'zu, %d) → %d% m", addr, length, advice, rc); |   STRACE("madvise(%p, %'zu, %d) → %d% m", addr, length, advice, rc); | ||||||
|   return rc; |   return rc; | ||||||
|  |  | ||||||
|  | @ -23,6 +23,10 @@ | ||||||
|  * Advises kernel about memory intentions, the POSIX way. |  * Advises kernel about memory intentions, the POSIX way. | ||||||
|  * |  * | ||||||
|  * @return 0 on success, or errno on error |  * @return 0 on success, or errno on error | ||||||
|  |  * @raise EINVAL if `advice` isn't valid or supported by system | ||||||
|  |  * @raise EINVAL on Linux if addr/length isn't page size aligned with | ||||||
|  |  *     respect to `getauxval(AT_PAGESZ)` | ||||||
|  |  * @raise ENOMEM on Linux if addr/length overlaps unmapped regions | ||||||
|  * @returnserrno |  * @returnserrno | ||||||
|  */ |  */ | ||||||
| errno_t posix_madvise(void *addr, uint64_t len, int advice) { | errno_t posix_madvise(void *addr, uint64_t len, int advice) { | ||||||
|  |  | ||||||
|  | @ -73,12 +73,6 @@ uint64_t VirtualQuery(const void *lpAddress, | ||||||
| void *VirtualAllocEx(int64_t hProcess, void *lpAddress, uint64_t dwSize, | void *VirtualAllocEx(int64_t hProcess, void *lpAddress, uint64_t dwSize, | ||||||
|                      uint32_t flAllocationType, uint32_t flProtect); |                      uint32_t flAllocationType, uint32_t flProtect); | ||||||
| 
 | 
 | ||||||
| bool32 PrefetchVirtualMemory(int64_t hProcess, const uint32_t *NumberOfEntries, |  | ||||||
|                              struct NtMemoryRangeEntry *VirtualAddresses, |  | ||||||
|                              uint32_t reserved_Flags); |  | ||||||
| bool32 OfferVirtualMemory(void *inout_VirtualAddress, size_t Size, |  | ||||||
|                           int Priority); |  | ||||||
| 
 |  | ||||||
| int64_t GetProcessHeap(void); | int64_t GetProcessHeap(void); | ||||||
| void *HeapAlloc(int64_t hHeap, uint32_t dwFlags, size_t dwBytes) __wur; | void *HeapAlloc(int64_t hHeap, uint32_t dwFlags, size_t dwBytes) __wur; | ||||||
| bool32 HeapFree(int64_t hHeap, uint32_t dwFlags, void *opt_lpMem); | bool32 HeapFree(int64_t hHeap, uint32_t dwFlags, void *opt_lpMem); | ||||||
|  |  | ||||||
							
								
								
									
										91
									
								
								test/libc/calls/madvise_test.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								test/libc/calls/madvise_test.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | ||||||
|  | /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
 | ||||||
|  | │ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8                               :vi │ | ||||||
|  | ╞══════════════════════════════════════════════════════════════════════════════╡ | ||||||
|  | │ Copyright 2023 Justine Alexandra Roberts Tunney                              │ | ||||||
|  | │                                                                              │ | ||||||
|  | │ Permission to use, copy, modify, and/or distribute this software for         │ | ||||||
|  | │ any purpose with or without fee is hereby granted, provided that the         │ | ||||||
|  | │ above copyright notice and this permission notice appear in all copies.      │ | ||||||
|  | │                                                                              │ | ||||||
|  | │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │ | ||||||
|  | │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │ | ||||||
|  | │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │ | ||||||
|  | │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │ | ||||||
|  | │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │ | ||||||
|  | │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │ | ||||||
|  | │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │ | ||||||
|  | │ PERFORMANCE OF THIS SOFTWARE.                                                │ | ||||||
|  | ╚─────────────────────────────────────────────────────────────────────────────*/ | ||||||
|  | #include "libc/calls/calls.h" | ||||||
|  | #include "libc/dce.h" | ||||||
|  | #include "libc/errno.h" | ||||||
|  | #include "libc/runtime/runtime.h" | ||||||
|  | #include "libc/sysv/consts/madv.h" | ||||||
|  | #include "libc/sysv/consts/map.h" | ||||||
|  | #include "libc/sysv/consts/o.h" | ||||||
|  | #include "libc/sysv/consts/prot.h" | ||||||
|  | #include "libc/testlib/testlib.h" | ||||||
|  | 
 | ||||||
|  | void SetUpOnce(void) { | ||||||
|  |   testlib_enable_tmp_setup_teardown(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(madvise, anon) { | ||||||
|  |   char *p; | ||||||
|  |   ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, | ||||||
|  |                                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); | ||||||
|  |   ASSERT_SYS(0, 0, madvise(p, FRAMESIZE, MADV_WILLNEED)); | ||||||
|  |   ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(madvise, file) { | ||||||
|  |   char *p; | ||||||
|  |   ASSERT_SYS(0, 3, creat("foo.dat", 0644)); | ||||||
|  |   ASSERT_SYS(0, 0, ftruncate(3, FRAMESIZE)); | ||||||
|  |   ASSERT_SYS(0, 0, close(3)); | ||||||
|  |   ASSERT_SYS(0, 3, open("foo.dat", O_RDWR)); | ||||||
|  |   ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, | ||||||
|  |                                   MAP_PRIVATE, 3, 0))); | ||||||
|  |   ASSERT_SYS(0, 0, madvise(p, FRAMESIZE, MADV_WILLNEED)); | ||||||
|  |   ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); | ||||||
|  |   ASSERT_SYS(0, 0, close(3)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(madvise, short) { | ||||||
|  |   char *p; | ||||||
|  |   ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, | ||||||
|  |                                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); | ||||||
|  |   ASSERT_SYS(0, 0, madvise(p, FRAMESIZE - 1, MADV_WILLNEED)); | ||||||
|  |   ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(madvise, subPages) { | ||||||
|  |   char *p; | ||||||
|  |   ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, | ||||||
|  |                                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); | ||||||
|  |   ASSERT_SYS(0, 0, madvise(p + 4096, FRAMESIZE - 4096, MADV_WILLNEED)); | ||||||
|  |   ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(madvise, misalign) { | ||||||
|  |   char *p; | ||||||
|  |   if (!IsLinux()) return;  // most platforms don't care
 | ||||||
|  |   ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, | ||||||
|  |                                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); | ||||||
|  |   ASSERT_SYS(EINVAL, -1, madvise(p + 1, FRAMESIZE - 1, MADV_WILLNEED)); | ||||||
|  |   ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(madvise, badAdvice) { | ||||||
|  |   char *p; | ||||||
|  |   ASSERT_NE(MAP_FAILED, (p = mmap(0, FRAMESIZE, PROT_READ | PROT_WRITE, | ||||||
|  |                                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))); | ||||||
|  |   ASSERT_SYS(EINVAL, -1, madvise(p, FRAMESIZE, 127)); | ||||||
|  |   ASSERT_SYS(0, 0, munmap(p, FRAMESIZE)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(madvise, missingMemory) { | ||||||
|  |   if (!IsLinux()) return; | ||||||
|  |   ASSERT_SYS(ENOMEM, -1, | ||||||
|  |              madvise((char *)0x83483838000, FRAMESIZE, MADV_WILLNEED)); | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue