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