pub struct VmiEvent<Arch>where
Arch: Architecture,{ /* private fields */ }Expand description
An event that occurred during VMI.
Implementations§
Source§impl<Arch> VmiEvent<Arch>where
Arch: Architecture,
impl<Arch> VmiEvent<Arch>where
Arch: Architecture,
Sourcepub fn new(
vcpu_id: VcpuId,
flags: VmiEventFlags,
view: Option<View>,
registers: <Arch as Architecture>::Registers,
reason: <Arch as Architecture>::EventReason,
) -> VmiEvent<Arch>
Available on crate features utils and injector only.
pub fn new( vcpu_id: VcpuId, flags: VmiEventFlags, view: Option<View>, registers: <Arch as Architecture>::Registers, reason: <Arch as Architecture>::EventReason, ) -> VmiEvent<Arch>
utils and injector only.Creates a new VMI event.
Sourcepub fn vcpu_id(&self) -> VcpuId
Available on crate features utils and injector only.
pub fn vcpu_id(&self) -> VcpuId
utils and injector only.Returns the ID of the virtual CPU where the event occurred.
Examples found in repository?
examples/windows-breakpoint-manager.rs (line 259)
240 fn memory_access(
241 &mut self,
242 vmi: &VmiContext<WindowsOs<Driver>>,
243 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
244 let memory_access = vmi.event().reason().as_memory_access();
245
246 tracing::trace!(
247 pa = %memory_access.pa,
248 va = %memory_access.va,
249 access = %memory_access.access,
250 );
251
252 if memory_access.access.contains(MemoryAccess::W) {
253 // It is assumed that a write memory access event is caused by a
254 // page table modification.
255 //
256 // The page table entry is marked as dirty in the page table monitor
257 // and a singlestep is performed to process the dirty entries.
258 self.ptm
259 .mark_dirty_entry(memory_access.pa, self.view, vmi.event().vcpu_id());
260
261 Ok(VmiEventResponse::singlestep().with_view(vmi.default_view()))
262 }
263 else if memory_access.access.contains(MemoryAccess::R) {
264 // When the guest tries to read from the memory, a fast-singlestep
265 // is performed over the instruction that tried to read the memory.
266 // This is done to allow the instruction to read the original memory
267 // content.
268 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
269 }
270 else {
271 panic!("Unhandled memory access: {memory_access:?}");
272 }
273 }
274
275 #[tracing::instrument(skip_all, fields(pid, process))]
276 fn interrupt(
277 &mut self,
278 vmi: &VmiContext<WindowsOs<Driver>>,
279 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
280 let tag = match self.bpm.get_by_event(vmi.event(), ()) {
281 Some(breakpoints) => {
282 // Breakpoints can have multiple tags, but we have set only one
283 // tag for each breakpoint.
284 let first_breakpoint = breakpoints.into_iter().next().expect("breakpoint");
285 first_breakpoint.tag()
286 }
287 None => {
288 if BreakpointController::is_breakpoint(vmi, vmi.event())? {
289 // This breakpoint was not set by us. Reinject it.
290 tracing::warn!("Unknown breakpoint, reinjecting");
291 return Ok(VmiEventResponse::reinject_interrupt());
292 }
293 else {
294 // We have received a breakpoint event, but there is no
295 // breakpoint instruction at the current memory location.
296 // This can happen if the event was triggered by a breakpoint
297 // we just removed.
298 tracing::warn!("Ignoring old breakpoint event");
299 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
300 }
301 }
302 };
303
304 let process = vmi.os().current_process()?;
305 let process_id = process.id()?;
306 let process_name = process.name()?;
307 tracing::Span::current()
308 .record("pid", process_id.0)
309 .record("process", process_name);
310
311 match tag {
312 "NtCreateFile" => self.NtCreateFile(vmi)?,
313 "NtWriteFile" => self.NtWriteFile(vmi)?,
314 "PspInsertProcess" => self.PspInsertProcess(vmi)?,
315 "MmCleanProcessAddressSpace" => self.MmCleanProcessAddressSpace(vmi)?,
316 _ => panic!("Unhandled tag: {tag}"),
317 }
318
319 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
320 }
321
322 #[tracing::instrument(skip_all)]
323 fn singlestep(
324 &mut self,
325 vmi: &VmiContext<WindowsOs<Driver>>,
326 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
327 // Get the page table modifications by processing the dirty page table
328 // entries.
329 let ptm_events = self.ptm.process_dirty_entries(vmi, vmi.event().vcpu_id())?;
330
331 // Let the breakpoint controller handle the page table modifications.
332 self.bpm.handle_ptm_events(vmi, ptm_events)?;
333
334 // Disable singlestep and switch back to our view.
335 Ok(VmiEventResponse::default().with_view(self.view))
336 }
337
338 #[tracing::instrument(skip_all)]
339 fn NtCreateFile(&mut self, vmi: &VmiContext<WindowsOs<Driver>>) -> Result<(), VmiError> {
340 //
341 // NTSTATUS
342 // NtCreateFile (
343 // _Out_ PHANDLE FileHandle,
344 // _In_ ACCESS_MASK DesiredAccess,
345 // _In_ POBJECT_ATTRIBUTES ObjectAttributes,
346 // _Out_ PIO_STATUS_BLOCK IoStatusBlock,
347 // _In_opt_ PLARGE_INTEGER AllocationSize,
348 // _In_ ULONG FileAttributes,
349 // _In_ ULONG ShareAccess,
350 // _In_ ULONG CreateDisposition,
351 // _In_ ULONG CreateOptions,
352 // _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
353 // _In_ ULONG EaLength
354 // );
355 //
356
357 let ObjectAttributes = Va(vmi.os().function_argument(2)?);
358
359 let object_attributes = vmi.os().object_attributes(ObjectAttributes)?;
360 let object_name = match object_attributes.object_name()? {
361 Some(object_name) => object_name,
362 None => {
363 tracing::warn!(%ObjectAttributes, "No object name found");
364 return Ok(());
365 }
366 };
367
368 tracing::info!(%object_name);
369
370 Ok(())
371 }
372
373 #[tracing::instrument(skip_all)]
374 fn NtWriteFile(&mut self, vmi: &VmiContext<WindowsOs<Driver>>) -> Result<(), VmiError> {
375 //
376 // NTSTATUS
377 // NtWriteFile (
378 // _In_ HANDLE FileHandle,
379 // _In_opt_ HANDLE Event,
380 // _In_opt_ PIO_APC_ROUTINE ApcRoutine,
381 // _In_opt_ PVOID ApcContext,
382 // _Out_ PIO_STATUS_BLOCK IoStatusBlock,
383 // _In_reads_bytes_(Length) PVOID Buffer,
384 // _In_ ULONG Length,
385 // _In_opt_ PLARGE_INTEGER ByteOffset,
386 // _In_opt_ PULONG Key
387 // );
388 //
389
390 let FileHandle = vmi.os().function_argument(0)?;
391
392 let file_object = match vmi
393 .os()
394 .current_process()?
395 .lookup_object::<WindowsFileObject<_>>(FileHandle)?
396 {
397 Some(file_object) => file_object,
398 None => {
399 tracing::warn!(FileHandle = %Hex(FileHandle), "No object found");
400 return Ok(());
401 }
402 };
403
404 let path = file_object.full_path()?;
405 tracing::info!(%path);
406
407 Ok(())
408 }
409
410 #[tracing::instrument(skip_all)]
411 fn PspInsertProcess(&mut self, vmi: &VmiContext<WindowsOs<Driver>>) -> Result<(), VmiError> {
412 //
413 // NTSTATUS
414 // PspInsertProcess (
415 // _In_ PEPROCESS NewProcess,
416 // _In_ PEPROCESS Parent,
417 // _In_ ULONG DesiredAccess,
418 // _In_ ULONG CreateFlags,
419 // ...
420 // );
421 //
422
423 let NewProcess = vmi.os().function_argument(0)?;
424 let Parent = vmi.os().function_argument(1)?;
425
426 let process = vmi.os().process(ProcessObject(Va(NewProcess)))?;
427 let process_id = process.id()?;
428
429 let parent_process = vmi.os().process(ProcessObject(Va(Parent)))?;
430 let parent_process_id = parent_process.id()?;
431
432 // We rely heavily on the 2nd argument to be the parent process object.
433 // If that ever changes, this assertion should catch it.
434 //
435 // So far it is verified that it works for Windows 7 up to Windows 11
436 // (23H2, build 22631).
437 debug_assert_eq!(parent_process_id, process.parent_id()?);
438
439 let name = process.name()?;
440 let image_base = process.image_base()?;
441 let peb = process.peb()?;
442
443 tracing::info!(
444 %process_id,
445 name,
446 %image_base,
447 ?peb,
448 );
449
450 Ok(())
451 }
452
453 #[tracing::instrument(skip_all)]
454 fn MmCleanProcessAddressSpace(
455 &mut self,
456 vmi: &VmiContext<WindowsOs<Driver>>,
457 ) -> Result<(), VmiError> {
458 //
459 // VOID
460 // MmCleanProcessAddressSpace (
461 // _In_ PEPROCESS Process
462 // );
463 //
464
465 let Process = vmi.os().function_argument(0)?;
466
467 let process = vmi.os().process(ProcessObject(Va(Process)))?;
468 let process_id = process.id()?;
469
470 let name = process.name()?;
471 let image_base = process.image_base()?;
472
473 tracing::info!(%process_id, name, %image_base);
474
475 Ok(())
476 }
477
478 fn dispatch(
479 &mut self,
480 vmi: &VmiContext<WindowsOs<Driver>>,
481 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
482 let event = vmi.event();
483 let result = match event.reason() {
484 EventReason::MemoryAccess(_) => self.memory_access(vmi),
485 EventReason::Interrupt(_) => self.interrupt(vmi),
486 EventReason::Singlestep(_) => self.singlestep(vmi),
487 _ => panic!("Unhandled event: {:?}", event.reason()),
488 };
489
490 // If VMI tries to read from a page that is not present, it will return
491 // a page fault error. In this case, we inject a page fault interrupt
492 // to the guest.
493 //
494 // Once the guest handles the page fault, it will retry to execute the
495 // instruction that caused the page fault.
496 if let Err(VmiError::Translation(pfs)) = result {
497 tracing::warn!(?pfs, "Page fault, injecting");
498 vmi.inject_interrupt(event.vcpu_id(), Interrupt::page_fault(pfs[0].va, 0))?;
499 return Ok(VmiEventResponse::default());
500 }
501
502 result
503 }Sourcepub fn flags(&self) -> VmiEventFlags
Available on crate features utils and injector only.
pub fn flags(&self) -> VmiEventFlags
utils and injector only.Returns flags associated with the event.
Sourcepub fn view(&self) -> Option<View>
Available on crate features utils and injector only.
pub fn view(&self) -> Option<View>
utils and injector only.Returns the view associated with the event, if any.
Sourcepub fn registers(&self) -> &<Arch as Architecture>::Registers
Available on crate features utils and injector only.
pub fn registers(&self) -> &<Arch as Architecture>::Registers
utils and injector only.Returns a reference to the CPU registers at the time of the event.
Sourcepub fn reason(&self) -> &<Arch as Architecture>::EventReason
Available on crate features utils and injector only.
pub fn reason(&self) -> &<Arch as Architecture>::EventReason
utils and injector only.Returns a reference to the reason for the event.
Examples found in repository?
examples/windows-breakpoint-manager.rs (line 244)
240 fn memory_access(
241 &mut self,
242 vmi: &VmiContext<WindowsOs<Driver>>,
243 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
244 let memory_access = vmi.event().reason().as_memory_access();
245
246 tracing::trace!(
247 pa = %memory_access.pa,
248 va = %memory_access.va,
249 access = %memory_access.access,
250 );
251
252 if memory_access.access.contains(MemoryAccess::W) {
253 // It is assumed that a write memory access event is caused by a
254 // page table modification.
255 //
256 // The page table entry is marked as dirty in the page table monitor
257 // and a singlestep is performed to process the dirty entries.
258 self.ptm
259 .mark_dirty_entry(memory_access.pa, self.view, vmi.event().vcpu_id());
260
261 Ok(VmiEventResponse::singlestep().with_view(vmi.default_view()))
262 }
263 else if memory_access.access.contains(MemoryAccess::R) {
264 // When the guest tries to read from the memory, a fast-singlestep
265 // is performed over the instruction that tried to read the memory.
266 // This is done to allow the instruction to read the original memory
267 // content.
268 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
269 }
270 else {
271 panic!("Unhandled memory access: {memory_access:?}");
272 }
273 }
274
275 #[tracing::instrument(skip_all, fields(pid, process))]
276 fn interrupt(
277 &mut self,
278 vmi: &VmiContext<WindowsOs<Driver>>,
279 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
280 let tag = match self.bpm.get_by_event(vmi.event(), ()) {
281 Some(breakpoints) => {
282 // Breakpoints can have multiple tags, but we have set only one
283 // tag for each breakpoint.
284 let first_breakpoint = breakpoints.into_iter().next().expect("breakpoint");
285 first_breakpoint.tag()
286 }
287 None => {
288 if BreakpointController::is_breakpoint(vmi, vmi.event())? {
289 // This breakpoint was not set by us. Reinject it.
290 tracing::warn!("Unknown breakpoint, reinjecting");
291 return Ok(VmiEventResponse::reinject_interrupt());
292 }
293 else {
294 // We have received a breakpoint event, but there is no
295 // breakpoint instruction at the current memory location.
296 // This can happen if the event was triggered by a breakpoint
297 // we just removed.
298 tracing::warn!("Ignoring old breakpoint event");
299 return Ok(VmiEventResponse::fast_singlestep(vmi.default_view()));
300 }
301 }
302 };
303
304 let process = vmi.os().current_process()?;
305 let process_id = process.id()?;
306 let process_name = process.name()?;
307 tracing::Span::current()
308 .record("pid", process_id.0)
309 .record("process", process_name);
310
311 match tag {
312 "NtCreateFile" => self.NtCreateFile(vmi)?,
313 "NtWriteFile" => self.NtWriteFile(vmi)?,
314 "PspInsertProcess" => self.PspInsertProcess(vmi)?,
315 "MmCleanProcessAddressSpace" => self.MmCleanProcessAddressSpace(vmi)?,
316 _ => panic!("Unhandled tag: {tag}"),
317 }
318
319 Ok(VmiEventResponse::fast_singlestep(vmi.default_view()))
320 }
321
322 #[tracing::instrument(skip_all)]
323 fn singlestep(
324 &mut self,
325 vmi: &VmiContext<WindowsOs<Driver>>,
326 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
327 // Get the page table modifications by processing the dirty page table
328 // entries.
329 let ptm_events = self.ptm.process_dirty_entries(vmi, vmi.event().vcpu_id())?;
330
331 // Let the breakpoint controller handle the page table modifications.
332 self.bpm.handle_ptm_events(vmi, ptm_events)?;
333
334 // Disable singlestep and switch back to our view.
335 Ok(VmiEventResponse::default().with_view(self.view))
336 }
337
338 #[tracing::instrument(skip_all)]
339 fn NtCreateFile(&mut self, vmi: &VmiContext<WindowsOs<Driver>>) -> Result<(), VmiError> {
340 //
341 // NTSTATUS
342 // NtCreateFile (
343 // _Out_ PHANDLE FileHandle,
344 // _In_ ACCESS_MASK DesiredAccess,
345 // _In_ POBJECT_ATTRIBUTES ObjectAttributes,
346 // _Out_ PIO_STATUS_BLOCK IoStatusBlock,
347 // _In_opt_ PLARGE_INTEGER AllocationSize,
348 // _In_ ULONG FileAttributes,
349 // _In_ ULONG ShareAccess,
350 // _In_ ULONG CreateDisposition,
351 // _In_ ULONG CreateOptions,
352 // _In_reads_bytes_opt_(EaLength) PVOID EaBuffer,
353 // _In_ ULONG EaLength
354 // );
355 //
356
357 let ObjectAttributes = Va(vmi.os().function_argument(2)?);
358
359 let object_attributes = vmi.os().object_attributes(ObjectAttributes)?;
360 let object_name = match object_attributes.object_name()? {
361 Some(object_name) => object_name,
362 None => {
363 tracing::warn!(%ObjectAttributes, "No object name found");
364 return Ok(());
365 }
366 };
367
368 tracing::info!(%object_name);
369
370 Ok(())
371 }
372
373 #[tracing::instrument(skip_all)]
374 fn NtWriteFile(&mut self, vmi: &VmiContext<WindowsOs<Driver>>) -> Result<(), VmiError> {
375 //
376 // NTSTATUS
377 // NtWriteFile (
378 // _In_ HANDLE FileHandle,
379 // _In_opt_ HANDLE Event,
380 // _In_opt_ PIO_APC_ROUTINE ApcRoutine,
381 // _In_opt_ PVOID ApcContext,
382 // _Out_ PIO_STATUS_BLOCK IoStatusBlock,
383 // _In_reads_bytes_(Length) PVOID Buffer,
384 // _In_ ULONG Length,
385 // _In_opt_ PLARGE_INTEGER ByteOffset,
386 // _In_opt_ PULONG Key
387 // );
388 //
389
390 let FileHandle = vmi.os().function_argument(0)?;
391
392 let file_object = match vmi
393 .os()
394 .current_process()?
395 .lookup_object::<WindowsFileObject<_>>(FileHandle)?
396 {
397 Some(file_object) => file_object,
398 None => {
399 tracing::warn!(FileHandle = %Hex(FileHandle), "No object found");
400 return Ok(());
401 }
402 };
403
404 let path = file_object.full_path()?;
405 tracing::info!(%path);
406
407 Ok(())
408 }
409
410 #[tracing::instrument(skip_all)]
411 fn PspInsertProcess(&mut self, vmi: &VmiContext<WindowsOs<Driver>>) -> Result<(), VmiError> {
412 //
413 // NTSTATUS
414 // PspInsertProcess (
415 // _In_ PEPROCESS NewProcess,
416 // _In_ PEPROCESS Parent,
417 // _In_ ULONG DesiredAccess,
418 // _In_ ULONG CreateFlags,
419 // ...
420 // );
421 //
422
423 let NewProcess = vmi.os().function_argument(0)?;
424 let Parent = vmi.os().function_argument(1)?;
425
426 let process = vmi.os().process(ProcessObject(Va(NewProcess)))?;
427 let process_id = process.id()?;
428
429 let parent_process = vmi.os().process(ProcessObject(Va(Parent)))?;
430 let parent_process_id = parent_process.id()?;
431
432 // We rely heavily on the 2nd argument to be the parent process object.
433 // If that ever changes, this assertion should catch it.
434 //
435 // So far it is verified that it works for Windows 7 up to Windows 11
436 // (23H2, build 22631).
437 debug_assert_eq!(parent_process_id, process.parent_id()?);
438
439 let name = process.name()?;
440 let image_base = process.image_base()?;
441 let peb = process.peb()?;
442
443 tracing::info!(
444 %process_id,
445 name,
446 %image_base,
447 ?peb,
448 );
449
450 Ok(())
451 }
452
453 #[tracing::instrument(skip_all)]
454 fn MmCleanProcessAddressSpace(
455 &mut self,
456 vmi: &VmiContext<WindowsOs<Driver>>,
457 ) -> Result<(), VmiError> {
458 //
459 // VOID
460 // MmCleanProcessAddressSpace (
461 // _In_ PEPROCESS Process
462 // );
463 //
464
465 let Process = vmi.os().function_argument(0)?;
466
467 let process = vmi.os().process(ProcessObject(Va(Process)))?;
468 let process_id = process.id()?;
469
470 let name = process.name()?;
471 let image_base = process.image_base()?;
472
473 tracing::info!(%process_id, name, %image_base);
474
475 Ok(())
476 }
477
478 fn dispatch(
479 &mut self,
480 vmi: &VmiContext<WindowsOs<Driver>>,
481 ) -> Result<VmiEventResponse<Amd64>, VmiError> {
482 let event = vmi.event();
483 let result = match event.reason() {
484 EventReason::MemoryAccess(_) => self.memory_access(vmi),
485 EventReason::Interrupt(_) => self.interrupt(vmi),
486 EventReason::Singlestep(_) => self.singlestep(vmi),
487 _ => panic!("Unhandled event: {:?}", event.reason()),
488 };
489
490 // If VMI tries to read from a page that is not present, it will return
491 // a page fault error. In this case, we inject a page fault interrupt
492 // to the guest.
493 //
494 // Once the guest handles the page fault, it will retry to execute the
495 // instruction that caused the page fault.
496 if let Err(VmiError::Translation(pfs)) = result {
497 tracing::warn!(?pfs, "Page fault, injecting");
498 vmi.inject_interrupt(event.vcpu_id(), Interrupt::page_fault(pfs[0].va, 0))?;
499 return Ok(VmiEventResponse::default());
500 }
501
502 result
503 }Trait Implementations§
Source§impl<Arch> Clone for VmiEvent<Arch>where
Arch: Clone + Architecture,
<Arch as Architecture>::Registers: Clone,
<Arch as Architecture>::EventReason: Clone,
impl<Arch> Clone for VmiEvent<Arch>where
Arch: Clone + Architecture,
<Arch as Architecture>::Registers: Clone,
<Arch as Architecture>::EventReason: Clone,
Source§impl<Arch> Debug for VmiEvent<Arch>where
Arch: Debug + Architecture,
<Arch as Architecture>::Registers: Debug,
<Arch as Architecture>::EventReason: Debug,
impl<Arch> Debug for VmiEvent<Arch>where
Arch: Debug + Architecture,
<Arch as Architecture>::Registers: Debug,
<Arch as Architecture>::EventReason: Debug,
impl<Arch> Copy for VmiEvent<Arch>where
Arch: Copy + Architecture,
<Arch as Architecture>::Registers: Copy,
<Arch as Architecture>::EventReason: Copy,
Auto Trait Implementations§
impl<Arch> Freeze for VmiEvent<Arch>
impl<Arch> RefUnwindSafe for VmiEvent<Arch>where
<Arch as Architecture>::Registers: RefUnwindSafe,
<Arch as Architecture>::EventReason: RefUnwindSafe,
impl<Arch> Send for VmiEvent<Arch>
impl<Arch> Sync for VmiEvent<Arch>
impl<Arch> Unpin for VmiEvent<Arch>
impl<Arch> UnsafeUnpin for VmiEvent<Arch>where
<Arch as Architecture>::Registers: UnsafeUnpin,
<Arch as Architecture>::EventReason: UnsafeUnpin,
impl<Arch> UnwindSafe for VmiEvent<Arch>where
<Arch as Architecture>::Registers: UnwindSafe,
<Arch as Architecture>::EventReason: UnwindSafe,
Blanket Implementations§
Source§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
Source§type ArchivedMetadata = ()
type ArchivedMetadata = ()
The archived version of the pointer metadata for this type.
Source§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Converts some archived metadata to the pointer metadata for itself.
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> LayoutRaw for T
impl<T> LayoutRaw for T
Source§fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>
Returns the layout of the type.
Source§impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
Source§unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool
Returns whether the given value has been niched. Read more
Source§fn resolve_niched(out: Place<NichedOption<T, N1>>)
fn resolve_niched(out: Place<NichedOption<T, N1>>)
Writes data to
out indicating that a T is niched.