btf.go (22361B)
1 package btf 2 3 import ( 4 "bufio" 5 "bytes" 6 "debug/elf" 7 "encoding/binary" 8 "errors" 9 "fmt" 10 "io" 11 "math" 12 "os" 13 "reflect" 14 15 "github.com/cilium/ebpf/internal" 16 "github.com/cilium/ebpf/internal/sys" 17 "github.com/cilium/ebpf/internal/unix" 18 ) 19 20 const btfMagic = 0xeB9F 21 22 // Errors returned by BTF functions. 23 var ( 24 ErrNotSupported = internal.ErrNotSupported 25 ErrNotFound = errors.New("not found") 26 ErrNoExtendedInfo = errors.New("no extended info") 27 ) 28 29 // ID represents the unique ID of a BTF object. 30 type ID = sys.BTFID 31 32 // Spec represents decoded BTF. 33 type Spec struct { 34 // Data from .BTF. 35 rawTypes []rawType 36 strings *stringTable 37 38 // All types contained by the spec. For the base type, the position of 39 // a type in the slice is its ID. 40 types types 41 42 // Type IDs indexed by type. 43 typeIDs map[Type]TypeID 44 45 // Types indexed by essential name. 46 // Includes all struct flavors and types with the same name. 47 namedTypes map[essentialName][]Type 48 49 byteOrder binary.ByteOrder 50 } 51 52 type btfHeader struct { 53 Magic uint16 54 Version uint8 55 Flags uint8 56 HdrLen uint32 57 58 TypeOff uint32 59 TypeLen uint32 60 StringOff uint32 61 StringLen uint32 62 } 63 64 // typeStart returns the offset from the beginning of the .BTF section 65 // to the start of its type entries. 66 func (h *btfHeader) typeStart() int64 { 67 return int64(h.HdrLen + h.TypeOff) 68 } 69 70 // stringStart returns the offset from the beginning of the .BTF section 71 // to the start of its string table. 72 func (h *btfHeader) stringStart() int64 { 73 return int64(h.HdrLen + h.StringOff) 74 } 75 76 // LoadSpec opens file and calls LoadSpecFromReader on it. 77 func LoadSpec(file string) (*Spec, error) { 78 fh, err := os.Open(file) 79 if err != nil { 80 return nil, err 81 } 82 defer fh.Close() 83 84 return LoadSpecFromReader(fh) 85 } 86 87 // LoadSpecFromReader reads from an ELF or a raw BTF blob. 88 // 89 // Returns ErrNotFound if reading from an ELF which contains no BTF. ExtInfos 90 // may be nil. 91 func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { 92 file, err := internal.NewSafeELFFile(rd) 93 if err != nil { 94 if bo := guessRawBTFByteOrder(rd); bo != nil { 95 // Try to parse a naked BTF blob. This will return an error if 96 // we encounter a Datasec, since we can't fix it up. 97 spec, err := loadRawSpec(io.NewSectionReader(rd, 0, math.MaxInt64), bo, nil, nil) 98 return spec, err 99 } 100 101 return nil, err 102 } 103 104 return loadSpecFromELF(file) 105 } 106 107 // LoadSpecAndExtInfosFromReader reads from an ELF. 108 // 109 // ExtInfos may be nil if the ELF doesn't contain section metadta. 110 // Returns ErrNotFound if the ELF contains no BTF. 111 func LoadSpecAndExtInfosFromReader(rd io.ReaderAt) (*Spec, *ExtInfos, error) { 112 file, err := internal.NewSafeELFFile(rd) 113 if err != nil { 114 return nil, nil, err 115 } 116 117 spec, err := loadSpecFromELF(file) 118 if err != nil { 119 return nil, nil, err 120 } 121 122 extInfos, err := loadExtInfosFromELF(file, spec.types, spec.strings) 123 if err != nil && !errors.Is(err, ErrNotFound) { 124 return nil, nil, err 125 } 126 127 return spec, extInfos, nil 128 } 129 130 // variableOffsets extracts all symbols offsets from an ELF and indexes them by 131 // section and variable name. 132 // 133 // References to variables in BTF data sections carry unsigned 32-bit offsets. 134 // Some ELF symbols (e.g. in vmlinux) may point to virtual memory that is well 135 // beyond this range. Since these symbols cannot be described by BTF info, 136 // ignore them here. 137 func variableOffsets(file *internal.SafeELFFile) (map[variable]uint32, error) { 138 symbols, err := file.Symbols() 139 if err != nil { 140 return nil, fmt.Errorf("can't read symbols: %v", err) 141 } 142 143 variableOffsets := make(map[variable]uint32) 144 for _, symbol := range symbols { 145 if idx := symbol.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE { 146 // Ignore things like SHN_ABS 147 continue 148 } 149 150 if symbol.Value > math.MaxUint32 { 151 // VarSecinfo offset is u32, cannot reference symbols in higher regions. 152 continue 153 } 154 155 if int(symbol.Section) >= len(file.Sections) { 156 return nil, fmt.Errorf("symbol %s: invalid section %d", symbol.Name, symbol.Section) 157 } 158 159 secName := file.Sections[symbol.Section].Name 160 variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) 161 } 162 163 return variableOffsets, nil 164 } 165 166 func loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) { 167 var ( 168 btfSection *elf.Section 169 sectionSizes = make(map[string]uint32) 170 ) 171 172 for _, sec := range file.Sections { 173 switch sec.Name { 174 case ".BTF": 175 btfSection = sec 176 default: 177 if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS { 178 break 179 } 180 181 if sec.Size > math.MaxUint32 { 182 return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name) 183 } 184 185 sectionSizes[sec.Name] = uint32(sec.Size) 186 } 187 } 188 189 if btfSection == nil { 190 return nil, fmt.Errorf("btf: %w", ErrNotFound) 191 } 192 193 vars, err := variableOffsets(file) 194 if err != nil { 195 return nil, err 196 } 197 198 if btfSection.ReaderAt == nil { 199 return nil, fmt.Errorf("compressed BTF is not supported") 200 } 201 202 rawTypes, rawStrings, err := parseBTF(btfSection.ReaderAt, file.ByteOrder, nil) 203 if err != nil { 204 return nil, err 205 } 206 207 err = fixupDatasec(rawTypes, rawStrings, sectionSizes, vars) 208 if err != nil { 209 return nil, err 210 } 211 212 return inflateSpec(rawTypes, rawStrings, file.ByteOrder, nil) 213 } 214 215 func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, 216 baseTypes types, baseStrings *stringTable) (*Spec, error) { 217 218 rawTypes, rawStrings, err := parseBTF(btf, bo, baseStrings) 219 if err != nil { 220 return nil, err 221 } 222 223 return inflateSpec(rawTypes, rawStrings, bo, baseTypes) 224 } 225 226 func inflateSpec(rawTypes []rawType, rawStrings *stringTable, bo binary.ByteOrder, 227 baseTypes types) (*Spec, error) { 228 229 types, err := inflateRawTypes(rawTypes, baseTypes, rawStrings) 230 if err != nil { 231 return nil, err 232 } 233 234 typeIDs, typesByName := indexTypes(types, TypeID(len(baseTypes))) 235 236 return &Spec{ 237 rawTypes: rawTypes, 238 namedTypes: typesByName, 239 typeIDs: typeIDs, 240 types: types, 241 strings: rawStrings, 242 byteOrder: bo, 243 }, nil 244 } 245 246 func indexTypes(types []Type, typeIDOffset TypeID) (map[Type]TypeID, map[essentialName][]Type) { 247 namedTypes := 0 248 for _, typ := range types { 249 if typ.TypeName() != "" { 250 // Do a pre-pass to figure out how big types by name has to be. 251 // Most types have unique names, so it's OK to ignore essentialName 252 // here. 253 namedTypes++ 254 } 255 } 256 257 typeIDs := make(map[Type]TypeID, len(types)) 258 typesByName := make(map[essentialName][]Type, namedTypes) 259 260 for i, typ := range types { 261 if name := newEssentialName(typ.TypeName()); name != "" { 262 typesByName[name] = append(typesByName[name], typ) 263 } 264 typeIDs[typ] = TypeID(i) + typeIDOffset 265 } 266 267 return typeIDs, typesByName 268 } 269 270 // LoadKernelSpec returns the current kernel's BTF information. 271 // 272 // Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system 273 // for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled. 274 func LoadKernelSpec() (*Spec, error) { 275 fh, err := os.Open("/sys/kernel/btf/vmlinux") 276 if err == nil { 277 defer fh.Close() 278 279 return loadRawSpec(fh, internal.NativeEndian, nil, nil) 280 } 281 282 file, err := findVMLinux() 283 if err != nil { 284 return nil, err 285 } 286 defer file.Close() 287 288 return loadSpecFromELF(file) 289 } 290 291 // findVMLinux scans multiple well-known paths for vmlinux kernel images. 292 func findVMLinux() (*internal.SafeELFFile, error) { 293 release, err := internal.KernelRelease() 294 if err != nil { 295 return nil, err 296 } 297 298 // use same list of locations as libbpf 299 // https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122 300 locations := []string{ 301 "/boot/vmlinux-%s", 302 "/lib/modules/%s/vmlinux-%[1]s", 303 "/lib/modules/%s/build/vmlinux", 304 "/usr/lib/modules/%s/kernel/vmlinux", 305 "/usr/lib/debug/boot/vmlinux-%s", 306 "/usr/lib/debug/boot/vmlinux-%s.debug", 307 "/usr/lib/debug/lib/modules/%s/vmlinux", 308 } 309 310 for _, loc := range locations { 311 file, err := internal.OpenSafeELFFile(fmt.Sprintf(loc, release)) 312 if errors.Is(err, os.ErrNotExist) { 313 continue 314 } 315 return file, err 316 } 317 318 return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported) 319 } 320 321 // parseBTFHeader parses the header of the .BTF section. 322 func parseBTFHeader(r io.Reader, bo binary.ByteOrder) (*btfHeader, error) { 323 var header btfHeader 324 if err := binary.Read(r, bo, &header); err != nil { 325 return nil, fmt.Errorf("can't read header: %v", err) 326 } 327 328 if header.Magic != btfMagic { 329 return nil, fmt.Errorf("incorrect magic value %v", header.Magic) 330 } 331 332 if header.Version != 1 { 333 return nil, fmt.Errorf("unexpected version %v", header.Version) 334 } 335 336 if header.Flags != 0 { 337 return nil, fmt.Errorf("unsupported flags %v", header.Flags) 338 } 339 340 remainder := int64(header.HdrLen) - int64(binary.Size(&header)) 341 if remainder < 0 { 342 return nil, errors.New("header length shorter than btfHeader size") 343 } 344 345 if _, err := io.CopyN(internal.DiscardZeroes{}, r, remainder); err != nil { 346 return nil, fmt.Errorf("header padding: %v", err) 347 } 348 349 return &header, nil 350 } 351 352 func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder { 353 buf := new(bufio.Reader) 354 for _, bo := range []binary.ByteOrder{ 355 binary.LittleEndian, 356 binary.BigEndian, 357 } { 358 buf.Reset(io.NewSectionReader(r, 0, math.MaxInt64)) 359 if _, err := parseBTFHeader(buf, bo); err == nil { 360 return bo 361 } 362 } 363 364 return nil 365 } 366 367 // parseBTF reads a .BTF section into memory and parses it into a list of 368 // raw types and a string table. 369 func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([]rawType, *stringTable, error) { 370 buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64) 371 header, err := parseBTFHeader(buf, bo) 372 if err != nil { 373 return nil, nil, fmt.Errorf("parsing .BTF header: %v", err) 374 } 375 376 rawStrings, err := readStringTable(io.NewSectionReader(btf, header.stringStart(), int64(header.StringLen)), 377 baseStrings) 378 if err != nil { 379 return nil, nil, fmt.Errorf("can't read type names: %w", err) 380 } 381 382 buf.Reset(io.NewSectionReader(btf, header.typeStart(), int64(header.TypeLen))) 383 rawTypes, err := readTypes(buf, bo, header.TypeLen) 384 if err != nil { 385 return nil, nil, fmt.Errorf("can't read types: %w", err) 386 } 387 388 return rawTypes, rawStrings, nil 389 } 390 391 type variable struct { 392 section string 393 name string 394 } 395 396 func fixupDatasec(rawTypes []rawType, rawStrings *stringTable, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) error { 397 for i, rawType := range rawTypes { 398 if rawType.Kind() != kindDatasec { 399 continue 400 } 401 402 name, err := rawStrings.Lookup(rawType.NameOff) 403 if err != nil { 404 return err 405 } 406 407 if name == ".kconfig" || name == ".ksyms" { 408 return fmt.Errorf("reference to %s: %w", name, ErrNotSupported) 409 } 410 411 if rawTypes[i].SizeType != 0 { 412 continue 413 } 414 415 size, ok := sectionSizes[name] 416 if !ok { 417 return fmt.Errorf("data section %s: missing size", name) 418 } 419 420 rawTypes[i].SizeType = size 421 422 secinfos := rawType.data.([]btfVarSecinfo) 423 for j, secInfo := range secinfos { 424 id := int(secInfo.Type - 1) 425 if id >= len(rawTypes) { 426 return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j) 427 } 428 429 varName, err := rawStrings.Lookup(rawTypes[id].NameOff) 430 if err != nil { 431 return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err) 432 } 433 434 offset, ok := variableOffsets[variable{name, varName}] 435 if !ok { 436 return fmt.Errorf("data section %s: missing offset for variable %s", name, varName) 437 } 438 439 secinfos[j].Offset = offset 440 } 441 } 442 443 return nil 444 } 445 446 // Copy creates a copy of Spec. 447 func (s *Spec) Copy() *Spec { 448 types := copyTypes(s.types, nil) 449 450 typeIDOffset := TypeID(0) 451 if len(s.types) != 0 { 452 typeIDOffset = s.typeIDs[s.types[0]] 453 } 454 typeIDs, typesByName := indexTypes(types, typeIDOffset) 455 456 // NB: Other parts of spec are not copied since they are immutable. 457 return &Spec{ 458 s.rawTypes, 459 s.strings, 460 types, 461 typeIDs, 462 typesByName, 463 s.byteOrder, 464 } 465 } 466 467 type marshalOpts struct { 468 ByteOrder binary.ByteOrder 469 StripFuncLinkage bool 470 } 471 472 func (s *Spec) marshal(opts marshalOpts) ([]byte, error) { 473 var ( 474 buf bytes.Buffer 475 header = new(btfHeader) 476 headerLen = binary.Size(header) 477 ) 478 479 // Reserve space for the header. We have to write it last since 480 // we don't know the size of the type section yet. 481 _, _ = buf.Write(make([]byte, headerLen)) 482 483 // Write type section, just after the header. 484 for _, raw := range s.rawTypes { 485 switch { 486 case opts.StripFuncLinkage && raw.Kind() == kindFunc: 487 raw.SetLinkage(StaticFunc) 488 } 489 490 if err := raw.Marshal(&buf, opts.ByteOrder); err != nil { 491 return nil, fmt.Errorf("can't marshal BTF: %w", err) 492 } 493 } 494 495 typeLen := uint32(buf.Len() - headerLen) 496 497 // Write string section after type section. 498 stringsLen := s.strings.Length() 499 buf.Grow(stringsLen) 500 if err := s.strings.Marshal(&buf); err != nil { 501 return nil, err 502 } 503 504 // Fill out the header, and write it out. 505 header = &btfHeader{ 506 Magic: btfMagic, 507 Version: 1, 508 Flags: 0, 509 HdrLen: uint32(headerLen), 510 TypeOff: 0, 511 TypeLen: typeLen, 512 StringOff: typeLen, 513 StringLen: uint32(stringsLen), 514 } 515 516 raw := buf.Bytes() 517 err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header) 518 if err != nil { 519 return nil, fmt.Errorf("can't write header: %v", err) 520 } 521 522 return raw, nil 523 } 524 525 type sliceWriter []byte 526 527 func (sw sliceWriter) Write(p []byte) (int, error) { 528 if len(p) != len(sw) { 529 return 0, errors.New("size doesn't match") 530 } 531 532 return copy(sw, p), nil 533 } 534 535 // TypeByID returns the BTF Type with the given type ID. 536 // 537 // Returns an error wrapping ErrNotFound if a Type with the given ID 538 // does not exist in the Spec. 539 func (s *Spec) TypeByID(id TypeID) (Type, error) { 540 return s.types.ByID(id) 541 } 542 543 // TypeID returns the ID for a given Type. 544 // 545 // Returns an error wrapping ErrNoFound if the type isn't part of the Spec. 546 func (s *Spec) TypeID(typ Type) (TypeID, error) { 547 if _, ok := typ.(*Void); ok { 548 // Equality is weird for void, since it is a zero sized type. 549 return 0, nil 550 } 551 552 id, ok := s.typeIDs[typ] 553 if !ok { 554 return 0, fmt.Errorf("no ID for type %s: %w", typ, ErrNotFound) 555 } 556 557 return id, nil 558 } 559 560 // AnyTypesByName returns a list of BTF Types with the given name. 561 // 562 // If the BTF blob describes multiple compilation units like vmlinux, multiple 563 // Types with the same name and kind can exist, but might not describe the same 564 // data structure. 565 // 566 // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. 567 func (s *Spec) AnyTypesByName(name string) ([]Type, error) { 568 types := s.namedTypes[newEssentialName(name)] 569 if len(types) == 0 { 570 return nil, fmt.Errorf("type name %s: %w", name, ErrNotFound) 571 } 572 573 // Return a copy to prevent changes to namedTypes. 574 result := make([]Type, 0, len(types)) 575 for _, t := range types { 576 // Match against the full name, not just the essential one 577 // in case the type being looked up is a struct flavor. 578 if t.TypeName() == name { 579 result = append(result, t) 580 } 581 } 582 return result, nil 583 } 584 585 // AnyTypeByName returns a Type with the given name. 586 // 587 // Returns an error if multiple types of that name exist. 588 func (s *Spec) AnyTypeByName(name string) (Type, error) { 589 types, err := s.AnyTypesByName(name) 590 if err != nil { 591 return nil, err 592 } 593 594 if len(types) > 1 { 595 return nil, fmt.Errorf("found multiple types: %v", types) 596 } 597 598 return types[0], nil 599 } 600 601 // TypeByName searches for a Type with a specific name. Since multiple 602 // Types with the same name can exist, the parameter typ is taken to 603 // narrow down the search in case of a clash. 604 // 605 // typ must be a non-nil pointer to an implementation of a Type. 606 // On success, the address of the found Type will be copied to typ. 607 // 608 // Returns an error wrapping ErrNotFound if no matching 609 // Type exists in the Spec. If multiple candidates are found, 610 // an error is returned. 611 func (s *Spec) TypeByName(name string, typ interface{}) error { 612 typValue := reflect.ValueOf(typ) 613 if typValue.Kind() != reflect.Ptr { 614 return fmt.Errorf("%T is not a pointer", typ) 615 } 616 617 typPtr := typValue.Elem() 618 if !typPtr.CanSet() { 619 return fmt.Errorf("%T cannot be set", typ) 620 } 621 622 wanted := typPtr.Type() 623 if !wanted.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) { 624 return fmt.Errorf("%T does not satisfy Type interface", typ) 625 } 626 627 types, err := s.AnyTypesByName(name) 628 if err != nil { 629 return err 630 } 631 632 var candidate Type 633 for _, typ := range types { 634 if reflect.TypeOf(typ) != wanted { 635 continue 636 } 637 638 if candidate != nil { 639 return fmt.Errorf("type %s: multiple candidates for %T", name, typ) 640 } 641 642 candidate = typ 643 } 644 645 if candidate == nil { 646 return fmt.Errorf("type %s: %w", name, ErrNotFound) 647 } 648 649 typPtr.Set(reflect.ValueOf(candidate)) 650 651 return nil 652 } 653 654 // LoadSplitSpecFromReader loads split BTF from a reader. 655 // 656 // Types from base are used to resolve references in the split BTF. 657 // The returned Spec only contains types from the split BTF, not from the base. 658 func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) { 659 return loadRawSpec(r, internal.NativeEndian, base.types, base.strings) 660 } 661 662 // TypesIterator iterates over types of a given spec. 663 type TypesIterator struct { 664 spec *Spec 665 index int 666 // The last visited type in the spec. 667 Type Type 668 } 669 670 // Iterate returns the types iterator. 671 func (s *Spec) Iterate() *TypesIterator { 672 return &TypesIterator{spec: s, index: 0} 673 } 674 675 // Next returns true as long as there are any remaining types. 676 func (iter *TypesIterator) Next() bool { 677 if len(iter.spec.types) <= iter.index { 678 return false 679 } 680 681 iter.Type = iter.spec.types[iter.index] 682 iter.index++ 683 return true 684 } 685 686 // Handle is a reference to BTF loaded into the kernel. 687 type Handle struct { 688 fd *sys.FD 689 690 // Size of the raw BTF in bytes. 691 size uint32 692 } 693 694 // NewHandle loads BTF into the kernel. 695 // 696 // Returns ErrNotSupported if BTF is not supported. 697 func NewHandle(spec *Spec) (*Handle, error) { 698 if err := haveBTF(); err != nil { 699 return nil, err 700 } 701 702 if spec.byteOrder != internal.NativeEndian { 703 return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian) 704 } 705 706 btf, err := spec.marshal(marshalOpts{ 707 ByteOrder: internal.NativeEndian, 708 StripFuncLinkage: haveFuncLinkage() != nil, 709 }) 710 if err != nil { 711 return nil, fmt.Errorf("can't marshal BTF: %w", err) 712 } 713 714 if uint64(len(btf)) > math.MaxUint32 { 715 return nil, errors.New("BTF exceeds the maximum size") 716 } 717 718 attr := &sys.BtfLoadAttr{ 719 Btf: sys.NewSlicePointer(btf), 720 BtfSize: uint32(len(btf)), 721 } 722 723 fd, err := sys.BtfLoad(attr) 724 if err != nil { 725 logBuf := make([]byte, 64*1024) 726 attr.BtfLogBuf = sys.NewSlicePointer(logBuf) 727 attr.BtfLogSize = uint32(len(logBuf)) 728 attr.BtfLogLevel = 1 729 // NB: The syscall will never return ENOSPC as of 5.18-rc4. 730 _, _ = sys.BtfLoad(attr) 731 return nil, internal.ErrorWithLog(err, logBuf) 732 } 733 734 return &Handle{fd, attr.BtfSize}, nil 735 } 736 737 // NewHandleFromID returns the BTF handle for a given id. 738 // 739 // Prefer calling [ebpf.Program.Handle] or [ebpf.Map.Handle] if possible. 740 // 741 // Returns ErrNotExist, if there is no BTF with the given id. 742 // 743 // Requires CAP_SYS_ADMIN. 744 func NewHandleFromID(id ID) (*Handle, error) { 745 fd, err := sys.BtfGetFdById(&sys.BtfGetFdByIdAttr{ 746 Id: uint32(id), 747 }) 748 if err != nil { 749 return nil, fmt.Errorf("get FD for ID %d: %w", id, err) 750 } 751 752 info, err := newHandleInfoFromFD(fd) 753 if err != nil { 754 _ = fd.Close() 755 return nil, err 756 } 757 758 return &Handle{fd, info.size}, nil 759 } 760 761 // Spec parses the kernel BTF into Go types. 762 // 763 // base is used to decode split BTF and may be nil. 764 func (h *Handle) Spec(base *Spec) (*Spec, error) { 765 var btfInfo sys.BtfInfo 766 btfBuffer := make([]byte, h.size) 767 btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer) 768 769 if err := sys.ObjInfo(h.fd, &btfInfo); err != nil { 770 return nil, err 771 } 772 773 var baseTypes types 774 var baseStrings *stringTable 775 if base != nil { 776 baseTypes = base.types 777 baseStrings = base.strings 778 } 779 780 return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, baseTypes, baseStrings) 781 } 782 783 // Close destroys the handle. 784 // 785 // Subsequent calls to FD will return an invalid value. 786 func (h *Handle) Close() error { 787 if h == nil { 788 return nil 789 } 790 791 return h.fd.Close() 792 } 793 794 // FD returns the file descriptor for the handle. 795 func (h *Handle) FD() int { 796 return h.fd.Int() 797 } 798 799 // Info returns metadata about the handle. 800 func (h *Handle) Info() (*HandleInfo, error) { 801 return newHandleInfoFromFD(h.fd) 802 } 803 804 func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte { 805 const minHeaderLength = 24 806 807 typesLen := uint32(binary.Size(types)) 808 header := btfHeader{ 809 Magic: btfMagic, 810 Version: 1, 811 HdrLen: minHeaderLength, 812 TypeOff: 0, 813 TypeLen: typesLen, 814 StringOff: typesLen, 815 StringLen: uint32(len(strings)), 816 } 817 818 buf := new(bytes.Buffer) 819 _ = binary.Write(buf, bo, &header) 820 _ = binary.Write(buf, bo, types) 821 buf.Write(strings) 822 823 return buf.Bytes() 824 } 825 826 var haveBTF = internal.FeatureTest("BTF", "5.1", func() error { 827 var ( 828 types struct { 829 Integer btfType 830 Var btfType 831 btfVar struct{ Linkage uint32 } 832 } 833 strings = []byte{0, 'a', 0} 834 ) 835 836 // We use a BTF_KIND_VAR here, to make sure that 837 // the kernel understands BTF at least as well as we 838 // do. BTF_KIND_VAR was introduced ~5.1. 839 types.Integer.SetKind(kindPointer) 840 types.Var.NameOff = 1 841 types.Var.SetKind(kindVar) 842 types.Var.SizeType = 1 843 844 btf := marshalBTF(&types, strings, internal.NativeEndian) 845 846 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ 847 Btf: sys.NewSlicePointer(btf), 848 BtfSize: uint32(len(btf)), 849 }) 850 if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { 851 // Treat both EINVAL and EPERM as not supported: loading the program 852 // might still succeed without BTF. 853 return internal.ErrNotSupported 854 } 855 if err != nil { 856 return err 857 } 858 859 fd.Close() 860 return nil 861 }) 862 863 var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() error { 864 if err := haveBTF(); err != nil { 865 return err 866 } 867 868 var ( 869 types struct { 870 FuncProto btfType 871 Func btfType 872 } 873 strings = []byte{0, 'a', 0} 874 ) 875 876 types.FuncProto.SetKind(kindFuncProto) 877 types.Func.SetKind(kindFunc) 878 types.Func.SizeType = 1 // aka FuncProto 879 types.Func.NameOff = 1 880 types.Func.SetLinkage(GlobalFunc) 881 882 btf := marshalBTF(&types, strings, internal.NativeEndian) 883 884 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ 885 Btf: sys.NewSlicePointer(btf), 886 BtfSize: uint32(len(btf)), 887 }) 888 if errors.Is(err, unix.EINVAL) { 889 return internal.ErrNotSupported 890 } 891 if err != nil { 892 return err 893 } 894 895 fd.Close() 896 return nil 897 })