@@ -4,6 +4,7 @@ use rustc_span::Symbol;
44use rustc_target:: abi:: Size ;
55use rustc_target:: spec:: abi:: Abi ;
66
7+ use crate :: helpers:: check_arg_count;
78use crate :: * ;
89use shims:: foreign_items:: EmulateByNameResult ;
910use shims:: windows:: handle:: { EvalContextExt as _, Handle , PseudoHandle } ;
@@ -453,10 +454,70 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
453454 // FIXME: this should return a nonzero value if this call does result in switching to another thread.
454455 this. write_null ( dest) ?;
455456 }
457+ "NtWriteFile" if this. frame_in_std ( ) => {
458+ this. check_abi_and_shim_symbol_clash (
459+ abi,
460+ Abi :: System { unwind : false } ,
461+ link_name,
462+ ) ?;
463+ nt_write_file ( this, args, dest) ?;
464+ }
456465
457466 _ => return Ok ( EmulateByNameResult :: NotSupported ) ,
458467 }
459468
460469 Ok ( EmulateByNameResult :: NeedsJumping )
461470 }
462471}
472+
473+ // Incomplete implementation of `NtWriteFile`.
474+ pub ( super ) fn nt_write_file < ' a , ' mir , ' tcx > (
475+ this : & ' a mut MiriInterpCx < ' mir , ' tcx > ,
476+ args : & [ OpTy < ' tcx , Provenance > ] ,
477+ dest : & PlaceTy < ' tcx , Provenance > ,
478+ ) -> InterpResult < ' tcx , ( ) > {
479+ let [ handle, _event, _apc_routine, _apc_context, io_status_block, buf, n, byte_offset, _key] =
480+ check_arg_count ( args) ?;
481+
482+ let handle = this. read_scalar ( handle) ?. to_machine_isize ( this) ?;
483+ let buf = this. read_pointer ( buf) ?;
484+ let n = this. read_scalar ( n) ?. to_u32 ( ) ?;
485+ let byte_offset = this. read_scalar ( byte_offset) ?. to_machine_usize ( this) ?; // is actually a pointer
486+ let io_status_block = this. deref_operand ( io_status_block) ?;
487+
488+ if byte_offset != 0 {
489+ throw_unsup_format ! (
490+ "`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported"
491+ ) ;
492+ }
493+
494+ let written = if handle == -11 || handle == -12 {
495+ // stdout/stderr
496+ use std:: io:: { self , Write } ;
497+
498+ let buf_cont = this. read_bytes_ptr_strip_provenance ( buf, Size :: from_bytes ( u64:: from ( n) ) ) ?;
499+ let res = if this. machine . mute_stdout_stderr {
500+ Ok ( buf_cont. len ( ) )
501+ } else if handle == -11 {
502+ io:: stdout ( ) . write ( buf_cont)
503+ } else {
504+ io:: stderr ( ) . write ( buf_cont)
505+ } ;
506+ // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
507+ res. ok ( ) . map ( |n| u32:: try_from ( n) . unwrap ( ) )
508+ } else {
509+ throw_unsup_format ! ( "on Windows, writing to anything except stdout/stderr is not supported" )
510+ } ;
511+ // We have to put the result into io_status_block.
512+ if let Some ( n) = written {
513+ let io_status_information = this. mplace_field_named ( & io_status_block, "Information" ) ?;
514+ this. write_scalar (
515+ Scalar :: from_machine_usize ( n. into ( ) , this) ,
516+ & io_status_information. into ( ) ,
517+ ) ?;
518+ }
519+ // Return whether this was a success. >= 0 is success.
520+ // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
521+ this. write_scalar ( Scalar :: from_u32 ( if written. is_some ( ) { 0 } else { 0xC0000185u32 } ) , dest) ?;
522+ Ok ( ( ) )
523+ }
0 commit comments