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 }