Skip to content

Commit cd6b856

Browse files
committed
Auto merge of #2606 - cbiffle:nostdio, r=RalfJung
Magic functions for writing to stdout/stderr. This enables I/O in no_std contexts (or, really, any Miri-specific OS-independent context). Combined with the `abort` intrinsic it should allow a reasonable test framework in no_std. **Question for maintainers:** So, the `no_std` panic test needs work, for two reasons: - First, its stdout includes Miri's whole message about the abort intrinsic having been used. I guess whatever panic handler you use in `std` contexts exits cleanly without triggering this message. Comparing the entire output with backtrace as golden seems fragile. - Second, likely for the same reason, the test framework appears to expect the test to exit successfully, when in fact it exits with status 1 due to the abort. This means the test doesn't actually pass right now. What shall I do there?
2 parents 13573e8 + 15b3a49 commit cd6b856

File tree

6 files changed

+111
-3
lines changed

6 files changed

+111
-3
lines changed

README.md

+10
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,16 @@ extern "Rust" {
542542
/// In particular, users should be aware that Miri will periodically attempt to garbage collect the
543543
/// contents of all stacks. Callers of this function may wish to pass `-Zmiri-tag-gc=0` to disable the GC.
544544
fn miri_print_stacks(alloc_id: u64);
545+
546+
/// Miri-provided extern function to print (from the interpreter, not the
547+
/// program) the contents of a section of program memory, as bytes. Bytes
548+
/// written using this function will emerge from the interpreter's stdout.
549+
fn miri_write_to_stdout(bytes: &[u8]);
550+
551+
/// Miri-provided extern function to print (from the interpreter, not the
552+
/// program) the contents of a section of program memory, as bytes. Bytes
553+
/// written using this function will emerge from the interpreter's stderr.
554+
fn miri_write_to_stderr(bytes: &[u8]);
545555
}
546556
```
547557

src/shims/foreign_items.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{collections::hash_map::Entry, iter};
1+
use std::{collections::hash_map::Entry, io::Write, iter};
22

33
use log::trace;
44

@@ -462,6 +462,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
462462
this.handle_miri_resolve_frame_names(abi, link_name, args)?;
463463
}
464464

465+
// Writes some bytes to the interpreter's stdout/stderr. See the
466+
// README for details.
467+
"miri_write_to_stdout" | "miri_write_to_stderr" => {
468+
let [bytes] = this.check_shim(abi, Abi::Rust, link_name, args)?;
469+
let (ptr, len) = this.read_immediate(bytes)?.to_scalar_pair();
470+
let ptr = ptr.to_pointer(this)?;
471+
let len = len.to_machine_usize(this)?;
472+
let msg = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
473+
474+
// Note: we're ignoring errors writing to host stdout/stderr.
475+
let _ignore = match link_name.as_str() {
476+
"miri_write_to_stdout" => std::io::stdout().write_all(msg),
477+
"miri_write_to_stderr" => std::io::stderr().write_all(msg),
478+
_ => unreachable!(),
479+
};
480+
}
481+
465482
// Standard C allocation
466483
"malloc" => {
467484
let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;

tests/fail/panic/no_std.rs

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#![feature(lang_items, start, core_intrinsics)]
2+
#![no_std]
3+
// windows tls dtors go through libstd right now, thus this test
4+
// cannot pass. When windows tls dtors go through the special magic
5+
// windows linker section, we can run this test on windows again.
6+
//@ignore-target-windows
7+
8+
// Plumbing to let us use `writeln!` to host stderr:
9+
10+
extern "Rust" {
11+
fn miri_write_to_stderr(bytes: &[u8]);
12+
}
13+
14+
struct HostErr;
15+
16+
use core::fmt::Write;
17+
18+
impl Write for HostErr {
19+
fn write_str(&mut self, s: &str) -> core::fmt::Result {
20+
unsafe {
21+
miri_write_to_stderr(s.as_bytes());
22+
}
23+
Ok(())
24+
}
25+
}
26+
27+
// Aaaand the test:
28+
29+
#[start]
30+
fn start(_: isize, _: *const *const u8) -> isize {
31+
panic!("blarg I am dead")
32+
}
33+
34+
#[panic_handler]
35+
fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
36+
writeln!(HostErr, "{panic_info}").ok();
37+
core::intrinsics::abort(); //~ ERROR: the program aborted execution
38+
}
39+
40+
#[lang = "eh_personality"]
41+
fn eh_personality() {}

tests/fail/panic/no_std.stderr

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
panicked at 'blarg I am dead', $DIR/no_std.rs:LL:CC
2+
error: abnormal termination: the program aborted execution
3+
--> $DIR/no_std.rs:LL:CC
4+
|
5+
LL | core::intrinsics::abort();
6+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ the program aborted execution
7+
|
8+
= note: inside `panic_handler` at $DIR/no_std.rs:LL:CC
9+
note: inside `start` at RUSTLIB/core/src/panic.rs:LL:CC
10+
--> $DIR/no_std.rs:LL:CC
11+
|
12+
LL | panic!("blarg I am dead")
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
14+
= 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)
15+
16+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
17+
18+
error: aborting due to previous error
19+

tests/pass/no_std.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,30 @@
55
// windows linker section, we can run this test on windows again.
66
//@ignore-target-windows
77

8+
// Plumbing to let us use `writeln!` to host stdout:
9+
10+
extern "Rust" {
11+
fn miri_write_to_stdout(bytes: &[u8]);
12+
}
13+
14+
struct Host;
15+
16+
use core::fmt::Write;
17+
18+
impl Write for Host {
19+
fn write_str(&mut self, s: &str) -> core::fmt::Result {
20+
unsafe {
21+
miri_write_to_stdout(s.as_bytes());
22+
}
23+
Ok(())
24+
}
25+
}
26+
27+
// Aaaand the test:
28+
829
#[start]
930
fn start(_: isize, _: *const *const u8) -> isize {
10-
for _ in 0..10 {}
11-
31+
writeln!(Host, "hello, world!").unwrap();
1232
0
1333
}
1434

tests/pass/no_std.stdout

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
hello, world!

0 commit comments

Comments
 (0)