Mark emit_to_memory as unsafe, and introduce a safe emit. (#281)

* Mark emit_to_memory as unsafe, and provide a safe compile_and_emit.

Mark `Context::emit_to_memory` and `MemoryCodeSink::new` as unsafe, as
`MemoryCodeSink` does not perform bounds checking when writing to
memory.

Add a `Context::compile_and_emit` function which provides a convenient
interface for doing `compile` and `emit_to_memory` in one step, and
which can also provide a safe interface, since it allocates memory of
the needed size itself.

* Mention that `MemoryCodeSink` can't guarantee that the pointer is valid.
This commit is contained in:
Dan Gohman
2018-04-18 06:35:47 -07:00
committed by GitHub
parent 80da1a1e9f
commit d7e13284b2
5 changed files with 56 additions and 19 deletions

View File

@@ -96,19 +96,18 @@ fn handle_module(
for (func, _) in test_file.functions {
let mut context = Context::new();
context.func = func;
let size = context.compile(isa).map_err(|err| {
pretty_error(&context.func, Some(isa), err)
})?;
if flag_print {
println!("{}", context.func.display(isa));
}
// Encode the result as machine code.
// Compile and encode the result to machine code.
let mut mem = Vec::new();
let mut relocs = PrintRelocs { flag_print };
let mut traps = PrintTraps { flag_print };
mem.resize(size as usize, 0);
context.emit_to_memory(mem.as_mut_ptr(), &mut relocs, &mut traps, &*isa);
context
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps)
.map_err(|err| pretty_error(&context.func, Some(isa), err))?;
if flag_print {
println!("{}", context.func.display(isa));
}
if flag_print {
print!(".byte ");

View File

@@ -38,7 +38,10 @@ pub struct MemoryCodeSink<'a> {
impl<'a> MemoryCodeSink<'a> {
/// Create a new memory code sink that writes a function to the memory pointed to by `data`.
pub fn new<'sink>(
///
/// This function is unsafe since `MemoryCodeSink` does not perform bounds checking on the
/// memory buffer, and it can't guarantee that the `data` pointer is valid.
pub unsafe fn new<'sink>(
data: *mut u8,
relocs: &'sink mut RelocSink,
traps: &'sink mut TrapSink,

View File

@@ -78,6 +78,36 @@ impl Context {
self.loop_analysis.clear();
}
/// Compile the function, and emit machine code into a `Vec<u8>`.
///
/// Run the function through all the passes necessary to generate code for the target ISA
/// represented by `isa`, as well as the final step of emitting machine code into a
/// `Vec<u8>`. The machine code is not relocated. Instead, any relocations are emitted
/// into `relocs`.
///
/// This function calls `compile` and `emit_to_memory`, taking care to resize `mem` as
/// needed, so it provides a safe interface.
pub fn compile_and_emit(
&mut self,
isa: &TargetIsa,
mem: &mut Vec<u8>,
relocs: &mut RelocSink,
traps: &mut TrapSink,
) -> CtonResult {
let code_size = self.compile(isa)?;
let old_len = mem.len();
mem.resize(old_len + code_size as usize, 0);
unsafe {
self.emit_to_memory(
isa,
mem.as_mut_ptr().offset(old_len as isize),
relocs,
traps,
)
};
Ok(())
}
/// Compile the function.
///
/// Run the function through all the passes necessary to generate code for the target ISA
@@ -119,12 +149,15 @@ impl Context {
/// code is returned by `compile` above.
///
/// The machine code is not relocated. Instead, any relocations are emitted into `relocs`.
pub fn emit_to_memory(
///
/// This function is unsafe since it does not perform bounds checking on the memory buffer,
/// and it can't guarantee that the `mem` pointer is valid.
pub unsafe fn emit_to_memory(
&self,
isa: &TargetIsa,
mem: *mut u8,
relocs: &mut RelocSink,
traps: &mut TrapSink,
isa: &TargetIsa,
) {
let _tt = timing::binemit();
isa.emit_function(&self.func, &mut MemoryCodeSink::new(mem, relocs, traps));

View File

@@ -107,12 +107,14 @@ impl Backend for FaerieBackend {
// that traps.
let mut trap_sink = NullTrapSink {};
ctx.emit_to_memory(
code.as_mut_ptr(),
&mut reloc_sink,
&mut trap_sink,
&*self.isa,
);
unsafe {
ctx.emit_to_memory(
&*self.isa,
code.as_mut_ptr(),
&mut reloc_sink,
&mut trap_sink,
)
};
}
self.artifact.define(name, code).expect(

View File

@@ -103,7 +103,7 @@ impl<'simple_jit_backend> Backend for SimpleJITBackend {
// Ignore traps for now. For now, frontends should just avoid generating code
// that traps.
let mut trap_sink = NullTrapSink {};
ctx.emit_to_memory(ptr, &mut reloc_sink, &mut trap_sink, &*self.isa);
unsafe { ctx.emit_to_memory(&*self.isa, ptr, &mut reloc_sink, &mut trap_sink) };
Ok(Self::CompiledFunction {
code: ptr,