wasmtime/runtime/
code_memory.rs

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