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