...

Source file src/github.com/cilium/ebpf/btf/btf.go

Documentation: github.com/cilium/ebpf/btf

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

View as plain text