1use crate::memory::Memory;
4use cranelift_codegen::binemit::{
5 Addend, CodeInfo, CodeOffset, Reloc, RelocSink, StackMap, StackMapSink, TrapSink,
6};
7use cranelift_codegen::isa::TargetIsa;
8use cranelift_codegen::settings::Configurable;
9use cranelift_codegen::{self, ir, settings};
10use cranelift_entity::SecondaryMap;
11use cranelift_module::{
12 DataContext, DataDescription, DataId, FuncId, FuncOrDataId, Init, Linkage, Module,
13 ModuleCompiledFunction, ModuleDeclarations, ModuleError, ModuleResult,
14};
15use cranelift_native;
16#[cfg(not(windows))]
17use libc;
18use log::info;
19use std::collections::HashMap;
20use std::convert::TryInto;
21use std::ffi::CString;
22use std::io::Write;
23use std::ptr;
24use target_lexicon::PointerWidth;
25#[cfg(windows)]
26use winapi;
27
28const EXECUTABLE_DATA_ALIGNMENT: u64 = 0x10;
29const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
30const READONLY_DATA_ALIGNMENT: u64 = 0x1;
31
32pub struct SimpleJITBuilder {
34 isa: Box<dyn TargetIsa>,
35 symbols: HashMap<String, *const u8>,
36 libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
37}
38
39impl SimpleJITBuilder {
40 pub fn new(libcall_names: Box<dyn Fn(ir::LibCall) -> String>) -> Self {
47 let mut flag_builder = settings::builder();
48 flag_builder.set("use_colocated_libcalls", "false").unwrap();
52 let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
53 panic!("host machine is not supported: {}", msg);
54 });
55 let isa = isa_builder.finish(settings::Flags::new(flag_builder));
56 Self::with_isa(isa, libcall_names)
57 }
58
59 pub fn with_isa(
72 isa: Box<dyn TargetIsa>,
73 libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
74 ) -> Self {
75 debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code");
76 let symbols = HashMap::new();
77 Self {
78 isa,
79 symbols,
80 libcall_names,
81 }
82 }
83
84 pub fn symbol<K>(&mut self, name: K, ptr: *const u8) -> &Self
99 where
100 K: Into<String>,
101 {
102 self.symbols.insert(name.into(), ptr);
103 self
104 }
105
106 pub fn symbols<It, K>(&mut self, symbols: It) -> &Self
110 where
111 It: IntoIterator<Item = (K, *const u8)>,
112 K: Into<String>,
113 {
114 for (name, ptr) in symbols {
115 self.symbols.insert(name.into(), ptr);
116 }
117 self
118 }
119}
120
121pub struct SimpleJITModule {
126 isa: Box<dyn TargetIsa>,
127 symbols: HashMap<String, *const u8>,
128 libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
129 memory: MemoryHandle,
130 declarations: ModuleDeclarations,
131 functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
132 data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
133 functions_to_finalize: Vec<FuncId>,
134 data_objects_to_finalize: Vec<DataId>,
135}
136
137#[derive(Clone)]
139struct RelocRecord {
140 offset: CodeOffset,
141 reloc: Reloc,
142 name: ir::ExternalName,
143 addend: Addend,
144}
145
146struct StackMapRecord {
147 #[allow(dead_code)]
148 offset: CodeOffset,
149 #[allow(dead_code)]
150 stack_map: StackMap,
151}
152
153#[derive(Clone)]
154struct CompiledBlob {
155 ptr: *mut u8,
156 size: usize,
157 relocs: Vec<RelocRecord>,
158}
159
160struct MemoryHandle {
162 code: Memory,
163 readonly: Memory,
164 writable: Memory,
165}
166
167pub struct SimpleJITProduct {
170 memory: MemoryHandle,
171 declarations: ModuleDeclarations,
172 functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
173 data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
174}
175
176impl SimpleJITProduct {
177 pub unsafe fn free_memory(&mut self) {
186 self.memory.code.free_memory();
187 self.memory.readonly.free_memory();
188 self.memory.writable.free_memory();
189 }
190
191 pub fn func_or_data_for_func(&self, name: &str) -> Option<FuncOrDataId> {
193 self.declarations.get_name(name)
194 }
195
196 pub fn lookup_func(&self, func_id: FuncId) -> *const u8 {
198 self.functions[func_id]
199 .as_ref()
200 .unwrap_or_else(|| panic!("{} is not defined", func_id))
201 .ptr
202 }
203
204 pub fn lookup_data(&self, data_id: DataId) -> (*const u8, usize) {
206 let data = self.data_objects[data_id]
207 .as_ref()
208 .unwrap_or_else(|| panic!("{} is not defined", data_id));
209 (data.ptr, data.size)
210 }
211}
212
213impl SimpleJITModule {
214 fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
215 self.symbols
216 .get(name)
217 .copied()
218 .or_else(|| lookup_with_dlsym(name))
219 }
220
221 fn get_definition(&self, name: &ir::ExternalName) -> *const u8 {
222 match *name {
223 ir::ExternalName::User { .. } => {
224 let (name, linkage) = if self.declarations.is_function(name) {
225 let func_id = self.declarations.get_function_id(name);
226 match &self.functions[func_id] {
227 Some(compiled) => return compiled.ptr,
228 None => {
229 let decl = self.declarations.get_function_decl(func_id);
230 (&decl.name, decl.linkage)
231 }
232 }
233 } else {
234 let data_id = self.declarations.get_data_id(name);
235 match &self.data_objects[data_id] {
236 Some(compiled) => return compiled.ptr,
237 None => {
238 let decl = self.declarations.get_data_decl(data_id);
239 (&decl.name, decl.linkage)
240 }
241 }
242 };
243 if let Some(ptr) = self.lookup_symbol(&name) {
244 ptr
245 } else if linkage == Linkage::Preemptible {
246 0 as *const u8
247 } else {
248 panic!("can't resolve symbol {}", name);
249 }
250 }
251 ir::ExternalName::LibCall(ref libcall) => {
252 let sym = (self.libcall_names)(*libcall);
253 self.lookup_symbol(&sym)
254 .unwrap_or_else(|| panic!("can't resolve libcall {}", sym))
255 }
256 _ => panic!("invalid ExternalName {}", name),
257 }
258 }
259
260 pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
262 let info = &self.functions[func_id];
263 debug_assert!(
264 !self.functions_to_finalize.iter().any(|x| *x == func_id),
265 "function not yet finalized"
266 );
267 info.as_ref()
268 .expect("function must be compiled before it can be finalized")
269 .ptr
270 }
271
272 pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
274 let info = &self.data_objects[data_id];
275 debug_assert!(
276 !self.data_objects_to_finalize.iter().any(|x| *x == data_id),
277 "data object not yet finalized"
278 );
279 let compiled = info
280 .as_ref()
281 .expect("data object must be compiled before it can be finalized");
282
283 (compiled.ptr, compiled.size)
284 }
285
286 fn record_function_for_perf(&self, ptr: *mut u8, size: usize, name: &str) {
287 if cfg!(target_os = "linux") && ::std::env::var_os("PERF_BUILDID_DIR").is_some() {
293 let mut map_file = ::std::fs::OpenOptions::new()
294 .create(true)
295 .append(true)
296 .open(format!("/tmp/perf-{}.map", ::std::process::id()))
297 .unwrap();
298
299 let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
300 }
301 }
302
303 fn finalize_function(&mut self, id: FuncId) {
304 use std::ptr::write_unaligned;
305
306 let func = self.functions[id]
307 .as_ref()
308 .expect("function must be compiled before it can be finalized");
309
310 for &RelocRecord {
311 reloc,
312 offset,
313 ref name,
314 addend,
315 } in &func.relocs
316 {
317 debug_assert!((offset as usize) < func.size);
318 let at = unsafe { func.ptr.offset(offset as isize) };
319 let base = self.get_definition(name);
320 let what = unsafe { base.offset(addend as isize) };
322 match reloc {
323 Reloc::Abs4 => {
324 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
326 unsafe {
327 write_unaligned(at as *mut u32, what as u32)
328 };
329 }
330 Reloc::Abs8 => {
331 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
332 unsafe {
333 write_unaligned(at as *mut u64, what as u64)
334 };
335 }
336 Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
337 let pcrel = ((what as isize) - (at as isize)) as i32;
339 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
340 unsafe {
341 write_unaligned(at as *mut i32, pcrel)
342 };
343 }
344 Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"),
345 _ => unimplemented!(),
346 }
347 }
348 }
349
350 fn finalize_data(&mut self, id: DataId) {
351 use std::ptr::write_unaligned;
352
353 let data = self.data_objects[id]
354 .as_ref()
355 .expect("data object must be compiled before it can be finalized");
356
357 for &RelocRecord {
358 reloc,
359 offset,
360 ref name,
361 addend,
362 } in &data.relocs
363 {
364 debug_assert!((offset as usize) < data.size);
365 let at = unsafe { data.ptr.offset(offset as isize) };
366 let base = self.get_definition(name);
367 let what = unsafe { base.offset(addend as isize) };
369 match reloc {
370 Reloc::Abs4 => {
371 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
373 unsafe {
374 write_unaligned(at as *mut u32, what as u32)
375 };
376 }
377 Reloc::Abs8 => {
378 #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
379 unsafe {
380 write_unaligned(at as *mut u64, what as u64)
381 };
382 }
383 Reloc::X86PCRel4
384 | Reloc::X86CallPCRel4
385 | Reloc::X86GOTPCRel4
386 | Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
387 _ => unimplemented!(),
388 }
389 }
390 }
391
392 pub fn finalize_definitions(&mut self) {
399 for func in std::mem::take(&mut self.functions_to_finalize) {
400 let decl = self.declarations.get_function_decl(func);
401 debug_assert!(decl.linkage.is_definable());
402 self.finalize_function(func);
403 }
404 for data in std::mem::take(&mut self.data_objects_to_finalize) {
405 let decl = self.declarations.get_data_decl(data);
406 debug_assert!(decl.linkage.is_definable());
407 self.finalize_data(data);
408 }
409
410 self.memory.readonly.set_readonly();
412 self.memory.code.set_readable_and_executable();
413 }
414
415 pub fn new(builder: SimpleJITBuilder) -> Self {
417 let memory = MemoryHandle {
418 code: Memory::new(),
419 readonly: Memory::new(),
420 writable: Memory::new(),
421 };
422
423 Self {
424 isa: builder.isa,
425 symbols: builder.symbols,
426 libcall_names: builder.libcall_names,
427 memory,
428 declarations: ModuleDeclarations::default(),
429 functions: SecondaryMap::new(),
430 data_objects: SecondaryMap::new(),
431 functions_to_finalize: Vec::new(),
432 data_objects_to_finalize: Vec::new(),
433 }
434 }
435}
436
437impl<'simple_jit_backend> Module for SimpleJITModule {
438 fn isa(&self) -> &dyn TargetIsa {
439 &*self.isa
440 }
441
442 fn declarations(&self) -> &ModuleDeclarations {
443 &self.declarations
444 }
445
446 fn declare_function(
447 &mut self,
448 name: &str,
449 linkage: Linkage,
450 signature: &ir::Signature,
451 ) -> ModuleResult<FuncId> {
452 let (id, _decl) = self
453 .declarations
454 .declare_function(name, linkage, signature)?;
455 Ok(id)
456 }
457
458 fn declare_data(
459 &mut self,
460 name: &str,
461 linkage: Linkage,
462 writable: bool,
463 tls: bool,
464 ) -> ModuleResult<DataId> {
465 assert!(!tls, "SimpleJIT doesn't yet support TLS");
466 let (id, _decl) = self
467 .declarations
468 .declare_data(name, linkage, writable, tls)?;
469 Ok(id)
470 }
471
472 fn define_function<TS>(
473 &mut self,
474 id: FuncId,
475 ctx: &mut cranelift_codegen::Context,
476 trap_sink: &mut TS,
477 ) -> ModuleResult<ModuleCompiledFunction>
478 where
479 TS: TrapSink,
480 {
481 info!("defining function {}: {}", id, ctx.func.display(self.isa()));
482 let CodeInfo {
483 total_size: code_size,
484 ..
485 } = ctx.compile(self.isa())?;
486
487 let decl = self.declarations.get_function_decl(id);
488 if !decl.linkage.is_definable() {
489 return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
490 }
491
492 if !self.functions[id].is_none() {
493 return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
494 }
495
496 self.functions_to_finalize.push(id);
497 let size = code_size as usize;
498 let ptr = self
499 .memory
500 .code
501 .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
502 .expect("TODO: handle OOM etc.");
503
504 self.record_function_for_perf(ptr, size, &decl.name);
505
506 let mut reloc_sink = SimpleJITRelocSink::default();
507 let mut stack_map_sink = SimpleJITStackMapSink::default();
508 unsafe {
509 ctx.emit_to_memory(
510 &*self.isa,
511 ptr,
512 &mut reloc_sink,
513 trap_sink,
514 &mut stack_map_sink,
515 )
516 };
517
518 self.functions[id] = Some(CompiledBlob {
519 ptr,
520 size,
521 relocs: reloc_sink.relocs,
522 });
523
524 Ok(ModuleCompiledFunction { size: code_size })
525 }
526
527 fn define_function_bytes(
528 &mut self,
529 id: FuncId,
530 bytes: &[u8],
531 ) -> ModuleResult<ModuleCompiledFunction> {
532 let decl = self.declarations.get_function_decl(id);
533 if !decl.linkage.is_definable() {
534 return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
535 }
536
537 let total_size: u32 = match bytes.len().try_into() {
538 Ok(total_size) => total_size,
539 _ => Err(ModuleError::FunctionTooLarge(decl.name.clone()))?,
540 };
541
542 if !self.functions[id].is_none() {
543 return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
544 }
545
546 self.functions_to_finalize.push(id);
547 let size = bytes.len();
548 let ptr = self
549 .memory
550 .code
551 .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
552 .expect("TODO: handle OOM etc.");
553
554 self.record_function_for_perf(ptr, size, &decl.name);
555
556 unsafe {
557 ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
558 }
559
560 self.functions[id] = Some(CompiledBlob {
561 ptr,
562 size,
563 relocs: vec![],
564 });
565
566 Ok(ModuleCompiledFunction { size: total_size })
567 }
568
569 fn define_data(&mut self, id: DataId, data: &DataContext) -> ModuleResult<()> {
570 let decl = self.declarations.get_data_decl(id);
571 if !decl.linkage.is_definable() {
572 return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
573 }
574
575 if !self.data_objects[id].is_none() {
576 return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
577 }
578
579 assert!(!decl.tls, "SimpleJIT doesn't yet support TLS");
580
581 self.data_objects_to_finalize.push(id);
582
583 let &DataDescription {
584 ref init,
585 ref function_decls,
586 ref data_decls,
587 ref function_relocs,
588 ref data_relocs,
589 custom_segment_section: _,
590 align,
591 } = data.description();
592
593 let size = init.size();
594 let ptr = if decl.writable {
595 self.memory
596 .writable
597 .allocate(size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
598 .expect("TODO: handle OOM etc.")
599 } else {
600 self.memory
601 .readonly
602 .allocate(size, align.unwrap_or(READONLY_DATA_ALIGNMENT))
603 .expect("TODO: handle OOM etc.")
604 };
605
606 match *init {
607 Init::Uninitialized => {
608 panic!("data is not initialized yet");
609 }
610 Init::Zeros { .. } => {
611 unsafe { ptr::write_bytes(ptr, 0, size) };
612 }
613 Init::Bytes { ref contents } => {
614 let src = contents.as_ptr();
615 unsafe { ptr::copy_nonoverlapping(src, ptr, size) };
616 }
617 }
618
619 let reloc = match self.isa.triple().pointer_width().unwrap() {
620 PointerWidth::U16 => panic!(),
621 PointerWidth::U32 => Reloc::Abs4,
622 PointerWidth::U64 => Reloc::Abs8,
623 };
624 let mut relocs = Vec::new();
625 for &(offset, id) in function_relocs {
626 relocs.push(RelocRecord {
627 reloc,
628 offset,
629 name: function_decls[id].clone(),
630 addend: 0,
631 });
632 }
633 for &(offset, id, addend) in data_relocs {
634 relocs.push(RelocRecord {
635 reloc,
636 offset,
637 name: data_decls[id].clone(),
638 addend,
639 });
640 }
641
642 self.data_objects[id] = Some(CompiledBlob { ptr, size, relocs });
643
644 Ok(())
645 }
646}
647
648impl SimpleJITModule {
649 pub fn finish(mut self) -> SimpleJITProduct {
657 self.finalize_definitions();
658
659 SimpleJITProduct {
660 memory: self.memory,
661 declarations: self.declarations,
662 functions: self.functions,
663 data_objects: self.data_objects,
664 }
665 }
666}
667
668#[cfg(not(windows))]
669fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
670 let c_str = CString::new(name).unwrap();
671 let c_str_ptr = c_str.as_ptr();
672 let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c_str_ptr) };
673 if sym.is_null() {
674 None
675 } else {
676 Some(sym as *const u8)
677 }
678}
679
680#[cfg(windows)]
681fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
682 const MSVCRT_DLL: &[u8] = b"msvcrt.dll\0";
683
684 let c_str = CString::new(name).unwrap();
685 let c_str_ptr = c_str.as_ptr();
686
687 unsafe {
688 let handles = [
689 ptr::null_mut(),
691 winapi::um::libloaderapi::GetModuleHandleA(MSVCRT_DLL.as_ptr() as *const i8),
693 ];
694
695 for handle in &handles {
696 let addr = winapi::um::libloaderapi::GetProcAddress(*handle, c_str_ptr);
697 if addr.is_null() {
698 continue;
699 }
700 return Some(addr as *const u8);
701 }
702
703 None
704 }
705}
706
707#[derive(Default)]
708struct SimpleJITRelocSink {
709 relocs: Vec<RelocRecord>,
710}
711
712impl RelocSink for SimpleJITRelocSink {
713 fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
714 unimplemented!();
715 }
716
717 fn reloc_external(
718 &mut self,
719 offset: CodeOffset,
720 _srcloc: ir::SourceLoc,
721 reloc: Reloc,
722 name: &ir::ExternalName,
723 addend: Addend,
724 ) {
725 self.relocs.push(RelocRecord {
726 offset,
727 reloc,
728 name: name.clone(),
729 addend,
730 });
731 }
732
733 fn reloc_jt(&mut self, _offset: CodeOffset, reloc: Reloc, _jt: ir::JumpTable) {
734 match reloc {
735 Reloc::X86PCRelRodata4 => {
736 }
739 _ => {
740 panic!("Unhandled reloc");
741 }
742 }
743 }
744
745 fn reloc_constant(&mut self, _offset: CodeOffset, reloc: Reloc, _constant: ir::ConstantOffset) {
746 match reloc {
747 Reloc::X86PCRelRodata4 => {
748 }
751 _ => {
752 panic!("Unhandled reloc");
753 }
754 }
755 }
756}
757
758#[derive(Default)]
759struct SimpleJITStackMapSink {
760 stack_maps: Vec<StackMapRecord>,
761}
762
763impl StackMapSink for SimpleJITStackMapSink {
764 fn add_stack_map(&mut self, offset: CodeOffset, stack_map: StackMap) {
765 self.stack_maps.push(StackMapRecord { offset, stack_map });
766 }
767}