github.com/cilium/ebpf@v0.16.0/cmd/bpf2go/gen/output.go (about) 1 package gen 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 "unicode" 14 "unicode/utf8" 15 16 "github.com/cilium/ebpf/btf" 17 b2gInt "github.com/cilium/ebpf/cmd/bpf2go/internal" 18 "github.com/cilium/ebpf/internal" 19 ) 20 21 //go:embed output.tpl 22 var commonRaw string 23 24 var commonTemplate = template.Must(template.New("common").Parse(commonRaw)) 25 26 type templateName string 27 28 func (n templateName) maybeExport(str string) string { 29 if token.IsExported(string(n)) { 30 return toUpperFirst(str) 31 } 32 33 return str 34 } 35 36 func (n templateName) Bytes() string { 37 return "_" + toUpperFirst(string(n)) + "Bytes" 38 } 39 40 func (n templateName) Specs() string { 41 return string(n) + "Specs" 42 } 43 44 func (n templateName) ProgramSpecs() string { 45 return string(n) + "ProgramSpecs" 46 } 47 48 func (n templateName) MapSpecs() string { 49 return string(n) + "MapSpecs" 50 } 51 52 func (n templateName) Load() string { 53 return n.maybeExport("load" + toUpperFirst(string(n))) 54 } 55 56 func (n templateName) LoadObjects() string { 57 return n.maybeExport("load" + toUpperFirst(string(n)) + "Objects") 58 } 59 60 func (n templateName) Objects() string { 61 return string(n) + "Objects" 62 } 63 64 func (n templateName) Maps() string { 65 return string(n) + "Maps" 66 } 67 68 func (n templateName) Programs() string { 69 return string(n) + "Programs" 70 } 71 72 func (n templateName) CloseHelper() string { 73 return "_" + toUpperFirst(string(n)) + "Close" 74 } 75 76 type GenerateArgs struct { 77 // Package of the resulting file. 78 Package string 79 // The prefix of all names declared at the top-level. 80 Stem string 81 // Build Constraints included in the resulting file. 82 Constraints constraint.Expr 83 // Maps to be emitted. 84 Maps []string 85 // Programs to be emitted. 86 Programs []string 87 // Types to be emitted. 88 Types []btf.Type 89 // Filename of the object to embed. 90 ObjectFile string 91 // Output to write template to. 92 Output io.Writer 93 } 94 95 // Generate bindings for a BPF ELF file. 96 func Generate(args GenerateArgs) error { 97 if !token.IsIdentifier(args.Stem) { 98 return fmt.Errorf("%q is not a valid identifier", args.Stem) 99 } 100 101 if strings.ContainsAny(args.ObjectFile, "\n") { 102 // Prevent injecting newlines into the template. 103 return fmt.Errorf("file %q contains an invalid character", args.ObjectFile) 104 } 105 106 for _, typ := range args.Types { 107 if _, ok := btf.As[*btf.Datasec](typ); ok { 108 // Avoid emitting .rodata, .bss, etc. for now. We might want to 109 // name these types differently, etc. 110 return fmt.Errorf("can't output btf.Datasec: %s", typ) 111 } 112 } 113 114 maps := make(map[string]string) 115 for _, name := range args.Maps { 116 maps[name] = internal.Identifier(name) 117 } 118 119 programs := make(map[string]string) 120 for _, name := range args.Programs { 121 programs[name] = internal.Identifier(name) 122 } 123 124 typeNames := make(map[btf.Type]string) 125 for _, typ := range args.Types { 126 // NB: This also deduplicates types. 127 typeNames[typ] = args.Stem + internal.Identifier(typ.TypeName()) 128 } 129 130 // Ensure we don't have conflicting names and generate a sorted list of 131 // named types so that the output is stable. 132 types, err := sortTypes(typeNames) 133 if err != nil { 134 return err 135 } 136 137 gf := &btf.GoFormatter{ 138 Names: typeNames, 139 Identifier: internal.Identifier, 140 } 141 142 ctx := struct { 143 *btf.GoFormatter 144 Module string 145 Package string 146 Constraints constraint.Expr 147 Name templateName 148 Maps map[string]string 149 Programs map[string]string 150 Types []btf.Type 151 TypeNames map[btf.Type]string 152 File string 153 }{ 154 gf, 155 b2gInt.CurrentModule, 156 args.Package, 157 args.Constraints, 158 templateName(args.Stem), 159 maps, 160 programs, 161 types, 162 typeNames, 163 args.ObjectFile, 164 } 165 166 var buf bytes.Buffer 167 if err := commonTemplate.Execute(&buf, &ctx); err != nil { 168 return fmt.Errorf("can't generate types: %s", err) 169 } 170 171 return internal.WriteFormatted(buf.Bytes(), args.Output) 172 } 173 174 // sortTypes returns a list of types sorted by their (generated) Go type name. 175 // 176 // Duplicate Go type names are rejected. 177 func sortTypes(typeNames map[btf.Type]string) ([]btf.Type, error) { 178 var types []btf.Type 179 var names []string 180 for typ, name := range typeNames { 181 i := sort.SearchStrings(names, name) 182 if i >= len(names) { 183 types = append(types, typ) 184 names = append(names, name) 185 continue 186 } 187 188 if names[i] == name { 189 return nil, fmt.Errorf("type name %q is used multiple times", name) 190 } 191 192 types = append(types[:i], append([]btf.Type{typ}, types[i:]...)...) 193 names = append(names[:i], append([]string{name}, names[i:]...)...) 194 } 195 196 return types, nil 197 } 198 199 func toUpperFirst(str string) string { 200 first, n := utf8.DecodeRuneInString(str) 201 return string(unicode.ToUpper(first)) + str[n:] 202 }