Skip to content

Commit fe76d33

Browse files
committed
Add and use helper function for calling a machine function and passing it some arguments
1 parent 3bf51f5 commit fe76d33

File tree

5 files changed

+131
-151
lines changed

5 files changed

+131
-151
lines changed

src/eval.rs

Lines changed: 81 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,8 @@ use rand::SeedableRng;
66
use rustc::hir::def_id::DefId;
77
use rustc::ty::layout::{LayoutOf, Size};
88
use rustc::ty::{self, TyCtxt};
9-
use syntax::source_map::DUMMY_SP;
109

11-
use crate::{
12-
EnvVars, Evaluator, FnVal, HelpersEvalContextExt, InterpCx, InterpError,
13-
InterpResult, MemoryExtra, MiriMemoryKind, Pointer, Scalar, StackPopCleanup, Tag,
14-
TlsEvalContextExt, MPlaceTy
15-
};
10+
use crate::*;
1611

1712
/// Configuration needed to spawn a Miri instance.
1813
#[derive(Clone)]
@@ -65,122 +60,108 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
6560
tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(main_ret_ty))),
6661
)
6762
.unwrap();
68-
let start_mir = ecx.load_mir(start_instance.def, None)?;
69-
70-
if start_mir.arg_count != 3 {
71-
bug!(
72-
"'start' lang item should have three arguments, but has {}",
73-
start_mir.arg_count
74-
);
75-
}
76-
77-
// Return value (in static memory so that it does not count as leak).
78-
let ret = ecx.layout_of(start_mir.return_ty())?;
79-
let ret_ptr = ecx.allocate(ret, MiriMemoryKind::Static.into());
80-
81-
// Push our stack frame.
82-
ecx.push_stack_frame(
83-
start_instance,
84-
// There is no call site.
85-
DUMMY_SP,
86-
start_mir,
87-
Some(ret_ptr.into()),
88-
StackPopCleanup::None { cleanup: true },
89-
)?;
90-
91-
let mut args = ecx.frame().body.args_iter();
9263

