diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index b9c92433e9..bd40bdf4ca 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -16602,43 +16602,21 @@ const WindowsCommandLineCache = struct { return self.script_cmd_line.?; } - fn cmdExePath(self: *WindowsCommandLineCache) ![:0]u16 { + fn cmdExePath(self: *WindowsCommandLineCache) Allocator.Error![:0]u16 { if (self.cmd_exe_path == null) { - self.cmd_exe_path = try windowsCmdExePath(self.allocator); + // Remove trailing slash from system directory path; we'll re-add it below + const system_dir = std.mem.trimEnd(u16, windows.getSystemDirectoryWtf16Le(), &.{ '/', '\\' }); + const suffix = std.unicode.utf8ToUtf16LeStringLiteral("\\cmd.exe"); + const buf = try self.allocator.allocSentinel(u16, system_dir.len + suffix.len, 0); + errdefer comptime unreachable; + @memcpy(buf[0..system_dir.len], system_dir); + @memcpy(buf[system_dir.len..], suffix); + self.cmd_exe_path = buf; } return self.cmd_exe_path.?; } }; -/// Returns the absolute path of `cmd.exe` within the Windows system directory. -/// The caller owns the returned slice. -fn windowsCmdExePath(allocator: Allocator) error{ OutOfMemory, Unexpected }![:0]u16 { - var buf = try std.ArrayList(u16).initCapacity(allocator, 128); - errdefer buf.deinit(allocator); - while (true) { - const unused_slice = buf.unusedCapacitySlice(); - // TODO: Get the system directory from PEB.ReadOnlyStaticServerData - const len = windows.kernel32.GetSystemDirectoryW(@ptrCast(unused_slice), @intCast(unused_slice.len)); - if (len == 0) { - switch (windows.GetLastError()) { - else => |err| return windows.unexpectedError(err), - } - } - if (len > unused_slice.len) { - try buf.ensureUnusedCapacity(allocator, len); - } else { - buf.items.len = len; - break; - } - } - switch (buf.items[buf.items.len - 1]) { - '/', '\\' => {}, - else => try buf.append(allocator, Dir.path.sep), - } - try buf.appendSlice(allocator, std.unicode.utf8ToUtf16LeStringLiteral("cmd.exe")); - return try buf.toOwnedSliceSentinel(allocator, 0); -} - const ArgvToScriptCommandLineError = error{ OutOfMemory, InvalidWtf8, @@ -16659,8 +16637,8 @@ const ArgvToScriptCommandLineError = error{ /// /// The return of this function will look like /// `cmd.exe /d /e:ON /v:OFF /c ""` -/// and should be used as the `lpCommandLine` of `CreateProcessW`, while the -/// return of `windowsCmdExePath` should be used as `lpApplicationName`. +/// and should be used as the `lpCommandLine` of `CreateProcessW`, while the return of +/// `WindowsCommandLineCache.cmdExePath` should be used as `lpApplicationName`. /// /// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise. /// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem. diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index b04e53a45c..f38b340703 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -4408,13 +4408,14 @@ pub const PEB = extern struct { // note: there is padding here on 64 bit TlsBitmap: *RTL_BITMAP, TlsBitmapBits: [2]ULONG, + /// Our base address of the memory region shared with the CSR server. ReadOnlySharedMemoryBase: PVOID, // Versions: 1703+ SharedData: PVOID, // Versions: all - ReadOnlyStaticServerData: *PVOID, + ReadOnlyStaticServerData: *UnknownStaticServerDataIndirection, AnsiCodePageData: PVOID, OemCodePageData: PVOID, UnicodeCaseTableData: PVOID, @@ -4501,6 +4502,7 @@ pub const PEB = extern struct { TracingFlags: ULONG, // Fields appended in 6.2 (Windows 8): + /// Base address in the CSRSS address space of the memory region shared with the CSR server. CsrServerReadOnlySharedMemoryBase: ULONGLONG, // Fields appended in 1511: @@ -4511,6 +4513,14 @@ pub const PEB = extern struct { // Fields appended in 1709: TelemetryCoverageHeader: PVOID, CloudFileFlags: ULONG, + + /// Details of this structure are unknown, but the existence of the field at offset 8 is known + /// from experimentation and from reverse-engineering kernelbase.dll. + const UnknownStaticServerDataIndirection = extern struct { + unknown: u64, + /// In the CSRSS address space. + base_static_server_data_addr: u64, + }; }; /// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process. @@ -5139,3 +5149,42 @@ pub fn wtf8ToWtf16Le(wtf16le: []u16, wtf8: []const u8) error{ BadPathName, NameT error.InvalidWtf8 => return error.BadPathName, }; } + +/// Returns the path to the system directory, typically "C:\\WINDOWS\\System32". +/// +/// Equivalent to `GetSystemDirectoryW` in kernel32. +pub fn getSystemDirectoryWtf16Le() [:0]const u16 { + const ssd: *const BASE_STATIC_SERVER_DATA = @ptrCast(@alignCast(relocateCsrssAddress( + peb().ReadOnlyStaticServerData.base_static_server_data_addr, + ))); + return ssd.windows_system_directory.relocate().sliceZ(); +} +// https://github.com/reactos/reactos/blob/4b75ec5508d47b726d1210e24f5a849dae4e3bda/sdk/include/reactos/subsys/win/base.h#L119 +const BASE_STATIC_SERVER_DATA = extern struct { + windows_directory: ForeignString, + windows_system_directory: ForeignString, + named_object_directory: ForeignString, + /// This matches the 64-bit version of `UNICODE_STRING`---even on 32-bit targets, this string is + /// from 64-bit code (since it comes from CSRSS which is running outside of WOW64). + const ForeignString = extern struct { + length: u16, + maximum_length: u16, + /// Address in the CSRSS address space. To convert this to a valid pointer in *our* address + /// space, see `relocateCsrssAddress` (or the `ForeignString.relocate` wrapper function). + buffer_address: u64, + fn relocate(str: ForeignString) UNICODE_STRING { + return .{ + .Length = str.length, + .MaximumLength = str.maximum_length, + .Buffer = @ptrCast(@alignCast(@constCast(relocateCsrssAddress(str.buffer_address)))), + }; + } + }; +}; +/// Takes an address in the CSRSS address space's mapped view of the shared memory region, and +/// returns the corresponding address in *our* mapped view of the shared memory region. +fn relocateCsrssAddress(addr: u64) *const anyopaque { + const base: [*]const u8 = @ptrCast(peb().ReadOnlySharedMemoryBase); + const offset: usize = @intCast(addr - peb().CsrServerReadOnlySharedMemoryBase); + return base + offset; +} diff --git a/lib/std/os/windows/kernel32.zig b/lib/std/os/windows/kernel32.zig index 30a9a22022..894ef96a7e 100644 --- a/lib/std/os/windows/kernel32.zig +++ b/lib/std/os/windows/kernel32.zig @@ -13,17 +13,9 @@ const THREAD_START_ROUTINE = windows.THREAD_START_ROUTINE; const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES; const SIZE_T = windows.SIZE_T; const STARTUPINFOW = windows.STARTUPINFOW; -const UINT = windows.UINT; const va_list = windows.va_list; const Win32Error = windows.Win32Error; -// I/O - Filesystem - -pub extern "kernel32" fn GetSystemDirectoryW( - lpBuffer: LPWSTR, - uSize: UINT, -) callconv(.winapi) UINT; - // Process Management pub extern "kernel32" fn CreateProcessW(