diff --git a/.github/workflows/build-proton.yml b/.github/workflows/build-proton.yml index d868472f25b..ff34437918b 100644 --- a/.github/workflows/build-proton.yml +++ b/.github/workflows/build-proton.yml @@ -331,13 +331,23 @@ jobs: Single SDK 28 build with 16KB page size support (works on Android 9+ and Android 15+). ### Features - - Experimental userspace ntsync (via [ntsync-android](https://github.com/GameNative/ntsync-android)) + fsync + - Experimental userspace ntsync (via [ntsync-android](https://github.com/GameNative/ntsync-android) V8, commit [`d00ac47`](https://github.com/GameNative/ntsync-android/commit/d00ac47491f2a5505d2ea3a2c8533f498a9811b0)) + fsync, with wineserver-side `ntsync_event_set` support + - `WINE_FAST_YIELD` fast-yield hook in `NtYieldExecution` - FEX integration (stats shm, `SkipThreadAttach` detach guard) via bylaws' arm64ec patches + - FEX unixlib loader: `MemoryWineLoadUnixLibByName` / wow64 / unload support with `$PREFIX/lib/wine` fallback + - `WINEVMEMMAXSIZE` virtual-address cap for FEX environments - Integrated lsteamclient built into Proton (supports GameNative bionic Steam) - Stripped, `-g0 -O2` build: smaller tree, faster install (zstd-packed `.wcp`) + ### Android / bionic fixes + - Anonymous PE-image mapping for bionic (vc4 devices); `LC_ALL=C.UTF-8` default; shell32 drive-root path fix + - Android clipboard bridge (user32/win32u) and Android URL/intent launching in winebrowser + - dnsapi libresolv bionic port; nsiproxy interface/route enumeration without netlink; ws2_32 socket tweaks + - winepulse audio period fix, MIDI mapper rework, SDL gamepad init fixes + - winex11 window/keyboard/mouse/GL/desktop tweaks; explorer desktop sizing; `GetUserNameA` on Android + ### Credits - Compile optimizations (ccache, -g0/strip, zstd packaging) are adopted from [The412Banner/proton-wine](https://github.com/The412Banner/proton-wine) release [build-p11-20260821](https://github.com/The412Banner/proton-wine/releases/tag/build-p11-20260821). + Compile optimizations (ccache, -g0/strip, zstd packaging) and runtime improvements — `WINE_FAST_YIELD` fast-yield, FEX unixlib loader, `WINEVMEMMAXSIZE` VA cap, vc4 bionic fixes (anonymous PE mappings, `LC_ALL=C.UTF-8`, shell32 drive-root fix), and xrandr/xrender enablement — are adopted from [The412Banner/proton-wine](https://github.com/The412Banner/proton-wine) release [build-p11-20260821](https://github.com/The412Banner/proton-wine/releases/tag/build-p11-20260821) (merged in commit `b08376b`). ### WCP Files **Proton Type:** diff --git a/android/ntsync_android/ntsync_user.h b/android/ntsync_android/ntsync_user.h index 4773b5d2503..a1c16b4ba45 100644 --- a/android/ntsync_android/ntsync_user.h +++ b/android/ntsync_android/ntsync_user.h @@ -23,7 +23,9 @@ * process that opens the same region path sees the same handles. Waits use * futexes on the shared pages. * - * Not implemented: alertable waits (ntsync_wait_args.alert must be 0). + * Alertable waits are supported: if ntsync_wait_args.alert is nonzero it + * names an event object that aborts the wait; the wait returns success with + * index == count, exactly like the kernel ioctls. * Divergence from the kernel: closing an object other threads are waiting on * fails those waits with -EINVAL; objects leaked by a crashed process must * be reclaimed with ntsync_sweep_dead(). @@ -68,7 +70,8 @@ struct ntsync_wait_args { uint32_t flags; /* In: owner tid used to acquire mutexes. */ uint32_t owner; - /* Unsupported; must be 0. */ + /* In: optional alert event handle (0 = none); aborts the wait, which + * then returns success with index == count. */ uint32_t alert; uint32_t pad; }; @@ -82,7 +85,8 @@ struct ntsync_wait_args { #define NTSYNC_ANDROID_USED_BY_SERVER 0x7eadfe01 /* Initialize the shared region. `path` may be NULL to use - * $TMPDIR/ntsync_userspace.shm (the caller must export TMPDIR). + * $TMPDIR/ntsync_userspace.shm (the caller must export TMPDIR); a layout + * version is inserted before the ".shm" extension (ntsync_userspace.vN.shm). * Idempotent; all other functions auto-initialize on first use. */ int32_t ntsync_init(const char *path); @@ -91,7 +95,9 @@ int32_t ntsync_init(const char *path); * process exits. Returns the number of freed objects or a negative errno. */ int32_t ntsync_sweep_dead(void); -/* Create objects. Return 0 and store the handle, or a negative errno. */ +/* Create objects. Return 0 and store the handle, or a negative errno. + * Handles are never 0: slot 0 is permanently reserved because 0 is the + * "no alert" sentinel in ntsync_wait_args.alert. */ int32_t ntsync_create_sem(uint32_t *out_handle, const struct ntsync_sem_args *args); int32_t ntsync_create_mutex(uint32_t *out_handle, const struct ntsync_mutex_args *args); int32_t ntsync_create_event(uint32_t *out_handle, const struct ntsync_event_args *args); diff --git a/android/patches/arm64ec/dlls_ntdll_unix_loader_c.patch b/android/patches/arm64ec/dlls_ntdll_unix_loader_c.patch index 19944b065cf..0859803ccfb 100644 --- a/android/patches/arm64ec/dlls_ntdll_unix_loader_c.patch +++ b/android/patches/arm64ec/dlls_ntdll_unix_loader_c.patch @@ -1,5 +1,5 @@ diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index f8aa468..35bc233 100644 +index f8aa468..291bf9b 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -340,7 +340,11 @@ static const char *get_pe_dir( WORD machine ) @@ -15,21 +15,142 @@ index f8aa468..35bc233 100644 case IMAGE_FILE_MACHINE_ARMNT: return "/arm-windows"; case IMAGE_FILE_MACHINE_ARM64: return "/aarch64-windows"; default: return ""; -@@ -2451,7 +2455,9 @@ static void start_main_thread(void) +@@ -362,6 +366,7 @@ static WORD get_alt_machine( WORD machine ) + static void set_dll_path(void) + { + char *p, *path = getenv( "WINEDLLPATH" ), *be_runtime = getenv( "PROTON_BATTLEYE_RUNTIME" ), *eac_runtime = getenv( "PROTON_EAC_RUNTIME" ); ++ char *prefix = getenv( "PREFIX" ); + int i, count = 0; + + if (path) for (p = path, count = 1; *p; p++) if (*p == ':') count++; +@@ -372,6 +377,9 @@ static void set_dll_path(void) + if (eac_runtime) + count += 2; + ++ if (prefix && *prefix) ++ count += 1; ++ + dll_paths = malloc( (count + 2) * sizeof(*dll_paths) ); + count = 0; + +@@ -420,6 +428,20 @@ static void set_dll_path(void) + dll_paths[count++] = p; + } + ++ /* Winlator-bionic containers set PREFIX to the imagefs mount; components ++ * (e.g. a FEX-unix companion) drop their unixlibs under $PREFIX/lib/wine. ++ * Add it as the lowest-priority search dir so the builtin's own dir wins. */ ++ if (prefix && *prefix) ++ { ++ const char suffix[] = "/lib/wine"; ++ ++ p = malloc( strlen(prefix) + strlen(suffix) + 1 ); ++ strcpy(p, prefix); ++ strcat(p, suffix); ++ ++ dll_paths[count++] = p; ++ } ++ + for (i = 0; i < count; i++) dll_path_maxlen = max( dll_path_maxlen, strlen(dll_paths[i]) ); + dll_paths[count] = NULL; + } +@@ -1785,6 +1807,81 @@ NTSTATUS load_builtin( const struct pe_image_info *image_info, UNICODE_STRING *n + } + + ++/*********************************************************************** ++ * load_unixlib_by_name ++ */ ++NTSTATUS load_unixlib_by_name( const UNICODE_STRING *nt_name, void **handle_ret ) ++{ ++ unsigned int i, pos, namepos, maxlen = 0; ++ unsigned int len = nt_name->Length / sizeof(WCHAR); ++ const char *so_dir = get_so_dir( current_machine ); ++ char *ptr = NULL, *file, *ext = NULL; ++ void *handle = NULL; ++ ++ if (!len) return STATUS_DLL_NOT_FOUND; ++ ++ for (i = namepos = 0; i < len; i++) ++ if (nt_name->Buffer[i] == '/' || nt_name->Buffer[i] == '\\') break; ++ ++ if (i < len) /* explicit path */ ++ { ++ UNICODE_STRING true_nt_name; ++ OBJECT_ATTRIBUTES attr; ++ ++ InitializeObjectAttributes( &attr, (UNICODE_STRING *)nt_name, 0, 0, NULL ); ++ if (!get_nt_and_unix_names( &attr, &true_nt_name, &file, FILE_OPEN, FALSE )) ++ handle = dlopen( file, RTLD_NOW ); ++ free( true_nt_name.Buffer ); ++ goto done; ++ } ++ ++ if (build_dir) maxlen = strlen(build_dir) + sizeof("/dlls/") + len; ++ maxlen = max( maxlen, dll_path_maxlen + 1 ) + len + sizeof("/aarch64-unix") + sizeof(".so"); ++ ++ if (!(file = malloc( maxlen ))) return STATUS_NO_MEMORY; ++ ++ pos = maxlen - len - 4; ++ ext = file + pos + len; ++ /* we don't want to depend on the current codepage here */ ++ for (i = 0; i < len; i++) ++ { ++ if (nt_name->Buffer[namepos + i] > 127) goto done; ++ file[pos + i] = (char)nt_name->Buffer[namepos + i]; ++ if (file[pos + i] >= 'A' && file[pos + i] <= 'Z') file[pos + i] += 'a' - 'A'; ++ else if (file[pos + i] == '.') ext = file + pos + i; ++ } ++ file[pos + len] = 0; ++ file[--pos] = '/'; ++ ++ if (build_dir) ++ { ++ ptr = prepend_build_dir_path( file + pos, ".so", "", "/dlls", build_dir ); ++ strcpy( ext, ".so" ); ++ if ((handle = dlopen( ptr, RTLD_NOW ))) goto done; ++ } ++ ++ strcpy( ext, ".so" ); ++ for (i = 0; dll_paths[i]; i++) ++ { ++ ptr = prepend( file + pos, so_dir, strlen(so_dir) ); ++ ptr = prepend( ptr, dll_paths[i], strlen(dll_paths[i]) ); ++ WARN_(module)( "load_unixlib_by_name: trying %s\n", ptr ); ++ if ((handle = dlopen( ptr, RTLD_NOW ))) goto done; ++ ++ ptr = prepend( file + pos, dll_paths[i], strlen(dll_paths[i]) ); ++ WARN_(module)( "load_unixlib_by_name: trying %s\n", ptr ); ++ if ((handle = dlopen( ptr, RTLD_NOW ))) goto done; ++ } ++ WARN_(module)( "load_unixlib_by_name: no unixlib found for %s\n", file + pos ); ++ ++ done: ++ free( file ); ++ if (!handle) return STATUS_DLL_NOT_FOUND; ++ *handle_ret = handle; ++ return STATUS_SUCCESS; ++} ++ ++ + /*************************************************************************** + * get_machine_wow64_dir + * +@@ -2451,7 +2548,9 @@ static void start_main_thread(void) set_thread_teb( teb ); #endif -+#if defined(M_PERTURB) ++#ifndef __ANDROID__ mallopt( M_PERTURB, 0xff ); +#endif init_startup_info(); *(ULONG_PTR *)&peb->CloudFileFlags = get_image_address(); set_load_order_app_name( main_wargv[0] ); -@@ -2460,7 +2466,9 @@ static void start_main_thread(void) +@@ -2460,7 +2559,9 @@ static void start_main_thread(void) load_ntdll(); load_wow64_ntdll( main_image_info.Machine ); load_apiset_dll(); -+#if defined(M_PERTURB) ++#ifndef __ANDROID__ mallopt( M_PERTURB, 0 ); +#endif server_init_process_done(); diff --git a/android/patches/arm64ec/dlls_ntdll_unix_virtual_c.patch b/android/patches/arm64ec/dlls_ntdll_unix_virtual_c.patch index bfc05de8369..db2b794dca3 100644 --- a/android/patches/arm64ec/dlls_ntdll_unix_virtual_c.patch +++ b/android/patches/arm64ec/dlls_ntdll_unix_virtual_c.patch @@ -1,5 +1,5 @@ diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index d5b93415c2a..8f55ebf95f0 100644 +index d5b9341..82c5931 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -100,6 +100,10 @@ @@ -13,7 +13,16 @@ index d5b93415c2a..8f55ebf95f0 100644 WINE_DEFAULT_DEBUG_CHANNEL(virtual); WINE_DECLARE_DEBUG_CHANNEL(module); WINE_DECLARE_DEBUG_CHANNEL(virtual_ranges); -@@ -373,6 +377,9 @@ static void *preload_reserve_end; +@@ -325,6 +329,8 @@ static void *working_set_limit = (void *)0x7fff0000; + + static void *host_addr_space_limit; /* top of the host virtual address space */ + ++static SIZE_T vmem_max_size = 0; /* WINEVMEMMAXSIZE: cap on a single VA allocation, in bytes (0 = unlimited) */ ++ + static struct file_view *arm64ec_view; + static const ptrdiff_t max_try_map_step = 0x40000000; + static BOOL increase_try_map_step = TRUE; +@@ -373,6 +379,9 @@ static void *preload_reserve_end; static BOOL force_exec_prot; /* whether to force PROT_EXEC on all PROT_READ mmaps */ static BOOL enable_write_exceptions; /* raise exception on writes to executable memory */ @@ -23,7 +32,7 @@ index d5b93415c2a..8f55ebf95f0 100644 struct range_entry { void *base; -@@ -413,6 +420,7 @@ void *anon_mmap_alloc( size_t size, int prot ) +@@ -413,6 +422,7 @@ void *anon_mmap_alloc( size_t size, int prot ) #ifdef USE_UFFD_WRITEWATCH static void kernel_writewatch_init(void) { @@ -31,7 +40,7 @@ index d5b93415c2a..8f55ebf95f0 100644 struct uffdio_api uffdio_api; uffd_fd = syscall( __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY ); -@@ -434,6 +442,10 @@ static void kernel_writewatch_init(void) +@@ -434,6 +444,10 @@ static void kernel_writewatch_init(void) } use_kernel_writewatch = 1; TRACE( "Using kernel write watches.\n" ); @@ -42,7 +51,179 @@ index d5b93415c2a..8f55ebf95f0 100644 } static void kernel_writewatch_reset( void *start, SIZE_T len ) -@@ -6558,6 +6570,49 @@ static unsigned int get_memory_image_info( HANDLE process, LPCVOID addr, MEMORY_ +@@ -987,6 +1001,18 @@ static void load_steam_overlay(const char *unix_lib_path) + } + } + ++/*********************************************************************** ++ * get_unixlib_funcs ++ */ ++static NTSTATUS get_unixlib_funcs( void *so_handle, BOOL wow, const void **funcs ) ++{ ++ const char *name = wow ? "__wine_unix_call_wow64_funcs" : "__wine_unix_call_funcs"; ++ ++ *funcs = dlsym( so_handle, name ); ++ return *funcs ? STATUS_SUCCESS : STATUS_ENTRYPOINT_NOT_FOUND; ++} ++ ++ + /*********************************************************************** + * get_builtin_unix_funcs + */ +@@ -2579,7 +2605,8 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, + * virtual_mutex must be held by caller. + */ + static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start, size_t size, +- off_t offset, unsigned int vprot, BOOL removable ) ++ off_t offset, unsigned int vprot, BOOL removable, ++ BOOL force_anon ) + { + char *map_addr, *host_addr; + size_t map_size, host_size; +@@ -2603,6 +2630,16 @@ static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start + #endif + } + ++ /* On strict-W^X kernels (e.g. Android arm64) and noexec filesystems (e.g. ++ * an FAT/exFAT SD card), a file-backed MAP_PRIVATE mapping that was ever ++ * writable cannot later be flipped to PROT_EXEC, which breaks Wine's PE ++ * loader after relocations. When the caller passes force_anon=TRUE for a ++ * private mapping, mark it removable so we skip the file mmap below and ++ * pread the contents into the existing anonymous view instead. Anonymous ++ * mappings have no W^X restriction. Shared mappings still need a real file ++ * mmap and must always pass force_anon=FALSE. */ ++ if (force_anon && (flags & MAP_PRIVATE)) removable = TRUE; ++ + map_size = ROUND_SIZE( start, size, page_mask ); + map_addr = ROUND_ADDR( (char *)view->base + start, page_mask ); + host_addr = ROUND_ADDR( (char *)view->base + start, host_page_mask ); +@@ -2959,10 +2996,15 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot + * + * Map the header of a PE file into memory. + */ +-static NTSTATUS map_pe_header( void *ptr, size_t size, size_t map_size, int fd, BOOL *removable ) ++static NTSTATUS map_pe_header( void *ptr, size_t size, size_t map_size, int fd, BOOL *removable, ++ BOOL force_anon ) + { + if (!size) return STATUS_INVALID_IMAGE_FORMAT; + ++ /* Force the pread fallback path when the caller wants the entire PE image ++ * to come up as anonymous (W^X-safe) memory. */ ++ if (force_anon) *removable = TRUE; ++ + map_size &= ~host_page_mask; + + if (!*removable && map_size) +@@ -3261,7 +3303,7 @@ static IMAGE_BASE_RELOCATION *process_relocation_block( char *page, IMAGE_BASE_R + */ + static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRING *nt_name, int fd, + struct pe_image_info *image_info, USHORT machine, +- int shared_fd, BOOL removable ) ++ int shared_fd, BOOL removable, BOOL force_anon ) + { + IMAGE_DOS_HEADER *dos; + IMAGE_NT_HEADERS *nt; +@@ -3284,7 +3326,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + fstat( fd, &st ); + header_size = min( image_info->header_size, st.st_size ); + header_map_size = min( image_info->header_map_size, ROUND_SIZE( 0, st.st_size, host_page_mask )); +- if ((status = map_pe_header( view->base, header_size, header_map_size, fd, &removable ))) ++ if ((status = map_pe_header( view->base, header_size, header_map_size, fd, &removable, force_anon ))) + return status; + + status = STATUS_INVALID_IMAGE_FORMAT; /* generic error */ +@@ -3314,7 +3356,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + + total_size = min( total_size, ROUND_SIZE( 0, st.st_size, page_mask )); + if (map_file_into_view( view, fd, 0, total_size, 0, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, +- removable ) != STATUS_SUCCESS) goto done; ++ removable, force_anon ) != STATUS_SUCCESS) goto done; + + /* check that all sections are loaded at the right offset */ + if (nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment) goto done; +@@ -3367,7 +3409,8 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + sec[i].PointerToRawData, (int)pos, file_size, map_size, + sec[i].Characteristics ); + if (map_file_into_view( view, shared_fd, sec[i].VirtualAddress, map_size, pos, +- VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE ) != STATUS_SUCCESS) ++ VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE, ++ FALSE /* MAP_SHARED, never anon */ ) != STATUS_SUCCESS) + { + ERR_(module)( "Could not map %s shared section %.8s\n", debugstr_us(nt_name), sec[i].Name ); + goto done; +@@ -3383,7 +3426,8 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + if (end > base) + map_file_into_view( view, shared_fd, base, end - base, + pos + (base - sec[i].VirtualAddress), +- VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, FALSE ); ++ VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, FALSE, ++ force_anon ); + } + pos += map_size; + continue; +@@ -3405,7 +3449,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + end < file_start || + map_file_into_view( view, fd, sec[i].VirtualAddress, file_size, file_start, + VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, +- removable ) != STATUS_SUCCESS) ++ removable, force_anon ) != STATUS_SUCCESS) + { + ERR_(module)( "Could not map %s section %.8s, file probably truncated\n", + debugstr_us(nt_name), sec[i].Name ); +@@ -3660,7 +3704,11 @@ static NTSTATUS virtual_map_image( HANDLE mapping, void **addr_ptr, SIZE_T *size + status = map_image_view( &view, image_info, size, limit_low, limit_high, alloc_type ); + if (status) goto done; + +- status = map_image_into_view( view, nt_name, unix_fd, image_info, machine, shared_fd, needs_close ); ++ /* Force PE images to come up as anonymous memory so later mprotect() calls ++ * that flip text sections to PROT_EXEC succeed on strict-W^X kernels ++ * (e.g. Android arm64) and noexec filesystems (e.g. an SD card). */ ++ status = map_image_into_view( view, nt_name, unix_fd, image_info, machine, shared_fd, ++ needs_close, TRUE ); + if (status == STATUS_SUCCESS) + { + if (offset) +@@ -3805,7 +3853,7 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P + if (res) goto done; + + TRACE( "handle=%p size=%lx offset=%s\n", handle, size, wine_dbgstr_longlong(offset.QuadPart) ); +- res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, needs_close ); ++ res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, needs_close, FALSE ); + if (res == STATUS_SUCCESS) + { + /* file mappings must always be accessible */ +@@ -3884,6 +3932,7 @@ void virtual_init(void) + const struct preload_info **preload_info = dlsym( RTLD_DEFAULT, "wine_main_preload_info" ); + struct r_debug **r_debug = dlsym( RTLD_DEFAULT, "wine_r_debug" ); + const char *preload; ++ const char *vmem_max_size_env; + size_t size; + int i; + pthread_mutexattr_t attr; +@@ -3930,6 +3979,12 @@ void virtual_init(void) + unsetenv( "WINEPRELOADRESERVE" ); + } + ++ if ((vmem_max_size_env = getenv( "WINEVMEMMAXSIZE" ))) ++ { ++ vmem_max_size = (SIZE_T)strtol( vmem_max_size_env, NULL, 10 ) << 20; ++ TRACE( "virtual memory max size: %ld\n", (long)vmem_max_size ); ++ } ++ + /* try to find space in a reserved area for the views and pages protection table */ + #ifdef _WIN64 + pages_vprot_size = ((size_t)host_addr_space_limit >> page_shift >> pages_vprot_shift) + 1; +@@ -5435,7 +5490,8 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ + + /* Round parameters to a page boundary */ + +- if (is_beyond_limit( 0, size, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE; ++ if (is_beyond_limit( 0, size, working_set_limit ) || (vmem_max_size > 0 && size > vmem_max_size)) ++ return STATUS_WORKING_SET_LIMIT_RANGE; + + if (*ret) + { +@@ -6558,6 +6614,49 @@ static unsigned int get_memory_image_info( HANDLE process, LPCVOID addr, MEMORY_ return status; } @@ -92,7 +273,7 @@ index d5b93415c2a..8f55ebf95f0 100644 /*********************************************************************** * NtQueryVirtualMemory (NTDLL.@) -@@ -6603,6 +6658,13 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr, +@@ -6603,6 +6702,44 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr, } return STATUS_INVALID_HANDLE; @@ -103,6 +284,37 @@ index d5b93415c2a..8f55ebf95f0 100644 + FIXME("MemoryFexStatsShm unsupported\n"); + break; +#endif ++ case MemoryWineLoadUnixLibByName: ++ case MemoryWineLoadUnixLibByNameWow64: ++ if (process == GetCurrentProcess()) ++ { ++ UINT64 res[2]; ++ const UNICODE_STRING *name = addr; ++ const void *funcs; ++ void *handle; ++ ++ if ((status = load_unixlib_by_name( name, &handle ))) return status; ++ res[0] = (UINT_PTR)handle; ++ if (len >= sizeof(res)) ++ { ++ if (!(status = get_unixlib_funcs( handle, info_class == MemoryWineLoadUnixLibByNameWow64, &funcs ))) ++ res[1] = (UINT_PTR)funcs; ++ } ++ if (status) dlclose( handle ); ++ else memcpy( buffer, res, min( len, sizeof(res) )); ++ return status; ++ } ++ return STATUS_INVALID_HANDLE; ++ ++ case MemoryWineUnloadUnixLib: ++ if (process == GetCurrentProcess()) ++ { ++ const unixlib_module_t *handle = addr; ++ ++ if (!dlclose( (void *)(UINT_PTR)*handle )) return STATUS_SUCCESS; ++ } ++ return STATUS_INVALID_HANDLE; ++ default: FIXME("(%p,%p,info_class=%d,%p,%ld,%p) Unknown information class\n", process, addr, info_class, buffer, len, res_len); diff --git a/android/patches/common/dlls_ntdll_unix_env_c.patch b/android/patches/common/dlls_ntdll_unix_env_c.patch new file mode 100644 index 00000000000..b8109d4cdfb --- /dev/null +++ b/android/patches/common/dlls_ntdll_unix_env_c.patch @@ -0,0 +1,20 @@ +diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c +index 042daa2..050d183 100644 +--- a/dlls/ntdll/unix/env.c ++++ b/dlls/ntdll/unix/env.c +@@ -848,6 +848,15 @@ void init_environment(void) + { + USHORT *case_table; + ++#ifdef __ANDROID__ ++ /* bionic (Android) ships no locale data beyond "C"/"C.UTF-8"; a bare ++ * setlocale(LC_*, "") therefore falls back to the 7-bit "C" locale, which ++ * breaks UTF-8 codeset detection (init_unix_codepage) and NLS conversion. ++ * Default LC_ALL to a UTF-8 capable locale before any setlocale() runs; ++ * the trailing 0 leaves an explicit environment value untouched. */ ++ setenv( "LC_ALL", "C.UTF-8", 0 ); ++#endif ++ + init_unix_codepage(); + init_locale(); + diff --git a/android/patches/common/dlls_ntdll_unix_sync_c.patch b/android/patches/common/dlls_ntdll_unix_sync_c.patch index 722e981ddf5..f012795725b 100644 --- a/android/patches/common/dlls_ntdll_unix_sync_c.patch +++ b/android/patches/common/dlls_ntdll_unix_sync_c.patch @@ -1,5 +1,5 @@ diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c -index 67ab945cef8..47f09ccd1f3 100644 +index 67ab945..d4c6657 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -61,7 +61,9 @@ @@ -215,3 +215,23 @@ index 67ab945cef8..47f09ccd1f3 100644 } } SERVER_END_REQ; +@@ -2426,6 +2588,19 @@ NTSTATUS WINAPI NtSignalAndWaitForSingleObject( HANDLE signal, HANDLE wait, + */ + NTSTATUS WINAPI NtYieldExecution(void) + { ++ static int fast_yield = -1; ++ ++ if (fast_yield < 0) ++ { ++ const char *e = getenv( "WINE_FAST_YIELD" ); ++ fast_yield = e && atoi( e ); ++ } ++ if (fast_yield) ++ { ++ usleep( 0 ); ++ return STATUS_SUCCESS; ++ } ++ + #ifdef HAVE_SCHED_YIELD + #ifdef RUSAGE_THREAD + struct rusage u1, u2; diff --git a/android/patches/common/dlls_ntdll_unix_unix_private_h.patch b/android/patches/common/dlls_ntdll_unix_unix_private_h.patch new file mode 100644 index 00000000000..9b4fd2ccfde --- /dev/null +++ b/android/patches/common/dlls_ntdll_unix_unix_private_h.patch @@ -0,0 +1,19 @@ +diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h +index 2f649ca..8700313 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -232,6 +232,7 @@ extern NTSTATUS exec_wineloader( char **argv, int socketfd, const struct pe_imag + extern NTSTATUS load_builtin( const struct pe_image_info *image_info, UNICODE_STRING *nt_name, + ANSI_STRING *exp_name, USHORT machine, SECTION_IMAGE_INFORMATION *info, + void **module, SIZE_T *size, ULONG_PTR limit_low, ULONG_PTR limit_high, off_t offset ); ++extern NTSTATUS load_unixlib_by_name( const UNICODE_STRING *nt_name, void **handle_ret ); + extern BOOL is_builtin_path( const UNICODE_STRING *path, WORD *machine ); + extern NTSTATUS load_main_exe( UNICODE_STRING *nt_name, USHORT load_machine, void **module ); + extern NTSTATUS load_start_exe( UNICODE_STRING *nt_name, void **module ); +@@ -724,4 +725,6 @@ static inline int is_gdt_sel( WORD sel ) + + #endif /* defined(__i386__) || defined(__x86_64__) */ + ++void DECLSPEC_NOINLINE __wine_dbg_notify_syscall_fault(void); ++ + #endif /* __NTDLL_UNIX_PRIVATE_H */ diff --git a/android/patches/common/dlls_shell32_shlfileop_c.patch b/android/patches/common/dlls_shell32_shlfileop_c.patch new file mode 100644 index 00000000000..266a4775397 --- /dev/null +++ b/android/patches/common/dlls_shell32_shlfileop_c.patch @@ -0,0 +1,42 @@ +diff --git a/dlls/shell32/shlfileop.c b/dlls/shell32/shlfileop.c +index 3f4d441..a3c0e80 100644 +--- a/dlls/shell32/shlfileop.c ++++ b/dlls/shell32/shlfileop.c +@@ -955,14 +955,33 @@ static void file_entry_init(FILE_ENTRY *file_entry, + ptr = wcsrchr(file_name, '\\'); + if (ptr) + { +- file_name_len = ptr - file_name + 1; +- file_entry->szDirectory = malloc(file_name_len * sizeof(WCHAR)); +- lstrcpynW(file_entry->szDirectory, file_name, file_name_len); ++ size_t dir_len = ptr - file_name + 1; ++ ++ /* For a bare drive root ("D:\" or "\") keep the trailing root ++ * backslash in the directory; otherwise szDirectory becomes a bogus ++ * drive-relative spec ("D:") and downstream path building misbehaves ++ * when FO_COPY copies between two roots. */ ++ if (ptr == file_name || (ptr == file_name + 2 && file_name[1] == ':')) ++ dir_len++; + +- file_name_len = wcslen(file_entry->szFullPath) - file_name_len + 1; ++ file_entry->szDirectory = malloc(dir_len * sizeof(WCHAR)); ++ lstrcpynW(file_entry->szDirectory, file_name, dir_len); ++ ++ file_name_len = wcslen(ptr + 1) + 1; /* component after the backslash */ + file_entry->szFilename = malloc(file_name_len * sizeof(WCHAR)); + lstrcpyW(file_entry->szFilename, ptr + 1); /* Skip over backslash. */ + } ++ else ++ { ++ /* No path separator at all (e.g. a bare drive spec "D:"): never leave ++ * szDirectory / szFilename NULL, or do_copy_move's unconditional ++ * wcscpy(target_dir, to->szDirectory) faults when copying between ++ * drive roots. */ ++ file_entry->szDirectory = malloc(file_name_len * sizeof(WCHAR)); ++ lstrcpyW(file_entry->szDirectory, file_name); ++ file_entry->szFilename = malloc(file_name_len * sizeof(WCHAR)); ++ lstrcpyW(file_entry->szFilename, file_name); ++ } + + file_entry->attributes = attributes; + file_entry->bFromWildcard = from_wildcard; diff --git a/android/patches/common/dlls_wow64_virtual_c.patch b/android/patches/common/dlls_wow64_virtual_c.patch new file mode 100644 index 00000000000..b5cc13bf7f2 --- /dev/null +++ b/android/patches/common/dlls_wow64_virtual_c.patch @@ -0,0 +1,23 @@ +diff --git a/dlls/wow64/virtual.c b/dlls/wow64/virtual.c +index cbaf6dd..004bb0d 100644 +--- a/dlls/wow64/virtual.c ++++ b/dlls/wow64/virtual.c +@@ -694,11 +694,18 @@ NTSTATUS WINAPI wow64_NtQueryVirtualMemory( UINT *args ) + } + + case MemoryWineUnixWow64Funcs: ++ case MemoryWineLoadUnixLibByNameWow64: + return STATUS_INVALID_INFO_CLASS; + + case MemoryWineUnixFuncs: + status = NtQueryVirtualMemory( handle, addr, MemoryWineUnixWow64Funcs, ptr, len, &res_len ); + break; ++ case MemoryWineLoadUnixLibByName: ++ status = NtQueryVirtualMemory( handle, addr, MemoryWineLoadUnixLibByNameWow64, ptr, len, &res_len ); ++ break; ++ case MemoryWineUnloadUnixLib: ++ status = NtQueryVirtualMemory( handle, addr, class, ptr, len, &res_len ); ++ break; + + default: + FIXME( "unsupported class %u\n", class ); diff --git a/android/patches/common/include_wine_unixlib_h.patch b/android/patches/common/include_wine_unixlib_h.patch new file mode 100644 index 00000000000..7e3816190b4 --- /dev/null +++ b/android/patches/common/include_wine_unixlib_h.patch @@ -0,0 +1,12 @@ +diff --git a/include/wine/unixlib.h b/include/wine/unixlib.h +index 364625b..ddd28b2 100644 +--- a/include/wine/unixlib.h ++++ b/include/wine/unixlib.h +@@ -28,6 +28,7 @@ + #include + + typedef UINT64 unixlib_handle_t; ++typedef UINT64 unixlib_module_t; + + #ifdef WINE_UNIX_LIB + diff --git a/android/patches/test-bylaws/include_winternl_h.patch b/android/patches/common/include_winternl_h.patch similarity index 80% rename from android/patches/test-bylaws/include_winternl_h.patch rename to android/patches/common/include_winternl_h.patch index 8338247fed2..f77d207c9eb 100644 --- a/android/patches/test-bylaws/include_winternl_h.patch +++ b/android/patches/common/include_winternl_h.patch @@ -1,5 +1,5 @@ diff --git a/include/winternl.h b/include/winternl.h -index a7ba1c12974..5f39d3e7879 100644 +index a7ba1c1..acd547f 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2037,8 +2037,15 @@ typedef enum _PROCESSINFOCLASS { @@ -18,15 +18,19 @@ index a7ba1c12974..5f39d3e7879 100644 #define MEM_EXECUTE_OPTION_DISABLE 0x01 #define MEM_EXECUTE_OPTION_ENABLE 0x02 #define MEM_EXECUTE_OPTION_DISABLE_THUNK_EMULATION 0x04 -@@ -2460,6 +2467,7 @@ typedef enum _MEMORY_INFORMATION_CLASS { +@@ -2459,7 +2466,11 @@ typedef enum _MEMORY_INFORMATION_CLASS { + #ifdef __WINESRC__ MemoryWineUnixFuncs = 1000, MemoryWineUnixWow64Funcs, ++ MemoryWineLoadUnixLibByName, ++ MemoryWineLoadUnixLibByNameWow64, ++ MemoryWineUnloadUnixLib, #endif + MemoryFexStatsShm = 2000, } MEMORY_INFORMATION_CLASS; typedef struct _MEMORY_SECTION_NAME -@@ -2509,6 +2517,12 @@ typedef struct _MEMORY_REGION_INFORMATION +@@ -2509,6 +2520,12 @@ typedef struct _MEMORY_REGION_INFORMATION ULONG_PTR NodePreference; } MEMORY_REGION_INFORMATION, *PMEMORY_REGION_INFORMATION; diff --git a/android/patches/test-bylaws/dlls_ntdll_unix_unix_private_h.patch b/android/patches/test-bylaws/dlls_ntdll_unix_unix_private_h.patch deleted file mode 100644 index 5382c00450a..00000000000 --- a/android/patches/test-bylaws/dlls_ntdll_unix_unix_private_h.patch +++ /dev/null @@ -1,11 +0,0 @@ -diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h -index 2f649ca399e..cd490295ee6 100644 ---- a/dlls/ntdll/unix/unix_private.h -+++ b/dlls/ntdll/unix/unix_private.h -@@ -724,4 +724,6 @@ static inline int is_gdt_sel( WORD sel ) - - #endif /* defined(__i386__) || defined(__x86_64__) */ - -+void DECLSPEC_NOINLINE __wine_dbg_notify_syscall_fault(void); -+ - #endif /* __NTDLL_UNIX_PRIVATE_H */ diff --git a/android/patches/x86_64/dlls_ntdll_unix_loader_c.patch b/android/patches/x86_64/dlls_ntdll_unix_loader_c.patch index ffa950e4bbb..caf224fc774 100644 --- a/android/patches/x86_64/dlls_ntdll_unix_loader_c.patch +++ b/android/patches/x86_64/dlls_ntdll_unix_loader_c.patch @@ -1,8 +1,129 @@ diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c -index f8aa468..bce79b8 100644 +index f8aa468..520d5e2 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c -@@ -2451,7 +2451,9 @@ static void start_main_thread(void) +@@ -362,6 +362,7 @@ static WORD get_alt_machine( WORD machine ) + static void set_dll_path(void) + { + char *p, *path = getenv( "WINEDLLPATH" ), *be_runtime = getenv( "PROTON_BATTLEYE_RUNTIME" ), *eac_runtime = getenv( "PROTON_EAC_RUNTIME" ); ++ char *prefix = getenv( "PREFIX" ); + int i, count = 0; + + if (path) for (p = path, count = 1; *p; p++) if (*p == ':') count++; +@@ -372,6 +373,9 @@ static void set_dll_path(void) + if (eac_runtime) + count += 2; + ++ if (prefix && *prefix) ++ count += 1; ++ + dll_paths = malloc( (count + 2) * sizeof(*dll_paths) ); + count = 0; + +@@ -420,6 +424,20 @@ static void set_dll_path(void) + dll_paths[count++] = p; + } + ++ /* Winlator-bionic containers set PREFIX to the imagefs mount; components ++ * (e.g. a FEX-unix companion) drop their unixlibs under $PREFIX/lib/wine. ++ * Add it as the lowest-priority search dir so the builtin's own dir wins. */ ++ if (prefix && *prefix) ++ { ++ const char suffix[] = "/lib/wine"; ++ ++ p = malloc( strlen(prefix) + strlen(suffix) + 1 ); ++ strcpy(p, prefix); ++ strcat(p, suffix); ++ ++ dll_paths[count++] = p; ++ } ++ + for (i = 0; i < count; i++) dll_path_maxlen = max( dll_path_maxlen, strlen(dll_paths[i]) ); + dll_paths[count] = NULL; + } +@@ -1785,6 +1803,81 @@ NTSTATUS load_builtin( const struct pe_image_info *image_info, UNICODE_STRING *n + } + + ++/*********************************************************************** ++ * load_unixlib_by_name ++ */ ++NTSTATUS load_unixlib_by_name( const UNICODE_STRING *nt_name, void **handle_ret ) ++{ ++ unsigned int i, pos, namepos, maxlen = 0; ++ unsigned int len = nt_name->Length / sizeof(WCHAR); ++ const char *so_dir = get_so_dir( current_machine ); ++ char *ptr = NULL, *file, *ext = NULL; ++ void *handle = NULL; ++ ++ if (!len) return STATUS_DLL_NOT_FOUND; ++ ++ for (i = namepos = 0; i < len; i++) ++ if (nt_name->Buffer[i] == '/' || nt_name->Buffer[i] == '\\') break; ++ ++ if (i < len) /* explicit path */ ++ { ++ UNICODE_STRING true_nt_name; ++ OBJECT_ATTRIBUTES attr; ++ ++ InitializeObjectAttributes( &attr, (UNICODE_STRING *)nt_name, 0, 0, NULL ); ++ if (!get_nt_and_unix_names( &attr, &true_nt_name, &file, FILE_OPEN, FALSE )) ++ handle = dlopen( file, RTLD_NOW ); ++ free( true_nt_name.Buffer ); ++ goto done; ++ } ++ ++ if (build_dir) maxlen = strlen(build_dir) + sizeof("/dlls/") + len; ++ maxlen = max( maxlen, dll_path_maxlen + 1 ) + len + sizeof("/aarch64-unix") + sizeof(".so"); ++ ++ if (!(file = malloc( maxlen ))) return STATUS_NO_MEMORY; ++ ++ pos = maxlen - len - 4; ++ ext = file + pos + len; ++ /* we don't want to depend on the current codepage here */ ++ for (i = 0; i < len; i++) ++ { ++ if (nt_name->Buffer[namepos + i] > 127) goto done; ++ file[pos + i] = (char)nt_name->Buffer[namepos + i]; ++ if (file[pos + i] >= 'A' && file[pos + i] <= 'Z') file[pos + i] += 'a' - 'A'; ++ else if (file[pos + i] == '.') ext = file + pos + i; ++ } ++ file[pos + len] = 0; ++ file[--pos] = '/'; ++ ++ if (build_dir) ++ { ++ ptr = prepend_build_dir_path( file + pos, ".so", "", "/dlls", build_dir ); ++ strcpy( ext, ".so" ); ++ if ((handle = dlopen( ptr, RTLD_NOW ))) goto done; ++ } ++ ++ strcpy( ext, ".so" ); ++ for (i = 0; dll_paths[i]; i++) ++ { ++ ptr = prepend( file + pos, so_dir, strlen(so_dir) ); ++ ptr = prepend( ptr, dll_paths[i], strlen(dll_paths[i]) ); ++ WARN_(module)( "load_unixlib_by_name: trying %s\n", ptr ); ++ if ((handle = dlopen( ptr, RTLD_NOW ))) goto done; ++ ++ ptr = prepend( file + pos, dll_paths[i], strlen(dll_paths[i]) ); ++ WARN_(module)( "load_unixlib_by_name: trying %s\n", ptr ); ++ if ((handle = dlopen( ptr, RTLD_NOW ))) goto done; ++ } ++ WARN_(module)( "load_unixlib_by_name: no unixlib found for %s\n", file + pos ); ++ ++ done: ++ free( file ); ++ if (!handle) return STATUS_DLL_NOT_FOUND; ++ *handle_ret = handle; ++ return STATUS_SUCCESS; ++} ++ ++ + /*************************************************************************** + * get_machine_wow64_dir + * +@@ -2451,7 +2544,9 @@ static void start_main_thread(void) set_thread_teb( teb ); #endif @@ -12,7 +133,7 @@ index f8aa468..bce79b8 100644 init_startup_info(); *(ULONG_PTR *)&peb->CloudFileFlags = get_image_address(); set_load_order_app_name( main_wargv[0] ); -@@ -2460,7 +2462,9 @@ static void start_main_thread(void) +@@ -2460,7 +2555,9 @@ static void start_main_thread(void) load_ntdll(); load_wow64_ntdll( main_image_info.Machine ); load_apiset_dll(); diff --git a/android/patches/x86_64/dlls_ntdll_unix_virtual_c.patch b/android/patches/x86_64/dlls_ntdll_unix_virtual_c.patch index fcd78f510f5..73bc50a095a 100644 --- a/android/patches/x86_64/dlls_ntdll_unix_virtual_c.patch +++ b/android/patches/x86_64/dlls_ntdll_unix_virtual_c.patch @@ -1,5 +1,5 @@ diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c -index d5b9341..cf7bab5 100644 +index d5b9341..b9be293 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -314,9 +314,15 @@ static void *address_space_start = (void *)0x110000; /* keep DOS area clear */ @@ -18,7 +18,16 @@ index d5b9341..cf7bab5 100644 #else static void *address_space_limit = (void *)0xc0000000; static void *user_space_limit = (void *)0x7fff0000; -@@ -413,6 +419,7 @@ void *anon_mmap_alloc( size_t size, int prot ) +@@ -325,6 +331,8 @@ static void *working_set_limit = (void *)0x7fff0000; + + static void *host_addr_space_limit; /* top of the host virtual address space */ + ++static SIZE_T vmem_max_size = 0; /* WINEVMEMMAXSIZE: cap on a single VA allocation, in bytes (0 = unlimited) */ ++ + static struct file_view *arm64ec_view; + static const ptrdiff_t max_try_map_step = 0x40000000; + static BOOL increase_try_map_step = TRUE; +@@ -413,6 +421,7 @@ void *anon_mmap_alloc( size_t size, int prot ) #ifdef USE_UFFD_WRITEWATCH static void kernel_writewatch_init(void) { @@ -26,7 +35,7 @@ index d5b9341..cf7bab5 100644 struct uffdio_api uffdio_api; uffd_fd = syscall( __NR_userfaultfd, O_CLOEXEC | O_NONBLOCK | UFFD_USER_MODE_ONLY ); -@@ -434,6 +441,10 @@ static void kernel_writewatch_init(void) +@@ -434,6 +443,10 @@ static void kernel_writewatch_init(void) } use_kernel_writewatch = 1; TRACE( "Using kernel write watches.\n" ); @@ -37,3 +46,213 @@ index d5b9341..cf7bab5 100644 } static void kernel_writewatch_reset( void *start, SIZE_T len ) +@@ -987,6 +1000,18 @@ static void load_steam_overlay(const char *unix_lib_path) + } + } + ++/*********************************************************************** ++ * get_unixlib_funcs ++ */ ++static NTSTATUS get_unixlib_funcs( void *so_handle, BOOL wow, const void **funcs ) ++{ ++ const char *name = wow ? "__wine_unix_call_wow64_funcs" : "__wine_unix_call_funcs"; ++ ++ *funcs = dlsym( so_handle, name ); ++ return *funcs ? STATUS_SUCCESS : STATUS_ENTRYPOINT_NOT_FOUND; ++} ++ ++ + /*********************************************************************** + * get_builtin_unix_funcs + */ +@@ -2579,7 +2604,8 @@ static NTSTATUS map_view( struct file_view **view_ret, void *base, size_t size, + * virtual_mutex must be held by caller. + */ + static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start, size_t size, +- off_t offset, unsigned int vprot, BOOL removable ) ++ off_t offset, unsigned int vprot, BOOL removable, ++ BOOL force_anon ) + { + char *map_addr, *host_addr; + size_t map_size, host_size; +@@ -2603,6 +2629,16 @@ static NTSTATUS map_file_into_view( struct file_view *view, int fd, size_t start + #endif + } + ++ /* On strict-W^X kernels (e.g. Android arm64) and noexec filesystems (e.g. ++ * an FAT/exFAT SD card), a file-backed MAP_PRIVATE mapping that was ever ++ * writable cannot later be flipped to PROT_EXEC, which breaks Wine's PE ++ * loader after relocations. When the caller passes force_anon=TRUE for a ++ * private mapping, mark it removable so we skip the file mmap below and ++ * pread the contents into the existing anonymous view instead. Anonymous ++ * mappings have no W^X restriction. Shared mappings still need a real file ++ * mmap and must always pass force_anon=FALSE. */ ++ if (force_anon && (flags & MAP_PRIVATE)) removable = TRUE; ++ + map_size = ROUND_SIZE( start, size, page_mask ); + map_addr = ROUND_ADDR( (char *)view->base + start, page_mask ); + host_addr = ROUND_ADDR( (char *)view->base + start, host_page_mask ); +@@ -2959,10 +2995,15 @@ static NTSTATUS allocate_dos_memory( struct file_view **view, unsigned int vprot + * + * Map the header of a PE file into memory. + */ +-static NTSTATUS map_pe_header( void *ptr, size_t size, size_t map_size, int fd, BOOL *removable ) ++static NTSTATUS map_pe_header( void *ptr, size_t size, size_t map_size, int fd, BOOL *removable, ++ BOOL force_anon ) + { + if (!size) return STATUS_INVALID_IMAGE_FORMAT; + ++ /* Force the pread fallback path when the caller wants the entire PE image ++ * to come up as anonymous (W^X-safe) memory. */ ++ if (force_anon) *removable = TRUE; ++ + map_size &= ~host_page_mask; + + if (!*removable && map_size) +@@ -3261,7 +3302,7 @@ static IMAGE_BASE_RELOCATION *process_relocation_block( char *page, IMAGE_BASE_R + */ + static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRING *nt_name, int fd, + struct pe_image_info *image_info, USHORT machine, +- int shared_fd, BOOL removable ) ++ int shared_fd, BOOL removable, BOOL force_anon ) + { + IMAGE_DOS_HEADER *dos; + IMAGE_NT_HEADERS *nt; +@@ -3284,7 +3325,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + fstat( fd, &st ); + header_size = min( image_info->header_size, st.st_size ); + header_map_size = min( image_info->header_map_size, ROUND_SIZE( 0, st.st_size, host_page_mask )); +- if ((status = map_pe_header( view->base, header_size, header_map_size, fd, &removable ))) ++ if ((status = map_pe_header( view->base, header_size, header_map_size, fd, &removable, force_anon ))) + return status; + + status = STATUS_INVALID_IMAGE_FORMAT; /* generic error */ +@@ -3314,7 +3355,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + + total_size = min( total_size, ROUND_SIZE( 0, st.st_size, page_mask )); + if (map_file_into_view( view, fd, 0, total_size, 0, VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, +- removable ) != STATUS_SUCCESS) goto done; ++ removable, force_anon ) != STATUS_SUCCESS) goto done; + + /* check that all sections are loaded at the right offset */ + if (nt->OptionalHeader.FileAlignment != nt->OptionalHeader.SectionAlignment) goto done; +@@ -3367,7 +3408,8 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + sec[i].PointerToRawData, (int)pos, file_size, map_size, + sec[i].Characteristics ); + if (map_file_into_view( view, shared_fd, sec[i].VirtualAddress, map_size, pos, +- VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE ) != STATUS_SUCCESS) ++ VPROT_COMMITTED | VPROT_READ | VPROT_WRITE, FALSE, ++ FALSE /* MAP_SHARED, never anon */ ) != STATUS_SUCCESS) + { + ERR_(module)( "Could not map %s shared section %.8s\n", debugstr_us(nt_name), sec[i].Name ); + goto done; +@@ -3383,7 +3425,8 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + if (end > base) + map_file_into_view( view, shared_fd, base, end - base, + pos + (base - sec[i].VirtualAddress), +- VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, FALSE ); ++ VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, FALSE, ++ force_anon ); + } + pos += map_size; + continue; +@@ -3405,7 +3448,7 @@ static NTSTATUS map_image_into_view( struct file_view *view, const UNICODE_STRIN + end < file_start || + map_file_into_view( view, fd, sec[i].VirtualAddress, file_size, file_start, + VPROT_COMMITTED | VPROT_READ | VPROT_WRITECOPY, +- removable ) != STATUS_SUCCESS) ++ removable, force_anon ) != STATUS_SUCCESS) + { + ERR_(module)( "Could not map %s section %.8s, file probably truncated\n", + debugstr_us(nt_name), sec[i].Name ); +@@ -3660,7 +3703,11 @@ static NTSTATUS virtual_map_image( HANDLE mapping, void **addr_ptr, SIZE_T *size + status = map_image_view( &view, image_info, size, limit_low, limit_high, alloc_type ); + if (status) goto done; + +- status = map_image_into_view( view, nt_name, unix_fd, image_info, machine, shared_fd, needs_close ); ++ /* Force PE images to come up as anonymous memory so later mprotect() calls ++ * that flip text sections to PROT_EXEC succeed on strict-W^X kernels ++ * (e.g. Android arm64) and noexec filesystems (e.g. an SD card). */ ++ status = map_image_into_view( view, nt_name, unix_fd, image_info, machine, shared_fd, ++ needs_close, TRUE ); + if (status == STATUS_SUCCESS) + { + if (offset) +@@ -3805,7 +3852,7 @@ static unsigned int virtual_map_section( HANDLE handle, PVOID *addr_ptr, ULONG_P + if (res) goto done; + + TRACE( "handle=%p size=%lx offset=%s\n", handle, size, wine_dbgstr_longlong(offset.QuadPart) ); +- res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, needs_close ); ++ res = map_file_into_view( view, unix_handle, 0, size, offset.QuadPart, vprot, needs_close, FALSE ); + if (res == STATUS_SUCCESS) + { + /* file mappings must always be accessible */ +@@ -3884,6 +3931,7 @@ void virtual_init(void) + const struct preload_info **preload_info = dlsym( RTLD_DEFAULT, "wine_main_preload_info" ); + struct r_debug **r_debug = dlsym( RTLD_DEFAULT, "wine_r_debug" ); + const char *preload; ++ const char *vmem_max_size_env; + size_t size; + int i; + pthread_mutexattr_t attr; +@@ -3930,6 +3978,12 @@ void virtual_init(void) + unsetenv( "WINEPRELOADRESERVE" ); + } + ++ if ((vmem_max_size_env = getenv( "WINEVMEMMAXSIZE" ))) ++ { ++ vmem_max_size = (SIZE_T)strtol( vmem_max_size_env, NULL, 10 ) << 20; ++ TRACE( "virtual memory max size: %ld\n", (long)vmem_max_size ); ++ } ++ + /* try to find space in a reserved area for the views and pages protection table */ + #ifdef _WIN64 + pages_vprot_size = ((size_t)host_addr_space_limit >> page_shift >> pages_vprot_shift) + 1; +@@ -5435,7 +5489,8 @@ static NTSTATUS allocate_virtual_memory( void **ret, SIZE_T *size_ptr, ULONG typ + + /* Round parameters to a page boundary */ + +- if (is_beyond_limit( 0, size, working_set_limit )) return STATUS_WORKING_SET_LIMIT_RANGE; ++ if (is_beyond_limit( 0, size, working_set_limit ) || (vmem_max_size > 0 && size > vmem_max_size)) ++ return STATUS_WORKING_SET_LIMIT_RANGE; + + if (*ret) + { +@@ -6603,6 +6658,37 @@ NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr, + } + return STATUS_INVALID_HANDLE; + ++ case MemoryWineLoadUnixLibByName: ++ case MemoryWineLoadUnixLibByNameWow64: ++ if (process == GetCurrentProcess()) ++ { ++ UINT64 res[2]; ++ const UNICODE_STRING *name = addr; ++ const void *funcs; ++ void *handle; ++ ++ if ((status = load_unixlib_by_name( name, &handle ))) return status; ++ res[0] = (UINT_PTR)handle; ++ if (len >= sizeof(res)) ++ { ++ if (!(status = get_unixlib_funcs( handle, info_class == MemoryWineLoadUnixLibByNameWow64, &funcs ))) ++ res[1] = (UINT_PTR)funcs; ++ } ++ if (status) dlclose( handle ); ++ else memcpy( buffer, res, min( len, sizeof(res) )); ++ return status; ++ } ++ return STATUS_INVALID_HANDLE; ++ ++ case MemoryWineUnloadUnixLib: ++ if (process == GetCurrentProcess()) ++ { ++ const unixlib_module_t *handle = addr; ++ ++ if (!dlclose( (void *)(UINT_PTR)*handle )) return STATUS_SUCCESS; ++ } ++ return STATUS_INVALID_HANDLE; ++ + default: + FIXME("(%p,%p,info_class=%d,%p,%ld,%p) Unknown information class\n", + process, addr, info_class, buffer, len, res_len); diff --git a/build-scripts/build-step-arm64ec.sh b/build-scripts/build-step-arm64ec.sh index 50c2b53d722..e787d9807bd 100755 --- a/build-scripts/build-step-arm64ec.sh +++ b/build-scripts/build-step-arm64ec.sh @@ -171,8 +171,8 @@ do --without-xcomposite \ --without-xfixes \ --without-xinerama \ - --without-xrandr \ - --without-xrender \ + --with-xrandr \ + --with-xrender \ --without-xshape \ --with-xshm \ --without-xxf86vm @@ -259,32 +259,29 @@ do "arm64ec/dlls_ntdll_loader_c.patch" "arm64ec/dlls_ntdll_unix_loader_c.patch" + # FEX unixlib loader (MemoryWineLoadUnixLibByName) support patches + "common/include_winternl_h.patch" + "common/include_wine_unixlib_h.patch" + "common/dlls_wow64_virtual_c.patch" + "common/dlls_ntdll_unix_unix_private_h.patch" + + # bionic bug-fixes + "common/dlls_ntdll_unix_env_c.patch" + "common/dlls_shell32_shlfileop_c.patch" + # fix build "arm64ec/programs_wineboot_wineboot_c.patch" - # 1. Extended State (XSTATE/YMM) Support Patches - - # 2. Thread Suspension Patches + # 1. Thread Suspension Patches "test-bylaws/dlls_ntdll_unix_debug_c.patch" "test-bylaws/dlls_ntdll_unix_signal_arm64_c.patch" "test-bylaws/dlls_ntdll_unix_signal_arm_c.patch" "test-bylaws/dlls_ntdll_unix_signal_i386_c.patch" - "test-bylaws/dlls_ntdll_unix_unix_private_h.patch" "test-bylaws/dlls_ntdll_ntdll_misc_h.patch" "test-bylaws/dlls_wow64_syscall_c.patch" - # 3. Process and Virtual Memory Management + # 2. Process and Virtual Memory Management "test-bylaws/dlls_ntdll_unix_process_c.patch" - - # 4. Server and Threading Infrastructure - - # 5. Internal Headers - "test-bylaws/include_winternl_h.patch" - - # 6. build vcruntime140_1 with aarch64 - - # 7. Build System (Optional) -# "test-bylaws/tools_makedep_c.patch" ) for patch in "${PATCHES[@]}"; do diff --git a/build-scripts/build-step-x86_64.sh b/build-scripts/build-step-x86_64.sh index 318e0d8c8be..09b2557ace9 100755 --- a/build-scripts/build-step-x86_64.sh +++ b/build-scripts/build-step-x86_64.sh @@ -171,8 +171,8 @@ do --without-xcomposite \ --without-xfixes \ --without-xinerama \ - --without-xrandr \ - --without-xrender \ + --with-xrandr \ + --with-xrender \ --without-xshape \ --without-xshm \ --without-xxf86vm @@ -257,6 +257,16 @@ do # loader patch "x86_64/dlls_ntdll_unix_loader_c.patch" + + # FEX unixlib loader (MemoryWineLoadUnixLibByName) support patches + "common/include_winternl_h.patch" + "common/include_wine_unixlib_h.patch" + "common/dlls_wow64_virtual_c.patch" + "common/dlls_ntdll_unix_unix_private_h.patch" + + # bionic bug-fixes + "common/dlls_ntdll_unix_env_c.patch" + "common/dlls_shell32_shlfileop_c.patch" ) for patch in "${PATCHES[@]}"; do