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}