mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2024-12-21 10:13:11 +00:00
122 lines
3 KiB
Go
122 lines
3 KiB
Go
|
package btf
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
|
||
|
"github.com/cilium/ebpf/internal/sys"
|
||
|
"github.com/cilium/ebpf/internal/unix"
|
||
|
)
|
||
|
|
||
|
// HandleInfo describes a Handle.
|
||
|
type HandleInfo struct {
|
||
|
// ID of this handle in the kernel. The ID is only valid as long as the
|
||
|
// associated handle is kept alive.
|
||
|
ID ID
|
||
|
|
||
|
// Name is an identifying name for the BTF, currently only used by the
|
||
|
// kernel.
|
||
|
Name string
|
||
|
|
||
|
// IsKernel is true if the BTF originated with the kernel and not
|
||
|
// userspace.
|
||
|
IsKernel bool
|
||
|
|
||
|
// Size of the raw BTF in bytes.
|
||
|
size uint32
|
||
|
}
|
||
|
|
||
|
func newHandleInfoFromFD(fd *sys.FD) (*HandleInfo, error) {
|
||
|
// We invoke the syscall once with a empty BTF and name buffers to get size
|
||
|
// information to allocate buffers. Then we invoke it a second time with
|
||
|
// buffers to receive the data.
|
||
|
var btfInfo sys.BtfInfo
|
||
|
if err := sys.ObjInfo(fd, &btfInfo); err != nil {
|
||
|
return nil, fmt.Errorf("get BTF info for fd %s: %w", fd, err)
|
||
|
}
|
||
|
|
||
|
if btfInfo.NameLen > 0 {
|
||
|
// NameLen doesn't account for the terminating NUL.
|
||
|
btfInfo.NameLen++
|
||
|
}
|
||
|
|
||
|
// Don't pull raw BTF by default, since it may be quite large.
|
||
|
btfSize := btfInfo.BtfSize
|
||
|
btfInfo.BtfSize = 0
|
||
|
|
||
|
nameBuffer := make([]byte, btfInfo.NameLen)
|
||
|
btfInfo.Name, btfInfo.NameLen = sys.NewSlicePointerLen(nameBuffer)
|
||
|
if err := sys.ObjInfo(fd, &btfInfo); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &HandleInfo{
|
||
|
ID: ID(btfInfo.Id),
|
||
|
Name: unix.ByteSliceToString(nameBuffer),
|
||
|
IsKernel: btfInfo.KernelBtf != 0,
|
||
|
size: btfSize,
|
||
|
}, nil
|
||
|
}
|
||
|
|
||
|
// IsModule returns true if the BTF is for the kernel itself.
|
||
|
func (i *HandleInfo) IsVmlinux() bool {
|
||
|
return i.IsKernel && i.Name == "vmlinux"
|
||
|
}
|
||
|
|
||
|
// IsModule returns true if the BTF is for a kernel module.
|
||
|
func (i *HandleInfo) IsModule() bool {
|
||
|
return i.IsKernel && i.Name != "vmlinux"
|
||
|
}
|
||
|
|
||
|
// HandleIterator allows enumerating BTF blobs loaded into the kernel.
|
||
|
type HandleIterator struct {
|
||
|
// The ID of the last retrieved handle. Only valid after a call to Next.
|
||
|
ID ID
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
// Next retrieves a handle for the next BTF blob.
|
||
|
//
|
||
|
// [Handle.Close] is called if *handle is non-nil to avoid leaking fds.
|
||
|
//
|
||
|
// Returns true if another BTF blob was found. Call [HandleIterator.Err] after
|
||
|
// the function returns false.
|
||
|
func (it *HandleIterator) Next(handle **Handle) bool {
|
||
|
if *handle != nil {
|
||
|
(*handle).Close()
|
||
|
*handle = nil
|
||
|
}
|
||
|
|
||
|
id := it.ID
|
||
|
for {
|
||
|
attr := &sys.BtfGetNextIdAttr{Id: id}
|
||
|
err := sys.BtfGetNextId(attr)
|
||
|
if errors.Is(err, os.ErrNotExist) {
|
||
|
// There are no more BTF objects.
|
||
|
return false
|
||
|
} else if err != nil {
|
||
|
it.err = fmt.Errorf("get next BTF ID: %w", err)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
id = attr.NextId
|
||
|
*handle, err = NewHandleFromID(id)
|
||
|
if errors.Is(err, os.ErrNotExist) {
|
||
|
// Try again with the next ID.
|
||
|
continue
|
||
|
} else if err != nil {
|
||
|
it.err = fmt.Errorf("retrieve handle for ID %d: %w", id, err)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
it.ID = id
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Err returns an error if iteration failed for some reason.
|
||
|
func (it *HandleIterator) Err() error {
|
||
|
return it.err
|
||
|
}
|