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.                                                │ | ||||
| ╚─────────────────────────────────────────────────────────────────────────────*/ | ||||
| #include "libc/calls/syscall_support-nt.internal.h" | ||||
| #include "libc/macros.internal.h" | ||||
| #include "libc/nt/enum/offerpriority.h" | ||||
| #include "libc/nt/memory.h" | ||||
| #include "libc/nt/runtime.h" | ||||
| #include "libc/nt/struct/memoryrangeentry.h" | ||||
| #include "libc/sysv/consts/madv.h" | ||||
| #include "libc/sysv/errfuns.h" | ||||
| 
 | ||||
| forceinline typeof(PrefetchVirtualMemory) *GetPrefetchVirtualMemory(void) { | ||||
|   static bool once; | ||||
|   static typeof(PrefetchVirtualMemory) *PrefetchVirtualMemory_; | ||||
|   if (!once) { | ||||
| typedef bool32 (*__msabi PrefetchVirtualMemoryPtr)( | ||||
|     int64_t hProcess, uintptr_t NumberOfEntries, | ||||
|     struct NtMemoryRangeEntry *VirtualAddresses, uint32_t reserved_Flags); | ||||
| 
 | ||||
| textwindows static PrefetchVirtualMemoryPtr GetPrefetchVirtualMemory(void) { | ||||
|   static PrefetchVirtualMemoryPtr PrefetchVirtualMemory_; | ||||
|   if (!PrefetchVirtualMemory_) { | ||||
|     PrefetchVirtualMemory_ = /* win8.1+ */ | ||||
|         GetProcAddressModule("Kernel32.dll", "PrefetchVirtualMemory"); | ||||
|     once = true; | ||||
|   } | ||||
|   return PrefetchVirtualMemory_; | ||||
| } | ||||
| 
 | ||||
| forceinline typeof(OfferVirtualMemory) *GetOfferVirtualMemory(void) { | ||||
|   static bool once; | ||||
|   static typeof(OfferVirtualMemory) *OfferVirtualMemory_; | ||||
|   if (!once) { | ||||
| typedef bool32 (*__msabi OfferVirtualMemoryPtr)(void *inout_VirtualAddress, | ||||
|                                                 size_t Size, int Priority); | ||||
| 
 | ||||
| textwindows static OfferVirtualMemoryPtr GetOfferVirtualMemory(void) { | ||||
|   static OfferVirtualMemoryPtr OfferVirtualMemory_; | ||||
|   if (!OfferVirtualMemory_) { | ||||
|     OfferVirtualMemory_ = /* win8.1+ */ | ||||
|         GetProcAddressModule("Kernel32.dll", "OfferVirtualMemory"); | ||||
|     once = true; | ||||
|   } | ||||
|   return OfferVirtualMemory_; | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|     typeof(PrefetchVirtualMemory) *fn = GetPrefetchVirtualMemory(); | ||||
|     PrefetchVirtualMemoryPtr fn = GetPrefetchVirtualMemory(); | ||||
|     if (fn) { | ||||
|       ranges[0].VirtualAddress = addr; | ||||
|       ranges[0].NumberOfBytes = length; | ||||
|       rangecount = ARRAYLEN(ranges); | ||||
|       if (fn(GetCurrentProcess(), &rangecount, ranges, 0)) { | ||||
|       if (fn(GetCurrentProcess(), 1, &(struct NtMemoryRangeEntry){addr, length}, | ||||
|              0)) { | ||||
|         return 0; | ||||
|       } else { | ||||
|         return __winerr(); | ||||
|  | @ -65,7 +62,7 @@ textwindows int sys_madvise_nt(void *addr, size_t length, int advice) { | |||
|       return enosys(); | ||||
|     } | ||||
|   } else if (advice == MADV_FREE) { | ||||
|     typeof(OfferVirtualMemory) *fn = GetOfferVirtualMemory(); | ||||
|     OfferVirtualMemoryPtr fn = GetOfferVirtualMemory(); | ||||
|     if (fn) { | ||||
|       if (fn(addr, length, kNtVmOfferPriorityNormal)) { | ||||
|         return 0; | ||||
|  |  | |||
|  | @ -29,12 +29,15 @@ | |||
|  * | ||||
|  * @param advice can be MADV_WILLNEED, MADV_SEQUENTIAL, MADV_FREE, etc. | ||||
|  * @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 fadvise() | ||||
|  */ | ||||
| int madvise(void *addr, size_t length, int advice) { | ||||
|   int rc; | ||||
|   if (advice != 127 /* see consts.sh */) { | ||||
|   if (IsAsan() && !__asan_is_valid(addr, length)) { | ||||
|     rc = efault(); | ||||
|   } else if (!IsWindows()) { | ||||
|  | @ -42,9 +45,6 @@ int madvise(void *addr, size_t length, int advice) { | |||
|   } else { | ||||
|     rc = sys_madvise_nt(addr, length, advice); | ||||
|   } | ||||
|   } else { | ||||
|     rc = einval(); | ||||
|   } | ||||
|   STRACE("madvise(%p, %'zu, %d) → %d% m", addr, length, advice, rc); | ||||
|   return rc; | ||||
| } | ||||
|  |  | |||
|  | @ -23,6 +23,10 @@ | |||
|  * Advises kernel about memory intentions, the POSIX way. | ||||
|  * | ||||
|  * @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 | ||||
|  */ | ||||
| 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, | ||||
|                      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); | ||||
| void *HeapAlloc(int64_t hHeap, uint32_t dwFlags, size_t dwBytes) __wur; | ||||
| 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