From b7ee37447484c8e6d9ed2e90b19e88a580554e43 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Mon, 26 Aug 2024 18:03:29 +0200 Subject: [PATCH] Provide `LLVMTypeWrapper` trait Instead of keeping raw pointer fields (of types like `LLVMValueRef`, `LLVMMetadataRef`) public and defining constructors with different names, provide the `LLVMTypeWrapper` trait with `from_ptr()` and `as_ptr()` methods. This will allow to convert all safe wrappers from and to raw pointers with one method, which is going to be helpful for building macros for them. --- src/linker.rs | 8 +- src/llvm/di.rs | 34 ++++--- src/llvm/mod.rs | 2 +- src/llvm/types/di.rs | 228 ++++++++++++++++++++++++++++-------------- src/llvm/types/ir.rs | 133 +++++++++++++++--------- src/llvm/types/mod.rs | 20 ++++ 6 files changed, 284 insertions(+), 141 deletions(-) diff --git a/src/linker.rs b/src/linker.rs index 8dd13cee..5f89036b 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -25,7 +25,7 @@ use llvm_sys::{ use thiserror::Error; use tracing::{debug, error, info, warn}; -use crate::llvm; +use crate::llvm::{self, types::LLVMTypeError}; /// Linker error #[derive(Debug, Error)] @@ -77,6 +77,10 @@ pub enum LinkerError { /// The input object file does not have embedded bitcode. #[error("no bitcode section found in {0}")] MissingBitcodeSection(PathBuf), + + /// Interaction with an LLVM type failed. + #[error(transparent)] + LLVMType(#[from] LLVMTypeError), } /// BPF Cpu type @@ -452,7 +456,7 @@ impl Linker { if self.options.btf { // if we want to emit BTF, we need to sanitize the debug information - llvm::DISanitizer::new(self.context, self.module).run(&self.options.export_symbols); + llvm::DISanitizer::new(self.context, self.module).run(&self.options.export_symbols)?; } else { // if we don't need BTF emission, we can strip DI let ok = unsafe { llvm::strip_debug_info(self.module) }; diff --git a/src/llvm/di.rs b/src/llvm/di.rs index c4a939d4..90dc9876 100644 --- a/src/llvm/di.rs +++ b/src/llvm/di.rs @@ -10,11 +10,14 @@ use gimli::{DW_TAG_pointer_type, DW_TAG_structure_type, DW_TAG_variant_part}; use llvm_sys::{core::*, debuginfo::*, prelude::*}; use tracing::{span, trace, warn, Level}; -use super::types::{ - di::DIType, - ir::{Function, MDNode, Metadata, Value}, +use crate::llvm::{ + iter::*, + types::{ + di::{DISubprogram, DIType}, + ir::{Function, MDNode, Metadata, Value}, + LLVMTypeError, LLVMTypeWrapper, + }, }; -use crate::llvm::{iter::*, types::di::DISubprogram}; // KSYM_NAME_LEN from linux kernel intentionally set // to lower value found accross kernel versions to ensure @@ -227,7 +230,7 @@ impl DISanitizer { // An operand with no value is valid and means that the operand is // not set (v, Item::Operand { .. }) if v.is_null() => return, - (v, _) if !v.is_null() => Value::new(v), + (v, _) if !v.is_null() => Value::from_ptr(v).unwrap(), // All other items should have values (_, item) => panic!("{item:?} has no value"), }; @@ -283,10 +286,13 @@ impl DISanitizer { } } - pub fn run(mut self, exported_symbols: &HashSet>) { + pub fn run( + mut self, + exported_symbols: &HashSet>, + ) -> Result<(), LLVMTypeError> { let module = self.module; - self.replace_operands = self.fix_subprogram_linkage(exported_symbols); + self.replace_operands = self.fix_subprogram_linkage(exported_symbols)?; for value in module.globals_iter() { self.visit_item(Item::GlobalVariable(value)); @@ -307,6 +313,8 @@ impl DISanitizer { } unsafe { LLVMDisposeDIBuilder(self.builder) }; + + Ok(()) } // Make it so that only exported symbols (programs marked as #[no_mangle]) get BTF @@ -325,13 +333,13 @@ impl DISanitizer { fn fix_subprogram_linkage( &mut self, export_symbols: &HashSet>, - ) -> HashMap { + ) -> Result, LLVMTypeError> { let mut replace = HashMap::new(); for mut function in self .module .functions_iter() - .map(|value| unsafe { Function::from_value_ref(value) }) + .map(|value| Function::from_ptr(value).unwrap()) { if export_symbols.contains(function.name()) { continue; @@ -370,7 +378,7 @@ impl DISanitizer { // replace retained nodes manually below. LLVMDIBuilderFinalizeSubprogram(self.builder, new_program); - DISubprogram::from_value_ref(LLVMMetadataAsValue(self.context, new_program)) + DISubprogram::from_ptr(LLVMMetadataAsValue(self.context, new_program)).unwrap() }; // Point the function to the new subprogram. @@ -396,13 +404,13 @@ impl DISanitizer { unsafe { LLVMMDNodeInContext2(self.context, core::ptr::null_mut(), 0) }; subprogram.set_retained_nodes(empty_node); - let ret = replace.insert(subprogram.value_ref as u64, unsafe { - LLVMValueAsMetadata(new_program.value_ref) + let ret = replace.insert(subprogram.as_ptr() as u64, unsafe { + LLVMValueAsMetadata(new_program.as_ptr()) }); assert!(ret.is_none()); } - replace + Ok(replace) } } diff --git a/src/llvm/mod.rs b/src/llvm/mod.rs index 86de1b04..23a529f1 100644 --- a/src/llvm/mod.rs +++ b/src/llvm/mod.rs @@ -1,6 +1,6 @@ mod di; mod iter; -mod types; +pub mod types; use std::{ borrow::Cow, diff --git a/src/llvm/types/di.rs b/src/llvm/types/di.rs index df072d1f..51e2364e 100644 --- a/src/llvm/types/di.rs +++ b/src/llvm/types/di.rs @@ -11,14 +11,17 @@ use llvm_sys::{ debuginfo::{ LLVMDIFileGetFilename, LLVMDIFlags, LLVMDIScopeGetFile, LLVMDISubprogramGetLine, LLVMDITypeGetFlags, LLVMDITypeGetLine, LLVMDITypeGetName, LLVMDITypeGetOffsetInBits, - LLVMGetDINodeTag, + LLVMGetDINodeTag, LLVMGetMetadataKind, LLVMMetadataKind, }, prelude::{LLVMContextRef, LLVMMetadataRef, LLVMValueRef}, }; use crate::llvm::{ mdstring_to_str, - types::ir::{MDNode, Metadata}, + types::{ + ir::{MDNode, Metadata}, + LLVMTypeError, LLVMTypeWrapper, + }, }; /// Returns a DWARF tag for the given debug info node. @@ -41,26 +44,33 @@ unsafe fn di_node_tag(metadata_ref: LLVMMetadataRef) -> DwTag { /// A `DIFile` debug info node, which represents a given file, is referenced by /// other debug info nodes which belong to the file. pub struct DIFile<'ctx> { - pub(super) metadata_ref: LLVMMetadataRef, + metadata_ref: LLVMMetadataRef, _marker: PhantomData<&'ctx ()>, } -impl<'ctx> DIFile<'ctx> { - /// Constructs a new [`DIFile`] from the given `metadata`. - /// - /// # Safety - /// - /// This method assumes that the given `metadata` corresponds to a valid - /// instance of [LLVM `DIFile`](https://llvm.org/doxygen/classllvm_1_1DIFile.html). - /// It's the caller's responsibility to ensure this invariant, as this - /// method doesn't perform any validation checks. - pub(crate) unsafe fn from_metadata_ref(metadata_ref: LLVMMetadataRef) -> Self { - Self { +impl<'ctx> LLVMTypeWrapper for DIFile<'ctx> { + type Target = LLVMMetadataRef; + + fn from_ptr(metadata_ref: Self::Target) -> Result { + if metadata_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_kind = unsafe { LLVMGetMetadataKind(metadata_ref) }; + if !matches!(metadata_kind, LLVMMetadataKind::LLVMDIFileMetadataKind) { + return Err(LLVMTypeError::InvalidPointerType("DIFile")); + } + Ok(Self { metadata_ref, _marker: PhantomData, - } + }) } + fn as_ptr(&self) -> Self::Target { + self.metadata_ref + } +} + +impl<'ctx> DIFile<'ctx> { pub fn filename(&self) -> Option<&CStr> { let mut len = 0; // `LLVMDIFileGetName` doesn't allocate any memory, it just returns @@ -109,29 +119,52 @@ unsafe fn di_type_name<'a>(metadata_ref: LLVMMetadataRef) -> Option<&'a CStr> { /// Represents the debug information for a primitive type in LLVM IR. pub struct DIType<'ctx> { - pub(super) metadata_ref: LLVMMetadataRef, - pub(super) value_ref: LLVMValueRef, + metadata_ref: LLVMMetadataRef, + value_ref: LLVMValueRef, _marker: PhantomData<&'ctx ()>, } -impl<'ctx> DIType<'ctx> { - /// Constructs a new [`DIType`] from the given `value`. - /// - /// # Safety - /// - /// This method assumes that the given `value` corresponds to a valid - /// instance of [LLVM `DIType`](https://llvm.org/doxygen/classllvm_1_1DIType.html). - /// It's the caller's responsibility to ensure this invariant, as this - /// method doesn't perform any validation checks. - pub unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self { +impl<'ctx> LLVMTypeWrapper for DIType<'ctx> { + type Target = LLVMValueRef; + + fn from_ptr(value_ref: Self::Target) -> Result { + if value_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) }; - Self { - metadata_ref, - value_ref, - _marker: PhantomData, + if metadata_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_kind = unsafe { LLVMGetMetadataKind(metadata_ref) }; + // The children of `DIType` are: + // + // - `DIBasicType` + // - `DICompositeType` + // - `DIDerivedType` + // - `DIStringType` + // - `DISubroutimeType` + // + // https://llvm.org/doxygen/classllvm_1_1DIType.html + match metadata_kind { + LLVMMetadataKind::LLVMDIBasicTypeMetadataKind + | LLVMMetadataKind::LLVMDICompositeTypeMetadataKind + | LLVMMetadataKind::LLVMDIDerivedTypeMetadataKind + | LLVMMetadataKind::LLVMDIStringTypeMetadataKind + | LLVMMetadataKind::LLVMDISubroutineTypeMetadataKind => Ok(Self { + metadata_ref, + value_ref, + _marker: PhantomData, + }), + _ => Err(LLVMTypeError::InvalidPointerType("DIType")), } } + fn as_ptr(&self) -> Self::Target { + self.value_ref + } +} + +impl<'ctx> DIType<'ctx> { /// Returns the offset of the type in bits. This offset is used in case the /// type is a member of a composite type. pub fn offset_in_bits(&self) -> usize { @@ -141,7 +174,7 @@ impl<'ctx> DIType<'ctx> { impl<'ctx> From> for DIType<'ctx> { fn from(di_derived_type: DIDerivedType) -> Self { - unsafe { Self::from_value_ref(di_derived_type.value_ref) } + Self::from_ptr(di_derived_type.value_ref).unwrap() } } @@ -165,29 +198,42 @@ pub struct DIDerivedType<'ctx> { _marker: PhantomData<&'ctx ()>, } -impl<'ctx> DIDerivedType<'ctx> { - /// Constructs a new [`DIDerivedType`] from the given `value`. - /// - /// # Safety - /// - /// This method assumes that the provided `value` corresponds to a valid - /// instance of [LLVM `DIDerivedType`](https://llvm.org/doxygen/classllvm_1_1DIDerivedType.html). - /// It's the caller's responsibility to ensure this invariant, as this - /// method doesn't perform any validation checks. - pub unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self { - let metadata_ref = LLVMValueAsMetadata(value_ref); - Self { +impl<'ctx> LLVMTypeWrapper for DIDerivedType<'ctx> { + type Target = LLVMValueRef; + + fn from_ptr(value_ref: Self::Target) -> Result { + if value_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) }; + if metadata_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_kind = unsafe { LLVMGetMetadataKind(metadata_ref) }; + if !matches!( + metadata_kind, + LLVMMetadataKind::LLVMDIDerivedTypeMetadataKind, + ) { + return Err(LLVMTypeError::InvalidPointerType("DIDerivedType")); + } + Ok(Self { metadata_ref, value_ref, _marker: PhantomData, - } + }) } + fn as_ptr(&self) -> Self::Target { + self.value_ref + } +} + +impl<'ctx> DIDerivedType<'ctx> { /// Returns the base type of this derived type. pub fn base_type(&self) -> Metadata { unsafe { let value = LLVMGetOperand(self.value_ref, DIDerivedTypeOperand::BaseType as u32); - Metadata::from_value_ref(value) + Metadata::from_value_ref(value).unwrap() } } @@ -226,24 +272,37 @@ pub struct DICompositeType<'ctx> { _marker: PhantomData<&'ctx ()>, } -impl<'ctx> DICompositeType<'ctx> { - /// Constructs a new [`DICompositeType`] from the given `value`. - /// - /// # Safety - /// - /// This method assumes that the provided `value` corresponds to a valid - /// instance of [LLVM `DICompositeType`](https://llvm.org/doxygen/classllvm_1_1DICompositeType.html). - /// It's the caller's responsibility to ensure this invariant, as this - /// method doesn't perform any validation checks. - pub unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self { - let metadata_ref = LLVMValueAsMetadata(value_ref); - Self { +impl<'ctx> LLVMTypeWrapper for DICompositeType<'ctx> { + type Target = LLVMValueRef; + + fn from_ptr(value_ref: Self::Target) -> Result { + if value_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) }; + if metadata_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_kind = unsafe { LLVMGetMetadataKind(metadata_ref) }; + if !matches!( + metadata_kind, + LLVMMetadataKind::LLVMDICompositeTypeMetadataKind, + ) { + return Err(LLVMTypeError::InvalidPointerType("DICompositeType")); + } + Ok(Self { metadata_ref, value_ref, _marker: PhantomData, - } + }) } + fn as_ptr(&self) -> Self::Target { + self.value_ref + } +} + +impl<'ctx> DICompositeType<'ctx> { /// Returns an iterator over elements (struct fields, enum variants, etc.) /// of the composite type. pub fn elements(&self) -> impl Iterator { @@ -253,8 +312,9 @@ impl<'ctx> DICompositeType<'ctx> { .map(|elements| unsafe { LLVMGetNumOperands(elements.as_ptr()) }) .unwrap_or(0); - (0..operands) - .map(move |i| unsafe { Metadata::from_value_ref(LLVMGetOperand(elements, i as u32)) }) + (0..operands).map(move |i| unsafe { + Metadata::from_value_ref(LLVMGetOperand(elements, i as u32)).unwrap() + }) } /// Returns the name of the composite type. @@ -266,7 +326,9 @@ impl<'ctx> DICompositeType<'ctx> { pub fn file(&self) -> DIFile { unsafe { let metadata = LLVMDIScopeGetFile(self.metadata_ref); - DIFile::from_metadata_ref(metadata) + // PANICS: We are sure that the pointer type is correct. There is + // no need to leak the error. + DIFile::from_ptr(metadata).unwrap() } } @@ -289,7 +351,7 @@ impl<'ctx> DICompositeType<'ctx> { LLVMReplaceMDNodeOperandWith( self.value_ref, DICompositeTypeOperand::Elements as u32, - LLVMValueAsMetadata(mdnode.value_ref), + LLVMValueAsMetadata(mdnode.as_ptr()), ) } } @@ -324,26 +386,40 @@ enum DISubprogramOperand { /// Represents the debug information for a subprogram (function) in LLVM IR. pub struct DISubprogram<'ctx> { - pub value_ref: LLVMValueRef, + value_ref: LLVMValueRef, _marker: PhantomData<&'ctx ()>, } -impl<'ctx> DISubprogram<'ctx> { - /// Constructs a new [`DISubprogram`] from the given `value`. - /// - /// # Safety - /// - /// This method assumes that the provided `value` corresponds to a valid - /// instance of [LLVM `DISubprogram`](https://llvm.org/doxygen/classllvm_1_1DISubprogram.html). - /// It's the caller's responsibility to ensure this invariant, as this - /// method doesn't perform any validation checks. - pub(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self { - DISubprogram { +impl<'ctx> LLVMTypeWrapper for DISubprogram<'ctx> { + type Target = LLVMValueRef; + + fn from_ptr(value_ref: Self::Target) -> Result { + if value_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) }; + if metadata_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + let metadata_kind = unsafe { LLVMGetMetadataKind(metadata_ref) }; + if !matches!( + metadata_kind, + LLVMMetadataKind::LLVMDISubprogramMetadataKind, + ) { + return Err(LLVMTypeError::InvalidPointerType("DISubprogram")); + } + Ok(DISubprogram { value_ref, _marker: PhantomData, - } + }) } + fn as_ptr(&self) -> Self::Target { + self.value_ref + } +} + +impl<'ctx> DISubprogram<'ctx> { /// Returns the name of the subprogram. pub fn name(&self) -> Option<&str> { let operand = unsafe { LLVMGetOperand(self.value_ref, DISubprogramOperand::Name as u32) }; diff --git a/src/llvm/types/ir.rs b/src/llvm/types/ir.rs index d3480345..8521cece 100644 --- a/src/llvm/types/ir.rs +++ b/src/llvm/types/ir.rs @@ -22,7 +22,10 @@ use llvm_sys::{ use crate::llvm::{ iter::IterBasicBlocks as _, symbol_name, - types::di::{DICompositeType, DIDerivedType, DISubprogram, DIType}, + types::{ + di::{DICompositeType, DIDerivedType, DISubprogram, DIType}, + LLVMTypeError, LLVMTypeWrapper, + }, Message, }; @@ -74,17 +77,29 @@ impl<'ctx> std::fmt::Debug for Value<'ctx> { } } -impl<'ctx> Value<'ctx> { - pub fn new(value: LLVMValueRef) -> Self { - if unsafe { !LLVMIsAMDNode(value).is_null() } { - let mdnode = unsafe { MDNode::from_value_ref(value) }; - return Value::MDNode(mdnode); - } else if unsafe { !LLVMIsAFunction(value).is_null() } { - return Value::Function(unsafe { Function::from_value_ref(value) }); +impl<'ctx> LLVMTypeWrapper for Value<'ctx> { + type Target = LLVMValueRef; + + fn from_ptr(value_ref: Self::Target) -> Result { + if unsafe { !LLVMIsAMDNode(value_ref).is_null() } { + let mdnode = MDNode::from_ptr(value_ref)?; + return Ok(Value::MDNode(mdnode)); + } else if unsafe { !LLVMIsAFunction(value_ref).is_null() } { + return Ok(Value::Function(Function::from_ptr(value_ref)?)); + } + Ok(Value::Other(value_ref)) + } + + fn as_ptr(&self) -> Self::Target { + match self { + Value::MDNode(mdnode) => mdnode.as_ptr(), + Value::Function(f) => f.as_ptr(), + Value::Other(value_ref) => *value_ref, } - Value::Other(value) } +} +impl<'ctx> Value<'ctx> { pub fn metadata_entries(&self) -> Option { let value = match self { Value::MDNode(node) => node.value_ref, @@ -124,21 +139,21 @@ impl<'ctx> Metadata<'ctx> { /// instance of [LLVM `Metadata`](https://llvm.org/doxygen/classllvm_1_1Metadata.html). /// It's the caller's responsibility to ensure this invariant, as this /// method doesn't perform any valiation checks. - pub(crate) unsafe fn from_value_ref(value: LLVMValueRef) -> Self { - let metadata = LLVMValueAsMetadata(value); + pub(crate) fn from_value_ref(value: LLVMValueRef) -> Result { + let metadata = unsafe { LLVMValueAsMetadata(value) }; match unsafe { LLVMGetMetadataKind(metadata) } { LLVMMetadataKind::LLVMDICompositeTypeMetadataKind => { - let di_composite_type = unsafe { DICompositeType::from_value_ref(value) }; - Metadata::DICompositeType(di_composite_type) + let di_composite_type = DICompositeType::from_ptr(value)?; + Ok(Metadata::DICompositeType(di_composite_type)) } LLVMMetadataKind::LLVMDIDerivedTypeMetadataKind => { - let di_derived_type = unsafe { DIDerivedType::from_value_ref(value) }; - Metadata::DIDerivedType(di_derived_type) + let di_derived_type = DIDerivedType::from_ptr(value)?; + Ok(Metadata::DIDerivedType(di_derived_type)) } LLVMMetadataKind::LLVMDISubprogramMetadataKind => { - let di_subprogram = unsafe { DISubprogram::from_value_ref(value) }; - Metadata::DISubprogram(di_subprogram) + let di_subprogram = DISubprogram::from_ptr(value)?; + Ok(Metadata::DISubprogram(di_subprogram)) } LLVMMetadataKind::LLVMDIGlobalVariableMetadataKind | LLVMMetadataKind::LLVMDICommonBlockMetadataKind @@ -172,27 +187,48 @@ impl<'ctx> Metadata<'ctx> { | LLVMMetadataKind::LLVMDIStringTypeMetadataKind | LLVMMetadataKind::LLVMDIGenericSubrangeMetadataKind | LLVMMetadataKind::LLVMDIArgListMetadataKind - | LLVMMetadataKind::LLVMDIAssignIDMetadataKind => Metadata::Other(value), + | LLVMMetadataKind::LLVMDIAssignIDMetadataKind => Ok(Metadata::Other(value)), } } } impl<'ctx> TryFrom> for Metadata<'ctx> { - type Error = (); + type Error = LLVMTypeError; fn try_from(md_node: MDNode) -> Result { // FIXME: fail if md_node isn't a Metadata node - Ok(unsafe { Self::from_value_ref(md_node.value_ref) }) + Self::from_value_ref(md_node.value_ref) } } /// Represents a metadata node. #[derive(Clone)] pub struct MDNode<'ctx> { - pub(super) value_ref: LLVMValueRef, + value_ref: LLVMValueRef, _marker: PhantomData<&'ctx ()>, } +impl<'ctx> LLVMTypeWrapper for MDNode<'ctx> { + type Target = LLVMValueRef; + + fn from_ptr(value_ref: Self::Target) -> Result { + if value_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + if unsafe { LLVMIsAMDNode(value_ref).is_null() } { + return Err(LLVMTypeError::InvalidPointerType("MDNode")); + } + Ok(Self { + value_ref, + _marker: PhantomData, + }) + } + + fn as_ptr(&self) -> Self::Target { + self.value_ref + } +} + impl<'ctx> MDNode<'ctx> { /// Constructs a new [`MDNode`] from the given `metadata`. /// @@ -205,10 +241,11 @@ impl<'ctx> MDNode<'ctx> { pub(crate) unsafe fn from_metadata_ref( context: LLVMContextRef, metadata: LLVMMetadataRef, - ) -> Self { - MDNode::from_value_ref(LLVMMetadataAsValue(context, metadata)) + ) -> Result { + MDNode::from_ptr(LLVMMetadataAsValue(context, metadata)) } + /// Constructs an empty metadata node. /// Constructs a new [`MDNode`] from the given `value`. /// /// # Safety @@ -217,17 +254,9 @@ impl<'ctx> MDNode<'ctx> { /// instance of [LLVM `MDNode`](https://llvm.org/doxygen/classllvm_1_1MDNode.html). /// It's the caller's responsibility to ensure this invariant, as this /// method doesn't perform any valiation checks. - pub(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self { - Self { - value_ref, - _marker: PhantomData, - } - } - - /// Constructs an empty metadata node. pub fn empty(context: LLVMContextRef) -> Self { let metadata = unsafe { LLVMMDNodeInContext2(context, core::ptr::null_mut(), 0) }; - unsafe { Self::from_metadata_ref(context, metadata) } + unsafe { Self::from_metadata_ref(context, metadata).unwrap() } } /// Constructs a new metadata node from an array of [`DIType`] elements. @@ -239,7 +268,7 @@ impl<'ctx> MDNode<'ctx> { let metadata = unsafe { let mut elements: Vec = elements .iter() - .map(|di_type| LLVMValueAsMetadata(di_type.value_ref)) + .map(|di_type| LLVMValueAsMetadata(di_type.as_ptr())) .collect(); LLVMMDNodeInContext2( context, @@ -247,7 +276,7 @@ impl<'ctx> MDNode<'ctx> { elements.len(), ) }; - unsafe { Self::from_metadata_ref(context, metadata) } + unsafe { Self::from_metadata_ref(context, metadata).unwrap() } } } @@ -292,26 +321,32 @@ impl Drop for MetadataEntries { /// Represents a metadata node. #[derive(Clone)] pub struct Function<'ctx> { - pub value_ref: LLVMValueRef, + value_ref: LLVMValueRef, _marker: PhantomData<&'ctx ()>, } -impl<'ctx> Function<'ctx> { - /// Constructs a new [`Function`] from the given `value`. - /// - /// # Safety - /// - /// This method assumes that the provided `value` corresponds to a valid - /// instance of [LLVM `Function`](https://llvm.org/doxygen/classllvm_1_1Function.html). - /// It's the caller's responsibility to ensure this invariant, as this - /// method doesn't perform any valiation checks. - pub(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self { - Self { +impl<'ctx> LLVMTypeWrapper for Function<'ctx> { + type Target = LLVMValueRef; + + fn from_ptr(value_ref: Self::Target) -> Result { + if value_ref.is_null() { + return Err(LLVMTypeError::NullPointer); + } + if unsafe { LLVMIsAFunction(value_ref).is_null() } { + return Err(LLVMTypeError::InvalidPointerType("Function")); + } + Ok(Self { value_ref, _marker: PhantomData, - } + }) } + fn as_ptr(&self) -> Self::Target { + self.value_ref + } +} + +impl<'ctx> Function<'ctx> { pub(crate) fn name(&self) -> &str { symbol_name(self.value_ref) } @@ -329,11 +364,11 @@ impl<'ctx> Function<'ctx> { pub(crate) fn subprogram(&self, context: LLVMContextRef) -> Option> { let subprogram = unsafe { LLVMGetSubprogram(self.value_ref) }; NonNull::new(subprogram).map(|_| unsafe { - DISubprogram::from_value_ref(LLVMMetadataAsValue(context, subprogram)) + DISubprogram::from_ptr(LLVMMetadataAsValue(context, subprogram)).unwrap() }) } pub(crate) fn set_subprogram(&mut self, subprogram: &DISubprogram) { - unsafe { LLVMSetSubprogram(self.value_ref, LLVMValueAsMetadata(subprogram.value_ref)) }; + unsafe { LLVMSetSubprogram(self.value_ref, LLVMValueAsMetadata(subprogram.as_ptr())) }; } } diff --git a/src/llvm/types/mod.rs b/src/llvm/types/mod.rs index ac874bda..0045d8ba 100644 --- a/src/llvm/types/mod.rs +++ b/src/llvm/types/mod.rs @@ -1,2 +1,22 @@ +use thiserror::Error; + pub mod di; pub mod ir; + +#[derive(Debug, Error)] +pub enum LLVMTypeError { + #[error("invalid pointer type, expected {0}")] + InvalidPointerType(&'static str), + #[error("null pointer")] + NullPointer, +} + +pub trait LLVMTypeWrapper { + type Target: ?Sized; + + /// Constructs a new [`Self`] from the given pointer `ptr`. + fn from_ptr(ptr: Self::Target) -> Result + where + Self: Sized; + fn as_ptr(&self) -> Self::Target; +}