wasmtime/runtime/code_memory.rs
1//! Memory management for executable code.
2
3use crate::Engine;
4use crate::prelude::*;
5use crate::runtime::vm::MmapVec;
6use alloc::sync::Arc;
7use core::ops::Range;
8use object::SectionFlags;
9use object::endian::Endianness;
10use object::read::{Object, ObjectSection, elf::ElfFile64};
11use wasmtime_environ::{Trap, lookup_trap_code, obj};
12use 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.
18pub 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
45impl 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
65fn _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.
72pub 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
107impl 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 /// Get the raw address range of this CodeMemory.
446 pub(crate) fn raw_addr_range(&self) -> Range<usize> {
447 let start = self.text().as_ptr().addr();
448 let end = start + self.text().len();
449 start..end
450 }
451
452 /// Create a "deep clone": a separate CodeMemory for the same code
453 /// that can be patched or mutated independently. Also returns a
454 /// "metadata and location" handle that can be registered with the
455 /// global module registry and used for trap metadata lookups.
456 #[cfg(feature = "debug")]
457 pub(crate) fn deep_clone(self: &Arc<Self>, engine: &Engine) -> Result<CodeMemory> {
458 let mmap = self.mmap.deep_clone()?;
459 Self::new(engine, mmap)
460 }
461}
462
463/// Returns the range of `inner` within `outer`, such that `outer[range]` is the
464/// same as `inner`.
465///
466/// This method requires that `inner` is a sub-slice of `outer`, and if that
467/// isn't true then this method will panic.
468fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> {
469 if inner.len() == 0 {
470 return 0..0;
471 }
472
473 assert!(outer.as_ptr() <= inner.as_ptr());
474 assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _));
475
476 let start = inner.as_ptr() as usize - outer.as_ptr() as usize;
477 start..start + inner.len()
478}