Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 15 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,29 +53,33 @@ jobs:
- rust: 1.86.0
llvm-version: 19
llvm-from: apt
features: di-sanitizer,llvm-19,llvm-sys-19/force-dynamic
exclude-features: default,llvm-20,llvm-21,rust-llvm-20,rust-llvm-21
- rust: 1.89.0
llvm-version: 20
llvm-from: apt
features: di-sanitizer,llvm-20,llvm-sys-20/force-dynamic
exclude-features: default,llvm-19,llvm-21,rust-llvm-19,rust-llvm-21
- rust: beta
llvm-version: 21
llvm-from: apt
features: di-sanitizer,llvm-21,llvm-sys-21/force-dynamic
exclude-features: default,llvm-19,llvm-20,rust-llvm-19,rust-llvm-20
- rust: nightly
llvm-version: 21
llvm-from: apt
features: di-sanitizer,llvm-21,llvm-sys-21/force-dynamic
exclude-features: llvm-19,llvm-20,rust-llvm-19,rust-llvm-20
- rust: nightly
llvm-version: 21
llvm-from: source
exclude-features: llvm-19,llvm-20,rust-llvm-19,rust-llvm-20
features: llvm-21,llvm-sys-21/force-dynamic
exclude-features: di-sanitizer,llvm-19,llvm-20,rust-llvm-19,rust-llvm-20
name: rustc=${{ matrix.rust }} llvm-version=${{ matrix.llvm-version }} llvm-from=${{ matrix.llvm-from }}
needs: llvm

env:
RUST_BACKTRACE: full
LLVM_FEATURES: llvm-${{ matrix.llvm-version }},llvm-sys-${{ matrix.llvm-version }}/force-dynamic

steps:
- uses: actions/checkout@v5
Expand Down Expand Up @@ -165,13 +169,13 @@ jobs:
run: |
cargo hack check --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ matrix.features }}

- name: Build
run: |
cargo hack build --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ matrix.features }}

# Toolchains provided by rustup include standard library artifacts
# only for Tier 1 targets, which do not include BPF targets.
Expand All @@ -185,7 +189,7 @@ jobs:
run: |
RUSTC_BOOTSTRAP=1 cargo hack test --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ matrix.features }}

# To make things easier for package maintainers, the step of building a
# custom sysroot can be skipped by setting the `BPFEL_SYSROOT_DIR`
Expand All @@ -209,18 +213,21 @@ jobs:

BPFEL_SYSROOT_DIR="$BPFEL_SYSROOT_DIR" cargo hack test --feature-powerset \
--exclude-features ${{ matrix.exclude-features }} \
--features ${{ env.LLVM_FEATURES }}
--features ${{ matrix.features }}

- uses: actions/checkout@v5
if: matrix.rust == 'nightly'
with:
repository: aya-rs/aya
# FIXME: Switch to upstream after https://github.com/aya-rs/aya/pull/1382
# is merged.
repository: vadorovsky/aya
ref: btf-sanitize-name
path: aya
submodules: recursive

- name: Install
if: matrix.rust == 'nightly'
run: cargo install --path . --no-default-features --features ${{ env.LLVM_FEATURES }}
run: cargo install --path . --no-default-features --features ${{ matrix.features }}

- name: Run aya integration tests
if: matrix.rust == 'nightly'
Expand Down
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ workspace = true
name = "bpf-linker"

