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#[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 #[allow(clippy::inherent_to_string_shadow_display)]
35 pub fn to_string(&self) -> String {
36 (*self).to_string_lossy().into_owned()
37 }
38
39 pub(crate) fn create_from_c_str(string: &CStr) -> LLVMString {
41 unsafe { LLVMString::new(LLVMCreateMessage(string.as_ptr() as *const _)) }
42 }
43
44 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#[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
122pub unsafe fn shutdown_llvm() {
125 use llvm_sys::core::LLVMShutdown;
126
127 LLVMShutdown()
128}
129
130#[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#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone, Copy)]
144pub enum LoadLibraryError {
145 #[error("The given path could not be converted to a `&str`")]
147 UnicodeError,
148 #[error("The given path could not be loaded as a library")]
150 LoadingError,
151}
152
153pub 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
175pub fn load_visible_symbols() {
177 unsafe { LLVMLoadLibraryPermanently(std::ptr::null()) };
178}
179
180pub 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
200pub 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
212pub(crate) fn to_c_str(mut s: &str) -> Cow<'_, CStr> {
218 if s.is_empty() {
219 s = "\0";
220 }
221
222 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}