dusk_wasmtime/runtime/
code_memory.rs1use anyhow::{anyhow, bail, Context, Result};
4use object::read::{File, Object, ObjectSection};
5use object::ObjectSymbol;
6use std::mem::ManuallyDrop;
7use std::ops::Range;
8use wasmtime_environ::obj;
9use wasmtime_jit_icache_coherence as icache_coherence;
10use wasmtime_runtime::{libcalls, MmapVec, UnwindRegistration};
11
12pub struct CodeMemory {
17 mmap: ManuallyDrop<MmapVec>,
20 unwind_registration: ManuallyDrop<Option<UnwindRegistration>>,
21 published: bool,
22 enable_branch_protection: bool,
23
24 relocations: Vec<(usize, obj::LibCall)>,
25
26 text: Range<usize>,
28 unwind: Range<usize>,
29 trap_data: Range<usize>,
30 wasm_data: Range<usize>,
31 address_map_data: Range<usize>,
32 func_name_data: Range<usize>,
33 info_data: Range<usize>,
34 dwarf: Range<usize>,
35}
36
37impl Drop for CodeMemory {
38 fn drop(&mut self) {
39 unsafe {
41 ManuallyDrop::drop(&mut self.unwind_registration);
42 ManuallyDrop::drop(&mut self.mmap);
43 }
44 }
45}
46
47fn _assert() {
48 fn _assert_send_sync<T: Send + Sync>() {}
49 _assert_send_sync::<CodeMemory>();
50}
51
52impl CodeMemory {
53 pub fn new(mmap: MmapVec) -> Result<Self> {
59 let obj = File::parse(&mmap[..])
60 .with_context(|| "failed to parse internal compilation artifact")?;
61
62 let mut relocations = Vec::new();
63 let mut text = 0..0;
64 let mut unwind = 0..0;
65 let mut enable_branch_protection = None;
66 let mut trap_data = 0..0;
67 let mut wasm_data = 0..0;
68 let mut address_map_data = 0..0;
69 let mut func_name_data = 0..0;
70 let mut info_data = 0..0;
71 let mut dwarf = 0..0;
72 for section in obj.sections() {
73 let data = section.data()?;
74 let name = section.name()?;
75 let range = subslice_range(data, &mmap);
76
77 if section.align() != 0 && data.len() != 0 {
79 if (data.as_ptr() as u64 - mmap.as_ptr() as u64) % section.align() != 0 {
80 bail!(
81 "section `{}` isn't aligned to {:#x}",
82 section.name().unwrap_or("ERROR"),
83 section.align()
84 );
85 }
86 }
87
88 match name {
89 obj::ELF_WASM_BTI => match data.len() {
90 1 => enable_branch_protection = Some(data[0] != 0),
91 _ => bail!("invalid `{name}` section"),
92 },
93 ".text" => {
94 text = range;
95
96 for (offset, reloc) in section.relocations() {
103 assert_eq!(reloc.kind(), object::RelocationKind::Absolute);
104 assert_eq!(reloc.encoding(), object::RelocationEncoding::Generic);
105 assert_eq!(usize::from(reloc.size()), std::mem::size_of::<usize>() * 8);
106 assert_eq!(reloc.addend(), 0);
107 let sym = match reloc.target() {
108 object::RelocationTarget::Symbol(id) => id,
109 other => panic!("unknown relocation target {other:?}"),
110 };
111 let sym = obj.symbol_by_index(sym).unwrap().name().unwrap();
112 let libcall = obj::LibCall::from_str(sym)
113 .unwrap_or_else(|| panic!("unknown symbol relocation: {sym}"));
114
115 let offset = usize::try_from(offset).unwrap();
116 relocations.push((offset, libcall));
117 }
118 }
119 UnwindRegistration::SECTION_NAME => unwind = range,
120 obj::ELF_WASM_DATA => wasm_data = range,
121 obj::ELF_WASMTIME_ADDRMAP => address_map_data = range,
122 obj::ELF_WASMTIME_TRAPS => trap_data = range,
123 obj::ELF_NAME_DATA => func_name_data = range,
124 obj::ELF_WASMTIME_INFO => info_data = range,
125 obj::ELF_WASMTIME_DWARF => dwarf = range,
126
127 _ => log::debug!("ignoring section {name}"),
128 }
129 }
130 Ok(Self {
131 mmap: ManuallyDrop::new(mmap),
132 unwind_registration: ManuallyDrop::new(None),
133 published: false,
134 enable_branch_protection: enable_branch_protection
135 .ok_or_else(|| anyhow!("missing `{}` section", obj::ELF_WASM_BTI))?,
136 text,
137 unwind,
138 trap_data,
139 address_map_data,
140 func_name_data,
141 dwarf,
142 info_data,
143 wasm_data,
144 relocations,
145 })
146 }
147
148 #[inline]
150 pub fn mmap(&self) -> &MmapVec {
151 &self.mmap
152 }
153
154 #[inline]
157 pub fn text(&self) -> &[u8] {
158 &self.mmap[self.text.clone()]
159 }
160
161 #[inline]
163 pub fn dwarf(&self) -> &[u8] {
164 &self.mmap[self.dwarf.clone()]
165 }
166
167 #[inline]
169 pub fn func_name_data(&self) -> &[u8] {
170 &self.mmap[self.func_name_data.clone()]
171 }
172
173 #[inline]
179 pub fn wasm_data(&self) -> &[u8] {
180 &self.mmap[self.wasm_data.clone()]
181 }
182
183 #[inline]
186 pub fn address_map_data(&self) -> &[u8] {
187 &self.mmap[self.address_map_data.clone()]
188 }
189
190 #[inline]
193 pub fn wasmtime_info(&self) -> &[u8] {
194 &self.mmap[self.info_data.clone()]
195 }
196
197 #[inline]
200 pub fn trap_data(&self) -> &[u8] {
201 &self.mmap[self.trap_data.clone()]
202 }
203
204 pub fn publish(&mut self) -> Result<()> {
215 assert!(!self.published);
216 self.published = true;
217
218 if self.text().is_empty() {
219 return Ok(());
220 }
221
222 unsafe {
231 self.apply_relocations()?;
237
238 self.mmap.make_readonly(0..self.mmap.len())?;
246
247 let text = self.text();
248
249 icache_coherence::clear_cache(text.as_ptr().cast(), text.len())
254 .expect("Failed cache clear");
255
256 self.mmap
258 .make_executable(self.text.clone(), self.enable_branch_protection)
259 .context("unable to make memory executable")?;
260
261 icache_coherence::pipeline_flush_mt().expect("Failed pipeline flush");
263
264 self.register_unwind_info()?;
269 }
270
271 Ok(())
272 }
273
274 unsafe fn apply_relocations(&mut self) -> Result<()> {
275 if self.relocations.is_empty() {
276 return Ok(());
277 }
278
279 for (offset, libcall) in self.relocations.iter() {
280 let offset = self.text.start + offset;
281 let libcall = match libcall {
282 obj::LibCall::FloorF32 => libcalls::relocs::floorf32 as usize,
283 obj::LibCall::FloorF64 => libcalls::relocs::floorf64 as usize,
284 obj::LibCall::NearestF32 => libcalls::relocs::nearestf32 as usize,
285 obj::LibCall::NearestF64 => libcalls::relocs::nearestf64 as usize,
286 obj::LibCall::CeilF32 => libcalls::relocs::ceilf32 as usize,
287 obj::LibCall::CeilF64 => libcalls::relocs::ceilf64 as usize,
288 obj::LibCall::TruncF32 => libcalls::relocs::truncf32 as usize,
289 obj::LibCall::TruncF64 => libcalls::relocs::truncf64 as usize,
290 obj::LibCall::FmaF32 => libcalls::relocs::fmaf32 as usize,
291 obj::LibCall::FmaF64 => libcalls::relocs::fmaf64 as usize,
292 #[cfg(target_arch = "x86_64")]
293 obj::LibCall::X86Pshufb => libcalls::relocs::x86_pshufb as usize,
294 #[cfg(not(target_arch = "x86_64"))]
295 obj::LibCall::X86Pshufb => unreachable!(),
296 };
297 self.mmap
298 .as_mut_ptr()
299 .add(offset)
300 .cast::<usize>()
301 .write_unaligned(libcall);
302 }
303 Ok(())
304 }
305
306 unsafe fn register_unwind_info(&mut self) -> Result<()> {
307 if self.unwind.len() == 0 {
308 return Ok(());
309 }
310 let text = self.text();
311 let unwind_info = &self.mmap[self.unwind.clone()];
312 let registration =
313 UnwindRegistration::new(text.as_ptr(), unwind_info.as_ptr(), unwind_info.len())
314 .context("failed to create unwind info registration")?;
315 *self.unwind_registration = Some(registration);
316 Ok(())
317 }
318}
319
320fn subslice_range(inner: &[u8], outer: &[u8]) -> Range<usize> {
326 if inner.len() == 0 {
327 return 0..0;
328 }
329
330 assert!(outer.as_ptr() <= inner.as_ptr());
331 assert!((&inner[inner.len() - 1] as *const _) <= (&outer[outer.len() - 1] as *const _));
332
333 let start = inner.as_ptr() as usize - outer.as_ptr() as usize;
334 start..start + inner.len()
335}