From 5d71e3051833d20dd2dbfe801e060b3a1a3afa69 Mon Sep 17 00:00:00 2001 From: Matthew Lugg Date: Fri, 13 Mar 2026 01:06:47 +0000 Subject: [PATCH] std: remove another kernel32 dependency We can directly access this path string from the PEB (albeit with some weirdness around addressing) and that ends up making the downstream code simpler and more efficient. (Almost like the kernel32 API isn't very good!) --- lib/std/Io/Threaded.zig | 44 +++++++--------------------- lib/std/os/windows.zig | 51 ++++++++++++++++++++++++++++++++- lib/std/os/windows/kernel32.zig | 8 ------ 3 files changed, 61 insertions(+), 42 deletions(-) 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(