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 }