Repository: cilium/ebpf
Pull request #747 (2022-07-25 16:27:45)

btf: minimal support for BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG
btf: add limited support for BTF_KIND_DECL_TAG and BTF_KIND_TYPE_TAG

    Parse decl and type tags from BTF so that we can read BTF on new kernels.
    The new types are not exported since the encoding of declTag.Index especially
    is cumbersome to use. We can export the types later if necessary.

    Treat typeTag as a qualifier, since accoriding to the BTF documentation it
    is usually used in a chain of types:

        ptr -> [type_tag]*
            -> [const | volatile | restrict | typedef]*
            -> base_type

    Updates #713

ti-mo: I think we should look at something like [bingo](https://www.bwplotka.dev/2020/bingo/) instead of `internal/tooldeps`. Could we drop that change from the PR and address it separately?
lmb: 
Can you tell me why tooldeps doesn't work? What problem does bingo solve 
that is important to us?
On 29 July 2022 16:04:48 Timo Beckers ***@***.***> wrote:
> I think we should look at something like bingo instead of 
> internal/tooldeps. Could we drop that change from the PR and address it 
> separately?
>
> —
> Reply to this email directly, view it on GitHub, or unsubscribe.
> You are receiving this because you authored the thread.



Patch:
diff --git a/btf/btf_types.go b/btf/btf_types.go
index 4810180..1ab0917 100644
--- a/btf/btf_types.go
+++ b/btf/btf_types.go
@@ -6,33 +6,36 @@ import (
 	"io"
 )
 
-//go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage
+//go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind
 
 // btfKind describes a Type.
 type btfKind uint8
 
 // Equivalents of the BTF_KIND_* constants.
 const (
-	kindUnknown btfKind = iota
-	kindInt
-	kindPointer
-	kindArray
-	kindStruct
-	kindUnion
-	kindEnum
-	kindForward
-	kindTypedef
-	kindVolatile
-	kindConst
-	kindRestrict
+	kindUnknown  btfKind = iota // Unknown
+	kindInt                     // Int
+	kindPointer                 // Pointer
+	kindArray                   // Array
+	kindStruct                  // Struct
+	kindUnion                   // Union
+	kindEnum                    // Enum
+	kindForward                 // Forward
+	kindTypedef                 // Typedef
+	kindVolatile                // Volatile
+	kindConst                   // Const
+	kindRestrict                // Restrict
 	// Added ~4.20
-	kindFunc
-	kindFuncProto
+	kindFunc      // Func
+	kindFuncProto // FuncProto
 	// Added ~5.1
-	kindVar
-	kindDatasec
+	kindVar     // Var
+	kindDatasec // Datasec
 	// Added ~5.13
-	kindFloat
+	kindFloat // Float
+	// Added 5.16
+	kindDeclTag // DeclTag
+	kindTypeTag // TypeTag
 )
 
 // FuncLinkage describes BTF function linkage metadata.
@@ -85,47 +88,6 @@ type btfType struct {
 	SizeType uint32
 }
 
-func (k btfKind) String() string {
-	switch k {
-	case kindUnknown:
-		return "Unknown"
-	case kindInt:
-		return "Integer"
-	case kindPointer:
-		return "Pointer"
-	case kindArray:
-		return "Array"
-	case kindStruct:
-		return "Struct"
-	case kindUnion:
-		return "Union"
-	case kindEnum:
-		return "Enumeration"
-	case kindForward:
-		return "Forward"
-	case kindTypedef:
-		return "Typedef"
-	case kindVolatile:
-		return "Volatile"
-	case kindConst:
-		return "Const"
-	case kindRestrict:
-		return "Restrict"
-	case kindFunc:
-		return "Function"
-	case kindFuncProto:
-		return "Function Proto"
-	case kindVar:
-		return "Variable"
-	case kindDatasec:
-		return "Section"
-	case kindFloat:
-		return "Float"
-	default:
-		return fmt.Sprintf("Unknown (%d)", k)
-	}
-}
-
 func mask(len uint32) uint32 {
 	return (1 << len) - 1
 }
@@ -209,11 +171,11 @@ func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error {
 
 // btfInt encodes additional data for integers.
 //
-//    ? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b
-//    ? = undefined
-//    e = encoding
-//    o = offset (bitfields?)
-//    b = bits (bitfields)
+//	? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b
+//	? = undefined
+//	e = encoding
+//	o = offset (bitfields?)
+//	b = bits (bitfields)
 type btfInt struct {
 	Raw uint32
 }
@@ -283,6 +245,10 @@ type btfParam struct {
 	Type    TypeID
 }
 
+type btfDeclTag struct {
+	ComponentIdx uint32
+}
+
 func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, error) {
 	var header btfType
 	// because of the interleaving between types and struct members it is difficult to
@@ -325,6 +291,9 @@ func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, err
 		case kindDatasec:
 			data = make([]btfVarSecinfo, header.Vlen())
 		case kindFloat:
+		case kindDeclTag:
+			data = new(btfDeclTag)
+		case kindTypeTag:
 		default:
 			return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
 		}
diff --git a/btf/btf_types_string.go b/btf/btf_types_string.go
index 0e0c17d..fdb2662 100644
--- a/btf/btf_types_string.go
+++ b/btf/btf_types_string.go
@@ -1,4 +1,4 @@
-// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage"; DO NOT EDIT.
+// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage,btfKind"; DO NOT EDIT.
 
 package btf
 
@@ -42,3 +42,38 @@ func (i VarLinkage) String() string {
 	}
 	return _VarLinkage_name[_VarLinkage_index[i]:_VarLinkage_index[i+1]]
 }
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[kindUnknown-0]
+	_ = x[kindInt-1]
+	_ = x[kindPointer-2]
+	_ = x[kindArray-3]
+	_ = x[kindStruct-4]
+	_ = x[kindUnion-5]
+	_ = x[kindEnum-6]
+	_ = x[kindForward-7]
+	_ = x[kindTypedef-8]
+	_ = x[kindVolatile-9]
+	_ = x[kindConst-10]
+	_ = x[kindRestrict-11]
+	_ = x[kindFunc-12]
+	_ = x[kindFuncProto-13]
+	_ = x[kindVar-14]
+	_ = x[kindDatasec-15]
+	_ = x[kindFloat-16]
+	_ = x[kindDeclTag-17]
+	_ = x[kindTypeTag-18]
+}
+
+const _btfKind_name = "UnknownIntPointerArrayStructUnionEnumForwardTypedefVolatileConstRestrictFuncFuncProtoVarDatasecFloatDeclTagTypeTag"
+
+var _btfKind_index = [...]uint8{0, 7, 10, 17, 22, 28, 33, 37, 44, 51, 59, 64, 72, 76, 85, 88, 95, 100, 107, 114}
+
+func (i btfKind) String() string {
+	if i >= btfKind(len(_btfKind_index)-1) {
+		return "btfKind(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	return _btfKind_name[_btfKind_index[i]:_btfKind_index[i+1]]
+}
diff --git a/btf/types.go b/btf/types.go
index 402a363..38ad3dd 100644
--- a/btf/types.go
+++ b/btf/types.go
@@ -541,6 +541,45 @@ func (f *Float) copy() Type {
 	return &cpy
 }
 
+// declTag associates metadata with a declaration.
+type declTag struct {
+	Type  Type
+	Value string
+	// The index this tag refers to in the target type. For composite types,
+	// a value of -1 indicates that the tag refers to the whole type. Otherwise
+	// it indicates which member or argument the tag applies to.
+	Index int
+}
+
+func (dt *declTag) Format(fs fmt.State, verb rune) {
+	formatType(fs, verb, dt, "type=", dt.Type, "value=", dt.Value, "index=", dt.Index)
+}
+
+func (dt *declTag) TypeName() string   { return "" }
+func (dt *declTag) walk(td *typeDeque) { td.push(&dt.Type) }
+func (dt *declTag) copy() Type {
+	cpy := *dt
+	return &cpy
+}
+
+// typeTag associates metadata with a type.
+type typeTag struct {
+	Type  Type
+	Value string
+}
+
+func (tt *typeTag) Format(fs fmt.State, verb rune) {
+	formatType(fs, verb, tt, "type=", tt.Type, "value=", tt.Value)
+}
+
+func (tt *typeTag) TypeName() string   { return "" }
+func (tt *typeTag) qualify() Type      { return tt.Type }
+func (tt *typeTag) walk(td *typeDeque) { td.push(&tt.Type) }
+func (tt *typeTag) copy() Type {
+	cpy := *tt
+	return &cpy
+}
+
 // cycle is a type which had to be elided since it exceeded maxTypeDepth.
 type cycle struct {
 	root Type
@@ -576,6 +615,7 @@ var (
 	_ qualifier = (*Const)(nil)
 	_ qualifier = (*Restrict)(nil)
 	_ qualifier = (*Volatile)(nil)
+	_ qualifier = (*typeTag)(nil)
 )
 
 // Sizeof returns the size of a type in bytes.
@@ -903,6 +943,7 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
 		return members, nil
 	}
 
+	var declTags []*declTag
 	for i, raw := range rawTypes {
 		var (
 			id  = typeIDOffset + TypeID(i)
@@ -1045,6 +1086,28 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
 		case kindFloat:
 			typ = &Float{name, raw.Size()}
 
+		case kindDeclTag:
+			btfIndex := raw.data.(*btfDeclTag).ComponentIdx
+			if uint64(btfIndex) > math.MaxInt {
+				return nil, fmt.Errorf("type id %d: index exceeds int", id)
+			}
+
+			index := int(btfIndex)
+			if btfIndex == math.MaxUint32 {
+				index = -1
+			}
+
+			dt := &declTag{nil, name, index}
+			fixup(raw.Type(), &dt.Type)
+			typ = dt
+
+			declTags = append(declTags, dt)
+
+		case kindTypeTag:
+			tt := &typeTag{nil, name}
+			fixup(raw.Type(), &tt.Type)
+			typ = tt
+
 		default:
 			return nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
 		}
@@ -1083,6 +1146,28 @@ func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTabl
 		}
 	}
 
