1use crate::{
4 compiled_blob::CompiledBlob,
5 memory::{BranchProtection, 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::HashMap;
20use std::ffi::CString;
21use std::io::Write;
22use std::ptr;
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>>,
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>) -> &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>,
176 declarations: ModuleDeclarations,
177 compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
178 compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
179 code_ranges: Vec<(usize, usize, FuncId)>,
180 functions_to_finalize: Vec<FuncId>,
181 data_objects_to_finalize: Vec<DataId>,
182}
183
184impl JITModule {
185 pub unsafe fn free_memory(mut self) {
194 self.memory.free_memory();
195 }
196
197 fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
198 match self.symbols.borrow_mut().entry(name.to_owned()) {
199 std::collections::hash_map::Entry::Occupied(occ) => Some(occ.get().0),
200 std::collections::hash_map::Entry::Vacant(vac) => {
201 let ptr = self
202 .lookup_symbols
203 .iter()
204 .rev() .find_map(|lookup| lookup(name));
206 if let Some(ptr) = ptr {
207 vac.insert(SendWrapper(ptr));
208 }
209 ptr
210 }
211 }
212 }
213
214 fn get_address(&self, name: &ModuleRelocTarget) -> *const u8 {
215 match name {
216 ModuleRelocTarget::User { .. } => {
217 let (name, linkage) = if ModuleDeclarations::is_function(name) {
218 let func_id = FuncId::from_name(name);
219 match &self.compiled_functions[func_id] {
220 Some(compiled) => return compiled.ptr,
221 None => {
222 let decl = self.declarations.get_function_decl(func_id);
223 (&decl.name, decl.linkage)
224 }
225 }
226 } else {
227 let data_id = DataId::from_name(name);
228 match &self.compiled_data_objects[data_id] {
229 Some(compiled) => return compiled.ptr,
230 None => {
231 let decl = self.declarations.get_data_decl(data_id);
232 (&decl.name, decl.linkage)
233 }
234 }
235 };
236 let name = name
237 .as_ref()
238 .expect("anonymous symbol must be defined locally");
239 if let Some(ptr) = self.lookup_symbol(name) {
240 ptr
241 } else if linkage == Linkage::Preemptible {
242 0 as *const u8
243 } else {
244 panic!("can't resolve symbol {name}");
245 }
246 }
247 ModuleRelocTarget::LibCall(libcall) => {
248 let sym = (self.libcall_names)(*libcall);
249 self.lookup_symbol(&sym)
250 .unwrap_or_else(|| panic!("can't resolve libcall {sym}"))
251 }
252 ModuleRelocTarget::FunctionOffset(func_id, offset) => {
253 match &self.compiled_functions[*func_id] {
254 Some(compiled) => return compiled.ptr.wrapping_add(*offset as usize),
255 None => todo!(),
256 }
257 }
258 name => panic!("invalid name {name:?}"),
259 }
260 }
261
262 pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
267 let info = &self.compiled_functions[func_id];
268 assert!(
269 !self.functions_to_finalize.iter().any(|x| *x == func_id),
270 "function not yet finalized"
271 );
272 info.as_ref()
273 .expect("function must be compiled before it can be finalized")
274 .ptr
275 }
276
277 pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
282 let info = &self.compiled_data_objects[data_id];
283 assert!(
284 !self.data_objects_to_finalize.iter().any(|x| *x == data_id),
285 "data object not yet finalized"
286 );
287 let compiled = info
288 .as_ref()
289 .expect("data object must be compiled before it can be finalized");
290
291 (compiled.ptr, compiled.size)
292 }
293
294 fn record_function_for_perf(&self, ptr: *mut u8, size: usize, name: &str) {
295 if cfg!(unix) && ::std::env::var_os("PERF_BUILDID_DIR").is_some() {
301 let mut map_file = ::std::fs::OpenOptions::new()
302 .create(true)
303 .append(true)
304 .open(format!("/tmp/perf-{}.map", ::std::process::id()))
305 .unwrap();
306
307 let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
308 }
309 }
310
311 pub fn finalize_definitions(&mut self) -> ModuleResult<()> {
320 for func in std::mem::take(&mut self.functions_to_finalize) {
321 let decl = self.declarations.get_function_decl(func);
322 assert!(decl.linkage.is_definable());
323 let func = self.compiled_functions[func]
324 .as_ref()
325 .expect("function must be compiled before it can be finalized");
326 func.perform_relocations(|name| self.get_address(name));
327 }
328
329 for data in std::mem::take(&mut self.data_objects_to_finalize) {
330 let decl = self.declarations.get_data_decl(data);
331 assert!(decl.linkage.is_definable());
332 let data = self.compiled_data_objects[data]
333 .as_ref()
334 .expect("data object must be compiled before it can be finalized");
335 data.perform_relocations(|name| self.get_address(name));
336 }
337
338 self.code_ranges
339 .sort_unstable_by_key(|(start, _end, _)| *start);
340
341 let branch_protection = if cfg!(target_arch = "aarch64") && use_bti(&self.isa.isa_flags()) {
343 BranchProtection::BTI
344 } else {
345 BranchProtection::None
346 };
347 self.memory.finalize(branch_protection)?;
348
349 Ok(())
350 }
351
352 pub fn new(builder: JITBuilder) -> Self {
354 assert!(
355 !builder.isa.flags().is_pic(),
356 "cranelift-jit needs is_pic=false"
357 );
358
359 let memory = builder
360 .memory
361 .unwrap_or_else(|| Box::new(SystemMemoryProvider::new()));
362 Self {
363 isa: builder.isa,
364 symbols: RefCell::new(builder.symbols),
365 lookup_symbols: builder.lookup_symbols,
366 libcall_names: builder.libcall_names,
367 memory,
368 declarations: ModuleDeclarations::default(),
369 compiled_functions: SecondaryMap::new(),
370 compiled_data_objects: SecondaryMap::new(),
371 code_ranges: Vec::new(),
372 functions_to_finalize: Vec::new(),
373 data_objects_to_finalize: Vec::new(),
374 }
375 }
376
377 #[cfg(feature = "wasmtime-unwinder")]
381 pub fn lookup_wasmtime_exception_data<'a>(
382 &'a self,
383 pc: usize,
384 ) -> Option<(usize, wasmtime_unwinder::ExceptionTable<'a>)> {
385 let idx = match self
387 .code_ranges
388 .binary_search_by_key(&pc, |(start, _end, _func)| *start)
389 {
390 Ok(exact_start_match) => Some(exact_start_match),
391 Err(least_upper_bound) if least_upper_bound > 0 => {
392 let last_range_before_pc = &self.code_ranges[least_upper_bound - 1];
393 if last_range_before_pc.0 <= pc && pc < last_range_before_pc.1 {
394 Some(least_upper_bound - 1)
395 } else {
396 None
397 }
398 }
399 _ => None,
400 }?;
401
402 let (start, _, func) = self.code_ranges[idx];
403
404 let data = self.compiled_functions[func]
408 .as_ref()
409 .unwrap()
410 .exception_data
411 .as_ref()?;
412 let exception_table = wasmtime_unwinder::ExceptionTable::parse(data).ok()?;
413 Some((start, exception_table))
414 }
415}
416
417impl Module for JITModule {
418 fn isa(&self) -> &dyn TargetIsa {
419 &*self.isa
420 }
421
422 fn declarations(&self) -> &ModuleDeclarations {
423 &self.declarations
424 }
425
426 fn declare_function(
427 &mut self,
428 name: &str,
429 linkage: Linkage,
430 signature: &ir::Signature,
431 ) -> ModuleResult<FuncId> {
432 let (id, _linkage) = self
433 .declarations
434 .declare_function(name, linkage, signature)?;
435 Ok(id)
436 }
437
438 fn declare_anonymous_function(&mut self, signature: &ir::Signature) -> ModuleResult<FuncId> {
439 let id = self.declarations.declare_anonymous_function(signature)?;
440 Ok(id)
441 }
442
443 fn declare_data(
444 &mut self,
445 name: &str,
446 linkage: Linkage,
447 writable: bool,
448 tls: bool,
449 ) -> ModuleResult<DataId> {
450 assert!(!tls, "JIT doesn't yet support TLS");
451 let (id, _linkage) = self
452 .declarations
453 .declare_data(name, linkage, writable, tls)?;
454 Ok(id)
455 }
456
457 fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> {
458 assert!(!tls, "JIT doesn't yet support TLS");
459 let id = self.declarations.declare_anonymous_data(writable, tls)?;
460 Ok(id)
461 }
462
463 fn define_function_with_control_plane(
464 &mut self,
465 id: FuncId,
466 ctx: &mut cranelift_codegen::Context,
467 ctrl_plane: &mut ControlPlane,
468 ) -> ModuleResult<()> {
469 info!("defining function {}: {}", id, ctx.func.display());
470 let decl = self.declarations.get_function_decl(id);
471 if !decl.linkage.is_definable() {
472 return Err(ModuleError::InvalidImportDefinition(
473 decl.linkage_name(id).into_owned(),
474 ));
475 }
476
477 if !self.compiled_functions[id].is_none() {
478 return Err(ModuleError::DuplicateDefinition(
479 decl.linkage_name(id).into_owned(),
480 ));
481 }
482
483 let res = ctx.compile(self.isa(), ctrl_plane)?;
485 let alignment = res.buffer.alignment as u64;
486 let compiled_code = ctx.compiled_code().unwrap();
487
488 let size = compiled_code.code_info().total_size as usize;
489 let align = alignment
490 .max(self.isa.function_alignment().minimum as u64)
491 .max(self.isa.symbol_alignment());
492 let ptr =
493 self.memory
494 .allocate_readexec(size, align)
495 .map_err(|e| ModuleError::Allocation {
496 message: "unable to alloc function",
497 err: e,
498 })?;
499
500 {
501 let mem = unsafe { std::slice::from_raw_parts_mut(ptr, size) };
502 mem.copy_from_slice(compiled_code.code_buffer());
503 }
504
505 let relocs = compiled_code
506 .buffer
507 .relocs()
508 .iter()
509 .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func, id))
510 .collect();
511
512 self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
513 self.compiled_functions[id] = Some(CompiledBlob {
514 ptr,
515 size,
516 relocs,
517 #[cfg(feature = "wasmtime-unwinder")]
518 exception_data: None,
519 });
520
521 let range_start = ptr as usize;
522 let range_end = range_start + size;
523 self.code_ranges.push((range_start, range_end, id));
525
526 #[cfg(feature = "wasmtime-unwinder")]
527 {
528 let mut exception_builder = wasmtime_unwinder::ExceptionTableBuilder::default();
529 exception_builder
530 .add_func(0, compiled_code.buffer.call_sites())
531 .map_err(|_| {
532 ModuleError::Compilation(cranelift_codegen::CodegenError::Unsupported(
533 "Invalid exception data".into(),
534 ))
535 })?;
536 self.compiled_functions[id].as_mut().unwrap().exception_data =
537 Some(exception_builder.to_vec());
538 }
539
540 self.functions_to_finalize.push(id);
541
542 Ok(())
543 }
544
545 fn define_function_bytes(
546 &mut self,
547 id: FuncId,
548 alignment: u64,
549 bytes: &[u8],
550 relocs: &[ModuleReloc],
551 ) -> ModuleResult<()> {
552 info!("defining function {id} with bytes");
553 let decl = self.declarations.get_function_decl(id);
554 if !decl.linkage.is_definable() {
555 return Err(ModuleError::InvalidImportDefinition(
556 decl.linkage_name(id).into_owned(),
557 ));
558 }
559
560 if !self.compiled_functions[id].is_none() {
561 return Err(ModuleError::DuplicateDefinition(
562 decl.linkage_name(id).into_owned(),
563 ));
564 }
565
566 let size = bytes.len();
567 let align = alignment
568 .max(self.isa.function_alignment().minimum as u64)
569 .max(self.isa.symbol_alignment());
570 let ptr =
571 self.memory
572 .allocate_readexec(size, align)
573 .map_err(|e| ModuleError::Allocation {
574 message: "unable to alloc function bytes",
575 err: e,
576 })?;
577
578 unsafe {
579 ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
580 }
581
582 self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
583 self.compiled_functions[id] = Some(CompiledBlob {
584 ptr,
585 size,
586 relocs: relocs.to_owned(),
587 #[cfg(feature = "wasmtime-unwinder")]
588 exception_data: None,
589 });
590
591 self.functions_to_finalize.push(id);
592
593 Ok(())
594 }
595
596 fn define_data(&mut self, id: DataId, data: &DataDescription) -> ModuleResult<()> {
597 let decl = self.declarations.get_data_decl(id);
598 if !decl.linkage.is_definable() {
599 return Err(ModuleError::InvalidImportDefinition(
600 decl.linkage_name(id).into_owned(),
601 ));
602 }
603
604 if !self.compiled_data_objects[id].is_none() {
605 return Err(ModuleError::DuplicateDefinition(
606 decl.linkage_name(id).into_owned(),
607 ));
608 }
609
610 assert!(!decl.tls, "JIT doesn't yet support TLS");
611
612 let &DataDescription {
613 ref init,
614 function_decls: _,
615 data_decls: _,
616 function_relocs: _,
617 data_relocs: _,
618 custom_segment_section: _,
619 align,
620 used: _,
621 } = data;
622
623 let alloc_size = std::cmp::max(init.size(), 1);
628
629 let ptr = if decl.writable {
630 self.memory
631 .allocate_readwrite(alloc_size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
632 .map_err(|e| ModuleError::Allocation {
633 message: "unable to alloc writable data",
634 err: e,
635 })?
636 } else {
637 self.memory
638 .allocate_readonly(alloc_size, align.unwrap_or(READONLY_DATA_ALIGNMENT))
639 .map_err(|e| ModuleError::Allocation {
640 message: "unable to alloc readonly data",
641 err: e,
642 })?
643 };
644
645 if ptr.is_null() {
646 std::alloc::handle_alloc_error(
648 std::alloc::Layout::from_size_align(
649 alloc_size,
650 align.unwrap_or(READONLY_DATA_ALIGNMENT).try_into().unwrap(),
651 )
652 .unwrap(),
653 );
654 }
655
656 match *init {
657 Init::Uninitialized => {
658 panic!("data is not initialized yet");
659 }
660 Init::Zeros { size } => {
661 unsafe { ptr::write_bytes(ptr, 0, size) };
662 }
663 Init::Bytes { ref contents } => {
664 let src = contents.as_ptr();
665 unsafe { ptr::copy_nonoverlapping(src, ptr, contents.len()) };
666 }
667 }
668
669 let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
670 PointerWidth::U16 => panic!(),
671 PointerWidth::U32 => Reloc::Abs4,
672 PointerWidth::U64 => Reloc::Abs8,
673 };
674 let relocs = data.all_relocs(pointer_reloc).collect::<Vec<_>>();
675
676 self.compiled_data_objects[id] = Some(CompiledBlob {
677 ptr,
678 size: init.size(),
679 relocs,
680 #[cfg(feature = "wasmtime-unwinder")]
681 exception_data: None,
682 });
683 self.data_objects_to_finalize.push(id);
684
685 Ok(())
686 }
687
688 fn get_name(&self, name: &str) -> Option<cranelift_module::FuncOrDataId> {
689 self.declarations().get_name(name)
690 }
691
692 fn target_config(&self) -> cranelift_codegen::isa::TargetFrontendConfig {
693 self.isa().frontend_config()
694 }
695
696 fn make_context(&self) -> cranelift_codegen::Context {
697 let mut ctx = cranelift_codegen::Context::new();
698 ctx.func.signature.call_conv = self.isa().default_call_conv();
699 ctx
700 }
701
702 fn clear_context(&self, ctx: &mut cranelift_codegen::Context) {
703 ctx.clear();
704 ctx.func.signature.call_conv = self.isa().default_call_conv();
705 }
706
707 fn make_signature(&self) -> ir::Signature {
708 ir::Signature::new(self.isa().default_call_conv())
709 }
710
711 fn clear_signature(&self, sig: &mut ir::Signature) {
712 sig.clear(self.isa().default_call_conv());
713 }
714}
715
716#[cfg(not(windows))]
717fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
718 let c_str = CString::new(name).unwrap();
719 let c_str_ptr = c_str.as_ptr();
720 let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c_str_ptr) };
721 if sym.is_null() {
722 None
723 } else {
724 Some(sym as *const u8)
725 }
726}
727
728#[cfg(windows)]
729fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
730 use std::os::windows::io::RawHandle;
731 use windows_sys::Win32::Foundation::HMODULE;
732 use windows_sys::Win32::System::LibraryLoader;
733
734 const UCRTBASE: &[u8] = b"ucrtbase.dll\0";
735
736 let c_str = CString::new(name).unwrap();
737 let c_str_ptr = c_str.as_ptr();
738
739 unsafe {
740 let handles = [
741 ptr::null_mut(),
743 LibraryLoader::GetModuleHandleA(UCRTBASE.as_ptr()) as RawHandle,
745 ];
746
747 for handle in &handles {
748 let addr = LibraryLoader::GetProcAddress(*handle as HMODULE, c_str_ptr.cast());
749 match addr {
750 None => continue,
751 Some(addr) => return Some(addr as *const u8),
752 }
753 }
754
755 None
756 }
757}
758
759fn use_bti(isa_flags: &Vec<settings::Value>) -> bool {
760 isa_flags
761 .iter()
762 .find(|&f| f.name == "use_bti")
763 .map_or(false, |f| f.as_bool().unwrap_or(false))
764}