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
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
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
269 if !args.skipGlobalTypes {
270 for _, typ := range collectMapTypes(spec.Maps) {
271 switch btf.UnderlyingType(typ).(type) {
272 case *btf.Datasec:
273
274
275 continue
276
277 case *btf.Int:
278
279 continue
280 }
281
282 typeNames[typ] = args.ident + internal.Identifier(typ.TypeName())
283 }
284 }
285
286
287
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
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
358
359
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