Skip to content

Commit

Permalink
Auto merge of #3769 - primoly:miri-start, r=RalfJung
Browse files Browse the repository at this point in the history
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](#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
  • Loading branch information
bors committed Aug 2, 2024
2 parents c13444c + e290a9e commit 4b649eb
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 11 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,19 @@ Miri provides some `extern` functions that programs can import to access
Miri-specific functionality. They are declared in
[/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

If you want to contribute to Miri, great! Please check out our
Expand Down
71 changes: 62 additions & 9 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ extern crate tracing;
extern crate rustc_data_structures;
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_hir_analysis;
extern crate rustc_interface;
extern crate rustc_log;
extern crate rustc_metadata;
extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_span;
extern crate rustc_target;

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

use tracing::debug;

use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
use rustc_data_structures::sync::Lrc;
use rustc_driver::Compilation;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_hir::{self as hir, Node};
use rustc_hir_analysis::check::check_function_signature;
use rustc_interface::interface::Config;
use rustc_middle::{
middle::{
codegen_fn_attrs::CodegenFnAttrFlags,
exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel},
},
query::LocalCrate,
ty::TyCtxt,
traits::{ObligationCause, ObligationCauseCode},
ty::{self, Ty, TyCtxt},
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::{CtfeBacktrace, EarlyDiagCtxt};

use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields};
use rustc_span::def_id::DefId;
use rustc_target::spec::abi::Abi;

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

let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) {
entry_def
} else {
tcx.dcx().fatal("miri can only run programs that have a main function");
};
let (entry_def_id, entry_type) = entry_fn(tcx);
let mut config = self.miri_config.clone();

// Add filename to `miri` arguments.
Expand Down Expand Up @@ -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() {
#[cfg(any(target_os = "linux", target_os = "macos"))]
jemalloc_magic();
Expand Down
21 changes: 21 additions & 0 deletions tests/fail/miri_start_wrong_sig.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions tests/fail/miri_start_wrong_sig.stderr
Original file line number Diff line number Diff line change
@@ -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`.
2 changes: 1 addition & 1 deletion tests/fail/no_main.rs
Original file line number Diff line number Diff line change
@@ -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]
9 changes: 8 additions & 1 deletion tests/fail/no_main.stderr
Original file line number Diff line number Diff line change
@@ -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

19 changes: 19 additions & 0 deletions tests/pass/miri_start.rs
Original file line number Diff line number Diff line change
@@ -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 change: 1 addition & 0 deletions tests/pass/miri_start.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello from miri_start!

0 comments on commit 4b649eb

Please sign in to comment.