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    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
46impl 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
70fn _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.
77pub 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
112impl 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.
554fn 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}