Skip to content

Commit 5852b24

Browse files
committed
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](rust-lang/miri#3498 (comment)). <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
2 parents 989b8f7 + 6bb1197 commit 5852b24

File tree

8 files changed

+140
-11
lines changed

8 files changed

+140
-11
lines changed

Diff for: src/tools/miri/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,19 @@ Miri provides some `extern` functions that programs can import to access
474474
Miri-specific functionality. They are declared in
475475
[/tests/utils/miri\_extern.rs](/tests/utils/miri_extern.rs).
476476

477+
## Entry point for no-std binaries
478+
479+
Binaries that do not use the standard library are expected to declare a function like this so that
480+
Miri knows where it is supposed to start execution:
481+
482+
```rust
483+
#[cfg(miri)]
484+
#[no_mangle]
485+
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
486+
// Call the actual start function that your project implements, based on your target's conventions.
487+
}
488+
```
489+
477490
## Contributing and getting help
478491

479492
If you want to contribute to Miri, great! Please check out our

Diff for: src/tools/miri/src/bin/miri.rs

+62-9
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ extern crate tracing;
1414
extern crate rustc_data_structures;
1515
extern crate rustc_driver;
1616
extern crate rustc_hir;
17+
extern crate rustc_hir_analysis;
1718
extern crate rustc_interface;
1819
extern crate rustc_log;
1920
extern crate rustc_metadata;
2021
extern crate rustc_middle;
2122
extern crate rustc_session;
23+
extern crate rustc_span;
24+
extern crate rustc_target;
2225

2326
use std::env::{self, VarError};
2427
use std::num::NonZero;
@@ -27,24 +30,28 @@ use std::str::FromStr;
2730

2831
use tracing::debug;
2932

33+
use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
3034
use rustc_data_structures::sync::Lrc;
3135
use rustc_driver::Compilation;
36+
use rustc_hir::def_id::LOCAL_CRATE;
3237
use rustc_hir::{self as hir, Node};
38+
use rustc_hir_analysis::check::check_function_signature;
3339
use rustc_interface::interface::Config;
3440
use rustc_middle::{
3541
middle::{
3642
codegen_fn_attrs::CodegenFnAttrFlags,
3743
exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel},
3844
},
3945
query::LocalCrate,
40-
ty::TyCtxt,
46+
traits::{ObligationCause, ObligationCauseCode},
47+
ty::{self, Ty, TyCtxt},
4148
util::Providers,
4249
};
43-
use rustc_session::config::{CrateType, ErrorOutputType, OptLevel};
50+
use rustc_session::config::{CrateType, EntryFnType, ErrorOutputType, OptLevel};
4451
use rustc_session::search_paths::PathKind;
4552
use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
46-
47-
use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
53+
use rustc_span::def_id::DefId;
54+
use rustc_target::spec::abi::Abi;
4855

4956
struct MiriCompilerCalls {
5057
miri_config: miri::MiriConfig,
@@ -82,11 +89,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
8289
tcx.dcx().fatal("miri only makes sense on bin crates");
8390
}
8491

85-
let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) {
86-
entry_def
87-
} else {
88-
tcx.dcx().fatal("miri can only run programs that have a main function");
89-
};
92+
let (entry_def_id, entry_type) = entry_fn(tcx);
9093
let mut config = self.miri_config.clone();
9194

9295
// Add filename to `miri` arguments.
@@ -351,6 +354,56 @@ fn jemalloc_magic() {
351354
}
352355
}
353356

357+
fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, EntryFnType) {
358+
if let Some(entry_def) = tcx.entry_fn(()) {
359+
return entry_def;
360+
}
361+
// Look for a symbol in the local crate named `miri_start`, and treat that as the entry point.
362+
let sym = tcx.exported_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| {
363+
if sym.symbol_name_for_local_instance(tcx).name == "miri_start" { Some(sym) } else { None }
364+
});
365+
if let Some(ExportedSymbol::NonGeneric(id)) = sym {
366+
let start_def_id = id.expect_local();
367+
let start_span = tcx.def_span(start_def_id);
368+
369+
let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig(
370+
[tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))],
371+
tcx.types.isize,
372+
false,
373+
hir::Safety::Safe,
374+
Abi::Rust,
375+
));
376+
377+
let correct_func_sig = check_function_signature(
378+
tcx,
379+
ObligationCause::new(start_span, start_def_id, ObligationCauseCode::Misc),
380+
*id,
381+
expected_sig,
382+
)
383+
.is_ok();
384+
385+
if correct_func_sig {
386+
(*id, EntryFnType::Start)
387+
} else {
388+
tcx.dcx().fatal(
389+
"`miri_start` must have the following signature:\n\
390+
fn miri_start(argc: isize, argv: *const *const u8) -> isize",
391+
);
392+
}
393+
} else {
394+
tcx.dcx().fatal(
395+
"Miri can only run programs that have a main function.\n\
396+
Alternatively, you can export a `miri_start` function:\n\
397+
\n\
398+
#[cfg(miri)]\n\
399+
#[no_mangle]\n\
400+
fn miri_start(argc: isize, argv: *const *const u8) -> isize {\
401+
\n // Call the actual start function that your project implements, based on your target's conventions.\n\
402+
}"
403+
);
404+
}
405+
}
406+
354407
fn main() {
355408
#[cfg(any(target_os = "linux", target_os = "macos"))]
356409
jemalloc_magic();

Diff for: src/tools/miri/tests/fail/miri_start_wrong_sig.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@compile-flags: -Cpanic=abort
2+
//@error-in-other-file: `miri_start` must have the following signature:
3+
#![no_main]
4+
#![no_std]
5+
6+
use core::fmt::Write;
7+
8+
#[path = "../utils/mod.no_std.rs"]
9+
mod utils;
10+
11+
#[no_mangle]
12+
fn miri_start() -> isize {
13+
//~^ ERROR: mismatched types
14+
writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap();
15+
0
16+
}
17+
18+
#[panic_handler]
19+
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
20+
loop {}
21+
}
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/miri_start_wrong_sig.rs:LL:CC
3+
|
4+
LL | fn miri_start() -> isize {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
6+
|
7+
= note: expected signature `fn(isize, *const *const u8) -> _`
8+
found signature `fn() -> _`
9+
10+
error: `miri_start` must have the following signature:
11+
fn miri_start(argc: isize, argv: *const *const u8) -> isize
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0308`.

Diff for: src/tools/miri/tests/fail/no_main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
//@error-in-other-file: miri can only run programs that have a main function
1+
//@error-in-other-file: Miri can only run programs that have a main function.
22
#![no_main]

Diff for: src/tools/miri/tests/fail/no_main.stderr

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
error: miri can only run programs that have a main function
1+
error: Miri can only run programs that have a main function.
2+
Alternatively, you can export a `miri_start` function:
3+
4+
#[cfg(miri)]
5+
#[no_mangle]
6+
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
7+
// Call the actual start function that your project implements, based on your target's conventions.
8+
}
29

310
error: aborting due to 1 previous error
411

Diff for: src/tools/miri/tests/pass/miri_start.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@compile-flags: -Cpanic=abort
2+
#![no_main]
3+
#![no_std]
4+
5+
use core::fmt::Write;
6+
7+
#[path = "../utils/mod.no_std.rs"]
8+
mod utils;
9+
10+
#[no_mangle]
11+
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
12+
writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap();
13+
0
14+
}
15+
16+
#[panic_handler]
17+
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
18+
loop {}
19+
}

Diff for: src/tools/miri/tests/pass/miri_start.stdout

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello from miri_start!

0 commit comments

Comments
 (0)