about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/miri/README.md10
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs19
-rw-r--r--src/tools/miri/tests/fail/panic/no_std.rs41
-rw-r--r--src/tools/miri/tests/fail/panic/no_std.stderr19
-rw-r--r--src/tools/miri/tests/pass/no_std.rs24
-rw-r--r--src/tools/miri/tests/pass/no_std.stdout1
6 files changed, 111 insertions, 3 deletions
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 32e616cb074..4f5d406288f 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -542,6 +542,16 @@ extern "Rust" {
     /// In particular, users should be aware that Miri will periodically attempt to garbage collect the
     /// contents of all stacks. Callers of this function may wish to pass `-Zmiri-tag-gc=0` to disable the GC.
     fn miri_print_stacks(alloc_id: u64);
+
+    /// Miri-provided extern function to print (from the interpreter, not the
+    /// program) the contents of a section of program memory, as bytes. Bytes
+    /// written using this function will emerge from the interpreter's stdout.
+    fn miri_write_to_stdout(bytes: &[u8]);
+
+    /// Miri-provided extern function to print (from the interpreter, not the
+    /// program) the contents of a section of program memory, as bytes. Bytes
+    /// written using this function will emerge from the interpreter's stderr.
+    fn miri_write_to_stderr(bytes: &[u8]);
 }
 ```
 
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 7e6a9595161..a49e6ba4ce3 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -1,4 +1,4 @@
-use std::{collections::hash_map::Entry, iter};
+use std::{collections::hash_map::Entry, io::Write, iter};
 
 use log::trace;
 
@@ -462,6 +462,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 this.handle_miri_resolve_frame_names(abi, link_name, args)?;
             }
 
+            // Writes some bytes to the interpreter's stdout/stderr. See the
+            // README for details.
+            "miri_write_to_stdout" | "miri_write_to_stderr" => {
+                let [bytes] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let (ptr, len) = this.read_immediate(bytes)?.to_scalar_pair();
+                let ptr = ptr.to_pointer(this)?;
+                let len = len.to_machine_usize(this)?;
+                let msg = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
+
+                // Note: we're ignoring errors writing to host stdout/stderr.
+                let _ignore = match link_name.as_str() {
+                    "miri_write_to_stdout" => std::io::stdout().write_all(msg),
+                    "miri_write_to_stderr" => std::io::stderr().write_all(msg),
+                    _ => unreachable!(),
+                };
+            }
+
             // Standard C allocation
             "malloc" => {
                 let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
diff --git a/src/tools/miri/tests/fail/panic/no_std.rs b/src/tools/miri/tests/fail/panic/no_std.rs
new file mode 100644
index 00000000000..b6a5c075570
--- /dev/null
+++ b/src/tools/miri/tests/fail/panic/no_std.rs
@@ -0,0 +1,41 @@
+#![feature(lang_items, start, core_intrinsics)]
+#![no_std]
+// windows tls dtors go through libstd right now, thus this test
+// cannot pass. When windows tls dtors go through the special magic
+// windows linker section, we can run this test on windows again.
+//@ignore-target-windows
+
+// Plumbing to let us use `writeln!` to host stderr:
+
+extern "Rust" {
+    fn miri_write_to_stderr(bytes: &[u8]);
+}
+
+struct HostErr;
+
+use core::fmt::Write;
+
+impl Write for HostErr {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        unsafe {
+            miri_write_to_stderr(s.as_bytes());
+        }
+        Ok(())
+    }
+}
+
+// Aaaand the test:
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+    panic!("blarg I am dead")
+}
+
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+    writeln!(HostErr, "{panic_info}").ok();
+    core::intrinsics::abort(); //~ ERROR: the program aborted execution
+}
+
+#[lang = "eh_personality"]
+fn eh_personality() {}
diff --git a/src/tools/miri/tests/fail/panic/no_std.stderr b/src/tools/miri/tests/fail/panic/no_std.stderr
new file mode 100644
index 00000000000..568b286e1d3
--- /dev/null
+++ b/src/tools/miri/tests/fail/panic/no_std.stderr
@@ -0,0 +1,19 @@
+panicked at 'blarg I am dead', $DIR/no_std.rs:LL:CC
+error: abnormal termination: the program aborted execution
+  --> $DIR/no_std.rs:LL:CC
+   |
+LL |     core::intrinsics::abort();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution
+   |
+   = note: inside `panic_handler` at $DIR/no_std.rs:LL:CC
+note: inside `start` at RUSTLIB/core/src/panic.rs:LL:CC
+  --> $DIR/no_std.rs:LL:CC
+   |
+LL |     panic!("blarg I am dead")
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/pass/no_std.rs b/src/tools/miri/tests/pass/no_std.rs
index 10632c2cce4..0203edfe181 100644
--- a/src/tools/miri/tests/pass/no_std.rs
+++ b/src/tools/miri/tests/pass/no_std.rs
@@ -5,10 +5,30 @@
 // windows linker section, we can run this test on windows again.
 //@ignore-target-windows
 
+// Plumbing to let us use `writeln!` to host stdout:
+
+extern "Rust" {
+    fn miri_write_to_stdout(bytes: &[u8]);
+}
+
+struct Host;
+
+use core::fmt::Write;
+
+impl Write for Host {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        unsafe {
+            miri_write_to_stdout(s.as_bytes());
+        }
+        Ok(())
+    }
+}
+
+// Aaaand the test:
+
 #[start]
 fn start(_: isize, _: *const *const u8) -> isize {
-    for _ in 0..10 {}
-
+    writeln!(Host, "hello, world!").unwrap();
     0
 }
 
diff --git a/src/tools/miri/tests/pass/no_std.stdout b/src/tools/miri/tests/pass/no_std.stdout
new file mode 100644
index 00000000000..270c611ee72
--- /dev/null
+++ b/src/tools/miri/tests/pass/no_std.stdout
@@ -0,0 +1 @@
+hello, world!