]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/dlsym.rs
pass clippy::cast_possible_truncation
[rust.git] / src / shims / windows / dlsym.rs
1 use rustc_middle::mir;
2 use rustc_target::abi::Size;
3 use rustc_target::spec::abi::Abi;
4
5 use log::trace;
6
7 use crate::helpers::check_arg_count;
8 use crate::*;
9
10 #[derive(Debug, Copy, Clone)]
11 pub enum Dlsym {
12     NtWriteFile,
13 }
14
15 impl Dlsym {
16     // Returns an error for unsupported symbols, and None if this symbol
17     // should become a NULL pointer (pretend it does not exist).
18     pub fn from_str<'tcx>(name: &str) -> InterpResult<'tcx, Option<Dlsym>> {
19         Ok(match name {
20             "GetSystemTimePreciseAsFileTime" => None,
21             "SetThreadDescription" => None,
22             "NtWriteFile" => Some(Dlsym::NtWriteFile),
23             _ => throw_unsup_format!("unsupported Windows dlsym: {}", name),
24         })
25     }
26 }
27
28 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
29 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
30     fn call_dlsym(
31         &mut self,
32         dlsym: Dlsym,
33         abi: Abi,
34         args: &[OpTy<'tcx, Provenance>],
35         dest: &PlaceTy<'tcx, Provenance>,
36         ret: Option<mir::BasicBlock>,
37     ) -> InterpResult<'tcx> {
38         let this = self.eval_context_mut();
39         let ret = ret.expect("we don't support any diverging dlsym");
40         assert!(this.tcx.sess.target.os == "windows");
41
42         this.check_abi(abi, Abi::System { unwind: false })?;
43
44         match dlsym {
45             Dlsym::NtWriteFile => {
46                 if !this.frame_in_std() {
47                     throw_unsup_format!(
48                         "NtWriteFile support is crude and just enough for stdout to work"
49                     );
50                 }
51
52                 let [
53                     handle,
54                     _event,
55                     _apc_routine,
56                     _apc_context,
57                     io_status_block,
58                     buf,
59                     n,
60                     byte_offset,
61                     _key,
62                 ] = check_arg_count(args)?;
63                 let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
64                 let buf = this.read_pointer(buf)?;
65                 let n = this.read_scalar(n)?.to_u32()?;
66                 let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer
67                 let io_status_block = this.deref_operand(io_status_block)?;
68
69                 if byte_offset != 0 {
70                     throw_unsup_format!(
71                         "NtWriteFile ByteOffset paremeter is non-null, which is unsupported"
72                     );
73                 }
74
75                 let written = if handle == -11 || handle == -12 {
76                     // stdout/stderr
77                     use std::io::{self, Write};
78
79                     let buf_cont = this.read_bytes_ptr(buf, Size::from_bytes(u64::from(n)))?;
80                     let res = if this.machine.mute_stdout_stderr {
81                         Ok(buf_cont.len())
82                     } else if handle == -11 {
83                         io::stdout().write(buf_cont)
84                     } else {
85                         io::stderr().write(buf_cont)
86                     };
87                     // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
88                     res.ok().map(|n| u32::try_from(n).unwrap())
89                 } else {
90                     throw_unsup_format!(
91                         "on Windows, writing to anything except stdout/stderr is not supported"
92                     )
93                 };
94                 // We have to put the result into io_status_block.
95                 if let Some(n) = written {
96                     let io_status_information =
97                         this.mplace_field_named(&io_status_block, "Information")?;
98                     this.write_scalar(
99                         Scalar::from_machine_usize(n.into(), this),
100                         &io_status_information.into(),
101                     )?;
102                 }
103                 // Return whether this was a success. >= 0 is success.
104                 // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
105                 this.write_scalar(
106                     Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
107                     dest,
108                 )?;
109             }
110         }
111
112         trace!("{:?}", this.dump_place(**dest));
113         this.go_to_block(ret);
114         Ok(())
115     }
116 }