Fix libcall relocations for precompiled modules (#5608)
* Fix libcall relocations for precompiled modules This commit fixes some asserts and support for relocation libcalls in precompiled modules loaded from disk. In doing so this reworks how mmaps are managed for files from disk. All non-file-backed `Mmap` entries are read/write but file-backed versions were readonly. This commit changes this such that all `Mmap` objects, even if they're file-backed, start as read/write. The file-based versions all use copy-on-write to preserve the private-ness of the mapping. This is not functionally intended to change anything. Instead this should have some more memory writable after a module is loaded but the text section, for example, is still left as read/execute when loading is finished. Additionally this makes modules compiled in memory more consistent with modules loaded from disk. * Update a comment * Force images to become readonly during publish This marks compiled images as entirely readonly during the `CodeMemory::publish` step which happens just before the text section becomes executable. This ensures that all images, no matter where they come from, are guaranteed frozen before they start executing.
This commit is contained in:
@@ -237,8 +237,22 @@ impl CodeMemory {
|
||||
// both the actual unwinding tables as well as the validity of the
|
||||
// pointers we pass in itself.
|
||||
unsafe {
|
||||
// First, if necessary, apply relocations. This can happen for
|
||||
// things like libcalls which happen late in the lowering process
|
||||
// that don't go through the Wasm-based libcalls layer that's
|
||||
// indirected through the `VMContext`. Note that most modules won't
|
||||
// have relocations, so this typically doesn't do anything.
|
||||
self.apply_relocations()?;
|
||||
|
||||
// Next freeze the contents of this image by making all of the
|
||||
// memory readonly. Nothing after this point should ever be modified
|
||||
// so commit everything. For a compiled-in-memory image this will
|
||||
// mean IPIs to evict writable mappings from other cores. For
|
||||
// loaded-from-disk images this shouldn't result in IPIs so long as
|
||||
// there weren't any relocations because nothing should have
|
||||
// otherwise written to the image at any point either.
|
||||
self.mmap.make_readonly(0..self.mmap.len())?;
|
||||
|
||||
let text = self.text();
|
||||
|
||||
// Clear the newly allocated code from cache if the processor requires it
|
||||
@@ -248,9 +262,7 @@ impl CodeMemory {
|
||||
icache_coherence::clear_cache(text.as_ptr().cast(), text.len())
|
||||
.expect("Failed cache clear");
|
||||
|
||||
// Switch the executable portion from read/write to
|
||||
// read/execute, notably not using read/write/execute to prevent
|
||||
// modifications.
|
||||
// Switch the executable portion from readonly to read/execute.
|
||||
self.mmap
|
||||
.make_executable(self.text.clone(), self.enable_branch_protection)
|
||||
.expect("unable to make memory executable");
|
||||
@@ -273,13 +285,6 @@ impl CodeMemory {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Mmaps currently all start as readonly so before updating relocations
|
||||
// the mapping needs to be made writable first. Note that this isn't
|
||||
// reset back to readonly since the `make_executable` call, which
|
||||
// happens after this, will implicitly remove the writable bit and leave
|
||||
// it as just read/execute.
|
||||
self.mmap.make_writable(self.text.clone())?;
|
||||
|
||||
for (offset, libcall) in self.relocations.iter() {
|
||||
let offset = self.text.start + offset;
|
||||
let libcall = match libcall {
|
||||
|
||||
Reference in New Issue
Block a user