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 { for (func, _) in test_file.functions {
let mut context = Context::new(); let mut context = Context::new();
context.func = func; 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 mem = Vec::new();
let mut relocs = PrintRelocs { flag_print }; let mut relocs = PrintRelocs { flag_print };
let mut traps = PrintTraps { flag_print }; let mut traps = PrintTraps { flag_print };
mem.resize(size as usize, 0); context
context.emit_to_memory(mem.as_mut_ptr(), &mut relocs, &mut traps, &*isa); .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 { if flag_print {
print!(".byte "); print!(".byte ");

View File

@@ -38,7 +38,10 @@ pub struct MemoryCodeSink<'a> {
impl<'a> MemoryCodeSink<'a> { impl<'a> MemoryCodeSink<'a> {
/// Create a new memory code sink that writes a function to the memory pointed to by `data`. /// 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, data: *mut u8,
relocs: &'sink mut RelocSink, relocs: &'sink mut RelocSink,
traps: &'sink mut TrapSink, traps: &'sink mut TrapSink,

View File

@@ -78,6 +78,36 @@ impl Context {
self.loop_analysis.clear(); 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. /// Compile the function.
/// ///
/// Run the function through all the passes necessary to generate code for the target ISA /// 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. /// code is returned by `compile` above.
/// ///
/// The machine code is not relocated. Instead, any relocations are emitted into `relocs`. /// 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, &self,
isa: &TargetIsa,
mem: *mut u8, mem: *mut u8,
relocs: &mut RelocSink, relocs: &mut RelocSink,
traps: &mut TrapSink, traps: &mut TrapSink,
isa: &TargetIsa,
) { ) {
let _tt = timing::binemit(); let _tt = timing::binemit();
isa.emit_function(&self.func, &mut MemoryCodeSink::new(mem, relocs, traps)); isa.emit_function(&self.func, &mut MemoryCodeSink::new(mem, relocs, traps));

View File

@@ -107,12 +107,14 @@ impl Backend for FaerieBackend {
// that traps. // that traps.
let mut trap_sink = NullTrapSink {}; let mut trap_sink = NullTrapSink {};
ctx.emit_to_memory( unsafe {
code.as_mut_ptr(), ctx.emit_to_memory(
&mut reloc_sink, &*self.isa,
&mut trap_sink, code.as_mut_ptr(),
&*self.isa, &mut reloc_sink,
); &mut trap_sink,
)
};
} }
self.artifact.define(name, code).expect( 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 // Ignore traps for now. For now, frontends should just avoid generating code
// that traps. // that traps.
let mut trap_sink = NullTrapSink {}; 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 { Ok(Self::CompiledFunction {
code: ptr, code: ptr,