github.com/cilium/ebpf@v0.10.0/btf/btf.go (about) 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 "sync" 15 16 "github.com/cilium/ebpf/internal" 17 "github.com/cilium/ebpf/internal/sys" 18 "github.com/cilium/ebpf/internal/unix" 19 ) 20 21 const btfMagic = 0xeB9F 22 23 // Errors returned by BTF functions. 24 var ( 25 ErrNotSupported = internal.ErrNotSupported 26 ErrNotFound = errors.New("not found") 27 ErrNoExtendedInfo = errors.New("no extended info") 28 ErrMultipleMatches = errors.New("multiple matching types") 29 ) 30 31 // ID represents the unique ID of a BTF object. 32 type ID = sys.BTFID 33 34 // Spec represents decoded BTF. 35 type Spec struct { 36 // Data from .BTF. 37 strings *stringTable 38 39 // All types contained by the spec, not including types from the base in 40 // case the spec was parsed from split BTF. 41 types []Type 42 43 // Type IDs indexed by type. 44 typeIDs map[Type]TypeID 45 46 // Types indexed by essential name. 47 // Includes all struct flavors and types with the same name. 48 namedTypes map[essentialName][]Type 49 50 byteOrder binary.ByteOrder 51 } 52 53 var btfHeaderLen = binary.Size(&btfHeader{}) 54 55 type btfHeader struct { 56 Magic uint16 57 Version uint8 58 Flags uint8 59 HdrLen uint32 60 61 TypeOff uint32 62 TypeLen uint32 63 StringOff uint32 64 StringLen uint32 65 } 66 67 // typeStart returns the offset from the beginning of the .BTF section 68 // to the start of its type entries. 69 func (h *btfHeader) typeStart() int64 { 70 return int64(h.HdrLen + h.TypeOff) 71 } 72 73 // stringStart returns the offset from the beginning of the .BTF section 74 // to the start of its string table. 75 func (h *btfHeader) stringStart() int64 { 76 return int64(h.HdrLen + h.StringOff) 77 } 78 79 // LoadSpec opens file and calls LoadSpecFromReader on it. 80 func LoadSpec(file string) (*Spec, error) { 81 fh, err := os.Open(file) 82 if err != nil { 83 return nil, err 84 } 85 defer fh.Close() 86 87 return LoadSpecFromReader(fh) 88 } 89 90 // LoadSpecFromReader reads from an ELF or a raw BTF blob. 91 // 92 // Returns ErrNotFound if reading from an ELF which contains no BTF. ExtInfos 93 // may be nil. 94 func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { 95 file, err := internal.NewSafeELFFile(rd) 96 if err != nil { 97 if bo := guessRawBTFByteOrder(rd); bo != nil { 98 return loadRawSpec(io.NewSectionReader(rd, 0, math.MaxInt64), bo, nil, nil) 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 metadata. 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 // symbolOffsets 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 symbolOffsets(file *internal.SafeELFFile) (map[symbol]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 offsets := make(map[symbol]uint32) 144 for _, sym := range symbols { 145 if idx := sym.Section; idx >= elf.SHN_LORESERVE && idx <= elf.SHN_HIRESERVE { 146 // Ignore things like SHN_ABS 147 continue 148 } 149 150 if sym.Value > math.MaxUint32 { 151 // VarSecinfo offset is u32, cannot reference symbols in higher regions. 152 continue 153 } 154 155 if int(sym.Section) >= len(file.Sections) { 156 return nil, fmt.Errorf("symbol %s: invalid section %d", sym.Name, sym.Section) 157 } 158 159 secName := file.Sections[sym.Section].Name 160 offsets[symbol{secName, sym.Name}] = uint32(sym.Value) 161 } 162 163 return offsets, 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 offsets, err := symbolOffsets(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 spec, err := loadRawSpec(btfSection.ReaderAt, file.ByteOrder, nil, nil) 203 if err != nil { 204 return nil, err 205 } 206 207 err = fixupDatasec(spec.types, sectionSizes, offsets) 208 if err != nil { 209 return nil, err 210 } 211 212 return spec, 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 types, err := inflateRawTypes(rawTypes, baseTypes, rawStrings) 224 if err != nil { 225 return nil, err 226 } 227 228 typeIDs, typesByName := indexTypes(types, TypeID(len(baseTypes))) 229 230 return &Spec{ 231 namedTypes: typesByName, 232 typeIDs: typeIDs, 233 types: types, 234 strings: rawStrings, 235 byteOrder: bo, 236 }, nil 237 } 238 239 func indexTypes(types []Type, typeIDOffset TypeID) (map[Type]TypeID, map[essentialName][]Type) { 240 namedTypes := 0 241 for _, typ := range types { 242 if typ.TypeName() != "" { 243 // Do a pre-pass to figure out how big types by name has to be. 244 // Most types have unique names, so it's OK to ignore essentialName 245 // here. 246 namedTypes++ 247 } 248 } 249 250 typeIDs := make(map[Type]TypeID, len(types)) 251 typesByName := make(map[essentialName][]Type, namedTypes) 252 253 for i, typ := range types { 254 if name := newEssentialName(typ.TypeName()); name != "" { 255 typesByName[name] = append(typesByName[name], typ) 256 } 257 typeIDs[typ] = TypeID(i) + typeIDOffset 258 } 259 260 return typeIDs, typesByName 261 } 262 263 // LoadKernelSpec returns the current kernel's BTF information. 264 // 265 // Defaults to /sys/kernel/btf/vmlinux and falls back to scanning the file system 266 // for vmlinux ELFs. Returns an error wrapping ErrNotSupported if BTF is not enabled. 267 func LoadKernelSpec() (*Spec, error) { 268 spec, _, err := kernelSpec() 269 return spec, err 270 } 271 272 var kernelBTF struct { 273 sync.RWMutex 274 spec *Spec 275 // True if the spec was read from an ELF instead of raw BTF in /sys. 276 fallback bool 277 } 278 279 // FlushKernelSpec removes any cached kernel type information. 280 func FlushKernelSpec() { 281 kernelBTF.Lock() 282 defer kernelBTF.Unlock() 283 284 kernelBTF.spec, kernelBTF.fallback = nil, false 285 } 286 287 func kernelSpec() (*Spec, bool, error) { 288 kernelBTF.RLock() 289 spec, fallback := kernelBTF.spec, kernelBTF.fallback 290 kernelBTF.RUnlock() 291 292 if spec == nil { 293 kernelBTF.Lock() 294 defer kernelBTF.Unlock() 295 296 spec, fallback = kernelBTF.spec, kernelBTF.fallback 297 } 298 299 if spec != nil { 300 return spec.Copy(), fallback, nil 301 } 302 303 spec, fallback, err := loadKernelSpec() 304 if err != nil { 305 return nil, false, err 306 } 307 308 kernelBTF.spec, kernelBTF.fallback = spec, fallback 309 return spec.Copy(), fallback, nil 310 } 311 312 func loadKernelSpec() (_ *Spec, fallback bool, _ error) { 313 fh, err := os.Open("/sys/kernel/btf/vmlinux") 314 if err == nil { 315 defer fh.Close() 316 317 spec, err := loadRawSpec(fh, internal.NativeEndian, nil, nil) 318 return spec, false, err 319 } 320 321 file, err := findVMLinux() 322 if err != nil { 323 return nil, false, err 324 } 325 defer file.Close() 326 327 spec, err := loadSpecFromELF(file) 328 return spec, true, err 329 } 330 331 // findVMLinux scans multiple well-known paths for vmlinux kernel images. 332 func findVMLinux() (*internal.SafeELFFile, error) { 333 release, err := internal.KernelRelease() 334 if err != nil { 335 return nil, err 336 } 337 338 // use same list of locations as libbpf 339 // https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122 340 locations := []string{ 341 "/boot/vmlinux-%s", 342 "/lib/modules/%s/vmlinux-%[1]s", 343 "/lib/modules/%s/build/vmlinux", 344 "/usr/lib/modules/%s/kernel/vmlinux", 345 "/usr/lib/debug/boot/vmlinux-%s", 346 "/usr/lib/debug/boot/vmlinux-%s.debug", 347 "/usr/lib/debug/lib/modules/%s/vmlinux", 348 } 349 350 for _, loc := range locations { 351 file, err := internal.OpenSafeELFFile(fmt.Sprintf(loc, release)) 352 if errors.Is(err, os.ErrNotExist) { 353 continue 354 } 355 return file, err 356 } 357 358 return nil, fmt.Errorf("no BTF found for kernel version %s: %w", release, internal.ErrNotSupported) 359 } 360 361 // parseBTFHeader parses the header of the .BTF section. 362 func parseBTFHeader(r io.Reader, bo binary.ByteOrder) (*btfHeader, error) { 363 var header btfHeader 364 if err := binary.Read(r, bo, &header); err != nil { 365 return nil, fmt.Errorf("can't read header: %v", err) 366 } 367 368 if header.Magic != btfMagic { 369 return nil, fmt.Errorf("incorrect magic value %v", header.Magic) 370 } 371 372 if header.Version != 1 { 373 return nil, fmt.Errorf("unexpected version %v", header.Version) 374 } 375 376 if header.Flags != 0 { 377 return nil, fmt.Errorf("unsupported flags %v", header.Flags) 378 } 379 380 remainder := int64(header.HdrLen) - int64(binary.Size(&header)) 381 if remainder < 0 { 382 return nil, errors.New("header length shorter than btfHeader size") 383 } 384 385 if _, err := io.CopyN(internal.DiscardZeroes{}, r, remainder); err != nil { 386 return nil, fmt.Errorf("header padding: %v", err) 387 } 388 389 return &header, nil 390 } 391 392 func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder { 393 buf := new(bufio.Reader) 394 for _, bo := range []binary.ByteOrder{ 395 binary.LittleEndian, 396 binary.BigEndian, 397 } { 398 buf.Reset(io.NewSectionReader(r, 0, math.MaxInt64)) 399 if _, err := parseBTFHeader(buf, bo); err == nil { 400 return bo 401 } 402 } 403 404 return nil 405 } 406 407 // parseBTF reads a .BTF section into memory and parses it into a list of 408 // raw types and a string table. 409 func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([]rawType, *stringTable, error) { 410 buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64) 411 header, err := parseBTFHeader(buf, bo) 412 if err != nil { 413 return nil, nil, fmt.Errorf("parsing .BTF header: %v", err) 414 } 415 416 rawStrings, err := readStringTable(io.NewSectionReader(btf, header.stringStart(), int64(header.StringLen)), 417 baseStrings) 418 if err != nil { 419 return nil, nil, fmt.Errorf("can't read type names: %w", err) 420 } 421 422 buf.Reset(io.NewSectionReader(btf, header.typeStart(), int64(header.TypeLen))) 423 rawTypes, err := readTypes(buf, bo, header.TypeLen) 424 if err != nil { 425 return nil, nil, fmt.Errorf("can't read types: %w", err) 426 } 427 428 return rawTypes, rawStrings, nil 429 } 430 431 type symbol struct { 432 section string 433 name string 434 } 435 436 func fixupDatasec(types []Type, sectionSizes map[string]uint32, offsets map[symbol]uint32) error { 437 for _, typ := range types { 438 ds, ok := typ.(*Datasec) 439 if !ok { 440 continue 441 } 442 443 name := ds.Name 444 if name == ".kconfig" || name == ".ksyms" { 445 return fmt.Errorf("reference to %s: %w", name, ErrNotSupported) 446 } 447 448 if ds.Size != 0 { 449 continue 450 } 451 452 ds.Size, ok = sectionSizes[name] 453 if !ok { 454 return fmt.Errorf("data section %s: missing size", name) 455 } 456 457 for i := range ds.Vars { 458 symName := ds.Vars[i].Type.TypeName() 459 ds.Vars[i].Offset, ok = offsets[symbol{name, symName}] 460 if !ok { 461 return fmt.Errorf("data section %s: missing offset for symbol %s", name, symName) 462 } 463 } 464 } 465 466 return nil 467 } 468 469 // Copy creates a copy of Spec. 470 func (s *Spec) Copy() *Spec { 471 types := copyTypes(s.types, nil) 472 473 typeIDs, typesByName := indexTypes(types, s.firstTypeID()) 474 475 // NB: Other parts of spec are not copied since they are immutable. 476 return &Spec{ 477 s.strings, 478 types, 479 typeIDs, 480 typesByName, 481 s.byteOrder, 482 } 483 } 484 485 type sliceWriter []byte 486 487 func (sw sliceWriter) Write(p []byte) (int, error) { 488 if len(p) != len(sw) { 489 return 0, errors.New("size doesn't match") 490 } 491 492 return copy(sw, p), nil 493 } 494 495 // TypeByID returns the BTF Type with the given type ID. 496 // 497 // Returns an error wrapping ErrNotFound if a Type with the given ID 498 // does not exist in the Spec. 499 func (s *Spec) TypeByID(id TypeID) (Type, error) { 500 firstID := s.firstTypeID() 501 lastID := firstID + TypeID(len(s.types)) 502 503 if id < firstID || id >= lastID { 504 return nil, fmt.Errorf("expected type ID between %d and %d, got %d: %w", firstID, lastID, id, ErrNotFound) 505 } 506 507 return s.types[id-firstID], nil 508 } 509 510 // TypeID returns the ID for a given Type. 511 // 512 // Returns an error wrapping ErrNoFound if the type isn't part of the Spec. 513 func (s *Spec) TypeID(typ Type) (TypeID, error) { 514 if _, ok := typ.(*Void); ok { 515 // Equality is weird for void, since it is a zero sized type. 516 return 0, nil 517 } 518 519 id, ok := s.typeIDs[typ] 520 if !ok { 521 return 0, fmt.Errorf("no ID for type %s: %w", typ, ErrNotFound) 522 } 523 524 return id, nil 525 } 526 527 // AnyTypesByName returns a list of BTF Types with the given name. 528 // 529 // If the BTF blob describes multiple compilation units like vmlinux, multiple 530 // Types with the same name and kind can exist, but might not describe the same 531 // data structure. 532 // 533 // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. 534 func (s *Spec) AnyTypesByName(name string) ([]Type, error) { 535 types := s.namedTypes[newEssentialName(name)] 536 if len(types) == 0 { 537 return nil, fmt.Errorf("type name %s: %w", name, ErrNotFound) 538 } 539 540 // Return a copy to prevent changes to namedTypes. 541 result := make([]Type, 0, len(types)) 542 for _, t := range types { 543 // Match against the full name, not just the essential one 544 // in case the type being looked up is a struct flavor. 545 if t.TypeName() == name { 546 result = append(result, t) 547 } 548 } 549 return result, nil 550 } 551 552 // AnyTypeByName returns a Type with the given name. 553 // 554 // Returns an error if multiple types of that name exist. 555 func (s *Spec) AnyTypeByName(name string) (Type, error) { 556 types, err := s.AnyTypesByName(name) 557 if err != nil { 558 return nil, err 559 } 560 561 if len(types) > 1 { 562 return nil, fmt.Errorf("found multiple types: %v", types) 563 } 564 565 return types[0], nil 566 } 567 568 // TypeByName searches for a Type with a specific name. Since multiple Types 569 // with the same name can exist, the parameter typ is taken to narrow down the 570 // search in case of a clash. 571 // 572 // typ must be a non-nil pointer to an implementation of a Type. On success, the 573 // address of the found Type will be copied to typ. 574 // 575 // Returns an error wrapping ErrNotFound if no matching Type exists in the Spec. 576 // Returns an error wrapping ErrMultipleTypes if multiple candidates are found. 577 func (s *Spec) TypeByName(name string, typ interface{}) error { 578 typeInterface := reflect.TypeOf((*Type)(nil)).Elem() 579 580 // typ may be **T or *Type 581 typValue := reflect.ValueOf(typ) 582 if typValue.Kind() != reflect.Ptr { 583 return fmt.Errorf("%T is not a pointer", typ) 584 } 585 586 typPtr := typValue.Elem() 587 if !typPtr.CanSet() { 588 return fmt.Errorf("%T cannot be set", typ) 589 } 590 591 wanted := typPtr.Type() 592 if wanted == typeInterface { 593 // This is *Type. Unwrap the value's type. 594 wanted = typPtr.Elem().Type() 595 } 596 597 if !wanted.AssignableTo(typeInterface) { 598 return fmt.Errorf("%T does not satisfy Type interface", typ) 599 } 600 601 types, err := s.AnyTypesByName(name) 602 if err != nil { 603 return err 604 } 605 606 var candidate Type 607 for _, typ := range types { 608 if reflect.TypeOf(typ) != wanted { 609 continue 610 } 611 612 if candidate != nil { 613 return fmt.Errorf("type %s(%T): %w", name, typ, ErrMultipleMatches) 614 } 615 616 candidate = typ 617 } 618 619 if candidate == nil { 620 return fmt.Errorf("%s %s: %w", wanted, name, ErrNotFound) 621 } 622 623 typPtr.Set(reflect.ValueOf(candidate)) 624 625 return nil 626 } 627 628 // firstTypeID returns the first type ID or zero. 629 func (s *Spec) firstTypeID() TypeID { 630 if len(s.types) > 0 { 631 return s.typeIDs[s.types[0]] 632 } 633 return 0 634 } 635 636 // LoadSplitSpecFromReader loads split BTF from a reader. 637 // 638 // Types from base are used to resolve references in the split BTF. 639 // The returned Spec only contains types from the split BTF, not from the base. 640 func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) { 641 return loadRawSpec(r, internal.NativeEndian, base.types, base.strings) 642 } 643 644 // TypesIterator iterates over types of a given spec. 645 type TypesIterator struct { 646 spec *Spec 647 index int 648 // The last visited type in the spec. 649 Type Type 650 } 651 652 // Iterate returns the types iterator. 653 func (s *Spec) Iterate() *TypesIterator { 654 return &TypesIterator{spec: s, index: 0} 655 } 656 657 // Next returns true as long as there are any remaining types. 658 func (iter *TypesIterator) Next() bool { 659 if len(iter.spec.types) <= iter.index { 660 return false 661 } 662 663 iter.Type = iter.spec.types[iter.index] 664 iter.index++ 665 return true 666 } 667 668 func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte { 669 const minHeaderLength = 24 670 671 typesLen := uint32(binary.Size(types)) 672 header := btfHeader{ 673 Magic: btfMagic, 674 Version: 1, 675 HdrLen: minHeaderLength, 676 TypeOff: 0, 677 TypeLen: typesLen, 678 StringOff: typesLen, 679 StringLen: uint32(len(strings)), 680 } 681 682 buf := new(bytes.Buffer) 683 _ = binary.Write(buf, bo, &header) 684 _ = binary.Write(buf, bo, types) 685 buf.Write(strings) 686 687 return buf.Bytes() 688 } 689 690 // haveBTF attempts to load a BTF blob containing an Int. It should pass on any 691 // kernel that supports BPF_BTF_LOAD. 692 var haveBTF = internal.NewFeatureTest("BTF", "4.18", func() error { 693 var ( 694 types struct { 695 Integer btfType 696 btfInt 697 } 698 strings = []byte{0} 699 ) 700 types.Integer.SetKind(kindInt) // 0-length anonymous integer 701 702 btf := marshalBTF(&types, strings, internal.NativeEndian) 703 704 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ 705 Btf: sys.NewSlicePointer(btf), 706 BtfSize: uint32(len(btf)), 707 }) 708 if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { 709 return internal.ErrNotSupported 710 } 711 if err != nil { 712 return err 713 } 714 715 fd.Close() 716 return nil 717 }) 718 719 // haveMapBTF attempts to load a minimal BTF blob containing a Var. It is 720 // used as a proxy for .bss, .data and .rodata map support, which generally 721 // come with a Var and Datasec. These were introduced in Linux 5.2. 722 var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", "5.2", func() error { 723 if err := haveBTF(); err != nil { 724 return err 725 } 726 727 var ( 728 types struct { 729 Integer btfType 730 Var btfType 731 btfVariable 732 } 733 strings = []byte{0, 'a', 0} 734 ) 735 736 types.Integer.SetKind(kindPointer) 737 types.Var.NameOff = 1 738 types.Var.SetKind(kindVar) 739 types.Var.SizeType = 1 740 741 btf := marshalBTF(&types, strings, internal.NativeEndian) 742 743 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ 744 Btf: sys.NewSlicePointer(btf), 745 BtfSize: uint32(len(btf)), 746 }) 747 if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { 748 // Treat both EINVAL and EPERM as not supported: creating the map may still 749 // succeed without Btf* attrs. 750 return internal.ErrNotSupported 751 } 752 if err != nil { 753 return err 754 } 755 756 fd.Close() 757 return nil 758 }) 759 760 // haveProgBTF attempts to load a BTF blob containing a Func and FuncProto. It 761 // is used as a proxy for ext_info (func_info) support, which depends on 762 // Func(Proto) by definition. 763 var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", "5.0", func() error { 764 if err := haveBTF(); err != nil { 765 return err 766 } 767 768 var ( 769 types struct { 770 FuncProto btfType 771 Func btfType 772 } 773 strings = []byte{0, 'a', 0} 774 ) 775 776 types.FuncProto.SetKind(kindFuncProto) 777 types.Func.SetKind(kindFunc) 778 types.Func.SizeType = 1 // aka FuncProto 779 types.Func.NameOff = 1 780 781 btf := marshalBTF(&types, strings, internal.NativeEndian) 782 783 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ 784 Btf: sys.NewSlicePointer(btf), 785 BtfSize: uint32(len(btf)), 786 }) 787 if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) { 788 return internal.ErrNotSupported 789 } 790 if err != nil { 791 return err 792 } 793 794 fd.Close() 795 return nil 796 }) 797 798 var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", "5.6", func() error { 799 if err := haveProgBTF(); err != nil { 800 return err 801 } 802 803 var ( 804 types struct { 805 FuncProto btfType 806 Func btfType 807 } 808 strings = []byte{0, 'a', 0} 809 ) 810 811 types.FuncProto.SetKind(kindFuncProto) 812 types.Func.SetKind(kindFunc) 813 types.Func.SizeType = 1 // aka FuncProto 814 types.Func.NameOff = 1 815 types.Func.SetLinkage(GlobalFunc) 816 817 btf := marshalBTF(&types, strings, internal.NativeEndian) 818 819 fd, err := sys.BtfLoad(&sys.BtfLoadAttr{ 820 Btf: sys.NewSlicePointer(btf), 821 BtfSize: uint32(len(btf)), 822 }) 823 if errors.Is(err, unix.EINVAL) { 824 return internal.ErrNotSupported 825 } 826 if err != nil { 827 return err 828 } 829 830 fd.Close() 831 return nil 832 })