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 }