9364
// First argument: pointer to `main()`.
9465
let main_ptr = ecx
9566
.memory
9667
.create_fn_alloc(FnVal::Instance(main_instance));
97-
let dest = ecx.local_place(args.next().unwrap())?;
98-
ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
99-
10068
// Second argument (argc): `1`.
101-
let dest = ecx.local_place(args.next().unwrap())?;
102-
let argc = Scalar::from_uint(config.args.len() as u128, dest.layout.size);
103-
ecx.write_scalar(argc, dest)?;
104-
// Store argc for macOS's `_NSGetArgc`.
105-
{
106-
let argc_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
107-
ecx.write_scalar(argc, argc_place.into())?;
108-
ecx.machine.argc = Some(argc_place.ptr);
109-
}
110-
69+
let argc = Scalar::from_uint(config.args.len() as u128, ecx.pointer_size());
11170
// Third argument (`argv`): created from `config.args`.
112-
let dest = ecx.local_place(args.next().unwrap())?;
113-
// For Windows, construct a command string with all the aguments.
114-
let mut cmd = String::new();
115-
for arg in config.args.iter() {
116-
if !cmd.is_empty() {
117-
cmd.push(' ');
71+
let argv = {
72+
// For Windows, construct a command string with all the aguments (before we take apart `config.args`).
73+
let mut cmd = String::new();
74+
for arg in config.args.iter() {
75+
if !cmd.is_empty() {
76+
cmd.push(' ');
77+
}
78+
cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
11879
}
119-
cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
120-
}
121-
// Don't forget `0` terminator.
122-
cmd.push(std::char::from_u32(0).unwrap());
123-
// Collect the pointers to the individual strings.
124-
let mut argvs = Vec::<Pointer<Tag>>::new();
125-
for arg in config.args {
126-
// Add `0` terminator.
127-
let mut arg = arg.into_bytes();
128-
arg.push(0);
129-
argvs.push(
130-
ecx.memory
131-
.allocate_static_bytes(arg.as_slice(), MiriMemoryKind::Static.into()),
132-
);
133-
}
134-
// Make an array with all these pointers, in the Miri memory.
135-
let argvs_layout = ecx.layout_of(
136-
tcx.mk_array(tcx.mk_imm_ptr(tcx.types.u8), argvs.len() as u64),
137-
)?;
138-
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Env.into());
139-
for (idx, arg) in argvs.into_iter().enumerate() {
140-
let place = ecx.mplace_field(argvs_place, idx as u64)?;
141-
ecx.write_scalar(Scalar::Ptr(arg), place.into())?;
142-
}
143-
ecx.memory
144-
.mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
145-
// Write a pointer to that place as the argument.
146-
let argv = argvs_place.ptr;
147-
ecx.write_scalar(argv, dest)?;
148-
// Store `argv` for macOS `_NSGetArgv`.
149-
{
150-
let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
151-
ecx.write_scalar(argv, argv_place.into())?;
152-
ecx.machine.argv = Some(argv_place.ptr);
153-
}
154-
// Store command line as UTF-16 for Windows `GetCommandLineW`.
155-
{
156-
let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
157-
let cmd_type = tcx.mk_array(tcx.types.u16, cmd_utf16.len() as u64);
158-
let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Env.into());
159-
ecx.machine.cmd_line = Some(cmd_place.ptr);
160-
// Store the UTF-16 string. We just allocated so we know the bounds are fine.
161-
let char_size = Size::from_bytes(2);
162-
for (idx, &c) in cmd_utf16.iter().enumerate() {
163-
let place = ecx.mplace_field(cmd_place, idx as u64)?;
164-
ecx.write_scalar(Scalar::from_uint(c, char_size), place.into())?;
80+
// Don't forget `0` terminator.
81+
cmd.push(std::char::from_u32(0).unwrap());
82+
// Collect the pointers to the individual strings.
83+
let mut argvs = Vec::<Pointer<Tag>>::new();
84+
for arg in config.args {
85+
// Add `0` terminator.
86+
let mut arg = arg.into_bytes();
87+
arg.push(0);
88+
argvs.push(
89+
ecx.memory
90+
.allocate_static_bytes(arg.as_slice(), MiriMemoryKind::Static.into()),
91+
);
16592
}
166-
}
93+
// Make an array with all these pointers, in the Miri memory.
94+
let argvs_layout = ecx.layout_of(
95+
tcx.mk_array(tcx.mk_imm_ptr(tcx.types.u8), argvs.len() as u64),
96+
)?;
97+
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Env.into());
98+
for (idx, arg) in argvs.into_iter().enumerate() {
99+
let place = ecx.mplace_field(argvs_place, idx as u64)?;
100+
ecx.write_scalar(Scalar::Ptr(arg), place.into())?;
101+
}
102+
ecx.memory
103+
.mark_immutable(argvs_place.ptr.assert_ptr().alloc_id)?;
104+
// A pointer to that place is the argument.
105+
let argv = argvs_place.ptr;
106+
// Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
107+
{
108+
let argc_place = ecx.allocate(
109+
ecx.layout_of(tcx.types.isize)?,
110+
MiriMemoryKind::Env.into(),
111+
);
112+
ecx.write_scalar(argc, argc_place.into())?;
113+
ecx.machine.argc = Some(argc_place.ptr);
114+
115+
let argv_place = ecx.allocate(
116+
ecx.layout_of(tcx.mk_imm_ptr(tcx.types.unit))?,
117+
MiriMemoryKind::Env.into(),
118+
);
119+
ecx.write_scalar(argv, argv_place.into())?;
120+
ecx.machine.argv = Some(argv_place.ptr);
121+
}
122+
// Store command line as UTF-16 for Windows `GetCommandLineW`.
123+
{
124+
let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
125+
let cmd_type = tcx.mk_array(tcx.types.u16, cmd_utf16.len() as u64);
126+
let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Env.into());
127+
ecx.machine.cmd_line = Some(cmd_place.ptr);
128+
// Store the UTF-16 string. We just allocated so we know the bounds are fine.
129+
let char_size = Size::from_bytes(2);
130+
for (idx, &c) in cmd_utf16.iter().enumerate() {
131+
let place = ecx.mplace_field(cmd_place, idx as u64)?;
132+
ecx.write_scalar(Scalar::from_uint(c, char_size), place.into())?;
133+
}
134+
}
135+
argv
136+
};
167137

