1use crate::{
4 compiled_blob::CompiledBlob,
5 memory::{BranchProtection, JITMemoryKind, JITMemoryProvider, SystemMemoryProvider},
6};
7use cranelift_codegen::binemit::Reloc;
8use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
9use cranelift_codegen::settings::Configurable;
10use cranelift_codegen::{ir, settings};
11use cranelift_control::ControlPlane;
12use cranelift_entity::SecondaryMap;
13use cranelift_module::{
14 DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleDeclarations, ModuleError,
15 ModuleReloc, ModuleRelocTarget, ModuleResult,
16};
17use log::info;
18use std::cell::RefCell;
19use std::collections::BTreeMap;
20use std::collections::HashMap;
21use std::ffi::CString;
22use std::io::Write;
23use target_lexicon::PointerWidth;
24
25const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
26const READONLY_DATA_ALIGNMENT: u64 = 0x1;
27
28pub struct JITBuilder {
30 isa: OwnedTargetIsa,
31 symbols: HashMap<String, SendWrapper<*const u8>>,
32 lookup_symbols: Vec<Box<dyn Fn(&str) -> Option<*const u8> + Send>>,
33 libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
34 memory: Option<Box<dyn JITMemoryProvider + Send>>,
35}
36
37impl JITBuilder {
38 pub fn new(
45 libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
46 ) -> ModuleResult<Self> {
47 Self::with_flags(&[], libcall_names)
48 }
49
50 pub fn with_flags(
57 flags: &[(&str, &str)],
58 libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
59 ) -> ModuleResult<Self> {
60 let mut flag_builder = settings::builder();
61 for (name, value) in flags {
62 flag_builder.set(name, value)?;
63 }
64
65 flag_builder.set("use_colocated_libcalls", "false").unwrap();
69 flag_builder.set("is_pic", "false").unwrap();
70 let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
71 panic!("host machine is not supported: {msg}");
72 });
73 let isa = isa_builder.finish(settings::Flags::new(flag_builder))?;
74 Ok(Self::with_isa(isa, libcall_names))
75 }
76
77 pub fn with_isa(
88 isa: OwnedTargetIsa,
89 libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
90 ) -> Self {
91 let symbols = HashMap::new();
92 let lookup_symbols = vec![Box::new(lookup_with_dlsym) as Box<_>];
93 Self {
94 isa,
95 symbols,
96 lookup_symbols,
97 libcall_names,
98 memory: None,
99 }
100 }
101
102 pub fn symbol<K>(&mut self, name: K, ptr: *const u8) -> &mut Self
117 where
118 K: Into<String>,
119 {
120 self.symbols.insert(name.into(), SendWrapper(ptr));
121 self
122 }
123
124 pub fn symbols<It, K>(&mut self, symbols: It) -> &mut Self
128 where
129 It: IntoIterator<Item = (K, *const u8)>,
130 K: Into<String>,
131 {
132 for (name, ptr) in symbols {
133 self.symbols.insert(name.into(), SendWrapper(ptr));
134 }
135 self
136 }
137
138 pub fn symbol_lookup_fn(
143 &mut self,
144 symbol_lookup_fn: Box<dyn Fn(&str) -> Option<*const u8> + Send>,
145 ) -> &mut Self {
146 self.lookup_symbols.push(symbol_lookup_fn);
147 self
148 }
149
150 pub fn memory_provider(&mut self, provider: Box<dyn JITMemoryProvider + Send>) -> &mut Self {
154 self.memory = Some(provider);
155 self
156 }
157}
158
159#[derive(Copy, Clone)]
163struct SendWrapper<T>(T);
164unsafe impl<T> Send for SendWrapper<T> {}
165
166pub struct JITModule {
171 isa: OwnedTargetIsa,
172 symbols: RefCell<HashMap<String, SendWrapper<*const u8>>>,
173 lookup_symbols: Vec<Box<dyn Fn(&str) -> Option<*const u8> + Send>>,
174 libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
175 memory: Box<dyn JITMemoryProvider + Send>,
176 declarations: ModuleDeclarations,
177 compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
178 compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
179 code_ranges: BTreeMap<usize, (usize, FuncId)>,
182 functions_to_finalize: Vec<FuncId>,
183 data_objects_to_finalize: Vec<DataId>,
184}
185
186impl JITModule {
187 pub unsafe fn free_memory(mut self) {
196 self.memory.free_memory();
197 }
198
199 fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
200 match self.symbols.borrow_mut().entry(name.to_owned()) {
201 std::collections::hash_map::Entry::Occupied(occ) => Some(occ.get().0),
202 std::collections::hash_map::Entry::Vacant(vac) => {
203 let ptr = self
204 .lookup_symbols
205 .iter()
206 .rev() .find_map(|lookup| lookup(name));
208 if let Some(ptr) = ptr {
209 vac.insert(SendWrapper(ptr));
210 }
211 ptr
212 }
213 }
214 }
215
216 pub fn get_address(&self, name: &ModuleRelocTarget) -> *const u8 {
223 match name {
224 ModuleRelocTarget::User { .. } => {
225 let (name, linkage) = if ModuleDeclarations::is_function(name) {
226 let func_id = FuncId::from_name(name);
227 match &self.compiled_functions[func_id] {
228 Some(compiled) => return compiled.ptr(),
229 None => {
230 let decl = self.declarations.get_function_decl(func_id);
231 (&decl.name, decl.linkage)
232 }
233 }
234 } else {
235 let data_id = DataId::from_name(name);
236 match &self.compiled_data_objects[data_id] {
237 Some(compiled) => return compiled.ptr(),
238 None => {
239 let decl = self.declarations.get_data_decl(data_id);
240 (&decl.name, decl.linkage)
241 }
242 }
243 };
244 let name = name
245 .as_ref()
246 .expect("anonymous symbol must be defined locally");
247 if let Some(ptr) = self.lookup_symbol(name) {
248 ptr
249 } else if linkage == Linkage::Preemptible {
250 0 as *const u8
251 } else {
252 panic!("can't resolve symbol {name}");
253 }
254 }
255 ModuleRelocTarget::LibCall(libcall) => {
256 let sym = (self.libcall_names)(*libcall);
257 self.lookup_symbol(&sym)
258 .unwrap_or_else(|| panic!("can't resolve libcall {sym}"))
259 }
260 ModuleRelocTarget::FunctionOffset(func_id, offset) => {
261 match &self.compiled_functions[*func_id] {
262 Some(compiled) => return compiled.ptr().wrapping_add(*offset as usize),
263 None => todo!(),
264 }
265 }
266 name => panic!("invalid name {name:?}"),
267 }
268 }
269
270 pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
275 let info = &self.compiled_functions[func_id];
276 assert!(
277 !self.functions_to_finalize.iter().any(|x| *x == func_id),
278 "function not yet finalized"
279 );
280 info.as_ref()
281 .expect("function must be compiled before it can be finalized")
282 .ptr()
283 }
284
285 pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
290 let info = &self.compiled_data_objects[data_id];
291 assert!(
292 !self.data_objects_to_finalize.iter().any(|x| *x == data_id),
293 "data object not yet finalized"
294 );
295 let compiled = info
296 .as_ref()
297 .expect("data object must be compiled before it can be finalized");
298
299 (compiled.ptr(), compiled.size())
300 }
301
302 fn record_function_for_perf(&self, ptr: *const u8, size: usize, name: &str) {
303 if cfg!(unix) && ::std::env::var_os("PERF_BUILDID_DIR").is_some() {
309 let mut map_file = ::std::fs::OpenOptions::new()
310 .create(true)
311 .append(true)
312 .open(format!("/tmp/perf-{}.map", ::std::process::id()))
313 .unwrap();
314
315 let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
316 }
317 }
318
319 pub fn finalize_definitions(&mut self) -> ModuleResult<()> {
328 for func in std::mem::take(&mut self.functions_to_finalize) {
329 let decl = self.declarations.get_function_decl(func);
330 assert!(decl.linkage.is_definable());
331 let func = self.compiled_functions[func]
332 .as_ref()
333 .expect("function must be compiled before it can be finalized");
334 func.perform_relocations(|name| self.get_address(name));
335 }
336
337 for data in std::mem::take(&mut self.data_objects_to_finalize) {
338 let decl = self.declarations.get_data_decl(data);
339 assert!(decl.linkage.is_definable());
340 let data = self.compiled_data_objects[data]
341 .as_ref()
342 .expect("data object must be compiled before it can be finalized");
343 data.perform_relocations(|name| self.get_address(name));
344 }
345
346 let branch_protection = if cfg!(target_arch = "aarch64") && use_bti(&self.isa.isa_flags()) {
348 BranchProtection::BTI
349 } else {
350 BranchProtection::None
351 };
352 self.memory.finalize(branch_protection)?;
353
354 Ok(())
355 }
356
357 pub fn new(builder: JITBuilder) -> Self {
359 assert!(
360 !builder.isa.flags().is_pic(),
361 "cranelift-jit needs is_pic=false"
362 );
363
364 let memory = builder
365 .memory
366 .unwrap_or_else(|| Box::new(SystemMemoryProvider::new()));
367 Self {
368 isa: builder.isa,
369 symbols: RefCell::new(builder.symbols),
370 lookup_symbols: builder.lookup_symbols,
371 libcall_names: builder.libcall_names,
372 memory,
373 declarations: ModuleDeclarations::default(),
374 compiled_functions: SecondaryMap::new(),
375 compiled_data_objects: SecondaryMap::new(),
376 code_ranges: BTreeMap::new(),
377 functions_to_finalize: Vec::new(),
378 data_objects_to_finalize: Vec::new(),
379 }
380 }
381
382 #[cfg(feature = "wasmtime-unwinder")]
386 pub fn lookup_wasmtime_exception_data<'a>(
387 &'a self,
388 pc: usize,
389 ) -> Option<(usize, wasmtime_unwinder::ExceptionTable<'a>)> {
390 let (&start, &(end, func)) = self.code_ranges.range(..=pc).next_back()?;
391 if pc >= end {
392 return None;
393 }
394
395 let data = self.compiled_functions[func]
399 .as_ref()
400 .unwrap()
401 .wasmtime_exception_data()?;
402 let exception_table = wasmtime_unwinder::ExceptionTable::parse(data).ok()?;
403 Some((start, exception_table))
404 }
405}
406
407impl Module for JITModule {
408 fn isa(&self) -> &dyn TargetIsa {
409 &*self.isa
410 }
411
412 fn declarations(&self) -> &ModuleDeclarations {
413 &self.declarations
414 }
415
416 fn declare_function(
417 &mut self,
418 name: &str,
419 linkage: Linkage,
420 signature: &ir::Signature,
421 ) -> ModuleResult<FuncId> {
422 let (id, _linkage) = self
423 .declarations
424 .declare_function(name, linkage, signature)?;
425 Ok(id)
426 }
427
428 fn declare_anonymous_function(&mut self, signature: &ir::Signature) -> ModuleResult<FuncId> {
429 let id = self.declarations.declare_anonymous_function(signature)?;
430 Ok(id)
431 }
432
433 fn declare_data(
434 &mut self,
435 name: &str,
436 linkage: Linkage,
437 writable: bool,
438 tls: bool,
439 ) -> ModuleResult<DataId> {
440 assert!(!tls, "JIT doesn't yet support TLS");
441 let (id, _linkage) = self
442 .declarations
443 .declare_data(name, linkage, writable, tls)?;
444 Ok(id)
445 }
446
447 fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> {
448 assert!(!tls, "JIT doesn't yet support TLS");
449 let id = self.declarations.declare_anonymous_data(writable, tls)?;
450 Ok(id)
451 }
452
453 fn define_function_with_control_plane(
454 &mut self,
455 id: FuncId,
456 ctx: &mut cranelift_codegen::Context,
457 ctrl_plane: &mut ControlPlane,
458 ) -> ModuleResult<()> {
459 info!("defining function {}: {}", id, ctx.func.display());
460 let decl = self.declarations.get_function_decl(id);
461 if !decl.linkage.is_definable() {
462 return Err(ModuleError::InvalidImportDefinition(
463 decl.linkage_name(id).into_owned(),
464 ));
465 }
466
467 if !self.compiled_functions[id].is_none() {
468 return Err(ModuleError::DuplicateDefinition(
469 decl.linkage_name(id).into_owned(),
470 ));
471 }
472
473 let res = ctx.compile(self.isa(), ctrl_plane)?;
475 let alignment = res.buffer.alignment as u64;
476 let compiled_code = ctx.compiled_code().unwrap();
477
478 let align = alignment
479 .max(self.isa.function_alignment().minimum as u64)
480 .max(self.isa.symbol_alignment());
481
482 let relocs = compiled_code
483 .buffer
484 .relocs()
485 .iter()
486 .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func, id))
487 .collect();
488
489 #[cfg(feature = "wasmtime-unwinder")]
490 let wasmtime_exception_data = {
491 let mut exception_builder = wasmtime_unwinder::ExceptionTableBuilder::default();
492 exception_builder
493 .add_func(0, compiled_code.buffer.call_sites())
494 .map_err(|_| {
495 ModuleError::Compilation(cranelift_codegen::CodegenError::Unsupported(
496 "Invalid exception data".into(),
497 ))
498 })?;
499 Some(exception_builder.to_vec())
500 };
501
502 let blob = self.compiled_functions[id].insert(CompiledBlob::new(
503 &mut *self.memory,
504 compiled_code.code_buffer(),
505 align,
506 relocs,
507 #[cfg(feature = "wasmtime-unwinder")]
508 wasmtime_exception_data,
509 JITMemoryKind::Executable,
510 )?);
511 let (ptr, size) = (blob.ptr(), blob.size());
512 self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
513
514 let range_start = ptr.addr();
515 let range_end = range_start + size;
516 self.code_ranges.insert(range_start, (range_end, id));
517
518 self.functions_to_finalize.push(id);
519
520 Ok(())
521 }
522
523 fn define_function_bytes(
524 &mut self,
525 id: FuncId,
526 alignment: u64,
527 bytes: &[u8],
528 relocs: &[ModuleReloc],
529 ) -> ModuleResult<()> {
530 info!("defining function {id} with bytes");
531 let decl = self.declarations.get_function_decl(id);
532 if !decl.linkage.is_definable() {
533 return Err(ModuleError::InvalidImportDefinition(
534 decl.linkage_name(id).into_owned(),
535 ));
536 }
537
538 if !self.compiled_functions[id].is_none() {
539 return Err(ModuleError::DuplicateDefinition(
540 decl.linkage_name(id).into_owned(),
541 ));
542 }
543
544 let align = alignment
545 .max(self.isa.function_alignment().minimum as u64)
546 .max(self.isa.symbol_alignment());
547
548 let blob = self.compiled_functions[id].insert(CompiledBlob::new(
549 &mut *self.memory,
550 bytes,
551 align,
552 relocs.to_owned(),
553 #[cfg(feature = "wasmtime-unwinder")]
554 None,
555 JITMemoryKind::Executable,
556 )?);
557 let (ptr, size) = (blob.ptr(), blob.size());
558 self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
559
560 self.functions_to_finalize.push(id);
561
562 Ok(())
563 }
564
565 fn define_data(&mut self, id: DataId, data: &DataDescription) -> ModuleResult<()> {
566 let decl = self.declarations.get_data_decl(id);
567 if !decl.linkage.is_definable() {
568 return Err(ModuleError::InvalidImportDefinition(
569 decl.linkage_name(id).into_owned(),
570 ));
571 }
572
573 if !self.compiled_data_objects[id].is_none() {
574 return Err(ModuleError::DuplicateDefinition(
575 decl.linkage_name(id).into_owned(),
576 ));
577 }
578
579 assert!(!decl.tls, "JIT doesn't yet support TLS");
580
581 let &DataDescription {
582 ref init,
583 function_decls: _,
584 data_decls: _,
585 function_relocs: _,
586 data_relocs: _,
587 custom_segment_section: _,
588 align,
589 used: _,
590 } = data;
591
592 let (align, kind) = if decl.writable {
593 (
594 align.unwrap_or(WRITABLE_DATA_ALIGNMENT),
595 JITMemoryKind::Writable,
596 )
597 } else {
598 (
599 align.unwrap_or(READONLY_DATA_ALIGNMENT),
600 JITMemoryKind::ReadOnly,
601 )
602 };
603
604 let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
605 PointerWidth::U16 => panic!(),
606 PointerWidth::U32 => Reloc::Abs4,
607 PointerWidth::U64 => Reloc::Abs8,
608 };
609 let relocs = data.all_relocs(pointer_reloc).collect::<Vec<_>>();
610
611 self.compiled_data_objects[id] = Some(match *init {
612 Init::Uninitialized => {
613 panic!("data is not initialized yet");
614 }
615 Init::Zeros { size } => CompiledBlob::new_zeroed(
616 &mut *self.memory,
617 size.max(1),
618 align,
619 relocs,
620 #[cfg(feature = "wasmtime-unwinder")]
621 None,
622 kind,
623 )?,
624 Init::Bytes { ref contents } => CompiledBlob::new(
625 &mut *self.memory,
626 if contents.is_empty() {
627 &[0]
633 } else {
634 &contents[..]
635 },
636 align,
637 relocs,
638 #[cfg(feature = "wasmtime-unwinder")]
639 None,
640 kind,
641 )?,
642 });
643
644 self.data_objects_to_finalize.push(id);
645
646 Ok(())
647 }
648}
649
650#[cfg(not(windows))]
651fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
652 let c_str = CString::new(name).unwrap();
653 let c_str_ptr = c_str.as_ptr();
654 let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c_str_ptr) };
655 if sym.is_null() {
656 None
657 } else {
658 Some(sym as *const u8)
659 }
660}
661
662#[cfg(windows)]
663fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
664 use std::os::windows::io::RawHandle;
665 use std::ptr;
666 use windows_sys::Win32::Foundation::HMODULE;
667 use windows_sys::Win32::System::LibraryLoader;
668
669 const UCRTBASE: &[u8] = b"ucrtbase.dll\0";
670
671 let c_str = CString::new(name).unwrap();
672 let c_str_ptr = c_str.as_ptr();
673
674 unsafe {
675 let handles = [
676 ptr::null_mut(),
678 LibraryLoader::GetModuleHandleA(UCRTBASE.as_ptr()) as RawHandle,
680 ];
681
682 for handle in &handles {
683 let addr = LibraryLoader::GetProcAddress(*handle as HMODULE, c_str_ptr.cast());
684 match addr {
685 None => continue,
686 Some(addr) => return Some(addr as *const u8),
687 }
688 }
689
690 None
691 }
692}
693
694fn use_bti(isa_flags: &Vec<settings::Value>) -> bool {
695 isa_flags
696 .iter()
697 .find(|&f| f.name == "use_bti")
698 .map_or(false, |f| f.as_bool().unwrap_or(false))
699}
700
701const _ASSERT_JIT_MODULE_IS_SEND: () = {
702 const fn assert_is_send<T: Send>() {}
703 assert_is_send::<JITModule>();
704};