vendor: cilium/ebbf 4032b1d8aae306b7bb94a2a11002932caf88c644

full diff: 60c3aa43f4...4032b1d8aa

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2020-04-04 00:31:39 +02:00
parent b0e71d7977
commit 4fb3410f65
25 changed files with 2304 additions and 349 deletions

530
vendor/github.com/cilium/ebpf/internal/btf/btf.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}