...

Source file src/github.com/cilium/ebpf/cmd/bpf2go/output.go

Documentation: github.com/cilium/ebpf/cmd/bpf2go

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/token"
     7  	"io"
     8  	"io/ioutil"
     9  	"path/filepath"
    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  const ebpfModule = "github.com/cilium/ebpf"
    20  
    21  const commonRaw = `// Code generated by bpf2go; DO NOT EDIT.
    22  {{- range .Tags }}
    23  // +build {{ . }}
    24  {{- end }}
    25  
    26  package {{ .Package }}
    27  
    28  import (
    29  	"bytes"
    30  	_ "embed"
    31  	"fmt"
    32  	"io"
    33  
    34  	"{{ .Module }}"
    35  )
    36  
    37  {{- if .Types }}
    38  {{- range $type := .Types }}
    39  {{ $.TypeDeclaration (index $.TypeNames $type) $type }}
    40  
    41  {{ end }}
    42  {{- end }}
    43  
    44  // {{ .Name.Load }} returns the embedded CollectionSpec for {{ .Name }}.
    45  func {{ .Name.Load }}() (*ebpf.CollectionSpec, error) {
    46  	reader := bytes.NewReader({{ .Name.Bytes }})
    47  	spec, err := ebpf.LoadCollectionSpecFromReader(reader)
    48  	if err != nil {
    49  		return nil, fmt.Errorf("can't load {{ .Name }}: %w", err)
    50  	}
    51  
    52  	return spec, err
    53  }
    54  
    55  // {{ .Name.LoadObjects }} loads {{ .Name }} and converts it into a struct.
    56  //
    57  // The following types are suitable as obj argument:
    58  //
    59  //     *{{ .Name.Objects }}
    60  //     *{{ .Name.Programs }}
    61  //     *{{ .Name.Maps }}
    62  //
    63  // See ebpf.CollectionSpec.LoadAndAssign documentation for details.
    64  func {{ .Name.LoadObjects }}(obj interface{}, opts *ebpf.CollectionOptions) (error) {
    65  	spec, err := {{ .Name.Load }}()
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	return spec.LoadAndAssign(obj, opts)
    71  }
    72  
    73  // {{ .Name.Specs }} contains maps and programs before they are loaded into the kernel.
    74  //
    75  // It can be passed ebpf.CollectionSpec.Assign.
    76  type {{ .Name.Specs }} struct {
    77  	{{ .Name.ProgramSpecs }}
    78  	{{ .Name.MapSpecs }}
    79  }
    80  
    81  // {{ .Name.Specs }} contains programs before they are loaded into the kernel.
    82  //
    83  // It can be passed ebpf.CollectionSpec.Assign.
    84  type {{ .Name.ProgramSpecs }} struct {
    85  {{- range $name, $id := .Programs }}
    86  	{{ $id }} *ebpf.ProgramSpec {{ tag $name }}
    87  {{- end }}
    88  }
    89  
    90  // {{ .Name.MapSpecs }} contains maps before they are loaded into the kernel.
    91  //
    92  // It can be passed ebpf.CollectionSpec.Assign.
    93  type {{ .Name.MapSpecs }} struct {
    94  {{- range $name, $id := .Maps }}
    95  	{{ $id }} *ebpf.MapSpec {{ tag $name }}
    96  {{- end }}
    97  }
    98  
    99  // {{ .Name.Objects }} contains all objects after they have been loaded into the kernel.
   100  //
   101  // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.
   102  type {{ .Name.Objects }} struct {
   103  	{{ .Name.Programs }}
   104  	{{ .Name.Maps }}
   105  }
   106  
   107  func (o *{{ .Name.Objects }}) Close() error {
   108  	return {{ .Name.CloseHelper }}(
   109  		&o.{{ .Name.Programs }},
   110  		&o.{{ .Name.Maps }},
   111  	)
   112  }
   113  
   114  // {{ .Name.Maps }} contains all maps after they have been loaded into the kernel.
   115  //
   116  // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.
   117  type {{ .Name.Maps }} struct {
   118  {{- range $name, $id := .Maps }}
   119  	{{ $id }} *ebpf.Map {{ tag $name }}
   120  {{- end }}
   121  }
   122  
   123  func (m *{{ .Name.Maps }}) Close() error {
   124  	return {{ .Name.CloseHelper }}(
   125  {{- range $id := .Maps }}
   126  		m.{{ $id }},
   127  {{- end }}
   128  	)
   129  }
   130  
   131  // {{ .Name.Programs }} contains all programs after they have been loaded into the kernel.
   132  //
   133  // It can be passed to {{ .Name.LoadObjects }} or ebpf.CollectionSpec.LoadAndAssign.
   134  type {{ .Name.Programs }} struct {
   135  {{- range $name, $id := .Programs }}
   136  	{{ $id }} *ebpf.Program {{ tag $name }}
   137  {{- end }}
   138  }
   139  
   140  func (p *{{ .Name.Programs }}) Close() error {
   141  	return {{ .Name.CloseHelper }}(
   142  {{- range $id := .Programs }}
   143  		p.{{ $id }},
   144  {{- end }}
   145  	)
   146  }
   147  
   148  func {{ .Name.CloseHelper }}(closers ...io.Closer) error {
   149  	for _, closer := range closers {
   150  		if err := closer.Close(); err != nil {
   151  			return err
   152  		}
   153  	}
   154  	return nil
   155  }
   156  
   157  // Do not access this directly.
   158  //go:embed {{ .File }}
   159  var {{ .Name.Bytes }} []byte
   160  
   161  `
   162  
   163  var (
   164  	tplFuncs = map[string]interface{}{
   165  		"tag": tag,
   166  	}
   167  	commonTemplate = template.Must(template.New("common").Funcs(tplFuncs).Parse(commonRaw))
   168  )
   169  
   170  type templateName string
   171  
   172  func (n templateName) maybeExport(str string) string {
   173  	if token.IsExported(string(n)) {
   174  		return toUpperFirst(str)
   175  	}
   176  
   177  	return str
   178  }
   179  
   180  func (n templateName) Bytes() string {
   181  	return "_" + toUpperFirst(string(n)) + "Bytes"
   182  }
   183  
   184  func (n templateName) Specs() string {
   185  	return string(n) + "Specs"
   186  }
   187  
   188  func (n templateName) ProgramSpecs() string {
   189  	return string(n) + "ProgramSpecs"
   190  }
   191  
   192  func (n templateName) MapSpecs() string {
   193  	return string(n) + "MapSpecs"
   194  }
   195  
   196  func (n templateName) Load() string {
   197  	return n.maybeExport("load" + toUpperFirst(string(n)))
   198  }
   199  
   200  func (n templateName) LoadObjects() string {
   201  	return n.maybeExport("load" + toUpperFirst(string(n)) + "Objects")
   202  }
   203  
   204  func (n templateName) Objects() string {
   205  	return string(n) + "Objects"
   206  }
   207  
   208  func (n templateName) Maps() string {
   209  	return string(n) + "Maps"
   210  }
   211  
   212  func (n templateName) Programs() string {
   213  	return string(n) + "Programs"
   214  }
   215  
   216  func (n templateName) CloseHelper() string {
   217  	return "_" + toUpperFirst(string(n)) + "Close"
   218  }
   219  
   220  type outputArgs struct {
   221  	pkg             string
   222  	ident           string
   223  	tags            []string
   224  	cTypes          []string
   225  	skipGlobalTypes bool
   226  	obj             string
   227  	out             io.Writer
   228  }
   229  
   230  func output(args outputArgs) error {
   231  	obj, err := ioutil.ReadFile(args.obj)
   232  	if err != nil {
   233  		return fmt.Errorf("read object file contents: %s", err)
   234  	}
   235  
   236  	rd := bytes.NewReader(obj)
   237  	spec, err := ebpf.LoadCollectionSpecFromReader(rd)
   238  	if err != nil {
   239  		return fmt.Errorf("can't load BPF from ELF: %s", err)
   240  	}
   241  
   242  	maps := make(map[string]string)
   243  	for name := range spec.Maps {
   244  		if strings.HasPrefix(name, ".") {
   245  			// Skip .rodata, .data, .bss, etc. sections
   246  			continue
   247  		}
   248  
   249  		maps[name] = internal.Identifier(name)
   250  	}
   251  
   252  	programs := make(map[string]string)
   253  	for name := range spec.Programs {
   254  		programs[name] = internal.Identifier(name)
   255  	}
   256  
   257  	// Collect any types which we've been asked for explicitly.
   258  	cTypes, err := collectCTypes(spec.Types, args.cTypes)
   259  	if err != nil {
   260  		return err
   261  	}
   262  
   263  	typeNames := make(map[btf.Type]string)
   264  	for _, cType := range cTypes {
   265  		typeNames[cType] = args.ident + internal.Identifier(cType.TypeName())
   266  	}
   267  
   268  	// Collect map key and value types, unless we've been asked not to.
   269  	if !args.skipGlobalTypes {
   270  		for _, typ := range collectMapTypes(spec.Maps) {
   271  			switch btf.UnderlyingType(typ).(type) {
   272  			case *btf.Datasec:
   273  				// Avoid emitting .rodata, .bss, etc. for now. We might want to
   274  				// name these types differently, etc.
   275  				continue
   276  
   277  			case *btf.Int:
   278  				// Don't emit primitive types by default.
   279  				continue
   280  			}
   281  
   282  			typeNames[typ] = args.ident + internal.Identifier(typ.TypeName())
   283  		}
   284  	}
   285  
   286  	// Ensure we don't have conflicting names and generate a sorted list of
   287  	// named types so that the output is stable.
   288  	types, err := sortTypes(typeNames)
   289  	if err != nil {
   290  		return err
   291  	}
   292  
   293  	gf := &btf.GoFormatter{
   294  		Names:      typeNames,
   295  		Identifier: internal.Identifier,
   296  	}
   297  
   298  	ctx := struct {
   299  		*btf.GoFormatter
   300  		Module    string
   301  		Package   string
   302  		Tags      []string
   303  		Name      templateName
   304  		Maps      map[string]string
   305  		Programs  map[string]string
   306  		Types     []btf.Type
   307  		TypeNames map[btf.Type]string
   308  		File      string
   309  	}{
   310  		gf,
   311  		ebpfModule,
   312  		args.pkg,
   313  		args.tags,
   314  		templateName(args.ident),
   315  		maps,
   316  		programs,
   317  		types,
   318  		typeNames,
   319  		filepath.Base(args.obj),
   320  	}
   321  
   322  	var buf bytes.Buffer
   323  	if err := commonTemplate.Execute(&buf, &ctx); err != nil {
   324  		return fmt.Errorf("can't generate types: %s", err)
   325  	}
   326  
   327  	return internal.WriteFormatted(buf.Bytes(), args.out)
   328  }
   329  
   330  func collectCTypes(types *btf.Spec, names []string) ([]btf.Type, error) {
   331  	var result []btf.Type
   332  	for _, cType := range names {
   333  		typ, err := types.AnyTypeByName(cType)
   334  		if err != nil {
   335  			return nil, err
   336  		}
   337  		result = append(result, typ)
   338  	}
   339  	return result, nil
   340  }
   341  
   342  // collectMapTypes returns a list of all types used as map keys or values.
   343  func collectMapTypes(maps map[string]*ebpf.MapSpec) []btf.Type {
   344  	var result []btf.Type
   345  	for _, m := range maps {
   346  		if m.Key != nil && m.Key.TypeName() != "" {
   347  			result = append(result, m.Key)
   348  		}
   349  
   350  		if m.Value != nil && m.Value.TypeName() != "" {
   351  			result = append(result, m.Value)
   352  		}
   353  	}
   354  	return result
   355  }
   356  
   357  // sortTypes returns a list of types sorted by their (generated) Go type name.
   358  //
   359  // Duplicate Go type names are rejected.
   360  func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) {
   361  	var types []btf.Type
   362  	var names []string
   363  	for typ, name := range typeNames {
   364  		i := sort.SearchStrings(names, name)
   365  		if i >= len(names) {
   366  			types = append(types, typ)
   367  			names = append(names, name)
   368  			continue
   369  		}
   370  
   371  		if names[i] == name {
   372  			return nil, fmt.Errorf("type name %q is used multiple times", name)
   373  		}
   374  
   375  		types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...)
   376  		names = append(names[:i], append([]string{name}, names[i:]...)...)
   377  	}
   378  
   379  	return types, nil
   380  }
   381  
   382  func tag(str string) string {
   383  	return "`ebpf:\"" + str + "\"`"
   384  }
   385  

View as plain text