ureeves_wasmtime/runtime/
code_memory.rs1use crate::prelude::*;
4use crate::runtime::vm::{libcalls, MmapVec, UnwindRegistration};
5use core::ops::Range;
6use object::endian::NativeEndian;
7use object::read::{elf::ElfFile64, Object, ObjectSection};
8use object::ObjectSymbol;
9use wasmtime_environ::{lookup_trap_code, obj, Trap};
10use wasmtime_jit_icache_coherence as icache_coherence;
11
12pub struct CodeMemory {
17 mmap: MmapVec,
18 unwind_registration: Option<UnwindRegistration>,
19 #[cfg(feature = "debug-builtins")]
20 debug_registration: Option<crate::runtime::vm::GdbJitImageRegistration>,
21 published: bool,
22 enable_branch_protection: bool,
23 #[cfg(feature = "debug-builtins")]
24 has_native_debug_info: bool,
25
26 relocations: Vec<(usize, obj::LibCall)>,
27
28 text: Range<usize>,
30 unwind: Range<usize>,
31 trap_data: Range<usize>,
32 wasm_data: Range<usize>,
33 address_map_data: Range<usize>,
34 func_name_data: Range<usize>,
35 info_data: Range<usize>,
36 wasm_dwarf: Range<usize>,
37}
38
39impl Drop for CodeMemory {
40 fn drop(&mut self) {
41 let _ = self.unwind_registration.take();
43 #[cfg(feature = "debug-builtins")]
44 let _ = self.debug_registration.take();
45 }
46}
47
48fn _assert() {
49 fn _assert_send_sync<T: Send + Sync>() {}
50 _assert_send_sync::<CodeMemory>();
51}
52
53impl CodeMemory {
54 pub fn new(mmap: MmapVec) -> Result<Self> {
60 let obj = ElfFile64::<NativeEndian>::parse(&mmap[..])
61 .err2anyhow()
62 .with_context(|| "failed to parse internal compilation artifact")?;
63
64 let mut relocations = Vec::new();
65 let mut text = 0..0;
66 let mut unwind = 0..0;
67 let mut enable_branch_protection = None;
68 #[cfg(feature = "debug-builtins")]
69 let mut has_native_debug_info = false;
70 let mut trap_data = 0..0;
71 let mut wasm_data = 0..0;
72 let mut address_map_data = 0..0;
73 let mut func_name_data = 0..0;
74 let mut info_data = 0..0;
75 let mut wasm_dwarf = 0..0;
76 for section in obj.sections() {
77 let data = section.data().err2anyhow()?;
78 let name = section.name().err2anyhow()?;
79 let range = subslice_range(data, &mmap);
80
81 if section.align() != 0 && data.len() != 0 {
83 if (data.as_ptr() as u64 - mmap.as_ptr() as u64) % section.align() != 0 {
84 bail!(
85 "section `{}` isn't aligned to {:#x}",
86 section.name().unwrap_or("ERROR"),
87 section.align()
88 );
89 }
90 }
91
92 match name {
93 obj::ELF_WASM_BTI => match data.len() {
94 1 => enable_branch_protection = Some(data[0] != 0),
95 _ => bail!("invalid `{name}` section"),
96 },
97 ".text" => {
98 text = range;
99
100 for (offset, reloc) in section.relocations() {
107 assert_eq!(reloc.kind(), object::RelocationKind::Absolute);
108 assert_eq!(reloc.encoding(), object::RelocationEncoding::Generic);
109 assert_eq!(usize::from(reloc.size()), core::mem::size_of::<usize>() * 8);
110 assert_eq!(reloc.addend(), 0);
111 let sym = match reloc.target() {
112 object::RelocationTarget::Symbol(id) => id,
113 other => panic!("unknown relocation target {other:?}"),
114 };
115 let sym = obj.symbol_by_index(sym).unwrap().name().unwrap();
116 let libcall = obj::LibCall::from_str(sym)
117 .unwrap_or_else(|| panic!("unknown symbol relocation: {sym}"));
118
119 let offset = usize::try_from(offset).unwrap();
120 relocations.push((offset, libcall));
121 }
122 }
123 UnwindRegistration::SECTION_NAME => unwind = range,
124 obj::ELF_WASM_DATA => wasm_data = range,
125 obj::ELF_WASMTIME_ADDRMAP => address_map_data = range,
126 obj::ELF_WASMTIME_TRAPS => trap_data = range,
127 obj::ELF_NAME_DATA => func_name_data = range,
128 obj::ELF_WASMTIME_INFO => info_data = range,
129 obj::ELF_WASMTIME_DWARF => wasm_dwarf = range,
130 #[cfg(feature = "debug-builtins")]
131 ".debug_info" => has_native_debug_info = true,
132
133 _ => log::debug!("ignoring section {name}"),
134 }
135 }
136 Ok(Self {
137 mmap,
138 unwind_registration: None,
139 #[cfg(feature = "debug-builtins")]
140 debug_registration: None,
141 published: false,
142 enable_branch_protection: enable_branch_protection
143 .ok_or_else(|| anyhow!("missing `{}` section", obj::ELF_WASM_BTI))?,
144 #[cfg(feature = "debug-builtins")]
145 has_native_debug_info,
146 text,
147 unwind,
148 trap_data,
149 address_map_data,
150 func_name_data,
151 wasm_dwarf,
152 info_data,
153 wasm_data,
154 relocations,
155 })
156 }
157
158 #[inline]
160 pub fn mmap(&self) -> &MmapVec {
161 &self.mmap
162 }
163
164 #[inline]
167 pub fn text(&self) -> &[u8] {
168 &self.mmap[self.text.clone()]
169 }
170
171 #[inline]
173 pub fn wasm_dwarf(&self) -> &[u8] {
174 &self.mmap[self.wasm_dwarf.clone()]
175 }
176
177 #[inline]
179 pub fn func_name_data(&self) -> &[u8] {
180 &self.mmap[self.func_name_data.clone()]
181 }
182
183 #[inline]
189 pub fn wasm_data(&self) -> &[u8] {
190 &self.mmap[self.wasm_data.clone()]
191 }
192
193 #[inline]
196 pub fn address_map_data(&self) -> &[u8] {
197 &self.mmap[self.address_map_data.clone()]
198 }
199
200 #[inline]
203 pub fn wasmtime_info(&self) -> &[u8] {
204 &self.mmap[self.info_data.clone()]
205 }
206
207 #[inline]
210 pub fn trap_data(&self) -> &[u8] {
211 &self.mmap[self.trap_data.clone()]
212 }
213
214 pub fn publish(&mut self) -> Result<()> {
226 assert!(!self.published);
227 self.published = true;
228
229 if self.text().is_empty() {
230 return Ok(());
231 }
232
233 unsafe {
242 self.apply_relocations()?;
248
249 self.mmap.make_readonly(0..self.mmap.len())?;
257
258 let text = self.text();
259
260 icache_coherence::clear_cache(text.as_ptr().cast(), text.len())
265 .expect("Failed cache clear");
266
267 self.mmap
269 .make_executable(self.text.clone(), self.enable_branch_protection)
270 .context("unable to make memory executable")?;
271
272 icache_coherence::pipeline_flush_mt().expect("Failed pipeline flush");
274
275 self.register_unwind_info()?;
280
281 #[cfg(feature = "debug-builtins")]
282 self.register_debug_image()?;
283 }
284
285 Ok(())
286 }
287
288 unsafe fn apply_relocations(&mut self) -> Result<()> {
289 if self.relocations.is_empty() {
290 return Ok(());
291 }
292
293 for (offset, libcall) in self.relocations.iter() {
294 let offset = self.text.start + offset;
295 let libcall = match libcall {
296 obj::LibCall::FloorF32 => libcalls::relocs::floorf32 as usize,
297 obj::LibCall::FloorF64 => libcalls::relocs::floorf64 as usize,
298 obj::LibCall::NearestF32 => libcalls::relocs::nearestf32 as usize,
299 obj::LibCall::NearestF64 => libcalls::relocs::nearestf64 as usize,
300 obj::LibCall::CeilF32 => libcalls::relocs::ceilf32 as usize,
301 obj::LibCall::CeilF64 => libcalls::relocs::ceilf64 as usize,
302 obj::LibCall::TruncF32 => libcalls::relocs::truncf32 as usize,
303 obj::LibCall::TruncF64 => libcalls::relocs::truncf64 as usize,
304 obj::LibCall::FmaF32 => libcalls::relocs::fmaf32 as usize,
305 obj::LibCall::FmaF64 => libcalls::relocs::fmaf64 as usize,
306 #[cfg(target_arch = "x86_64")]
307 obj::LibCall::X86Pshufb => libcalls::relocs::x86_pshufb as usize,
308 #[cfg(not(target_arch = "x86_64"))]
309 obj::LibCall::X86Pshufb => unreachable!(),
310 };
311 self.mmap
312 .as_mut_ptr()
313 .add(offset)
314 .cast::<usize>()
315 .write_unaligned(libcall);
316 }
317 Ok(())
318 }
319
320 unsafe fn register_unwind_info(&mut self) -> Result<()> {
321 if self.unwind.len() == 0 {
322 return Ok(());
323 }
324 let text = self.text();
325 let unwind_info = &self.mmap[self.unwind.clone()];
326 let registration =
327 UnwindRegistration::new(text.as_ptr(), unwind_info.as_ptr(), unwind_info.len())
328 .context("failed to create unwind info registration")?;
329 self.unwind_registration = Some(registration);
330 Ok(())
331 }
332
333 #[cfg(feature = "debug-builtins")]
334 fn register_debug_image(&mut self) -> Result<()> {
335 if !self.has_native_debug_info {
336 return Ok(());
337 }
338
339 let image = self.mmap().to_vec();
343 let text: &[u8] = self.text();
344 let bytes = crate::debug::create_gdbjit_image(image, (text.as_ptr(), text.len()))?;
345 let reg = crate::runtime::vm::GdbJitImageRegistration::register(bytes);
346 self.debug_registration = Some(reg);
347 Ok(())
348 }
349
350 pub fn lookup_trap_code(&self, text_offset: usize) -> Option<Trap> {
353 lookup_trap_code(self.trap_data(), text_offset)
354 }
355}
356
357fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> {
363 if inner.len() == 0 {
364 return 0..0;
365 }
366
367 assert!(outer.as_ptr() <= inner.as_ptr());
368 assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _));
369
370 let start = inner.as_ptr() as usize - outer.as_ptr() as usize;
371 start..start + inner.len()
372}