cranelift_jit/
backend.rs

1//! Defines `JITModule`.
2
3use crate::{compiled_blob::CompiledBlob, memory::BranchProtection, memory::Memory};
4use cranelift_codegen::binemit::Reloc;
5use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
6use cranelift_codegen::settings::Configurable;
7use cranelift_codegen::{ir, settings};
8use cranelift_control::ControlPlane;
9use cranelift_entity::SecondaryMap;
10use cranelift_module::{
11    DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleDeclarations, ModuleError,
12    ModuleReloc, ModuleRelocTarget, ModuleResult,
13};
14use log::info;
15use std::cell::RefCell;
16use std::collections::HashMap;
17use std::ffi::CString;
18use std::io::Write;
19use std::ptr;
20use std::ptr::NonNull;
21use std::sync::atomic::{AtomicPtr, Ordering};
22use target_lexicon::PointerWidth;
23
24const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
25const READONLY_DATA_ALIGNMENT: u64 = 0x1;
26
27/// A builder for `JITModule`.
28pub struct JITBuilder {
29    isa: OwnedTargetIsa,
30    symbols: HashMap<String, SendWrapper<*const u8>>,
31    lookup_symbols: Vec<Box<dyn Fn(&str) -> Option<*const u8> + Send>>,
32    libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
33}
34
35impl JITBuilder {
36    /// Create a new `JITBuilder`.
37    ///
38    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
39    /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
40    /// floating point instructions, and for stack probes. If you don't know what to use for this
41    /// argument, use `cranelift_module::default_libcall_names()`.
42    pub fn new(
43        libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
44    ) -> ModuleResult<Self> {
45        Self::with_flags(&[], libcall_names)
46    }
47
48    /// Create a new `JITBuilder` with the given flags.
49    ///
50    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
51    /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
52    /// floating point instructions, and for stack probes. If you don't know what to use for this
53    /// argument, use `cranelift_module::default_libcall_names()`.
54    pub fn with_flags(
55        flags: &[(&str, &str)],
56        libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
57    ) -> ModuleResult<Self> {
58        let mut flag_builder = settings::builder();
59        for (name, value) in flags {
60            flag_builder.set(name, value)?;
61        }
62
63        // On at least AArch64, "colocated" calls use shorter-range relocations,
64        // which might not reach all definitions; we can't handle that here, so
65        // we require long-range relocation types.
66        flag_builder.set("use_colocated_libcalls", "false").unwrap();
67        flag_builder.set("is_pic", "true").unwrap();
68        let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
69            panic!("host machine is not supported: {msg}");
70        });
71        let isa = isa_builder.finish(settings::Flags::new(flag_builder))?;
72        Ok(Self::with_isa(isa, libcall_names))
73    }
74
75    /// Create a new `JITBuilder` with an arbitrary target. This is mainly
76    /// useful for testing.
77    ///
78    /// To create a `JITBuilder` for native use, use the `new` or `with_flags`
79    /// constructors instead.
80    ///
81    /// The `libcall_names` function provides a way to translate `cranelift_codegen`'s `ir::LibCall`
82    /// enum to symbols. LibCalls are inserted in the IR as part of the legalization for certain
83    /// floating point instructions, and for stack probes. If you don't know what to use for this
84    /// argument, use `cranelift_module::default_libcall_names()`.
85    pub fn with_isa(
86        isa: OwnedTargetIsa,
87        libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
88    ) -> Self {
89        let symbols = HashMap::new();
90        let lookup_symbols = vec![Box::new(lookup_with_dlsym) as Box<_>];
91        Self {
92            isa,
93            symbols,
94            lookup_symbols,
95            libcall_names,
96        }
97    }
98
99    /// Define a symbol in the internal symbol table.
100    ///
101    /// The JIT will use the symbol table to resolve names that are declared,
102    /// but not defined, in the module being compiled.  A common example is
103    /// external functions.  With this method, functions and data can be exposed
104    /// to the code being compiled which are defined by the host.
105    ///
106    /// If a symbol is defined more than once, the most recent definition will
107    /// be retained.
108    ///
109    /// If the JIT fails to find a symbol in its internal table, it will fall
110    /// back to a platform-specific search (this typically involves searching
111    /// the current process for public symbols, followed by searching the
112    /// platform's C runtime).
113    pub fn symbol<K>(&mut self, name: K, ptr: *const u8) -> &mut Self
114    where
115        K: Into<String>,
116    {
117        self.symbols.insert(name.into(), SendWrapper(ptr));
118        self
119    }
120
121    /// Define multiple symbols in the internal symbol table.
122    ///
123    /// Using this is equivalent to calling `symbol` on each element.
124    pub fn symbols<It, K>(&mut self, symbols: It) -> &mut Self
125    where
126        It: IntoIterator<Item = (K, *const u8)>,
127        K: Into<String>,
128    {
129        for (name, ptr) in symbols {
130            self.symbols.insert(name.into(), SendWrapper(ptr));
131        }
132        self
133    }
134
135    /// Add a symbol lookup fn.
136    ///
137    /// Symbol lookup fn's are used to lookup symbols when they couldn't be found in the internal
138    /// symbol table. Symbol lookup fn's are called in reverse of the order in which they were added.
139    pub fn symbol_lookup_fn(
140        &mut self,
141        symbol_lookup_fn: Box<dyn Fn(&str) -> Option<*const u8> + Send>,
142    ) -> &mut Self {
143        self.lookup_symbols.push(symbol_lookup_fn);
144        self
145    }
146}
147
148/// A pending update to the GOT.
149struct GotUpdate {
150    /// The entry that is to be updated.
151    entry: NonNull<AtomicPtr<u8>>,
152
153    /// The new value of the entry.
154    ptr: *const u8,
155}
156
157unsafe impl Send for GotUpdate {}
158
159/// A wrapper that impls Send for the contents.
160///
161/// SAFETY: This must not be used for any types where it would be UB for them to be Send
162#[derive(Copy, Clone)]
163struct SendWrapper<T>(T);
164unsafe impl<T> Send for SendWrapper<T> {}
165
166/// A `JITModule` implements `Module` and emits code and data into memory where it can be
167/// directly called and accessed.
168///
169/// See the `JITBuilder` for a convenient way to construct `JITModule` instances.
170pub 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: MemoryHandle,
176    declarations: ModuleDeclarations,
177    function_got_entries: SecondaryMap<FuncId, Option<SendWrapper<NonNull<AtomicPtr<u8>>>>>,
178    function_plt_entries: SecondaryMap<FuncId, Option<SendWrapper<NonNull<[u8; 16]>>>>,
179    data_object_got_entries: SecondaryMap<DataId, Option<SendWrapper<NonNull<AtomicPtr<u8>>>>>,
180    libcall_got_entries: HashMap<ir::LibCall, SendWrapper<NonNull<AtomicPtr<u8>>>>,
181    libcall_plt_entries: HashMap<ir::LibCall, SendWrapper<NonNull<[u8; 16]>>>,
182    compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
183    compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
184    functions_to_finalize: Vec<FuncId>,
185    data_objects_to_finalize: Vec<DataId>,
186
187    /// Updates to the GOT awaiting relocations to be made and region protections to be set
188    pending_got_updates: Vec<GotUpdate>,
189}
190
191/// A handle to allow freeing memory allocated by the `Module`.
192struct MemoryHandle {
193    code: Memory,
194    readonly: Memory,
195    writable: Memory,
196}
197
198impl JITModule {
199    /// Free memory allocated for code and data segments of compiled functions.
200    ///
201    /// # Safety
202    ///
203    /// Because this function invalidates any pointers retrieved from the
204    /// corresponding module, it should only be used when none of the functions
205    /// from that module are currently executing and none of the `fn` pointers
206    /// are called afterwards.
207    pub unsafe fn free_memory(mut self) {
208        self.memory.code.free_memory();
209        self.memory.readonly.free_memory();
210        self.memory.writable.free_memory();
211    }
212
213    fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
214        match self.symbols.borrow_mut().entry(name.to_owned()) {
215            std::collections::hash_map::Entry::Occupied(occ) => Some(occ.get().0),
216            std::collections::hash_map::Entry::Vacant(vac) => {
217                let ptr = self
218                    .lookup_symbols
219                    .iter()
220                    .rev() // Try last lookup function first
221                    .find_map(|lookup| lookup(name));
222                if let Some(ptr) = ptr {
223                    vac.insert(SendWrapper(ptr));
224                }
225                ptr
226            }
227        }
228    }
229
230    fn new_got_entry(&mut self, val: *const u8) -> NonNull<AtomicPtr<u8>> {
231        let got_entry = self
232            .memory
233            .writable
234            .allocate(
235                std::mem::size_of::<AtomicPtr<u8>>(),
236                std::mem::align_of::<AtomicPtr<u8>>().try_into().unwrap(),
237            )
238            .unwrap()
239            .cast::<AtomicPtr<u8>>();
240        unsafe {
241            std::ptr::write(got_entry, AtomicPtr::new(val as *mut _));
242        }
243        NonNull::new(got_entry).unwrap()
244    }
245
246    fn new_plt_entry(&mut self, got_entry: NonNull<AtomicPtr<u8>>) -> NonNull<[u8; 16]> {
247        let plt_entry = self
248            .memory
249            .code
250            .allocate(
251                std::mem::size_of::<[u8; 16]>(),
252                self.isa
253                    .symbol_alignment()
254                    .max(self.isa.function_alignment().minimum as u64),
255            )
256            .unwrap()
257            .cast::<[u8; 16]>();
258        unsafe {
259            Self::write_plt_entry_bytes(plt_entry, got_entry);
260        }
261        NonNull::new(plt_entry).unwrap()
262    }
263
264    fn new_func_plt_entry(&mut self, id: FuncId, val: *const u8) {
265        let got_entry = self.new_got_entry(val);
266        self.function_got_entries[id] = Some(SendWrapper(got_entry));
267        let plt_entry = self.new_plt_entry(got_entry);
268        self.record_function_for_perf(
269            plt_entry.as_ptr().cast(),
270            std::mem::size_of::<[u8; 16]>(),
271            &format!(
272                "{}@plt",
273                self.declarations.get_function_decl(id).linkage_name(id)
274            ),
275        );
276        self.function_plt_entries[id] = Some(SendWrapper(plt_entry));
277    }
278
279    fn new_data_got_entry(&mut self, id: DataId, val: *const u8) {
280        let got_entry = self.new_got_entry(val);
281        self.data_object_got_entries[id] = Some(SendWrapper(got_entry));
282    }
283
284    unsafe fn write_plt_entry_bytes(plt_ptr: *mut [u8; 16], got_ptr: NonNull<AtomicPtr<u8>>) {
285        assert!(
286            cfg!(target_arch = "x86_64"),
287            "PLT is currently only supported on x86_64"
288        );
289        // jmp *got_ptr; ud2; ud2; ud2; ud2; ud2
290        let mut plt_val = [
291            0xff, 0x25, 0, 0, 0, 0, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b,
292        ];
293        let what = got_ptr.as_ptr() as isize - 4;
294        let at = plt_ptr as isize + 2;
295        plt_val[2..6].copy_from_slice(&i32::to_ne_bytes(i32::try_from(what - at).unwrap()));
296        std::ptr::write(plt_ptr, plt_val);
297    }
298
299    fn get_address(&self, name: &ModuleRelocTarget) -> *const u8 {
300        match *name {
301            ModuleRelocTarget::User { .. } => {
302                let (name, linkage) = if ModuleDeclarations::is_function(name) {
303                    let func_id = FuncId::from_name(name);
304                    match &self.compiled_functions[func_id] {
305                        Some(compiled) => return compiled.ptr,
306                        None => {
307                            let decl = self.declarations.get_function_decl(func_id);
308                            (&decl.name, decl.linkage)
309                        }
310                    }
311                } else {
312                    let data_id = DataId::from_name(name);
313                    match &self.compiled_data_objects[data_id] {
314                        Some(compiled) => return compiled.ptr,
315                        None => {
316                            let decl = self.declarations.get_data_decl(data_id);
317                            (&decl.name, decl.linkage)
318                        }
319                    }
320                };
321                let name = name
322                    .as_ref()
323                    .expect("anonymous symbol must be defined locally");
324                if let Some(ptr) = self.lookup_symbol(name) {
325                    ptr
326                } else if linkage == Linkage::Preemptible {
327                    0 as *const u8
328                } else {
329                    panic!("can't resolve symbol {name}");
330                }
331            }
332            ModuleRelocTarget::LibCall(ref libcall) => {
333                let sym = (self.libcall_names)(*libcall);
334                self.lookup_symbol(&sym)
335                    .unwrap_or_else(|| panic!("can't resolve libcall {sym}"))
336            }
337            _ => panic!("invalid name"),
338        }
339    }
340
341    /// Returns the given function's entry in the Global Offset Table.
342    ///
343    /// Panics if there's no entry in the table for the given function.
344    pub fn read_got_entry(&self, func_id: FuncId) -> *const u8 {
345        let got_entry = self.function_got_entries[func_id].unwrap();
346        unsafe { got_entry.0.as_ref() }.load(Ordering::SeqCst)
347    }
348
349    fn get_got_address(&self, name: &ModuleRelocTarget) -> NonNull<AtomicPtr<u8>> {
350        match *name {
351            ModuleRelocTarget::User { .. } => {
352                if ModuleDeclarations::is_function(name) {
353                    let func_id = FuncId::from_name(name);
354                    self.function_got_entries[func_id].unwrap().0
355                } else {
356                    let data_id = DataId::from_name(name);
357                    self.data_object_got_entries[data_id].unwrap().0
358                }
359            }
360            ModuleRelocTarget::LibCall(ref libcall) => {
361                self.libcall_got_entries
362                    .get(libcall)
363                    .unwrap_or_else(|| panic!("can't resolve libcall {libcall}"))
364                    .0
365            }
366            _ => panic!("invalid name"),
367        }
368    }
369
370    fn get_plt_address(&self, name: &ModuleRelocTarget) -> *const u8 {
371        match *name {
372            ModuleRelocTarget::User { .. } => {
373                if ModuleDeclarations::is_function(name) {
374                    let func_id = FuncId::from_name(name);
375                    self.function_plt_entries[func_id]
376                        .unwrap()
377                        .0
378                        .as_ptr()
379                        .cast::<u8>()
380                } else {
381                    unreachable!("PLT relocations can only have functions as target");
382                }
383            }
384            ModuleRelocTarget::LibCall(ref libcall) => self
385                .libcall_plt_entries
386                .get(libcall)
387                .unwrap_or_else(|| panic!("can't resolve libcall {libcall}"))
388                .0
389                .as_ptr()
390                .cast::<u8>(),
391            _ => panic!("invalid name"),
392        }
393    }
394
395    /// Returns the address of a finalized function.
396    ///
397    /// The pointer remains valid until either [`JITModule::free_memory`] is called or in the future
398    /// some way of deallocating this individual function is used.
399    pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
400        let info = &self.compiled_functions[func_id];
401        assert!(
402            !self.functions_to_finalize.iter().any(|x| *x == func_id),
403            "function not yet finalized"
404        );
405        info.as_ref()
406            .expect("function must be compiled before it can be finalized")
407            .ptr
408    }
409
410    /// Returns the address and size of a finalized data object.
411    ///
412    /// The pointer remains valid until either [`JITModule::free_memory`] is called or in the future
413    /// some way of deallocating this individual data object is used.
414    pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
415        let info = &self.compiled_data_objects[data_id];
416        assert!(
417            !self.data_objects_to_finalize.iter().any(|x| *x == data_id),
418            "data object not yet finalized"
419        );
420        let compiled = info
421            .as_ref()
422            .expect("data object must be compiled before it can be finalized");
423
424        (compiled.ptr, compiled.size)
425    }
426
427    fn record_function_for_perf(&self, ptr: *mut u8, size: usize, name: &str) {
428        // The Linux perf tool supports JIT code via a /tmp/perf-$PID.map file,
429        // which contains memory regions and their associated names.  If we
430        // are profiling with perf and saving binaries to PERF_BUILDID_DIR
431        // for post-profile analysis, write information about each function
432        // we define.
433        if cfg!(unix) && ::std::env::var_os("PERF_BUILDID_DIR").is_some() {
434            let mut map_file = ::std::fs::OpenOptions::new()
435                .create(true)
436                .append(true)
437                .open(format!("/tmp/perf-{}.map", ::std::process::id()))
438                .unwrap();
439
440            let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
441        }
442    }
443
444    /// Finalize all functions and data objects that are defined but not yet finalized.
445    /// All symbols referenced in their bodies that are declared as needing a definition
446    /// must be defined by this point.
447    ///
448    /// Use `get_finalized_function` and `get_finalized_data` to obtain the final
449    /// artifacts.
450    ///
451    /// Returns ModuleError in case of allocation or syscall failure
452    pub fn finalize_definitions(&mut self) -> ModuleResult<()> {
453        for func in std::mem::take(&mut self.functions_to_finalize) {
454            let decl = self.declarations.get_function_decl(func);
455            assert!(decl.linkage.is_definable());
456            let func = self.compiled_functions[func]
457                .as_ref()
458                .expect("function must be compiled before it can be finalized");
459            func.perform_relocations(
460                |name| self.get_address(name),
461                |name| self.get_got_address(name).as_ptr().cast(),
462                |name| self.get_plt_address(name),
463            );
464        }
465
466        for data in std::mem::take(&mut self.data_objects_to_finalize) {
467            let decl = self.declarations.get_data_decl(data);
468            assert!(decl.linkage.is_definable());
469            let data = self.compiled_data_objects[data]
470                .as_ref()
471                .expect("data object must be compiled before it can be finalized");
472            data.perform_relocations(
473                |name| self.get_address(name),
474                |name| self.get_got_address(name).as_ptr().cast(),
475                |name| self.get_plt_address(name),
476            );
477        }
478
479        // Now that we're done patching, prepare the memory for execution!
480        self.memory.readonly.set_readonly()?;
481        self.memory.code.set_readable_and_executable()?;
482
483        for update in self.pending_got_updates.drain(..) {
484            unsafe { update.entry.as_ref() }.store(update.ptr as *mut _, Ordering::SeqCst);
485        }
486        Ok(())
487    }
488
489    /// Create a new `JITModule`.
490    pub fn new(builder: JITBuilder) -> Self {
491        let branch_protection =
492            if cfg!(target_arch = "aarch64") && use_bti(&builder.isa.isa_flags()) {
493                BranchProtection::BTI
494            } else {
495                BranchProtection::None
496            };
497        let mut module = Self {
498            isa: builder.isa,
499            symbols: RefCell::new(builder.symbols),
500            lookup_symbols: builder.lookup_symbols,
501            libcall_names: builder.libcall_names,
502            memory: MemoryHandle {
503                code: Memory::new(branch_protection),
504                // Branch protection is not applicable to non-executable memory.
505                readonly: Memory::new(BranchProtection::None),
506                writable: Memory::new(BranchProtection::None),
507            },
508            declarations: ModuleDeclarations::default(),
509            function_got_entries: SecondaryMap::new(),
510            function_plt_entries: SecondaryMap::new(),
511            data_object_got_entries: SecondaryMap::new(),
512            libcall_got_entries: HashMap::new(),
513            libcall_plt_entries: HashMap::new(),
514            compiled_functions: SecondaryMap::new(),
515            compiled_data_objects: SecondaryMap::new(),
516            functions_to_finalize: Vec::new(),
517            data_objects_to_finalize: Vec::new(),
518            pending_got_updates: Vec::new(),
519        };
520
521        // Pre-create a GOT and PLT entry for each libcall.
522        let all_libcalls = if module.isa.flags().is_pic() {
523            ir::LibCall::all_libcalls()
524        } else {
525            &[] // Not PIC, so no GOT and PLT entries necessary
526        };
527        for &libcall in all_libcalls {
528            let sym = (module.libcall_names)(libcall);
529            let addr = if let Some(addr) = module.lookup_symbol(&sym) {
530                addr
531            } else {
532                continue;
533            };
534            let got_entry = module.new_got_entry(addr);
535            module
536                .libcall_got_entries
537                .insert(libcall, SendWrapper(got_entry));
538            let plt_entry = module.new_plt_entry(got_entry);
539            module
540                .libcall_plt_entries
541                .insert(libcall, SendWrapper(plt_entry));
542        }
543
544        module
545    }
546}
547
548impl Module for JITModule {
549    fn isa(&self) -> &dyn TargetIsa {
550        &*self.isa
551    }
552
553    fn declarations(&self) -> &ModuleDeclarations {
554        &self.declarations
555    }
556
557    fn declare_function(
558        &mut self,
559        name: &str,
560        linkage: Linkage,
561        signature: &ir::Signature,
562    ) -> ModuleResult<FuncId> {
563        let (id, linkage) = self
564            .declarations
565            .declare_function(name, linkage, signature)?;
566        if self.function_got_entries[id].is_none() && self.isa.flags().is_pic() {
567            // FIXME populate got entries with a null pointer when defined
568            let val = if linkage == Linkage::Import {
569                self.lookup_symbol(name).unwrap_or(std::ptr::null())
570            } else {
571                std::ptr::null()
572            };
573            self.new_func_plt_entry(id, val);
574        }
575        Ok(id)
576    }
577
578    fn declare_anonymous_function(&mut self, signature: &ir::Signature) -> ModuleResult<FuncId> {
579        let id = self.declarations.declare_anonymous_function(signature)?;
580        if self.isa.flags().is_pic() {
581            self.new_func_plt_entry(id, std::ptr::null());
582        }
583        Ok(id)
584    }
585
586    fn declare_data(
587        &mut self,
588        name: &str,
589        linkage: Linkage,
590        writable: bool,
591        tls: bool,
592    ) -> ModuleResult<DataId> {
593        assert!(!tls, "JIT doesn't yet support TLS");
594        let (id, linkage) = self
595            .declarations
596            .declare_data(name, linkage, writable, tls)?;
597        if self.data_object_got_entries[id].is_none() && self.isa.flags().is_pic() {
598            // FIXME populate got entries with a null pointer when defined
599            let val = if linkage == Linkage::Import {
600                self.lookup_symbol(name).unwrap_or(std::ptr::null())
601            } else {
602                std::ptr::null()
603            };
604            self.new_data_got_entry(id, val);
605        }
606        Ok(id)
607    }
608
609    fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> {
610        assert!(!tls, "JIT doesn't yet support TLS");
611        let id = self.declarations.declare_anonymous_data(writable, tls)?;
612        if self.isa.flags().is_pic() {
613            self.new_data_got_entry(id, std::ptr::null());
614        }
615        Ok(id)
616    }
617
618    fn define_function_with_control_plane(
619        &mut self,
620        id: FuncId,
621        ctx: &mut cranelift_codegen::Context,
622        ctrl_plane: &mut ControlPlane,
623    ) -> ModuleResult<()> {
624        info!("defining function {}: {}", id, ctx.func.display());
625        let decl = self.declarations.get_function_decl(id);
626        if !decl.linkage.is_definable() {
627            return Err(ModuleError::InvalidImportDefinition(
628                decl.linkage_name(id).into_owned(),
629            ));
630        }
631
632        if !self.compiled_functions[id].is_none() {
633            return Err(ModuleError::DuplicateDefinition(
634                decl.linkage_name(id).into_owned(),
635            ));
636        }
637
638        // work around borrow-checker to allow reuse of ctx below
639        let res = ctx.compile(self.isa(), ctrl_plane)?;
640        let alignment = res.buffer.alignment as u64;
641        let compiled_code = ctx.compiled_code().unwrap();
642
643        let size = compiled_code.code_info().total_size as usize;
644        let align = alignment.max(self.isa.symbol_alignment());
645        let ptr = self
646            .memory
647            .code
648            .allocate(size, align)
649            .map_err(|e| ModuleError::Allocation {
650                message: "unable to alloc function",
651                err: e,
652            })?;
653
654        {
655            let mem = unsafe { std::slice::from_raw_parts_mut(ptr, size) };
656            mem.copy_from_slice(compiled_code.code_buffer());
657        }
658
659        let relocs = compiled_code
660            .buffer
661            .relocs()
662            .iter()
663            .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func, id))
664            .collect();
665
666        self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
667        self.compiled_functions[id] = Some(CompiledBlob { ptr, size, relocs });
668
669        if self.isa.flags().is_pic() {
670            self.pending_got_updates.push(GotUpdate {
671                entry: self.function_got_entries[id].unwrap().0,
672                ptr,
673            })
674        }
675
676        self.functions_to_finalize.push(id);
677
678        Ok(())
679    }
680
681    fn define_function_bytes(
682        &mut self,
683        id: FuncId,
684        alignment: u64,
685        bytes: &[u8],
686        relocs: &[ModuleReloc],
687    ) -> ModuleResult<()> {
688        info!("defining function {} with bytes", id);
689        let decl = self.declarations.get_function_decl(id);
690        if !decl.linkage.is_definable() {
691            return Err(ModuleError::InvalidImportDefinition(
692                decl.linkage_name(id).into_owned(),
693            ));
694        }
695
696        if !self.compiled_functions[id].is_none() {
697            return Err(ModuleError::DuplicateDefinition(
698                decl.linkage_name(id).into_owned(),
699            ));
700        }
701
702        let size = bytes.len();
703        let align = alignment.max(self.isa.symbol_alignment());
704        let ptr = self
705            .memory
706            .code
707            .allocate(size, align)
708            .map_err(|e| ModuleError::Allocation {
709                message: "unable to alloc function bytes",
710                err: e,
711            })?;
712
713        unsafe {
714            ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
715        }
716
717        self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
718        self.compiled_functions[id] = Some(CompiledBlob {
719            ptr,
720            size,
721            relocs: relocs.to_owned(),
722        });
723
724        if self.isa.flags().is_pic() {
725            self.pending_got_updates.push(GotUpdate {
726                entry: self.function_got_entries[id].unwrap().0,
727                ptr,
728            })
729        }
730
731        self.functions_to_finalize.push(id);
732
733        Ok(())
734    }
735
736    fn define_data(&mut self, id: DataId, data: &DataDescription) -> ModuleResult<()> {
737        let decl = self.declarations.get_data_decl(id);
738        if !decl.linkage.is_definable() {
739            return Err(ModuleError::InvalidImportDefinition(
740                decl.linkage_name(id).into_owned(),
741            ));
742        }
743
744        if !self.compiled_data_objects[id].is_none() {
745            return Err(ModuleError::DuplicateDefinition(
746                decl.linkage_name(id).into_owned(),
747            ));
748        }
749
750        assert!(!decl.tls, "JIT doesn't yet support TLS");
751
752        let &DataDescription {
753            ref init,
754            function_decls: _,
755            data_decls: _,
756            function_relocs: _,
757            data_relocs: _,
758            custom_segment_section: _,
759            align,
760        } = data;
761
762        // Make sure to allocate at least 1 byte. Allocating 0 bytes is UB. Previously a dummy
763        // value was used, however as it turns out this will cause pc-relative relocations to
764        // fail on architectures where pc-relative offsets are range restricted as the dummy
765        // value is not close enough to the code that has the pc-relative relocation.
766        let alloc_size = std::cmp::max(init.size(), 1);
767
768        let ptr = if decl.writable {
769            self.memory
770                .writable
771                .allocate(alloc_size, align.unwrap_or(WRITABLE_DATA_ALIGNMENT))
772                .map_err(|e| ModuleError::Allocation {
773                    message: "unable to alloc writable data",
774                    err: e,
775                })?
776        } else {
777            self.memory
778                .readonly
779                .allocate(alloc_size, align.unwrap_or(READONLY_DATA_ALIGNMENT))
780                .map_err(|e| ModuleError::Allocation {
781                    message: "unable to alloc readonly data",
782                    err: e,
783                })?
784        };
785
786        if ptr.is_null() {
787            // FIXME pass a Layout to allocate and only compute the layout once.
788            std::alloc::handle_alloc_error(
789                std::alloc::Layout::from_size_align(
790                    alloc_size,
791                    align.unwrap_or(READONLY_DATA_ALIGNMENT).try_into().unwrap(),
792                )
793                .unwrap(),
794            );
795        }
796
797        match *init {
798            Init::Uninitialized => {
799                panic!("data is not initialized yet");
800            }
801            Init::Zeros { size } => {
802                unsafe { ptr::write_bytes(ptr, 0, size) };
803            }
804            Init::Bytes { ref contents } => {
805                let src = contents.as_ptr();
806                unsafe { ptr::copy_nonoverlapping(src, ptr, contents.len()) };
807            }
808        }
809
810        let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
811            PointerWidth::U16 => panic!(),
812            PointerWidth::U32 => Reloc::Abs4,
813            PointerWidth::U64 => Reloc::Abs8,
814        };
815        let relocs = data.all_relocs(pointer_reloc).collect::<Vec<_>>();
816
817        self.compiled_data_objects[id] = Some(CompiledBlob {
818            ptr,
819            size: init.size(),
820            relocs,
821        });
822        self.data_objects_to_finalize.push(id);
823        if self.isa.flags().is_pic() {
824            self.pending_got_updates.push(GotUpdate {
825                entry: self.data_object_got_entries[id].unwrap().0,
826                ptr,
827            })
828        }
829
830        Ok(())
831    }
832
833    fn get_name(&self, name: &str) -> Option<cranelift_module::FuncOrDataId> {
834        self.declarations().get_name(name)
835    }
836
837    fn target_config(&self) -> cranelift_codegen::isa::TargetFrontendConfig {
838        self.isa().frontend_config()
839    }
840
841    fn make_context(&self) -> cranelift_codegen::Context {
842        let mut ctx = cranelift_codegen::Context::new();
843        ctx.func.signature.call_conv = self.isa().default_call_conv();
844        ctx
845    }
846
847    fn clear_context(&self, ctx: &mut cranelift_codegen::Context) {
848        ctx.clear();
849        ctx.func.signature.call_conv = self.isa().default_call_conv();
850    }
851
852    fn make_signature(&self) -> ir::Signature {
853        ir::Signature::new(self.isa().default_call_conv())
854    }
855
856    fn clear_signature(&self, sig: &mut ir::Signature) {
857        sig.clear(self.isa().default_call_conv());
858    }
859}
860
861#[cfg(not(windows))]
862fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
863    let c_str = CString::new(name).unwrap();
864    let c_str_ptr = c_str.as_ptr();
865    let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c_str_ptr) };
866    if sym.is_null() {
867        None
868    } else {
869        Some(sym as *const u8)
870    }
871}
872
873#[cfg(windows)]
874fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
875    use std::os::windows::io::RawHandle;
876    use windows_sys::Win32::Foundation::HMODULE;
877    use windows_sys::Win32::System::LibraryLoader;
878
879    const UCRTBASE: &[u8] = b"ucrtbase.dll\0";
880
881    let c_str = CString::new(name).unwrap();
882    let c_str_ptr = c_str.as_ptr();
883
884    unsafe {
885        let handles = [
886            // try to find the searched symbol in the currently running executable
887            ptr::null_mut(),
888            // try to find the searched symbol in local c runtime
889            LibraryLoader::GetModuleHandleA(UCRTBASE.as_ptr()) as RawHandle,
890        ];
891
892        for handle in &handles {
893            let addr = LibraryLoader::GetProcAddress(*handle as HMODULE, c_str_ptr.cast());
894            match addr {
895                None => continue,
896                Some(addr) => return Some(addr as *const u8),
897            }
898        }
899
900        None
901    }
902}
903
904fn use_bti(isa_flags: &Vec<settings::Value>) -> bool {
905    isa_flags
906        .iter()
907        .find(|&f| f.name == "use_bti")
908        .map_or(false, |f| f.as_bool().unwrap_or(false))
909}