github.com/cilium/ebpf@v0.10.0/btf/format.go (about) 1 package btf 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 ) 8 9 var errNestedTooDeep = errors.New("nested too deep") 10 11 // GoFormatter converts a Type to Go syntax. 12 // 13 // A zero GoFormatter is valid to use. 14 type GoFormatter struct { 15 w strings.Builder 16 17 // Types present in this map are referred to using the given name if they 18 // are encountered when outputting another type. 19 Names map[Type]string 20 21 // Identifier is called for each field of struct-like types. By default the 22 // field name is used as is. 23 Identifier func(string) string 24 25 // EnumIdentifier is called for each element of an enum. By default the 26 // name of the enum type is concatenated with Identifier(element). 27 EnumIdentifier func(name, element string) string 28 } 29 30 // TypeDeclaration generates a Go type declaration for a BTF type. 31 func (gf *GoFormatter) TypeDeclaration(name string, typ Type) (string, error) { 32 gf.w.Reset() 33 if err := gf.writeTypeDecl(name, typ); err != nil { 34 return "", err 35 } 36 return gf.w.String(), nil 37 } 38 39 func (gf *GoFormatter) identifier(s string) string { 40 if gf.Identifier != nil { 41 return gf.Identifier(s) 42 } 43 44 return s 45 } 46 47 func (gf *GoFormatter) enumIdentifier(name, element string) string { 48 if gf.EnumIdentifier != nil { 49 return gf.EnumIdentifier(name, element) 50 } 51 52 return name + gf.identifier(element) 53 } 54 55 // writeTypeDecl outputs a declaration of the given type. 56 // 57 // It encodes https://golang.org/ref/spec#Type_declarations: 58 // 59 // type foo struct { bar uint32; } 60 // type bar int32 61 func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error { 62 if name == "" { 63 return fmt.Errorf("need a name for type %s", typ) 64 } 65 66 typ = skipQualifiers(typ) 67 fmt.Fprintf(&gf.w, "type %s ", name) 68 if err := gf.writeTypeLit(typ, 0); err != nil { 69 return err 70 } 71 72 e, ok := typ.(*Enum) 73 if !ok || len(e.Values) == 0 { 74 return nil 75 } 76 77 gf.w.WriteString("; const ( ") 78 for _, ev := range e.Values { 79 id := gf.enumIdentifier(name, ev.Name) 80 fmt.Fprintf(&gf.w, "%s %s = %d; ", id, name, ev.Value) 81 } 82 gf.w.WriteString(")") 83 84 return nil 85 } 86 87 // writeType outputs the name of a named type or a literal describing the type. 88 // 89 // It encodes https://golang.org/ref/spec#Types. 90 // 91 // foo (if foo is a named type) 92 // uint32 93 func (gf *GoFormatter) writeType(typ Type, depth int) error { 94 typ = skipQualifiers(typ) 95 96 name := gf.Names[typ] 97 if name != "" { 98 gf.w.WriteString(name) 99 return nil 100 } 101 102 return gf.writeTypeLit(typ, depth) 103 } 104 105 // writeTypeLit outputs a literal describing the type. 106 // 107 // The function ignores named types. 108 // 109 // It encodes https://golang.org/ref/spec#TypeLit. 110 // 111 // struct { bar uint32; } 112 // uint32 113 func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error { 114 depth++ 115 if depth > maxTypeDepth { 116 return errNestedTooDeep 117 } 118 119 var err error 120 switch v := skipQualifiers(typ).(type) { 121 case *Int: 122 err = gf.writeIntLit(v) 123 124 case *Enum: 125 if !v.Signed { 126 gf.w.WriteRune('u') 127 } 128 switch v.Size { 129 case 1: 130 gf.w.WriteString("int8") 131 case 2: 132 gf.w.WriteString("int16") 133 case 4: 134 gf.w.WriteString("int32") 135 case 8: 136 gf.w.WriteString("int64") 137 default: 138 err = fmt.Errorf("invalid enum size %d", v.Size) 139 } 140 141 case *Typedef: 142 err = gf.writeType(v.Type, depth) 143 144 case *Array: 145 fmt.Fprintf(&gf.w, "[%d]", v.Nelems) 146 err = gf.writeType(v.Type, depth) 147 148 case *Struct: 149 err = gf.writeStructLit(v.Size, v.Members, depth) 150 151 case *Union: 152 // Always choose the first member to represent the union in Go. 153 err = gf.writeStructLit(v.Size, v.Members[:1], depth) 154 155 case *Datasec: 156 err = gf.writeDatasecLit(v, depth) 157 158 default: 159 return fmt.Errorf("type %T: %w", v, ErrNotSupported) 160 } 161 162 if err != nil { 163 return fmt.Errorf("%s: %w", typ, err) 164 } 165 166 return nil 167 } 168 169 func (gf *GoFormatter) writeIntLit(i *Int) error { 170 bits := i.Size * 8 171 switch i.Encoding { 172 case Bool: 173 if i.Size != 1 { 174 return fmt.Errorf("bool with size %d", i.Size) 175 } 176 gf.w.WriteString("bool") 177 case Char: 178 if i.Size != 1 { 179 return fmt.Errorf("char with size %d", i.Size) 180 } 181 // BTF doesn't have a way to specify the signedness of a char. Assume 182 // we are dealing with unsigned, since this works nicely with []byte 183 // in Go code. 184 fallthrough 185 case Unsigned, Signed: 186 stem := "uint" 187 if i.Encoding == Signed { 188 stem = "int" 189 } 190 if i.Size > 8 { 191 fmt.Fprintf(&gf.w, "[%d]byte /* %s%d */", i.Size, stem, i.Size*8) 192 } else { 193 fmt.Fprintf(&gf.w, "%s%d", stem, bits) 194 } 195 default: 196 return fmt.Errorf("can't encode %s", i.Encoding) 197 } 198 return nil 199 } 200 201 func (gf *GoFormatter) writeStructLit(size uint32, members []Member, depth int) error { 202 gf.w.WriteString("struct { ") 203 204 prevOffset := uint32(0) 205 skippedBitfield := false 206 for i, m := range members { 207 if m.BitfieldSize > 0 { 208 skippedBitfield = true 209 continue 210 } 211 212 offset := m.Offset.Bytes() 213 if n := offset - prevOffset; skippedBitfield && n > 0 { 214 fmt.Fprintf(&gf.w, "_ [%d]byte /* unsupported bitfield */; ", n) 215 } else { 216 gf.writePadding(n) 217 } 218 219 fieldSize, err := Sizeof(m.Type) 220 if err != nil { 221 return fmt.Errorf("field %d: %w", i, err) 222 } 223 224 prevOffset = offset + uint32(fieldSize) 225 if prevOffset > size { 226 return fmt.Errorf("field %d of size %d exceeds type size %d", i, fieldSize, size) 227 } 228 229 if err := gf.writeStructField(m, depth); err != nil { 230 return fmt.Errorf("field %d: %w", i, err) 231 } 232 } 233 234 gf.writePadding(size - prevOffset) 235 gf.w.WriteString("}") 236 return nil 237 } 238 239 func (gf *GoFormatter) writeStructField(m Member, depth int) error { 240 if m.BitfieldSize > 0 { 241 return fmt.Errorf("bitfields are not supported") 242 } 243 if m.Offset%8 != 0 { 244 return fmt.Errorf("unsupported offset %d", m.Offset) 245 } 246 247 if m.Name == "" { 248 // Special case a nested anonymous union like 249 // struct foo { union { int bar; int baz }; } 250 // by replacing the whole union with its first member. 251 union, ok := m.Type.(*Union) 252 if !ok { 253 return fmt.Errorf("anonymous fields are not supported") 254 255 } 256 257 if len(union.Members) == 0 { 258 return errors.New("empty anonymous union") 259 } 260 261 depth++ 262 if depth > maxTypeDepth { 263 return errNestedTooDeep 264 } 265 266 m := union.Members[0] 267 size, err := Sizeof(m.Type) 268 if err != nil { 269 return err 270 } 271 272 if err := gf.writeStructField(m, depth); err != nil { 273 return err 274 } 275 276 gf.writePadding(union.Size - uint32(size)) 277 return nil 278 279 } 280 281 fmt.Fprintf(&gf.w, "%s ", gf.identifier(m.Name)) 282 283 if err := gf.writeType(m.Type, depth); err != nil { 284 return err 285 } 286 287 gf.w.WriteString("; ") 288 return nil 289 } 290 291 func (gf *GoFormatter) writeDatasecLit(ds *Datasec, depth int) error { 292 gf.w.WriteString("struct { ") 293 294 prevOffset := uint32(0) 295 for i, vsi := range ds.Vars { 296 v, ok := vsi.Type.(*Var) 297 if !ok { 298 return fmt.Errorf("can't format %s as part of data section", vsi.Type) 299 } 300 301 if v.Linkage != GlobalVar { 302 // Ignore static, extern, etc. for now. 303 continue 304 } 305 306 if v.Name == "" { 307 return fmt.Errorf("variable %d: empty name", i) 308 } 309 310 gf.writePadding(vsi.Offset - prevOffset) 311 prevOffset = vsi.Offset + vsi.Size 312 313 fmt.Fprintf(&gf.w, "%s ", gf.identifier(v.Name)) 314 315 if err := gf.writeType(v.Type, depth); err != nil { 316 return fmt.Errorf("variable %d: %w", i, err) 317 } 318 319 gf.w.WriteString("; ") 320 } 321 322 gf.writePadding(ds.Size - prevOffset) 323 gf.w.WriteString("}") 324 return nil 325 } 326 327 func (gf *GoFormatter) writePadding(bytes uint32) { 328 if bytes > 0 { 329 fmt.Fprintf(&gf.w, "_ [%d]byte; ", bytes) 330 } 331 } 332 333 func skipQualifiers(typ Type) Type { 334 result := typ 335 for depth := 0; depth <= maxTypeDepth; depth++ { 336 switch v := (result).(type) { 337 case qualifier: 338 result = v.qualify() 339 default: 340 return result 341 } 342 } 343 return &cycle{typ} 344 }