If a "start" lang item incl. MIR is present, run that instead of running main directly
This fixes the memory leaks when running a simple "Hello World" with MIR-libstd
This commit is contained in:
parent
720c5f874e
commit
cd6e3e6431
@ -84,7 +84,7 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) {
|
||||
if i.attrs.iter().any(|attr| attr.name().map_or(false, |n| n == "test")) {
|
||||
let did = self.1.hir.body_owner_def_id(body_id);
|
||||
println!("running test: {}", self.1.hir.def_path(did).to_string(self.1));
|
||||
miri::eval_main(self.1, did, self.0);
|
||||
miri::eval_main(self.1, did, None, self.0);
|
||||
self.2.session.abort_if_errors();
|
||||
}
|
||||
}
|
||||
@ -95,7 +95,9 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) {
|
||||
state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state));
|
||||
} else if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() {
|
||||
let entry_def_id = tcx.hir.local_def_id(entry_node_id);
|
||||
miri::eval_main(tcx, entry_def_id, limits);
|
||||
let start_wrapper = tcx.lang_items.start_fn()
|
||||
.and_then(|start_fn| if tcx.is_mir_available(start_fn) { Some(start_fn) } else { None });
|
||||
miri::eval_main(tcx, entry_def_id, start_wrapper, limits);
|
||||
|
||||
state.session.abort_if_errors();
|
||||
} else {
|
||||
|
@ -126,6 +126,7 @@ impl Default for ResourceLimits {
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
|
||||
// Register array drop glue code
|
||||
let source_info = mir::SourceInfo {
|
||||
span: DUMMY_SP,
|
||||
scope: mir::ARGUMENT_VISIBILITY_SCOPE
|
||||
@ -852,7 +853,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance);
|
||||
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
|
||||
},
|
||||
ref other => bug!("reify fn pointer on {:?}", other),
|
||||
ref other => bug!("closure fn pointer on {:?}", other),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -1676,62 +1677,120 @@ impl<'tcx> Frame<'tcx> {
|
||||
|
||||
pub fn eval_main<'a, 'tcx: 'a>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
def_id: DefId,
|
||||
main_id: DefId,
|
||||
start_wrapper: Option<DefId>,
|
||||
limits: ResourceLimits,
|
||||
) {
|
||||
let mut ecx = EvalContext::new(tcx, limits);
|
||||
let instance = ty::Instance::mono(tcx, def_id);
|
||||
let mir = ecx.load_mir(instance.def).expect("main function's MIR not found");
|
||||
fn run_main<'a, 'tcx: 'a>(
|
||||
ecx: &mut EvalContext<'a, 'tcx>,
|
||||
main_id: DefId,
|
||||
start_wrapper: Option<DefId>,
|
||||
) -> EvalResult<'tcx> {
|
||||
let main_instance = ty::Instance::mono(ecx.tcx, main_id);
|
||||
let main_mir = ecx.load_mir(main_instance.def)?;
|
||||
|
||||
if !mir.return_ty.is_nil() || mir.arg_count != 0 {
|
||||
let msg = "miri does not support main functions without `fn()` type signatures";
|
||||
tcx.sess.err(&EvalError::Unimplemented(String::from(msg)).to_string());
|
||||
return;
|
||||
if !main_mir.return_ty.is_nil() || main_mir.arg_count != 0 {
|
||||
return Err(EvalError::Unimplemented("miri does not support main functions without `fn()` type signatures".to_owned()));
|
||||
}
|
||||
|
||||
if let Some(start_id) = start_wrapper {
|
||||
let start_instance = ty::Instance::mono(ecx.tcx, start_id);
|
||||
let start_mir = ecx.load_mir(start_instance.def)?;
|
||||
|
||||
if start_mir.arg_count != 3 {
|
||||
return Err(EvalError::AbiViolation(format!("'start' lang item should have three arguments, but has {}", start_mir.arg_count)));
|
||||
}
|
||||
|
||||
// Push our stack frame
|
||||
ecx.push_stack_frame(
|
||||
start_instance,
|
||||
start_mir.span,
|
||||
start_mir,
|
||||
Lvalue::from_ptr(Pointer::zst_ptr()), // we'll fix the return lvalue later
|
||||
StackPopCleanup::None,
|
||||
)?;
|
||||
|
||||
let mut args = ecx.frame().mir.args_iter();
|
||||
|
||||
// First argument: pointer to main()
|
||||
let main_ptr = ecx.memory.create_fn_alloc(main_instance);
|
||||
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
|
||||
let main_ty = main_instance.def.def_ty(ecx.tcx);
|
||||
let main_ptr_ty = ecx.tcx.mk_fn_ptr(main_ty.fn_sig());
|
||||
ecx.write_value(Value::ByVal(PrimVal::Ptr(main_ptr)), dest, main_ptr_ty)?;
|
||||
|
||||
// Second argument (argc): 0
|
||||
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
|
||||
let ty = ecx.tcx.types.isize;
|
||||
ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?;
|
||||
|
||||
// Third argument (argv): 0
|
||||
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
|
||||
let ty = ecx.tcx.mk_imm_ptr(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8));
|
||||
ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?;
|
||||
} else {
|
||||
ecx.push_stack_frame(
|
||||
main_instance,
|
||||
main_mir.span,
|
||||
main_mir,
|
||||
Lvalue::from_ptr(Pointer::zst_ptr()),
|
||||
StackPopCleanup::None,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Allocate memory for the return value. We have to do this when a stack frame was already pushed as the type code below
|
||||
// calls EvalContext::substs, which needs a frame to be allocated (?!?)
|
||||
let ret_ptr = {
|
||||
let ty = ecx.tcx.types.isize;
|
||||
let layout = ecx.type_layout(ty)?;
|
||||
let size = layout.size(&ecx.tcx.data_layout).bytes();
|
||||
let align = layout.align(&ecx.tcx.data_layout).pref(); // FIXME is this right?
|
||||
ecx.memory.allocate(size, align)?
|
||||
};
|
||||
ecx.frame_mut().return_lvalue = Lvalue::from_ptr(ret_ptr);
|
||||
|
||||
loop {
|
||||
if !ecx.step()? {
|
||||
ecx.memory.deallocate(ret_ptr)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ecx.push_stack_frame(
|
||||
instance,
|
||||
DUMMY_SP,
|
||||
mir,
|
||||
Lvalue::from_ptr(Pointer::zst_ptr()),
|
||||
StackPopCleanup::None,
|
||||
).expect("could not allocate first stack frame");
|
||||
|
||||
loop {
|
||||
match ecx.step() {
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
let leaks = ecx.memory.leak_report();
|
||||
if leaks != 0 {
|
||||
tcx.sess.err("the evaluated program leaked memory");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Err(e) => {
|
||||
report(tcx, &ecx, e);
|
||||
return;
|
||||
let mut ecx = EvalContext::new(tcx, limits);
|
||||
match run_main(&mut ecx, main_id, start_wrapper) {
|
||||
Ok(()) => {
|
||||
let leaks = ecx.memory.leak_report();
|
||||
if leaks != 0 {
|
||||
tcx.sess.err("the evaluated program leaked memory");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
report(tcx, &ecx, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) {
|
||||
let frame = ecx.stack().last().expect("stackframe was empty");
|
||||
let block = &frame.mir.basic_blocks()[frame.block];
|
||||
let span = if frame.stmt < block.statements.len() {
|
||||
block.statements[frame.stmt].source_info.span
|
||||
} else {
|
||||
block.terminator().source_info.span
|
||||
};
|
||||
let mut err = tcx.sess.struct_span_err(span, &e.to_string());
|
||||
for &Frame { instance, span, .. } in ecx.stack().iter().rev() {
|
||||
if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
|
||||
err.span_note(span, "inside call to closure");
|
||||
continue;
|
||||
if let Some(frame) = ecx.stack().last() {
|
||||
let block = &frame.mir.basic_blocks()[frame.block];
|
||||
let span = if frame.stmt < block.statements.len() {
|
||||
block.statements[frame.stmt].source_info.span
|
||||
} else {
|
||||
block.terminator().source_info.span
|
||||
};
|
||||
let mut err = tcx.sess.struct_span_err(span, &e.to_string());
|
||||
for &Frame { instance, span, .. } in ecx.stack().iter().rev() {
|
||||
if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
|
||||
err.span_note(span, "inside call to closure");
|
||||
continue;
|
||||
}
|
||||
err.span_note(span, &format!("inside call to {}", instance));
|
||||
}
|
||||
err.span_note(span, &format!("inside call to {}", instance));
|
||||
err.emit();
|
||||
} else {
|
||||
tcx.sess.err(&e.to_string());
|
||||
}
|
||||
err.emit();
|
||||
}
|
||||
|
||||
// TODO(solson): Upstream these methods into rustc::ty::layout.
|
||||
|
12
src/step.rs
12
src/step.rs
@ -43,14 +43,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
Lvalue::from_ptr(Pointer::zst_ptr()),
|
||||
StackPopCleanup::None,
|
||||
)?;
|
||||
if let Some(arg_local) = self.frame().mir.args_iter().next() {
|
||||
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
|
||||
let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
|
||||
self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?;
|
||||
} else {
|
||||
return Err(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()));
|
||||
}
|
||||
|
||||
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("TLS dtor does not take enough arguments.".to_owned()))?;
|
||||
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
|
||||
let ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
|
||||
self.write_value(Value::ByVal(PrimVal::Ptr(ptr)), dest, ty)?;
|
||||
return Ok(true);
|
||||
}
|
||||
return Ok(false);
|
||||
|
@ -637,23 +637,33 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?;
|
||||
}
|
||||
|
||||
// unix panic code inside libstd will read the return value of this function
|
||||
"pthread_rwlock_rdlock" => {
|
||||
// Some things needed for sys::thread initialization to go through
|
||||
"signal" | "sigaction" | "sigaltstack" => {
|
||||
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
|
||||
}
|
||||
|
||||
"sysconf" => {
|
||||
let name = self.value_to_primval(args[0], usize)?.to_u64()?;
|
||||
trace!("sysconf() called with name {}", name);
|
||||
let result = match name {
|
||||
30 => 4096, // _SC_PAGESIZE
|
||||
_ => return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name)))
|
||||
};
|
||||
self.write_primval(dest, PrimVal::Bytes(result), dest_ty)?;
|
||||
}
|
||||
|
||||
"mmap" => {
|
||||
// This is a horrible hack, but well... the guard page mechanism calls mmap and expects a particular return value, so we give it that value
|
||||
let addr = args[0].read_ptr(&self.memory)?;
|
||||
self.write_primval(dest, PrimVal::Ptr(addr), dest_ty)?;
|
||||
}
|
||||
|
||||
// Hook pthread calls that go to the thread-local storage memory subsystem
|
||||
"pthread_key_create" => {
|
||||
let key_ptr = args[0].read_ptr(&self.memory)?;
|
||||
|
||||
// Extract the function type out of the signature (that seems easier than constructing it ourselves...)
|
||||
let dtor_fn_ty = match self.operand_ty(&arg_operands[1]).sty {
|
||||
TypeVariants::TyAdt(_, ref substs) => {
|
||||
substs.type_at(0)
|
||||
}
|
||||
_ => return Err(EvalError::AbiViolation("Wrong signature used for pthread_key_create: Second argument must be option of a function pointer.".to_owned()))
|
||||
};
|
||||
let dtor_ptr = self.value_to_primval(args[1], dtor_fn_ty)?.to_ptr()?;
|
||||
let dtor_ptr = args[1].read_ptr(&self.memory)?;
|
||||
// TODO: The null-pointer case here is entirely untested
|
||||
let dtor = if dtor_ptr.is_null_ptr() { None } else { Some(self.memory.get_fn(dtor_ptr.alloc_id)?) };
|
||||
|
||||
@ -699,8 +709,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
|
||||
}
|
||||
|
||||
// Stub out all the other pthread calls to just return 0
|
||||
link_name if link_name.starts_with("pthread_") => {
|
||||
warn!("ignoring C ABI call: {}", link_name);
|
||||
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
|
||||
},
|
||||
|
||||
_ => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
#![feature(custom_attribute, attr_literals)]
|
||||
#![miri(memory_size=0)]
|
||||
#![miri(memory_size=20)]
|
||||
|
||||
fn main() {
|
||||
let _x = [42; 10];
|
||||
//~^ERROR tried to allocate 40 more bytes, but only 0 bytes are free of the 0 byte memory
|
||||
//~^ERROR tried to allocate 40 more bytes, but only
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user