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