168-
args.next().expect_none("start lang item has more arguments than expected");
138+
// Return place (in static memory so that it does not count as leak).
139+
let ret_place = ecx.allocate(
140+
ecx.layout_of(tcx.types.isize)?,
141+
MiriMemoryKind::Static.into(),
142+
);
143+
// Call start function.
144+
ecx.call_function(
145+
start_instance,
146+
&[main_ptr.into(), argc, argv],
147+
Some(ret_place.into()),
148+
StackPopCleanup::None { cleanup: true },
149+
)?;
169150

170151
// Set the last_error to 0
171152
let errno_layout = ecx.layout_of(tcx.types.u32)?;
172153
let errno_place = ecx.allocate(errno_layout, MiriMemoryKind::Static.into());
173154
ecx.write_scalar(Scalar::from_u32(0), errno_place.into())?;
174155
ecx.machine.last_error = Some(errno_place);
175156

176-
Ok((ecx, ret_ptr))
157+
Ok((ecx, ret_place))
177158
}
178159

179160
/// Evaluates the main function specified by `main_id`.
180161
/// Returns `Some(return_code)` if program executed completed.
181162
/// Returns `None` if an evaluation error occured.
182163
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
183-
let (mut ecx, ret_ptr) = match create_ecx(tcx, main_id, config) {
164+
let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
184165
Ok(v) => v,
185166
Err(mut err) => {
186167
err.print_backtrace();
@@ -193,7 +174,7 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
193174
ecx.run()?;
194175
// Read the return code pointer *before* we run TLS destructors, to assert
195176
// that it was written to by the time that `start` lang item returned.
196-
let return_code = ecx.read_scalar(ret_ptr.into())?.not_undef()?.to_machine_isize(&ecx)?;
177+
let return_code = ecx.read_scalar(ret_place.into())?.not_undef()?.to_machine_isize(&ecx)?;
197178
ecx.run_tls_dtors()?;
198179
Ok(return_code)
199180
})();

src/helpers.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{mem, iter};
22
use std::ffi::{OsStr, OsString};
33

4+
use syntax::source_map::DUMMY_SP;
45
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
56
use rustc::mir;
67
use rustc::ty::{
@@ -118,6 +119,40 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
118119
this.memory.write_bytes(ptr, data.iter().copied())
119120
}
120121

