github.com/cilium/ebpf@v0.10.0/cmd/bpf2go/output.go (about) 1 package main 2 3 import ( 4 "bytes" 5 "fmt" 6 "go/token" 7 "io" 8 "os" 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 := os.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 }