github.com/cilium/ebpf@v0.10.0/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  		fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value)
    81  	}
    82  	gf.w.WriteString(")")
    83  
    84  	return nil
    85  }
    86  
    87  // writeType outputs the name of a named type or a literal describing the type.
    88  //
    89  // It encodes https://golang.org/ref/spec#Types.
    90  //
    91  //	foo                  (if foo is a named type)
    92  //	uint32
    93  func (gf *GoFormatter) writeType(typ Type, depth int) error {
    94  	typ = skipQualifiers(typ)
    95  
    96  	name := gf.Names[typ]
    97  	if name != "" {
    98  		gf.w.WriteString(name)
    99  		return nil
   100  	}
   101  
   102  	return gf.writeTypeLit(typ, depth)
   103  }
   104  
   105  // writeTypeLit outputs a literal describing the type.
   106  //
   107  // The function ignores named types.
   108  //
   109  // It encodes https://golang.org/ref/spec#TypeLit.
   110  //
   111  //	struct { bar uint32; }
   112  //	uint32
   113  func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
   114  	depth++
   115  	if depth > maxTypeDepth {
   116  		return errNestedTooDeep
   117  	}
   118  
   119  	var err error
   120  	switch v := skipQualifiers(typ).(type) {
   121  	case *Int:
   122  		err = gf.writeIntLit(v)
   123  
   124  	case *Enum:
   125  		if !v.Signed {
   126  			gf.w.WriteRune('u')
   127  		}
   128  		switch v.Size {
   129  		case 1:
   130  			gf.w.WriteString("int8")
   131  		case 2:
   132  			gf.w.WriteString("int16")
   133  		case 4:
   134  			gf.w.WriteString("int32")
   135  		case 8:
   136  			gf.w.WriteString("int64")
   137  		default:
   138  			err = fmt.Errorf("invalid enum size %d", v.Size)
   139  		}
   140  
   141  	case *Typedef:
   142  		err = gf.writeType(v.Type, depth)
   143  
   144  	case *Array:
   145  		fmt.Fprintf(&gf.w, "[%d]", v.Nelems)
   146  		err = gf.writeType(v.Type, depth)
   147  
   148  	case *Struct:
   149  		err = gf.writeStructLit(v.Size, v.Members, depth)
   150  
   151  	case *Union:
   152  		// Always choose the first member to represent the union in Go.
   153  		err = gf.writeStructLit(v.Size, v.Members[:1], depth)
   154  
   155  	case *Datasec:
   156  		err = gf.writeDatasecLit(v, depth)
   157  
   158  	default:
   159  		return fmt.Errorf("type %T: %w", v, ErrNotSupported)
   160  	}
   161  
   162  	if err != nil {
   163  		return fmt.Errorf("%s: %w", typ, err)
   164  	}
   165  
   166  	return nil
   167  }
   168  
   169  func (gf *GoFormatter) writeIntLit(i *Int) error {
   170  	bits := i.Size * 8
   171  	switch i.Encoding {
   172  	case Bool:
   173  		if i.Size != 1 {
   174  			return fmt.Errorf("bool with size %d", i.Size)
   175  		}
   176  		gf.w.WriteString("bool")
   177  	case Char:
   178  		if i.Size != 1 {
   179  			return fmt.Errorf("char with size %d", i.Size)
   180  		}
   181  		// BTF doesn't have a way to specify the signedness of a char. Assume
   182  		// we are dealing with unsigned, since this works nicely with []byte
   183  		// in Go code.
   184  		fallthrough
   185  	case Unsigned, Signed:
   186  		stem := "uint"
   187  		if i.Encoding == Signed {
   188  			stem = "int"
   189  		}
   190  		if i.Size > 8 {
   191  			fmt.Fprintf(&gf.w, "[%d]byte /* %s%d */", i.Size, stem, i.Size*8)
   192  		} else {
   193  			fmt.Fprintf(&gf.w, "%s%d", stem, bits)
   194  		}
   195  	default:
   196  		return fmt.Errorf("can't encode %s", i.Encoding)
   197  	}
   198  	return nil
   199  }
   200  
   201  func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error {
   202  	gf.w.WriteString("struct { ")
   203  
   204  	prevOffset := uint32(0)
   205  	skippedBitfield := false
   206  	for i, m := range members {
   207  		if m.BitfieldSize > 0 {
   208  			skippedBitfield = true
   209  			continue
   210  		}
   211  
   212  		offset := m.Offset.Bytes()
   213  		if n := offset - prevOffset; skippedBitfield && n > 0 {
   214  			fmt.Fprintf(&gf.w, "_ [%d]byte /* unsupported bitfield */; ", n)
   215  		} else {
   216  			gf.writePadding(n)
   217  		}
   218  
   219  		fieldSize, err := Sizeof(m.Type)
   220  		if err != nil {
   221  			return fmt.Errorf("field %d: %w", i, err)
   222  		}
   223  
   224  		prevOffset = offset + uint32(fieldSize)
   225  		if prevOffset > size {
   226  			return fmt.Errorf("field %d of size %d exceeds type size %d", i, fieldSize, size)
   227  		}
   228  
   229  		if err := gf.writeStructField(m, depth); err != nil {
   230  			return fmt.Errorf("field %d: %w", i, err)
   231  		}
   232  	}
   233  
   234  	gf.writePadding(size - prevOffset)
   235  	gf.w.WriteString("}")
   236  	return nil
   237  }
   238  
   239  func (gf *GoFormatter) writeStructField(m Member, depth int) error {
   240  	if m.BitfieldSize > 0 {
   241  		return fmt.Errorf("bitfields are not supported")
   242  	}
   243  	if m.Offset%8 != 0 {
   244  		return fmt.Errorf("unsupported offset %d", m.Offset)
   245  	}
   246  
   247  	if m.Name == "" {
   248  		// Special case a nested anonymous union like
   249  		//     struct foo { union { int bar; int baz }; }
   250  		// by replacing the whole union with its first member.
   251  		union, ok := m.Type.(*Union)
   252  		if !ok {
   253  			return fmt.Errorf("anonymous fields are not supported")
   254  
   255  		}
   256  
   257  		if len(union.Members) == 0 {
   258  			return errors.New("empty anonymous union")
   259  		}
   260  
   261  		depth++
   262  		if depth > maxTypeDepth {
   263  			return errNestedTooDeep
   264  		}
   265  
   266  		m := union.Members[0]
   267  		size, err := Sizeof(m.Type)
   268  		if err != nil {
   269  			return err
   270  		}
   271  
   272  		if err := gf.writeStructField(m, depth); err != nil {
   273  			return err
   274  		}
   275  
   276  		gf.writePadding(union.Size - uint32(size))
   277  		return nil
   278  
   279  	}
   280  
   281  	fmt.Fprintf(&gf.w, "%s ", gf.identifier(m.Name))
   282  
   283  	if err := gf.writeType(m.Type, depth); err != nil {
   284  		return err
   285  	}
   286  
   287  	gf.w.WriteString("; ")
   288  	return nil
   289  }
   290  
   291  func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error {
   292  	gf.w.WriteString("struct { ")
   293  
   294  	prevOffset := uint32(0)
   295  	for i, vsi := range ds.Vars {
   296  		v, ok := vsi.Type.(*Var)
   297  		if !ok {
   298  			return fmt.Errorf("can't format %s as part of data section", vsi.Type)
   299  		}
   300  
   301  		if v.Linkage != GlobalVar {
   302  			// Ignore static, extern, etc. for now.
   303  			continue
   304  		}
   305  
   306  		if v.Name == "" {
   307  			return fmt.Errorf("variable %d: empty name", i)
   308  		}
   309  
   310  		gf.writePadding(vsi.Offset - prevOffset)
   311  		prevOffset = vsi.Offset + vsi.Size
   312  
   313  		fmt.Fprintf(&gf.w, "%s ", gf.identifier(v.Name))
   314  
   315  		if err := gf.writeType(v.Type, depth); err != nil {
   316  			return fmt.Errorf("variable %d: %w", i, err)
   317  		}
   318  
   319  		gf.w.WriteString("; ")
   320  	}
   321  
   322  	gf.writePadding(ds.Size - prevOffset)
   323  	gf.w.WriteString("}")
   324  	return nil
   325  }
   326  
   327  func (gf *GoFormatter) writePadding(bytes uint32) {
   328  	if bytes > 0 {
   329  		fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes)
   330  	}
   331  }
   332  
   333  func skipQualifiers(typ Type) Type {
   334  	result := typ
   335  	for depth := 0; depth <= maxTypeDepth; depth++ {
   336  		switch v := (result).(type) {
   337  		case qualifier:
   338  			result = v.qualify()
   339  		default:
   340  			return result
   341  		}
   342  	}
   343  	return &cycle{typ}
   344  }