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