authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-26 17:34:45+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-11-28 17:07:34+01:00
log7ea2c7fbcdc4c0555dcc45aebc1eb8e1bf4793b3
treec3a5042c6163a78df142ea7628adc1c4731aa3df
parent153afed877e6f9a068d5db61f42cdb872c72aeab

windows: use RtlQueryRegistryValues to query reg in a single syscall


4 files changed, 261 insertions(+), 186 deletions(-)

lib/std/os/windows.zig+127-184
......@@ -2088,6 +2088,7 @@ pub const LPWSTR = [*:0]WCHAR;
20882088pub const LPCWSTR = [*:0]const WCHAR;
20892089pub const PVOID = *anyopaque;
20902090pub const PWSTR = [*:0]WCHAR;
2091pub const PCWSTR = [*:0]const WCHAR;
20912092pub const SIZE_T = usize;
20922093pub const UINT = c_uint;
20932094pub const ULONG_PTR = usize;
......@@ -2876,8 +2877,134 @@ pub const ACCESS_MASK = DWORD;
28762877pub const LSTATUS = LONG;
28772878
28782879pub const HKEY = HANDLE;
2880
28792881pub const HKEY_LOCAL_MACHINE: HKEY = @intToPtr(HKEY, 0x80000002);
28802882
2883/// Combines the STANDARD_RIGHTS_REQUIRED, KEY_QUERY_VALUE, KEY_SET_VALUE, KEY_CREATE_SUB_KEY,
2884/// KEY_ENUMERATE_SUB_KEYS, KEY_NOTIFY, and KEY_CREATE_LINK access rights.
2885pub const KEY_ALL_ACCESS = 0xF003F;
2886/// Reserved for system use.
2887pub const KEY_CREATE_LINK = 0x0020;
2888/// Required to create a subkey of a registry key.
2889pub const KEY_CREATE_SUB_KEY = 0x0004;
2890/// Required to enumerate the subkeys of a registry key.
2891pub const KEY_ENUMERATE_SUB_KEYS = 0x0008;
2892/// Equivalent to KEY_READ.
2893pub const KEY_EXECUTE = 0x20019;
2894/// Required to request change notifications for a registry key or for subkeys of a registry key.
2895pub const KEY_NOTIFY = 0x0010;
2896/// Required to query the values of a registry key.
2897pub const KEY_QUERY_VALUE = 0x0001;
2898/// Combines the STANDARD_RIGHTS_READ, KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, and KEY_NOTIFY values.
2899pub const KEY_READ = 0x20019;
2900/// Required to create, delete, or set a registry value.
2901pub const KEY_SET_VALUE = 0x0002;
2902/// Indicates that an application on 64-bit Windows should operate on the 32-bit registry view.
2903/// This flag is ignored by 32-bit Windows.
2904pub const KEY_WOW64_32KEY = 0x0200;
2905/// Indicates that an application on 64-bit Windows should operate on the 64-bit registry view.
2906/// This flag is ignored by 32-bit Windows.
2907pub const KEY_WOW64_64KEY = 0x0100;
2908/// Combines the STANDARD_RIGHTS_WRITE, KEY_SET_VALUE, and KEY_CREATE_SUB_KEY access rights.
2909pub const KEY_WRITE = 0x20006;
2910
2911/// Open symbolic link.
2912pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
2913
2914pub const RTL_QUERY_REGISTRY_TABLE = extern struct {
2915 QueryRoutine: RTL_QUERY_REGISTRY_ROUTINE,
2916 Flags: ULONG,
2917 Name: ?PWSTR,
2918 EntryContext: ?*anyopaque,
2919 DefaultType: ULONG,
2920 DefaultData: ?*anyopaque,
2921 DefaultLength: ULONG,
2922};
2923
2924pub const RTL_QUERY_REGISTRY_ROUTINE = ?std.meta.FnPtr(fn (
2925 PWSTR,
2926 ULONG,
2927 ?*anyopaque,
2928 ULONG,
2929 ?*anyopaque,
2930 ?*anyopaque,
2931) callconv(WINAPI) NTSTATUS);
2932
2933/// Path is a full path
2934pub const RTL_REGISTRY_ABSOLUTE = 0;
2935/// \Registry\Machine\System\CurrentControlSet\Services
2936pub const RTL_REGISTRY_SERVICES = 1;
2937/// \Registry\Machine\System\CurrentControlSet\Control
2938pub const RTL_REGISTRY_CONTROL = 2;
2939/// \Registry\Machine\Software\Microsoft\Windows NT\CurrentVersion
2940pub const RTL_REGISTRY_WINDOWS_NT = 3;
2941/// \Registry\Machine\Hardware\DeviceMap
2942pub const RTL_REGISTRY_DEVICEMAP = 4;
2943/// \Registry\User\CurrentUser
2944pub const RTL_REGISTRY_USER = 5;
2945pub const RTL_REGISTRY_MAXIMUM = 6;
2946
2947/// Low order bits are registry handle
2948pub const RTL_REGISTRY_HANDLE = 0x40000000;
2949/// Indicates the key node is optional
2950pub const RTL_REGISTRY_OPTIONAL = 0x80000000;
2951
2952/// Name is a subkey and remainder of table or until next subkey are value
2953/// names for that subkey to look at.
2954pub const RTL_QUERY_REGISTRY_SUBKEY = 0x00000001;
2955
2956/// Reset current key to original key for this and all following table entries.
2957pub const RTL_QUERY_REGISTRY_TOPKEY = 0x00000002;
2958
2959/// Fail if no match found for this table entry.
2960pub const RTL_QUERY_REGISTRY_REQUIRED = 0x00000004;
2961
2962/// Used to mark a table entry that has no value name, just wants a call out, not
2963/// an enumeration of all values.
2964pub const RTL_QUERY_REGISTRY_NOVALUE = 0x00000008;
2965
2966/// Used to suppress the expansion of REG_MULTI_SZ into multiple callouts or
2967/// to prevent the expansion of environment variable values in REG_EXPAND_SZ.
2968pub const RTL_QUERY_REGISTRY_NOEXPAND = 0x00000010;
2969
2970/// QueryRoutine field ignored. EntryContext field points to location to store value.
2971/// For null terminated strings, EntryContext points to UNICODE_STRING structure that
2972/// that describes maximum size of buffer. If .Buffer field is NULL then a buffer is
2973/// allocated.
2974pub const RTL_QUERY_REGISTRY_DIRECT = 0x00000020;
2975
2976/// Used to delete value keys after they are queried.
2977pub const RTL_QUERY_REGISTRY_DELETE = 0x00000040;
2978
2979/// Use this flag with the RTL_QUERY_REGISTRY_DIRECT flag to verify that the REG_XXX type
2980/// of the stored registry value matches the type expected by the caller.
2981/// If the types do not match, the call fails.
2982pub const RTL_QUERY_REGISTRY_TYPECHECK = 0x00000100;
2983
2984/// No value type
2985pub const REG_NONE = 0;
2986/// Unicode nul terminated string
2987pub const REG_SZ = 1;
2988/// Unicode nul terminated string (with environment variable references)
2989pub const REG_EXPAND_SZ = 2;
2990/// Free form binary
2991pub const REG_BINARY = 3;
2992/// 32-bit number
2993pub const REG_DWORD = 4;
2994/// 32-bit number (same as REG_DWORD)
2995pub const REG_DWORD_LITTLE_ENDIAN = 4;
2996/// 32-bit number
2997pub const REG_DWORD_BIG_ENDIAN = 5;
2998/// Symbolic Link (unicode)
2999pub const REG_LINK = 6;
3000/// Multiple Unicode strings
3001pub const REG_MULTI_SZ = 7;
3002/// Resource list in the resource map
3003pub const REG_RESOURCE_LIST = 8;
3004/// Resource list in the hardware description
3005pub const REG_FULL_RESOURCE_DESCRIPTOR = 9;
3006pub const REG_RESOURCE_REQUIREMENTS_LIST = 10;
3007
28813008pub const FILE_NOTIFY_INFORMATION = extern struct {
28823009 NextEntryOffset: DWORD,
28833010 Action: DWORD,
......@@ -4020,187 +4147,3 @@ pub fn IsProcessorFeaturePresent(feature: PF) bool {
40204147 if (@enumToInt(feature) >= PROCESSOR_FEATURE_MAX) return false;
40214148 return SharedUserData.ProcessorFeatures[@enumToInt(feature)] == 1;
40224149}
4023
4024pub const KEY_QUERY_VALUE = 0x0001;
4025
4026/// Open symbolic link.
4027pub const REG_OPTION_OPEN_LINK: DWORD = 0x8;
4028
4029inline fn IsPredefKey(hkey: HKEY) bool {
4030 return @ptrToInt(hkey) & 0xF0000000 == 0x80000000;
4031}
4032
4033inline fn GetPredefKeyIndex(hkey: HKEY) usize {
4034 return @ptrToInt(hkey) & 0x0FFFFFFF;
4035}
4036
4037inline fn ClosePredefKey(hkey: HKEY) void {
4038 if (@ptrToInt(hkey) & 0x1 != 0) {
4039 assert(ntdll.NtClose(hkey) == .SUCCESS);
4040 }
4041}
4042
4043const MAX_DEFAULT_HANDLES = 6;
4044pub const REG_MAX_NAME_SIZE = 256;
4045
4046pub const RegOpenKeyOpts = struct {
4047 ulOptions: DWORD = 0,
4048 samDesired: ACCESS_MASK = KEY_QUERY_VALUE,
4049};
4050
4051/// Pulls existing key from the registry.
4052pub fn RegOpenKey(hkey: HKEY, lpSubKey: []const u16, opts: RegOpenKeyOpts) !HKEY {
4053 if (IsPredefKey(hkey) and lpSubKey.len == 0) {
4054 return hkey;
4055 }
4056
4057 const key_handle = try MapDefaultKey(hkey);
4058 defer ClosePredefKey(key_handle);
4059
4060 var subkey_string: UNICODE_STRING = undefined;
4061 if (lpSubKey.len == 0 or mem.eql(u16, &[_]u16{'\\'}, lpSubKey)) {
4062 subkey_string = .{
4063 .Length = 0,
4064 .MaximumLength = 0,
4065 .Buffer = @intToPtr([*]u16, @ptrToInt(&[0]u16{})),
4066 };
4067 } else {
4068 const len_bytes = math.cast(u16, lpSubKey.len * 2) orelse return error.NameTooLong;
4069 subkey_string = .{
4070 .Length = len_bytes,
4071 .MaximumLength = len_bytes,
4072 .Buffer = @intToPtr([*]u16, @ptrToInt(lpSubKey.ptr)),
4073 };
4074 }
4075
4076 var attributes: ULONG = OBJ_CASE_INSENSITIVE;
4077 if (opts.ulOptions & REG_OPTION_OPEN_LINK != 0) {
4078 attributes |= OBJ_OPENLINK;
4079 }
4080
4081 var attr = OBJECT_ATTRIBUTES{
4082 .Length = @sizeOf(OBJECT_ATTRIBUTES),
4083 .RootDirectory = key_handle,
4084 .Attributes = attributes,
4085 .ObjectName = &subkey_string,
4086 .SecurityDescriptor = null,
4087 .SecurityQualityOfService = null,
4088 };
4089
4090 var result: HKEY = undefined;
4091 const rc = ntdll.NtOpenKey(
4092 &result,
4093 opts.samDesired,
4094 attr,
4095 );
4096 switch (rc) {
4097 .SUCCESS => return result,
4098 else => return unexpectedStatus(rc),
4099 }
4100}
4101
4102pub fn RegCloseKey(hkey: HKEY) void {
4103 if (IsPredefKey(hkey)) return;
4104 assert(ntdll.NtClose(hkey) == .SUCCESS);
4105}
4106
4107extern var DefaultHandleHKUDisabled: BOOLEAN;
4108extern var DefaultHandlesDisabled: BOOLEAN;
4109extern var DefaultHandleTable: [MAX_DEFAULT_HANDLES]?HANDLE;
4110
4111fn MapDefaultKey(key: HKEY) !HANDLE {
4112 if (!IsPredefKey(key)) return @intToPtr(HANDLE, @ptrToInt(key) & ~@as(usize, 0x1));
4113
4114 const index = GetPredefKeyIndex(key);
4115 if (index >= MAX_DEFAULT_HANDLES) {
4116 return error.InvalidParameter;
4117 }
4118
4119 const def_disabled = if (key == HKEY_LOCAL_MACHINE) DefaultHandleHKUDisabled else DefaultHandlesDisabled;
4120
4121 var handle: HANDLE = undefined;
4122 var do_open: bool = true;
4123
4124 if (def_disabled != 0) {
4125 const tmp = DefaultHandleTable[index];
4126 if (tmp) |h| {
4127 do_open = false;
4128 handle = h;
4129 }
4130 }
4131
4132 if (do_open) {
4133 handle = try OpenPredefinedKey(index);
4134 }
4135
4136 if (def_disabled == 0) {
4137 handle = @intToPtr(HANDLE, @ptrToInt(handle) | 0x1);
4138 }
4139
4140 return handle;
4141}
4142
4143fn OpenPredefinedKey(index: usize) !HANDLE {
4144 switch (index) {
4145 0 => {
4146 // HKEY_CLASSES_ROOT
4147 return error.Unimplemented;
4148 },
4149 1 => {
4150 // HKEY_CURRENT_USER
4151 return error.Unimplemented;
4152 },
4153 2 => {
4154 // HKEY_LOCAL_MACHINE
4155 return OpenLocalMachineKey();
4156 },
4157 3 => {
4158 // HKEY_USERS
4159 return error.Unimplemented;
4160 },
4161 5 => {
4162 // HKEY_CURRENT_CONFIG
4163 return error.Unimplemented;
4164 },
4165 6 => {
4166 // HKEY_DYN_DATA
4167 return error.Unimplemented;
4168 },
4169 else => {
4170 return error.InvalidParameter;
4171 },
4172 }
4173}
4174
4175fn OpenLocalMachineKey() !HANDLE {
4176 const path = "\\Registry\\Machine";
4177 var path_u16: [REG_MAX_NAME_SIZE]u16 = undefined;
4178 const path_len_u16 = try std.unicode.utf8ToUtf16Le(&path_u16, path);
4179 const path_len_bytes = @intCast(u16, path_len_u16 * 2);
4180
4181 var key_name = UNICODE_STRING{
4182 .Length = path_len_bytes,
4183 .MaximumLength = path_len_bytes,
4184 .Buffer = @intToPtr([*]u16, @ptrToInt(&path_u16)),
4185 };
4186
4187 var attr = OBJECT_ATTRIBUTES{
4188 .Length = @sizeOf(OBJECT_ATTRIBUTES),
4189 .RootDirectory = null,
4190 .Attributes = OBJ_CASE_INSENSITIVE,
4191 .ObjectName = &key_name,
4192 .SecurityDescriptor = null,
4193 .SecurityQualityOfService = null,
4194 };
4195
4196 var result: HKEY = undefined;
4197 const rc = ntdll.NtOpenKey(
4198 &result,
4199 MAXIMUM_ALLOWED,
4200 attr,
4201 );
4202 switch (rc) {
4203 .SUCCESS => return result,
4204 else => return unexpectedStatus(rc),
4205 }
4206}
lib/std/os/windows/kernel32.zig+11
......@@ -10,6 +10,7 @@ const DWORD = windows.DWORD;
1010const FILE_INFO_BY_HANDLE_CLASS = windows.FILE_INFO_BY_HANDLE_CLASS;
1111const HANDLE = windows.HANDLE;
1212const HMODULE = windows.HMODULE;
13const HKEY = windows.HKEY;
1314const HRESULT = windows.HRESULT;
1415const LARGE_INTEGER = windows.LARGE_INTEGER;
1516const LPCWSTR = windows.LPCWSTR;
......@@ -57,6 +58,8 @@ const UCHAR = windows.UCHAR;
5758const FARPROC = windows.FARPROC;
5859const INIT_ONCE_FN = windows.INIT_ONCE_FN;
5960const PMEMORY_BASIC_INFORMATION = windows.PMEMORY_BASIC_INFORMATION;
61const REGSAM = windows.REGSAM;
62const LSTATUS = windows.LSTATUS;
6063
6164pub extern "kernel32" fn AddVectoredExceptionHandler(First: c_ulong, Handler: ?VECTORED_EXCEPTION_HANDLER) callconv(WINAPI) ?*anyopaque;
6265pub extern "kernel32" fn RemoveVectoredExceptionHandler(Handle: HANDLE) callconv(WINAPI) c_ulong;
......@@ -412,3 +415,11 @@ pub extern "kernel32" fn SleepConditionVariableSRW(
412415pub extern "kernel32" fn TryAcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) BOOLEAN;
413416pub extern "kernel32" fn AcquireSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
414417pub extern "kernel32" fn ReleaseSRWLockExclusive(s: *SRWLOCK) callconv(WINAPI) void;
418
419pub extern "kernel32" fn RegOpenKeyExW(
420 hkey: HKEY,
421 lpSubKey: LPCWSTR,
422 ulOptions: DWORD,
423 samDesired: REGSAM,
424 phkResult: *HANDLE,
425) callconv(WINAPI) LSTATUS;
lib/std/os/windows/ntdll.zig+10
......@@ -22,6 +22,8 @@ const RTL_OSVERSIONINFOW = windows.RTL_OSVERSIONINFOW;
2222const FILE_BASIC_INFORMATION = windows.FILE_BASIC_INFORMATION;
2323const SIZE_T = windows.SIZE_T;
2424const CURDIR = windows.CURDIR;
25const PCWSTR = windows.PCWSTR;
26const RTL_QUERY_REGISTRY_TABLE = windows.RTL_QUERY_REGISTRY_TABLE;
2527
2628pub const THREADINFOCLASS = enum(c_int) {
2729 ThreadBasicInformation,
......@@ -259,3 +261,11 @@ pub extern "ntdll" fn NtOpenKey(
259261 DesiredAccess: ACCESS_MASK,
260262 ObjectAttributes: OBJECT_ATTRIBUTES,
261263) callconv(WINAPI) NTSTATUS;
264
265pub extern "ntdll" fn RtlQueryRegistryValues(
266 RelativeTo: ULONG,
267 Path: PCWSTR,
268 QueryTable: [*]RTL_QUERY_REGISTRY_TABLE,
269 Context: ?*anyopaque,
270 Environment: ?*anyopaque,
271) callconv(WINAPI) NTSTATUS;
lib/std/zig/system/windows.zig+113-2
......@@ -43,13 +43,124 @@ pub fn detectRuntimeVersion() WindowsVersion {
4343 return @intToEnum(WindowsVersion, version);
4444}
4545
46fn detectCpuModelArm64() !*const Target.Cpu.Model {
47 // Pull the CPU identifier from the registry.
48 // Assume max number of cores to be at 128.
49 const max_cpu_count = 128;
50 const cpu_count = getCpuCount();
51
52 if (cpu_count > max_cpu_count) return error.TooManyCpus;
53
54 const table_size = max_cpu_count * 3 + 1;
55 const actual_table_size = cpu_count * 3 + 1;
56 var table: [table_size]std.os.windows.RTL_QUERY_REGISTRY_TABLE = undefined;
57
58 // Table sentinel
59 table[actual_table_size - 1] = .{
60 .QueryRoutine = null,
61 .Flags = 0,
62 .Name = null,
63 .EntryContext = null,
64 .DefaultType = 0,
65 .DefaultData = null,
66 .DefaultLength = 0,
67 };
68
69 // Technically, a registry value can be as long as 16k u16s. However, MS recommends storing
70 // values larger than 2048 in a file rather than directly in the registry, and since we
71 // are only accessing a system hive \Registry\Machine, we stick to MS guidelines.
72 // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits
73 const max_sz_value = 2048;
74 const key_name = std.unicode.utf8ToUtf16LeStringLiteral("Identifier");
75
76 var i: usize = 0;
77 var index: usize = 0;
78 while (i < cpu_count) : (i += 1) {
79 var buf: [max_sz_value]u16 = undefined;
80 var buf_uni = std.os.windows.UNICODE_STRING{
81 .Length = buf.len * 2,
82 .MaximumLength = buf.len * 2,
83 .Buffer = &buf,
84 };
85
86 var next_cpu_buf: [std.math.log2(max_cpu_count)]u8 = undefined;
87 const next_cpu = try std.fmt.bufPrint(&next_cpu_buf, "{d}", .{i});
88
89 var subkey: [std.math.log2(max_cpu_count) / 2]u16 = undefined;
90 const subkey_len = try std.unicode.utf8ToUtf16Le(&subkey, next_cpu);
91 subkey[subkey_len] = 0;
92
93 table[index] = .{
94 .QueryRoutine = null,
95 .Flags = std.os.windows.RTL_QUERY_REGISTRY_SUBKEY | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
96 .Name = subkey[0..subkey_len :0],
97 .EntryContext = null,
98 .DefaultType = std.os.windows.REG_NONE,
99 .DefaultData = null,
100 .DefaultLength = 0,
101 };
102
103 table[index + 1] = .{
104 .QueryRoutine = null,
105 .Flags = std.os.windows.RTL_QUERY_REGISTRY_DIRECT | std.os.windows.RTL_QUERY_REGISTRY_REQUIRED,
106 .Name = @intToPtr([*:0]u16, @ptrToInt(key_name)),
107 .EntryContext = &buf_uni,
108 .DefaultType = std.os.windows.REG_NONE,
109 .DefaultData = null,
110 .DefaultLength = 0,
111 };
112
113 table[index + 2] = .{
114 .QueryRoutine = null,
115 .Flags = std.os.windows.RTL_QUERY_REGISTRY_TOPKEY,
116 .Name = null,
117 .EntryContext = null,
118 .DefaultType = std.os.windows.REG_NONE,
119 .DefaultData = null,
120 .DefaultLength = 0,
121 };
122
123 index += 3;
124 }
125
126 const topkey = std.unicode.utf8ToUtf16LeStringLiteral("\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor");
127 const res = std.os.windows.ntdll.RtlQueryRegistryValues(
128 std.os.windows.RTL_REGISTRY_ABSOLUTE,
129 topkey,
130 &table,
131 null,
132 null,
133 );
134 switch (res) {
135 .SUCCESS => {},
136 else => return error.QueryRegistryFailed,
137 }
138
139 // Parse the models from strings
140 i = 0;
141 index = 0;
142 while (i < cpu_count) : (i += 1) {
143 const entry = @ptrCast(*align(1) const std.os.windows.UNICODE_STRING, table[index + 1].EntryContext);
144 index += 3;
145
146 var identifier_buf: [max_sz_value * 2]u8 = undefined;
147 const len = try std.unicode.utf16leToUtf8(&identifier_buf, entry.Buffer[0 .. entry.Length / 2]);
148 const identifier = identifier_buf[0..len];
149 _ = identifier;
150 }
151
152 return &Target.aarch64.cpu.microsoft_sq3;
153}
154
46155fn detectNativeCpuAndFeaturesArm64() Target.Cpu {
47156 const Feature = Target.aarch64.Feature;
48157
158 const model = detectCpuModelArm64() catch Target.Cpu.Model.generic(.aarch64);
159
49160 var cpu = Target.Cpu{
50161 .arch = .aarch64,
51 .model = Target.Cpu.Model.generic(.aarch64),
52 .features = Target.Cpu.Feature.Set.empty,
162 .model = model,
163 .features = model.features,
53164 };
54165
55166 if (IsProcessorFeaturePresent(PF.ARM_NEON_INSTRUCTIONS_AVAILABLE)) {