Skip to main content

cranelift_jit/
backend.rs

1//! Defines `JITModule`.
2
3use crate::{
4    compiled_blob::CompiledBlob,
5    memory::{BranchProtection, JITMemoryKind, JITMemoryProvider, SystemMemoryProvider},
6};
7use cranelift_codegen::binemit::Reloc;
8use cranelift_codegen::isa::{OwnedTargetIsa, TargetIsa};
9use cranelift_codegen::settings::Configurable;
10use cranelift_codegen::{ir, settings};
11use cranelift_control::ControlPlane;
12use cranelift_entity::SecondaryMap;
13use cranelift_module::{
14    DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleDeclarations, ModuleError,
15    ModuleReloc, ModuleRelocTarget, ModuleResult,
16};
17use log::info;
18use std::cell::RefCell;
19use std::collections::BTreeMap;
20use std::collections::HashMap;
21use std::ffi::CString;
22use std::io::Write;
23use target_lexicon::PointerWidth;
24
25const WRITABLE_DATA_ALIGNMENT: u64 = 0x8;
26const READONLY_DATA_ALIGNMENT: u64 = 0x1;
27
28/// A builder for `JITModule`.
29pub struct JITBuilder {
30    isa: OwnedTargetIsa,
31    symbols: HashMap<String, SendWrapper<*const u8>>,
32    lookup_symbols: Vec<Box<dyn Fn(&str) -> Option<*const u8> + Send>>,
33    libcall_names: Box<dyn Fn(ir::LibCall) -> String + Send + Sync>,
34    memory: Option<Box<dyn JITMemoryProvider + Send>>,
35}
36
37impl JITBuilder {
38    /// 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", "false").unwrap();
70        let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| {
71            panic!("host machine is not supported: {msg}");
72        });
73        let isa = isa_builder.finish(settings::Flags::new(flag_builder))?;
74        Ok(Self::with_isa(isa, libcall_names))
75    }
76
77    /// 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            memory: None,
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(), SendWrapper(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(), SendWrapper(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> + Send>,
145    ) -> &mut Self {
146        self.lookup_symbols.push(symbol_lookup_fn);
147        self
148    }
149
150    /// Set the memory provider for the module.
151    ///
152    /// If unset, defaults to [`SystemMemoryProvider`].
153    pub fn memory_provider(&mut self, provider: Box<dyn JITMemoryProvider + Send>) -> &mut Self {
154        self.memory = Some(provider);
155        self
156    }
157}
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: Box<dyn JITMemoryProvider + Send>,
176    declarations: ModuleDeclarations,
177    compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
178    compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
179    /// Map from a function's start address to its (end address, FuncId), used
180    /// to resolve a PC back to its function for exception unwinding.
181    code_ranges: BTreeMap<usize, (usize, FuncId)>,
182    functions_to_finalize: Vec<FuncId>,
183    data_objects_to_finalize: Vec<DataId>,
184}
185
186impl JITModule {
187    /// Free memory allocated for code and data segments of compiled functions.
188    ///
189    /// # Safety
190    ///
191    /// Because this function invalidates any pointers retrieved from the
192    /// corresponding module, it should only be used when none of the functions
193    /// from that module are currently executing and none of the `fn` pointers
194    /// are called afterwards.
195    pub unsafe fn free_memory(mut self) {
196        self.memory.free_memory();
197    }
198
199    fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
200        match self.symbols.borrow_mut().entry(name.to_owned()) {
201            std::collections::hash_map::Entry::Occupied(occ) => Some(occ.get().0),
202            std::collections::hash_map::Entry::Vacant(vac) => {
203                let ptr = self
204                    .lookup_symbols
205                    .iter()
206                    .rev() // Try last lookup function first
207                    .find_map(|lookup| lookup(name));
208                if let Some(ptr) = ptr {
209                    vac.insert(SendWrapper(ptr));
210                }
211                ptr
212            }
213        }
214    }
215
216    /// Get the address that the given [`ModuleRelocTarget`] would resolve to.
217    ///
218    /// For locally defined functions and data objects this is equivalent to
219    /// [`Self::get_finalized_function`] cq [`Self::get_finalized_data`], while
220    /// for external functions and data objects this will iterate through the
221    /// registered symbol lookup functions until one matches.
222    pub fn get_address(&self, name: &ModuleRelocTarget) -> *const u8 {
223        match name {
224            ModuleRelocTarget::User { .. } => {
225                let (name, linkage) = if ModuleDeclarations::is_function(name) {
226                    let func_id = FuncId::from_name(name);
227                    match &self.compiled_functions[func_id] {
228                        Some(compiled) => return compiled.ptr(),
229                        None => {
230                            let decl = self.declarations.get_function_decl(func_id);
231                            (&decl.name, decl.linkage)
232                        }
233                    }
234                } else {
235                    let data_id = DataId::from_name(name);
236                    match &self.compiled_data_objects[data_id] {
237                        Some(compiled) => return compiled.ptr(),
238                        None => {
239                            let decl = self.declarations.get_data_decl(data_id);
240                            (&decl.name, decl.linkage)
241                        }
242                    }
243                };
244                let name = name
245                    .as_ref()
246                    .expect("anonymous symbol must be defined locally");
247                if let Some(ptr) = self.lookup_symbol(name) {
248                    ptr
249                } else if linkage == Linkage::Preemptible {
250                    0 as *const u8
251                } else {
252                    panic!("can't resolve symbol {name}");
253                }
254            }
255            ModuleRelocTarget::LibCall(libcall) => {
256                let sym = (self.libcall_names)(*libcall);
257                self.lookup_symbol(&sym)
258                    .unwrap_or_else(|| panic!("can't resolve libcall {sym}"))
259            }
260            ModuleRelocTarget::FunctionOffset(func_id, offset) => {
261                match &self.compiled_functions[*func_id] {
262                    Some(compiled) => return compiled.ptr().wrapping_add(*offset as usize),
263                    None => todo!(),
264                }
265            }
266            name => panic!("invalid name {name:?}"),
267        }
268    }
269
270    /// Returns the address of a finalized function.
271    ///
272    /// The pointer remains valid until either [`JITModule::free_memory`] is called or in the future
273    /// some way of deallocating this individual function is used.
274    pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
275        let info = &self.compiled_functions[func_id];
276        assert!(
277            !self.functions_to_finalize.iter().any(|x| *x == func_id),
278            "function not yet finalized"
279        );
280        info.as_ref()
281            .expect("function must be compiled before it can be finalized")
282            .ptr()
283    }
284
285    /// Returns the address and size of a finalized data object.
286    ///
287    /// The pointer remains valid until either [`JITModule::free_memory`] is called or in the future
288    /// some way of deallocating this individual data object is used.
289    pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
290        let info = &self.compiled_data_objects[data_id];
291        assert!(
292            !self.data_objects_to_finalize.iter().any(|x| *x == data_id),
293            "data object not yet finalized"
294        );
295        let compiled = info
296            .as_ref()
297            .expect("data object must be compiled before it can be finalized");
298
299        (compiled.ptr(), compiled.size())
300    }
301
302    fn record_function_for_perf(&self, ptr: *const u8, size: usize, name: &str) {
303        // The Linux perf tool supports JIT code via a /tmp/perf-$PID.map file,
304        // which contains memory regions and their associated names.  If we
305        // are profiling with perf and saving binaries to PERF_BUILDID_DIR
306        // for post-profile analysis, write information about each function
307        // we define.
308        if cfg!(unix) && ::std::env::var_os("PERF_BUILDID_DIR").is_some() {
309            let mut map_file = ::std::fs::OpenOptions::new()
310                .create(true)
311                .append(true)
312                .open(format!("/tmp/perf-{}.map", ::std::process::id()))
313                .unwrap();
314
315            let _ = writeln!(map_file, "{:x} {:x} {}", ptr as usize, size, name);
316        }
317    }
318
319    /// Finalize all functions and data objects that are defined but not yet finalized.
320    /// All symbols referenced in their bodies that are declared as needing a definition
321    /// must be defined by this point.
322    ///
323    /// Use `get_finalized_function` and `get_finalized_data` to obtain the final
324    /// artifacts.
325    ///
326    /// Returns ModuleError in case of allocation or syscall failure
327    pub fn finalize_definitions(&mut self) -> ModuleResult<()> {
328        for func in std::mem::take(&mut self.functions_to_finalize) {
329            let decl = self.declarations.get_function_decl(func);
330            assert!(decl.linkage.is_definable());
331            let func = self.compiled_functions[func]
332                .as_ref()
333                .expect("function must be compiled before it can be finalized");
334            func.perform_relocations(|name| self.get_address(name));
335        }
336
337        for data in std::mem::take(&mut self.data_objects_to_finalize) {
338            let decl = self.declarations.get_data_decl(data);
339            assert!(decl.linkage.is_definable());
340            let data = self.compiled_data_objects[data]
341                .as_ref()
342                .expect("data object must be compiled before it can be finalized");
343            data.perform_relocations(|name| self.get_address(name));
344        }
345
346        // Now that we're done patching, prepare the memory for execution!
347        let branch_protection = if cfg!(target_arch = "aarch64") && use_bti(&self.isa.isa_flags()) {
348            BranchProtection::BTI
349        } else {
350            BranchProtection::None
351        };
352        self.memory.finalize(branch_protection)?;
353
354        Ok(())
355    }
356
357    /// Create a new `JITModule`.
358    pub fn new(builder: JITBuilder) -> Self {
359        assert!(
360            !builder.isa.flags().is_pic(),
361            "cranelift-jit needs is_pic=false"
362        );
363
364        let memory = builder
365            .memory
366            .unwrap_or_else(|| Box::new(SystemMemoryProvider::new()));
367        Self {
368            isa: builder.isa,
369            symbols: RefCell::new(builder.symbols),
370            lookup_symbols: builder.lookup_symbols,
371            libcall_names: builder.libcall_names,
372            memory,
373            declarations: ModuleDeclarations::default(),
374            compiled_functions: SecondaryMap::new(),
375            compiled_data_objects: SecondaryMap::new(),
376            code_ranges: BTreeMap::new(),
377            functions_to_finalize: Vec::new(),
378            data_objects_to_finalize: Vec::new(),
379        }
380    }
381
382    /// Look up the Wasmtime unwind ExceptionTable and corresponding
383    /// base PC, if any, for a given PC that may be within one of the
384    /// CompiledBlobs in this module.
385    #[cfg(feature = "wasmtime-unwinder")]
386    pub fn lookup_wasmtime_exception_data<'a>(
387        &'a self,
388        pc: usize,
389    ) -> Option<(usize, wasmtime_unwinder::ExceptionTable<'a>)> {
390        let (&start, &(end, func)) = self.code_ranges.range(..=pc).next_back()?;
391        if pc >= end {
392            return None;
393        }
394
395        // Get the ExceptionTable. The "parse" here simply reads two
396        // u32s for lengths and constructs borrowed slices, so it's
397        // cheap.
398        let data = self.compiled_functions[func]
399            .as_ref()
400            .unwrap()
401            .wasmtime_exception_data()?;
402        let exception_table = wasmtime_unwinder::ExceptionTable::parse(data).ok()?;
403        Some((start, exception_table))
404    }
405}
406
407impl Module for JITModule {
408    fn isa(&self) -> &dyn TargetIsa {
409        &*self.isa
410    }
411
412    fn declarations(&self) -> &ModuleDeclarations {
413        &self.declarations
414    }
415
416    fn declare_function(
417        &mut self,
418        name: &str,
419        linkage: Linkage,
420        signature: &ir::Signature,
421    ) -> ModuleResult<FuncId> {
422        let (id, _linkage) = self
423            .declarations
424            .declare_function(name, linkage, signature)?;
425        Ok(id)
426    }
427
428    fn declare_anonymous_function(&mut self, signature: &ir::Signature) -> ModuleResult<FuncId> {
429        let id = self.declarations.declare_anonymous_function(signature)?;
430        Ok(id)
431    }
432
433    fn declare_data(
434        &mut self,
435        name: &str,
436        linkage: Linkage,
437        writable: bool,
438        tls: bool,
439    ) -> ModuleResult<DataId> {
440        assert!(!tls, "JIT doesn't yet support TLS");
441        let (id, _linkage) = self
442            .declarations
443            .declare_data(name, linkage, writable, tls)?;
444        Ok(id)
445    }
446
447    fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> {
448        assert!(!tls, "JIT doesn't yet support TLS");
449        let id = self.declarations.declare_anonymous_data(writable, tls)?;
450        Ok(id)
451    }
452
453    fn define_function_with_control_plane(
454        &mut self,
455        id: FuncId,
456        ctx: &mut cranelift_codegen::Context,
457        ctrl_plane: &mut ControlPlane,
458    ) -> ModuleResult<()> {
459        info!("defining function {}: {}", id, ctx.func.display());
460        let decl = self.declarations.get_function_decl(id);
461        if !decl.linkage.is_definable() {
462            return Err(ModuleError::InvalidImportDefinition(
463                decl.linkage_name(id).into_owned(),
464            ));
465        }
466
467        if !self.compiled_functions[id].is_none() {
468            return Err(ModuleError::DuplicateDefinition(
469                decl.linkage_name(id).into_owned(),
470            ));
471        }
472
473        // work around borrow-checker to allow reuse of ctx below
474        let res = ctx.compile(self.isa(), ctrl_plane)?;
475        let alignment = res.buffer.alignment as u64;
476        let compiled_code = ctx.compiled_code().unwrap();
477
478        let align = alignment
479            .max(self.isa.function_alignment().minimum as u64)
480            .max(self.isa.symbol_alignment());
481
482        let relocs = compiled_code
483            .buffer
484            .relocs()
485            .iter()
486            .map(|reloc| ModuleReloc::from_mach_reloc(reloc, &ctx.func, id))
487            .collect();
488
489        #[cfg(feature = "wasmtime-unwinder")]
490        let wasmtime_exception_data = {
491            let mut exception_builder = wasmtime_unwinder::ExceptionTableBuilder::default();
492            exception_builder
493                .add_func(0, compiled_code.buffer.call_sites())
494                .map_err(|_| {
495                    ModuleError::Compilation(cranelift_codegen::CodegenError::Unsupported(
496                        "Invalid exception data".into(),
497                    ))
498                })?;
499            Some(exception_builder.to_vec())
500        };
501
502        let blob = self.compiled_functions[id].insert(CompiledBlob::new(
503            &mut *self.memory,
504            compiled_code.code_buffer(),
505            align,
506            relocs,
507            #[cfg(feature = "wasmtime-unwinder")]
508            wasmtime_exception_data,
509            JITMemoryKind::Executable,
510        )?);
511        let (ptr, size) = (blob.ptr(), blob.size());
512        self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
513
514        let range_start = ptr.addr();
515        let range_end = range_start + size;
516        self.code_ranges.insert(range_start, (range_end, id));
517
518        self.functions_to_finalize.push(id);
519
520        Ok(())
521    }
522
523    fn define_function_bytes(
524        &mut self,
525        id: FuncId,
526        alignment: u64,
527        bytes: &[u8],
528        relocs: &[ModuleReloc],
529    ) -> ModuleResult<()> {
530        info!("defining function {id} with bytes");
531        let decl = self.declarations.get_function_decl(id);
532        if !decl.linkage.is_definable() {
533            return Err(ModuleError::InvalidImportDefinition(
534                decl.linkage_name(id).into_owned(),
535            ));
536        }
537
538        if !self.compiled_functions[id].is_none() {
539            return Err(ModuleError::DuplicateDefinition(
540                decl.linkage_name(id).into_owned(),
541            ));
542        }
543
544        let align = alignment
545            .max(self.isa.function_alignment().minimum as u64)
546            .max(self.isa.symbol_alignment());
547
548        let blob = self.compiled_functions[id].insert(CompiledBlob::new(
549            &mut *self.memory,
550            bytes,
551            align,
552            relocs.to_owned(),
553            #[cfg(feature = "wasmtime-unwinder")]
554            None,
555            JITMemoryKind::Executable,
556        )?);
557        let (ptr, size) = (blob.ptr(), blob.size());
558        self.record_function_for_perf(ptr, size, &decl.linkage_name(id));
559
560        self.functions_to_finalize.push(id);
561
562        Ok(())
563    }
564
565    fn define_data(&mut self, id: DataId, data: &DataDescription) -> ModuleResult<()> {
566        let decl = self.declarations.get_data_decl(id);
567        if !decl.linkage.is_definable() {
568            return Err(ModuleError::InvalidImportDefinition(
569                decl.linkage_name(id).into_owned(),
570            ));
571        }
572
573        if !self.compiled_data_objects[id].is_none() {
574            return Err(ModuleError::DuplicateDefinition(
575                decl.linkage_name(id).into_owned(),
576            ));
577        }
578
579        assert!(!decl.tls, "JIT doesn't yet support TLS");
580
581        let &DataDescription {
582            ref init,
583            function_decls: _,
584            data_decls: _,
585            function_relocs: _,
586            data_relocs: _,
587            custom_segment_section: _,
588            align,
589            used: _,
590        } = data;
591
592        let (align, kind) = if decl.writable {
593            (
594                align.unwrap_or(WRITABLE_DATA_ALIGNMENT),
595                JITMemoryKind::Writable,
596            )
597        } else {
598            (
599                align.unwrap_or(READONLY_DATA_ALIGNMENT),
600                JITMemoryKind::ReadOnly,
601            )
602        };
603
604        let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
605            PointerWidth::U16 => panic!(),
606            PointerWidth::U32 => Reloc::Abs4,
607            PointerWidth::U64 => Reloc::Abs8,
608        };
609        let relocs = data.all_relocs(pointer_reloc).collect::<Vec<_>>();
610
611        self.compiled_data_objects[id] = Some(match *init {
612            Init::Uninitialized => {
613                panic!("data is not initialized yet");
614            }
615            Init::Zeros { size } => CompiledBlob::new_zeroed(
616                &mut *self.memory,
617                size.max(1),
618                align,
619                relocs,
620                #[cfg(feature = "wasmtime-unwinder")]
621                None,
622                kind,
623            )?,
624            Init::Bytes { ref contents } => CompiledBlob::new(
625                &mut *self.memory,
626                if contents.is_empty() {
627                    // Make sure to allocate at least 1 byte. Allocating 0 bytes is UB. Previously
628                    // a dummy value was used, however as it turns out this will cause pc-relative
629                    // relocations to fail on architectures where pc-relative offsets are range
630                    // restricted as the dummy value is not close enough to the code that has the
631                    // pc-relative relocation.
632                    &[0]
633                } else {
634                    &contents[..]
635                },
636                align,
637                relocs,
638                #[cfg(feature = "wasmtime-unwinder")]
639                None,
640                kind,
641            )?,
642        });
643
644        self.data_objects_to_finalize.push(id);
645
646        Ok(())
647    }
648}
649
650#[cfg(not(windows))]
651fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
652    let c_str = CString::new(name).unwrap();
653    let c_str_ptr = c_str.as_ptr();
654    let sym = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c_str_ptr) };
655    if sym.is_null() {
656        None
657    } else {
658        Some(sym as *const u8)
659    }
660}
661
662#[cfg(windows)]
663fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
664    use std::os::windows::io::RawHandle;
665    use std::ptr;
666    use windows_sys::Win32::Foundation::HMODULE;
667    use windows_sys::Win32::System::LibraryLoader;
668
669    const UCRTBASE: &[u8] = b"ucrtbase.dll\0";
670
671    let c_str = CString::new(name).unwrap();
672    let c_str_ptr = c_str.as_ptr();
673
674    unsafe {
675        let handles = [
676            // try to find the searched symbol in the currently running executable
677            ptr::null_mut(),
678            // try to find the searched symbol in local c runtime
679            LibraryLoader::GetModuleHandleA(UCRTBASE.as_ptr()) as RawHandle,
680        ];
681
682        for handle in &handles {
683            let addr = LibraryLoader::GetProcAddress(*handle as HMODULE, c_str_ptr.cast());
684            match addr {
685                None => continue,
686                Some(addr) => return Some(addr as *const u8),
687            }
688        }
689
690        None
691    }
692}
693
694fn use_bti(isa_flags: &Vec<settings::Value>) -> bool {
695    isa_flags
696        .iter()
697        .find(|&f| f.name == "use_bti")
698        .map_or(false, |f| f.as_bool().unwrap_or(false))
699}
700
701const _ASSERT_JIT_MODULE_IS_SEND: () = {
702    const fn assert_is_send<T: Send>() {}
703    assert_is_send::<JITModule>();
704};