+	for _, dt := range declTags {
+		switch t := dt.Type.(type) {
+		case *Var, *Typedef:
+			if dt.Index != -1 {
+				return nil, fmt.Errorf("type %s: index %d is not -1", dt, dt.Index)
+			}
+
+		case composite:
+			if dt.Index >= len(t.members()) {
+				return nil, fmt.Errorf("type %s: index %d exceeds members of %s", dt, dt.Index, t)
+			}
+
+		case *Func:
+			if dt.Index >= len(t.Type.(*FuncProto).Params) {
+				return nil, fmt.Errorf("type %s: index %d exceeds params of %s", dt, dt.Index, t)
+			}
+
+		default:
+			return nil, fmt.Errorf("type %s: decl tag for type %s is not supported", dt, t)
+		}
+	}
+
 	return types, nil
 }
 


Test patch:
diff --git a/btf/types_test.go b/btf/types_test.go
index 5a80db4..c143753 100644
--- a/btf/types_test.go
+++ b/btf/types_test.go
@@ -134,6 +134,9 @@ func TestType(t *testing.T) {
 				Vars: []VarSecinfo{{Type: &Void{}}},
 			}
 		},
+		func() Type { return &Float{} },
+		func() Type { return &declTag{Type: &Void{}} },
+		func() Type { return &typeTag{Type: &Void{}} },
 		func() Type { return &cycle{&Void{}} },
 	}
 
