Skip to main content

inkwell/support/
mod.rs

1#[deny(missing_docs)]
2pub mod error_handling;
3
4use libc::c_char;
5#[llvm_versions(16..)]
6use llvm_sys::core::LLVMGetVersion;
7use llvm_sys::core::{LLVMCreateMessage, LLVMDisposeMessage};
8use llvm_sys::error_handling::LLVMEnablePrettyStackTrace;
9use llvm_sys::support::{LLVMLoadLibraryPermanently, LLVMSearchForAddressOfSymbol};
10
11use std::borrow::Cow;
12use std::error::Error;
13use std::ffi::{CStr, CString};
14use std::fmt::{self, Debug, Display, Formatter};
15use std::ops::Deref;
16use std::path::Path;
17
18/// An owned LLVM String. Also known as a LLVM Message
19#[derive(Eq)]
20pub struct LLVMString {
21    pub(crate) ptr: *const c_char,
22}
23
24impl LLVMString {
25    pub(crate) unsafe fn new(ptr: *const c_char) -> Self {
26        LLVMString { ptr }
27    }
28
29    /// This is a convenience method for creating a Rust `String`,
30    /// however; it *will* reallocate. `LLVMString` should be used
31    /// as much as possible to save memory since it is allocated by
32    /// LLVM. It's essentially a `CString` with a custom LLVM
33    /// deallocator
34    #[allow(clippy::inherent_to_string_shadow_display)]
35    pub fn to_string(&self) -> String {
36        (*self).to_string_lossy().into_owned()
37    }
38
39    /// This method will allocate a c string through LLVM
40    pub(crate) fn create_from_c_str(string: &CStr) -> LLVMString {
41        unsafe { LLVMString::new(LLVMCreateMessage(string.as_ptr() as *const _)) }
42    }
43
44    /// This method will allocate a c string through LLVM
45    pub(crate) fn create_from_str(string: &str) -> LLVMString {
46        debug_assert_eq!(string.as_bytes()[string.len() - 1], 0);
47
48        unsafe { LLVMString::new(LLVMCreateMessage(string.as_ptr() as *const _)) }
49    }
50}
51
52impl Deref for LLVMString {
53    type Target = CStr;
54
55    fn deref(&self) -> &Self::Target {
56        unsafe { CStr::from_ptr(self.ptr) }
57    }
58}
59
60impl Debug for LLVMString {
61    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
62        write!(f, "{:?}", self.deref())
63    }
64}
65
66impl Display for LLVMString {
67    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
68        write!(f, "{:?}", self.deref())
69    }
70}
71
72impl PartialEq for LLVMString {
73    fn eq(&self, other: &LLVMString) -> bool {
74        **self == **other
75    }
76}
77
78impl Error for LLVMString {
79    fn description(&self) -> &str {
80        self.to_str()
81            .expect("Could not convert LLVMString to str (likely invalid unicode)")
82    }
83
84    fn cause(&self) -> Option<&dyn Error> {
85        None
86    }
87}
88
89impl Drop for LLVMString {
90    fn drop(&mut self) {
91        unsafe {
92            LLVMDisposeMessage(self.ptr as *mut _);
93        }
94    }
95}
96
97// Similar to Cow; however does not provide ability to clone
98// since memory is allocated by LLVM. Could use a better name
99// too. This is meant to be an internal wrapper only. Maybe
100// belongs in a private utils module.
101#[derive(Eq)]
102pub(crate) enum LLVMStringOrRaw {
103    Owned(LLVMString),
104    Borrowed(*const c_char),
105}
106
107impl LLVMStringOrRaw {
108    pub fn as_str(&self) -> &CStr {
109        match self {
110            LLVMStringOrRaw::Owned(llvm_string) => llvm_string.deref(),
111            LLVMStringOrRaw::Borrowed(ptr) => unsafe { CStr::from_ptr(*ptr) },
112        }
113    }
114}
115
116impl PartialEq for LLVMStringOrRaw {
117    fn eq(&self, other: &LLVMStringOrRaw) -> bool {
118        self.as_str() == other.as_str()
119    }
120}
121
122/// This function is very unsafe. Any reference to LLVM data after this function is called will likely segfault.
123/// Probably only ever useful to call before your program ends. Might not even be absolutely necessary.
124pub unsafe fn shutdown_llvm() {
125    use llvm_sys::core::LLVMShutdown;
126
127    LLVMShutdown()
128}
129
130/// Returns the major, minor, and patch version of the LLVM in use
131#[llvm_versions(16..)]
132pub fn get_llvm_version() -> (u32, u32, u32) {
133    let mut major: u32 = 0;
134    let mut minor: u32 = 0;
135    let mut patch: u32 = 0;
136
137    unsafe { LLVMGetVersion(&mut major, &mut minor, &mut patch) };
138
139    (major, minor, patch)
140}
141
142/// Possible errors that can occur when loading a library
143#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone, Copy)]
144pub enum LoadLibraryError {
145    /// The given path could not be converted to a [`&str`]
146    #[error("The given path could not be converted to a `&str`")]
147    UnicodeError,
148    /// The given path could not be loaded as a library
149    #[error("The given path could not be loaded as a library")]
150    LoadingError,
151}
152
153/// Permanently load the dynamic library at the given `path`.
154///
155/// It is safe to call this function multiple times for the same library.
156pub fn load_library_permanently(path: &Path) -> Result<(), LoadLibraryError> {
157    let filename = to_c_str(path.to_str().ok_or(LoadLibraryError::UnicodeError)?);
158
159    let error = unsafe { LLVMLoadLibraryPermanently(filename.as_ptr()) == 1 };
160    if error {
161        return Err(LoadLibraryError::LoadingError);
162    }
163
164    Ok(())
165}
166
167#[test]
168fn test_load_library_permanently() {
169    assert_eq!(
170        load_library_permanently(Path::new("missing.dll")),
171        Err(LoadLibraryError::LoadingError)
172    );
173}
174
175/// Permanently loads all the symbols visible inside the current program
176pub fn load_visible_symbols() {
177    unsafe { LLVMLoadLibraryPermanently(std::ptr::null()) };
178}
179
180/// Search through all previously loaded dynamic libraries for `symbol`.
181///
182/// Returns an address of the symbol, if found
183pub fn search_for_address_of_symbol(symbol: &str) -> Option<usize> {
184    let symbol = to_c_str(symbol);
185
186    let address = unsafe { LLVMSearchForAddressOfSymbol(symbol.as_ptr()) };
187    if address.is_null() {
188        return None;
189    }
190    Some(address as usize)
191}
192
193#[test]
194fn test_load_visible_symbols() {
195    assert!(search_for_address_of_symbol("malloc").is_none());
196    load_visible_symbols();
197    assert!(search_for_address_of_symbol("malloc").is_some());
198}
199
200/// Determines whether or not LLVM has been configured to run in multithreaded mode. (Inkwell currently does
201/// not officially support multithreaded mode)
202pub fn is_multithreaded() -> bool {
203    use llvm_sys::core::LLVMIsMultithreaded;
204
205    unsafe { LLVMIsMultithreaded() == 1 }
206}
207
208pub fn enable_llvm_pretty_stack_trace() {
209    unsafe { LLVMEnablePrettyStackTrace() }
210}
211
212/// This function takes in a Rust string and either:
213///
214/// A) Finds a terminating null byte in the Rust string and can reference it directly like a C string.
215///
216/// B) Finds no null byte and allocates a new C string based on the input Rust string.
217pub(crate) fn to_c_str(mut s: &str) -> Cow<'_, CStr> {
218    if s.is_empty() {
219        s = "\0";
220    }
221
222    // Start from the end of the string as it's the most likely place to find a null byte
223    if !s.chars().rev().any(|ch| ch == '\0') {
224        return Cow::from(CString::new(s).expect("unreachable since null bytes are checked"));
225    }
226
227    unsafe { Cow::from(CStr::from_ptr(s.as_ptr() as *const _)) }
228}
229
230#[test]
231fn test_to_c_str() {
232    assert!(matches!(to_c_str("my string"), Cow::Owned(_)));
233    assert!(matches!(to_c_str("my string\0"), Cow::Borrowed(_)));
234}