Browse Source

Merge pull request #453 from alessandrod/btf-kind-enum64

Add support for BTF_KIND_ENUM64
Alessandro Decina 2 years ago
parent
commit
e8e2767
3 changed files with 162 additions and 20 deletions
  1. 47 16
      aya-obj/src/btf/relocation.rs
  2. 75 4
      aya-obj/src/btf/types.rs
  3. 40 0
      test/integration-test/src/tests/relocations.rs

+ 47 - 16
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,
                 _ => (),
             },

+ 75 - 4
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<BtfEnum64>,
+}
+
+impl Enum64 {
+    pub(crate) fn to_bytes(&self) -> Vec<u8> {
+        let mut buf = vec![];
+        buf.extend(bytes_of::<u32>(&self.name_offset));
+        buf.extend(bytes_of::<u32>(&self.info));
+        buf.extend(bytes_of::<u32>(&self.size));
+        for v in &self.variants {
+            buf.extend(bytes_of::<u32>(&v.name_offset));
+            buf.extend(bytes_of::<u32>(&v.value_high));
+            buf.extend(bytes_of::<u32>(&v.value_low));
+        }
+        buf
+    }
+
+    pub(crate) fn kind(&self) -> BtfKind {
+        BtfKind::Enum64
+    }
+
+    pub(crate) fn type_info_size(&self) -> usize {
+        mem::size_of::<Fwd>() + mem::size_of::<BtfEnum64>() * 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<u32> for BtfKind {
@@ -853,6 +899,7 @@ impl TryFrom<u32> 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::<BtfEnum>(data, vlen)? },
             }),
+            BtfKind::Enum64 => BtfType::Enum64(Enum64 {
+                name_offset: ty[0],
+                info: ty[1],
+                size: ty[2],
+                variants: unsafe { read_array::<BtfEnum64>(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<BtfKind, BtfError> {
@@ -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();
 

+ 40 - 0
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 {