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  })