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?
This commit is contained in:
bors 2022-10-20 11:09:41 +00:00
commit 4dcf51b08f
6 changed files with 111 additions and 3 deletions

View File

@ -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]);
}
```

View File

@ -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 @@ fn emulate_foreign_item_by_name(
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)?;

View File

@ -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() {}

View File

@ -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

View File

@ -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
}

View File

@ -0,0 +1 @@
hello, world!