github.com/kubeshark/ebpf@v0.9.2/internal/cmd/gentypes/main.go (about) 1 // Program gentypes reads a compressed vmlinux .BTF section and generates 2 // syscall bindings from it. 3 // 4 // Output is written to "types.go". 5 package main 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "os" 12 "sort" 13 "strings" 14 15 "github.com/kubeshark/ebpf/btf" 16 "github.com/kubeshark/ebpf/internal" 17 "github.com/kubeshark/ebpf/internal/sys" 18 ) 19 20 type syscallRetval int 21 22 const ( 23 retError syscallRetval = iota 24 retFd 25 ) 26 27 func main() { 28 if err := run(os.Args[1:]); err != nil { 29 fmt.Fprintln(os.Stderr, "Error:", err) 30 os.Exit(1) 31 } 32 } 33 34 func run(args []string) error { 35 if len(args) != 1 { 36 return fmt.Errorf("expect location of compressed vmlinux .BTF as argument") 37 } 38 39 raw, err := internal.ReadAllCompressed(args[0]) 40 if err != nil { 41 return err 42 } 43 44 spec, err := btf.LoadSpecFromReader(bytes.NewReader(raw)) 45 if err != nil { 46 return err 47 } 48 49 output, err := generateTypes(spec) 50 if err != nil { 51 return err 52 } 53 54 w, err := os.Create("types.go") 55 if err != nil { 56 return err 57 } 58 defer w.Close() 59 60 return internal.WriteFormatted(output, w) 61 } 62 63 func generateTypes(spec *btf.Spec) ([]byte, error) { 64 objName := &btf.Array{Nelems: 16, Type: &btf.Int{Encoding: btf.Char, Size: 1}} 65 linkID := &btf.Int{Size: 4} 66 btfID := &btf.Int{Size: 4} 67 pointer := &btf.Int{Size: 8} 68 69 // Pre-declare handwritten types sys.ObjName and sys.Pointer so that 70 // generated types can refer to them. 71 var ( 72 _ sys.Pointer 73 _ sys.ObjName 74 _ sys.LinkID 75 ) 76 77 gf := &btf.GoFormatter{ 78 Names: map[btf.Type]string{ 79 objName: "ObjName", 80 linkID: "LinkID", 81 btfID: "BTFID", 82 pointer: "Pointer", 83 }, 84 Identifier: internal.Identifier, 85 EnumIdentifier: func(name, element string) string { 86 return element 87 }, 88 } 89 90 w := bytes.NewBuffer(nil) 91 w.WriteString(`// Code generated by internal/cmd/gentypes; DO NOT EDIT. 92 93 package sys 94 95 import ( 96 "unsafe" 97 ) 98 99 `) 100 101 enums := []struct { 102 goType string 103 cType string 104 }{ 105 {"Cmd", "bpf_cmd"}, 106 {"MapType", "bpf_map_type"}, 107 {"ProgType", "bpf_prog_type"}, 108 {"AttachType", "bpf_attach_type"}, 109 {"LinkType", "bpf_link_type"}, 110 {"StatsType", "bpf_stats_type"}, 111 {"SkAction", "sk_action"}, 112 {"StackBuildIdStatus", "bpf_stack_build_id_status"}, 113 {"FunctionId", "bpf_func_id"}, 114 {"AdjRoomMode", "bpf_adj_room_mode"}, 115 {"HdrStartOff", "bpf_hdr_start_off"}, 116 {"RetCode", "bpf_ret_code"}, 117 {"XdpAction", "xdp_action"}, 118 } 119 120 sort.Slice(enums, func(i, j int) bool { 121 return enums[i].goType < enums[j].goType 122 }) 123 124 enumTypes := make(map[string]btf.Type) 125 for _, o := range enums { 126 fmt.Println("enum", o.goType) 127 128 var t *btf.Enum 129 if err := spec.TypeByName(o.cType, &t); err != nil { 130 return nil, err 131 } 132 133 // Add the enum as a predeclared type so that generated structs 134 // refer to the Go types. 135 if name := gf.Names[t]; name != "" { 136 return nil, fmt.Errorf("type %q is already declared as %s", o.cType, name) 137 } 138 gf.Names[t] = o.goType 139 enumTypes[o.goType] = t 140 141 decl, err := gf.TypeDeclaration(o.goType, t) 142 if err != nil { 143 return nil, fmt.Errorf("generate %q: %w", o.goType, err) 144 } 145 146 w.WriteString(decl) 147 w.WriteRune('\n') 148 } 149 150 // Assorted structs 151 152 structs := []struct { 153 goType string 154 cType string 155 patches []patch 156 }{ 157 { 158 "ProgInfo", "bpf_prog_info", 159 []patch{ 160 replace(objName, "name"), 161 replace(pointer, "xlated_prog_insns"), 162 replace(pointer, "map_ids"), 163 }, 164 }, 165 { 166 "MapInfo", "bpf_map_info", 167 []patch{replace(objName, "name")}, 168 }, 169 { 170 "BtfInfo", "bpf_btf_info", 171 []patch{ 172 replace(pointer, "btf", "name"), 173 replace(btfID, "id"), 174 }, 175 }, 176 { 177 "LinkInfo", "bpf_link_info", 178 []patch{ 179 replace(enumTypes["LinkType"], "type"), 180 replace(linkID, "id"), 181 name(3, "extra"), 182 replaceWithBytes("extra"), 183 }, 184 }, 185 {"FuncInfo", "bpf_func_info", nil}, 186 {"LineInfo", "bpf_line_info", nil}, 187 {"XdpMd", "xdp_md", nil}, 188 { 189 "SkLookup", "bpf_sk_lookup", 190 []patch{ 191 choose(0, "cookie"), 192 replaceWithBytes("remote_ip4", "remote_ip6", "local_ip4", "local_ip6"), 193 }, 194 }, 195 } 196 197 sort.Slice(structs, func(i, j int) bool { 198 return structs[i].goType < structs[j].goType 199 }) 200 201 for _, s := range structs { 202 fmt.Println("struct", s.goType) 203 204 var t *btf.Struct 205 if err := spec.TypeByName(s.cType, &t); err != nil { 206 return nil, err 207 } 208 209 if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil { 210 return nil, fmt.Errorf("output %q: %w", s.goType, err) 211 } 212 } 213 214 // Attrs 215 216 attrs := []struct { 217 goType string 218 ret syscallRetval 219 cType string 220 cmd string 221 patches []patch 222 }{ 223 { 224 "MapCreate", retFd, "map_create", "BPF_MAP_CREATE", 225 []patch{ 226 replace(objName, "map_name"), 227 replace(enumTypes["MapType"], "map_type"), 228 }, 229 }, 230 { 231 "MapLookupElem", retError, "map_elem", "BPF_MAP_LOOKUP_ELEM", 232 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 233 }, 234 { 235 "MapLookupAndDeleteElem", retError, "map_elem", "BPF_MAP_LOOKUP_AND_DELETE_ELEM", 236 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 237 }, 238 { 239 "MapUpdateElem", retError, "map_elem", "BPF_MAP_UPDATE_ELEM", 240 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 241 }, 242 { 243 "MapDeleteElem", retError, "map_elem", "BPF_MAP_DELETE_ELEM", 244 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 245 }, 246 { 247 "MapGetNextKey", retError, "map_elem", "BPF_MAP_GET_NEXT_KEY", 248 []patch{ 249 choose(2, "next_key"), replace(pointer, "key", "next_key"), 250 truncateAfter("next_key"), 251 }, 252 }, 253 { 254 "MapFreeze", retError, "map_elem", "BPF_MAP_FREEZE", 255 []patch{truncateAfter("map_fd")}, 256 }, 257 { 258 "MapLookupBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_BATCH", 259 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 260 }, 261 { 262 "MapLookupAndDeleteBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_AND_DELETE_BATCH", 263 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 264 }, 265 { 266 "MapUpdateBatch", retError, "map_elem_batch", "BPF_MAP_UPDATE_BATCH", 267 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 268 }, 269 { 270 "MapDeleteBatch", retError, "map_elem_batch", "BPF_MAP_DELETE_BATCH", 271 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 272 }, 273 { 274 "ProgLoad", retFd, "prog_load", "BPF_PROG_LOAD", 275 []patch{ 276 replace(objName, "prog_name"), 277 replace(enumTypes["ProgType"], "prog_type"), 278 replace(enumTypes["AttachType"], "expected_attach_type"), 279 replace(pointer, 280 "insns", 281 "license", 282 "log_buf", 283 "func_info", 284 "line_info", 285 "fd_array", 286 "core_relos", 287 ), 288 }, 289 }, 290 { 291 "ProgBindMap", retError, "prog_bind_map", "BPF_PROG_BIND_MAP", 292 nil, 293 }, 294 { 295 "ObjPin", retError, "obj_pin", "BPF_OBJ_PIN", 296 []patch{replace(pointer, "pathname")}, 297 }, 298 { 299 "ObjGet", retFd, "obj_pin", "BPF_OBJ_GET", 300 []patch{replace(pointer, "pathname")}, 301 }, 302 { 303 "ProgAttach", retError, "prog_attach", "BPF_PROG_ATTACH", 304 nil, 305 }, 306 { 307 "ProgDetach", retError, "prog_attach", "BPF_PROG_DETACH", 308 []patch{truncateAfter("attach_type")}, 309 }, 310 { 311 "ProgRun", retError, "prog_run", "BPF_PROG_TEST_RUN", 312 []patch{replace(pointer, "data_in", "data_out", "ctx_in", "ctx_out")}, 313 }, 314 { 315 "ProgGetNextId", retError, "obj_next_id", "BPF_PROG_GET_NEXT_ID", 316 []patch{ 317 choose(0, "start_id"), rename("start_id", "id"), 318 truncateAfter("next_id"), 319 }, 320 }, 321 { 322 "MapGetNextId", retError, "obj_next_id", "BPF_MAP_GET_NEXT_ID", 323 []patch{ 324 choose(0, "start_id"), rename("start_id", "id"), 325 truncateAfter("next_id"), 326 }, 327 }, 328 { 329 "BtfGetNextId", retError, "obj_next_id", "BPF_BTF_GET_NEXT_ID", 330 []patch{ 331 choose(0, "start_id"), rename("start_id", "id"), 332 replace(btfID, "id", "next_id"), 333 truncateAfter("next_id"), 334 }, 335 }, 336 // These piggy back on the obj_next_id decl, but only support the 337 // first field... 338 { 339 "BtfGetFdById", retFd, "obj_next_id", "BPF_BTF_GET_FD_BY_ID", 340 []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, 341 }, 342 { 343 "MapGetFdById", retFd, "obj_next_id", "BPF_MAP_GET_FD_BY_ID", 344 []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, 345 }, 346 { 347 "ProgGetFdById", retFd, "obj_next_id", "BPF_PROG_GET_FD_BY_ID", 348 []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, 349 }, 350 { 351 "ObjGetInfoByFd", retError, "info_by_fd", "BPF_OBJ_GET_INFO_BY_FD", 352 []patch{replace(pointer, "info")}, 353 }, 354 { 355 "RawTracepointOpen", retFd, "raw_tracepoint_open", "BPF_RAW_TRACEPOINT_OPEN", 356 []patch{replace(pointer, "name")}, 357 }, 358 { 359 "BtfLoad", retFd, "btf_load", "BPF_BTF_LOAD", 360 []patch{replace(pointer, "btf", "btf_log_buf")}, 361 }, 362 { 363 "LinkCreate", retFd, "link_create", "BPF_LINK_CREATE", 364 []patch{replace(enumTypes["AttachType"], "attach_type")}, 365 }, 366 { 367 "LinkCreateIter", retFd, "link_create", "BPF_LINK_CREATE", 368 []patch{ 369 chooseNth(4, 1), 370 replace(enumTypes["AttachType"], "attach_type"), 371 flattenAnon, 372 replace(pointer, "iter_info"), 373 }, 374 }, 375 { 376 "LinkCreatePerfEvent", retFd, "link_create", "BPF_LINK_CREATE", 377 []patch{ 378 chooseNth(4, 2), 379 replace(enumTypes["AttachType"], "attach_type"), 380 flattenAnon, 381 }, 382 }, 383 { 384 "LinkUpdate", retError, "link_update", "BPF_LINK_UPDATE", 385 nil, 386 }, 387 { 388 "EnableStats", retFd, "enable_stats", "BPF_ENABLE_STATS", 389 nil, 390 }, 391 { 392 "IterCreate", retFd, "iter_create", "BPF_ITER_CREATE", 393 nil, 394 }, 395 } 396 397 sort.Slice(attrs, func(i, j int) bool { 398 return attrs[i].goType < attrs[j].goType 399 }) 400 401 var bpfAttr *btf.Union 402 if err := spec.TypeByName("bpf_attr", &bpfAttr); err != nil { 403 return nil, err 404 } 405 attrTypes, err := splitUnion(bpfAttr, types{ 406 {"map_create", "map_type"}, 407 {"map_elem", "map_fd"}, 408 {"map_elem_batch", "batch"}, 409 {"prog_load", "prog_type"}, 410 {"obj_pin", "pathname"}, 411 {"prog_attach", "target_fd"}, 412 {"prog_run", "test"}, 413 {"obj_next_id", ""}, 414 {"info_by_fd", "info"}, 415 {"prog_query", "query"}, 416 {"raw_tracepoint_open", "raw_tracepoint"}, 417 {"btf_load", "btf"}, 418 {"task_fd_query", "task_fd_query"}, 419 {"link_create", "link_create"}, 420 {"link_update", "link_update"}, 421 {"link_detach", "link_detach"}, 422 {"enable_stats", "enable_stats"}, 423 {"iter_create", "iter_create"}, 424 {"prog_bind_map", "prog_bind_map"}, 425 }) 426 if err != nil { 427 return nil, fmt.Errorf("splitting bpf_attr: %w", err) 428 } 429 430 for _, s := range attrs { 431 fmt.Println("attr", s.goType) 432 433 t := attrTypes[s.cType] 434 if t == nil { 435 return nil, fmt.Errorf("unknown attr %q", s.cType) 436 } 437 438 goAttrType := s.goType + "Attr" 439 if err := outputPatchedStruct(gf, w, goAttrType, t, s.patches); err != nil { 440 return nil, fmt.Errorf("output %q: %w", goAttrType, err) 441 } 442 443 switch s.ret { 444 case retError: 445 fmt.Fprintf(w, "func %s(attr *%s) error { _, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); return err }\n\n", s.goType, goAttrType, s.cmd) 446 case retFd: 447 fmt.Fprintf(w, "func %s(attr *%s) (*FD, error) { fd, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); if err != nil { return nil, err }; return NewFD(int(fd)) }\n\n", s.goType, goAttrType, s.cmd) 448 } 449 } 450 451 // Link info type specific 452 453 linkInfoExtraTypes := []struct { 454 goType string 455 cType string 456 patches []patch 457 }{ 458 {"CgroupLinkInfo", "cgroup", []patch{replace(enumTypes["AttachType"], "attach_type")}}, 459 {"IterLinkInfo", "iter", []patch{replace(pointer, "target_name"), truncateAfter("target_name_len")}}, 460 {"NetNsLinkInfo", "netns", []patch{replace(enumTypes["AttachType"], "attach_type")}}, 461 {"RawTracepointLinkInfo", "raw_tracepoint", []patch{replace(pointer, "tp_name")}}, 462 {"TracingLinkInfo", "tracing", []patch{replace(enumTypes["AttachType"], "attach_type")}}, 463 {"XDPLinkInfo", "xdp", nil}, 464 } 465 466 sort.Slice(linkInfoExtraTypes, func(i, j int) bool { 467 return linkInfoExtraTypes[i].goType < linkInfoExtraTypes[j].goType 468 }) 469 470 var bpfLinkInfo *btf.Struct 471 if err := spec.TypeByName("bpf_link_info", &bpfLinkInfo); err != nil { 472 return nil, err 473 } 474 475 member := bpfLinkInfo.Members[len(bpfLinkInfo.Members)-1] 476 bpfLinkInfoUnion, ok := member.Type.(*btf.Union) 477 if !ok { 478 return nil, fmt.Errorf("there is not type-specific union") 479 } 480 481 linkInfoTypes, err := splitUnion(bpfLinkInfoUnion, types{ 482 {"raw_tracepoint", "raw_tracepoint"}, 483 {"tracing", "tracing"}, 484 {"cgroup", "cgroup"}, 485 {"iter", "iter"}, 486 {"netns", "netns"}, 487 {"xdp", "xdp"}, 488 }) 489 if err != nil { 490 return nil, fmt.Errorf("splitting linkInfo: %w", err) 491 } 492 493 for _, s := range linkInfoExtraTypes { 494 t := linkInfoTypes[s.cType] 495 if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil { 496 return nil, fmt.Errorf("output %q: %w", s.goType, err) 497 } 498 } 499 500 return w.Bytes(), nil 501 } 502 503 func outputPatchedStruct(gf *btf.GoFormatter, w *bytes.Buffer, id string, s *btf.Struct, patches []patch) error { 504 s = btf.Copy(s, nil).(*btf.Struct) 505 506 for i, p := range patches { 507 if err := p(s); err != nil { 508 return fmt.Errorf("patch %d: %w", i, err) 509 } 510 } 511 512 decl, err := gf.TypeDeclaration(id, s) 513 if err != nil { 514 return err 515 } 516 517 w.WriteString(decl) 518 w.WriteString("\n\n") 519 return nil 520 } 521 522 type types []struct { 523 name string 524 cFieldOrFirstMember string 525 } 526 527 func splitUnion(union *btf.Union, types types) (map[string]*btf.Struct, error) { 528 structs := make(map[string]*btf.Struct) 529 530 for i, t := range types { 531 member := union.Members[i] 532 s, ok := member.Type.(*btf.Struct) 533 if !ok { 534 return nil, fmt.Errorf("%q: %s is not a struct", t.name, member.Type) 535 } 536 537 if member.Name == "" { 538 // This is an anonymous struct, check the name of the first member instead. 539 if name := s.Members[0].Name; name != t.cFieldOrFirstMember { 540 return nil, fmt.Errorf("first field of %q is %q, not %q", t.name, name, t.cFieldOrFirstMember) 541 } 542 } else if member.Name != t.cFieldOrFirstMember { 543 return nil, fmt.Errorf("name for %q is %q, not %q", t.name, member.Name, t.cFieldOrFirstMember) 544 } 545 546 structs[t.name] = s 547 } 548 549 return structs, nil 550 } 551 552 type patch func(*btf.Struct) error 553 554 func modify(fn func(*btf.Member) error, members ...string) patch { 555 return func(s *btf.Struct) error { 556 want := make(map[string]bool) 557 for _, name := range members { 558 want[name] = true 559 } 560 561 for i, m := range s.Members { 562 if want[m.Name] { 563 if err := fn(&s.Members[i]); err != nil { 564 return err 565 } 566 delete(want, m.Name) 567 } 568 } 569 570 if len(want) == 0 { 571 return nil 572 } 573 574 var missing []string 575 for name := range want { 576 missing = append(missing, name) 577 } 578 sort.Strings(missing) 579 580 return fmt.Errorf("missing members: %v", strings.Join(missing, ", ")) 581 } 582 } 583 584 func modifyNth(fn func(*btf.Member) error, indices ...int) patch { 585 return func(s *btf.Struct) error { 586 for _, i := range indices { 587 if i >= len(s.Members) { 588 return fmt.Errorf("index %d is out of bounds", i) 589 } 590 591 if err := fn(&s.Members[i]); err != nil { 592 return fmt.Errorf("member #%d: %w", i, err) 593 } 594 } 595 return nil 596 } 597 } 598 599 func replace(t btf.Type, members ...string) patch { 600 return modify(func(m *btf.Member) error { 601 m.Type = t 602 return nil 603 }, members...) 604 } 605 606 func choose(member int, name string) patch { 607 return modifyNth(func(m *btf.Member) error { 608 union, ok := m.Type.(*btf.Union) 609 if !ok { 610 return fmt.Errorf("member %d is %s, not a union", member, m.Type) 611 } 612 613 for _, um := range union.Members { 614 if um.Name == name { 615 m.Name = um.Name 616 m.Type = um.Type 617 return nil 618 } 619 } 620 621 return fmt.Errorf("%s has no member %q", union, name) 622 }, member) 623 } 624 625 func chooseNth(member int, n int) patch { 626 return modifyNth(func(m *btf.Member) error { 627 union, ok := m.Type.(*btf.Union) 628 if !ok { 629 return fmt.Errorf("member %d is %s, not a union", member, m.Type) 630 } 631 632 if n >= len(union.Members) { 633 return fmt.Errorf("member %d is out of bounds", n) 634 } 635 636 um := union.Members[n] 637 m.Name = um.Name 638 m.Type = um.Type 639 return nil 640 }, member) 641 } 642 643 func flattenAnon(s *btf.Struct) error { 644 for i := range s.Members { 645 m := &s.Members[i] 646 647 cs, ok := m.Type.(*btf.Struct) 648 if !ok || cs.TypeName() != "" { 649 continue 650 } 651 652 for j := range cs.Members { 653 cs.Members[j].Offset += m.Offset 654 } 655 656 newMembers := make([]btf.Member, 0, len(s.Members)+len(cs.Members)-1) 657 newMembers = append(newMembers, s.Members[:i]...) 658 newMembers = append(newMembers, cs.Members...) 659 newMembers = append(newMembers, s.Members[i+1:]...) 660 661 s.Members = newMembers 662 } 663 664 return nil 665 } 666 667 func truncateAfter(name string) patch { 668 return func(s *btf.Struct) error { 669 for i, m := range s.Members { 670 if m.Name != name { 671 continue 672 } 673 674 size, err := btf.Sizeof(m.Type) 675 if err != nil { 676 return err 677 } 678 679 s.Members = s.Members[:i+1] 680 s.Size = m.Offset.Bytes() + uint32(size) 681 return nil 682 } 683 684 return fmt.Errorf("no member %q", name) 685 } 686 } 687 688 func rename(from, to string) patch { 689 return func(s *btf.Struct) error { 690 for i, m := range s.Members { 691 if m.Name == from { 692 s.Members[i].Name = to 693 return nil 694 } 695 } 696 return fmt.Errorf("no member named %q", from) 697 } 698 } 699 700 func name(member int, name string) patch { 701 return modifyNth(func(m *btf.Member) error { 702 if m.Name != "" { 703 return fmt.Errorf("member already has name %q", m.Name) 704 } 705 706 m.Name = name 707 return nil 708 }, member) 709 } 710 711 func replaceWithBytes(members ...string) patch { 712 return modify(func(m *btf.Member) error { 713 if m.BitfieldSize != 0 { 714 return errors.New("replaceWithBytes: member is a bitfield") 715 } 716 717 size, err := btf.Sizeof(m.Type) 718 if err != nil { 719 return fmt.Errorf("replaceWithBytes: size of %s: %w", m.Type, err) 720 } 721 722 m.Type = &btf.Array{ 723 Type: &btf.Int{Size: 1}, 724 Nelems: uint32(size), 725 } 726 727 return nil 728 }, members...) 729 }