github.com/cilium/ebpf@v0.10.0/btf/handle.go (about)

     1  package btf
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"math"
     8  	"os"
     9  
    10  	"github.com/cilium/ebpf/internal"
    11  	"github.com/cilium/ebpf/internal/sys"
    12  	"github.com/cilium/ebpf/internal/unix"
    13  )
    14  
    15  // Handle is a reference to BTF loaded into the kernel.
    16  type Handle struct {
    17  	fd *sys.FD
    18  
    19  	// Size of the raw BTF in bytes.
    20  	size uint32
    21  
    22  	needsKernelBase bool
    23  }
    24  
    25  // NewHandle loads BTF into the kernel.
    26  //
    27  // Returns ErrNotSupported if BTF is not supported.
    28  func NewHandle(spec *Spec) (*Handle, error) {
    29  	if spec.byteOrder != nil && spec.byteOrder != internal.NativeEndian {
    30  		return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian)
    31  	}
    32  
    33  	enc := newEncoder(kernelEncoderOptions, newStringTableBuilderFromTable(spec.strings))
    34  
    35  	for _, typ := range spec.types {
    36  		_, err := enc.Add(typ)
    37  		if err != nil {
    38  			return nil, fmt.Errorf("add %s: %w", typ, err)
    39  		}
    40  	}
    41  
    42  	btf, err := enc.Encode()
    43  	if err != nil {
    44  		return nil, fmt.Errorf("marshal BTF: %w", err)
    45  	}
    46  
    47  	return newHandleFromRawBTF(btf)
    48  }
    49  
    50  func newHandleFromRawBTF(btf []byte) (*Handle, error) {
    51  	if uint64(len(btf)) > math.MaxUint32 {
    52  		return nil, errors.New("BTF exceeds the maximum size")
    53  	}
    54  
    55  	attr := &sys.BtfLoadAttr{
    56  		Btf:     sys.NewSlicePointer(btf),
    57  		BtfSize: uint32(len(btf)),
    58  	}
    59  
    60  	fd, err := sys.BtfLoad(attr)
    61  	if err == nil {
    62  		return &Handle{fd, attr.BtfSize, false}, nil
    63  	}
    64  
    65  	if err := haveBTF(); err != nil {
    66  		return nil, err
    67  	}
    68  
    69  	logBuf := make([]byte, 64*1024)
    70  	attr.BtfLogBuf = sys.NewSlicePointer(logBuf)
    71  	attr.BtfLogSize = uint32(len(logBuf))
    72  	attr.BtfLogLevel = 1
    73  
    74  	// Up until at least kernel 6.0, the BTF verifier does not return ENOSPC
    75  	// if there are other verification errors. ENOSPC is only returned when
    76  	// the BTF blob is correct, a log was requested, and the provided buffer
    77  	// is too small.
    78  	_, ve := sys.BtfLoad(attr)
    79  	return nil, internal.ErrorWithLog("load btf", err, logBuf, errors.Is(ve, unix.ENOSPC))
    80  }
    81  
    82  // NewHandleFromID returns the BTF handle for a given id.
    83  //
    84  // Prefer calling [ebpf.Program.Handle] or [ebpf.Map.Handle] if possible.
    85  //
    86  // Returns ErrNotExist, if there is no BTF with the given id.
    87  //
    88  // Requires CAP_SYS_ADMIN.
    89  func NewHandleFromID(id ID) (*Handle, error) {
    90  	fd, err := sys.BtfGetFdById(&sys.BtfGetFdByIdAttr{
    91  		Id: uint32(id),
    92  	})
    93  	if err != nil {
    94  		return nil, fmt.Errorf("get FD for ID %d: %w", id, err)
    95  	}
    96  
    97  	info, err := newHandleInfoFromFD(fd)
    98  	if err != nil {
    99  		_ = fd.Close()
   100  		return nil, err
   101  	}
   102  
   103  	return &Handle{fd, info.size, info.IsModule()}, nil
   104  }
   105  
   106  // Spec parses the kernel BTF into Go types.
   107  func (h *Handle) Spec() (*Spec, error) {
   108  	var btfInfo sys.BtfInfo
   109  	btfBuffer := make([]byte, h.size)
   110  	btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer)
   111  
   112  	if err := sys.ObjInfo(h.fd, &btfInfo); err != nil {
   113  		return nil, err
   114  	}
   115  
   116  	if !h.needsKernelBase {
   117  		return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil, nil)
   118  	}
   119  
   120  	base, fallback, err := kernelSpec()
   121  	if err != nil {
   122  		return nil, fmt.Errorf("load BTF base: %w", err)
   123  	}
   124  
   125  	if fallback {
   126  		return nil, fmt.Errorf("can't load split BTF without access to /sys")
   127  	}
   128  
   129  	return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, base.types, base.strings)
   130  }
   131  
   132  // Close destroys the handle.
   133  //
   134  // Subsequent calls to FD will return an invalid value.
   135  func (h *Handle) Close() error {
   136  	if h == nil {
   137  		return nil
   138  	}
   139  
   140  	return h.fd.Close()
   141  }
   142  
   143  // FD returns the file descriptor for the handle.
   144  func (h *Handle) FD() int {
   145  	return h.fd.Int()
   146  }
   147  
   148  // Info returns metadata about the handle.
   149  func (h *Handle) Info() (*HandleInfo, error) {
   150  	return newHandleInfoFromFD(h.fd)
   151  }
   152  
   153  // HandleInfo describes a Handle.
   154  type HandleInfo struct {
   155  	// ID of this handle in the kernel. The ID is only valid as long as the
   156  	// associated handle is kept alive.
   157  	ID ID
   158  
   159  	// Name is an identifying name for the BTF, currently only used by the
   160  	// kernel.
   161  	Name string
   162  
   163  	// IsKernel is true if the BTF originated with the kernel and not
   164  	// userspace.
   165  	IsKernel bool
   166  
   167  	// Size of the raw BTF in bytes.
   168  	size uint32
   169  }
   170  
   171  func newHandleInfoFromFD(fd *sys.FD) (*HandleInfo, error) {
   172  	// We invoke the syscall once with a empty BTF and name buffers to get size
   173  	// information to allocate buffers. Then we invoke it a second time with
   174  	// buffers to receive the data.
   175  	var btfInfo sys.BtfInfo
   176  	if err := sys.ObjInfo(fd, &btfInfo); err != nil {
   177  		return nil, fmt.Errorf("get BTF info for fd %s: %w", fd, err)
   178  	}
   179  
   180  	if btfInfo.NameLen > 0 {
   181  		// NameLen doesn't account for the terminating NUL.
   182  		btfInfo.NameLen++
   183  	}
   184  
   185  	// Don't pull raw BTF by default, since it may be quite large.
   186  	btfSize := btfInfo.BtfSize
   187  	btfInfo.BtfSize = 0
   188  
   189  	nameBuffer := make([]byte, btfInfo.NameLen)
   190  	btfInfo.Name, btfInfo.NameLen = sys.NewSlicePointerLen(nameBuffer)
   191  	if err := sys.ObjInfo(fd, &btfInfo); err != nil {
   192  		return nil, err
   193  	}
   194  
   195  	return &HandleInfo{
   196  		ID:       ID(btfInfo.Id),
   197  		Name:     unix.ByteSliceToString(nameBuffer),
   198  		IsKernel: btfInfo.KernelBtf != 0,
   199  		size:     btfSize,
   200  	}, nil
   201  }
   202  
   203  // IsModule returns true if the BTF is for the kernel itself.
   204  func (i *HandleInfo) IsVmlinux() bool {
   205  	return i.IsKernel && i.Name == "vmlinux"
   206  }
   207  
   208  // IsModule returns true if the BTF is for a kernel module.
   209  func (i *HandleInfo) IsModule() bool {
   210  	return i.IsKernel && i.Name != "vmlinux"
   211  }
   212  
   213  // HandleIterator allows enumerating BTF blobs loaded into the kernel.
   214  type HandleIterator struct {
   215  	// The ID of the current handle. Only valid after a call to Next.
   216  	ID ID
   217  	// The current Handle. Only valid until a call to Next.
   218  	// See Take if you want to retain the handle.
   219  	Handle *Handle
   220  	err    error
   221  }
   222  
   223  // Next retrieves a handle for the next BTF object.
   224  //
   225  // Returns true if another BTF object was found. Call [HandleIterator.Err] after
   226  // the function returns false.
   227  func (it *HandleIterator) Next() bool {
   228  	id := it.ID
   229  	for {
   230  		attr := &sys.BtfGetNextIdAttr{Id: id}
   231  		err := sys.BtfGetNextId(attr)
   232  		if errors.Is(err, os.ErrNotExist) {
   233  			// There are no more BTF objects.
   234  			break
   235  		} else if err != nil {
   236  			it.err = fmt.Errorf("get next BTF ID: %w", err)
   237  			break
   238  		}
   239  
   240  		id = attr.NextId
   241  		handle, err := NewHandleFromID(id)
   242  		if errors.Is(err, os.ErrNotExist) {
   243  			// Try again with the next ID.
   244  			continue
   245  		} else if err != nil {
   246  			it.err = fmt.Errorf("retrieve handle for ID %d: %w", id, err)
   247  			break
   248  		}
   249  
   250  		it.Handle.Close()
   251  		it.ID, it.Handle = id, handle
   252  		return true
   253  	}
   254  
   255  	// No more handles or we encountered an error.
   256  	it.Handle.Close()
   257  	it.Handle = nil
   258  	return false
   259  }
   260  
   261  // Take the ownership of the current handle.
   262  //
   263  // It's the callers responsibility to close the handle.
   264  func (it *HandleIterator) Take() *Handle {
   265  	handle := it.Handle
   266  	it.Handle = nil
   267  	return handle
   268  }
   269  
   270  // Err returns an error if iteration failed for some reason.
   271  func (it *HandleIterator) Err() error {
   272  	return it.err
   273  }
   274  
   275  // FindHandle returns the first handle for which predicate returns true.
   276  //
   277  // Requires CAP_SYS_ADMIN.
   278  //
   279  // Returns an error wrapping ErrNotFound if predicate never returns true or if
   280  // there is no BTF loaded into the kernel.
   281  func FindHandle(predicate func(info *HandleInfo) bool) (*Handle, error) {
   282  	it := new(HandleIterator)
   283  	defer it.Handle.Close()
   284  
   285  	for it.Next() {
   286  		info, err := it.Handle.Info()
   287  		if err != nil {
   288  			return nil, fmt.Errorf("info for ID %d: %w", it.ID, err)
   289  		}
   290  
   291  		if predicate(info) {
   292  			return it.Take(), nil
   293  		}
   294  	}
   295  	if err := it.Err(); err != nil {
   296  		return nil, fmt.Errorf("iterate handles: %w", err)
   297  	}
   298  
   299  	return nil, fmt.Errorf("find handle: %w", ErrNotFound)
   300  }