summaryrefslogtreecommitdiff
path: root/vendor/github.com/cilium/ebpf/btf/format.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cilium/ebpf/btf/format.go')
-rw-r--r--vendor/github.com/cilium/ebpf/btf/format.go319
1 files changed, 319 insertions, 0 deletions
diff --git a/vendor/github.com/cilium/ebpf/btf/format.go b/vendor/github.com/cilium/ebpf/btf/format.go
new file mode 100644
index 000000000..e7688a2a6
--- /dev/null
+++ b/vendor/github.com/cilium/ebpf/btf/format.go
@@ -0,0 +1,319 @@
+package btf
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+)
+
+var errNestedTooDeep = errors.New("nested too deep")
+
+// GoFormatter converts a Type to Go syntax.
+//
+// A zero GoFormatter is valid to use.
+type GoFormatter struct {
+ w strings.Builder
+
+ // Types present in this map are referred to using the given name if they
+ // are encountered when outputting another type.
+ Names map[Type]string
+
+ // Identifier is called for each field of struct-like types. By default the
+ // field name is used as is.
+ Identifier func(string) string
+
+ // EnumIdentifier is called for each element of an enum. By default the
+ // name of the enum type is concatenated with Identifier(element).
+ EnumIdentifier func(name, element string) string
+}
+
+// TypeDeclaration generates a Go type declaration for a BTF type.
+func (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) {
+ gf.w.Reset()
+ if err := gf.writeTypeDecl(name, typ); err != nil {
+ return "", err
+ }
+ return gf.w.String(), nil
+}
+
+func (gf *GoFormatter) identifier(s string) string {
+ if gf.Identifier != nil {
+ return gf.Identifier(s)
+ }
+
+ return s
+}
+
+func (gf *GoFormatter) enumIdentifier(name, element string) string {
+ if gf.EnumIdentifier != nil {
+ return gf.EnumIdentifier(name, element)
+ }
+
+ return name + gf.identifier(element)
+}
+
+// writeTypeDecl outputs a declaration of the given type.
+//
+// It encodes https://golang.org/ref/spec#Type_declarations:
+//
+// type foo struct { bar uint32; }
+// type bar int32
+func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {
+ if name == "" {
+ return fmt.Errorf("need a name for type %s", typ)
+ }
+
+ switch v := skipQualifiers(typ).(type) {
+ case *Enum:
+ fmt.Fprintf(&gf.w, "type %s ", name)
+ switch v.Size {
+ case 1:
+ gf.w.WriteString("int8")
+ case 2:
+ gf.w.WriteString("int16")
+ case 4:
+ gf.w.WriteString("int32")
+ case 8:
+ gf.w.WriteString("int64")
+ default:
+ return fmt.Errorf("%s: invalid enum size %d", typ, v.Size)
+ }
+
+ if len(v.Values) == 0 {
+ return nil
+ }
+
+ gf.w.WriteString("; const ( ")
+ for _, ev := range v.Values {
+ id := gf.enumIdentifier(name, ev.Name)
+ fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value)
+ }
+ gf.w.WriteString(")")
+
+ return nil
+
+ default:
+ fmt.Fprintf(&gf.w, "type %s ", name)
+ return gf.writeTypeLit(v, 0)
+ }
+}
+
+// writeType outputs the name of a named type or a literal describing the type.
+//
+// It encodes https://golang.org/ref/spec#Types.
+//
+// foo (if foo is a named type)
+// uint32
+func (gf *GoFormatter) writeType(typ Type, depth int) error {
+ typ = skipQualifiers(typ)
+
+ name := gf.Names[typ]
+ if name != "" {
+ gf.w.WriteString(name)
+ return nil
+ }
+
+ return gf.writeTypeLit(typ, depth)
+}
+
+// writeTypeLit outputs a literal describing the type.
+//
+// The function ignores named types.
+//
+// It encodes https://golang.org/ref/spec#TypeLit.
+//
+// struct { bar uint32; }
+// uint32
+func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
+ depth++
+ if depth > maxTypeDepth {
+ return errNestedTooDeep
+ }
+
+ var err error
+ switch v := skipQualifiers(typ).(type) {
+ case *Int:
+ gf.writeIntLit(v)
+
+ case *Enum:
+ gf.w.WriteString("int32")
+
+ case *Typedef:
+ err = gf.writeType(v.Type, depth)
+
+ case *Array:
+ fmt.Fprintf(&gf.w, "[%d]", v.Nelems)
+ err = gf.writeType(v.Type, depth)
+
+ case *Struct:
+ err = gf.writeStructLit(v.Size, v.Members, depth)
+
+ case *Union:
+ // Always choose the first member to represent the union in Go.
+ err = gf.writeStructLit(v.Size, v.Members[:1], depth)
+
+ case *Datasec:
+ err = gf.writeDatasecLit(v, depth)
+
+ default:
+ return fmt.Errorf("type %T: %w", v, ErrNotSupported)
+ }
+
+ if err != nil {
+ return fmt.Errorf("%s: %w", typ, err)
+ }
+
+ return nil
+}
+
+func (gf *GoFormatter) writeIntLit(i *Int) {
+ // NB: Encoding.IsChar is ignored.
+ if i.Encoding.IsBool() && i.Size == 1 {
+ gf.w.WriteString("bool")
+ return
+ }
+
+ bits := i.Size * 8
+ if i.Encoding.IsSigned() {
+ fmt.Fprintf(&gf.w, "int%d", bits)
+ } else {
+ fmt.Fprintf(&gf.w, "uint%d", bits)
+ }
+}
+
+func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error {
+ gf.w.WriteString("struct { ")
+
+ prevOffset := uint32(0)
+ skippedBitfield := false
+ for i, m := range members {
+ if m.BitfieldSize > 0 {
+ skippedBitfield = true
+ continue
+ }
+
+ offset := m.Offset.Bytes()
+ if n := offset - prevOffset; skippedBitfield && n > 0 {
+ fmt.Fprintf(&gf.w, "_ [%d]byte /* unsupported bitfield */; ", n)
+ } else {
+ gf.writePadding(n)
+ }
+
+ size, err := Sizeof(m.Type)
+ if err != nil {
+ return fmt.Errorf("field %d: %w", i, err)
+ }
+ prevOffset = offset + uint32(size)
+
+ if err := gf.writeStructField(m, depth); err != nil {
+ return fmt.Errorf("field %d: %w", i, err)
+ }
+ }
+
+ gf.writePadding(size - prevOffset)
+ gf.w.WriteString("}")
+ return nil
+}
+
+func (gf *GoFormatter) writeStructField(m Member, depth int) error {
+ if m.BitfieldSize > 0 {
+ return fmt.Errorf("bitfields are not supported")
+ }
+ if m.Offset%8 != 0 {
+ return fmt.Errorf("unsupported offset %d", m.Offset)
+ }
+
+ if m.Name == "" {
+ // Special case a nested anonymous union like
+ // struct foo { union { int bar; int baz }; }
+ // by replacing the whole union with its first member.
+ union, ok := m.Type.(*Union)
+ if !ok {
+ return fmt.Errorf("anonymous fields are not supported")
+
+ }
+
+ if len(union.Members) == 0 {
+ return errors.New("empty anonymous union")
+ }
+
+ depth++
+ if depth > maxTypeDepth {
+ return errNestedTooDeep
+ }
+
+ m := union.Members[0]
+ size, err := Sizeof(m.Type)
+ if err != nil {
+ return err
+ }
+
+ if err := gf.writeStructField(m, depth); err != nil {
+ return err
+ }
+
+ gf.writePadding(union.Size - uint32(size))
+ return nil
+
+ }
+
+ fmt.Fprintf(&gf.w, "%s ", gf.identifier(m.Name))
+
+ if err := gf.writeType(m.Type, depth); err != nil {
+ return err
+ }
+
+ gf.w.WriteString("; ")
+ return nil
+}
+
+func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error {
+ gf.w.WriteString("struct { ")
+
+ prevOffset := uint32(0)
+ for i, vsi := range ds.Vars {
+ v := vsi.Type.(*Var)
+ if v.Linkage != GlobalVar {
+ // Ignore static, extern, etc. for now.
+ continue
+ }
+
+ if v.Name == "" {
+ return fmt.Errorf("variable %d: empty name", i)
+ }
+
+ gf.writePadding(vsi.Offset - prevOffset)
+ prevOffset = vsi.Offset + vsi.Size
+
+ fmt.Fprintf(&gf.w, "%s ", gf.identifier(v.Name))
+
+ if err := gf.writeType(v.Type, depth); err != nil {
+ return fmt.Errorf("variable %d: %w", i, err)
+ }
+
+ gf.w.WriteString("; ")
+ }
+
+ gf.writePadding(ds.Size - prevOffset)
+ gf.w.WriteString("}")
+ return nil
+}
+
+func (gf *GoFormatter) writePadding(bytes uint32) {
+ if bytes > 0 {
+ fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes)
+ }
+}
+
+func skipQualifiers(typ Type) Type {
+ result := typ
+ for depth := 0; depth <= maxTypeDepth; depth++ {
+ switch v := (result).(type) {
+ case qualifier:
+ result = v.qualify()
+ default:
+ return result
+ }
+ }
+ return &cycle{typ}
+}