authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-13 01:06:47+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-03-13 20:56:00+01:00
log5d71e3051833d20dd2dbfe801e060b3a1a3afa69
treef9d7a465c6703ea881314400fc81012275129c82
parent090ae3042e8e2fec2c2f8797bf78c8f6e6335a30

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!)

3 files changed, 61 insertions(+), 42 deletions(-)

lib/std/Io/Threaded.zig+11-33
...@@ -16602,43 +16602,21 @@ const WindowsCommandLineCache = struct {...@@ -16602,43 +16602,21 @@ const WindowsCommandLineCache = struct {
16602 return self.script_cmd_line.?;16602 return self.script_cmd_line.?;
16603 }16603 }
1660416604
16605 fn cmdExePath(self: *WindowsCommandLineCache) ![:0]u16 {16605 fn cmdExePath(self: *WindowsCommandLineCache) Allocator.Error![:0]u16 {
16606 if (self.cmd_exe_path == null) {16606 if (self.cmd_exe_path == null) {
16607 self.cmd_exe_path = try windowsCmdExePath(self.allocator);16607 // Remove trailing slash from system directory path; we'll re-add it below
16608 const system_dir = std.mem.trimEnd(u16, windows.getSystemDirectoryWtf16Le(), &.{ '/', '\\' });
16609 const suffix = std.unicode.utf8ToUtf16LeStringLiteral("\\cmd.exe");
16610 const buf = try self.allocator.allocSentinel(u16, system_dir.len + suffix.len, 0);
16611 errdefer comptime unreachable;
16612 @memcpy(buf[0..system_dir.len], system_dir);
16613 @memcpy(buf[system_dir.len..], suffix);
16614 self.cmd_exe_path = buf;
16608 }16615 }
16609 return self.cmd_exe_path.?;16616 return self.cmd_exe_path.?;
16610 }16617 }
16611};16618};
1661216619
16613/// Returns the absolute path of `cmd.exe` within the Windows system directory.
16614/// The caller owns the returned slice.
16615fn windowsCmdExePath(allocator: Allocator) error{ OutOfMemory, Unexpected }![:0]u16 {
16616 var buf = try std.ArrayList(u16).initCapacity(allocator, 128);
16617 errdefer buf.deinit(allocator);
16618 while (true) {
16619 const unused_slice = buf.unusedCapacitySlice();
16620 // TODO: Get the system directory from PEB.ReadOnlyStaticServerData
16621 const len = windows.kernel32.GetSystemDirectoryW(@ptrCast(unused_slice), @intCast(unused_slice.len));
16622 if (len == 0) {
16623 switch (windows.GetLastError()) {
16624 else => |err| return windows.unexpectedError(err),
16625 }
16626 }
16627 if (len > unused_slice.len) {
16628 try buf.ensureUnusedCapacity(allocator, len);
16629 } else {
16630 buf.items.len = len;
16631 break;
16632 }
16633 }
16634 switch (buf.items[buf.items.len - 1]) {
16635 '/', '\\' => {},
16636 else => try buf.append(allocator, Dir.path.sep),
16637 }
16638 try buf.appendSlice(allocator, std.unicode.utf8ToUtf16LeStringLiteral("cmd.exe"));
16639 return try buf.toOwnedSliceSentinel(allocator, 0);
16640}
16641
16642const ArgvToScriptCommandLineError = error{16620const ArgvToScriptCommandLineError = error{
16643 OutOfMemory,16621 OutOfMemory,
16644 InvalidWtf8,16622 InvalidWtf8,
...@@ -16659,8 +16637,8 @@ const ArgvToScriptCommandLineError = error{...@@ -16659,8 +16637,8 @@ const ArgvToScriptCommandLineError = error{
16659///16637///
16660/// The return of this function will look like16638/// The return of this function will look like
16661/// `cmd.exe /d /e:ON /v:OFF /c "<escaped command line>"`16639/// `cmd.exe /d /e:ON /v:OFF /c "<escaped command line>"`
16662/// and should be used as the `lpCommandLine` of `CreateProcessW`, while the16640/// and should be used as the `lpCommandLine` of `CreateProcessW`, while the return of
16663/// return of `windowsCmdExePath` should be used as `lpApplicationName`.16641/// `WindowsCommandLineCache.cmdExePath` should be used as `lpApplicationName`.
16664///16642///
16665/// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise.16643/// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise.
16666/// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem.16644/// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem.
lib/std/os/windows.zig+50-1
...@@ -4408,13 +4408,14 @@ pub const PEB = extern struct {...@@ -4408,13 +4408,14 @@ pub const PEB = extern struct {
4408 // note: there is padding here on 64 bit4408 // note: there is padding here on 64 bit
4409 TlsBitmap: *RTL_BITMAP,4409 TlsBitmap: *RTL_BITMAP,
4410 TlsBitmapBits: [2]ULONG,4410 TlsBitmapBits: [2]ULONG,
4411 /// Our base address of the memory region shared with the CSR server.
4411 ReadOnlySharedMemoryBase: PVOID,4412 ReadOnlySharedMemoryBase: PVOID,
44124413
4413 // Versions: 1703+4414 // Versions: 1703+
4414 SharedData: PVOID,4415 SharedData: PVOID,
44154416
4416 // Versions: all4417 // Versions: all
4417 ReadOnlyStaticServerData: *PVOID,4418 ReadOnlyStaticServerData: *UnknownStaticServerDataIndirection,
4418 AnsiCodePageData: PVOID,4419 AnsiCodePageData: PVOID,
4419 OemCodePageData: PVOID,4420 OemCodePageData: PVOID,
4420 UnicodeCaseTableData: PVOID,4421 UnicodeCaseTableData: PVOID,
...@@ -4501,6 +4502,7 @@ pub const PEB = extern struct {...@@ -4501,6 +4502,7 @@ pub const PEB = extern struct {
4501 TracingFlags: ULONG,4502 TracingFlags: ULONG,
45024503
4503 // Fields appended in 6.2 (Windows 8):4504 // Fields appended in 6.2 (Windows 8):
4505 /// Base address in the CSRSS address space of the memory region shared with the CSR server.
4504 CsrServerReadOnlySharedMemoryBase: ULONGLONG,4506 CsrServerReadOnlySharedMemoryBase: ULONGLONG,
45054507
4506 // Fields appended in 1511:4508 // Fields appended in 1511:
...@@ -4511,6 +4513,14 @@ pub const PEB = extern struct {...@@ -4511,6 +4513,14 @@ pub const PEB = extern struct {
4511 // Fields appended in 1709:4513 // Fields appended in 1709:
4512 TelemetryCoverageHeader: PVOID,4514 TelemetryCoverageHeader: PVOID,
4513 CloudFileFlags: ULONG,4515 CloudFileFlags: ULONG,
4516
4517 /// Details of this structure are unknown, but the existence of the field at offset 8 is known
4518 /// from experimentation and from reverse-engineering kernelbase.dll.
4519 const UnknownStaticServerDataIndirection = extern struct {
4520 unknown: u64,
4521 /// In the CSRSS address space.
4522 base_static_server_data_addr: u64,
4523 };
4514};4524};
45154525
4516/// The `PEB_LDR_DATA` structure is the main record of what modules are loaded in a process.4526/// 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...@@ -5139,3 +5149,42 @@ pub fn wtf8ToWtf16Le(wtf16le: []u16, wtf8: []const u8) error{ BadPathName, NameT
5139 error.InvalidWtf8 => return error.BadPathName,5149 error.InvalidWtf8 => return error.BadPathName,
5140 };5150 };
5141}5151}
5152
5153/// Returns the path to the system directory, typically "C:\\WINDOWS\\System32".
5154///
5155/// Equivalent to `GetSystemDirectoryW` in kernel32.
5156pub fn getSystemDirectoryWtf16Le() [:0]const u16 {
5157 const ssd: *const BASE_STATIC_SERVER_DATA = @ptrCast(@alignCast(relocateCsrssAddress(
5158 peb().ReadOnlyStaticServerData.base_static_server_data_addr,
5159 )));
5160 return ssd.windows_system_directory.relocate().sliceZ();
5161}
5162// https://github.com/reactos/reactos/blob/4b75ec5508d47b726d1210e24f5a849dae4e3bda/sdk/include/reactos/subsys/win/base.h#L119
5163const BASE_STATIC_SERVER_DATA = extern struct {
5164 windows_directory: ForeignString,
5165 windows_system_directory: ForeignString,
5166 named_object_directory: ForeignString,
5167 /// This matches the 64-bit version of `UNICODE_STRING`---even on 32-bit targets, this string is
5168 /// from 64-bit code (since it comes from CSRSS which is running outside of WOW64).
5169 const ForeignString = extern struct {
5170 length: u16,
5171 maximum_length: u16,
5172 /// Address in the CSRSS address space. To convert this to a valid pointer in *our* address
5173 /// space, see `relocateCsrssAddress` (or the `ForeignString.relocate` wrapper function).
5174 buffer_address: u64,
5175 fn relocate(str: ForeignString) UNICODE_STRING {
5176 return .{
5177 .Length = str.length,
5178 .MaximumLength = str.maximum_length,
5179 .Buffer = @ptrCast(@alignCast(@constCast(relocateCsrssAddress(str.buffer_address)))),
5180 };
5181 }
5182 };
5183};
5184/// Takes an address in the CSRSS address space's mapped view of the shared memory region, and
5185/// returns the corresponding address in *our* mapped view of the shared memory region.
5186fn relocateCsrssAddress(addr: u64) *const anyopaque {
5187 const base: [*]const u8 = @ptrCast(peb().ReadOnlySharedMemoryBase);
5188 const offset: usize = @intCast(addr - peb().CsrServerReadOnlySharedMemoryBase);
5189 return base + offset;
5190}
lib/std/os/windows/kernel32.zig-8
...@@ -13,17 +13,9 @@ const THREAD_START_ROUTINE = windows.THREAD_START_ROUTINE;...@@ -13,17 +13,9 @@ const THREAD_START_ROUTINE = windows.THREAD_START_ROUTINE;
13const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;13const SECURITY_ATTRIBUTES = windows.SECURITY_ATTRIBUTES;
14const SIZE_T = windows.SIZE_T;14const SIZE_T = windows.SIZE_T;
15const STARTUPINFOW = windows.STARTUPINFOW;15const STARTUPINFOW = windows.STARTUPINFOW;
16const UINT = windows.UINT;
17const va_list = windows.va_list;16const va_list = windows.va_list;
18const Win32Error = windows.Win32Error;17const Win32Error = windows.Win32Error;
1918
20// I/O - Filesystem
21
22pub extern "kernel32" fn GetSystemDirectoryW(
23 lpBuffer: LPWSTR,
24 uSize: UINT,
25) callconv(.winapi) UINT;
26
27// Process Management19// Process Management
2820
29pub extern "kernel32" fn CreateProcessW(21pub extern "kernel32" fn CreateProcessW(