From bc34e86c72d2207a7dbe3f46d99f6dfddac65ca1 Mon Sep 17 00:00:00 2001 From: Lorenz Bauer Date: Mon, 25 Jul 2022 16:26:21 +0000 Subject: [PATCH] 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 --- btf/btf_types.go | 87 +++++++++++++---------------------------- btf/btf_types_string.go | 37 +++++++++++++++++- btf/types.go | 85 ++++++++++++++++++++++++++++++++++++++++ btf/types_test.go | 4 ++ elf_reader_test.go | 3 -- 5 files changed, 153 insertions(+), 63 deletions(-) diff --git a/btf/btf_types.go b/btf/btf_types.go index 178d04c0e..7870da58e 100644 --- a/btf/btf_types.go +++ b/btf/btf_types.go @@ -6,33 +6,36 @@ import ( "io" ) -//go:generate go run golang.org/x/tools/cmd/stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage +//go:generate go run golang.org/x/tools/cmd/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 } @@ -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 0e0c17d68..fdb2662b0 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 402a363c2..38ad3dd14 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 } diff --git a/btf/types_test.go b/btf/types_test.go index 5a80db41f..c1437533a 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 960e56265..25f6e8956 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") }