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 use wasmtime_unwinder::ExceptionTable; 13 14 /// Management of executable memory within a `MmapVec` 15 /// 16 /// This type consumes ownership of a region of memory and will manage the 17 /// executable permissions of the contained JIT code as necessary. 18 pub struct CodeMemory { 19 mmap: MmapVec, 20 #[cfg(has_host_compiler_backend)] 21 unwind_registration: Option<crate::runtime::vm::UnwindRegistration>, 22 #[cfg(feature = "debug-builtins")] 23 debug_registration: Option<crate::runtime::vm::GdbJitImageRegistration>, 24 published: bool, 25 registered: bool, 26 enable_branch_protection: bool, 27 needs_executable: bool, 28 #[cfg(feature = "debug-builtins")] 29 has_native_debug_info: bool, 30 custom_code_memory: Option<Arc<dyn CustomCodeMemory>>, 31 32 // Ranges within `self.mmap` of where the particular sections lie. 33 text: Range<usize>, 34 unwind: Range<usize>, 35 trap_data: Range<usize>, 36 wasm_data: Range<usize>, 37 address_map_data: Range<usize>, 38 stack_map_data: Range<usize>, 39 exception_data: Range<usize>, 40 frame_tables_data: Range<usize>, 41 func_name_data: Range<usize>, 42 info_data: Range<usize>, 43 wasm_dwarf: Range<usize>, 44 } 45 46 impl Drop for CodeMemory { 47 fn drop(&mut self) { 48 // If there is a custom code memory handler, restore the 49 // original (non-executable) state of the memory. 50 // 51 // We do this rather than invoking `unpublish()` because we 52 // want to skip the mprotect() if we natively own the mmap and 53 // are going to munmap soon anyway. 54 if let Some(mem) = self.custom_code_memory.as_ref() { 55 if self.published && self.needs_executable { 56 let text = self.text(); 57 mem.unpublish_executable(text.as_ptr(), text.len()) 58 .expect("Executable memory unpublish failed"); 59 } 60 } 61 62 // Drop the registrations before `self.mmap` since they (implicitly) refer to it. 63 #[cfg(has_host_compiler_backend)] 64 let _ = self.unwind_registration.take(); 65 #[cfg(feature = "debug-builtins")] 66 let _ = self.debug_registration.take(); 67 } 68 } 69 70 fn _assert() { 71 fn _assert_send_sync<T: Send + Sync>() {} 72 _assert_send_sync::<CodeMemory>(); 73 } 74 75 /// Interface implemented by an embedder to provide custom 76 /// implementations of code-memory protection and execute permissions. 77 pub trait CustomCodeMemory: Send + Sync { 78 /// The minimal alignment granularity for an address region that 79 /// can be made executable. 80 /// 81 /// Wasmtime does not assume the system page size for this because 82 /// custom code-memory protection can be used when all other uses 83 /// of virtual memory are disabled. 84 fn required_alignment(&self) -> usize; 85 86 /// Publish a region of memory as executable. 87 /// 88 /// This should update permissions from the default RW 89 /// (readable/writable but not executable) to RX 90 /// (readable/executable but not writable), enforcing W^X 91 /// discipline. 92 /// 93 /// If the platform requires any data/instruction coherence 94 /// action, that should be performed as part of this hook as well. 95 /// 96 /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as 97 /// per `required_alignment()`. 98 fn publish_executable(&self, ptr: *const u8, len: usize) -> anyhow::Result<()>; 99 100 /// Unpublish a region of memory. 101 /// 102 /// This should perform the opposite effect of `make_executable`, 103 /// switching a range of memory back from RX (readable/executable) 104 /// to RW (readable/writable). It is guaranteed that no code is 105 /// running anymore from this region. 106 /// 107 /// `ptr` and `ptr.offset(len)` are guaranteed to be aligned as 108 /// per `required_alignment()`. 109 fn unpublish_executable(&self, ptr: *const u8, len: usize) -> anyhow::Result<()>; 110 } 111 112 impl CodeMemory { 113 /// Creates a new `CodeMemory` by taking ownership of the provided 114 /// `MmapVec`. 115 /// 116 /// The returned `CodeMemory` manages the internal `MmapVec` and the 117 /// `publish` method is used to actually make the memory executable. 118 pub fn new(engine: &Engine, mmap: MmapVec) -> Result<Self> { 119 let obj = ElfFile64::<Endianness>::parse(&mmap[..]) 120 .map_err(obj::ObjectCrateErrorWrapper) 121 .with_context(|| "failed to parse internal compilation artifact")?; 122 123 let mut text = 0..0; 124 let mut unwind = 0..0; 125 let mut enable_branch_protection = None; 126 let mut needs_executable = true; 127 #[cfg(feature = "debug-builtins")] 128 let mut has_native_debug_info = false; 129 let mut trap_data = 0..0; 130 let mut exception_data = 0..0; 131 let mut frame_tables_data = 0..0; 132 let mut wasm_data = 0..0; 133 let mut address_map_data = 0..0; 134 let mut stack_map_data = 0..0; 135 let mut func_name_data = 0..0; 136 let mut info_data = 0..0; 137 let mut wasm_dwarf = 0..0; 138 for section in obj.sections() { 139 let data = section.data().map_err(obj::ObjectCrateErrorWrapper)?; 140 let name = section.name().map_err(obj::ObjectCrateErrorWrapper)?; 141 let range = subslice_range(data, &mmap); 142 143 // Double-check that sections are all aligned properly. 144 if section.align() != 0 && data.len() != 0 { 145 if (data.as_ptr() as u64 - mmap.as_ptr() as u64) % section.align() != 0 { 146 bail!( 147 "section `{}` isn't aligned to {:#x}", 148 section.name().unwrap_or("ERROR"), 149 section.align() 150 ); 151 } 152 } 153 154 match name { 155 obj::ELF_WASM_BTI => match data.len() { 156 1 => enable_branch_protection = Some(data[0] != 0), 157 _ => bail!("invalid `{name}` section"), 158 }, 159 ".text" => { 160 text = range; 161 162 if let SectionFlags::Elf { sh_flags } = section.flags() { 163 if sh_flags & obj::SH_WASMTIME_NOT_EXECUTED != 0 { 164 needs_executable = false; 165 } 166 } 167 168 // Assert that Cranelift hasn't inserted any calls that need to be 169 // relocated. We avoid using things like Cranelift's floor/ceil/etc. 170 // operators in the Wasm-to-Cranelift translator specifically to 171 // avoid having to do any relocations here. This also ensures that 172 // all builtins use the same trampoline mechanism. 173 assert!(section.relocations().next().is_none()); 174 } 175 #[cfg(has_host_compiler_backend)] 176 crate::runtime::vm::UnwindRegistration::SECTION_NAME => unwind = range, 177 obj::ELF_WASM_DATA => wasm_data = range, 178 obj::ELF_WASMTIME_ADDRMAP => address_map_data = range, 179 obj::ELF_WASMTIME_STACK_MAP => stack_map_data = range, 180 obj::ELF_WASMTIME_TRAPS => trap_data = range, 181 obj::ELF_WASMTIME_EXCEPTIONS => exception_data = range, 182 obj::ELF_WASMTIME_FRAMES => frame_tables_data = range, 183 obj::ELF_NAME_DATA => func_name_data = range, 184 obj::ELF_WASMTIME_INFO => info_data = range, 185 obj::ELF_WASMTIME_DWARF => wasm_dwarf = range, 186 #[cfg(feature = "debug-builtins")] 187 ".debug_info" => has_native_debug_info = true, 188 189 _ => log::debug!("ignoring section {name}"), 190 } 191 } 192 193 // require mutability even when this is turned off 194 #[cfg(not(has_host_compiler_backend))] 195 let _ = &mut unwind; 196 197 // Ensure that the exception table is well-formed. This parser 198 // construction is cheap: it reads the header and validates 199 // ranges but nothing else. We do this only in debug-assertion 200 // builds because we otherwise require for safety that the 201 // compiled artifact is as-produced-by this version of 202 // Wasmtime, and we should always produce a correct exception 203 // table (i.e., we are not expecting untrusted data here). 204 if cfg!(debug_assertions) { 205 let _ = ExceptionTable::parse(&mmap[exception_data.clone()])?; 206 } 207 208 Ok(Self { 209 mmap, 210 #[cfg(has_host_compiler_backend)] 211 unwind_registration: None, 212 #[cfg(feature = "debug-builtins")] 213 debug_registration: None, 214 published: false, 215 registered: false, 216 enable_branch_protection: enable_branch_protection 217 .ok_or_else(|| anyhow!("missing `{}` section", obj::ELF_WASM_BTI))?, 218 needs_executable, 219 #[cfg(feature = "debug-builtins")] 220 has_native_debug_info, 221 custom_code_memory: engine.custom_code_memory().cloned(), 222 text, 223 unwind, 224 trap_data, 225 address_map_data, 226 stack_map_data, 227 exception_data, 228 frame_tables_data, 229 func_name_data, 230 wasm_dwarf, 231 info_data, 232 wasm_data, 233 }) 234 } 235 236 /// Returns a reference to the underlying `MmapVec` this memory owns. 237 #[inline] 238 pub fn mmap(&self) -> &MmapVec { 239 &self.mmap 240 } 241 242 /// Returns the contents of the text section of the ELF executable this 243 /// represents. 244 #[inline] 245 pub fn text(&self) -> &[u8] { 246 &self.mmap[self.text.clone()] 247 } 248 249 /// Returns the contents of the `ELF_WASMTIME_DWARF` section. 250 #[inline] 251 pub fn wasm_dwarf(&self) -> &[u8] { 252 &self.mmap[self.wasm_dwarf.clone()] 253 } 254 255 /// Returns the data in the `ELF_NAME_DATA` section. 256 #[inline] 257 pub fn func_name_data(&self) -> &[u8] { 258 &self.mmap[self.func_name_data.clone()] 259 } 260 261 /// Returns the concatenated list of all data associated with this wasm 262 /// module. 263 /// 264 /// This is used for initialization of memories and all data ranges stored 265 /// in a `Module` are relative to the slice returned here. 266 #[inline] 267 pub fn wasm_data(&self) -> &[u8] { 268 &self.mmap[self.wasm_data.clone()] 269 } 270 271 /// Returns the encoded address map section used to pass to 272 /// `wasmtime_environ::lookup_file_pos`. 273 #[inline] 274 pub fn address_map_data(&self) -> &[u8] { 275 &self.mmap[self.address_map_data.clone()] 276 } 277 278 /// Returns the encoded stack map section used to pass to 279 /// `wasmtime_environ::StackMap::lookup`. 280 pub fn stack_map_data(&self) -> &[u8] { 281 &self.mmap[self.stack_map_data.clone()] 282 } 283 284 /// Returns the encoded exception-tables section to pass to 285 /// `wasmtime_unwinder::ExceptionTable::parse`. 286 pub fn exception_tables(&self) -> &[u8] { 287 &self.mmap[self.exception_data.clone()] 288 } 289 290 /// Returns the encoded frame-tables section to pass to 291 /// `wasmtime_environ::FrameTable::parse`. 292 pub fn frame_tables(&self) -> &[u8] { 293 &self.mmap[self.frame_tables_data.clone()] 294 } 295 296 /// Returns the contents of the `ELF_WASMTIME_INFO` section, or an empty 297 /// slice if it wasn't found. 298 #[inline] 299 pub fn wasmtime_info(&self) -> &[u8] { 300 &self.mmap[self.info_data.clone()] 301 } 302 303 /// Returns the contents of the `ELF_WASMTIME_TRAPS` section, or an empty 304 /// slice if it wasn't found. 305 #[inline] 306 pub fn trap_data(&self) -> &[u8] { 307 &self.mmap[self.trap_data.clone()] 308 } 309 310 /// Publishes the internal ELF image to be ready for execution. 311 /// 312 /// This method can only be when the image is not published (its 313 /// default state) and will panic if called when already 314 /// published. This will parse the ELF image from the original 315 /// `MmapVec` and do everything necessary to get it ready for 316 /// execution, including: 317 /// 318 /// * Change page protections from read/write to read/execute. 319 /// * Register unwinding information with the OS 320 /// * Register this image with the debugger if native DWARF is present 321 /// 322 /// After this function executes all JIT code should be ready to execute. 323 /// 324 /// The action may be reversed by calling [`unpublish`], as long 325 /// as that method's safety requirements are upheld. 326 pub fn publish(&mut self) -> Result<()> { 327 assert!(!self.published); 328 self.published = true; 329 330 if self.text().is_empty() { 331 return Ok(()); 332 } 333 334 // The unsafety here comes from a few things: 335 // 336 // * We're actually updating some page protections to executable memory. 337 // 338 // * We're registering unwinding information which relies on the 339 // correctness of the information in the first place. This applies to 340 // both the actual unwinding tables as well as the validity of the 341 // pointers we pass in itself. 342 unsafe { 343 // Next freeze the contents of this image by making all of the 344 // memory readonly. Nothing after this point should ever be modified 345 // so commit everything. For a compiled-in-memory image this will 346 // mean IPIs to evict writable mappings from other cores. For 347 // loaded-from-disk images this shouldn't result in IPIs so long as 348 // there weren't any relocations because nothing should have 349 // otherwise written to the image at any point either. 350 // 351 // Note that if virtual memory is disabled this is skipped because 352 // we aren't able to make it readonly, but this is just a 353 // defense-in-depth measure and isn't required for correctness. 354 #[cfg(has_virtual_memory)] 355 if self.mmap.supports_virtual_memory() { 356 self.mmap.make_readonly(0..self.mmap.len())?; 357 } 358 359 // Switch the executable portion from readonly to read/execute. 360 if self.needs_executable { 361 if !self.custom_publish()? { 362 if !self.mmap.supports_virtual_memory() { 363 bail!("this target requires virtual memory to be enabled"); 364 } 365 #[cfg(has_virtual_memory)] 366 self.mmap 367 .make_executable(self.text.clone(), self.enable_branch_protection) 368 .context("unable to make memory executable")?; 369 } 370 } 371 372 if !self.registered { 373 // With all our memory set up use the platform-specific 374 // `UnwindRegistration` implementation to inform the general 375 // runtime that there's unwinding information available for all 376 // our just-published JIT functions. 377 self.register_unwind_info()?; 378 379 #[cfg(feature = "debug-builtins")] 380 self.register_debug_image()?; 381 self.registered = true; 382 } 383 } 384 385 Ok(()) 386 } 387 388 fn custom_publish(&mut self) -> Result<bool> { 389 if let Some(mem) = self.custom_code_memory.as_ref() { 390 let text = self.text(); 391 // The text section should be aligned to 392 // `custom_code_memory.required_alignment()` due to a 393 // combination of two invariants: 394 // 395 // - MmapVec aligns its start address, even in owned-Vec mode; and 396 // - The text segment inside the ELF image will be aligned according 397 // to the platform's requirements. 398 let text_addr = text.as_ptr() as usize; 399 assert_eq!(text_addr & (mem.required_alignment() - 1), 0); 400 401 // The custom code memory handler will ensure the 402 // memory is executable and also handle icache 403 // coherence. 404 mem.publish_executable(text.as_ptr(), text.len())?; 405 Ok(true) 406 } else { 407 Ok(false) 408 } 409 } 410 411 /// "Unpublish" code memory (transition it from executable to read/writable). 412 /// 413 /// This may be used to edit the code image, as long as the 414 /// overall size of the memory remains the same. Note the hazards 415 /// inherent in editing code that may have been executed: any 416 /// stack frames with PC still active in this code must be 417 /// suspended (e.g., called into a hostcall that is then invoking 418 /// this method, or async-yielded) and any active PC values must 419 /// point to valid instructions. Thus this is mostly useful for 420 /// patching in-place at particular sites, such as by the use of 421 /// Cranelift's `patchable_call` instruction. 422 /// 423 /// If this fails, then the memory remains executable. 424 pub fn unpublish(&mut self) -> Result<()> { 425 assert!(self.published); 426 self.published = false; 427 428 if self.text().is_empty() { 429 return Ok(()); 430 } 431 432 if self.custom_unpublish()? { 433 return Ok(()); 434 } 435 436 if !self.mmap.supports_virtual_memory() { 437 bail!("this target requires virtual memory to be enabled"); 438 } 439 440 // SAFETY: we are guaranteed by our own safety conditions that 441 // we have exclusive access to this code and can change its 442 // permissions (removing the execute bit) without causing 443 // problems. 444 #[cfg(has_virtual_memory)] 445 unsafe { 446 self.mmap.make_readwrite(0..self.mmap.len())?; 447 } 448 449 // Note that we do *not* unregister: we expect unpublish 450 // to be used for temporary edits, so we want the 451 // registration to "stick" after the initial publish and 452 // not toggle in subsequent unpublish/publish cycles. 453 454 Ok(()) 455 } 456 457 fn custom_unpublish(&mut self) -> Result<bool> { 458 if let Some(mem) = self.custom_code_memory.as_ref() { 459 let text = self.text(); 460 mem.unpublish_executable(text.as_ptr(), text.len())?; 461 Ok(true) 462 } else { 463 Ok(false) 464 } 465 } 466 467 /// Return a mutable borrow to the code, suitable for editing. 468 /// 469 /// Must not be published. 470 /// 471 /// # Panics 472 /// 473 /// This method panics if the code has been published (and not 474 /// subsequently unpublished). 475 pub fn text_mut(&mut self) -> &mut [u8] { 476 assert!(!self.published); 477 // SAFETY: we assert !published, which means we either have 478 // not yet applied readonly + execute permissinos, or we have 479 // undone that and flipped back to read-write via unpublish. 480 unsafe { &mut self.mmap.as_mut_slice()[self.text.clone()] } 481 } 482 483 unsafe fn register_unwind_info(&mut self) -> Result<()> { 484 if self.unwind.len() == 0 { 485 return Ok(()); 486 } 487 #[cfg(has_host_compiler_backend)] 488 { 489 let text = self.text(); 490 let unwind_info = &self.mmap[self.unwind.clone()]; 491 let registration = unsafe { 492 crate::runtime::vm::UnwindRegistration::new( 493 text.as_ptr(), 494 unwind_info.as_ptr(), 495 unwind_info.len(), 496 ) 497 .context("failed to create unwind info registration")? 498 }; 499 self.unwind_registration = Some(registration); 500 return Ok(()); 501 } 502 #[cfg(not(has_host_compiler_backend))] 503 { 504 bail!("should not have unwind info for non-native backend") 505 } 506 } 507 508 #[cfg(feature = "debug-builtins")] 509 fn register_debug_image(&mut self) -> Result<()> { 510 if !self.has_native_debug_info { 511 return Ok(()); 512 } 513 514 // TODO-DebugInfo: we're copying the whole image here, which is pretty wasteful. 515 // Use the existing memory by teaching code here about relocations in DWARF sections 516 // and anything else necessary that is done in "create_gdbjit_image" right now. 517 let image = self.mmap().to_vec(); 518 let text: &[u8] = self.text(); 519 let bytes = crate::native_debug::create_gdbjit_image(image, (text.as_ptr(), text.len()))?; 520 let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes); 521 self.debug_registration = Some(reg); 522 Ok(()) 523 } 524 525 /// Looks up the given offset within this module's text section and returns 526 /// the trap code associated with that instruction, if there is one. 527 pub fn lookup_trap_code(&self, text_offset: usize) -> Option<Trap> { 528 lookup_trap_code(self.trap_data(), text_offset) 529 } 530 531 /// Get the raw address range of this CodeMemory. 532 pub(crate) fn raw_addr_range(&self) -> Range<usize> { 533 let start = self.text().as_ptr().addr(); 534 let end = start + self.text().len(); 535 start..end 536 } 537 538 /// Create a "deep clone": a separate CodeMemory for the same code 539 /// that can be patched or mutated independently. Also returns a 540 /// "metadata and location" handle that can be registered with the 541 /// global module registry and used for trap metadata lookups. 542 #[cfg(feature = "debug")] 543 pub(crate) fn deep_clone(self: &Arc<Self>, engine: &Engine) -> Result<CodeMemory> { 544 let mmap = self.mmap.deep_clone()?; 545 Self::new(engine, mmap) 546 } 547 } 548 549 /// Returns the range of `inner` within `outer`, such that `outer[range]` is the 550 /// same as `inner`. 551 /// 552 /// This method requires that `inner` is a sub-slice of `outer`, and if that 553 /// isn't true then this method will panic. 554 fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> { 555 if inner.len() == 0 { 556 return 0..0; 557 } 558 559 assert!(outer.as_ptr() <= inner.as_ptr()); 560 assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _)); 561 562 let start = inner.as_ptr() as usize - outer.as_ptr() as usize; 563 start..start + inner.len() 564 } 565