diff --git a/btf/btf_gen_types.go b/btf/btf_gen_types.go new file mode 100644 index 000000000..9e91efdd6 --- /dev/null +++ b/btf/btf_gen_types.go @@ -0,0 +1,47 @@ +// Code generated by btf/cmd/genbtftypes; DO NOT EDIT. + +package btf + +type btfArray struct { + Type TypeID + IndexType TypeID + Nelems uint32 +} + +type btfDeclTag struct{ ComponentIdx uint32 } + +type btfEnum struct { + NameOff uint32 + Val uint32 +} + +type btfEnum64 struct { + NameOff uint32 + ValLo32 uint32 + ValHi32 uint32 +} + +type btfMember struct { + NameOff uint32 + Type TypeID + Offset uint32 +} + +type btfParam struct { + NameOff uint32 + Type TypeID +} + +type btfType struct { + NameOff uint32 + Info uint32 + SizeType uint32 +} + +type btfVarSecinfo struct { + Type TypeID + Offset uint32 + Size uint32 +} + +type btfVariable struct{ Linkage uint32 } diff --git a/btf/btf_types.go b/btf/btf_types.go index f0e327abc..f7ea780e2 100644 --- a/btf/btf_types.go +++ b/btf/btf_types.go @@ -131,28 +131,6 @@ func parseBTFHeader(r io.Reader, bo binary.ByteOrder) (*btfHeader, error) { var btfTypeLen = binary.Size(btfType{}) -// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst. -type btfType struct { - NameOff uint32 - /* "info" bits arrangement - * bits 0-15: vlen (e.g. # of struct's members), linkage - * bits 16-23: unused - * bits 24-28: kind (e.g. int, ptr, array...etc) - * bits 29-30: unused - * bit 31: kind_flag, currently used by - * struct, union and fwd - */ - Info uint32 - /* "size" is used by INT, ENUM, STRUCT and UNION. - * "size" tells the size of the type it is describing. - * - * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, - * FUNC and FUNC_PROTO. - * "type" is a type_id referring to another type. - */ - SizeType uint32 -} - var btfTypeSize = int(unsafe.Sizeof(btfType{})) func unmarshalBtfType(bt *btfType, b []byte, bo binary.ByteOrder) (int, error) { @@ -348,12 +326,6 @@ func (bi *btfInt) SetBits(bits byte) { bi.Raw = writeBits(bi.Raw, btfIntBitsLen, btfIntBitsShift, uint32(bits)) } -type btfArray struct { - Type TypeID - IndexType TypeID - Nelems uint32 -} - var btfArrayLen = int(unsafe.Sizeof(btfArray{})) func unmarshalBtfArray(ba *btfArray, b []byte, bo binary.ByteOrder) (int, error) { @@ -367,12 +339,6 @@ func unmarshalBtfArray(ba *btfArray, b []byte, bo binary.ByteOrder) (int, error) return btfArrayLen, nil } -type btfMember struct { - NameOff uint32 - Type TypeID - Offset uint32 -} - var btfMemberLen = int(unsafe.Sizeof(btfMember{})) func unmarshalBtfMembers(members []btfMember, b []byte, bo binary.ByteOrder) (int, error) { @@ -392,12 +358,6 @@ func unmarshalBtfMembers(members []btfMember, b []byte, bo binary.ByteOrder) (in return off, nil } -type btfVarSecinfo struct { - Type TypeID - Offset uint32 - Size uint32 -} - var btfVarSecinfoLen = int(unsafe.Sizeof(btfVarSecinfo{})) func unmarshalBtfVarSecInfos(secinfos []btfVarSecinfo, b []byte, bo binary.ByteOrder) (int, error) { @@ -417,10 +377,6 @@ func unmarshalBtfVarSecInfos(secinfos []btfVarSecinfo, b []byte, bo binary.ByteO return off, nil } -type btfVariable struct { - Linkage uint32 -} - var btfVariableLen = int(unsafe.Sizeof(btfVariable{})) func unmarshalBtfVariable(bv *btfVariable, b []byte, bo binary.ByteOrder) (int, error) { @@ -432,11 +388,6 @@ func unmarshalBtfVariable(bv *btfVariable, b []byte, bo binary.ByteOrder) (int, return btfVariableLen, nil } -type btfEnum struct { - NameOff uint32 - Val uint32 -} - var btfEnumLen = int(unsafe.Sizeof(btfEnum{})) func unmarshalBtfEnums(enums []btfEnum, b []byte, bo binary.ByteOrder) (int, error) { @@ -455,12 +406,6 @@ func unmarshalBtfEnums(enums []btfEnum, b []byte, bo binary.ByteOrder) (int, err return off, nil } -type btfEnum64 struct { - NameOff uint32 - ValLo32 uint32 - ValHi32 uint32 -} - var btfEnum64Len = int(unsafe.Sizeof(btfEnum64{})) func unmarshalBtfEnums64(enums []btfEnum64, b []byte, bo binary.ByteOrder) (int, error) { @@ -480,11 +425,6 @@ func unmarshalBtfEnums64(enums []btfEnum64, b []byte, bo binary.ByteOrder) (int, return off, nil } -type btfParam struct { - NameOff uint32 - Type TypeID -} - var btfParamLen = int(unsafe.Sizeof(btfParam{})) func unmarshalBtfParams(params []btfParam, b []byte, bo binary.ByteOrder) (int, error) { @@ -503,10 +443,6 @@ func unmarshalBtfParams(params []btfParam, b []byte, bo binary.ByteOrder) (int, return off, nil } -type btfDeclTag struct { - ComponentIdx uint32 -} - var btfDeclTagLen = int(unsafe.Sizeof(btfDeclTag{})) func unmarshalBtfDeclTag(bdt *btfDeclTag, b []byte, bo binary.ByteOrder) (int, error) { diff --git a/btf/cmd/genbtftypes/.gitignore b/btf/cmd/genbtftypes/.gitignore new file mode 100644 index 000000000..16255677b --- /dev/null +++ b/btf/cmd/genbtftypes/.gitignore @@ -0,0 +1 @@ +genbtftypes \ No newline at end of file diff --git a/btf/cmd/genbtftypes/main.go b/btf/cmd/genbtftypes/main.go new file mode 100644 index 000000000..1d5916a40 --- /dev/null +++ b/btf/cmd/genbtftypes/main.go @@ -0,0 +1,272 @@ +// Program gentypes reads a compressed vmlinux .BTF section and generates +// types from it. +// +// Output is written to "btf_gen_types.go". +package main + +import ( + "bytes" + "errors" + "fmt" + "os" + "sort" + "strings" + + "github.com/cilium/ebpf/btf" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/sys" + + "golang.org/x/exp/slices" +) + +func main() { + if err := run(os.Args[1:]); err != nil { + fmt.Fprintln(os.Stderr, "Error:", err) + os.Exit(1) + } +} + +func run(args []string) error { + if len(args) != 1 { + return fmt.Errorf("expect location of compressed vmlinux .BTF as argument") + } + + raw, err := internal.ReadAllCompressed(args[0]) + if err != nil { + return err + } + + spec, err := btf.LoadSpecFromReader(bytes.NewReader(raw)) + if err != nil { + return err + } + + output, err := generateTypes(spec) + var fpe *failedPatchError + if errors.As(err, &fpe) { + fmt.Fprintf(os.Stderr, " %v\n", fpe.Type) + for _, member := range fpe.Type.Members { + fmt.Fprintf(os.Stderr, " %q %v\n", member.Name, member.Type) + } + } + if err != nil { + return err + } + + w, err := os.Create("btf_gen_types.go") + if err != nil { + return err + } + defer w.Close() + + return internal.WriteFormatted(output, w) +} + +func generateTypes(spec *btf.Spec) ([]byte, error) { + typeID := &btf.Int{Size: 4} + uint32T := &btf.Int{Size: 4, Encoding: btf.Unsigned} + + gf := &btf.GoFormatter{ + Names: map[btf.Type]string{ + typeID: internal.GoTypeName(sys.TypeID(0)), + }, + Identifier: internal.Identifier, + EnumIdentifier: func(name, element string) string { + return element + }, + } + + w := bytes.NewBuffer(nil) + w.WriteString(`// Code generated by btf/cmd/genbtftypes; DO NOT EDIT. + +package btf + +`) + + // Assorted structs + + structs := []struct { + goType string + cType string + patches []patch + }{ + { + "btfType", "btf_type", + []patch{ + flattenAnon, + rename("size", "size_type"), + }, + }, + { + "btfEnum", "btf_enum", + []patch{ + replace(uint32T, "val"), + }, + }, + { + "btfArray", "btf_array", + []patch{ + replace(typeID, "type", "index_type"), + }, + }, + { + "btfMember", "btf_member", + []patch{ + replace(typeID, "type"), + }, + }, + { + "btfParam", "btf_param", + []patch{ + replace(typeID, "type"), + }, + }, + {"btfVariable", "btf_var", nil}, + { + "btfVarSecinfo", "btf_var_secinfo", + []patch{ + replace(typeID, "type"), + }, + }, + { + "btfDeclTag", "btf_decl_tag", + []patch{ + replace(uint32T, "component_idx"), + }, + }, + {"btfEnum64", "btf_enum64", nil}, + } + + sort.Slice(structs, func(i, j int) bool { + return structs[i].goType < structs[j].goType + }) + + for _, s := range structs { + fmt.Println("struct", s.goType) + + var t *btf.Struct + if err := spec.TypeByName(s.cType, &t); err != nil { + return nil, err + } + + if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil { + return nil, fmt.Errorf("output %q: %w", s.goType, err) + } + } + + return w.Bytes(), nil +} + +type failedPatchError struct { + Type *btf.Struct + number int + err error +} + +func (fpe *failedPatchError) Unwrap() error { + return fpe.err +} + +func (fpe *failedPatchError) Error() string { + return fmt.Sprintf("patch %d: %v", fpe.number, fpe.err) +} + +func outputPatchedStruct(gf *btf.GoFormatter, w *bytes.Buffer, id string, s *btf.Struct, patches []patch) error { + s = btf.Copy(s, nil).(*btf.Struct) + + for i, p := range patches { + if err := p(s); err != nil { + return &failedPatchError{s, i, err} + } + } + + decl, err := gf.TypeDeclaration(id, s) + if err != nil { + return err + } + + w.WriteString(decl) + w.WriteString("\n\n") + return nil +} + +type patch func(*btf.Struct) error + +func modify(fn func(*btf.Member) error, members ...string) patch { + return func(s *btf.Struct) error { + want := make(map[string]bool) + for _, name := range members { + want[name] = true + } + + for i, m := range s.Members { + if want[m.Name] { + if err := fn(&s.Members[i]); err != nil { + return err + } + delete(want, m.Name) + } + } + + if len(want) == 0 { + return nil + } + + var missing []string + for name := range want { + missing = append(missing, name) + } + sort.Strings(missing) + + return fmt.Errorf("missing members: %v", strings.Join(missing, ", ")) + } +} + +func replace(t btf.Type, members ...string) patch { + return modify(func(m *btf.Member) error { + m.Type = t + return nil + }, members...) +} + +func flattenAnon(s *btf.Struct) error { + for i := range s.Members { + m := &s.Members[i] + + if m.Type.TypeName() != "" { + continue + } + + var newMembers []btf.Member + switch cs := m.Type.(type) { + case *btf.Struct: + for j := range cs.Members { + cs.Members[j].Offset += m.Offset + } + newMembers = cs.Members + + case *btf.Union: + cs.Members[0].Offset += m.Offset + newMembers = []btf.Member{cs.Members[0]} + + default: + continue + } + + s.Members = slices.Replace(s.Members, i, i+1, newMembers...) + } + + return nil +} + +func rename(from, to string) patch { + return func(s *btf.Struct) error { + for i, m := range s.Members { + if m.Name == from { + s.Members[i].Name = to + return nil + } + } + return fmt.Errorf("no member named %q", from) + } +} diff --git a/btf/doc.go b/btf/doc.go index b1f4b1fc3..ea2540cc9 100644 --- a/btf/doc.go +++ b/btf/doc.go @@ -3,3 +3,7 @@ // The canonical documentation lives in the Linux kernel repository and is // available at https://www.kernel.org/doc/html/latest/bpf/btf.html package btf + +// Regenerate btf_gen_types.go by invoking go generate in the current directory. + +//go:generate go run github.com/cilium/ebpf/btf/cmd/genbtftypes testdata/vmlinux.btf.gz