[features]
llvm-19 = ["dep:llvm-sys-19"]
llvm-20 = ["dep:llvm-sys-20"]
di-sanitizer = []
llvm-19 = ["dep:llvm-sys-19", "di-sanitizer"]
llvm-20 = ["dep:llvm-sys-20", "di-sanitizer"]
llvm-21 = ["dep:llvm-sys-21"]
rust-llvm-19 = [
"dep:aya-rustc-llvm-proxy",
Expand All @@ -69,6 +70,11 @@ rust-llvm-21 = [
default = [
"llvm-21",
"rust-llvm-21",
# Remove once Rust nightly and Linux distributions update to LLVM 21.1.5,
# that contains fixes making the sanitizer obsolete[0].
#
# [0] https://github.com/llvm/llvm-project/pull/165154
"di-sanitizer"
]

[workspace]
Expand Down
5 changes: 5 additions & 0 deletions src/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,10 @@ fn create_target_machine(

fn optimize<'ctx, 'a, E>(
options: &LinkerOptions,
#[cfg_attr(
not(feature = "di-sanitizer"),
expect(unused, reason = "used when `di-sanitizer` feature is enabled")
)]
context: &'ctx LLVMContext,
target_machine: &LLVMTargetMachine,
module: &mut LLVMModule<'ctx>,
Expand Down Expand Up @@ -707,6 +711,7 @@ where

if *btf {
// if we want to emit BTF, we need to sanitize the debug information
#[cfg(feature = "di-sanitizer")]
llvm::DISanitizer::new(context, module).run(&export_symbols);
} else {
// if we don't need BTF emission, we can strip DI
Expand Down
46 changes: 2 additions & 44 deletions src/llvm/di.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ use gimli::{DW_TAG_pointer_type, DW_TAG_structure_type, DW_TAG_variant_part};
use llvm_sys::{core::*, debuginfo::*, prelude::*};
use tracing::{Level, span, trace, warn};

use super::types::{
di::DIType,
ir::{Function, MDNode, Metadata, Value},
};
use super::types::ir::{Function, MDNode, Metadata, Value};
use crate::llvm::{LLVMContext, LLVMModule, iter::*, types::di::DISubprogram};

// KSYM_NAME_LEN from linux kernel intentionally set
Expand Down Expand Up @@ -90,8 +87,6 @@ impl<'ctx> DISanitizer<'ctx> {
}

let mut is_data_carrying_enum = false;
let mut remove_name = false;
let mut members: Vec<DIType<'_>> = Vec::new();
for element in di_composite_type.elements() {
match element {
Metadata::DICompositeType(di_composite_type_inner) => {
Expand Down Expand Up @@ -123,50 +118,13 @@ impl<'ctx> DISanitizer<'ctx> {
_ => {}
}
}
Metadata::DIDerivedType(di_derived_type) => {
let base_type = di_derived_type.base_type();

match base_type {
Metadata::DICompositeType(base_type_di_composite_type) => {
if let Some(base_type_name) =
base_type_di_composite_type.name()
{
// `AyaBtfMapMarker` is a type which is used in fields of BTF map
// structs. We need to make such structs anonymous in order to get
// BTF maps accepted by the Linux kernel.
if base_type_name == b"AyaBtfMapMarker" {
// Remove the name from the struct.
remove_name = true;
// And don't include the field in the sanitized DI.
} else {
members.push(di_derived_type.into());
}
} else {
members.push(di_derived_type.into());
}
}
_ => {
members.push(di_derived_type.into());
}
}
}
_ => {}
}
}
if is_data_carrying_enum {
di_composite_type.replace_elements(MDNode::empty(self.context));
} else if !members.is_empty() {
members.sort_by_cached_key(|di_type| di_type.offset_in_bits());
let sorted_elements =
MDNode::with_elements(self.context, members.as_mut_slice());
di_composite_type.replace_elements(sorted_elements);
}
if remove_name {
// `AyaBtfMapMarker` is a type which is used in fields of BTF map
// structs. We need to make such structs anonymous in order to get
// BTF maps accepted by the Linux kernel.
di_composite_type.replace_name(self.context, &[])
} else if let Some((_, sanitized_name)) = names {
if let Some((_, sanitized_name)) = names {
// Clear the name from characters incompatible with C.
di_composite_type.replace_name(self.context, sanitized_name.as_slice())
}
Expand Down
19 changes: 14 additions & 5 deletions src/llvm/iter.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
use std::marker::PhantomData;

#[cfg(feature = "di-sanitizer")]
use llvm_sys::{
core::{
LLVMGetFirstBasicBlock, LLVMGetFirstFunction, LLVMGetFirstGlobal, LLVMGetFirstGlobalAlias,
LLVMGetFirstInstruction, LLVMGetLastBasicBlock, LLVMGetLastFunction, LLVMGetLastGlobal,
LLVMGetLastGlobalAlias, LLVMGetLastInstruction, LLVMGetNextBasicBlock, LLVMGetNextFunction,
LLVMGetNextGlobal, LLVMGetNextGlobalAlias, LLVMGetNextInstruction,
LLVMGetFirstBasicBlock, LLVMGetFirstInstruction, LLVMGetLastBasicBlock,
LLVMGetLastInstruction, LLVMGetNextBasicBlock, LLVMGetNextInstruction,
},
prelude::{LLVMBasicBlockRef, LLVMModuleRef, LLVMValueRef},
prelude::LLVMBasicBlockRef,
};
use llvm_sys::{
core::{
LLVMGetFirstFunction, LLVMGetFirstGlobal, LLVMGetFirstGlobalAlias, LLVMGetLastFunction,
LLVMGetLastGlobal, LLVMGetLastGlobalAlias, LLVMGetNextFunction, LLVMGetNextGlobal,
LLVMGetNextGlobalAlias,
},
prelude::{LLVMModuleRef, LLVMValueRef},
};

macro_rules! llvm_iterator {
Expand Down Expand Up @@ -90,6 +97,7 @@ llvm_iterator! {
LLVMGetNextFunction,
}

#[cfg(feature = "di-sanitizer")]
llvm_iterator!(
IterBasicBlocks,
BasicBlockIter,
Expand All @@ -101,6 +109,7 @@ llvm_iterator!(
LLVMGetNextBasicBlock
);

#[cfg(feature = "di-sanitizer")]
llvm_iterator!(
IterInstructions,
InstructionsIter,
Expand Down
9 changes: 6 additions & 3 deletions src/llvm/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#[cfg(feature = "di-sanitizer")]
mod di;
mod iter;
mod types;
Expand All @@ -10,16 +11,18 @@ use std::{
ptr, slice, str,
};

#[cfg(feature = "di-sanitizer")]
pub(crate) use di::DISanitizer;
use iter::{IterModuleFunctions as _, IterModuleGlobalAliases as _, IterModuleGlobals as _};
#[cfg(feature = "di-sanitizer")]
use llvm_sys::core::LLVMGetMDString;
use llvm_sys::{
LLVMAttributeFunctionIndex, LLVMLinkage, LLVMVisibility,
bit_reader::LLVMParseBitcodeInContext2,
core::{
LLVMCreateMemoryBufferWithMemoryRange, LLVMDisposeMemoryBuffer, LLVMDisposeMessage,
LLVMGetEnumAttributeKindForName, LLVMGetMDString, LLVMGetModuleInlineAsm, LLVMGetTarget,
LLVMGetValueName2, LLVMRemoveEnumAttributeAtIndex, LLVMSetLinkage, LLVMSetModuleInlineAsm2,
LLVMSetVisibility,
LLVMGetEnumAttributeKindForName, LLVMGetModuleInlineAsm, LLVMGetTarget, LLVMGetValueName2,
LLVMRemoveEnumAttributeAtIndex, LLVMSetLinkage, LLVMSetModuleInlineAsm2, LLVMSetVisibility,
},
error::{
LLVMDisposeErrorMessage, LLVMGetErrorMessage, LLVMGetErrorTypeId, LLVMGetStringErrorTypeId,
Expand Down
65 changes: 1 addition & 64 deletions src/llvm/types/di.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use llvm_sys::{
core::{LLVMGetNumOperands, LLVMGetOperand, LLVMReplaceMDNodeOperandWith, LLVMValueAsMetadata},
debuginfo::{
LLVMDIFileGetFilename, LLVMDIFlags, LLVMDIScopeGetFile, LLVMDISubprogramGetLine,
LLVMDITypeGetFlags, LLVMDITypeGetLine, LLVMDITypeGetName, LLVMDITypeGetOffsetInBits,
LLVMGetDINodeTag,
LLVMDITypeGetFlags, LLVMDITypeGetLine, LLVMDITypeGetName, LLVMGetDINodeTag,
},
prelude::{LLVMContextRef, LLVMMetadataRef, LLVMValueRef},
};
Expand Down Expand Up @@ -108,60 +107,6 @@ unsafe fn di_type_name<'a>(metadata_ref: LLVMMetadataRef) -> Option<&'a [u8]> {
(!ptr.is_null()).then(|| unsafe { slice::from_raw_parts(ptr.cast(), len) })
}

/// Represents the debug information for a primitive type in LLVM IR.
pub(crate) struct DIType<'ctx> {
pub(super) metadata_ref: LLVMMetadataRef,
pub(super) value_ref: LLVMValueRef,
_marker: PhantomData<&'ctx ()>,
}

impl DIType<'_> {
/// 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(crate) unsafe fn from_value_ref(value_ref: LLVMValueRef) -> Self {
let metadata_ref = unsafe { LLVMValueAsMetadata(value_ref) };
Self {
metadata_ref,
value_ref,
_marker: PhantomData,
}
}

/// Returns the offset of the type in bits. This offset is used in case the
/// type is a member of a composite type.
pub(crate) fn offset_in_bits(&self) -> u64 {
unsafe { LLVMDITypeGetOffsetInBits(self.metadata_ref) }
}
}

impl<'ctx> From<DIDerivedType<'ctx>> for DIType<'ctx> {
fn from(di_derived_type: DIDerivedType<'_>) -> Self {
unsafe { Self::from_value_ref(di_derived_type.value_ref) }
}
}

/// Represents the operands for a [`DIDerivedType`]. The enum values correspond
/// to the operand indices within metadata nodes.
#[repr(u32)]
enum DIDerivedTypeOperand {
/// [`DIType`] representing a base type of the given derived type.
/// Reference in [LLVM 19-20][llvm-19] and [LLVM 21][llvm-21].
///
/// [llvm-19]: https://github.com/llvm/llvm-project/blob/llvmorg-19.1.7/llvm/include/llvm/IR/DebugInfoMetadata.h#L1084
/// [llvm-21]: https://github.com/llvm/llvm-project/blob/llvmorg-21.1.0-rc3/llvm/include/llvm/IR/DebugInfoMetadata.h#L1386
///
#[cfg(any(feature = "llvm-19", feature = "llvm-20"))]
BaseType = 3,
#[cfg(feature = "llvm-21")]
BaseType = 5,
}

/// Represents the debug information for a derived type in LLVM IR.
///
/// The types derived from other types usually add a level of indirection or an
Expand Down Expand Up @@ -191,14 +136,6 @@ impl DIDerivedType<'_> {
}
}

/// Returns the base type of this derived type.
pub(crate) fn base_type(&self) -> Metadata<'_> {
unsafe {
let value = LLVMGetOperand(self.value_ref, DIDerivedTypeOperand::BaseType as u32);
Metadata::from_value_ref(value)
}
}

/// Replaces the name of the type with a new name.
///
/// # Errors
Expand Down
22 changes: 1 addition & 21 deletions src/llvm/types/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::llvm::{
Message,
iter::IterBasicBlocks as _,
symbol_name,
types::di::{DICompositeType, DIDerivedType, DISubprogram, DIType},
types::di::{DICompositeType, DIDerivedType, DISubprogram},
};

pub(crate) fn replace_name(
Expand Down Expand Up @@ -229,26 +229,6 @@ impl MDNode<'_> {
let metadata = unsafe { LLVMMDNodeInContext2(context, core::ptr::null_mut(), 0) };
unsafe { Self::from_metadata_ref(context, metadata) }
}

/// Constructs a new metadata node from an array of [`DIType`] elements.
///
/// This function is used to create composite metadata structures, such as
/// arrays or tuples of different types or values, which can then be used
/// to represent complex data structures within the metadata system.
pub(crate) fn with_elements(context: LLVMContextRef, elements: &[DIType<'_>]) -> Self {
let metadata = unsafe {
let mut elements: Vec<LLVMMetadataRef> = elements
.iter()
.map(|di_type| LLVMValueAsMetadata(di_type.value_ref))
.collect();
LLVMMDNodeInContext2(
context,
elements.as_mut_slice().as_mut_ptr(),
elements.len(),
)
};
unsafe { Self::from_metadata_ref(context, metadata) }
}
}

pub(crate) struct MetadataEntries {
Expand Down
Loading