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