Skip to content

make no_std work on Windows #2696

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ function run_tests_minimal {
./miri test -- "$@"

# Ensure that a small smoke test of cargo-miri works.
# Note: This doesn't work on windows because of TLS.
cargo miri run --manifest-path test-cargo-miri/no-std-smoke/Cargo.toml
}

Expand Down
3 changes: 2 additions & 1 deletion src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::thread;
use log::info;

use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{
self,
Expand Down Expand Up @@ -195,7 +196,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
MiriMachine::late_init(&mut ecx, config)?;

// Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"]);
let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS);
if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
tcx.sess.fatal(
"the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \
Expand Down
93 changes: 63 additions & 30 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ pub mod convert;

use std::cmp;
use std::iter;
use std::mem;
use std::num::NonZeroUsize;
use std::time::Duration;

use log::trace;

use rustc_hir::def::{DefKind, Namespace};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_middle::mir;
use rustc_middle::ty::{
Expand Down Expand Up @@ -74,48 +74,79 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
};

/// Gets an instance for a path.
fn try_resolve_did<'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId> {
tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == path[0]).and_then(
|krate| {
let krate = DefId { krate: *krate, index: CRATE_DEF_INDEX };
let mut items = tcx.module_children(krate);
let mut path_it = path.iter().skip(1).peekable();

while let Some(segment) = path_it.next() {
for item in mem::take(&mut items).iter() {
if item.ident.name.as_str() == *segment {
if path_it.peek().is_none() {
return Some(item.res.def_id());
}
///
/// A `None` namespace indicates we are looking for a module.
fn try_resolve_did<'tcx>(
tcx: TyCtxt<'tcx>,
path: &[&str],
namespace: Option<Namespace>,
) -> Option<DefId> {
/// Yield all children of the given item, that have the given name.
fn find_children<'tcx: 'a, 'a>(
tcx: TyCtxt<'tcx>,
item: DefId,
name: &'a str,
) -> impl Iterator<Item = DefId> + 'a {
tcx.module_children(item)
.iter()
.filter(move |item| item.ident.name.as_str() == name)
.map(move |item| item.res.def_id())
}

items = tcx.module_children(item.res.def_id());
break;
}
}
}
None
},
)
// Take apart the path: leading crate, a sequence of modules, and potentially a final item.
let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
let (modules, item) = if let Some(namespace) = namespace {
let (&item_name, modules) =
path.split_last().expect("non-module paths must have at least 2 segments");
(modules, Some((item_name, namespace)))
} else {
(path, None)
};

// First find the crate.
let krate =
tcx.crates(()).iter().find(|&&krate| tcx.crate_name(krate).as_str() == crate_name)?;
let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
// Then go over the modules.
for &segment in modules {
cur_item = find_children(tcx, cur_item, segment)
.find(|item| tcx.def_kind(item) == DefKind::Mod)?;
}
// Finally, look up the desired item in this module, if any.
match item {
Some((item_name, namespace)) =>
Some(
find_children(tcx, cur_item, item_name)
.find(|item| tcx.def_kind(item).ns() == Some(namespace))?,
),
None => Some(cur_item),
}
}

pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Checks if the given crate/module exists.
fn have_module(&self, path: &[&str]) -> bool {
try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
}

/// Gets an instance for a path; fails gracefully if the path does not exist.
fn try_resolve_path(&self, path: &[&str]) -> Option<ty::Instance<'tcx>> {
let did = try_resolve_did(self.eval_context_ref().tcx.tcx, path)?;
Some(ty::Instance::mono(self.eval_context_ref().tcx.tcx, did))
fn try_resolve_path(&self, path: &[&str], namespace: Namespace) -> Option<ty::Instance<'tcx>> {
let tcx = self.eval_context_ref().tcx.tcx;
let did = try_resolve_did(tcx, path, Some(namespace))?;
Some(ty::Instance::mono(tcx, did))
}

/// Gets an instance for a path.
fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> {
self.try_resolve_path(path)
fn resolve_path(&self, path: &[&str], namespace: Namespace) -> ty::Instance<'tcx> {
self.try_resolve_path(path, namespace)
.unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
}

/// Evaluates the scalar at the specified path. Returns Some(val)
/// if the path could be resolved, and None otherwise
fn eval_path_scalar(&self, path: &[&str]) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_ref();
let instance = this.resolve_path(path);
let instance = this.resolve_path(path, Namespace::ValueNS);
let cid = GlobalId { instance, promoted: None };
// We don't give a span -- this isn't actually used directly by the program anyway.
let const_val = this.eval_global(cid, None)?;
Expand Down Expand Up @@ -147,15 +178,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
/// Helper function to get the `TyAndLayout` of a `libc` type
fn libc_ty_layout(&self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
let this = self.eval_context_ref();
let ty = this.resolve_path(&["libc", name]).ty(*this.tcx, ty::ParamEnv::reveal_all());
let ty = this
.resolve_path(&["libc", name], Namespace::TypeNS)
.ty(*this.tcx, ty::ParamEnv::reveal_all());
this.layout_of(ty)
}

/// Helper function to get the `TyAndLayout` of a `windows` type
fn windows_ty_layout(&self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> {
let this = self.eval_context_ref();
let ty = this
.resolve_path(&["std", "sys", "windows", "c", name])
.resolve_path(&["std", "sys", "windows", "c", name], Namespace::TypeNS)
.ty(*this.tcx, ty::ParamEnv::reveal_all());
this.layout_of(ty)
}
Expand Down
5 changes: 5 additions & 0 deletions src/shims/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// (that would be basically https://github.com/rust-lang/miri/issues/450),
// we specifically look up the static in libstd that we know is placed
// in that section.
if !this.have_module(&["std"]) {
// Looks like we are running in a `no_std` crate.
// That also means no TLS dtors callback to call.
return Ok(());
}
let thread_callback =
this.eval_windows("thread_local_key", "p_thread_callback")?.to_pointer(this)?;
let thread_callback = this.get_ptr_fn(thread_callback)?.as_instance()?;
Expand Down
8 changes: 1 addition & 7 deletions src/shims/unix/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use std::time::SystemTime;
use log::trace;

use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::{self, layout::LayoutOf};
use rustc_target::abi::{Align, Size};

use crate::shims::os_str::bytes_to_os_str;
Expand Down Expand Up @@ -1006,12 +1005,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// as `isize`s instead of having the proper types. Thus, we have to recover the layout of
// `statxbuf_op` by using the `libc::statx` struct type.
let statxbuf = {
// FIXME: This long path is required because `libc::statx` is an struct and also a
// function and `resolve_path` is returning the latter.
let statx_ty = this
.resolve_path(&["libc", "unix", "linux_like", "linux", "gnu", "statx"])
.ty(*this.tcx, ty::ParamEnv::reveal_all());
let statx_layout = this.layout_of(statx_ty)?;
let statx_layout = this.libc_ty_layout("statx")?;
MPlaceTy::from_aligned_ptr(statxbuf_ptr, statx_layout)
};

Expand Down
4 changes: 0 additions & 4 deletions tests/pass/no_std.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
#![feature(lang_items, start)]
#![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: no-std not supported on Windows

// Plumbing to let us use `writeln!` to host stdout:

Expand Down