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 }