@@ -154,6 +154,146 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
154
154
}
155
155
}
156
156
157
+ pub const CreateFileMappingError = error {
158
+ NameTooLong ,
159
+ } || UnexpectedError ;
160
+
161
+ pub const CreateSectionOptions = struct {
162
+ /// One or more of the `SECTION_*` flags.
163
+ access : ACCESS_MASK ,
164
+ /// The size of the section in bytes.
165
+ size : u64 ,
166
+ /// One or more of the `PAGE_*` flags.
167
+ page_attributes : ULONG ,
168
+ /// One or more of the `SEC_*` flags.
169
+ section_attributes : ULONG = SEC_COMMIT ,
170
+ /// A backing file from which pages should be mapped.
171
+ ///
172
+ /// If `null`, then the page file is used.
173
+ file : ? HANDLE = null ,
174
+ /// The security attributes which should be applied to the kernel object.
175
+ sa : ? * const SECURITY_ATTRIBUTES = null ,
176
+ /// The name of the kernel object.
177
+ ///
178
+ /// See [CreateFileMappingEx](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-createfilemappingw)
179
+ /// for more information on what format this may take.
180
+ name : ? []const u16 = null ,
181
+ };
182
+
183
+ /// Allocate virtual memory for memory-mapping pages from a file into this process.
184
+ ///
185
+ /// Asserts `options.size` is non-zero if `options.file == null`.
186
+ pub fn CreateSection (options : CreateSectionOptions ) CreateFileMappingError ! HANDLE {
187
+ std .debug .assert (options .file != null or options .size > 0 );
188
+
189
+ var result : HANDLE = undefined ;
190
+
191
+ // Create the string struct for the name. Since the name is optional, this is complicated
192
+ // somewhat compared to `OpenFile`.
193
+ var nt_name = if (options .name ) | n | blk : {
194
+ const name_len_bytes = math .cast (u16 , n .len * 2 ) orelse return error .NameTooLong ;
195
+ break :blk UNICODE_STRING {
196
+ .Length = name_len_bytes ,
197
+ .MaximumLength = name_len_bytes ,
198
+ .Buffer = @constCast (n .ptr ),
199
+ };
200
+ } else UNICODE_STRING {
201
+ .Length = 0 ,
202
+ .MaximumLength = 0 ,
203
+ .Buffer = null ,
204
+ };
205
+
206
+ // Create the object attributes for the section. This includes the name and a decomposition of
207
+ // the `sattr` into ntdll terms.
208
+ var attr = OBJECT_ATTRIBUTES {
209
+ .Length = @sizeOf (OBJECT_ATTRIBUTES ),
210
+ .RootDirectory = null ,
211
+ .Attributes = if (options .sa ) | ptr |
212
+ if (ptr .bInheritHandle == TRUE ) OBJ_INHERIT else 0
213
+ else
214
+ 0 ,
215
+ .ObjectName = & nt_name ,
216
+ .SecurityDescriptor = if (options .sa ) | ptr | ptr .lpSecurityDescriptor else null ,
217
+ .SecurityQualityOfService = null ,
218
+ };
219
+
220
+ var max_size : LARGE_INTEGER = @intCast (options .size );
221
+
222
+ const status = ntdll .NtCreateSection (
223
+ & result ,
224
+ options .access ,
225
+ & attr ,
226
+ & max_size ,
227
+ options .page_attributes ,
228
+ options .section_attributes ,
229
+ options .file ,
230
+ );
231
+ return switch (status ) {
232
+ .SUCCESS = > result ,
233
+ // TODO: Map out all the possible failure modes of this function. It isn't documented
234
+ // anywhere immediately obvious, so it will have to be done as and when people find errors
235
+ // from which they want to recover.
236
+ else = > unexpectedStatus (status ),
237
+ };
238
+ }
239
+
240
+ pub const MapViewOfFileError = UnexpectedError ;
241
+
242
+ pub const MapViewOfSectionOptions = struct {
243
+ /// Whether the mapping should be inherited by subprocesses.
244
+ inheritance : SECTION_INHERIT ,
245
+ /// One of the `PAGE_*` flags.
246
+ protection : ULONG ,
247
+ /// The number of bytes starting at `offset` which should be mapped.
248
+ length : usize ,
249
+ /// The byte offset within the file to start mapping.
250
+ offset : u64 = 0 ,
251
+ /// Optionally, a pointer which hints to the kernel where to place the map in virtual memory.
252
+ hint : ? [* ]align (std .mem .page_size ) u8 = null ,
253
+ };
254
+
255
+ /// Memory-map a range within a section into virtual memory.
256
+ pub fn MapViewOfSection (
257
+ section_handle : HANDLE ,
258
+ options : MapViewOfSectionOptions ,
259
+ ) MapViewOfFileError ! []align (std .mem .page_size ) volatile u8 {
260
+ const process_handle = GetCurrentProcess ();
261
+ var result : usize = @intFromPtr (options .hint );
262
+ var section_offset : LARGE_INTEGER = @intCast (options .offset );
263
+ var actual_length : SIZE_T = @intCast (options .length );
264
+
265
+ const status = ntdll .NtMapViewOfSection (
266
+ section_handle ,
267
+ process_handle ,
268
+ @ptrCast (& result ),
269
+ null , // zero bits
270
+ 0 , // commit size
271
+ & section_offset ,
272
+ & actual_length ,
273
+ options .inheritance ,
274
+ 0 , // allocation type
275
+ options .protection ,
276
+ );
277
+ switch (status ) {
278
+ .SUCCESS = > {
279
+ // Ensure the actual size meets the requested size.
280
+ std .debug .assert (actual_length >= options .length );
281
+ const ptr : [* ]align (std .mem .page_size ) volatile u8 = @ptrFromInt (result );
282
+ return ptr [0.. options .length ];
283
+ },
284
+ // TODO: Map out all the possible failure modes of this function. It isn't documented
285
+ // anywhere immediately obvious, so it will have to be done as and when people find errors
286
+ // from which they want to recover.
287
+ else = > return unexpectedStatus (status ),
288
+ }
289
+ }
290
+
291
+ pub fn UnmapViewOfSection (base : [* ]align (std.mem.page_size ) u8 ) void {
292
+ const process_handle = GetCurrentProcess ();
293
+ const status = ntdll .NtUnmapViewOfSection (process_handle , @ptrCast (base ));
294
+ std .debug .assert (status == .SUCCESS ); // Resource deallocation must succeed.
295
+ }
296
+
157
297
pub fn GetCurrentProcess () HANDLE {
158
298
const process_pseudo_handle : usize = @bitCast (@as (isize , -1 ));
159
299
return @ptrFromInt (process_pseudo_handle );
0 commit comments