@@ -331,6 +334,7 @@ func TestUnderlyingType(t *testing.T) {
 		{"volatile", func(t Type) Type { return &Volatile{Type: t} }},
 		{"restrict", func(t Type) Type { return &Restrict{Type: t} }},
 		{"typedef", func(t Type) Type { return &Typedef{Type: t} }},
+		{"type tag", func(t Type) Type { return &typeTag{Type: t} }},
 	}
 
 	for _, test := range wrappers {
diff --git a/elf_reader_test.go b/elf_reader_test.go
index f9edc99..385b645 100644
--- a/elf_reader_test.go
+++ b/elf_reader_test.go
@@ -740,9 +740,6 @@ func TestLibBPFCompat(t *testing.T) {
 		case "bloom_filter_map.o", "bloom_filter_map.linked3.o",
 			"bloom_filter_bench.o", "bloom_filter_bench.linked3.o":
 			t.Skip("Skipping due to missing MapExtra field in MapSpec")
-		case "btf_type_tag.o", "btf_type_tag.linked3.o", "test_btf_decl_tag.o",
-			"test_btf_decl_tag.linked3.o":
-			t.Skip("Skipping due to missing support for BTF_KIND_TYPE_TAG and BTF_KIND_DECL_TAG")
 		case "netif_receive_skb.linked3.o":
 			t.Skip("Skipping due to possible bug in upstream CO-RE generation")
 		}