diff --git a/aya-obj/src/btf/relocation.rs b/aya-obj/src/btf/relocation.rs index 4a618c0a..7b8cf002 100644 --- a/aya-obj/src/btf/relocation.rs +++ b/aya-obj/src/btf/relocation.rs @@ -406,19 +406,35 @@ fn match_candidate<'target>( let target_ty = candidate.btf.type_by_id(target_id)?; // the first accessor is guaranteed to have a name by construction let local_variant_name = local_spec.accessors[0].name.as_ref().unwrap(); + let match_enum = + |name_offset, index, target_spec: &mut AccessSpec| -> Result<_, BtfError> { + let target_variant_name = candidate.btf.string_at(name_offset)?; + if flavorless_name(local_variant_name) == flavorless_name(&target_variant_name) + { + target_spec.parts.push(index); + target_spec.accessors.push(Accessor { + index, + type_id: target_id, + name: None, + }); + Ok(Some(())) + } else { + Ok(None) + } + }; match target_ty { BtfType::Enum(en) => { for (index, member) in en.variants.iter().enumerate() { - let target_variant_name = candidate.btf.string_at(member.name_offset)?; - if flavorless_name(local_variant_name) - == flavorless_name(&target_variant_name) + if let Ok(Some(_)) = match_enum(member.name_offset, index, &mut target_spec) + { + return Ok(Some(target_spec)); + } + } + } + BtfType::Enum64(en) => { + for (index, member) in en.variants.iter().enumerate() { + if let Ok(Some(_)) = match_enum(member.name_offset, index, &mut target_spec) { - target_spec.parts.push(index); - target_spec.accessors.push(Accessor { - index, - type_id: target_id, - name: None, - }); return Ok(Some(target_spec)); } } @@ -620,29 +636,39 @@ impl<'a> AccessSpec<'a> { } } RelocationKind::EnumVariantExists | RelocationKind::EnumVariantValue => match ty { - BtfType::Enum(en) => { + BtfType::Enum(_) | BtfType::Enum64(_) => { if parts.len() != 1 { return Err(RelocationError::InvalidAccessString { access_str: spec.to_string(), }); } let index = parts[0]; - if index >= en.variants.len() { + + let (n_variants, name_offset) = match ty { + BtfType::Enum(en) => ( + en.variants.len(), + en.variants.get(index).map(|v| v.name_offset), + ), + BtfType::Enum64(en) => ( + en.variants.len(), + en.variants.get(index).map(|v| v.name_offset), + ), + _ => unreachable!(), + }; + + if name_offset.is_none() { return Err(RelocationError::InvalidAccessIndex { type_name: btf.err_type_name(ty), spec: spec.to_string(), index, - max_index: en.variants.len(), + max_index: n_variants, error: "tried to access nonexistant enum variant".to_string(), }); } let accessors = vec![Accessor { type_id, index, - name: Some( - btf.string_at(en.variants.get(index).unwrap().name_offset)? - .to_string(), - ), + name: Some(btf.string_at(name_offset.unwrap())?.to_string()), }]; AccessSpec { @@ -946,6 +972,10 @@ impl ComputedRelocation { value as u64 } } + BtfType::Enum64(en) => { + let variant = &en.variants[accessor.index]; + (variant.value_high as u64) << 32 | variant.value_low as u64 + } // candidate selection ensures that rel_kind == local_kind == target_kind _ => unreachable!(), } @@ -1079,6 +1109,7 @@ impl ComputedRelocation { } FieldSigned => match member_ty { BtfType::Enum(en) => value.value = en.is_signed() as u64, + BtfType::Enum64(en) => value.value = en.is_signed() as u64, BtfType::Int(i) => value.value = i.encoding() as u64 & IntEncoding::Signed as u64, _ => (), }, diff --git a/aya-obj/src/btf/types.rs b/aya-obj/src/btf/types.rs index 629e54f5..dcd869fe 100644 --- a/aya-obj/src/btf/types.rs +++ b/aya-obj/src/btf/types.rs @@ -28,6 +28,7 @@ pub enum BtfType { DataSec(DataSec), DeclTag(DeclTag), TypeTag(TypeTag), + Enum64(Enum64), } #[repr(C)] @@ -438,6 +439,50 @@ impl Enum { } } +#[repr(C)] +#[derive(Debug, Clone)] +pub struct BtfEnum64 { + pub(crate) name_offset: u32, + pub(crate) value_low: u32, + pub(crate) value_high: u32, +} + +#[repr(C)] +#[derive(Clone, Debug)] +pub struct Enum64 { + pub(crate) name_offset: u32, + info: u32, + pub(crate) size: u32, + pub(crate) variants: Vec, +} + +impl Enum64 { + pub(crate) fn to_bytes(&self) -> Vec { + let mut buf = vec![]; + buf.extend(bytes_of::(&self.name_offset)); + buf.extend(bytes_of::(&self.info)); + buf.extend(bytes_of::(&self.size)); + for v in &self.variants { + buf.extend(bytes_of::(&v.name_offset)); + buf.extend(bytes_of::(&v.value_high)); + buf.extend(bytes_of::(&v.value_low)); + } + buf + } + + pub(crate) fn kind(&self) -> BtfKind { + BtfKind::Enum64 + } + + pub(crate) fn type_info_size(&self) -> usize { + mem::size_of::() + mem::size_of::() * self.variants.len() + } + + pub(crate) fn is_signed(&self) -> bool { + self.info >> 31 == 1 + } +} + #[repr(C)] #[derive(Clone, Debug)] pub(crate) struct BtfMember { @@ -826,6 +871,7 @@ pub enum BtfKind { Float = 16, DeclTag = 17, TypeTag = 18, + Enum64 = 19, } impl TryFrom for BtfKind { @@ -853,6 +899,7 @@ impl TryFrom for BtfKind { 16 => Float, 17 => DeclTag, 18 => TypeTag, + 19 => Enum64, kind => return Err(BtfError::InvalidTypeKind { kind }), }) } @@ -880,6 +927,7 @@ impl Display for BtfKind { BtfKind::DataSec => write!(f, "[DATASEC]"), BtfKind::DeclTag => write!(f, "[DECL_TAG]"), BtfKind::TypeTag => write!(f, "[TYPE_TAG]"), + BtfKind::Enum64 => write!(f, "[ENUM64]"), } } } @@ -974,6 +1022,12 @@ impl BtfType { size: ty[2], variants: unsafe { read_array::(data, vlen)? }, }), + BtfKind::Enum64 => BtfType::Enum64(Enum64 { + name_offset: ty[0], + info: ty[1], + size: ty[2], + variants: unsafe { read_array::(data, vlen)? }, + }), BtfKind::Array => BtfType::Array(Array { name_offset: ty[0], info: ty[1], @@ -1037,6 +1091,7 @@ impl BtfType { BtfType::Int(t) => t.to_bytes(), BtfType::Float(t) => t.to_bytes(), BtfType::Enum(t) => t.to_bytes(), + BtfType::Enum64(t) => t.to_bytes(), BtfType::Array(t) => t.to_bytes(), BtfType::Struct(t) => t.to_bytes(), BtfType::Union(t) => t.to_bytes(), @@ -1053,6 +1108,7 @@ impl BtfType { BtfType::Int(t) => Some(t.size), BtfType::Float(t) => Some(t.size), BtfType::Enum(t) => Some(t.size), + BtfType::Enum64(t) => Some(t.size), BtfType::Struct(t) => Some(t.size), BtfType::Union(t) => Some(t.size), BtfType::DataSec(t) => Some(t.size), @@ -1090,6 +1146,7 @@ impl BtfType { BtfType::Int(t) => t.type_info_size(), BtfType::Float(t) => t.type_info_size(), BtfType::Enum(t) => t.type_info_size(), + BtfType::Enum64(t) => t.type_info_size(), BtfType::Array(t) => t.type_info_size(), BtfType::Struct(t) => t.type_info_size(), BtfType::Union(t) => t.type_info_size(), @@ -1114,6 +1171,7 @@ impl BtfType { BtfType::Int(t) => t.name_offset, BtfType::Float(t) => t.name_offset, BtfType::Enum(t) => t.name_offset, + BtfType::Enum64(t) => t.name_offset, BtfType::Array(t) => t.name_offset, BtfType::Struct(t) => t.name_offset, BtfType::Union(t) => t.name_offset, @@ -1138,6 +1196,7 @@ impl BtfType { BtfType::Int(t) => t.kind(), BtfType::Float(t) => t.kind(), BtfType::Enum(t) => t.kind(), + BtfType::Enum64(t) => t.kind(), BtfType::Array(t) => t.kind(), BtfType::Struct(t) => t.kind(), BtfType::Union(t) => t.kind(), @@ -1176,6 +1235,17 @@ impl BtfType { _ => None, } } + + pub(crate) fn is_compatible(&self, other: &BtfType) -> bool { + if self.kind() == other.kind() { + return true; + } + + matches!( + (self.kind(), other.kind()), + (BtfKind::Enum, BtfKind::Enum64) | (BtfKind::Enum64, BtfKind::Enum) + ) + } } fn type_kind(info: u32) -> Result { @@ -1197,7 +1267,7 @@ pub(crate) fn types_are_compatible( let local_ty = local_btf.type_by_id(local_id)?; let target_ty = target_btf.type_by_id(target_id)?; - if local_ty.kind() != target_ty.kind() { + if !local_ty.is_compatible(target_ty) { return Ok(false); } @@ -1207,7 +1277,7 @@ pub(crate) fn types_are_compatible( let local_ty = local_btf.type_by_id(local_id)?; let target_ty = target_btf.type_by_id(target_id)?; - if local_ty.kind() != target_ty.kind() { + if !local_ty.is_compatible(target_ty) { return Ok(false); } @@ -1216,6 +1286,7 @@ pub(crate) fn types_are_compatible( | BtfType::Struct(_) | BtfType::Union(_) | BtfType::Enum(_) + | BtfType::Enum64(_) | BtfType::Fwd(_) | BtfType::Float(_) => return Ok(true), BtfType::Int(local) => { @@ -1279,12 +1350,12 @@ pub(crate) fn fields_are_compatible( return Ok(true); } - if local_ty.kind() != target_ty.kind() { + if !local_ty.is_compatible(target_ty) { return Ok(false); } match local_ty { - BtfType::Fwd(_) | BtfType::Enum(_) => { + BtfType::Fwd(_) | BtfType::Enum(_) | BtfType::Enum64(_) => { let flavorless_name = |name: &str| name.split_once("___").map_or(name, |x| x.0).to_string(); diff --git a/test/integration-test/src/tests/relocations.rs b/test/integration-test/src/tests/relocations.rs index 74eee2f9..53de8b5d 100644 --- a/test/integration-test/src/tests/relocations.rs +++ b/test/integration-test/src/tests/relocations.rs @@ -80,6 +80,46 @@ fn relocate_enum_signed() { assert_eq!(test.run_no_btf().unwrap() as i64, -0x7AAAAAAAi64); } +#[integration_test] +fn relocate_enum64() { + let test = RelocationTest { + local_definition: r#" + enum foo { D = 0xAAAAAAAABBBBBBBB }; + "#, + target_btf: r#" + enum foo { D = 0xCCCCCCCCDDDDDDDD } e1; + "#, + relocation_code: r#" + #define BPF_ENUMVAL_VALUE 1 + value = __builtin_preserve_enum_value(*(typeof(enum foo) *)D, BPF_ENUMVAL_VALUE); + "#, + } + .build() + .unwrap(); + assert_eq!(test.run().unwrap(), 0xCCCCCCCCDDDDDDDD); + assert_eq!(test.run_no_btf().unwrap(), 0xAAAAAAAABBBBBBBB); +} + +#[integration_test] +fn relocate_enum64_signed() { + let test = RelocationTest { + local_definition: r#" + enum foo { D = -0xAAAAAAABBBBBBBB }; + "#, + target_btf: r#" + enum foo { D = -0xCCCCCCCDDDDDDDD } e1; + "#, + relocation_code: r#" + #define BPF_ENUMVAL_VALUE 1 + value = __builtin_preserve_enum_value(*(typeof(enum foo) *)D, BPF_ENUMVAL_VALUE); + "#, + } + .build() + .unwrap(); + assert_eq!(test.run().unwrap() as i64, -0xCCCCCCCDDDDDDDDi64); + assert_eq!(test.run_no_btf().unwrap() as i64, -0xAAAAAAABBBBBBBBi64); +} + #[integration_test] fn relocate_pointer() { let test = RelocationTest {