vendor: cilium/ebbf 4032b1d8aae306b7bb94a2a11002932caf88c644
full diff: 60c3aa43f4...4032b1d8aa
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
530
vendor/github.com/cilium/ebpf/internal/btf/btf.go
generated
vendored
Normal file
530
vendor/github.com/cilium/ebpf/internal/btf/btf.go
generated
vendored
Normal file
@@ -0,0 +1,530 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cilium/ebpf/internal"
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const btfMagic = 0xeB9F
|
||||
|
||||
// Spec represents decoded BTF.
|
||||
type Spec struct {
|
||||
rawTypes []rawType
|
||||
strings stringTable
|
||||
types map[string][]Type
|
||||
funcInfos map[string]extInfo
|
||||
lineInfos map[string]extInfo
|
||||
}
|
||||
|
||||
type btfHeader struct {
|
||||
Magic uint16
|
||||
Version uint8
|
||||
Flags uint8
|
||||
HdrLen uint32
|
||||
|
||||
TypeOff uint32
|
||||
TypeLen uint32
|
||||
StringOff uint32
|
||||
StringLen uint32
|
||||
}
|
||||
|
||||
// LoadSpecFromReader reads BTF sections from an ELF.
|
||||
//
|
||||
// Returns a nil Spec and no error if no BTF was present.
|
||||
func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
|
||||
file, err := elf.NewFile(rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var (
|
||||
btfSection *elf.Section
|
||||
btfExtSection *elf.Section
|
||||
)
|
||||
|
||||
for _, sec := range file.Sections {
|
||||
switch sec.Name {
|
||||
case ".BTF":
|
||||
btfSection = sec
|
||||
case ".BTF.ext":
|
||||
btfExtSection = sec
|
||||
}
|
||||
}
|
||||
|
||||
if btfSection == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
spec, err := parseBTF(btfSection.Open(), file.ByteOrder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if btfExtSection != nil {
|
||||
spec.funcInfos, spec.lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't read ext info")
|
||||
}
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) (*Spec, error) {
|
||||
rawBTF, err := ioutil.ReadAll(btf)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't read BTF")
|
||||
}
|
||||
|
||||
rd := bytes.NewReader(rawBTF)
|
||||
|
||||
var header btfHeader
|
||||
if err := binary.Read(rd, bo, &header); err != nil {
|
||||
return nil, errors.Wrap(err, "can't read header")
|
||||
}
|
||||
|
||||
if header.Magic != btfMagic {
|
||||
return nil, errors.Errorf("incorrect magic value %v", header.Magic)
|
||||
}
|
||||
|
||||
if header.Version != 1 {
|
||||
return nil, errors.Errorf("unexpected version %v", header.Version)
|
||||
}
|
||||
|
||||
if header.Flags != 0 {
|
||||
return nil, errors.Errorf("unsupported flags %v", header.Flags)
|
||||
}
|
||||
|
||||
remainder := int64(header.HdrLen) - int64(binary.Size(&header))
|
||||
if remainder < 0 {
|
||||
return nil, errors.New("header is too short")
|
||||
}
|
||||
|
||||
if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil {
|
||||
return nil, errors.Wrap(err, "header padding")
|
||||
}
|
||||
|
||||
if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil {
|
||||
return nil, errors.Wrap(err, "can't seek to start of string section")
|
||||
}
|
||||
|
||||
strings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen)))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't read type names")
|
||||
}
|
||||
|
||||
if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil {
|
||||
return nil, errors.Wrap(err, "can't seek to start of type section")
|
||||
}
|
||||
|
||||
rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't read types")
|
||||
}
|
||||
|
||||
types, err := inflateRawTypes(rawTypes, strings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Spec{
|
||||
rawTypes: rawTypes,
|
||||
types: types,
|
||||
strings: strings,
|
||||
funcInfos: make(map[string]extInfo),
|
||||
lineInfos: make(map[string]extInfo),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) {
|
||||
var (
|
||||
buf bytes.Buffer
|
||||
header = new(btfHeader)
|
||||
headerLen = binary.Size(header)
|
||||
)
|
||||
|
||||
// Reserve space for the header. We have to write it last since
|
||||
// we don't know the size of the type section yet.
|
||||
_, _ = buf.Write(make([]byte, headerLen))
|
||||
|
||||
// Write type section, just after the header.
|
||||
for _, typ := range s.rawTypes {
|
||||
if typ.Kind() == kindDatasec {
|
||||
// Datasec requires patching with information from the ELF
|
||||
// file. We don't support this at the moment, so patch
|
||||
// out any Datasec by turning it into a void*.
|
||||
typ = rawType{}
|
||||
typ.SetKind(kindPointer)
|
||||
}
|
||||
|
||||
if err := typ.Marshal(&buf, bo); err != nil {
|
||||
return nil, errors.Wrap(err, "can't marshal BTF")
|
||||
}
|
||||
}
|
||||
|
||||
typeLen := uint32(buf.Len() - headerLen)
|
||||
|
||||
// Write string section after type section.
|
||||
_, _ = buf.Write(s.strings)
|
||||
|
||||
// Fill out the header, and write it out.
|
||||
header = &btfHeader{
|
||||
Magic: btfMagic,
|
||||
Version: 1,
|
||||
Flags: 0,
|
||||
HdrLen: uint32(headerLen),
|
||||
TypeOff: 0,
|
||||
TypeLen: typeLen,
|
||||
StringOff: typeLen,
|
||||
StringLen: uint32(len(s.strings)),
|
||||
}
|
||||
|
||||
raw := buf.Bytes()
|
||||
err := binary.Write(sliceWriter(raw[:headerLen]), bo, header)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't write header")
|
||||
}
|
||||
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
type sliceWriter []byte
|
||||
|
||||
func (sw sliceWriter) Write(p []byte) (int, error) {
|
||||
if len(p) != len(sw) {
|
||||
return 0, errors.New("size doesn't match")
|
||||
}
|
||||
|
||||
return copy(sw, p), nil
|
||||
}
|
||||
|
||||
// Program finds the BTF for a specific section.
|
||||
//
|
||||
// Length is the number of bytes in the raw BPF instruction stream.
|
||||
//
|
||||
// Returns an error if there is no BTF.
|
||||
func (s *Spec) Program(name string, length uint64) (*Program, error) {
|
||||
if length == 0 {
|
||||
return nil, errors.New("length musn't be zero")
|
||||
}
|
||||
|
||||
funcInfos, funcOK := s.funcInfos[name]
|
||||
lineInfos, lineOK := s.lineInfos[name]
|
||||
|
||||
if !funcOK && !lineOK {
|
||||
return nil, errors.Errorf("no BTF for program %s", name)
|
||||
}
|
||||
|
||||
return &Program{s, length, funcInfos, lineInfos}, nil
|
||||
}
|
||||
|
||||
// Map finds the BTF for a map.
|
||||
//
|
||||
// Returns an error if there is no BTF for the given name.
|
||||
func (s *Spec) Map(name string) (*Map, error) {
|
||||
var mapVar Var
|
||||
if err := s.FindType(name, &mapVar); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mapStruct, ok := mapVar.Type.(*Struct)
|
||||
if !ok {
|
||||
return nil, errors.Errorf("expected struct, have %s", mapVar.Type)
|
||||
}
|
||||
|
||||
var key, value Type
|
||||
for _, member := range mapStruct.Members {
|
||||
switch member.Name {
|
||||
case "key":
|
||||
key = member.Type
|
||||
|
||||
case "value":
|
||||
value = member.Type
|
||||
}
|
||||
}
|
||||
|
||||
if key == nil {
|
||||
return nil, errors.Errorf("map %s: missing 'key' in type", name)
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
return nil, errors.Errorf("map %s: missing 'value' in type", name)
|
||||
}
|
||||
|
||||
return &Map{mapStruct, s, key, value}, nil
|
||||
}
|
||||
|
||||
var errNotFound = errors.New("not found")
|
||||
|
||||
// FindType searches for a type with a specific name.
|
||||
//
|
||||
// hint determines the type of the returned Type.
|
||||
//
|
||||
// Returns an error if there is no or multiple matches.
|
||||
func (s *Spec) FindType(name string, typ Type) error {
|
||||
var (
|
||||
wanted = reflect.TypeOf(typ)
|
||||
candidate Type
|
||||
)
|
||||
|
||||
for _, typ := range s.types[name] {
|
||||
if reflect.TypeOf(typ) != wanted {
|
||||
continue
|
||||
}
|
||||
|
||||
if candidate != nil {
|
||||
return errors.Errorf("type %s: multiple candidates for %T", name, typ)
|
||||
}
|
||||
|
||||
candidate = typ
|
||||
}
|
||||
|
||||
if candidate == nil {
|
||||
return errors.WithMessagef(errNotFound, "type %s", name)
|
||||
}
|
||||
|
||||
value := reflect.Indirect(reflect.ValueOf(copyType(candidate)))
|
||||
reflect.Indirect(reflect.ValueOf(typ)).Set(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle is a reference to BTF loaded into the kernel.
|
||||
type Handle struct {
|
||||
fd *internal.FD
|
||||
}
|
||||
|
||||
// NewHandle loads BTF into the kernel.
|
||||
//
|
||||
// Returns an error if BTF is not supported, which can
|
||||
// be checked by IsNotSupported.
|
||||
func NewHandle(spec *Spec) (*Handle, error) {
|
||||
if err := haveBTF(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
btf, err := spec.marshal(internal.NativeEndian)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't marshal BTF")
|
||||
}
|
||||
|
||||
if uint64(len(btf)) > math.MaxUint32 {
|
||||
return nil, errors.New("BTF exceeds the maximum size")
|
||||
}
|
||||
|
||||
attr := &bpfLoadBTFAttr{
|
||||
btf: internal.NewSlicePointer(btf),
|
||||
btfSize: uint32(len(btf)),
|
||||
}
|
||||
|
||||
fd, err := bpfLoadBTF(attr)
|
||||
if err != nil {
|
||||
logBuf := make([]byte, 64*1024)
|
||||
attr.logBuf = internal.NewSlicePointer(logBuf)
|
||||
attr.btfLogSize = uint32(len(logBuf))
|
||||
attr.btfLogLevel = 1
|
||||
_, logErr := bpfLoadBTF(attr)
|
||||
return nil, internal.ErrorWithLog(err, logBuf, logErr)
|
||||
}
|
||||
|
||||
return &Handle{fd}, nil
|
||||
}
|
||||
|
||||
// Close destroys the handle.
|
||||
//
|
||||
// Subsequent calls to FD will return an invalid value.
|
||||
func (h *Handle) Close() error {
|
||||
return h.fd.Close()
|
||||
}
|
||||
|
||||
// FD returns the file descriptor for the handle.
|
||||
func (h *Handle) FD() int {
|
||||
value, err := h.fd.Value()
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
return int(value)
|
||||
}
|
||||
|
||||
// Map is the BTF for a map.
|
||||
type Map struct {
|
||||
definition *Struct
|
||||
spec *Spec
|
||||
key, value Type
|
||||
}
|
||||
|
||||
// MapSpec should be a method on Map, but is a free function
|
||||
// to hide it from users of the ebpf package.
|
||||
func MapSpec(m *Map) *Spec {
|
||||
return m.spec
|
||||
}
|
||||
|
||||
// MapType should be a method on Map, but is a free function
|
||||
// to hide it from users of the ebpf package.
|
||||
func MapType(m *Map) *Struct {
|
||||
return m.definition
|
||||
}
|
||||
|
||||
// MapKey should be a method on Map, but is a free function
|
||||
// to hide it from users of the ebpf package.
|
||||
func MapKey(m *Map) Type {
|
||||
return m.key
|
||||
}
|
||||
|
||||
// MapValue should be a method on Map, but is a free function
|
||||
// to hide it from users of the ebpf package.
|
||||
func MapValue(m *Map) Type {
|
||||
return m.value
|
||||
}
|
||||
|
||||
// Program is the BTF information for a stream of instructions.
|
||||
type Program struct {
|
||||
spec *Spec
|
||||
length uint64
|
||||
funcInfos, lineInfos extInfo
|
||||
}
|
||||
|
||||
// ProgramSpec returns the Spec needed for loading function and line infos into the kernel.
|
||||
//
|
||||
// This is a free function instead of a method to hide it from users
|
||||
// of package ebpf.
|
||||
func ProgramSpec(s *Program) *Spec {
|
||||
return s.spec
|
||||
}
|
||||
|
||||
// ProgramAppend the information from other to the Program.
|
||||
//
|
||||
// This is a free function instead of a method to hide it from users
|
||||
// of package ebpf.
|
||||
func ProgramAppend(s, other *Program) error {
|
||||
funcInfos, err := s.funcInfos.append(other.funcInfos, s.length)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "func infos")
|
||||
}
|
||||
|
||||
lineInfos, err := s.lineInfos.append(other.lineInfos, s.length)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "line infos")
|
||||
}
|
||||
|
||||
s.length += other.length
|
||||
s.funcInfos = funcInfos
|
||||
s.lineInfos = lineInfos
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProgramFuncInfos returns the binary form of BTF function infos.
|
||||
//
|
||||
// This is a free function instead of a method to hide it from users
|
||||
// of package ebpf.
|
||||
func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
|
||||
bytes, err = s.funcInfos.MarshalBinary()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return s.funcInfos.recordSize, bytes, nil
|
||||
}
|
||||
|
||||
// ProgramLineInfos returns the binary form of BTF line infos.
|
||||
//
|
||||
// This is a free function instead of a method to hide it from users
|
||||
// of package ebpf.
|
||||
func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
|
||||
bytes, err = s.lineInfos.MarshalBinary()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
return s.lineInfos.recordSize, bytes, nil
|
||||
}
|
||||
|
||||
// IsNotSupported returns true if the error indicates that the kernel
|
||||
// doesn't support BTF.
|
||||
func IsNotSupported(err error) bool {
|
||||
ufe, ok := errors.Cause(err).(*internal.UnsupportedFeatureError)
|
||||
return ok && ufe.Name == "BTF"
|
||||
}
|
||||
|
||||
type bpfLoadBTFAttr struct {
|
||||
btf internal.Pointer
|
||||
logBuf internal.Pointer
|
||||
btfSize uint32
|
||||
btfLogSize uint32
|
||||
btfLogLevel uint32
|
||||
}
|
||||
|
||||
func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) {
|
||||
const _BTFLoad = 18
|
||||
|
||||
fd, err := internal.BPF(_BTFLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return internal.NewFD(uint32(fd)), nil
|
||||
}
|
||||
|
||||
func minimalBTF(bo binary.ByteOrder) []byte {
|
||||
const minHeaderLength = 24
|
||||
|
||||
var (
|
||||
types struct {
|
||||
Integer btfType
|
||||
Var btfType
|
||||
btfVar struct{ Linkage uint32 }
|
||||
}
|
||||
typLen = uint32(binary.Size(&types))
|
||||
strings = []byte{0, 'a', 0}
|
||||
header = btfHeader{
|
||||
Magic: btfMagic,
|
||||
Version: 1,
|
||||
HdrLen: minHeaderLength,
|
||||
TypeOff: 0,
|
||||
TypeLen: typLen,
|
||||
StringOff: typLen,
|
||||
StringLen: uint32(len(strings)),
|
||||
}
|
||||
)
|
||||
|
||||
// We use a BTF_KIND_VAR here, to make sure that
|
||||
// the kernel understands BTF at least as well as we
|
||||
// do. BTF_KIND_VAR was introduced ~5.1.
|
||||
types.Integer.SetKind(kindPointer)
|
||||
types.Var.NameOff = 1
|
||||
types.Var.SetKind(kindVar)
|
||||
types.Var.SizeType = 1
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
_ = binary.Write(buf, bo, &header)
|
||||
_ = binary.Write(buf, bo, &types)
|
||||
buf.Write(strings)
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool {
|
||||
btf := minimalBTF(internal.NativeEndian)
|
||||
fd, err := bpfLoadBTF(&bpfLoadBTFAttr{
|
||||
btf: internal.NewSlicePointer(btf),
|
||||
btfSize: uint32(len(btf)),
|
||||
})
|
||||
if err == nil {
|
||||
fd.Close()
|
||||
}
|
||||
// Check for EINVAL specifically, rather than err != nil since we
|
||||
// otherwise misdetect due to insufficient permissions.
|
||||
return errors.Cause(err) != unix.EINVAL
|
||||
})
|
||||
190
vendor/github.com/cilium/ebpf/internal/btf/btf_types.go
generated
vendored
Normal file
190
vendor/github.com/cilium/ebpf/internal/btf/btf_types.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// btfKind describes a Type.
|
||||
type btfKind uint8
|
||||
|
||||
// Equivalents of the BTF_KIND_* constants.
|
||||
const (
|
||||
kindUnknown btfKind = iota
|
||||
kindInt
|
||||
kindPointer
|
||||
kindArray
|
||||
kindStruct
|
||||
kindUnion
|
||||
kindEnum
|
||||
kindForward
|
||||
kindTypedef
|
||||
kindVolatile
|
||||
kindConst
|
||||
kindRestrict
|
||||
// Added ~4.20
|
||||
kindFunc
|
||||
kindFuncProto
|
||||
// Added ~5.1
|
||||
kindVar
|
||||
kindDatasec
|
||||
)
|
||||
|
||||
const (
|
||||
btfTypeKindShift = 24
|
||||
btfTypeKindLen = 4
|
||||
btfTypeVlenShift = 0
|
||||
btfTypeVlenMask = 16
|
||||
)
|
||||
|
||||
// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst.
|
||||
type btfType struct {
|
||||
NameOff uint32
|
||||
/* "info" bits arrangement
|
||||
* bits 0-15: vlen (e.g. # of struct's members)
|
||||
* bits 16-23: unused
|
||||
* bits 24-27: kind (e.g. int, ptr, array...etc)
|
||||
* bits 28-30: unused
|
||||
* bit 31: kind_flag, currently used by
|
||||
* struct, union and fwd
|
||||
*/
|
||||
Info uint32
|
||||
/* "size" is used by INT, ENUM, STRUCT and UNION.
|
||||
* "size" tells the size of the type it is describing.
|
||||
*
|
||||
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
|
||||
* FUNC and FUNC_PROTO.
|
||||
* "type" is a type_id referring to another type.
|
||||
*/
|
||||
SizeType uint32
|
||||
}
|
||||
|
||||
func mask(len uint32) uint32 {
|
||||
return (1 << len) - 1
|
||||
}
|
||||
|
||||
func (bt *btfType) info(len, shift uint32) uint32 {
|
||||
return (bt.Info >> shift) & mask(len)
|
||||
}
|
||||
|
||||
func (bt *btfType) setInfo(value, len, shift uint32) {
|
||||
bt.Info &^= mask(len) << shift
|
||||
bt.Info |= (value & mask(len)) << shift
|
||||
}
|
||||
|
||||
func (bt *btfType) Kind() btfKind {
|
||||
return btfKind(bt.info(btfTypeKindLen, btfTypeKindShift))
|
||||
}
|
||||
|
||||
func (bt *btfType) SetKind(kind btfKind) {
|
||||
bt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift)
|
||||
}
|
||||
|
||||
func (bt *btfType) Vlen() int {
|
||||
return int(bt.info(btfTypeVlenMask, btfTypeVlenShift))
|
||||
}
|
||||
|
||||
func (bt *btfType) SetVlen(vlen int) {
|
||||
bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift)
|
||||
}
|
||||
|
||||
func (bt *btfType) Type() TypeID {
|
||||
// TODO: Panic here if wrong kind?
|
||||
return TypeID(bt.SizeType)
|
||||
}
|
||||
|
||||
func (bt *btfType) Size() uint32 {
|
||||
// TODO: Panic here if wrong kind?
|
||||
return bt.SizeType
|
||||
}
|
||||
|
||||
type rawType struct {
|
||||
btfType
|
||||
data interface{}
|
||||
}
|
||||
|
||||
func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error {
|
||||
if err := binary.Write(w, bo, &rt.btfType); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if rt.data == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return binary.Write(w, bo, rt.data)
|
||||
}
|
||||
|
||||
type btfArray struct {
|
||||
Type TypeID
|
||||
IndexType TypeID
|
||||
Nelems uint32
|
||||
}
|
||||
|
||||
type btfMember struct {
|
||||
NameOff uint32
|
||||
Type TypeID
|
||||
Offset uint32
|
||||
}
|
||||
|
||||
func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
|
||||
var (
|
||||
header btfType
|
||||
types []rawType
|
||||
)
|
||||
|
||||
for id := TypeID(1); ; id++ {
|
||||
if err := binary.Read(r, bo, &header); err == io.EOF {
|
||||
return types, nil
|
||||
} else if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't read type info for id %v", id)
|
||||
}
|
||||
|
||||
var data interface{}
|
||||
switch header.Kind() {
|
||||
case kindInt:
|
||||
// sizeof(uint32)
|
||||
data = make([]byte, 4)
|
||||
case kindPointer:
|
||||
case kindArray:
|
||||
data = new(btfArray)
|
||||
case kindStruct:
|
||||
fallthrough
|
||||
case kindUnion:
|
||||
data = make([]btfMember, header.Vlen())
|
||||
case kindEnum:
|
||||
// sizeof(struct btf_enum)
|
||||
data = make([]byte, header.Vlen()*4*2)
|
||||
case kindForward:
|
||||
case kindTypedef:
|
||||
case kindVolatile:
|
||||
case kindConst:
|
||||
case kindRestrict:
|
||||
case kindFunc:
|
||||
case kindFuncProto:
|
||||
// sizeof(struct btf_param)
|
||||
data = make([]byte, header.Vlen()*4*2)
|
||||
case kindVar:
|
||||
// sizeof(struct btf_variable)
|
||||
data = make([]byte, 4)
|
||||
case kindDatasec:
|
||||
// sizeof(struct btf_var_secinfo)
|
||||
data = make([]byte, header.Vlen()*4*3)
|
||||
default:
|
||||
return nil, errors.Errorf("type id %v: unknown kind: %v", id, header.Kind())
|
||||
}
|
||||
|
||||
if data == nil {
|
||||
types = append(types, rawType{header, nil})
|
||||
continue
|
||||
}
|
||||
|
||||
if err := binary.Read(r, bo, data); err != nil {
|
||||
return nil, errors.Wrapf(err, "type id %d: kind %v: can't read %T", id, header.Kind(), data)
|
||||
}
|
||||
|
||||
types = append(types, rawType{header, data})
|
||||
}
|
||||
}
|
||||
8
vendor/github.com/cilium/ebpf/internal/btf/doc.go
generated
vendored
Normal file
8
vendor/github.com/cilium/ebpf/internal/btf/doc.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Package btf handles data encoded according to the BPF Type Format.
|
||||
//
|
||||
// The canonical documentation lives in the Linux kernel repository and is
|
||||
// available at https://www.kernel.org/doc/html/latest/bpf/btf.html
|
||||
//
|
||||
// The API is very much unstable. You should only use this via the main
|
||||
// ebpf library.
|
||||
package btf
|
||||
184
vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
generated
vendored
Normal file
184
vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type btfExtHeader struct {
|
||||
Magic uint16
|
||||
Version uint8
|
||||
Flags uint8
|
||||
HdrLen uint32
|
||||
|
||||
FuncInfoOff uint32
|
||||
FuncInfoLen uint32
|
||||
LineInfoOff uint32
|
||||
LineInfoLen uint32
|
||||
}
|
||||
|
||||
func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) {
|
||||
const expectedMagic = 0xeB9F
|
||||
|
||||
var header btfExtHeader
|
||||
if err := binary.Read(r, bo, &header); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "can't read header")
|
||||
}
|
||||
|
||||
if header.Magic != expectedMagic {
|
||||
return nil, nil, errors.Errorf("incorrect magic value %v", header.Magic)
|
||||
}
|
||||
|
||||
if header.Version != 1 {
|
||||
return nil, nil, errors.Errorf("unexpected version %v", header.Version)
|
||||
}
|
||||
|
||||
if header.Flags != 0 {
|
||||
return nil, nil, errors.Errorf("unsupported flags %v", header.Flags)
|
||||
}
|
||||
|
||||
remainder := int64(header.HdrLen) - int64(binary.Size(&header))
|
||||
if remainder < 0 {
|
||||
return nil, nil, errors.New("header is too short")
|
||||
}
|
||||
|
||||
// Of course, the .BTF.ext header has different semantics than the
|
||||
// .BTF ext header. We need to ignore non-null values.
|
||||
_, err = io.CopyN(ioutil.Discard, r, remainder)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "header padding")
|
||||
}
|
||||
|
||||
if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "can't seek to function info section")
|
||||
}
|
||||
|
||||
funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "function info")
|
||||
}
|
||||
|
||||
if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "can't seek to line info section")
|
||||
}
|
||||
|
||||
lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "line info")
|
||||
}
|
||||
|
||||
return funcInfo, lineInfo, nil
|
||||
}
|
||||
|
||||
type btfExtInfoSec struct {
|
||||
SecNameOff uint32
|
||||
NumInfo uint32
|
||||
}
|
||||
|
||||
type extInfoRecord struct {
|
||||
InsnOff uint64
|
||||
Opaque []byte
|
||||
}
|
||||
|
||||
type extInfo struct {
|
||||
recordSize uint32
|
||||
records []extInfoRecord
|
||||
}
|
||||
|
||||
func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
|
||||
if other.recordSize != ei.recordSize {
|
||||
return extInfo{}, errors.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize)
|
||||
}
|
||||
|
||||
records := make([]extInfoRecord, 0, len(ei.records)+len(other.records))
|
||||
records = append(records, ei.records...)
|
||||
for _, info := range other.records {
|
||||
records = append(records, extInfoRecord{
|
||||
InsnOff: info.InsnOff + offset,
|
||||
Opaque: info.Opaque,
|
||||
})
|
||||
}
|
||||
return extInfo{ei.recordSize, records}, nil
|
||||
}
|
||||
|
||||
func (ei extInfo) MarshalBinary() ([]byte, error) {
|
||||
if len(ei.records) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(make([]byte, 0, int(ei.recordSize)*len(ei.records)))
|
||||
for _, info := range ei.records {
|
||||
// The kernel expects offsets in number of raw bpf instructions,
|
||||
// while the ELF tracks it in bytes.
|
||||
insnOff := uint32(info.InsnOff / asm.InstructionSize)
|
||||
if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil {
|
||||
return nil, errors.Wrap(err, "can't write instruction offset")
|
||||
}
|
||||
|
||||
buf.Write(info.Opaque)
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) {
|
||||
var recordSize uint32
|
||||
if err := binary.Read(r, bo, &recordSize); err != nil {
|
||||
return nil, errors.Wrap(err, "can't read record size")
|
||||
}
|
||||
|
||||
if recordSize < 4 {
|
||||
// Need at least insnOff
|
||||
return nil, errors.New("record size too short")
|
||||
}
|
||||
|
||||
result := make(map[string]extInfo)
|
||||
for {
|
||||
var infoHeader btfExtInfoSec
|
||||
if err := binary.Read(r, bo, &infoHeader); err == io.EOF {
|
||||
return result, nil
|
||||
} else if err != nil {
|
||||
return nil, errors.Wrap(err, "can't read ext info header")
|
||||
}
|
||||
|
||||
secName, err := strings.Lookup(infoHeader.SecNameOff)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't get section name")
|
||||
}
|
||||
|
||||
if infoHeader.NumInfo == 0 {
|
||||
return nil, errors.Errorf("section %s has invalid number of records", secName)
|
||||
}
|
||||
|
||||
var records []extInfoRecord
|
||||
for i := uint32(0); i < infoHeader.NumInfo; i++ {
|
||||
var byteOff uint32
|
||||
if err := binary.Read(r, bo, &byteOff); err != nil {
|
||||
return nil, errors.Wrapf(err, "section %v: can't read extended info offset", secName)
|
||||
}
|
||||
|
||||
buf := make([]byte, int(recordSize-4))
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return nil, errors.Wrapf(err, "section %v: can't read record", secName)
|
||||
}
|
||||
|
||||
if byteOff%asm.InstructionSize != 0 {
|
||||
return nil, errors.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff)
|
||||
}
|
||||
|
||||
records = append(records, extInfoRecord{uint64(byteOff), buf})
|
||||
}
|
||||
|
||||
result[secName] = extInfo{
|
||||
recordSize,
|
||||
records,
|
||||
}
|
||||
}
|
||||
}
|
||||
60
vendor/github.com/cilium/ebpf/internal/btf/strings.go
generated
vendored
Normal file
60
vendor/github.com/cilium/ebpf/internal/btf/strings.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type stringTable []byte
|
||||
|
||||
func readStringTable(r io.Reader) (stringTable, error) {
|
||||
contents, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't read string table")
|
||||
}
|
||||
|
||||
if len(contents) < 1 {
|
||||
return nil, errors.New("string table is empty")
|
||||
}
|
||||
|
||||
if contents[0] != '\x00' {
|
||||
return nil, errors.New("first item in string table is non-empty")
|
||||
}
|
||||
|
||||
if contents[len(contents)-1] != '\x00' {
|
||||
return nil, errors.New("string table isn't null terminated")
|
||||
}
|
||||
|
||||
return stringTable(contents), nil
|
||||
}
|
||||
|
||||
func (st stringTable) Lookup(offset uint32) (string, error) {
|
||||
if int64(offset) > int64(^uint(0)>>1) {
|
||||
return "", errors.Errorf("offset %d overflows int", offset)
|
||||
}
|
||||
|
||||
pos := int(offset)
|
||||
if pos >= len(st) {
|
||||
return "", errors.Errorf("offset %d is out of bounds", offset)
|
||||
}
|
||||
|
||||
if pos > 0 && st[pos-1] != '\x00' {
|
||||
return "", errors.Errorf("offset %d isn't start of a string", offset)
|
||||
}
|
||||
|
||||
str := st[pos:]
|
||||
end := bytes.IndexByte(str, '\x00')
|
||||
if end == -1 {
|
||||
return "", errors.Errorf("offset %d isn't null terminated", offset)
|
||||
}
|
||||
|
||||
return string(str[:end]), nil
|
||||
}
|
||||
|
||||
func (st stringTable) LookupName(offset uint32) (Name, error) {
|
||||
str, err := st.Lookup(offset)
|
||||
return Name(str), err
|
||||
}
|
||||
550
vendor/github.com/cilium/ebpf/internal/btf/types.go
generated
vendored
Normal file
550
vendor/github.com/cilium/ebpf/internal/btf/types.go
generated
vendored
Normal file
@@ -0,0 +1,550 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const maxTypeDepth = 32
|
||||
|
||||
// TypeID identifies a type in a BTF section.
|
||||
type TypeID uint32
|
||||
|
||||
// ID implements part of the Type interface.
|
||||
func (tid TypeID) ID() TypeID {
|
||||
return tid
|
||||
}
|
||||
|
||||
// Type represents a type described by BTF.
|
||||
type Type interface {
|
||||
ID() TypeID
|
||||
|
||||
// Make a copy of the type, without copying Type members.
|
||||
copy() Type
|
||||
|
||||
walk(*copyStack)
|
||||
}
|
||||
|
||||
// Name identifies a type.
|
||||
//
|
||||
// Anonymous types have an empty name.
|
||||
type Name string
|
||||
|
||||
func (n Name) name() string {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
// Void is the unit type of BTF.
|
||||
type Void struct{}
|
||||
|
||||
func (v Void) ID() TypeID { return 0 }
|
||||
func (v Void) copy() Type { return Void{} }
|
||||
func (v Void) walk(*copyStack) {}
|
||||
|
||||
// Int is an integer of a given length.
|
||||
type Int struct {
|
||||
TypeID
|
||||
Name
|
||||
|
||||
// The size of the integer in bytes.
|
||||
Size uint32
|
||||
}
|
||||
|
||||
func (i *Int) size() uint32 { return i.Size }
|
||||
func (i *Int) walk(*copyStack) {}
|
||||
func (i *Int) copy() Type {
|
||||
cpy := *i
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Pointer is a pointer to another type.
|
||||
type Pointer struct {
|
||||
TypeID
|
||||
Target Type
|
||||
}
|
||||
|
||||
func (p *Pointer) size() uint32 { return 8 }
|
||||
func (p *Pointer) walk(cs *copyStack) { cs.push(&p.Target) }
|
||||
func (p *Pointer) copy() Type {
|
||||
cpy := *p
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Array is an array with a fixed number of elements.
|
||||
type Array struct {
|
||||
TypeID
|
||||
Type Type
|
||||
Nelems uint32
|
||||
}
|
||||
|
||||
func (arr *Array) walk(cs *copyStack) { cs.push(&arr.Type) }
|
||||
func (arr *Array) copy() Type {
|
||||
cpy := *arr
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Struct is a compound type of consecutive members.
|
||||
type Struct struct {
|
||||
TypeID
|
||||
Name
|
||||
// The size of the struct including padding, in bytes
|
||||
Size uint32
|
||||
Members []Member
|
||||
}
|
||||
|
||||
func (s *Struct) size() uint32 { return s.Size }
|
||||
|
||||
func (s *Struct) walk(cs *copyStack) {
|
||||
for i := range s.Members {
|
||||
cs.push(&s.Members[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Struct) copy() Type {
|
||||
cpy := *s
|
||||
cpy.Members = copyMembers(cpy.Members)
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Union is a compound type where members occupy the same memory.
|
||||
type Union struct {
|
||||
TypeID
|
||||
Name
|
||||
// The size of the union including padding, in bytes.
|
||||
Size uint32
|
||||
Members []Member
|
||||
}
|
||||
|
||||
func (u *Union) size() uint32 { return u.Size }
|
||||
|
||||
func (u *Union) walk(cs *copyStack) {
|
||||
for i := range u.Members {
|
||||
cs.push(&u.Members[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *Union) copy() Type {
|
||||
cpy := *u
|
||||
cpy.Members = copyMembers(cpy.Members)
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Member is part of a Struct or Union.
|
||||
//
|
||||
// It is not a valid Type.
|
||||
type Member struct {
|
||||
Name
|
||||
Type Type
|
||||
Offset uint32
|
||||
}
|
||||
|
||||
func copyMembers(in []Member) []Member {
|
||||
cpy := make([]Member, 0, len(in))
|
||||
for _, member := range in {
|
||||
cpy = append(cpy, member)
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
// Enum lists possible values.
|
||||
type Enum struct {
|
||||
TypeID
|
||||
Name
|
||||
}
|
||||
|
||||
func (e *Enum) size() uint32 { return 4 }
|
||||
func (e *Enum) walk(*copyStack) {}
|
||||
func (e *Enum) copy() Type {
|
||||
cpy := *e
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Fwd is a forward declaration of a Type.
|
||||
type Fwd struct {
|
||||
TypeID
|
||||
Name
|
||||
}
|
||||
|
||||
func (f *Fwd) walk(*copyStack) {}
|
||||
func (f *Fwd) copy() Type {
|
||||
cpy := *f
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Typedef is an alias of a Type.
|
||||
type Typedef struct {
|
||||
TypeID
|
||||
Name
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (td *Typedef) walk(cs *copyStack) { cs.push(&td.Type) }
|
||||
func (td *Typedef) copy() Type {
|
||||
cpy := *td
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Volatile is a modifier.
|
||||
type Volatile struct {
|
||||
TypeID
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (v *Volatile) walk(cs *copyStack) { cs.push(&v.Type) }
|
||||
func (v *Volatile) copy() Type {
|
||||
cpy := *v
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Const is a modifier.
|
||||
type Const struct {
|
||||
TypeID
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (c *Const) walk(cs *copyStack) { cs.push(&c.Type) }
|
||||
func (c *Const) copy() Type {
|
||||
cpy := *c
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Restrict is a modifier.
|
||||
type Restrict struct {
|
||||
TypeID
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (r *Restrict) walk(cs *copyStack) { cs.push(&r.Type) }
|
||||
func (r *Restrict) copy() Type {
|
||||
cpy := *r
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Func is a function definition.
|
||||
type Func struct {
|
||||
TypeID
|
||||
Name
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (f *Func) walk(cs *copyStack) { cs.push(&f.Type) }
|
||||
func (f *Func) copy() Type {
|
||||
cpy := *f
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// FuncProto is a function declaration.
|
||||
type FuncProto struct {
|
||||
TypeID
|
||||
Return Type
|
||||
// Parameters not supported yet
|
||||
}
|
||||
|
||||
func (fp *FuncProto) walk(cs *copyStack) { cs.push(&fp.Return) }
|
||||
func (fp *FuncProto) copy() Type {
|
||||
cpy := *fp
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Var is a global variable.
|
||||
type Var struct {
|
||||
TypeID
|
||||
Name
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (v *Var) walk(cs *copyStack) { cs.push(&v.Type) }
|
||||
func (v *Var) copy() Type {
|
||||
cpy := *v
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Datasec is a global program section containing data.
|
||||
type Datasec struct {
|
||||
TypeID
|
||||
Name
|
||||
Size uint32
|
||||
}
|
||||
|
||||
func (ds *Datasec) size() uint32 { return ds.Size }
|
||||
func (ds *Datasec) walk(*copyStack) {}
|
||||
func (ds *Datasec) copy() Type {
|
||||
cpy := *ds
|
||||
return &cpy
|
||||
}
|
||||
|
||||
type sizer interface {
|
||||
size() uint32
|
||||
}
|
||||
|
||||
var (
|
||||
_ sizer = (*Int)(nil)
|
||||
_ sizer = (*Pointer)(nil)
|
||||
_ sizer = (*Struct)(nil)
|
||||
_ sizer = (*Union)(nil)
|
||||
_ sizer = (*Enum)(nil)
|
||||
_ sizer = (*Datasec)(nil)
|
||||
)
|
||||
|
||||
// Sizeof returns the size of a type in bytes.
|
||||
//
|
||||
// Returns an error if the size can't be computed.
|
||||
func Sizeof(typ Type) (int, error) {
|
||||
var (
|
||||
n = int64(1)
|
||||
elem int64
|
||||
)
|
||||
|
||||
for i := 0; i < maxTypeDepth; i++ {
|
||||
switch v := typ.(type) {
|
||||
case *Array:
|
||||
if n > 0 && int64(v.Nelems) > math.MaxInt64/n {
|
||||
return 0, errors.New("overflow")
|
||||
}
|
||||
|
||||
// Arrays may be of zero length, which allows
|
||||
// n to be zero as well.
|
||||
n *= int64(v.Nelems)
|
||||
typ = v.Type
|
||||
continue
|
||||
|
||||
case sizer:
|
||||
elem = int64(v.size())
|
||||
|
||||
case *Typedef:
|
||||
typ = v.Type
|
||||
continue
|
||||
case *Volatile:
|
||||
typ = v.Type
|
||||
continue
|
||||
case *Const:
|
||||
typ = v.Type
|
||||
continue
|
||||
case *Restrict:
|
||||
typ = v.Type
|
||||
continue
|
||||
|
||||
default:
|
||||
return 0, errors.Errorf("unrecognized type %T", typ)
|
||||
}
|
||||
|
||||
if n > 0 && elem > math.MaxInt64/n {
|
||||
return 0, errors.New("overflow")
|
||||
}
|
||||
|
||||
size := n * elem
|
||||
if int64(int(size)) != size {
|
||||
return 0, errors.New("overflow")
|
||||
}
|
||||
|
||||
return int(size), nil
|
||||
}
|
||||
|
||||
return 0, errors.New("exceeded type depth")
|
||||
}
|
||||
|
||||
// copy a Type recursively.
|
||||
//
|
||||
// typ may form a cycle.
|
||||
func copyType(typ Type) Type {
|
||||
var (
|
||||
copies = make(map[Type]Type)
|
||||
work copyStack
|
||||
)
|
||||
|
||||
for t := &typ; t != nil; t = work.pop() {
|
||||
// *t is the identity of the type.
|
||||
if cpy := copies[*t]; cpy != nil {
|
||||
*t = cpy
|
||||
continue
|
||||
}
|
||||
|
||||
cpy := (*t).copy()
|
||||
copies[*t] = cpy
|
||||
*t = cpy
|
||||
|
||||
// Mark any nested types for copying.
|
||||
cpy.walk(&work)
|
||||
}
|
||||
|
||||
return typ
|
||||
}
|
||||
|
||||
// copyStack keeps track of pointers to types which still
|
||||
// need to be copied.
|
||||
type copyStack []*Type
|
||||
|
||||
// push adds a type to the stack.
|
||||
func (cs *copyStack) push(t *Type) {
|
||||
*cs = append(*cs, t)
|
||||
}
|
||||
|
||||
// pop returns the topmost Type, or nil.
|
||||
func (cs *copyStack) pop() *Type {
|
||||
n := len(*cs)
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
t := (*cs)[n-1]
|
||||
*cs = (*cs)[:n-1]
|
||||
return t
|
||||
}
|
||||
|
||||
type namer interface {
|
||||
name() string
|
||||
}
|
||||
|
||||
var _ namer = Name("")
|
||||
|
||||
// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns
|
||||
// it into a graph of Types connected via pointers.
|
||||
//
|
||||
// Returns a map of named types (so, where NameOff is non-zero). Since BTF ignores
|
||||
// compilation units, multiple types may share the same name. A Type may form a
|
||||
// cyclic graph by pointing at itself.
|
||||
func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map[string][]Type, err error) {
|
||||
type fixup struct {
|
||||
id TypeID
|
||||
typ *Type
|
||||
}
|
||||
|
||||
var fixups []fixup
|
||||
convertMembers := func(raw []btfMember) ([]Member, error) {
|
||||
// NB: The fixup below relies on pre-allocating this array to
|
||||
// work, since otherwise append might re-allocate members.
|
||||
members := make([]Member, 0, len(raw))
|
||||
for i, btfMember := range raw {
|
||||
name, err := rawStrings.LookupName(btfMember.NameOff)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't get name for member %d", i)
|
||||
}
|
||||
members = append(members, Member{
|
||||
Name: name,
|
||||
Offset: btfMember.Offset,
|
||||
})
|
||||
}
|
||||
for i := range members {
|
||||
fixups = append(fixups, fixup{raw[i].Type, &members[i].Type})
|
||||
}
|
||||
return members, nil
|
||||
}
|
||||
|
||||
types := make([]Type, 0, len(rawTypes))
|
||||
types = append(types, Void{})
|
||||
namedTypes = make(map[string][]Type)
|
||||
|
||||
for i, raw := range rawTypes {
|
||||
var (
|
||||
// Void is defined to always be type ID 0, and is thus
|
||||
// omitted from BTF.
|
||||
id = TypeID(i + 1)
|
||||
typ Type
|
||||
)
|
||||
|
||||
name, err := rawStrings.LookupName(raw.NameOff)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't get name for type id %d", id)
|
||||
}
|
||||
|
||||
switch raw.Kind() {
|
||||
case kindInt:
|
||||
typ = &Int{id, name, raw.Size()}
|
||||
|
||||
case kindPointer:
|
||||
ptr := &Pointer{id, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &ptr.Target})
|
||||
typ = ptr
|
||||
|
||||
case kindArray:
|
||||
btfArr := raw.data.(*btfArray)
|
||||
|
||||
// IndexType is unused according to btf.rst.
|
||||
// Don't make it available right now.
|
||||
arr := &Array{id, nil, btfArr.Nelems}
|
||||
fixups = append(fixups, fixup{btfArr.Type, &arr.Type})
|
||||
typ = arr
|
||||
|
||||
case kindStruct:
|
||||
members, err := convertMembers(raw.data.([]btfMember))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "struct %s (id %d)", name, id)
|
||||
}
|
||||
typ = &Struct{id, name, raw.Size(), members}
|
||||
|
||||
case kindUnion:
|
||||
members, err := convertMembers(raw.data.([]btfMember))
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "union %s (id %d)", name, id)
|
||||
}
|
||||
typ = &Union{id, name, raw.Size(), members}
|
||||
|
||||
case kindEnum:
|
||||
typ = &Enum{id, name}
|
||||
|
||||
case kindForward:
|
||||
typ = &Fwd{id, name}
|
||||
|
||||
case kindTypedef:
|
||||
typedef := &Typedef{id, name, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &typedef.Type})
|
||||
typ = typedef
|
||||
|
||||
case kindVolatile:
|
||||
volatile := &Volatile{id, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &volatile.Type})
|
||||
typ = volatile
|
||||
|
||||
case kindConst:
|
||||
cnst := &Const{id, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &cnst.Type})
|
||||
typ = cnst
|
||||
|
||||
case kindRestrict:
|
||||
restrict := &Restrict{id, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &restrict.Type})
|
||||
typ = restrict
|
||||
|
||||
case kindFunc:
|
||||
fn := &Func{id, name, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &fn.Type})
|
||||
typ = fn
|
||||
|
||||
case kindFuncProto:
|
||||
fp := &FuncProto{id, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &fp.Return})
|
||||
typ = fp
|
||||
|
||||
case kindVar:
|
||||
v := &Var{id, name, nil}
|
||||
fixups = append(fixups, fixup{raw.Type(), &v.Type})
|
||||
typ = v
|
||||
|
||||
case kindDatasec:
|
||||
typ = &Datasec{id, name, raw.SizeType}
|
||||
|
||||
default:
|
||||
return nil, errors.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
|
||||
}
|
||||
|
||||
types = append(types, typ)
|
||||
|
||||
if namer, ok := typ.(namer); ok {
|
||||
if name := namer.name(); name != "" {
|
||||
namedTypes[name] = append(namedTypes[name], typ)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, fixup := range fixups {
|
||||
i := int(fixup.id)
|
||||
if i >= len(types) {
|
||||
return nil, errors.Errorf("reference to invalid type id: %d", fixup.id)
|
||||
}
|
||||
|
||||
*fixup.typ = types[i]
|
||||
}
|
||||
|
||||
return namedTypes, nil
|
||||
}
|
||||
50
vendor/github.com/cilium/ebpf/internal/errors.go
generated
vendored
Normal file
50
vendor/github.com/cilium/ebpf/internal/errors.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// ErrorWithLog returns an error that includes logs from the
|
||||
// kernel verifier.
|
||||
//
|
||||
// logErr should be the error returned by the syscall that generated
|
||||
// the log. It is used to check for truncation of the output.
|
||||
func ErrorWithLog(err error, log []byte, logErr error) error {
|
||||
logStr := strings.Trim(CString(log), "\t\r\n ")
|
||||
if errors.Cause(logErr) == unix.ENOSPC {
|
||||
logStr += " (truncated...)"
|
||||
}
|
||||
|
||||
return &loadError{err, logStr}
|
||||
}
|
||||
|
||||
type loadError struct {
|
||||
cause error
|
||||
log string
|
||||
}
|
||||
|
||||
func (le *loadError) Error() string {
|
||||
if le.log == "" {
|
||||
return le.cause.Error()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s: %s", le.cause, le.log)
|
||||
}
|
||||
|
||||
func (le *loadError) Cause() error {
|
||||
return le.cause
|
||||
}
|
||||
|
||||
// CString turns a NUL / zero terminated byte buffer into a string.
|
||||
func CString(in []byte) string {
|
||||
inLen := bytes.IndexByte(in, 0)
|
||||
if inLen == -1 {
|
||||
return ""
|
||||
}
|
||||
return string(in[:inLen])
|
||||
}
|
||||
63
vendor/github.com/cilium/ebpf/internal/fd.go
generated
vendored
Normal file
63
vendor/github.com/cilium/ebpf/internal/fd.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var ErrClosedFd = errors.New("use of closed file descriptor")
|
||||
|
||||
type FD struct {
|
||||
raw int64
|
||||
}
|
||||
|
||||
func NewFD(value uint32) *FD {
|
||||
fd := &FD{int64(value)}
|
||||
runtime.SetFinalizer(fd, (*FD).Close)
|
||||
return fd
|
||||
}
|
||||
|
||||
func (fd *FD) String() string {
|
||||
return strconv.FormatInt(fd.raw, 10)
|
||||
}
|
||||
|
||||
func (fd *FD) Value() (uint32, error) {
|
||||
if fd.raw < 0 {
|
||||
return 0, ErrClosedFd
|
||||
}
|
||||
|
||||
return uint32(fd.raw), nil
|
||||
}
|
||||
|
||||
func (fd *FD) Close() error {
|
||||
if fd.raw < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
value := int(fd.raw)
|
||||
fd.raw = -1
|
||||
|
||||
fd.Forget()
|
||||
return unix.Close(value)
|
||||
}
|
||||
|
||||
func (fd *FD) Forget() {
|
||||
runtime.SetFinalizer(fd, nil)
|
||||
}
|
||||
|
||||
func (fd *FD) Dup() (*FD, error) {
|
||||
if fd.raw < 0 {
|
||||
return nil, ErrClosedFd
|
||||
}
|
||||
|
||||
dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "can't dup fd")
|
||||
}
|
||||
|
||||
return NewFD(uint32(dup)), nil
|
||||
}
|
||||
16
vendor/github.com/cilium/ebpf/internal/io.go
generated
vendored
Normal file
16
vendor/github.com/cilium/ebpf/internal/io.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package internal
|
||||
|
||||
import "github.com/pkg/errors"
|
||||
|
||||
// DiscardZeroes makes sure that all written bytes are zero
|
||||
// before discarding them.
|
||||
type DiscardZeroes struct{}
|
||||
|
||||
func (DiscardZeroes) Write(p []byte) (int, error) {
|
||||
for _, b := range p {
|
||||
if b != 0 {
|
||||
return 0, errors.New("encountered non-zero byte")
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
26
vendor/github.com/cilium/ebpf/internal/ptr.go
generated
vendored
Normal file
26
vendor/github.com/cilium/ebpf/internal/ptr.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
package internal
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// NewPointer creates a 64-bit pointer from an unsafe Pointer.
|
||||
func NewPointer(ptr unsafe.Pointer) Pointer {
|
||||
return Pointer{ptr: ptr}
|
||||
}
|
||||
|
||||
// NewSlicePointer creates a 64-bit pointer from a byte slice.
|
||||
func NewSlicePointer(buf []byte) Pointer {
|
||||
if len(buf) == 0 {
|
||||
return Pointer{}
|
||||
}
|
||||
|
||||
return Pointer{ptr: unsafe.Pointer(&buf[0])}
|
||||
}
|
||||
|
||||
// NewStringPointer creates a 64-bit pointer from a string.
|
||||
func NewStringPointer(str string) Pointer {
|
||||
if str == "" {
|
||||
return Pointer{}
|
||||
}
|
||||
|
||||
return Pointer{ptr: unsafe.Pointer(&[]byte(str)[0])}
|
||||
}
|
||||
14
vendor/github.com/cilium/ebpf/internal/ptr_32_be.go
generated
vendored
Normal file
14
vendor/github.com/cilium/ebpf/internal/ptr_32_be.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build armbe mips mips64p32
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Pointer wraps an unsafe.Pointer to be 64bit to
|
||||
// conform to the syscall specification.
|
||||
type Pointer struct {
|
||||
pad uint32
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
14
vendor/github.com/cilium/ebpf/internal/ptr_32_le.go
generated
vendored
Normal file
14
vendor/github.com/cilium/ebpf/internal/ptr_32_le.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build 386 amd64p32 arm mipsle mips64p32le
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Pointer wraps an unsafe.Pointer to be 64bit to
|
||||
// conform to the syscall specification.
|
||||
type Pointer struct {
|
||||
ptr unsafe.Pointer
|
||||
pad uint32
|
||||
}
|
||||
14
vendor/github.com/cilium/ebpf/internal/ptr_64.go
generated
vendored
Normal file
14
vendor/github.com/cilium/ebpf/internal/ptr_64.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le
|
||||
// +build !armbe,!mips,!mips64p32
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Pointer wraps an unsafe.Pointer to be 64bit to
|
||||
// conform to the syscall specification.
|
||||
type Pointer struct {
|
||||
ptr unsafe.Pointer
|
||||
}
|
||||
23
vendor/github.com/cilium/ebpf/internal/syscall.go
generated
vendored
Normal file
23
vendor/github.com/cilium/ebpf/internal/syscall.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
// BPF wraps SYS_BPF.
|
||||
//
|
||||
// Any pointers contained in attr must use the Pointer type from this package.
|
||||
func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) {
|
||||
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
|
||||
runtime.KeepAlive(attr)
|
||||
|
||||
var err error
|
||||
if errNo != 0 {
|
||||
err = errNo
|
||||
}
|
||||
|
||||
return r1, err
|
||||
}
|
||||
Reference in New Issue
Block a user