cranelift: improve syscall error/oom handling in JIT module (#5173)

* cranelift: improve syscall error/oom handling in JIT module

The JIT module has several places where it `expect`s or `panic`s
on syscall or allocator errors. For example, `mmap` and `mprotect`
can fail if Linux `vm.max_map_count` is not high enough, and some
users may wish to handle this error rather than immediately
crashing.

This commit plumbs these errors upward as new `ModuleError`
types, so that callers of jit module functions like
`finalize_definitions` and `define_function` can handle them
(or just `unwrap()`, as desired).

* cranelift: Remove ModuleError::Syscall variant

Syscall errors can just be folded into the generic Backend error,
which is an anyhow::Error

* cranelift-jit: return io::ErrorKind::OutOfMemory for alloc failure

Just using `io::Error::last_os_error()` is not correct as global
allocator impls are not required to set errno
This commit is contained in:
11evan
2022-11-03 16:59:41 -07:00
committed by GitHub
parent 5285ba15b1
commit 387426e7f4
6 changed files with 69 additions and 24 deletions

View File

@@ -427,7 +427,9 @@ impl JITModule {
///
/// Use `get_finalized_function` and `get_finalized_data` to obtain the final
/// artifacts.
pub fn finalize_definitions(&mut self) {
///
/// Returns ModuleError in case of allocation or syscall failure
pub fn finalize_definitions(&mut self) -> ModuleResult<()> {
for func in std::mem::take(&mut self.functions_to_finalize) {
let decl = self.declarations.get_function_decl(func);
assert!(decl.linkage.is_definable());
@@ -455,12 +457,13 @@ impl JITModule {
}
// Now that we're done patching, prepare the memory for execution!
self.memory.readonly.set_readonly();
self.memory.code.set_readable_and_executable();
self.memory.readonly.set_readonly()?;
self.memory.code.set_readable_and_executable()?;
for update in self.pending_got_updates.drain(..) {
unsafe { update.entry.as_ref() }.store(update.ptr as *mut _, Ordering::SeqCst);
}
Ok(())
}
/// Create a new `JITModule`.
@@ -688,7 +691,10 @@ impl Module for JITModule {
.memory
.code
.allocate(size, align)
.expect("TODO: handle OOM etc.");
.map_err(|e| ModuleError::Allocation {
message: "unable to alloc function",
err: e,
})?;
{
let mem = unsafe { std::slice::from_raw_parts_mut(ptr, size) };
@@ -770,7 +776,10 @@ impl Module for JITModule {
.memory
.code
.allocate(size, align)
.expect("TODO: handle OOM etc.");
.map_err(|e| ModuleError::Allocation {
message: "unable to alloc function bytes",
err: e,
})?;
unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
@@ -836,12 +845,18 @@ impl Module for JITModule {
self.memory
.writable
.allocate(size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
.expect("TODO: handle OOM etc.")
.map_err(|e| ModuleError::Allocation {
message: "unable to alloc writable data",
err: e,
})?
} else {
self.memory
.readonly
.allocate(size, align.unwrap_or(READONLY_DATA_ALIGNMENT))
.expect("TODO: handle OOM etc.")
.map_err(|e| ModuleError::Allocation {
message: "unable to alloc readonly data",
err: e,
})?
};
match *init {