github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/btf/format.go (about)

     1  package btf
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  )
     8  
     9  var errNestedTooDeep = errors.New("nested too deep")
    10  
    11  // GoFormatter converts a Type to Go syntax.
    12  //
    13  // A zero GoFormatter is valid to use.
    14  type GoFormatter struct {
    15  	w strings.Builder
    16  
    17  	// Types present in this map are referred to using the given name if they
    18  	// are encountered when outputting another type.
    19  	Names map[Type]string
    20  
    21  	// Identifier is called for each field of struct-like types. By default the
    22  	// field name is used as is.
    23  	Identifier func(string) string
    24  
    25  	// EnumIdentifier is called for each element of an enum. By default the
    26  	// name of the enum type is concatenated with Identifier(element).
    27  	EnumIdentifier func(name, element string) string
    28  }
    29  
    30  // TypeDeclaration generates a Go type declaration for a BTF type.
    31  func (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) {
    32  	gf.w.Reset()
    33  	if err := gf.writeTypeDecl(name, typ); err != nil {
    34  		return "", err
    35  	}
    36  	return gf.w.String(), nil
    37  }
    38  
    39  func (gf *GoFormatter) identifier(s string) string {
    40  	if gf.Identifier != nil {
    41  		return gf.Identifier(s)
    42  	}
    43  
    44  	return s
    45  }
    46  
    47  func (gf *GoFormatter) enumIdentifier(name, element string) string {
    48  	if gf.EnumIdentifier != nil {
    49  		return gf.EnumIdentifier(name, element)
    50  	}
    51  
    52  	return name + gf.identifier(element)
    53  }
    54  
    55  // writeTypeDecl outputs a declaration of the given type.
    56  //
    57  // It encodes https://golang.org/ref/spec#Type_declarations:
    58  //
    59  //	type foo struct { bar uint32; }
    60  //	type bar int32
    61  func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {
    62  	if name == "" {
    63  		return fmt.Errorf("need a name for type %s", typ)
    64  	}
    65  
    66  	typ = skipQualifiers(typ)
    67  	fmt.Fprintf(&gf.w, "type %s ", name)
    68  	if err := gf.writeTypeLit(typ, 0); err != nil {
    69  		return err
    70  	}
    71  
    72  	e, ok := typ.(*Enum)
    73  	if !ok || len(e.Values) == 0 {
    74  		return nil
    75  	}
    76  
    77  	gf.w.WriteString("; const ( ")
    78  	for _, ev := range e.Values {
    79  		id := gf.enumIdentifier(name, ev.Name)
    80  		var value any
    81  		if e.Signed {
    82  			value = int64(ev.Value)
    83  		} else {
    84  			value = ev.Value
    85  		}
    86  		fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, value)
    87  	}
    88  	gf.w.WriteString(")")
    89  
    90  	return nil
    91  }
    92  
    93  // writeType outputs the name of a named type or a literal describing the type.
    94  //
    95  // It encodes https://golang.org/ref/spec#Types.
    96  //
    97  //	foo                  (if foo is a named type)
    98  //	uint32
    99  func (gf *GoFormatter) writeType(typ Type, depth int) error {
   100  	typ = skipQualifiers(typ)
   101  
   102  	name := gf.Names[typ]
   103  	if name != "" {
   104  		gf.w.WriteString(name)
   105  		return nil
   106  	}
   107  
   108  	return gf.writeTypeLit(typ, depth)
   109  }
   110  
   111  // writeTypeLit outputs a literal describing the type.
   112  //
   113  // The function ignores named types.
   114  //
   115  // It encodes https://golang.org/ref/spec#TypeLit.
   116  //
   117  //	struct { bar uint32; }
   118  //	uint32
   119  func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
   120  	depth++
   121  	if depth > maxResolveDepth {
   122  		return errNestedTooDeep
   123  	}
   124  
   125  	var err error
   126  	switch v := skipQualifiers(typ).(type) {
   127  	case *Int:
   128  		err = gf.writeIntLit(v)
   129  
   130  	case *Enum:
   131  		if !v.Signed {
   132  			gf.w.WriteRune('u')
   133  		}
   134  		switch v.Size {
   135  		case 1:
   136  			gf.w.WriteString("int8")
   137  		case 2:
   138  			gf.w.WriteString("int16")
   139  		case 4:
   140  			gf.w.WriteString("int32")
   141  		case 8:
   142  			gf.w.WriteString("int64")
   143  		default:
   144  			err = fmt.Errorf("invalid enum size %d", v.Size)
   145  		}
   146  
   147  	case *Typedef:
   148  		err = gf.writeType(v.Type, depth)
   149  
   150  	case *Array:
   151  		fmt.Fprintf(&gf.w, "[%d]", v.Nelems)
   152  		err = gf.writeType(v.Type, depth)
   153  
   154  	case *Struct:
   155  		err = gf.writeStructLit(v.Size, v.Members, depth)
   156  
   157  	case *Union:
   158  		// Always choose the first member to represent the union in Go.
   159  		err = gf.writeStructLit(v.Size, v.Members[:1], depth)
   160  
   161  	case *Datasec:
   162  		err = gf.writeDatasecLit(v, depth)
   163  
   164  	default:
   165  		return fmt.Errorf("type %T: %w", v, ErrNotSupported)
   166  	}
   167  
   168  	if err != nil {
   169  		return fmt.Errorf("%s: %w", typ, err)
   170  	}
   171  
   172  	return nil
   173  }
   174  
   175  func (gf *GoFormatter) writeIntLit(i *Int) error {
   176  	bits := i.Size * 8
   177  	switch i.Encoding {
   178  	case Bool:
   179  		if i.Size != 1 {
   180  			return fmt.Errorf("bool with size %d", i.Size)
   181  		}
   182  		gf.w.WriteString("bool")
   183  	case Char:
   184  		if i.Size != 1 {
   185  			return fmt.Errorf("char with size %d", i.Size)
   186  		}
   187  		// BTF doesn't have a way to specify the signedness of a char. Assume
   188  		// we are dealing with unsigned, since this works nicely with []byte
   189  		// in Go code.
   190  		fallthrough
   191  	case Unsigned, Signed:
   192  		stem := "uint"
   193  		if i.Encoding == Signed {
   194  			stem = "int"
   195  		}
   196  		if i.Size > 8 {
   197  			fmt.Fprintf(&gf.w, "[%d]byte /* %s%d */", i.Size, stem, i.Size*8)
   198  		} else {
   199  			fmt.Fprintf(&gf.w, "%s%d", stem, bits)
   200  		}
   201  	default:
   202  		return fmt.Errorf("can't encode %s", i.Encoding)
   203  	}
   204  	return nil
   205  }
   206  
   207  func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error {
   208  	gf.w.WriteString("struct { ")
   209  
   210  	prevOffset := uint32(0)
   211  	skippedBitfield := false
   212  	for i, m := range members {
   213  		if m.BitfieldSize > 0 {
   214  			skippedBitfield = true
   215  			continue
   216  		}
   217  
   218  		offset := m.Offset.Bytes()
   219  		if n := offset - prevOffset; skippedBitfield && n > 0 {
   220  			fmt.Fprintf(&gf.w, "_ [%d]byte /* unsupported bitfield */; ", n)
   221  		} else {
   222  			gf.writePadding(n)
   223  		}
   224  
   225  		fieldSize, err := Sizeof(m.Type)
   226  		if err != nil {
   227  			return fmt.Errorf("field %d: %w", i, err)
   228  		}
   229  
   230  		prevOffset = offset + uint32(fieldSize)
   231  		if prevOffset > size {
   232  			return fmt.Errorf("field %d of size %d exceeds type size %d", i, fieldSize, size)
   233  		}
   234  
   235  		if err := gf.writeStructField(m, depth); err != nil {
   236  			return fmt.Errorf("field %d: %w", i, err)
   237  		}
   238  	}
   239  
   240  	gf.writePadding(size - prevOffset)
   241  	gf.w.WriteString("}")
   242  	return nil
   243  }
   244  
   245  func (gf *GoFormatter) writeStructField(m Member, depth int) error {
   246  	if m.BitfieldSize > 0 {
   247  		return fmt.Errorf("bitfields are not supported")
   248  	}
   249  	if m.Offset%8 != 0 {
   250  		return fmt.Errorf("unsupported offset %d", m.Offset)
   251  	}
   252  
   253  	if m.Name == "" {
   254  		// Special case a nested anonymous union like
   255  		//     struct foo { union { int bar; int baz }; }
   256  		// by replacing the whole union with its first member.
   257  		union, ok := m.Type.(*Union)
   258  		if !ok {
   259  			return fmt.Errorf("anonymous fields are not supported")
   260  
   261  		}
   262  
   263  		if len(union.Members) == 0 {
   264  			return errors.New("empty anonymous union")
   265  		}
   266  
   267  		depth++
   268  		if depth > maxResolveDepth {
   269  			return errNestedTooDeep
   270  		}
   271  
   272  		m := union.Members[0]
   273  		size, err := Sizeof(m.Type)
   274  		if err != nil {
   275  			return err
   276  		}
   277  
   278  		if err := gf.writeStructField(m, depth); err != nil {
   279  			return err
   280  		}
   281  
   282  		gf.writePadding(union.Size - uint32(size))
   283  		return nil
   284  
   285  	}
   286  
   287  	fmt.Fprintf(&gf.w, "%s ", gf.identifier(m.Name))
   288  
   289  	if err := gf.writeType(m.Type, depth); err != nil {
   290  		return err
   291  	}
   292  
   293  	gf.w.WriteString("; ")
   294  	return nil
   295  }
   296  
   297  func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error {
   298  	gf.w.WriteString("struct { ")
   299  
   300  	prevOffset := uint32(0)
   301  	for i, vsi := range ds.Vars {
   302  		v, ok := vsi.Type.(*Var)
   303  		if !ok {
   304  			return fmt.Errorf("can't format %s as part of data section", vsi.Type)
   305  		}
   306  
   307  		if v.Linkage != GlobalVar {
   308  			// Ignore static, extern, etc. for now.
   309  			continue
   310  		}
   311  
   312  		if v.Name == "" {
   313  			return fmt.Errorf("variable %d: empty name", i)
   314  		}
   315  
   316  		gf.writePadding(vsi.Offset - prevOffset)
   317  		prevOffset = vsi.Offset + vsi.Size
   318  
   319  		fmt.Fprintf(&gf.w, "%s ", gf.identifier(v.Name))
   320  
   321  		if err := gf.writeType(v.Type, depth); err != nil {
   322  			return fmt.Errorf("variable %d: %w", i, err)
   323  		}
   324  
   325  		gf.w.WriteString("; ")
   326  	}
   327  
   328  	gf.writePadding(ds.Size - prevOffset)
   329  	gf.w.WriteString("}")
   330  	return nil
   331  }
   332  
   333  func (gf *GoFormatter) writePadding(bytes uint32) {
   334  	if bytes > 0 {
   335  		fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes)
   336  	}
   337  }
   338  
   339  func skipQualifiers(typ Type) Type {
   340  	result := typ
   341  	for depth := 0; depth <= maxResolveDepth; depth++ {
   342  		switch v := (result).(type) {
   343  		case qualifier:
   344  			result = v.qualify()
   345  		default:
   346  			return result
   347  		}
   348  	}
   349  	return &cycle{typ}
   350  }