github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/cmd/bpf2go/output.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	_ "embed"
     6  	"fmt"
     7  	"go/build/constraint"
     8  	"go/token"
     9  	"io"
    10  	"sort"
    11  	"strings"
    12  	"text/template"
    13  
    14  	"github.com/cilium/ebpf"
    15  	"github.com/cilium/ebpf/btf"
    16  	"github.com/cilium/ebpf/internal"
    17  )
    18  
    19  //go:embed output.tpl
    20  var commonRaw string
    21  
    22  var commonTemplate = template.Must(template.New("common").Parse(commonRaw))
    23  
    24  type templateName string
    25  
    26  func (n templateName) maybeExport(str string) string {
    27  	if token.IsExported(string(n)) {
    28  		return toUpperFirst(str)
    29  	}
    30  
    31  	return str
    32  }
    33  
    34  func (n templateName) Bytes() string {
    35  	return "_" + toUpperFirst(string(n)) + "Bytes"
    36  }
    37  
    38  func (n templateName) Specs() string {
    39  	return string(n) + "Specs"
    40  }
    41  
    42  func (n templateName) ProgramSpecs() string {
    43  	return string(n) + "ProgramSpecs"
    44  }
    45  
    46  func (n templateName) MapSpecs() string {
    47  	return string(n) + "MapSpecs"
    48  }
    49  
    50  func (n templateName) Load() string {
    51  	return n.maybeExport("load" + toUpperFirst(string(n)))
    52  }
    53  
    54  func (n templateName) LoadObjects() string {
    55  	return n.maybeExport("load" + toUpperFirst(string(n)) + "Objects")
    56  }
    57  
    58  func (n templateName) Objects() string {
    59  	return string(n) + "Objects"
    60  }
    61  
    62  func (n templateName) Maps() string {
    63  	return string(n) + "Maps"
    64  }
    65  
    66  func (n templateName) Programs() string {
    67  	return string(n) + "Programs"
    68  }
    69  
    70  func (n templateName) CloseHelper() string {
    71  	return "_" + toUpperFirst(string(n)) + "Close"
    72  }
    73  
    74  type outputArgs struct {
    75  	// Package of the resulting file.
    76  	pkg string
    77  	// The prefix of all names declared at the top-level.
    78  	stem string
    79  	// Build constraints included in the resulting file.
    80  	constraints constraint.Expr
    81  	// Maps to be emitted.
    82  	maps []string
    83  	// Programs to be emitted.
    84  	programs []string
    85  	// Types to be emitted.
    86  	types []btf.Type
    87  	// Filename of the ELF object to embed.
    88  	obj string
    89  	out io.Writer
    90  }
    91  
    92  func output(args outputArgs) error {
    93  	maps := make(map[string]string)
    94  	for _, name := range args.maps {
    95  		maps[name] = internal.Identifier(name)
    96  	}
    97  
    98  	programs := make(map[string]string)
    99  	for _, name := range args.programs {
   100  		programs[name] = internal.Identifier(name)
   101  	}
   102  
   103  	typeNames := make(map[btf.Type]string)
   104  	for _, typ := range args.types {
   105  		typeNames[typ] = args.stem + internal.Identifier(typ.TypeName())
   106  	}
   107  
   108  	// Ensure we don't have conflicting names and generate a sorted list of
   109  	// named types so that the output is stable.
   110  	types, err := sortTypes(typeNames)
   111  	if err != nil {
   112  		return err
   113  	}
   114  
   115  	module := currentModule()
   116  
   117  	gf := &btf.GoFormatter{
   118  		Names:      typeNames,
   119  		Identifier: internal.Identifier,
   120  	}
   121  
   122  	ctx := struct {
   123  		*btf.GoFormatter
   124  		Module      string
   125  		Package     string
   126  		Constraints constraint.Expr
   127  		Name        templateName
   128  		Maps        map[string]string
   129  		Programs    map[string]string
   130  		Types       []btf.Type
   131  		TypeNames   map[btf.Type]string
   132  		File        string
   133  	}{
   134  		gf,
   135  		module,
   136  		args.pkg,
   137  		args.constraints,
   138  		templateName(args.stem),
   139  		maps,
   140  		programs,
   141  		types,
   142  		typeNames,
   143  		args.obj,
   144  	}
   145  
   146  	var buf bytes.Buffer
   147  	if err := commonTemplate.Execute(&buf, &ctx); err != nil {
   148  		return fmt.Errorf("can't generate types: %s", err)
   149  	}
   150  
   151  	return internal.WriteFormatted(buf.Bytes(), args.out)
   152  }
   153  
   154  func collectFromSpec(spec *ebpf.CollectionSpec, cTypes []string, skipGlobalTypes bool) (maps, programs []string, types []btf.Type, _ error) {
   155  	for name := range spec.Maps {
   156  		// Skip .rodata, .data, .bss, etc. sections
   157  		if !strings.HasPrefix(name, ".") {
   158  			maps = append(maps, name)
   159  		}
   160  	}
   161  
   162  	for name := range spec.Programs {
   163  		programs = append(programs, name)
   164  	}
   165  
   166  	types, err := collectCTypes(spec.Types, cTypes)
   167  	if err != nil {
   168  		return nil, nil, nil, fmt.Errorf("collect C types: %w", err)
   169  	}
   170  
   171  	// Collect map key and value types, unless we've been asked not to.
   172  	if skipGlobalTypes {
   173  		return maps, programs, types, nil
   174  	}
   175  
   176  	for _, typ := range collectMapTypes(spec.Maps) {
   177  		switch btf.UnderlyingType(typ).(type) {
   178  		case *btf.Datasec:
   179  			// Avoid emitting .rodata, .bss, etc. for now. We might want to
   180  			// name these types differently, etc.
   181  			continue
   182  
   183  		case *btf.Int:
   184  			// Don't emit primitive types by default.
   185  			continue
   186  		}
   187  
   188  		types = append(types, typ)
   189  	}
   190  
   191  	return maps, programs, types, nil
   192  }
   193  
   194  func collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) {
   195  	var result []btf.Type
   196  	for _, cType := range names {
   197  		typ, err := types.AnyTypeByName(cType)
   198  		if err != nil {
   199  			return nil, err
   200  		}
   201  		result = append(result, typ)
   202  	}
   203  	return result, nil
   204  }
   205  
   206  // collectMapTypes returns a list of all types used as map keys or values.
   207  func collectMapTypes(maps map[string]*ebpf.MapSpec) []btf.Type {
   208  	var result []btf.Type
   209  	for _, m := range maps {
   210  		if m.Key != nil && m.Key.TypeName() != "" {
   211  			result = append(result, m.Key)
   212  		}
   213  
   214  		if m.Value != nil && m.Value.TypeName() != "" {
   215  			result = append(result, m.Value)
   216  		}
   217  	}
   218  	return result
   219  }
   220  
   221  // sortTypes returns a list of types sorted by their (generated) Go type name.
   222  //
   223  // Duplicate Go type names are rejected.
   224  func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) {
   225  	var types []btf.Type
   226  	var names []string
   227  	for typ, name := range typeNames {
   228  		i := sort.SearchStrings(names, name)
   229  		if i >= len(names) {
   230  			types = append(types, typ)
   231  			names = append(names, name)
   232  			continue
   233  		}
   234  
   235  		if names[i] == name {
   236  			return nil, fmt.Errorf("type name %q is used multiple times", name)
   237  		}
   238  
   239  		types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...)
   240  		names = append(names[:i], append([]string{name}, names[i:]...)...)
   241  	}
   242  
   243  	return types, nil
   244  }