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