#[deny(missing_docs)]
pub mod error_handling;
use libc::c_char;
#[llvm_versions(16.0..=latest)]
use llvm_sys::core::LLVMGetVersion;
use llvm_sys::core::{LLVMCreateMessage, LLVMDisposeMessage};
use llvm_sys::error_handling::LLVMEnablePrettyStackTrace;
use llvm_sys::support::{LLVMLoadLibraryPermanently, LLVMSearchForAddressOfSymbol};
use std::borrow::Cow;
use std::error::Error;
use std::ffi::{CStr, CString};
use std::fmt::{self, Debug, Display, Formatter};
use std::ops::Deref;
use std::path::Path;
#[derive(Eq)]
pub struct LLVMString {
pub(crate) ptr: *const c_char,
}
impl LLVMString {
pub(crate) unsafe fn new(ptr: *const c_char) -> Self {
LLVMString { ptr }
}
pub fn to_string(&self) -> String {
(*self).to_string_lossy().into_owned()
}
pub(crate) fn create_from_c_str(string: &CStr) -> LLVMString {
unsafe { LLVMString::new(LLVMCreateMessage(string.as_ptr() as *const _)) }
}
pub(crate) fn create_from_str(string: &str) -> LLVMString {
debug_assert_eq!(string.as_bytes()[string.as_bytes().len() - 1], 0);
unsafe { LLVMString::new(LLVMCreateMessage(string.as_ptr() as *const _)) }
}
}
impl Deref for LLVMString {
type Target = CStr;
fn deref(&self) -> &Self::Target {
unsafe { CStr::from_ptr(self.ptr) }
}
}
impl Debug for LLVMString {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?}", self.deref())
}
}
impl Display for LLVMString {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
write!(f, "{:?}", self.deref())
}
}
impl PartialEq for LLVMString {
fn eq(&self, other: &LLVMString) -> bool {
**self == **other
}
}
impl Error for LLVMString {
fn description(&self) -> &str {
self.to_str()
.expect("Could not convert LLVMString to str (likely invalid unicode)")
}
fn cause(&self) -> Option<&dyn Error> {
None
}
}
impl Drop for LLVMString {
fn drop(&mut self) {
unsafe {
LLVMDisposeMessage(self.ptr as *mut _);
}
}
}
#[derive(Eq)]
pub(crate) enum LLVMStringOrRaw {
Owned(LLVMString),
Borrowed(*const c_char),
}
impl LLVMStringOrRaw {
pub fn as_str(&self) -> &CStr {
match self {
LLVMStringOrRaw::Owned(llvm_string) => llvm_string.deref(),
LLVMStringOrRaw::Borrowed(ptr) => unsafe { CStr::from_ptr(*ptr) },
}
}
}
impl PartialEq for LLVMStringOrRaw {
fn eq(&self, other: &LLVMStringOrRaw) -> bool {
self.as_str() == other.as_str()
}
}
pub unsafe fn shutdown_llvm() {
use llvm_sys::core::LLVMShutdown;
LLVMShutdown()
}
#[llvm_versions(16.0..=latest)]
pub fn get_llvm_version() -> (u32, u32, u32) {
let mut major: u32 = 0;
let mut minor: u32 = 0;
let mut patch: u32 = 0;
unsafe { LLVMGetVersion(&mut major, &mut minor, &mut patch) };
return (major, minor, patch);
}
#[derive(thiserror::Error, Debug, PartialEq, Eq, Clone, Copy)]
pub enum LoadLibraryError {
#[error("The given path could not be converted to a `&str`")]
UnicodeError,
#[error("The given path could not be loaded as a library")]
LoadingError,
}
pub fn load_library_permanently(path: &Path) -> Result<(), LoadLibraryError> {
let filename = to_c_str(path.to_str().ok_or(LoadLibraryError::UnicodeError)?);
let error = unsafe { LLVMLoadLibraryPermanently(filename.as_ptr()) == 1 };
if error {
return Err(LoadLibraryError::LoadingError);
}
Ok(())
}
#[test]
fn test_load_library_permanently() {
assert_eq!(
load_library_permanently(Path::new("missing.dll")),
Err(LoadLibraryError::LoadingError)
);
}
pub fn load_visible_symbols() {
unsafe { LLVMLoadLibraryPermanently(std::ptr::null()) };
}
pub fn search_for_address_of_symbol(symbol: &str) -> Option<usize> {
let symbol = to_c_str(symbol);
let address = unsafe { LLVMSearchForAddressOfSymbol(symbol.as_ptr()) };
if address.is_null() {
return None;
}
return Some(address as usize);
}
#[test]
fn test_load_visible_symbols() {
assert!(search_for_address_of_symbol("malloc").is_none());
load_visible_symbols();
assert!(search_for_address_of_symbol("malloc").is_some());
}
pub fn is_multithreaded() -> bool {
use llvm_sys::core::LLVMIsMultithreaded;
unsafe { LLVMIsMultithreaded() == 1 }
}
pub fn enable_llvm_pretty_stack_trace() {
unsafe { LLVMEnablePrettyStackTrace() }
}
pub(crate) fn to_c_str<'s>(mut s: &'s str) -> Cow<'s, CStr> {
if s.is_empty() {
s = "\0";
}
if !s.chars().rev().any(|ch| ch == '\0') {
return Cow::from(CString::new(s).expect("unreachable since null bytes are checked"));
}
unsafe { Cow::from(CStr::from_ptr(s.as_ptr() as *const _)) }
}
#[test]
fn test_to_c_str() {
assert!(matches!(to_c_str("my string"), Cow::Owned(_)));
assert!(matches!(to_c_str("my string\0"), Cow::Borrowed(_)));
}