github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/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 "slices" 13 "sort" 14 "strings" 15 16 "github.com/cilium/ebpf/btf" 17 "github.com/cilium/ebpf/internal" 18 "github.com/cilium/ebpf/internal/sys" 19 ) 20 21 type syscallRetval int 22 23 const ( 24 retError syscallRetval = iota 25 retFd 26 ) 27 28 func main() { 29 if err := run(os.Args[1:]); err != nil { 30 fmt.Fprintln(os.Stderr, "Error:", err) 31 os.Exit(1) 32 } 33 } 34 35 func run(args []string) error { 36 if len(args) != 1 { 37 return fmt.Errorf("expect location of compressed vmlinux .BTF as argument") 38 } 39 40 raw, err := internal.ReadAllCompressed(args[0]) 41 if err != nil { 42 return err 43 } 44 45 spec, err := btf.LoadSpecFromReader(bytes.NewReader(raw)) 46 if err != nil { 47 return err 48 } 49 50 output, err := generateTypes(spec) 51 var fpe *failedPatchError 52 if errors.As(err, &fpe) { 53 fmt.Fprintf(os.Stderr, " %v\n", fpe.Type) 54 for _, member := range fpe.Type.Members { 55 fmt.Fprintf(os.Stderr, " %q %v\n", member.Name, member.Type) 56 } 57 } 58 if err != nil { 59 return err 60 } 61 62 w, err := os.Create("types.go") 63 if err != nil { 64 return err 65 } 66 defer w.Close() 67 68 return internal.WriteFormatted(output, w) 69 } 70 71 func generateTypes(spec *btf.Spec) ([]byte, error) { 72 objName := &btf.Array{Nelems: 16, Type: &btf.Int{Encoding: btf.Char, Size: 1}} 73 linkID := &btf.Int{Size: 4} 74 btfID := &btf.Int{Size: 4} 75 typeID := &btf.Int{Size: 4} 76 pointer := &btf.Int{Size: 8} 77 logLevel := &btf.Int{Size: 4} 78 mapFlags := &btf.Int{Size: 4} 79 80 gf := &btf.GoFormatter{ 81 Names: map[btf.Type]string{ 82 objName: internal.GoTypeName(sys.ObjName{}), 83 linkID: internal.GoTypeName(sys.LinkID(0)), 84 btfID: internal.GoTypeName(sys.BTFID(0)), 85 typeID: internal.GoTypeName(sys.TypeID(0)), 86 pointer: internal.GoTypeName(sys.Pointer{}), 87 logLevel: internal.GoTypeName(sys.LogLevel(0)), 88 mapFlags: internal.GoTypeName(sys.MapFlags(0)), 89 }, 90 Identifier: internal.Identifier, 91 EnumIdentifier: func(name, element string) string { 92 return element 93 }, 94 } 95 96 w := bytes.NewBuffer(nil) 97 w.WriteString(`// Code generated by internal/cmd/gentypes; DO NOT EDIT. 98 99 package sys 100 101 import ( 102 "unsafe" 103 ) 104 105 `) 106 107 enums := []struct { 108 goType string 109 cType string 110 }{ 111 {"Cmd", "bpf_cmd"}, 112 {"MapType", "bpf_map_type"}, 113 {"ProgType", "bpf_prog_type"}, 114 {"AttachType", "bpf_attach_type"}, 115 {"LinkType", "bpf_link_type"}, 116 {"StatsType", "bpf_stats_type"}, 117 {"SkAction", "sk_action"}, 118 {"StackBuildIdStatus", "bpf_stack_build_id_status"}, 119 {"FunctionId", "bpf_func_id"}, 120 {"AdjRoomMode", "bpf_adj_room_mode"}, 121 {"HdrStartOff", "bpf_hdr_start_off"}, 122 {"RetCode", "bpf_ret_code"}, 123 {"XdpAction", "xdp_action"}, 124 {"TcxActionBase", "tcx_action_base"}, 125 {"PerfEventType", "bpf_perf_event_type"}, 126 } 127 128 sort.Slice(enums, func(i, j int) bool { 129 return enums[i].goType < enums[j].goType 130 }) 131 132 enumTypes := make(map[string]btf.Type) 133 for _, o := range enums { 134 fmt.Println("enum", o.goType) 135 136 var t *btf.Enum 137 if err := spec.TypeByName(o.cType, &t); err != nil { 138 return nil, err 139 } 140 141 // Add the enum as a predeclared type so that generated structs 142 // refer to the Go types. 143 if name := gf.Names[t]; name != "" { 144 return nil, fmt.Errorf("type %q is already declared as %s", o.cType, name) 145 } 146 gf.Names[t] = o.goType 147 enumTypes[o.goType] = t 148 149 decl, err := gf.TypeDeclaration(o.goType, t) 150 if err != nil { 151 return nil, fmt.Errorf("generate %q: %w", o.goType, err) 152 } 153 154 w.WriteString(decl) 155 w.WriteRune('\n') 156 } 157 158 // Assorted structs 159 160 structs := []struct { 161 goType string 162 cType string 163 patches []patch 164 }{ 165 { 166 "ProgInfo", "bpf_prog_info", 167 []patch{ 168 replace(objName, "name"), 169 replace(pointer, "xlated_prog_insns"), 170 replace(pointer, "map_ids"), 171 replace(pointer, "line_info"), 172 replace(pointer, "func_info"), 173 replace(btfID, "btf_id", "attach_btf_obj_id"), 174 replace(typeID, "attach_btf_id"), 175 }, 176 }, 177 { 178 "MapInfo", "bpf_map_info", 179 []patch{ 180 replace(objName, "name"), 181 replace(mapFlags, "map_flags"), 182 replace(typeID, "btf_vmlinux_value_type_id", "btf_key_type_id", "btf_value_type_id"), 183 }, 184 }, 185 { 186 "BtfInfo", "bpf_btf_info", 187 []patch{ 188 replace(pointer, "btf", "name"), 189 replace(btfID, "id"), 190 }, 191 }, 192 { 193 "LinkInfo", "bpf_link_info", 194 []patch{ 195 replace(enumTypes["LinkType"], "type"), 196 replace(linkID, "id"), 197 name(3, "extra"), 198 replaceWithBytes("extra"), 199 }, 200 }, 201 {"FuncInfo", "bpf_func_info", nil}, 202 {"LineInfo", "bpf_line_info", nil}, 203 {"XdpMd", "xdp_md", nil}, 204 { 205 "SkLookup", "bpf_sk_lookup", 206 []patch{ 207 choose(0, "cookie"), 208 replaceWithBytes("remote_ip4", "remote_ip6", "local_ip4", "local_ip6"), 209 }, 210 }, 211 } 212 213 sort.Slice(structs, func(i, j int) bool { 214 return structs[i].goType < structs[j].goType 215 }) 216 217 for _, s := range structs { 218 fmt.Println("struct", s.goType) 219 220 var t *btf.Struct 221 if err := spec.TypeByName(s.cType, &t); err != nil { 222 return nil, err 223 } 224 225 if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil { 226 return nil, fmt.Errorf("output %q: %w", s.goType, err) 227 } 228 } 229 230 // Attrs 231 232 attrs := []struct { 233 goType string 234 ret syscallRetval 235 cType string 236 cmd string 237 patches []patch 238 }{ 239 { 240 "MapCreate", retFd, "map_create", "BPF_MAP_CREATE", 241 []patch{ 242 replace(objName, "map_name"), 243 replace(enumTypes["MapType"], "map_type"), 244 replace(mapFlags, "map_flags"), 245 replace(typeID, "btf_vmlinux_value_type_id", "btf_key_type_id", "btf_value_type_id"), 246 }, 247 }, 248 { 249 "MapLookupElem", retError, "map_elem", "BPF_MAP_LOOKUP_ELEM", 250 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 251 }, 252 { 253 "MapLookupAndDeleteElem", retError, "map_elem", "BPF_MAP_LOOKUP_AND_DELETE_ELEM", 254 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 255 }, 256 { 257 "MapUpdateElem", retError, "map_elem", "BPF_MAP_UPDATE_ELEM", 258 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 259 }, 260 { 261 "MapDeleteElem", retError, "map_elem", "BPF_MAP_DELETE_ELEM", 262 []patch{choose(2, "value"), replace(pointer, "key", "value")}, 263 }, 264 { 265 "MapGetNextKey", retError, "map_elem", "BPF_MAP_GET_NEXT_KEY", 266 []patch{ 267 choose(2, "next_key"), replace(pointer, "key", "next_key"), 268 truncateAfter("next_key"), 269 }, 270 }, 271 { 272 "MapFreeze", retError, "map_elem", "BPF_MAP_FREEZE", 273 []patch{truncateAfter("map_fd")}, 274 }, 275 { 276 "MapLookupBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_BATCH", 277 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 278 }, 279 { 280 "MapLookupAndDeleteBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_AND_DELETE_BATCH", 281 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 282 }, 283 { 284 "MapUpdateBatch", retError, "map_elem_batch", "BPF_MAP_UPDATE_BATCH", 285 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 286 }, 287 { 288 "MapDeleteBatch", retError, "map_elem_batch", "BPF_MAP_DELETE_BATCH", 289 []patch{replace(pointer, "in_batch", "out_batch", "keys", "values")}, 290 }, 291 { 292 "ProgLoad", retFd, "prog_load", "BPF_PROG_LOAD", 293 []patch{ 294 replace(objName, "prog_name"), 295 replace(enumTypes["ProgType"], "prog_type"), 296 replace(enumTypes["AttachType"], "expected_attach_type"), 297 replace(logLevel, "log_level"), 298 replace(pointer, 299 "insns", 300 "license", 301 "log_buf", 302 "func_info", 303 "line_info", 304 "fd_array", 305 "core_relos", 306 ), 307 replace(typeID, "attach_btf_id"), 308 choose(20, "attach_btf_obj_fd"), 309 }, 310 }, 311 { 312 "ProgBindMap", retError, "prog_bind_map", "BPF_PROG_BIND_MAP", 313 nil, 314 }, 315 { 316 "ObjPin", retError, "obj_pin", "BPF_OBJ_PIN", 317 []patch{replace(pointer, "pathname")}, 318 }, 319 { 320 "ObjGet", retFd, "obj_pin", "BPF_OBJ_GET", 321 []patch{replace(pointer, "pathname")}, 322 }, 323 { 324 "ProgAttach", retError, "prog_attach", "BPF_PROG_ATTACH", 325 []patch{ 326 flattenAnon, 327 rename("target_fd", "target_fd_or_ifindex"), 328 rename("relative_fd", "relative_fd_or_id"), 329 }, 330 }, 331 { 332 "ProgDetach", retError, "prog_attach", "BPF_PROG_DETACH", 333 []patch{ 334 flattenAnon, 335 rename("target_fd", "target_fd_or_ifindex"), 336 truncateAfter("expected_revision"), 337 rename("relative_fd", "relative_fd_or_id"), 338 remove("replace_bpf_fd"), 339 }, 340 }, 341 { 342 "ProgRun", retError, "prog_run", "BPF_PROG_TEST_RUN", 343 []patch{replace(pointer, "data_in", "data_out", "ctx_in", "ctx_out")}, 344 }, 345 { 346 "ProgGetNextId", retError, "obj_next_id", "BPF_PROG_GET_NEXT_ID", 347 []patch{ 348 choose(0, "start_id"), rename("start_id", "id"), 349 truncateAfter("next_id"), 350 }, 351 }, 352 { 353 "MapGetNextId", retError, "obj_next_id", "BPF_MAP_GET_NEXT_ID", 354 []patch{ 355 choose(0, "start_id"), rename("start_id", "id"), 356 truncateAfter("next_id"), 357 }, 358 }, 359 { 360 "BtfGetNextId", retError, "obj_next_id", "BPF_BTF_GET_NEXT_ID", 361 []patch{ 362 choose(0, "start_id"), rename("start_id", "id"), 363 replace(btfID, "id", "next_id"), 364 truncateAfter("next_id"), 365 }, 366 }, 367 { 368 "LinkGetNextId", retError, "obj_next_id", "BPF_LINK_GET_NEXT_ID", 369 []patch{ 370 choose(0, "start_id"), rename("start_id", "id"), 371 replace(linkID, "id", "next_id"), 372 truncateAfter("next_id"), 373 }, 374 }, 375 // These piggy back on the obj_next_id decl, but only support the 376 // first field... 377 { 378 "BtfGetFdById", retFd, "obj_next_id", "BPF_BTF_GET_FD_BY_ID", 379 []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, 380 }, 381 { 382 "MapGetFdById", retFd, "obj_next_id", "BPF_MAP_GET_FD_BY_ID", 383 []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, 384 }, 385 { 386 "ProgGetFdById", retFd, "obj_next_id", "BPF_PROG_GET_FD_BY_ID", 387 []patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")}, 388 }, 389 { 390 "LinkGetFdById", retFd, "obj_next_id", "BPF_LINK_GET_FD_BY_ID", 391 []patch{choose(0, "start_id"), rename("start_id", "id"), replace(linkID, "id"), truncateAfter("id")}, 392 }, 393 { 394 "ObjGetInfoByFd", retError, "info_by_fd", "BPF_OBJ_GET_INFO_BY_FD", 395 []patch{replace(pointer, "info")}, 396 }, 397 { 398 "RawTracepointOpen", retFd, "raw_tracepoint_open", "BPF_RAW_TRACEPOINT_OPEN", 399 []patch{replace(pointer, "name")}, 400 }, 401 { 402 "BtfLoad", retFd, "btf_load", "BPF_BTF_LOAD", 403 []patch{replace(pointer, "btf", "btf_log_buf")}, 404 }, 405 { 406 "LinkCreate", retFd, "link_create", "BPF_LINK_CREATE", 407 []patch{ 408 replace(enumTypes["AttachType"], "attach_type"), 409 choose(4, "target_btf_id"), 410 replace(typeID, "target_btf_id"), 411 }, 412 }, 413 { 414 "LinkCreateIter", retFd, "link_create", "BPF_LINK_CREATE", 415 []patch{ 416 chooseNth(4, 1), 417 replace(enumTypes["AttachType"], "attach_type"), 418 flattenAnon, 419 replace(pointer, "iter_info"), 420 }, 421 }, 422 { 423 "LinkCreatePerfEvent", retFd, "link_create", "BPF_LINK_CREATE", 424 []patch{ 425 chooseNth(4, 2), 426 replace(enumTypes["AttachType"], "attach_type"), 427 flattenAnon, 428 }, 429 }, 430 { 431 "LinkCreateKprobeMulti", retFd, "link_create", "BPF_LINK_CREATE", 432 []patch{ 433 chooseNth(4, 3), 434 replace(enumTypes["AttachType"], "attach_type"), 435 modify(func(m *btf.Member) error { 436 return rename("flags", "kprobe_multi_flags")(m.Type.(*btf.Struct)) 437 }, "kprobe_multi"), 438 flattenAnon, 439 replace(pointer, "cookies"), 440 replace(pointer, "addrs"), 441 replace(pointer, "syms"), 442 rename("cnt", "count"), 443 }, 444 }, 445 { 446 "LinkCreateNetfilter", retFd, "link_create", "BPF_LINK_CREATE", 447 []patch{ 448 chooseNth(4, 5), 449 replace(enumTypes["AttachType"], "attach_type"), 450 modify(func(m *btf.Member) error { 451 return rename("flags", "netfilter_flags")(m.Type.(*btf.Struct)) 452 }, "netfilter"), 453 flattenAnon, 454 }, 455 }, 456 { 457 "LinkCreateTracing", retFd, "link_create", "BPF_LINK_CREATE", 458 []patch{ 459 chooseNth(4, 4), 460 replace(enumTypes["AttachType"], "attach_type"), 461 flattenAnon, 462 replace(btfID, "target_btf_id"), 463 }, 464 }, 465 { 466 "LinkCreateTcx", retFd, "link_create", "BPF_LINK_CREATE", 467 []patch{ 468 choose(1, "target_ifindex"), 469 choose(4, "tcx"), 470 replace(enumTypes["AttachType"], "attach_type"), 471 flattenAnon, 472 flattenAnon, // flatten tcx member 473 rename("relative_fd", "relative_fd_or_id"), 474 }, 475 }, 476 { 477 "LinkCreateUprobeMulti", retFd, "link_create", "BPF_LINK_CREATE", 478 []patch{ 479 chooseNth(4, 7), 480 replace(enumTypes["AttachType"], "attach_type"), 481 modify(func(m *btf.Member) error { 482 return rename("flags", "uprobe_multi_flags")(m.Type.(*btf.Struct)) 483 }, "uprobe_multi"), 484 flattenAnon, 485 replace(pointer, "path"), 486 replace(pointer, "offsets"), 487 replace(pointer, "ref_ctr_offsets"), 488 replace(pointer, "cookies"), 489 rename("cnt", "count"), 490 }, 491 }, 492 { 493 "LinkCreateNetkit", retFd, "link_create", "BPF_LINK_CREATE", 494 []patch{ 495 choose(1, "target_ifindex"), 496 choose(4, "netkit"), 497 replace(enumTypes["AttachType"], "attach_type"), 498 flattenAnon, 499 flattenAnon, 500 rename("relative_fd", "relative_fd_or_id"), 501 }, 502 }, 503 { 504 "LinkUpdate", retError, "link_update", "BPF_LINK_UPDATE", 505 nil, 506 }, 507 { 508 "EnableStats", retFd, "enable_stats", "BPF_ENABLE_STATS", 509 nil, 510 }, 511 { 512 "IterCreate", retFd, "iter_create", "BPF_ITER_CREATE", 513 nil, 514 }, 515 { 516 "ProgQuery", retError, "prog_query", "BPF_PROG_QUERY", 517 []patch{ 518 replace(enumTypes["AttachType"], "attach_type"), 519 replace(pointer, "prog_ids", "prog_attach_flags"), 520 replace(pointer, "link_ids", "link_attach_flags"), 521 flattenAnon, 522 rename("prog_cnt", "count"), 523 rename("target_fd", "target_fd_or_ifindex"), 524 }, 525 }, 526 } 527 528 sort.Slice(attrs, func(i, j int) bool { 529 return attrs[i].goType < attrs[j].goType 530 }) 531 532 var bpfAttr *btf.Union 533 if err := spec.TypeByName("bpf_attr", &bpfAttr); err != nil { 534 return nil, err 535 } 536 attrTypes, err := splitUnion(bpfAttr, types{ 537 {"map_create", "map_type"}, 538 {"map_elem", "map_fd"}, 539 {"map_elem_batch", "batch"}, 540 {"prog_load", "prog_type"}, 541 {"obj_pin", "pathname"}, 542 {"prog_attach", ""}, 543 {"prog_run", "test"}, 544 {"obj_next_id", ""}, 545 {"info_by_fd", "info"}, 546 {"prog_query", "query"}, 547 {"raw_tracepoint_open", "raw_tracepoint"}, 548 {"btf_load", "btf"}, 549 {"task_fd_query", "task_fd_query"}, 550 {"link_create", "link_create"}, 551 {"link_update", "link_update"}, 552 {"link_detach", "link_detach"}, 553 {"enable_stats", "enable_stats"}, 554 {"iter_create", "iter_create"}, 555 {"prog_bind_map", "prog_bind_map"}, 556 }) 557 if err != nil { 558 return nil, fmt.Errorf("split bpf_attr: %w", err) 559 } 560 561 for _, s := range attrs { 562 fmt.Println("attr", s.goType) 563 564 t := attrTypes[s.cType] 565 if t == nil { 566 return nil, fmt.Errorf("unknown attr %q", s.cType) 567 } 568 569 goAttrType := s.goType + "Attr" 570 if err := outputPatchedStruct(gf, w, goAttrType, t, s.patches); err != nil { 571 return nil, fmt.Errorf("output %q: %w", goAttrType, err) 572 } 573 574 switch s.ret { 575 case retError: 576 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) 577 case retFd: 578 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) 579 } 580 } 581 582 // Link info type specific 583 linkInfoExtraTypes := []struct { 584 goType string 585 patches []patch 586 }{ 587 {"CgroupLinkInfo", 588 []patch{ 589 choose(3, "cgroup"), 590 flattenAnon, 591 replace(enumTypes["AttachType"], "attach_type"), 592 }, 593 }, 594 {"IterLinkInfo", 595 []patch{ 596 choose(3, "iter"), 597 flattenAnon, 598 replace(pointer, "target_name"), 599 truncateAfter("target_name_len"), 600 }, 601 }, 602 {"NetNsLinkInfo", 603 []patch{choose(3, "netns"), 604 flattenAnon, 605 replace(enumTypes["AttachType"], "attach_type"), 606 }, 607 }, 608 {"RawTracepointLinkInfo", 609 []patch{choose(3, "raw_tracepoint"), 610 flattenAnon, 611 replace(pointer, "tp_name"), 612 }, 613 }, 614 {"TracingLinkInfo", 615 []patch{ 616 choose(3, "tracing"), 617 flattenAnon, 618 replace(enumTypes["AttachType"], "attach_type"), 619 replace(typeID, "target_btf_id"), 620 }, 621 }, 622 {"XDPLinkInfo", 623 []patch{choose(3, "xdp"), 624 flattenAnon, 625 }, 626 }, 627 {"TcxLinkInfo", 628 []patch{ 629 choose(3, "tcx"), 630 flattenAnon, 631 replace(enumTypes["AttachType"], "attach_type"), 632 }, 633 }, 634 {"NetfilterLinkInfo", 635 []patch{ 636 choose(3, "netfilter"), 637 flattenAnon, 638 }, 639 }, 640 {"NetkitLinkInfo", 641 []patch{ 642 choose(3, "netkit"), 643 flattenAnon, 644 replace(enumTypes["AttachType"], "attach_type"), 645 }, 646 }, 647 {"KprobeMultiLinkInfo", 648 []patch{ 649 choose(3, "kprobe_multi"), 650 flattenAnon, 651 replace(pointer, "addrs"), 652 }, 653 }, 654 {"PerfEventLinkInfo", 655 []patch{ 656 choose(3, "perf_event"), 657 flattenAnon, 658 renameNth(3, "perf_event_type"), 659 replace(enumTypes["PerfEventType"], "perf_event_type"), 660 truncateAfter("perf_event_type"), 661 }, 662 }, 663 {"KprobeLinkInfo", 664 []patch{ 665 choose(3, "perf_event"), 666 flattenAnon, 667 renameNth(3, "perf_event_type"), 668 replace(enumTypes["PerfEventType"], "perf_event_type"), 669 choose(4, "kprobe"), 670 flattenAnon, 671 replace(pointer, "func_name"), 672 }, 673 }, 674 } 675 676 sort.Slice(linkInfoExtraTypes, func(i, j int) bool { 677 return linkInfoExtraTypes[i].goType < linkInfoExtraTypes[j].goType 678 }) 679 680 var bpfLinkInfo *btf.Struct 681 if err := spec.TypeByName("bpf_link_info", &bpfLinkInfo); err != nil { 682 return nil, err 683 } 684 685 patches := []patch{ 686 replace(enumTypes["LinkType"], "type"), 687 replace(linkID, "id"), 688 } 689 690 for _, s := range linkInfoExtraTypes { 691 if err := outputPatchedStruct(gf, w, s.goType, bpfLinkInfo, append(patches, s.patches...)); err != nil { 692 return nil, fmt.Errorf("output %q: %w", s.goType, err) 693 } 694 } 695 696 return w.Bytes(), nil 697 } 698 699 type failedPatchError struct { 700 Type *btf.Struct 701 number int 702 err error 703 } 704 705 func (fpe *failedPatchError) Unwrap() error { 706 return fpe.err 707 } 708 709 func (fpe *failedPatchError) Error() string { 710 return fmt.Sprintf("patch %d: %v", fpe.number, fpe.err) 711 } 712 713 func outputPatchedStruct(gf *btf.GoFormatter, w *bytes.Buffer, id string, s *btf.Struct, patches []patch) error { 714 s = btf.Copy(s).(*btf.Struct) 715 716 for i, p := range patches { 717 if err := p(s); err != nil { 718 return &failedPatchError{s, i, err} 719 } 720 } 721 722 decl, err := gf.TypeDeclaration(id, s) 723 if err != nil { 724 return err 725 } 726 727 w.WriteString(decl) 728 w.WriteString("\n\n") 729 return nil 730 } 731 732 type types []struct { 733 name string 734 cFieldOrFirstMember string 735 } 736 737 func splitUnion(union *btf.Union, types types) (map[string]*btf.Struct, error) { 738 structs := make(map[string]*btf.Struct) 739 740 for i, t := range types { 741 member := union.Members[i] 742 s, ok := member.Type.(*btf.Struct) 743 if !ok { 744 return nil, fmt.Errorf("%q: %s is not a struct", t.name, member.Type) 745 } 746 747 if member.Name == "" { 748 // This is an anonymous struct, check the name of the first member instead. 749 if name := s.Members[0].Name; name != t.cFieldOrFirstMember { 750 return nil, fmt.Errorf("first field of %q is %q, not %q", t.name, name, t.cFieldOrFirstMember) 751 } 752 } else if member.Name != t.cFieldOrFirstMember { 753 return nil, fmt.Errorf("name for %q is %q, not %q", t.name, member.Name, t.cFieldOrFirstMember) 754 } 755 756 structs[t.name] = s 757 } 758 759 return structs, nil 760 } 761 762 type patch func(*btf.Struct) error 763 764 func modify(fn func(*btf.Member) error, members ...string) patch { 765 return func(s *btf.Struct) error { 766 want := make(map[string]bool) 767 for _, name := range members { 768 want[name] = true 769 } 770 771 for i, m := range s.Members { 772 if want[m.Name] { 773 if err := fn(&s.Members[i]); err != nil { 774 return err 775 } 776 delete(want, m.Name) 777 } 778 } 779 780 if len(want) == 0 { 781 return nil 782 } 783 784 var missing []string 785 for name := range want { 786 missing = append(missing, name) 787 } 788 sort.Strings(missing) 789 790 return fmt.Errorf("missing members: %v", strings.Join(missing, ", ")) 791 } 792 } 793 794 func modifyNth(fn func(*btf.Member) error, indices ...int) patch { 795 return func(s *btf.Struct) error { 796 for _, i := range indices { 797 if i >= len(s.Members) { 798 return fmt.Errorf("index %d is out of bounds", i) 799 } 800 801 if err := fn(&s.Members[i]); err != nil { 802 return fmt.Errorf("member #%d: %w", i, err) 803 } 804 } 805 return nil 806 } 807 } 808 809 func replace(t btf.Type, members ...string) patch { 810 return modify(func(m *btf.Member) error { 811 m.Type = t 812 return nil 813 }, members...) 814 } 815 816 func choose(member int, name string) patch { 817 return modifyNth(func(m *btf.Member) error { 818 union, ok := m.Type.(*btf.Union) 819 if !ok { 820 return fmt.Errorf("member %d is %s, not a union", member, m.Type) 821 } 822 823 for _, um := range union.Members { 824 if um.Name == name { 825 m.Name = um.Name 826 m.Type = um.Type 827 return nil 828 } 829 } 830 831 return fmt.Errorf("%s has no member %q", union, name) 832 }, member) 833 } 834 835 func chooseNth(member int, n int) patch { 836 return modifyNth(func(m *btf.Member) error { 837 union, ok := m.Type.(*btf.Union) 838 if !ok { 839 return fmt.Errorf("member %d is %s, not a union", member, m.Type) 840 } 841 842 if n >= len(union.Members) { 843 return fmt.Errorf("member %d is out of bounds", n) 844 } 845 846 um := union.Members[n] 847 m.Name = um.Name 848 m.Type = um.Type 849 return nil 850 }, member) 851 } 852 853 func flattenAnon(s *btf.Struct) error { 854 for i := range s.Members { 855 m := &s.Members[i] 856 857 if m.Type.TypeName() != "" { 858 continue 859 } 860 861 var newMembers []btf.Member 862 switch cs := m.Type.(type) { 863 case *btf.Struct: 864 for j := range cs.Members { 865 cs.Members[j].Offset += m.Offset 866 } 867 newMembers = cs.Members 868 869 case *btf.Union: 870 cs.Members[0].Offset += m.Offset 871 newMembers = []btf.Member{cs.Members[0]} 872 873 default: 874 continue 875 } 876 877 s.Members = slices.Replace(s.Members, i, i+1, newMembers...) 878 } 879 880 return nil 881 } 882 883 func truncateAfter(name string) patch { 884 return func(s *btf.Struct) error { 885 for i, m := range s.Members { 886 if m.Name != name { 887 continue 888 } 889 890 size, err := btf.Sizeof(m.Type) 891 if err != nil { 892 return err 893 } 894 895 s.Members = s.Members[:i+1] 896 s.Size = m.Offset.Bytes() + uint32(size) 897 return nil 898 } 899 900 return fmt.Errorf("no member %q", name) 901 } 902 } 903 904 func rename(from, to string) patch { 905 return func(s *btf.Struct) error { 906 for i, m := range s.Members { 907 if m.Name == from { 908 s.Members[i].Name = to 909 return nil 910 } 911 } 912 return fmt.Errorf("no member named %q", from) 913 } 914 } 915 916 func renameNth(idx int, to string) patch { 917 return func(s *btf.Struct) error { 918 if idx >= len(s.Members) { 919 return fmt.Errorf("index %d is out of bounds", idx) 920 } 921 s.Members[idx].Name = to 922 return nil 923 } 924 } 925 926 func name(member int, name string) patch { 927 return modifyNth(func(m *btf.Member) error { 928 if m.Name != "" { 929 return fmt.Errorf("member already has name %q", m.Name) 930 } 931 932 m.Name = name 933 return nil 934 }, member) 935 } 936 937 func replaceWithBytes(members ...string) patch { 938 return modify(func(m *btf.Member) error { 939 if m.BitfieldSize != 0 { 940 return errors.New("replaceWithBytes: member is a bitfield") 941 } 942 943 size, err := btf.Sizeof(m.Type) 944 if err != nil { 945 return fmt.Errorf("replaceWithBytes: size of %s: %w", m.Type, err) 946 } 947 948 m.Type = &btf.Array{ 949 Type: &btf.Int{Size: 1}, 950 Nelems: uint32(size), 951 } 952 953 return nil 954 }, members...) 955 } 956 957 func remove(member string) patch { 958 return func(s *btf.Struct) error { 959 for i, m := range s.Members { 960 if m.Name == member { 961 s.Members = slices.Delete(s.Members, i, i+1) 962 return nil 963 } 964 } 965 return fmt.Errorf("member %q not found", member) 966 } 967 }