1 //! Memory management for executable code. 2 3 use crate::Engine; 4 use crate::prelude::*; 5 use crate::runtime::vm::MmapVec; 6 use alloc::sync::Arc; 7 use core::ops::Range; 8 use object::SectionFlags; 9 use object::endian::Endianness; 10 use object::read::{Object, ObjectSection, elf::ElfFile64}; 11 use wasmtime_environ::{Trap, lookup_trap_code, obj}; 12 13 /// Management of executable memory within a `MmapVec` 14 /// 15 /// This type consumes ownership of a region of memory and will manage the 16 /// executable permissions of the contained JIT code as necessary. 17 pub struct CodeMemory { 18 mmap: MmapVec, 19 #[cfg(has_host_compiler_backend)] 20 unwind_registration: Option<crate::runtime::vm::UnwindRegistration>, 21 #[cfg(feature = "debug-builtins")] 22 debug_registration: Option<crate::runtime::vm::GdbJitImageRegistration>, 23 published: bool, 24 enable_branch_protection: bool, 25 needs_executable: bool, 26 #[cfg(feature = "debug-builtins")] 27 has_native_debug_info: bool, 28 custom_code_memory: Option<Arc<dyn CustomCodeMemory>>, 29 30 // Ranges within `self.mmap` of where the particular sections lie. 31 text: Range<usize>, 32 unwind: Range<usize>, 33 trap_data: Range<usize>, 34 wasm_data: Range<usize>, 35 address_map_data: Range<usize>, 36 stack_map_data: Range<usize>, 37 func_name_data: Range<usize>, 38 info_data: Range<usize>, 39 wasm_dwarf: Range<usize>, 40 } 41 42 impl Drop for CodeMemory { 43 fn drop(&mut self) { 44 // If there is a custom code memory handler, restore the 45 // original (non-executable) state of the memory. 46 if let Some(mem) = self.custom_code_memory.as_ref() { 47 if self.published && self.needs_executable { 48 let text = self.text(); 49 mem.unpublish_executable(text.as_ptr(), text.len()) 50 .expect("Executable memory unpublish failed"); 51 } 52 } 53 54 // Drop the registrations before `self.mmap` since they (implicitly) refer to it. 55 #[cfg(has_host_compiler_backend)] 56 let _ = self.unwind_registration.take(); 57 #[cfg(feature = "debug-builtins")] 58 let _ = self.debug_registration.take(); 59 } 60 } 61 62 fn _assert() { 63 fn _assert_send_sync<T: Send + Sync>() {} 64 _assert_send_sync::<CodeMemory>(); 65 } 66 67 /// Interface implemented by an embedder to provide custom 68 /// implementations of code-memory protection and execute permissions. 69 pub trait CustomCodeMemory: Send + Sync { 70 /// The minimal alignment granularity for an address region that 71 /// can be made executable. 72 /// 73 /// Wasmtime does not assume the system page size for this because 74 /// custom code-memory protection can be used when all other uses 75 /// of virtual memory are disabled. 76 fn required_alignment(&self) -> usize; 77 78 /// Publish a region of memory as executable. 79 /// 80 /// This should update permissions from the default RW 81 /// (readable/writable but not executable) to RX 82 /// (readable/executable but not writable), enforcing W^X 83 /// discipline. 84 /// 85 /// If the platform requires any data/instruction coherence 86 /// action, that should be performed as part of this hook as well. 87 /// 88 /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as 89 /// per `required_alignment()`. 90 fn publish_executable(&self, ptr: *const u8, len: usize) -> anyhow::Result<()>; 91 92 /// Unpublish a region of memory. 93 /// 94 /// This should perform the opposite effect of `make_executable`, 95 /// switching a range of memory back from RX (readable/executable) 96 /// to RW (readable/writable). It is guaranteed that no code is 97 /// running anymore from this region. 98 /// 99 /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as 100 /// per `required_alignment()`. 101 fn unpublish_executable(&self, ptr: *const u8, len: usize) -> anyhow::Result<()>; 102 } 103 104 impl CodeMemory { 105 /// Creates a new `CodeMemory` by taking ownership of the provided 106 /// `MmapVec`. 107 /// 108 /// The returned `CodeMemory` manages the internal `MmapVec` and the 109 /// `publish` method is used to actually make the memory executable. 110 pub fn new(engine: &Engine, mmap: MmapVec) -> Result<Self> { 111 let obj = ElfFile64::<Endianness>::parse(&mmap[..]) 112 .map_err(obj::ObjectCrateErrorWrapper) 113 .with_context(|| "failed to parse internal compilation artifact")?; 114 115 let mut text = 0..0; 116 let mut unwind = 0..0; 117 let mut enable_branch_protection = None; 118 let mut needs_executable = true; 119 #[cfg(feature = "debug-builtins")] 120 let mut has_native_debug_info = false; 121 let mut trap_data = 0..0; 122 let mut wasm_data = 0..0; 123 let mut address_map_data = 0..0; 124 let mut stack_map_data = 0..0; 125 let mut func_name_data = 0..0; 126 let mut info_data = 0..0; 127 let mut wasm_dwarf = 0..0; 128 for section in obj.sections() { 129 let data = section.data().map_err(obj::ObjectCrateErrorWrapper)?; 130 let name = section.name().map_err(obj::ObjectCrateErrorWrapper)?; 131 let range = subslice_range(data, &mmap); 132 133 // Double-check that sections are all aligned properly. 134 if section.align() != 0 && data.len() != 0 { 135 if (data.as_ptr() as u64 - mmap.as_ptr() as u64) % section.align() != 0 { 136 bail!( 137 "section `{}` isn't aligned to {:#x}", 138 section.name().unwrap_or("ERROR"), 139 section.align() 140 ); 141 } 142 } 143 144 match name { 145 obj::ELF_WASM_BTI => match data.len() { 146 1 => enable_branch_protection = Some(data[0] != 0), 147 _ => bail!("invalid `{name}` section"), 148 }, 149 ".text" => { 150 text = range; 151 152 if let SectionFlags::Elf { sh_flags } = section.flags() { 153 if sh_flags & obj::SH_WASMTIME_NOT_EXECUTED != 0 { 154 needs_executable = false; 155 } 156 } 157 158 // Assert that Cranelift hasn't inserted any calls that need to be 159 // relocated. We avoid using things like Cranelift's floor/ceil/etc. 160 // operators in the Wasm-to-Cranelift translator specifically to 161 // avoid having to do any relocations here. This also ensures that 162 // all builtins use the same trampoline mechanism. 163 assert!(section.relocations().next().is_none()); 164 } 165 #[cfg(has_host_compiler_backend)] 166 crate::runtime::vm::UnwindRegistration::SECTION_NAME => unwind = range, 167 obj::ELF_WASM_DATA => wasm_data = range, 168 obj::ELF_WASMTIME_ADDRMAP => address_map_data = range, 169 obj::ELF_WASMTIME_STACK_MAP => stack_map_data = range, 170 obj::ELF_WASMTIME_TRAPS => trap_data = range, 171 obj::ELF_NAME_DATA => func_name_data = range, 172 obj::ELF_WASMTIME_INFO => info_data = range, 173 obj::ELF_WASMTIME_DWARF => wasm_dwarf = range, 174 #[cfg(feature = "debug-builtins")] 175 ".debug_info" => has_native_debug_info = true, 176 177 _ => log::debug!("ignoring section {name}"), 178 } 179 } 180 181 // require mutability even when this is turned off 182 #[cfg(not(has_host_compiler_backend))] 183 let _ = &mut unwind; 184 185 Ok(Self { 186 mmap, 187 #[cfg(has_host_compiler_backend)] 188 unwind_registration: None, 189 #[cfg(feature = "debug-builtins")] 190 debug_registration: None, 191 published: false, 192 enable_branch_protection: enable_branch_protection 193 .ok_or_else(|| anyhow!("missing `{}` section", obj::ELF_WASM_BTI))?, 194 needs_executable, 195 #[cfg(feature = "debug-builtins")] 196 has_native_debug_info, 197 custom_code_memory: engine.custom_code_memory().cloned(), 198 text, 199 unwind, 200 trap_data, 201 address_map_data, 202 stack_map_data, 203 func_name_data, 204 wasm_dwarf, 205 info_data, 206 wasm_data, 207 }) 208 } 209 210 /// Returns a reference to the underlying `MmapVec` this memory owns. 211 #[inline] 212 pub fn mmap(&self) -> &MmapVec { 213 &self.mmap 214 } 215 216 /// Returns the contents of the text section of the ELF executable this 217 /// represents. 218 #[inline] 219 pub fn text(&self) -> &[u8] { 220 &self.mmap[self.text.clone()] 221 } 222 223 /// Returns the contents of the `ELF_WASMTIME_DWARF` section. 224 #[inline] 225 pub fn wasm_dwarf(&self) -> &[u8] { 226 &self.mmap[self.wasm_dwarf.clone()] 227 } 228 229 /// Returns the data in the `ELF_NAME_DATA` section. 230 #[inline] 231 pub fn func_name_data(&self) -> &[u8] { 232 &self.mmap[self.func_name_data.clone()] 233 } 234 235 /// Returns the concatenated list of all data associated with this wasm 236 /// module. 237 /// 238 /// This is used for initialization of memories and all data ranges stored 239 /// in a `Module` are relative to the slice returned here. 240 #[inline] 241 pub fn wasm_data(&self) -> &[u8] { 242 &self.mmap[self.wasm_data.clone()] 243 } 244 245 /// Returns the encoded address map section used to pass to 246 /// `wasmtime_environ::lookup_file_pos`. 247 #[inline] 248 pub fn address_map_data(&self) -> &[u8] { 249 &self.mmap[self.address_map_data.clone()] 250 } 251 252 /// Returns the encoded stack map section used to pass to 253 /// `wasmtime_environ::StackMap::lookup`. 254 pub fn stack_map_data(&self) -> &[u8] { 255 &self.mmap[self.stack_map_data.clone()] 256 } 257 258 /// Returns the contents of the `ELF_WASMTIME_INFO` section, or an empty 259 /// slice if it wasn't found. 260 #[inline] 261 pub fn wasmtime_info(&self) -> &[u8] { 262 &self.mmap[self.info_data.clone()] 263 } 264 265 /// Returns the contents of the `ELF_WASMTIME_TRAPS` section, or an empty 266 /// slice if it wasn't found. 267 #[inline] 268 pub fn trap_data(&self) -> &[u8] { 269 &self.mmap[self.trap_data.clone()] 270 } 271 272 /// Publishes the internal ELF image to be ready for execution. 273 /// 274 /// This method can only be called once and will panic if called twice. This 275 /// will parse the ELF image from the original `MmapVec` and do everything 276 /// necessary to get it ready for execution, including: 277 /// 278 /// * Change page protections from read/write to read/execute. 279 /// * Register unwinding information with the OS 280 /// * Register this image with the debugger if native DWARF is present 281 /// 282 /// After this function executes all JIT code should be ready to execute. 283 pub fn publish(&mut self) -> Result<()> { 284 assert!(!self.published); 285 self.published = true; 286 287 if self.text().is_empty() { 288 return Ok(()); 289 } 290 291 // The unsafety here comes from a few things: 292 // 293 // * We're actually updating some page protections to executable memory. 294 // 295 // * We're registering unwinding information which relies on the 296 // correctness of the information in the first place. This applies to 297 // both the actual unwinding tables as well as the validity of the 298 // pointers we pass in itself. 299 unsafe { 300 // Next freeze the contents of this image by making all of the 301 // memory readonly. Nothing after this point should ever be modified 302 // so commit everything. For a compiled-in-memory image this will 303 // mean IPIs to evict writable mappings from other cores. For 304 // loaded-from-disk images this shouldn't result in IPIs so long as 305 // there weren't any relocations because nothing should have 306 // otherwise written to the image at any point either. 307 // 308 // Note that if virtual memory is disabled this is skipped because 309 // we aren't able to make it readonly, but this is just a 310 // defense-in-depth measure and isn't required for correctness. 311 #[cfg(has_virtual_memory)] 312 if self.mmap.supports_virtual_memory() { 313 self.mmap.make_readonly(0..self.mmap.len())?; 314 } 315 316 // Switch the executable portion from readonly to read/execute. 317 if self.needs_executable { 318 if !self.custom_publish()? { 319 if !self.mmap.supports_virtual_memory() { 320 bail!("this target requires virtual memory to be enabled"); 321 } 322 if !cfg!(feature = "std") { 323 bail!( 324 "with the `std` feature disabled at compile time \ 325 there must be a custom implementation of publishing \ 326 code memory" 327 ); 328 } 329 330 #[cfg(all(has_virtual_memory, feature = "std"))] 331 { 332 let text = self.text(); 333 334 use wasmtime_jit_icache_coherence as icache_coherence; 335 336 // Clear the newly allocated code from cache if the processor requires it 337 // 338 // Do this before marking the memory as R+X, technically we should be able to do it after 339 // but there are some CPU's that have had errata about doing this with read only memory. 340 icache_coherence::clear_cache(text.as_ptr().cast(), text.len()) 341 .expect("Failed cache clear"); 342 343 self.mmap 344 .make_executable(self.text.clone(), self.enable_branch_protection) 345 .context("unable to make memory executable")?; 346 347 // Flush any in-flight instructions from the pipeline 348 icache_coherence::pipeline_flush_mt().expect("Failed pipeline flush"); 349 } 350 } 351 } 352 353 // With all our memory set up use the platform-specific 354 // `UnwindRegistration` implementation to inform the general 355 // runtime that there's unwinding information available for all 356 // our just-published JIT functions. 357 self.register_unwind_info()?; 358 359 #[cfg(feature = "debug-builtins")] 360 self.register_debug_image()?; 361 } 362 363 Ok(()) 364 } 365 366 fn custom_publish(&mut self) -> Result<bool> { 367 if let Some(mem) = self.custom_code_memory.as_ref() { 368 let text = self.text(); 369 // The text section should be aligned to 370 // `custom_code_memory.required_alignment()` due to a 371 // combination of two invariants: 372 // 373 // - MmapVec aligns its start address, even in owned-Vec mode; and 374 // - The text segment inside the ELF image will be aligned according 375 // to the platform's requirements. 376 let text_addr = text.as_ptr() as usize; 377 assert_eq!(text_addr & (mem.required_alignment() - 1), 0); 378 379 // The custom code memory handler will ensure the 380 // memory is executable and also handle icache 381 // coherence. 382 mem.publish_executable(text.as_ptr(), text.len())?; 383 Ok(true) 384 } else { 385 Ok(false) 386 } 387 } 388 389 unsafe fn register_unwind_info(&mut self) -> Result<()> { 390 if self.unwind.len() == 0 { 391 return Ok(()); 392 } 393 #[cfg(has_host_compiler_backend)] 394 { 395 let text = self.text(); 396 let unwind_info = &self.mmap[self.unwind.clone()]; 397 let registration = unsafe { 398 crate::runtime::vm::UnwindRegistration::new( 399 text.as_ptr(), 400 unwind_info.as_ptr(), 401 unwind_info.len(), 402 ) 403 .context("failed to create unwind info registration")? 404 }; 405 self.unwind_registration = Some(registration); 406 return Ok(()); 407 } 408 #[cfg(not(has_host_compiler_backend))] 409 { 410 bail!("should not have unwind info for non-native backend") 411 } 412 } 413 414 #[cfg(feature = "debug-builtins")] 415 fn register_debug_image(&mut self) -> Result<()> { 416 if !self.has_native_debug_info { 417 return Ok(()); 418 } 419 420 // TODO-DebugInfo: we're copying the whole image here, which is pretty wasteful. 421 // Use the existing memory by teaching code here about relocations in DWARF sections 422 // and anything else necessary that is done in "create_gdbjit_image" right now. 423 let image = self.mmap().to_vec(); 424 let text: &[u8] = self.text(); 425 let bytes = crate::debug::create_gdbjit_image(image, (text.as_ptr(), text.len()))?; 426 let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes); 427 self.debug_registration = Some(reg); 428 Ok(()) 429 } 430 431 /// Looks up the given offset within this module's text section and returns 432 /// the trap code associated with that instruction, if there is one. 433 pub fn lookup_trap_code(&self, text_offset: usize) -> Option<Trap> { 434 lookup_trap_code(self.trap_data(), text_offset) 435 } 436 } 437 438 /// Returns the range of `inner` within `outer`, such that `outer[range]` is the 439 /// same as `inner`. 440 /// 441 /// This method requires that `inner` is a sub-slice of `outer`, and if that 442 /// isn't true then this method will panic. 443 fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> { 444 if inner.len() == 0 { 445 return 0..0; 446 } 447 448 assert!(outer.as_ptr() <= inner.as_ptr()); 449 assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _)); 450 451 let start = inner.as_ptr() as usize - outer.as_ptr() as usize; 452 start..start + inner.len() 453 } 454