122+
/// Call a function: Push the stack frame and pass the arguments.
123+
/// For now, arguments must be scalars (so that the caller does not have to know the layout).
124+
fn call_function(
125+
&mut self,
126+
f: ty::Instance<'tcx>,
127+
args: &[Scalar<Tag>],
128+
dest: Option<PlaceTy<'tcx, Tag>>,
129+
stack_pop: StackPopCleanup,
130+
) -> InterpResult<'tcx> {
131+
let this = self.eval_context_mut();
132+
133+
// Push frame.
134+
let mir = this.load_mir(f.def, None)?;
135+
this.push_stack_frame(
136+
f,
137+
DUMMY_SP, // There is no call site.
138+
mir,
139+
dest,
140+
stack_pop,
141+
)?;
142+
143+
// Initialize arguments.
144+
let mut callee_args = this.frame().body.args_iter();
145+
for arg in args {
146+
let callee_arg = this.local_place(
147+
callee_args.next().expect("callee has fewer arguments than expected")
148+
)?;
149+
this.write_scalar(*arg, callee_arg)?;
150+
}
151+
callee_args.next().expect_none("callee has more arguments than expected");
152+
153+
Ok(())
154+
}
155+
121156
/// Visits the memory covered by `place`, sensitive to freezing: the 3rd parameter
122157
/// will be true if this is frozen, false if this is in an `UnsafeCell`.
123158
fn visit_freeze_sensitive(

src/machine.rs

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -230,37 +230,26 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
230230
dest: PlaceTy<'tcx, Tag>,
231231
) -> InterpResult<'tcx> {
232232
trace!("box_alloc for {:?}", dest.layout.ty);
233+
let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
234+
// First argument: `size`.
235+
// (`0` is allowed here -- this is expected to be handled by the lang item).
236+
let size = Scalar::from_uint(layout.size.bytes(), ecx.pointer_size());
237+
238+
// Second argument: `align`.
239+
let align = Scalar::from_uint(layout.align.abi.bytes(), ecx.pointer_size());
240+
233241
// Call the `exchange_malloc` lang item.
234242
let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
235243
let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
236-
let malloc_mir = ecx.load_mir(malloc.def, None)?;
237-
ecx.push_stack_frame(
244+
ecx.call_function(
238245
malloc,
239-
malloc_mir.span,
240-
malloc_mir,
246+
&[size, align],
241247
Some(dest),
242248
// Don't do anything when we are done. The `statement()` function will increment
243249
// the old stack frame's stmt counter to the next statement, which means that when
244250
// `exchange_malloc` returns, we go on evaluating exactly where we want to be.
245251
StackPopCleanup::None { cleanup: true },
246252
)?;
247-
248-
let mut args = ecx.frame().body.args_iter();
249-
let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
250-
251-
// First argument: `size`.
252-
// (`0` is allowed here -- this is expected to be handled by the lang item).
253-
let arg = ecx.local_place(args.next().unwrap())?;
254-
let size = layout.size.bytes();
255-
ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;
256-
257-
// Second argument: `align`.
258-
let arg = ecx.local_place(args.next().unwrap())?;
259-
let align = layout.align.abi.bytes();
260-
ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;
261-
262-
// No more arguments.
263-
args.next().expect_none("`exchange_malloc` lang item has more arguments than expected");
264253
Ok(())
265254
}
266255

src/shims/panic.rs

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
3737

3838
/// Handles the special "miri_start_panic" intrinsic, which is called
3939
/// by libpanic_unwind to delegate the actual unwinding process to Miri.
40-
#[inline(always)]
4140
fn handle_miri_start_panic(
4241
&mut self,
4342
args: &[OpTy<'tcx, Tag>],
@@ -57,7 +56,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
5756
return Ok(())
5857
}
5958

60-
#[inline(always)]
6159
fn handle_catch_panic(
6260
&mut self,
6361
args: &[OpTy<'tcx, Tag>],
@@ -83,30 +81,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
8381
// Now we make a function call, and pass `f_arg` as first and only argument.
8482
let f_instance = this.memory.get_fn(f)?.as_instance()?;
8583
trace!("__rust_maybe_catch_panic: {:?}", f_instance);
86-
// TODO: consider making this reusable? `InterpCx::step` does something similar
87-
// for the TLS destructors, and of course `eval_main`.
88-
let mir = this.load_mir(f_instance.def, None)?;
8984
let ret_place =
9085
MPlaceTy::dangling(this.layout_of(tcx.mk_unit())?, this).into();
91-
this.push_stack_frame(
86+
this.call_function(
9287
f_instance,
93-
mir.span,
94-
mir,
88+
&[f_arg],
9589
Some(ret_place),
9690
// Directly return to caller.
9791
StackPopCleanup::Goto { ret: Some(ret), unwind: None },
9892
)?;
9993

100-
let mut args = this.frame().body.args_iter();
101-
// First argument.
102-
let arg_local = args
103-
.next()
104-
.expect("Argument to __rust_maybe_catch_panic does not take enough arguments.");
105-
let arg_dest = this.local_place(arg_local)?;
106-
this.write_scalar(f_arg, arg_dest)?;
107-
// No more arguments.
108-
args.next().expect_none("__rust_maybe_catch_panic argument has more arguments than expected");
109-
11094
// We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
11195
this.write_null(dest)?;
11296

@@ -124,7 +108,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
124108
return Ok(());
125109
}
126110

127-
#[inline(always)]
128111
fn handle_stack_pop(
129112
&mut self,
130113
mut extra: FrameData<'tcx>,

0 commit comments

Comments
 (0)