|  | @@ -13,8 +13,8 @@ use object::SectionIndex;
 | 
	
		
			
				|  |  |  use crate::{
 | 
	
		
			
				|  |  |      Function, Object,
 | 
	
		
			
				|  |  |      btf::{
 | 
	
		
			
				|  |  | -        Array, Btf, BtfError, BtfMember, BtfType, IntEncoding, MAX_SPEC_LEN, Struct, Union,
 | 
	
		
			
				|  |  | -        fields_are_compatible, types_are_compatible,
 | 
	
		
			
				|  |  | +        Array, Btf, BtfError, BtfKind, BtfMember, BtfType, IntEncoding, MAX_SPEC_LEN, Struct,
 | 
	
		
			
				|  |  | +        Union, fields_are_compatible, types_are_compatible,
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      generated::{
 | 
	
		
			
				|  |  |          BPF_ALU, BPF_ALU64, BPF_B, BPF_CALL, BPF_DW, BPF_H, BPF_JMP, BPF_K, BPF_LD, BPF_LDX,
 | 
	
	
		
			
				|  | @@ -409,10 +409,26 @@ fn find_candidates<'target>(
 | 
	
		
			
				|  |  |  ) -> Result<Vec<Candidate<'target>>, BtfError> {
 | 
	
		
			
				|  |  |      let mut candidates = Vec::new();
 | 
	
		
			
				|  |  |      let local_name = flavorless_name(local_name);
 | 
	
		
			
				|  |  | -    for (type_id, ty) in target_btf.types().enumerate() {
 | 
	
		
			
				|  |  | -        if local_ty.kind() != ty.kind() {
 | 
	
		
			
				|  |  | -            continue;
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +    let local_kind = local_ty.kind();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    // When we downgrade an ENUM64 to a UNION we still want to match the enum
 | 
	
		
			
				|  |  | +    // definition recorded in the target BTF. If the sanitized type has a
 | 
	
		
			
				|  |  | +    // fallback, allow ENUM64 candidates through the kind check.
 | 
	
		
			
				|  |  | +    //
 | 
	
		
			
				|  |  | +    // Note that we do not sanitize the target BTF so the kinds will not
 | 
	
		
			
				|  |  | +    // naturally match!
 | 
	
		
			
				|  |  | +    let allow_enum_match = matches!(
 | 
	
		
			
				|  |  | +        local_ty,
 | 
	
		
			
				|  |  | +        BtfType::Union(Union {
 | 
	
		
			
				|  |  | +            enum64_fallback: Some(_),
 | 
	
		
			
				|  |  | +            ..
 | 
	
		
			
				|  |  | +        })
 | 
	
		
			
				|  |  | +    );
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    for (type_id, ty) in target_btf.types().enumerate().filter(|(_, ty)| {
 | 
	
		
			
				|  |  | +        let candidate_kind = ty.kind();
 | 
	
		
			
				|  |  | +        candidate_kind == local_kind || (allow_enum_match && candidate_kind == BtfKind::Enum64)
 | 
	
		
			
				|  |  | +    }) {
 | 
	
		
			
				|  |  |          let name = &*target_btf.type_name(ty)?;
 | 
	
		
			
				|  |  |          if local_name != flavorless_name(name) {
 | 
	
		
			
				|  |  |              continue;
 | 
	
	
		
			
				|  | @@ -488,6 +504,14 @@ fn match_candidate<'target>(
 | 
	
		
			
				|  |  |                  BtfType::Enum64(en) => {
 | 
	
		
			
				|  |  |                      match_enum(&mut en.variants.iter().map(|member| member.name_offset))
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | +                BtfType::Union(Union {
 | 
	
		
			
				|  |  | +                    enum64_fallback: Some(fallback),
 | 
	
		
			
				|  |  | +                    ..
 | 
	
		
			
				|  |  | +                }) => {
 | 
	
		
			
				|  |  | +                    // Local ENUM64 types become UNIONs during sanitisation; the fallback retains
 | 
	
		
			
				|  |  | +                    // their original variant names so we can line them up with target enums.
 | 
	
		
			
				|  |  | +                    match_enum(&mut fallback.variants.iter().map(|variant| variant.name_offset))
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |                  _ => Ok(None),
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -708,6 +732,17 @@ impl<'a> AccessSpec<'a> {
 | 
	
		
			
				|  |  |                              index,
 | 
	
		
			
				|  |  |                          )
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | +                    BtfType::Union(Union {
 | 
	
		
			
				|  |  | +                        enum64_fallback: Some(fallback),
 | 
	
		
			
				|  |  | +                        ..
 | 
	
		
			
				|  |  | +                    }) => {
 | 
	
		
			
				|  |  | +                        let index = index()?;
 | 
	
		
			
				|  |  | +                        (
 | 
	
		
			
				|  |  | +                            fallback.variants.len(),
 | 
	
		
			
				|  |  | +                            fallback.variants.get(index).map(|v| v.name_offset),
 | 
	
		
			
				|  |  | +                            index,
 | 
	
		
			
				|  |  | +                        )
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                      _ => {
 | 
	
		
			
				|  |  |                          return Err(RelocationError::InvalidRelocationKindForType {
 | 
	
		
			
				|  |  |                              relocation_number: relocation.number,
 | 
	
	
		
			
				|  | @@ -861,7 +896,7 @@ struct ComputedRelocation {
 | 
	
		
			
				|  |  |      target: Option<ComputedRelocationValue>,
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#[derive(Debug)]
 | 
	
		
			
				|  |  | +#[derive(Clone, Copy, Debug)]
 | 
	
		
			
				|  |  |  struct ComputedRelocationValue {
 | 
	
		
			
				|  |  |      value: u64,
 | 
	
		
			
				|  |  |      size: u32,
 | 
	
	
		
			
				|  | @@ -1059,6 +1094,17 @@ impl ComputedRelocation {
 | 
	
		
			
				|  |  |                          let variant = &en.variants[accessor.index];
 | 
	
		
			
				|  |  |                          (u64::from(variant.value_high) << 32) | u64::from(variant.value_low)
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  | +                    BtfType::Union(Union {
 | 
	
		
			
				|  |  | +                        enum64_fallback: Some(fallback),
 | 
	
		
			
				|  |  | +                        ..
 | 
	
		
			
				|  |  | +                    }) => {
 | 
	
		
			
				|  |  | +                        let variant = &fallback.variants[accessor.index];
 | 
	
		
			
				|  |  | +                        if fallback.signed {
 | 
	
		
			
				|  |  | +                            (variant.value as i64) as u64
 | 
	
		
			
				|  |  | +                        } else {
 | 
	
		
			
				|  |  | +                            variant.value
 | 
	
		
			
				|  |  | +                        }
 | 
	
		
			
				|  |  | +                    }
 | 
	
		
			
				|  |  |                      // candidate selection ensures that rel_kind == local_kind == target_kind
 | 
	
		
			
				|  |  |                      _ => unreachable!(),
 | 
	
		
			
				|  |  |                  }
 |