gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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