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