Auto merge of #3769 - primoly:miri-start, r=RalfJung
Add `miri_start` support This PR uses a function with the exported symbol `miri_start` as a drop-in alternative to `#[start]`. So the signature stays the same as suggested in [this comment](https://github.com/rust-lang/miri/issues/3498#issuecomment-2088560526). <del>I’ve also removed Miri’s restriction to only work on bin crates as I don’t think this is necessary anymore.</del> Closes #3758
This commit is contained in:
commit
5852b24490
@ -474,6 +474,19 @@ Miri provides some `extern` functions that programs can import to access
|
|||||||
Miri-specific functionality. They are declared in
|
Miri-specific functionality. They are declared in
|
||||||
[/tests/utils/miri\_extern.rs](/tests/utils/miri_extern.rs).
|
[/tests/utils/miri\_extern.rs](/tests/utils/miri_extern.rs).
|
||||||
|
|
||||||
|
## Entry point for no-std binaries
|
||||||
|
|
||||||
|
Binaries that do not use the standard library are expected to declare a function like this so that
|
||||||
|
Miri knows where it is supposed to start execution:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[cfg(miri)]
|
||||||
|
#[no_mangle]
|
||||||
|
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
|
||||||
|
// Call the actual start function that your project implements, based on your target's conventions.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing and getting help
|
## Contributing and getting help
|
||||||
|
|
||||||
If you want to contribute to Miri, great! Please check out our
|
If you want to contribute to Miri, great! Please check out our
|
||||||
|
@ -14,11 +14,14 @@
|
|||||||
extern crate rustc_data_structures;
|
extern crate rustc_data_structures;
|
||||||
extern crate rustc_driver;
|
extern crate rustc_driver;
|
||||||
extern crate rustc_hir;
|
extern crate rustc_hir;
|
||||||
|
extern crate rustc_hir_analysis;
|
||||||
extern crate rustc_interface;
|
extern crate rustc_interface;
|
||||||
extern crate rustc_log;
|
extern crate rustc_log;
|
||||||
extern crate rustc_metadata;
|
extern crate rustc_metadata;
|
||||||
extern crate rustc_middle;
|
extern crate rustc_middle;
|
||||||
extern crate rustc_session;
|
extern crate rustc_session;
|
||||||
|
extern crate rustc_span;
|
||||||
|
extern crate rustc_target;
|
||||||
|
|
||||||
use std::env::{self, VarError};
|
use std::env::{self, VarError};
|
||||||
use std::num::NonZero;
|
use std::num::NonZero;
|
||||||
@ -27,9 +30,12 @@
|
|||||||
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_driver::Compilation;
|
use rustc_driver::Compilation;
|
||||||
|
use rustc_hir::def_id::LOCAL_CRATE;
|
||||||
use rustc_hir::{self as hir, Node};
|
use rustc_hir::{self as hir, Node};
|
||||||
|
use rustc_hir_analysis::check::check_function_signature;
|
||||||
use rustc_interface::interface::Config;
|
use rustc_interface::interface::Config;
|
||||||
use rustc_middle::{
|
use rustc_middle::{
|
||||||
middle::{
|
middle::{
|
||||||
@ -37,14 +43,15 @@
|
|||||||
exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel},
|
exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel},
|
||||||
},
|
},
|
||||||
query::LocalCrate,
|
query::LocalCrate,
|
||||||
ty::TyCtxt,
|
traits::{ObligationCause, ObligationCauseCode},
|
||||||
|
ty::{self, Ty, TyCtxt},
|
||||||
util::Providers,
|
util::Providers,
|
||||||
};
|
};
|
||||||
use rustc_session::config::{CrateType, ErrorOutputType, OptLevel};
|
use rustc_session::config::{CrateType, EntryFnType, ErrorOutputType, OptLevel};
|
||||||
use rustc_session::search_paths::PathKind;
|
use rustc_session::search_paths::PathKind;
|
||||||
use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
|
use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
|
||||||
|
use rustc_span::def_id::DefId;
|
||||||
use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
struct MiriCompilerCalls {
|
struct MiriCompilerCalls {
|
||||||
miri_config: miri::MiriConfig,
|
miri_config: miri::MiriConfig,
|
||||||
@ -82,11 +89,7 @@ fn after_analysis<'tcx>(
|
|||||||
tcx.dcx().fatal("miri only makes sense on bin crates");
|
tcx.dcx().fatal("miri only makes sense on bin crates");
|
||||||
}
|
}
|
||||||
|
|
||||||
let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) {
|
let (entry_def_id, entry_type) = entry_fn(tcx);
|
||||||
entry_def
|
|
||||||
} else {
|
|
||||||
tcx.dcx().fatal("miri can only run programs that have a main function");
|
|
||||||
};
|
|
||||||
let mut config = self.miri_config.clone();
|
let mut config = self.miri_config.clone();
|
||||||
|
|
||||||
// Add filename to `miri` arguments.
|
// Add filename to `miri` arguments.
|
||||||
@ -351,6 +354,56 @@ fn jemalloc_magic() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, EntryFnType) {
|
||||||
|
if let Some(entry_def) = tcx.entry_fn(()) {
|
||||||
|
return entry_def;
|
||||||
|
}
|
||||||
|
// Look for a symbol in the local crate named `miri_start`, and treat that as the entry point.
|
||||||
|
let sym = tcx.exported_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| {
|
||||||
|
if sym.symbol_name_for_local_instance(tcx).name == "miri_start" { Some(sym) } else { None }
|
||||||
|
});
|
||||||
|
if let Some(ExportedSymbol::NonGeneric(id)) = sym {
|
||||||
|
let start_def_id = id.expect_local();
|
||||||
|
let start_span = tcx.def_span(start_def_id);
|
||||||
|
|
||||||
|
let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig(
|
||||||
|
[tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))],
|
||||||
|
tcx.types.isize,
|
||||||
|
false,
|
||||||
|
hir::Safety::Safe,
|
||||||
|
Abi::Rust,
|
||||||
|
));
|
||||||
|
|
||||||
|
let correct_func_sig = check_function_signature(
|
||||||
|
tcx,
|
||||||
|
ObligationCause::new(start_span, start_def_id, ObligationCauseCode::Misc),
|
||||||
|
*id,
|
||||||
|
expected_sig,
|
||||||
|
)
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
if correct_func_sig {
|
||||||
|
(*id, EntryFnType::Start)
|
||||||
|
} else {
|
||||||
|
tcx.dcx().fatal(
|
||||||
|
"`miri_start` must have the following signature:\n\
|
||||||
|
fn miri_start(argc: isize, argv: *const *const u8) -> isize",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tcx.dcx().fatal(
|
||||||
|
"Miri can only run programs that have a main function.\n\
|
||||||
|
Alternatively, you can export a `miri_start` function:\n\
|
||||||
|
\n\
|
||||||
|
#[cfg(miri)]\n\
|
||||||
|
#[no_mangle]\n\
|
||||||
|
fn miri_start(argc: isize, argv: *const *const u8) -> isize {\
|
||||||
|
\n // Call the actual start function that your project implements, based on your target's conventions.\n\
|
||||||
|
}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
jemalloc_magic();
|
jemalloc_magic();
|
||||||
|
21
src/tools/miri/tests/fail/miri_start_wrong_sig.rs
Normal file
21
src/tools/miri/tests/fail/miri_start_wrong_sig.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//@compile-flags: -Cpanic=abort
|
||||||
|
//@error-in-other-file: `miri_start` must have the following signature:
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
#[path = "../utils/mod.no_std.rs"]
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
fn miri_start() -> isize {
|
||||||
|
//~^ ERROR: mismatched types
|
||||||
|
writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap();
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
15
src/tools/miri/tests/fail/miri_start_wrong_sig.stderr
Normal file
15
src/tools/miri/tests/fail/miri_start_wrong_sig.stderr
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/miri_start_wrong_sig.rs:LL:CC
|
||||||
|
|
|
||||||
|
LL | fn miri_start() -> isize {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
|
||||||
|
|
|
||||||
|
= note: expected signature `fn(isize, *const *const u8) -> _`
|
||||||
|
found signature `fn() -> _`
|
||||||
|
|
||||||
|
error: `miri_start` must have the following signature:
|
||||||
|
fn miri_start(argc: isize, argv: *const *const u8) -> isize
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
@ -1,2 +1,2 @@
|
|||||||
//@error-in-other-file: miri can only run programs that have a main function
|
//@error-in-other-file: Miri can only run programs that have a main function.
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
error: miri can only run programs that have a main function
|
error: Miri can only run programs that have a main function.
|
||||||
|
Alternatively, you can export a `miri_start` function:
|
||||||
|
|
||||||
|
#[cfg(miri)]
|
||||||
|
#[no_mangle]
|
||||||
|
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
|
||||||
|
// Call the actual start function that your project implements, based on your target's conventions.
|
||||||
|
}
|
||||||
|
|
||||||
error: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
19
src/tools/miri/tests/pass/miri_start.rs
Normal file
19
src/tools/miri/tests/pass/miri_start.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
//@compile-flags: -Cpanic=abort
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
#[path = "../utils/mod.no_std.rs"]
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||||
|
writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap();
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
1
src/tools/miri/tests/pass/miri_start.stdout
Normal file
1
src/tools/miri/tests/pass/miri_start.stdout
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from miri_start!
|
Loading…
Reference in New Issue
Block a user