vendor: update cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775

full diff: a9f01edf17...1c8d4c9ef7

drops support for go1.12, and removes dependency on the golang.org/x/xerrors
transitional package.

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn 2020-07-02 14:44:55 +02:00
parent 6f5edda901
commit f49adb5277
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
44 changed files with 1091 additions and 1367 deletions

2
go.mod
View File

@ -4,7 +4,7 @@ go 1.14
require ( require (
github.com/checkpoint-restore/go-criu/v4 v4.0.2 github.com/checkpoint-restore/go-criu/v4 v4.0.2
github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3 github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775
github.com/containerd/console v1.0.0 github.com/containerd/console v1.0.0
github.com/coreos/go-systemd/v22 v22.0.0 github.com/coreos/go-systemd/v22 v22.0.0
github.com/cyphar/filepath-securejoin v0.2.2 github.com/cyphar/filepath-securejoin v0.2.2

12
go.sum
View File

@ -1,10 +1,8 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/checkpoint-restore/go-criu/v4 v4.0.2 h1:jt+rnBIhFtPw0fhtpYGcUOilh4aO9Hj7r+YLEtf30uA= github.com/checkpoint-restore/go-criu/v4 v4.0.2 h1:jt+rnBIhFtPw0fhtpYGcUOilh4aO9Hj7r+YLEtf30uA=
github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= github.com/checkpoint-restore/go-criu/v4 v4.0.2/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/cilium/ebpf v0.0.0-20200319110858-a7172c01168f h1:W1RQPz3nR8RxUw/Uqk71GU3JlZ7pNa1pXrHs98h0o9U= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775 h1:cHzBGGVew0ezFsq2grfy2RsB8hO/eNyBgOLHBCqfR1U=
github.com/cilium/ebpf v0.0.0-20200319110858-a7172c01168f/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s= github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3 h1:qcqzLJa2xCo9sgdCzpT/SJSYxROTEstuhf7ZBHMirms=
github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3/go.mod h1:XT+cAw5wfvsodedcijoh1l9cf7v1x9FlFB/3VmF/O8s=
github.com/containerd/console v1.0.0 h1:fU3UuQapBs+zLJu82NhR11Rif1ny2zfMMAyPJzSN5tQ= github.com/containerd/console v1.0.0 h1:fU3UuQapBs+zLJu82NhR11Rif1ny2zfMMAyPJzSN5tQ=
github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= github.com/containerd/console v1.0.0/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28= github.com/coreos/go-systemd/v22 v22.0.0 h1:XJIw/+VlJ+87J+doOxznsAWIdmWuViOVhkQamW5YV28=
@ -27,12 +25,8 @@ github.com/moby/sys/mountinfo v0.1.3 h1:KIrhRO14+AkwKvG/g2yIpNMOUVZ02xNhOw8KY1Ws
github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6SlFjaXFubv0xX0HsuC9qJsdqm7bNQpYM= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618 h1:7InQ7/zrOh6SlFjaXFubv0xX0HsuC9qJsdqm7bNQpYM=
github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0= github.com/mrunalp/fileutils v0.0.0-20171103030105-7d4729fb3618/go.mod h1:x8F1gnqOkIEiO4rqoeEEEqQbo7HjGMTvyoq3gej4iT0=
github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 h1:9mv9SC7GWmRWE0J/+oD8w3GsN2KYGKtg6uwLN7hfP5E= github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2 h1:9mv9SC7GWmRWE0J/+oD8w3GsN2KYGKtg6uwLN7hfP5E=
github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.3-0.20200520003142-237cc4f519e2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.4.0 h1:cpiX/2wWIju/6My60T6/z9CxNG7c8xTQyEmA9fChpUo=
github.com/opencontainers/selinux v1.4.0/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/opencontainers/selinux v1.5.1 h1:jskKwSMFYqyTrHEuJgQoUlTcId0av64S6EWObrIfn5Y= github.com/opencontainers/selinux v1.5.1 h1:jskKwSMFYqyTrHEuJgQoUlTcId0av64S6EWObrIfn5Y=
github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g= github.com/opencontainers/selinux v1.5.1/go.mod h1:yTcKuYAh6R95iDpefGLQaPaRwJFwyzAJufJyiTt7s0g=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -65,7 +59,5 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

15
vendor/github.com/cilium/ebpf/abi.go generated vendored
View File

@ -3,14 +3,13 @@ package ebpf
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
"syscall" "syscall"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"golang.org/x/xerrors"
) )
// MapABI are the attributes of a Map which are available across all supported kernels. // MapABI are the attributes of a Map which are available across all supported kernels.
@ -35,7 +34,7 @@ func newMapABIFromSpec(spec *MapSpec) *MapABI {
func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) { func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) {
info, err := bpfGetMapInfoByFD(fd) info, err := bpfGetMapInfoByFD(fd)
if err != nil { if err != nil {
if xerrors.Is(err, syscall.EINVAL) { if errors.Is(err, syscall.EINVAL) {
abi, err := newMapABIFromProc(fd) abi, err := newMapABIFromProc(fd)
return "", abi, err return "", abi, err
} }
@ -98,7 +97,7 @@ func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI {
func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) { func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) {
info, err := bpfGetProgInfoByFD(fd) info, err := bpfGetProgInfoByFD(fd)
if err != nil { if err != nil {
if xerrors.Is(err, syscall.EINVAL) { if errors.Is(err, syscall.EINVAL) {
return newProgramABIFromProc(fd) return newProgramABIFromProc(fd)
} }
@ -127,7 +126,7 @@ func newProgramABIFromProc(fd *internal.FD) (string, *ProgramABI, error) {
"prog_type": &abi.Type, "prog_type": &abi.Type,
"prog_tag": &name, "prog_tag": &name,
}) })
if xerrors.Is(err, errMissingFields) { if errors.Is(err, errMissingFields) {
return "", nil, &internal.UnsupportedFeatureError{ return "", nil, &internal.UnsupportedFeatureError{
Name: "reading ABI from /proc/self/fdinfo", Name: "reading ABI from /proc/self/fdinfo",
MinimumVersion: internal.Version{4, 11, 0}, MinimumVersion: internal.Version{4, 11, 0},
@ -153,12 +152,12 @@ func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error {
defer fh.Close() defer fh.Close()
if err := scanFdInfoReader(fh, fields); err != nil { if err := scanFdInfoReader(fh, fields); err != nil {
return xerrors.Errorf("%s: %w", fh.Name(), err) return fmt.Errorf("%s: %w", fh.Name(), err)
} }
return nil return nil
} }
var errMissingFields = xerrors.New("missing fields") var errMissingFields = errors.New("missing fields")
func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
var ( var (
@ -179,7 +178,7 @@ func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
} }
if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 { if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 {
return xerrors.Errorf("can't parse field %s: %v", name, err) return fmt.Errorf("can't parse field %s: %v", name, err)
} }
scanned++ scanned++

View File

@ -2,13 +2,11 @@ package asm
import ( import (
"encoding/binary" "encoding/binary"
"errors"
"fmt" "fmt"
"github.com/cilium/ebpf/internal"
"io" "io"
"math" "math"
"strings" "strings"
"golang.org/x/xerrors"
) )
// InstructionSize is the size of a BPF instruction in bytes // InstructionSize is the size of a BPF instruction in bytes
@ -40,10 +38,12 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err
} }
ins.OpCode = bi.OpCode ins.OpCode = bi.OpCode
ins.Dst = bi.Registers.Dst()
ins.Src = bi.Registers.Src()
ins.Offset = bi.Offset ins.Offset = bi.Offset
ins.Constant = int64(bi.Constant) ins.Constant = int64(bi.Constant)
ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo)
if err != nil {
return 0, fmt.Errorf("can't unmarshal registers: %s", err)
}
if !bi.OpCode.isDWordLoad() { if !bi.OpCode.isDWordLoad() {
return InstructionSize, nil return InstructionSize, nil
@ -52,10 +52,10 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err
var bi2 bpfInstruction var bi2 bpfInstruction
if err := binary.Read(r, bo, &bi2); err != nil { if err := binary.Read(r, bo, &bi2); err != nil {
// No Wrap, to avoid io.EOF clash // No Wrap, to avoid io.EOF clash
return 0, xerrors.New("64bit immediate is missing second half") return 0, errors.New("64bit immediate is missing second half")
} }
if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 { if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 {
return 0, xerrors.New("64bit immediate has non-zero fields") return 0, errors.New("64bit immediate has non-zero fields")
} }
ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant))) ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant)))
@ -65,7 +65,7 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err
// Marshal encodes a BPF instruction. // Marshal encodes a BPF instruction.
func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) { func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {
if ins.OpCode == InvalidOpCode { if ins.OpCode == InvalidOpCode {
return 0, xerrors.New("invalid opcode") return 0, errors.New("invalid opcode")
} }
isDWordLoad := ins.OpCode.isDWordLoad() isDWordLoad := ins.OpCode.isDWordLoad()
@ -76,9 +76,14 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
cons = int32(uint32(ins.Constant)) cons = int32(uint32(ins.Constant))
} }
regs, err := newBPFRegisters(ins.Dst, ins.Src, bo)
if err != nil {
return 0, fmt.Errorf("can't marshal registers: %s", err)
}
bpfi := bpfInstruction{ bpfi := bpfInstruction{
ins.OpCode, ins.OpCode,
newBPFRegisters(ins.Dst, ins.Src), regs,
ins.Offset, ins.Offset,
cons, cons,
} }
@ -107,11 +112,11 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
// Returns an error if the instruction doesn't load a map. // Returns an error if the instruction doesn't load a map.
func (ins *Instruction) RewriteMapPtr(fd int) error { func (ins *Instruction) RewriteMapPtr(fd int) error {
if !ins.OpCode.isDWordLoad() { if !ins.OpCode.isDWordLoad() {
return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode) return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
} }
if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue { if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue {
return xerrors.New("not a load from a map") return errors.New("not a load from a map")
} }
// Preserve the offset value for direct map loads. // Preserve the offset value for direct map loads.
@ -130,11 +135,11 @@ func (ins *Instruction) mapPtr() uint32 {
// Returns an error if the instruction is not a direct load. // Returns an error if the instruction is not a direct load.
func (ins *Instruction) RewriteMapOffset(offset uint32) error { func (ins *Instruction) RewriteMapOffset(offset uint32) error {
if !ins.OpCode.isDWordLoad() { if !ins.OpCode.isDWordLoad() {
return xerrors.Errorf("%s is not a 64 bit load", ins.OpCode) return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
} }
if ins.Src != PseudoMapValue { if ins.Src != PseudoMapValue {
return xerrors.New("not a direct load from a map") return errors.New("not a direct load from a map")
} }
fd := uint64(ins.Constant) & math.MaxUint32 fd := uint64(ins.Constant) & math.MaxUint32
@ -245,7 +250,7 @@ func (insns Instructions) String() string {
// Returns an error if the symbol isn't used, see IsUnreferencedSymbol. // Returns an error if the symbol isn't used, see IsUnreferencedSymbol.
func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
if symbol == "" { if symbol == "" {
return xerrors.New("empty symbol") return errors.New("empty symbol")
} }
found := false found := false
@ -280,7 +285,7 @@ func (insns Instructions) SymbolOffsets() (map[string]int, error) {
} }
if _, ok := offsets[ins.Symbol]; ok { if _, ok := offsets[ins.Symbol]; ok {
return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol) return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol)
} }
offsets[ins.Symbol] = i offsets[ins.Symbol] = i
@ -318,7 +323,7 @@ func (insns Instructions) marshalledOffsets() (map[string]int, error) {
} }
if _, ok := symbols[ins.Symbol]; ok { if _, ok := symbols[ins.Symbol]; ok {
return nil, xerrors.Errorf("duplicate symbol %s", ins.Symbol) return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol)
} }
symbols[ins.Symbol] = currentPos symbols[ins.Symbol] = currentPos
@ -399,7 +404,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
// Rewrite bpf to bpf call // Rewrite bpf to bpf call
offset, ok := absoluteOffsets[ins.Reference] offset, ok := absoluteOffsets[ins.Reference]
if !ok { if !ok {
return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
} }
ins.Constant = int64(offset - num - 1) ins.Constant = int64(offset - num - 1)
@ -408,7 +413,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
// Rewrite jump to label // Rewrite jump to label
offset, ok := absoluteOffsets[ins.Reference] offset, ok := absoluteOffsets[ins.Reference]
if !ok { if !ok {
return xerrors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference) return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
} }
ins.Offset = int16(offset - num - 1) ins.Offset = int16(offset - num - 1)
@ -416,7 +421,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
n, err := ins.Marshal(w, bo) n, err := ins.Marshal(w, bo)
if err != nil { if err != nil {
return xerrors.Errorf("instruction %d: %w", i, err) return fmt.Errorf("instruction %d: %w", i, err)
} }
num += int(n / InstructionSize) num += int(n / InstructionSize)
@ -433,27 +438,25 @@ type bpfInstruction struct {
type bpfRegisters uint8 type bpfRegisters uint8
func newBPFRegisters(dst, src Register) bpfRegisters { func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
if internal.NativeEndian == binary.LittleEndian { switch bo {
return bpfRegisters((src << 4) | (dst & 0xF)) case binary.LittleEndian:
} else { return bpfRegisters((src << 4) | (dst & 0xF)), nil
return bpfRegisters((dst << 4) | (src & 0xF)) case binary.BigEndian:
return bpfRegisters((dst << 4) | (src & 0xF)), nil
default:
return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
} }
} }
func (r bpfRegisters) Dst() Register { func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) {
if internal.NativeEndian == binary.LittleEndian { switch bo {
return Register(r & 0xF) case binary.LittleEndian:
}else { return Register(r & 0xF), Register(r >> 4), nil
return Register(r >> 4) case binary.BigEndian:
} return Register(r >> 4), Register(r & 0xf), nil
} default:
return 0, 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
func (r bpfRegisters) Src() Register {
if internal.NativeEndian == binary.LittleEndian {
return Register(r >> 4)
} else {
return Register(r & 0xf)
} }
} }

View File

@ -225,7 +225,7 @@ func (op OpCode) String() string {
} }
default: default:
fmt.Fprintf(&f, "%#x", op) fmt.Fprintf(&f, "OpCode(%#x)", uint8(op))
} }
return f.String() return f.String()

View File

@ -1,12 +1,13 @@
package ebpf package ebpf
import ( import (
"errors"
"fmt"
"math" "math"
"github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/btf"
"golang.org/x/xerrors"
) )
// CollectionOptions control loading a collection into the kernel. // CollectionOptions control loading a collection into the kernel.
@ -64,12 +65,12 @@ func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
// Not all programs need to use the map // Not all programs need to use the map
default: default:
return xerrors.Errorf("program %s: %w", progName, err) return fmt.Errorf("program %s: %w", progName, err)
} }
} }
if !seen { if !seen {
return xerrors.Errorf("map %s not referenced by any programs", symbol) return fmt.Errorf("map %s not referenced by any programs", symbol)
} }
// Prevent NewCollection from creating rewritten maps // Prevent NewCollection from creating rewritten maps
@ -96,21 +97,21 @@ func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error { func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error {
rodata := cs.Maps[".rodata"] rodata := cs.Maps[".rodata"]
if rodata == nil { if rodata == nil {
return xerrors.New("missing .rodata section") return errors.New("missing .rodata section")
} }
if rodata.BTF == nil { if rodata.BTF == nil {
return xerrors.New(".rodata section has no BTF") return errors.New(".rodata section has no BTF")
} }
if n := len(rodata.Contents); n != 1 { if n := len(rodata.Contents); n != 1 {
return xerrors.Errorf("expected one key in .rodata, found %d", n) return fmt.Errorf("expected one key in .rodata, found %d", n)
} }
kv := rodata.Contents[0] kv := rodata.Contents[0]
value, ok := kv.Value.([]byte) value, ok := kv.Value.([]byte)
if !ok { if !ok {
return xerrors.Errorf("first value in .rodata is %T not []byte", kv.Value) return fmt.Errorf("first value in .rodata is %T not []byte", kv.Value)
} }
buf := make([]byte, len(value)) buf := make([]byte, len(value))
@ -185,14 +186,14 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (col
var handle *btf.Handle var handle *btf.Handle
if mapSpec.BTF != nil { if mapSpec.BTF != nil {
handle, err = loadBTF(btf.MapSpec(mapSpec.BTF)) handle, err = loadBTF(btf.MapSpec(mapSpec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { if err != nil && !errors.Is(err, btf.ErrNotSupported) {
return nil, err return nil, err
} }
} }
m, err := newMapWithBTF(mapSpec, handle) m, err := newMapWithBTF(mapSpec, handle)
if err != nil { if err != nil {
return nil, xerrors.Errorf("map %s: %w", mapName, err) return nil, fmt.Errorf("map %s: %w", mapName, err)
} }
maps[mapName] = m maps[mapName] = m
} }
@ -216,29 +217,29 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (col
m := maps[ins.Reference] m := maps[ins.Reference]
if m == nil { if m == nil {
return nil, xerrors.Errorf("program %s: missing map %s", progName, ins.Reference) return nil, fmt.Errorf("program %s: missing map %s", progName, ins.Reference)
} }
fd := m.FD() fd := m.FD()
if fd < 0 { if fd < 0 {
return nil, xerrors.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd) return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
} }
if err := ins.RewriteMapPtr(m.FD()); err != nil { if err := ins.RewriteMapPtr(m.FD()); err != nil {
return nil, xerrors.Errorf("progam %s: map %s: %w", progName, ins.Reference, err) return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err)
} }
} }
var handle *btf.Handle var handle *btf.Handle
if progSpec.BTF != nil { if progSpec.BTF != nil {
handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF)) handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { if err != nil && !errors.Is(err, btf.ErrNotSupported) {
return nil, err return nil, err
} }
} }
prog, err := newProgramWithBTF(progSpec, handle, opts.Programs) prog, err := newProgramWithBTF(progSpec, handle, opts.Programs)
if err != nil { if err != nil {
return nil, xerrors.Errorf("program %s: %w", progName, err) return nil, fmt.Errorf("program %s: %w", progName, err)
} }
progs[progName] = prog progs[progName] = prog
} }

View File

@ -4,6 +4,8 @@ import (
"bytes" "bytes"
"debug/elf" "debug/elf"
"encoding/binary" "encoding/binary"
"errors"
"fmt"
"io" "io"
"math" "math"
"os" "os"
@ -13,8 +15,6 @@ import (
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
) )
type elfCode struct { type elfCode struct {
@ -35,7 +35,7 @@ func LoadCollectionSpec(file string) (*CollectionSpec, error) {
spec, err := LoadCollectionSpecFromReader(f) spec, err := LoadCollectionSpecFromReader(f)
if err != nil { if err != nil {
return nil, xerrors.Errorf("file %s: %w", file, err) return nil, fmt.Errorf("file %s: %w", file, err)
} }
return spec, nil return spec, nil
} }
@ -50,7 +50,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
symbols, err := f.Symbols() symbols, err := f.Symbols()
if err != nil { if err != nil {
return nil, xerrors.Errorf("load symbols: %v", err) return nil, fmt.Errorf("load symbols: %v", err)
} }
ec := &elfCode{f, symbols, symbolsPerSection(symbols), "", 0} ec := &elfCode{f, symbols, symbolsPerSection(symbols), "", 0}
@ -79,13 +79,13 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
dataSections[elf.SectionIndex(i)] = sec dataSections[elf.SectionIndex(i)] = sec
case sec.Type == elf.SHT_REL: case sec.Type == elf.SHT_REL:
if int(sec.Info) >= len(ec.Sections) { if int(sec.Info) >= len(ec.Sections) {
return nil, xerrors.Errorf("found relocation section %v for missing section %v", i, sec.Info) return nil, fmt.Errorf("found relocation section %v for missing section %v", i, sec.Info)
} }
// Store relocations under the section index of the target // Store relocations under the section index of the target
idx := elf.SectionIndex(sec.Info) idx := elf.SectionIndex(sec.Info)
if relSections[idx] != nil { if relSections[idx] != nil {
return nil, xerrors.Errorf("section %d has multiple relocation sections", sec.Info) return nil, fmt.Errorf("section %d has multiple relocation sections", sec.Info)
} }
relSections[idx] = sec relSections[idx] = sec
case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0:
@ -95,44 +95,52 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
ec.license, err = loadLicense(licenseSection) ec.license, err = loadLicense(licenseSection)
if err != nil { if err != nil {
return nil, xerrors.Errorf("load license: %w", err) return nil, fmt.Errorf("load license: %w", err)
} }
ec.version, err = loadVersion(versionSection, ec.ByteOrder) ec.version, err = loadVersion(versionSection, ec.ByteOrder)
if err != nil { if err != nil {
return nil, xerrors.Errorf("load version: %w", err) return nil, fmt.Errorf("load version: %w", err)
} }
btfSpec, err := btf.LoadSpecFromReader(rd) btfSpec, err := btf.LoadSpecFromReader(rd)
if err != nil { if err != nil {
return nil, xerrors.Errorf("load BTF: %w", err) return nil, fmt.Errorf("load BTF: %w", err)
}
relocations, referencedSections, err := ec.loadRelocations(relSections)
if err != nil {
return nil, fmt.Errorf("load relocations: %w", err)
} }
maps := make(map[string]*MapSpec) maps := make(map[string]*MapSpec)
if err := ec.loadMaps(maps, mapSections); err != nil { if err := ec.loadMaps(maps, mapSections); err != nil {
return nil, xerrors.Errorf("load maps: %w", err) return nil, fmt.Errorf("load maps: %w", err)
} }
if len(btfMaps) > 0 { if len(btfMaps) > 0 {
if err := ec.loadBTFMaps(maps, btfMaps, btfSpec); err != nil { if err := ec.loadBTFMaps(maps, btfMaps, btfSpec); err != nil {
return nil, xerrors.Errorf("load BTF maps: %w", err) return nil, fmt.Errorf("load BTF maps: %w", err)
} }
} }
if len(dataSections) > 0 { if len(dataSections) > 0 {
if err := ec.loadDataSections(maps, dataSections, btfSpec); err != nil { for idx := range dataSections {
return nil, xerrors.Errorf("load data sections: %w", err) if !referencedSections[idx] {
// Prune data sections which are not referenced by any
// instructions.
delete(dataSections, idx)
}
} }
}
relocations, err := ec.loadRelocations(relSections) if err := ec.loadDataSections(maps, dataSections, btfSpec); err != nil {
if err != nil { return nil, fmt.Errorf("load data sections: %w", err)
return nil, xerrors.Errorf("load relocations: %w", err) }
} }
progs, err := ec.loadPrograms(progSections, relocations, btfSpec) progs, err := ec.loadPrograms(progSections, relocations, btfSpec)
if err != nil { if err != nil {
return nil, xerrors.Errorf("load programs: %w", err) return nil, fmt.Errorf("load programs: %w", err)
} }
return &CollectionSpec{maps, progs}, nil return &CollectionSpec{maps, progs}, nil
@ -140,11 +148,12 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
func loadLicense(sec *elf.Section) (string, error) { func loadLicense(sec *elf.Section) (string, error) {
if sec == nil { if sec == nil {
return "", xerrors.New("missing license section") return "", nil
} }
data, err := sec.Data() data, err := sec.Data()
if err != nil { if err != nil {
return "", xerrors.Errorf("section %s: %v", sec.Name, err) return "", fmt.Errorf("section %s: %v", sec.Name, err)
} }
return string(bytes.TrimRight(data, "\000")), nil return string(bytes.TrimRight(data, "\000")), nil
} }
@ -156,12 +165,12 @@ func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) {
var version uint32 var version uint32
if err := binary.Read(sec.Open(), bo, &version); err != nil { if err := binary.Read(sec.Open(), bo, &version); err != nil {
return 0, xerrors.Errorf("section %s: %v", sec.Name, err) return 0, fmt.Errorf("section %s: %v", sec.Name, err)
} }
return version, nil return version, nil
} }
func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btf *btf.Spec) (map[string]*ProgramSpec, error) { func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section, relocations map[elf.SectionIndex]map[uint64]elf.Symbol, btfSpec *btf.Spec) (map[string]*ProgramSpec, error) {
var ( var (
progs []*ProgramSpec progs []*ProgramSpec
libs []*ProgramSpec libs []*ProgramSpec
@ -170,34 +179,36 @@ func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section,
for idx, sec := range progSections { for idx, sec := range progSections {
syms := ec.symbolsPerSection[idx] syms := ec.symbolsPerSection[idx]
if len(syms) == 0 { if len(syms) == 0 {
return nil, xerrors.Errorf("section %v: missing symbols", sec.Name) return nil, fmt.Errorf("section %v: missing symbols", sec.Name)
} }
funcSym, ok := syms[0] funcSym, ok := syms[0]
if !ok { if !ok {
return nil, xerrors.Errorf("section %v: no label at start", sec.Name) return nil, fmt.Errorf("section %v: no label at start", sec.Name)
} }
insns, length, err := ec.loadInstructions(sec, syms, relocations[idx]) insns, length, err := ec.loadInstructions(sec, syms, relocations[idx])
if err != nil { if err != nil {
return nil, xerrors.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err) return nil, fmt.Errorf("program %s: can't unmarshal instructions: %w", funcSym.Name, err)
} }
progType, attachType := getProgType(sec.Name) progType, attachType, attachTo := getProgType(sec.Name)
spec := &ProgramSpec{ spec := &ProgramSpec{
Name: funcSym.Name, Name: funcSym.Name,
Type: progType, Type: progType,
AttachType: attachType, AttachType: attachType,
AttachTo: attachTo,
License: ec.license, License: ec.license,
KernelVersion: ec.version, KernelVersion: ec.version,
Instructions: insns, Instructions: insns,
ByteOrder: ec.ByteOrder,
} }
if btf != nil { if btfSpec != nil {
spec.BTF, err = btf.Program(sec.Name, length) spec.BTF, err = btfSpec.Program(sec.Name, length)
if err != nil { if err != nil && !errors.Is(err, btf.ErrNoExtendedInfo) {
return nil, xerrors.Errorf("BTF for section %s (program %s): %w", sec.Name, funcSym.Name, err) return nil, fmt.Errorf("program %s: %w", funcSym.Name, err)
} }
} }
@ -215,7 +226,7 @@ func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section,
for _, prog := range progs { for _, prog := range progs {
err := link(prog, libs) err := link(prog, libs)
if err != nil { if err != nil {
return nil, xerrors.Errorf("program %s: %w", prog.Name, err) return nil, fmt.Errorf("program %s: %w", prog.Name, err)
} }
res[prog.Name] = prog res[prog.Name] = prog
} }
@ -236,14 +247,14 @@ func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations m
return insns, offset, nil return insns, offset, nil
} }
if err != nil { if err != nil {
return nil, 0, xerrors.Errorf("offset %d: %w", offset, err) return nil, 0, fmt.Errorf("offset %d: %w", offset, err)
} }
ins.Symbol = symbols[offset].Name ins.Symbol = symbols[offset].Name
if rel, ok := relocations[offset]; ok { if rel, ok := relocations[offset]; ok {
if err = ec.relocateInstruction(&ins, rel); err != nil { if err = ec.relocateInstruction(&ins, rel); err != nil {
return nil, 0, xerrors.Errorf("offset %d: can't relocate instruction: %w", offset, err) return nil, 0, fmt.Errorf("offset %d: can't relocate instruction: %w", offset, err)
} }
} }
@ -264,7 +275,7 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err
// from the section itself. // from the section itself.
idx := int(rel.Section) idx := int(rel.Section)
if idx > len(ec.Sections) { if idx > len(ec.Sections) {
return xerrors.New("out-of-bounds section index") return errors.New("out-of-bounds section index")
} }
name = ec.Sections[idx].Name name = ec.Sections[idx].Name
@ -284,7 +295,7 @@ outer:
// section. Weirdly, the offset of the real symbol in the // section. Weirdly, the offset of the real symbol in the
// section is encoded in the instruction stream. // section is encoded in the instruction stream.
if bind != elf.STB_LOCAL { if bind != elf.STB_LOCAL {
return xerrors.Errorf("direct load: %s: unsupported relocation %s", name, bind) return fmt.Errorf("direct load: %s: unsupported relocation %s", name, bind)
} }
// For some reason, clang encodes the offset of the symbol its // For some reason, clang encodes the offset of the symbol its
@ -306,13 +317,13 @@ outer:
case elf.STT_OBJECT: case elf.STT_OBJECT:
if bind != elf.STB_GLOBAL { if bind != elf.STB_GLOBAL {
return xerrors.Errorf("load: %s: unsupported binding: %s", name, bind) return fmt.Errorf("load: %s: unsupported binding: %s", name, bind)
} }
ins.Src = asm.PseudoMapFD ins.Src = asm.PseudoMapFD
default: default:
return xerrors.Errorf("load: %s: unsupported relocation: %s", name, typ) return fmt.Errorf("load: %s: unsupported relocation: %s", name, typ)
} }
// Mark the instruction as needing an update when creating the // Mark the instruction as needing an update when creating the
@ -323,18 +334,18 @@ outer:
case ins.OpCode.JumpOp() == asm.Call: case ins.OpCode.JumpOp() == asm.Call:
if ins.Src != asm.PseudoCall { if ins.Src != asm.PseudoCall {
return xerrors.Errorf("call: %s: incorrect source register", name) return fmt.Errorf("call: %s: incorrect source register", name)
} }
switch typ { switch typ {
case elf.STT_NOTYPE, elf.STT_FUNC: case elf.STT_NOTYPE, elf.STT_FUNC:
if bind != elf.STB_GLOBAL { if bind != elf.STB_GLOBAL {
return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind) return fmt.Errorf("call: %s: unsupported binding: %s", name, bind)
} }
case elf.STT_SECTION: case elf.STT_SECTION:
if bind != elf.STB_LOCAL { if bind != elf.STB_LOCAL {
return xerrors.Errorf("call: %s: unsupported binding: %s", name, bind) return fmt.Errorf("call: %s: unsupported binding: %s", name, bind)
} }
// The function we want to call is in the indicated section, // The function we want to call is in the indicated section,
@ -343,23 +354,23 @@ outer:
// A value of -1 references the first instruction in the section. // A value of -1 references the first instruction in the section.
offset := int64(int32(ins.Constant)+1) * asm.InstructionSize offset := int64(int32(ins.Constant)+1) * asm.InstructionSize
if offset < 0 { if offset < 0 {
return xerrors.Errorf("call: %s: invalid offset %d", name, offset) return fmt.Errorf("call: %s: invalid offset %d", name, offset)
} }
sym, ok := ec.symbolsPerSection[rel.Section][uint64(offset)] sym, ok := ec.symbolsPerSection[rel.Section][uint64(offset)]
if !ok { if !ok {
return xerrors.Errorf("call: %s: no symbol at offset %d", name, offset) return fmt.Errorf("call: %s: no symbol at offset %d", name, offset)
} }
ins.Constant = -1 ins.Constant = -1
name = sym.Name name = sym.Name
default: default:
return xerrors.Errorf("call: %s: invalid symbol type %s", name, typ) return fmt.Errorf("call: %s: invalid symbol type %s", name, typ)
} }
default: default:
return xerrors.Errorf("relocation for unsupported instruction: %s", ins.OpCode) return fmt.Errorf("relocation for unsupported instruction: %s", ins.OpCode)
} }
ins.Reference = name ins.Reference = name
@ -370,11 +381,11 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
for idx, sec := range mapSections { for idx, sec := range mapSections {
syms := ec.symbolsPerSection[idx] syms := ec.symbolsPerSection[idx]
if len(syms) == 0 { if len(syms) == 0 {
return xerrors.Errorf("section %v: no symbols", sec.Name) return fmt.Errorf("section %v: no symbols", sec.Name)
} }
if sec.Size%uint64(len(syms)) != 0 { if sec.Size%uint64(len(syms)) != 0 {
return xerrors.Errorf("section %v: map descriptors are not of equal size", sec.Name) return fmt.Errorf("section %v: map descriptors are not of equal size", sec.Name)
} }
var ( var (
@ -384,11 +395,11 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size { for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size {
mapSym, ok := syms[offset] mapSym, ok := syms[offset]
if !ok { if !ok {
return xerrors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
} }
if maps[mapSym.Name] != nil { if maps[mapSym.Name] != nil {
return xerrors.Errorf("section %v: map %v already exists", sec.Name, mapSym) return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym)
} }
lr := io.LimitReader(r, int64(size)) lr := io.LimitReader(r, int64(size))
@ -398,19 +409,19 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
} }
switch { switch {
case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil: case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
return xerrors.Errorf("map %v: missing type", mapSym) return fmt.Errorf("map %v: missing type", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil: case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
return xerrors.Errorf("map %v: missing key size", mapSym) return fmt.Errorf("map %v: missing key size", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil: case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
return xerrors.Errorf("map %v: missing value size", mapSym) return fmt.Errorf("map %v: missing value size", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil: case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
return xerrors.Errorf("map %v: missing max entries", mapSym) return fmt.Errorf("map %v: missing max entries", mapSym)
case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil: case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
return xerrors.Errorf("map %v: missing flags", mapSym) return fmt.Errorf("map %v: missing flags", mapSym)
} }
if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil { if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil {
return xerrors.Errorf("map %v: unknown and non-zero fields in definition", mapSym) return fmt.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
} }
maps[mapSym.Name] = &spec maps[mapSym.Name] = &spec
@ -422,84 +433,116 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error { func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error {
if spec == nil { if spec == nil {
return xerrors.Errorf("missing BTF") return fmt.Errorf("missing BTF")
} }
for idx, sec := range mapSections { for idx, sec := range mapSections {
syms := ec.symbolsPerSection[idx] syms := ec.symbolsPerSection[idx]
if len(syms) == 0 { if len(syms) == 0 {
return xerrors.Errorf("section %v: no symbols", sec.Name) return fmt.Errorf("section %v: no symbols", sec.Name)
} }
for _, sym := range syms { for _, sym := range syms {
name := sym.Name name := sym.Name
if maps[name] != nil { if maps[name] != nil {
return xerrors.Errorf("section %v: map %v already exists", sec.Name, sym) return fmt.Errorf("section %v: map %v already exists", sec.Name, sym)
} }
btfMap, btfMapMembers, err := spec.Map(name) mapSpec, err := mapSpecFromBTF(spec, name)
if err != nil { if err != nil {
return xerrors.Errorf("map %v: can't get BTF: %w", name, err) return fmt.Errorf("map %v: %w", name, err)
} }
spec, err := mapSpecFromBTF(btfMap, btfMapMembers) maps[name] = mapSpec
if err != nil {
return xerrors.Errorf("map %v: %w", name, err)
}
maps[name] = spec
} }
} }
return nil return nil
} }
func mapSpecFromBTF(btfMap *btf.Map, btfMapMembers []btf.Member) (*MapSpec, error) { func mapSpecFromBTF(spec *btf.Spec, name string) (*MapSpec, error) {
btfMap, btfMapMembers, err := spec.Map(name)
if err != nil {
return nil, fmt.Errorf("can't get BTF: %w", err)
}
keyType := btf.MapKey(btfMap)
size, err := btf.Sizeof(keyType)
if err != nil {
return nil, fmt.Errorf("can't get size of BTF key: %w", err)
}
keySize := uint32(size)
valueType := btf.MapValue(btfMap)
size, err = btf.Sizeof(valueType)
if err != nil {
return nil, fmt.Errorf("can't get size of BTF value: %w", err)
}
valueSize := uint32(size)
var ( var (
mapType, flags, maxEntries uint32 mapType, flags, maxEntries uint32
err error
) )
for _, member := range btfMapMembers { for _, member := range btfMapMembers {
switch member.Name { switch member.Name {
case "type": case "type":
mapType, err = uintFromBTF(member.Type) mapType, err = uintFromBTF(member.Type)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get type: %w", err) return nil, fmt.Errorf("can't get type: %w", err)
} }
case "map_flags": case "map_flags":
flags, err = uintFromBTF(member.Type) flags, err = uintFromBTF(member.Type)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get BTF map flags: %w", err) return nil, fmt.Errorf("can't get BTF map flags: %w", err)
} }
case "max_entries": case "max_entries":
maxEntries, err = uintFromBTF(member.Type) maxEntries, err = uintFromBTF(member.Type)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get BTF map max entries: %w", err) return nil, fmt.Errorf("can't get BTF map max entries: %w", err)
} }
case "key": case "key_size":
case "value": if _, isVoid := keyType.(*btf.Void); !isVoid {
return nil, errors.New("both key and key_size given")
}
keySize, err = uintFromBTF(member.Type)
if err != nil {
return nil, fmt.Errorf("can't get BTF key size: %w", err)
}
case "value_size":
if _, isVoid := valueType.(*btf.Void); !isVoid {
return nil, errors.New("both value and value_size given")
}
valueSize, err = uintFromBTF(member.Type)
if err != nil {
return nil, fmt.Errorf("can't get BTF value size: %w", err)
}
case "pinning":
pinning, err := uintFromBTF(member.Type)
if err != nil {
return nil, fmt.Errorf("can't get pinning: %w", err)
}
if pinning != 0 {
return nil, fmt.Errorf("'pinning' attribute not supported: %w", ErrNotSupported)
}
case "key", "value":
default: default:
return nil, xerrors.Errorf("unrecognized field %s in BTF map definition", member.Name) return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name)
} }
} }
keySize, err := btf.Sizeof(btf.MapKey(btfMap))
if err != nil {
return nil, xerrors.Errorf("can't get size of BTF key: %w", err)
}
valueSize, err := btf.Sizeof(btf.MapValue(btfMap))
if err != nil {
return nil, xerrors.Errorf("can't get size of BTF value: %w", err)
}
return &MapSpec{ return &MapSpec{
Type: MapType(mapType), Type: MapType(mapType),
KeySize: uint32(keySize), KeySize: keySize,
ValueSize: uint32(valueSize), ValueSize: valueSize,
MaxEntries: maxEntries, MaxEntries: maxEntries,
Flags: flags, Flags: flags,
BTF: btfMap, BTF: btfMap,
@ -511,12 +554,12 @@ func mapSpecFromBTF(btfMap *btf.Map, btfMapMembers []btf.Member) (*MapSpec, erro
func uintFromBTF(typ btf.Type) (uint32, error) { func uintFromBTF(typ btf.Type) (uint32, error) {
ptr, ok := typ.(*btf.Pointer) ptr, ok := typ.(*btf.Pointer)
if !ok { if !ok {
return 0, xerrors.Errorf("not a pointer: %v", typ) return 0, fmt.Errorf("not a pointer: %v", typ)
} }
arr, ok := ptr.Target.(*btf.Array) arr, ok := ptr.Target.(*btf.Array)
if !ok { if !ok {
return 0, xerrors.Errorf("not a pointer to array: %v", typ) return 0, fmt.Errorf("not a pointer to array: %v", typ)
} }
return arr.Nelems, nil return arr.Nelems, nil
@ -524,7 +567,7 @@ func uintFromBTF(typ btf.Type) (uint32, error) {
func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error { func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error {
if spec == nil { if spec == nil {
return xerrors.New("data sections require BTF") return errors.New("data sections require BTF, make sure all consts are marked as static")
} }
for _, sec := range dataSections { for _, sec := range dataSections {
@ -535,11 +578,11 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[e
data, err := sec.Data() data, err := sec.Data()
if err != nil { if err != nil {
return xerrors.Errorf("data section %s: can't get contents: %w", sec.Name, err) return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err)
} }
if uint64(len(data)) > math.MaxUint32 { if uint64(len(data)) > math.MaxUint32 {
return xerrors.Errorf("data section %s: contents exceed maximum size", sec.Name) return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name)
} }
mapSpec := &MapSpec{ mapSpec := &MapSpec{
@ -566,91 +609,79 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec, dataSections map[e
return nil return nil
} }
func getProgType(v string) (ProgramType, AttachType) { func getProgType(sectionName string) (ProgramType, AttachType, string) {
types := map[string]ProgramType{ types := map[string]struct {
// From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568 progType ProgramType
"socket": SocketFilter, attachType AttachType
"seccomp": SocketFilter, }{
"kprobe/": Kprobe, // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c
"uprobe/": Kprobe, "socket": {SocketFilter, AttachNone},
"kretprobe/": Kprobe, "seccomp": {SocketFilter, AttachNone},
"uretprobe/": Kprobe, "kprobe/": {Kprobe, AttachNone},
"tracepoint/": TracePoint, "uprobe/": {Kprobe, AttachNone},
"raw_tracepoint/": RawTracepoint, "kretprobe/": {Kprobe, AttachNone},
"xdp": XDP, "uretprobe/": {Kprobe, AttachNone},
"perf_event": PerfEvent, "tracepoint/": {TracePoint, AttachNone},
"lwt_in": LWTIn, "raw_tracepoint/": {RawTracepoint, AttachNone},
"lwt_out": LWTOut, "xdp": {XDP, AttachNone},
"lwt_xmit": LWTXmit, "perf_event": {PerfEvent, AttachNone},
"lwt_seg6local": LWTSeg6Local, "lwt_in": {LWTIn, AttachNone},
"sockops": SockOps, "lwt_out": {LWTOut, AttachNone},
"sk_skb": SkSKB, "lwt_xmit": {LWTXmit, AttachNone},
"sk_msg": SkMsg, "lwt_seg6local": {LWTSeg6Local, AttachNone},
"lirc_mode2": LircMode2, "sockops": {SockOps, AttachCGroupSockOps},
"flow_dissector": FlowDissector, "sk_skb/stream_parser": {SkSKB, AttachSkSKBStreamParser},
"sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamParser},
"sk_msg": {SkMsg, AttachSkSKBStreamVerdict},
"lirc_mode2": {LircMode2, AttachLircMode2},
"flow_dissector": {FlowDissector, AttachFlowDissector},
"iter/": {Tracing, AttachTraceIter},
"cgroup_skb/": CGroupSKB, "cgroup_skb/ingress": {CGroupSKB, AttachCGroupInetIngress},
"cgroup/dev": CGroupDevice, "cgroup_skb/egress": {CGroupSKB, AttachCGroupInetEgress},
"cgroup/skb": CGroupSKB, "cgroup/dev": {CGroupDevice, AttachCGroupDevice},
"cgroup/sock": CGroupSock, "cgroup/skb": {CGroupSKB, AttachNone},
"cgroup/post_bind": CGroupSock, "cgroup/sock": {CGroupSock, AttachCGroupInetSockCreate},
"cgroup/bind": CGroupSockAddr, "cgroup/post_bind4": {CGroupSock, AttachCGroupInet4PostBind},
"cgroup/connect": CGroupSockAddr, "cgroup/post_bind6": {CGroupSock, AttachCGroupInet6PostBind},
"cgroup/sendmsg": CGroupSockAddr, "cgroup/bind4": {CGroupSockAddr, AttachCGroupInet4Bind},
"cgroup/recvmsg": CGroupSockAddr, "cgroup/bind6": {CGroupSockAddr, AttachCGroupInet6Bind},
"cgroup/sysctl": CGroupSysctl, "cgroup/connect4": {CGroupSockAddr, AttachCGroupInet4Connect},
"cgroup/getsockopt": CGroupSockopt, "cgroup/connect6": {CGroupSockAddr, AttachCGroupInet6Connect},
"cgroup/setsockopt": CGroupSockopt, "cgroup/sendmsg4": {CGroupSockAddr, AttachCGroupUDP4Sendmsg},
"classifier": SchedCLS, "cgroup/sendmsg6": {CGroupSockAddr, AttachCGroupUDP6Sendmsg},
"action": SchedACT, "cgroup/recvmsg4": {CGroupSockAddr, AttachCGroupUDP4Recvmsg},
} "cgroup/recvmsg6": {CGroupSockAddr, AttachCGroupUDP6Recvmsg},
attachTypes := map[string]AttachType{ "cgroup/sysctl": {CGroupSysctl, AttachCGroupSysctl},
"cgroup_skb/ingress": AttachCGroupInetIngress, "cgroup/getsockopt": {CGroupSockopt, AttachCGroupGetsockopt},
"cgroup_skb/egress": AttachCGroupInetEgress, "cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt},
"cgroup/sock": AttachCGroupInetSockCreate, "classifier": {SchedCLS, AttachNone},
"cgroup/post_bind4": AttachCGroupInet4PostBind, "action": {SchedACT, AttachNone},
"cgroup/post_bind6": AttachCGroupInet6PostBind,
"cgroup/dev": AttachCGroupDevice,
"sockops": AttachCGroupSockOps,
"sk_skb/stream_parser": AttachSkSKBStreamParser,
"sk_skb/stream_verdict": AttachSkSKBStreamVerdict,
"sk_msg": AttachSkSKBStreamVerdict,
"lirc_mode2": AttachLircMode2,
"flow_dissector": AttachFlowDissector,
"cgroup/bind4": AttachCGroupInet4Bind,
"cgroup/bind6": AttachCGroupInet6Bind,
"cgroup/connect4": AttachCGroupInet4Connect,
"cgroup/connect6": AttachCGroupInet6Connect,
"cgroup/sendmsg4": AttachCGroupUDP4Sendmsg,
"cgroup/sendmsg6": AttachCGroupUDP6Sendmsg,
"cgroup/recvmsg4": AttachCGroupUDP4Recvmsg,
"cgroup/recvmsg6": AttachCGroupUDP6Recvmsg,
"cgroup/sysctl": AttachCGroupSysctl,
"cgroup/getsockopt": AttachCGroupGetsockopt,
"cgroup/setsockopt": AttachCGroupSetsockopt,
}
attachType := AttachNone
for k, t := range attachTypes {
if strings.HasPrefix(v, k) {
attachType = t
}
} }
for k, t := range types { for prefix, t := range types {
if strings.HasPrefix(v, k) { if !strings.HasPrefix(sectionName, prefix) {
return t, attachType continue
} }
if !strings.HasSuffix(prefix, "/") {
return t.progType, t.attachType, ""
}
return t.progType, t.attachType, sectionName[len(prefix):]
} }
return UnspecifiedProgram, AttachNone
return UnspecifiedProgram, AttachNone, ""
} }
func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, error) { func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (map[elf.SectionIndex]map[uint64]elf.Symbol, map[elf.SectionIndex]bool, error) {
result := make(map[elf.SectionIndex]map[uint64]elf.Symbol) result := make(map[elf.SectionIndex]map[uint64]elf.Symbol)
targets := make(map[elf.SectionIndex]bool)
for idx, sec := range sections { for idx, sec := range sections {
rels := make(map[uint64]elf.Symbol) rels := make(map[uint64]elf.Symbol)
if sec.Entsize < 16 { if sec.Entsize < 16 {
return nil, xerrors.Errorf("section %s: relocations are less than 16 bytes", sec.Name) return nil, nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name)
} }
r := sec.Open() r := sec.Open()
@ -659,20 +690,22 @@ func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (
var rel elf.Rel64 var rel elf.Rel64
if binary.Read(ent, ec.ByteOrder, &rel) != nil { if binary.Read(ent, ec.ByteOrder, &rel) != nil {
return nil, xerrors.Errorf("can't parse relocation at offset %v", off) return nil, nil, fmt.Errorf("can't parse relocation at offset %v", off)
} }
symNo := int(elf.R_SYM64(rel.Info) - 1) symNo := int(elf.R_SYM64(rel.Info) - 1)
if symNo >= len(ec.symbols) { if symNo >= len(ec.symbols) {
return nil, xerrors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo) return nil, nil, fmt.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo)
} }
symbol := ec.symbols[symNo]
targets[symbol.Section] = true
rels[rel.Off] = ec.symbols[symNo] rels[rel.Off] = ec.symbols[symNo]
} }
result[idx] = rels result[idx] = rels
} }
return result, nil return result, targets, nil
} }
func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]elf.Symbol { func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]elf.Symbol {

View File

@ -1,8 +1,5 @@
module github.com/cilium/ebpf module github.com/cilium/ebpf
go 1.12 go 1.13
require ( require golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
)

View File

@ -1,6 +1,2 @@
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7 h1:HmbHVPwrPEKPGLAcHSrMe6+hqSUlvZU0rab6x5EXfGU=
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -4,23 +4,27 @@ import (
"bytes" "bytes"
"debug/elf" "debug/elf"
"encoding/binary" "encoding/binary"
"errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math" "math"
"os"
"reflect" "reflect"
"sync"
"unsafe" "unsafe"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
) )
const btfMagic = 0xeB9F const btfMagic = 0xeB9F
// Errors returned by BTF functions. // Errors returned by BTF functions.
var ( var (
ErrNotSupported = internal.ErrNotSupported ErrNotSupported = internal.ErrNotSupported
ErrNotFound = errors.New("not found")
ErrNoExtendedInfo = errors.New("no extended info")
) )
// Spec represents decoded BTF. // Spec represents decoded BTF.
@ -30,6 +34,7 @@ type Spec struct {
types map[string][]Type types map[string][]Type
funcInfos map[string]extInfo funcInfos map[string]extInfo
lineInfos map[string]extInfo lineInfos map[string]extInfo
byteOrder binary.ByteOrder
} }
type btfHeader struct { type btfHeader struct {
@ -72,7 +77,7 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
} }
if sec.Size > math.MaxUint32 { if sec.Size > math.MaxUint32 {
return nil, xerrors.Errorf("section %s exceeds maximum size", sec.Name) return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
} }
sectionSizes[sec.Name] = uint32(sec.Size) sectionSizes[sec.Name] = uint32(sec.Size)
@ -85,7 +90,7 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
symbols, err := file.Symbols() symbols, err := file.Symbols()
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't read symbols: %v", err) return nil, fmt.Errorf("can't read symbols: %v", err)
} }
variableOffsets := make(map[variable]uint32) variableOffsets := make(map[variable]uint32)
@ -101,13 +106,31 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
} }
if symbol.Value > math.MaxUint32 { if symbol.Value > math.MaxUint32 {
return nil, xerrors.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name) return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name)
} }
variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value) variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value)
} }
rawTypes, rawStrings, err := parseBTF(btfSection.Open(), file.ByteOrder) spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets)
if err != nil {
return nil, err
}
if btfExtSection == nil {
return spec, nil
}
spec.funcInfos, spec.lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
if err != nil {
return nil, fmt.Errorf("can't read ext info: %w", err)
}
return spec, nil
}
func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
rawTypes, rawStrings, err := parseBTF(btf, bo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -122,76 +145,99 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
return nil, err return nil, err
} }
var (
funcInfos = make(map[string]extInfo)
lineInfos = make(map[string]extInfo)
)
if btfExtSection != nil {
funcInfos, lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, rawStrings)
if err != nil {
return nil, xerrors.Errorf("can't read ext info: %w", err)
}
}
return &Spec{ return &Spec{
rawTypes: rawTypes, rawTypes: rawTypes,
types: types, types: types,
strings: rawStrings, strings: rawStrings,
funcInfos: funcInfos, byteOrder: bo,
lineInfos: lineInfos,
}, nil }, nil
} }
var kernelBTF struct {
sync.Mutex
*Spec
}
// LoadKernelSpec returns the current kernel's BTF information.
//
// Requires a >= 5.5 kernel with CONFIG_DEBUG_INFO_BTF enabled. Returns
// ErrNotSupported if BTF is not enabled.
func LoadKernelSpec() (*Spec, error) {
kernelBTF.Lock()
defer kernelBTF.Unlock()
if kernelBTF.Spec != nil {
return kernelBTF.Spec, nil
}
var err error
kernelBTF.Spec, err = loadKernelSpec()
return kernelBTF.Spec, err
}
func loadKernelSpec() (*Spec, error) {
fh, err := os.Open("/sys/kernel/btf/vmlinux")
if os.IsNotExist(err) {
return nil, fmt.Errorf("can't open kernel BTF at /sys/kernel/btf/vmlinux: %w", ErrNotFound)
}
if err != nil {
return nil, fmt.Errorf("can't read kernel BTF: %s", err)
}
defer fh.Close()
return loadNakedSpec(fh, internal.NativeEndian, nil, nil)
}
func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) { func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) {
rawBTF, err := ioutil.ReadAll(btf) rawBTF, err := ioutil.ReadAll(btf)
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("can't read BTF: %v", err) return nil, nil, fmt.Errorf("can't read BTF: %v", err)
} }
rd := bytes.NewReader(rawBTF) rd := bytes.NewReader(rawBTF)
var header btfHeader var header btfHeader
if err := binary.Read(rd, bo, &header); err != nil { if err := binary.Read(rd, bo, &header); err != nil {
return nil, nil, xerrors.Errorf("can't read header: %v", err) return nil, nil, fmt.Errorf("can't read header: %v", err)
} }
if header.Magic != btfMagic { if header.Magic != btfMagic {
return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic) return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic)
} }
if header.Version != 1 { if header.Version != 1 {
return nil, nil, xerrors.Errorf("unexpected version %v", header.Version) return nil, nil, fmt.Errorf("unexpected version %v", header.Version)
} }
if header.Flags != 0 { if header.Flags != 0 {
return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags) return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags)
} }
remainder := int64(header.HdrLen) - int64(binary.Size(&header)) remainder := int64(header.HdrLen) - int64(binary.Size(&header))
if remainder < 0 { if remainder < 0 {
return nil, nil, xerrors.New("header is too short") return nil, nil, errors.New("header is too short")
} }
if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil { if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil {
return nil, nil, xerrors.Errorf("header padding: %v", err) return nil, nil, fmt.Errorf("header padding: %v", err)
} }
if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil { if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to start of string section: %v", err) return nil, nil, fmt.Errorf("can't seek to start of string section: %v", err)
} }
rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen))) rawStrings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen)))
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("can't read type names: %w", err) return nil, nil, fmt.Errorf("can't read type names: %w", err)
} }
if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil { if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to start of type section: %v", err) return nil, nil, fmt.Errorf("can't seek to start of type section: %v", err)
} }
rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo) rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo)
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("can't read types: %w", err) return nil, nil, fmt.Errorf("can't read types: %w", err)
} }
return rawTypes, rawStrings, nil return rawTypes, rawStrings, nil
@ -213,9 +259,13 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s
return err return err
} }
if name == ".kconfig" || name == ".ksym" {
return fmt.Errorf("reference to %s: %w", name, ErrNotSupported)
}
size, ok := sectionSizes[name] size, ok := sectionSizes[name]
if !ok { if !ok {
return xerrors.Errorf("data section %s: missing size", name) return fmt.Errorf("data section %s: missing size", name)
} }
rawTypes[i].SizeType = size rawTypes[i].SizeType = size
@ -224,17 +274,17 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s
for j, secInfo := range secinfos { for j, secInfo := range secinfos {
id := int(secInfo.Type - 1) id := int(secInfo.Type - 1)
if id >= len(rawTypes) { if id >= len(rawTypes) {
return xerrors.Errorf("data section %s: invalid type id %d for variable %d", name, id, j) return fmt.Errorf("data section %s: invalid type id %d for variable %d", name, id, j)
} }
varName, err := rawStrings.Lookup(rawTypes[id].NameOff) varName, err := rawStrings.Lookup(rawTypes[id].NameOff)
if err != nil { if err != nil {
return xerrors.Errorf("data section %s: can't get name for type %d: %w", name, id, err) return fmt.Errorf("data section %s: can't get name for type %d: %w", name, id, err)
} }
offset, ok := variableOffsets[variable{name, varName}] offset, ok := variableOffsets[variable{name, varName}]
if !ok { if !ok {
return xerrors.Errorf("data section %s: missing offset for variable %s", name, varName) return fmt.Errorf("data section %s: missing offset for variable %s", name, varName)
} }
secinfos[j].Offset = offset secinfos[j].Offset = offset
@ -244,7 +294,12 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s
return nil return nil
} }
func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) { type marshalOpts struct {
ByteOrder binary.ByteOrder
StripFuncLinkage bool
}
func (s *Spec) marshal(opts marshalOpts) ([]byte, error) {
var ( var (
buf bytes.Buffer buf bytes.Buffer
header = new(btfHeader) header = new(btfHeader)
@ -256,9 +311,14 @@ func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) {
_, _ = buf.Write(make([]byte, headerLen)) _, _ = buf.Write(make([]byte, headerLen))
// Write type section, just after the header. // Write type section, just after the header.
for _, typ := range s.rawTypes { for _, raw := range s.rawTypes {
if err := typ.Marshal(&buf, bo); err != nil { switch {
return nil, xerrors.Errorf("can't marshal BTF: %w", err) case opts.StripFuncLinkage && raw.Kind() == kindFunc:
raw.SetLinkage(linkageStatic)
}
if err := raw.Marshal(&buf, opts.ByteOrder); err != nil {
return nil, fmt.Errorf("can't marshal BTF: %w", err)
} }
} }
@ -280,9 +340,9 @@ func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) {
} }
raw := buf.Bytes() raw := buf.Bytes()
err := binary.Write(sliceWriter(raw[:headerLen]), bo, header) err := binary.Write(sliceWriter(raw[:headerLen]), opts.ByteOrder, header)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't write header: %v", err) return nil, fmt.Errorf("can't write header: %v", err)
} }
return raw, nil return raw, nil
@ -292,7 +352,7 @@ type sliceWriter []byte
func (sw sliceWriter) Write(p []byte) (int, error) { func (sw sliceWriter) Write(p []byte) (int, error) {
if len(p) != len(sw) { if len(p) != len(sw) {
return 0, xerrors.New("size doesn't match") return 0, errors.New("size doesn't match")
} }
return copy(sw, p), nil return copy(sw, p), nil
@ -302,17 +362,22 @@ func (sw sliceWriter) Write(p []byte) (int, error) {
// //
// Length is the number of bytes in the raw BPF instruction stream. // Length is the number of bytes in the raw BPF instruction stream.
// //
// Returns an error if there is no BTF. // Returns an error which may wrap ErrNoExtendedInfo if the Spec doesn't
// contain extended BTF info.
func (s *Spec) Program(name string, length uint64) (*Program, error) { func (s *Spec) Program(name string, length uint64) (*Program, error) {
if length == 0 { if length == 0 {
return nil, xerrors.New("length musn't be zero") return nil, errors.New("length musn't be zero")
}
if s.funcInfos == nil && s.lineInfos == nil {
return nil, fmt.Errorf("BTF for section %s: %w", name, ErrNoExtendedInfo)
} }
funcInfos, funcOK := s.funcInfos[name] funcInfos, funcOK := s.funcInfos[name]
lineInfos, lineOK := s.lineInfos[name] lineInfos, lineOK := s.lineInfos[name]
if !funcOK && !lineOK { if !funcOK && !lineOK {
return nil, xerrors.Errorf("no BTF for program %s", name) return nil, fmt.Errorf("no extended BTF info for section %s", name)
} }
return &Program{s, length, funcInfos, lineInfos}, nil return &Program{s, length, funcInfos, lineInfos}, nil
@ -329,7 +394,7 @@ func (s *Spec) Map(name string) (*Map, []Member, error) {
mapStruct, ok := mapVar.Type.(*Struct) mapStruct, ok := mapVar.Type.(*Struct)
if !ok { if !ok {
return nil, nil, xerrors.Errorf("expected struct, have %s", mapVar.Type) return nil, nil, fmt.Errorf("expected struct, have %s", mapVar.Type)
} }
var key, value Type var key, value Type
@ -344,11 +409,11 @@ func (s *Spec) Map(name string) (*Map, []Member, error) {
} }
if key == nil { if key == nil {
return nil, nil, xerrors.Errorf("map %s: missing 'key' in type", name) key = (*Void)(nil)
} }
if value == nil { if value == nil {
return nil, nil, xerrors.Errorf("map %s: missing 'value' in type", name) value = (*Void)(nil)
} }
return &Map{s, key, value}, mapStruct.Members, nil return &Map{s, key, value}, mapStruct.Members, nil
@ -358,19 +423,18 @@ func (s *Spec) Map(name string) (*Map, []Member, error) {
func (s *Spec) Datasec(name string) (*Map, error) { func (s *Spec) Datasec(name string) (*Map, error) {
var datasec Datasec var datasec Datasec
if err := s.FindType(name, &datasec); err != nil { if err := s.FindType(name, &datasec); err != nil {
return nil, xerrors.Errorf("data section %s: can't get BTF: %w", name, err) return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err)
} }
return &Map{s, &Void{}, &datasec}, nil return &Map{s, &Void{}, &datasec}, nil
} }
var errNotFound = xerrors.New("not found")
// FindType searches for a type with a specific name. // FindType searches for a type with a specific name.
// //
// hint determines the type of the returned Type. // hint determines the type of the returned Type.
// //
// Returns an error if there is no or multiple matches. // Returns an error wrapping ErrNotFound if no matching
// type exists in spec.
func (s *Spec) FindType(name string, typ Type) error { func (s *Spec) FindType(name string, typ Type) error {
var ( var (
wanted = reflect.TypeOf(typ) wanted = reflect.TypeOf(typ)
@ -383,14 +447,14 @@ func (s *Spec) FindType(name string, typ Type) error {
} }
if candidate != nil { if candidate != nil {
return xerrors.Errorf("type %s: multiple candidates for %T", name, typ) return fmt.Errorf("type %s: multiple candidates for %T", name, typ)
} }
candidate = typ candidate = typ
} }
if candidate == nil { if candidate == nil {
return xerrors.Errorf("type %s: %w", name, errNotFound) return fmt.Errorf("type %s: %w", name, ErrNotFound)
} }
value := reflect.Indirect(reflect.ValueOf(copyType(candidate))) value := reflect.Indirect(reflect.ValueOf(copyType(candidate)))
@ -411,13 +475,20 @@ func NewHandle(spec *Spec) (*Handle, error) {
return nil, err return nil, err
} }
btf, err := spec.marshal(internal.NativeEndian) if spec.byteOrder != internal.NativeEndian {
return nil, fmt.Errorf("can't load %s BTF on %s", spec.byteOrder, internal.NativeEndian)
}
btf, err := spec.marshal(marshalOpts{
ByteOrder: internal.NativeEndian,
StripFuncLinkage: haveFuncLinkage() != nil,
})
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't marshal BTF: %w", err) return nil, fmt.Errorf("can't marshal BTF: %w", err)
} }
if uint64(len(btf)) > math.MaxUint32 { if uint64(len(btf)) > math.MaxUint32 {
return nil, xerrors.New("BTF exceeds the maximum size") return nil, errors.New("BTF exceeds the maximum size")
} }
attr := &bpfLoadBTFAttr{ attr := &bpfLoadBTFAttr{
@ -501,12 +572,12 @@ func ProgramSpec(s *Program) *Spec {
func ProgramAppend(s, other *Program) error { func ProgramAppend(s, other *Program) error {
funcInfos, err := s.funcInfos.append(other.funcInfos, s.length) funcInfos, err := s.funcInfos.append(other.funcInfos, s.length)
if err != nil { if err != nil {
return xerrors.Errorf("func infos: %w", err) return fmt.Errorf("func infos: %w", err)
} }
lineInfos, err := s.lineInfos.append(other.lineInfos, s.length) lineInfos, err := s.lineInfos.append(other.lineInfos, s.length)
if err != nil { if err != nil {
return xerrors.Errorf("line infos: %w", err) return fmt.Errorf("line infos: %w", err)
} }
s.length += other.length s.length += other.length
@ -560,26 +631,36 @@ func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) {
return internal.NewFD(uint32(fd)), nil return internal.NewFD(uint32(fd)), nil
} }
func minimalBTF(bo binary.ByteOrder) []byte { func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte {
const minHeaderLength = 24 const minHeaderLength = 24
typesLen := uint32(binary.Size(types))
header := btfHeader{
Magic: btfMagic,
Version: 1,
HdrLen: minHeaderLength,
TypeOff: 0,
TypeLen: typesLen,
StringOff: typesLen,
StringLen: uint32(len(strings)),
}
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, error) {
var ( var (
types struct { types struct {
Integer btfType Integer btfType
Var btfType Var btfType
btfVar struct{ Linkage uint32 } btfVar struct{ Linkage uint32 }
} }
typLen = uint32(binary.Size(&types))
strings = []byte{0, 'a', 0} 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 // We use a BTF_KIND_VAR here, to make sure that
@ -590,16 +671,8 @@ func minimalBTF(bo binary.ByteOrder) []byte {
types.Var.SetKind(kindVar) types.Var.SetKind(kindVar)
types.Var.SizeType = 1 types.Var.SizeType = 1
buf := new(bytes.Buffer) btf := marshalBTF(&types, strings, internal.NativeEndian)
_ = 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{ fd, err := bpfLoadBTF(&bpfLoadBTFAttr{
btf: internal.NewSlicePointer(btf), btf: internal.NewSlicePointer(btf),
btfSize: uint32(len(btf)), btfSize: uint32(len(btf)),
@ -609,5 +682,35 @@ var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool {
} }
// Check for EINVAL specifically, rather than err != nil since we // Check for EINVAL specifically, rather than err != nil since we
// otherwise misdetect due to insufficient permissions. // otherwise misdetect due to insufficient permissions.
return !xerrors.Is(err, unix.EINVAL) return !errors.Is(err, unix.EINVAL), nil
})
var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() (bool, error) {
var (
types struct {
FuncProto btfType
Func btfType
}
strings = []byte{0, 'a', 0}
)
types.FuncProto.SetKind(kindFuncProto)
types.Func.SetKind(kindFunc)
types.Func.SizeType = 1 // aka FuncProto
types.Func.NameOff = 1
types.Func.SetLinkage(linkageGlobal)
btf := marshalBTF(&types, strings, 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.Is(err, unix.EINVAL), nil
}) })

View File

@ -4,8 +4,6 @@ import (
"encoding/binary" "encoding/binary"
"fmt" "fmt"
"io" "io"
"golang.org/x/xerrors"
) )
// btfKind describes a Type. // btfKind describes a Type.
@ -33,6 +31,14 @@ const (
kindDatasec kindDatasec
) )
type btfFuncLinkage uint8
const (
linkageStatic btfFuncLinkage = iota
linkageGlobal
linkageExtern
)
const ( const (
btfTypeKindShift = 24 btfTypeKindShift = 24
btfTypeKindLen = 4 btfTypeKindLen = 4
@ -44,7 +50,7 @@ const (
type btfType struct { type btfType struct {
NameOff uint32 NameOff uint32
/* "info" bits arrangement /* "info" bits arrangement
* bits 0-15: vlen (e.g. # of struct's members) * bits 0-15: vlen (e.g. # of struct's members), linkage
* bits 16-23: unused * bits 16-23: unused
* bits 24-27: kind (e.g. int, ptr, array...etc) * bits 24-27: kind (e.g. int, ptr, array...etc)
* bits 28-30: unused * bits 28-30: unused
@ -130,6 +136,14 @@ func (bt *btfType) SetVlen(vlen int) {
bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift) bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift)
} }
func (bt *btfType) Linkage() btfFuncLinkage {
return btfFuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift))
}
func (bt *btfType) SetLinkage(linkage btfFuncLinkage) {
bt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift)
}
func (bt *btfType) Type() TypeID { func (bt *btfType) Type() TypeID {
// TODO: Panic here if wrong kind? // TODO: Panic here if wrong kind?
return TypeID(bt.SizeType) return TypeID(bt.SizeType)
@ -179,6 +193,16 @@ type btfVariable struct {
Linkage uint32 Linkage uint32
} }
type btfEnum struct {
NameOff uint32
Val int32
}
type btfParam struct {
NameOff uint32
Type TypeID
}
func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
var ( var (
header btfType header btfType
@ -189,14 +213,13 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
if err := binary.Read(r, bo, &header); err == io.EOF { if err := binary.Read(r, bo, &header); err == io.EOF {
return types, nil return types, nil
} else if err != nil { } else if err != nil {
return nil, xerrors.Errorf("can't read type info for id %v: %v", id, err) return nil, fmt.Errorf("can't read type info for id %v: %v", id, err)
} }
var data interface{} var data interface{}
switch header.Kind() { switch header.Kind() {
case kindInt: case kindInt:
// sizeof(uint32) data = new(uint32)
data = make([]byte, 4)
case kindPointer: case kindPointer:
case kindArray: case kindArray:
data = new(btfArray) data = new(btfArray)
@ -205,8 +228,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
case kindUnion: case kindUnion:
data = make([]btfMember, header.Vlen()) data = make([]btfMember, header.Vlen())
case kindEnum: case kindEnum:
// sizeof(struct btf_enum) data = make([]btfEnum, header.Vlen())
data = make([]byte, header.Vlen()*4*2)
case kindForward: case kindForward:
case kindTypedef: case kindTypedef:
case kindVolatile: case kindVolatile:
@ -214,14 +236,13 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
case kindRestrict: case kindRestrict:
case kindFunc: case kindFunc:
case kindFuncProto: case kindFuncProto:
// sizeof(struct btf_param) data = make([]btfParam, header.Vlen())
data = make([]byte, header.Vlen()*4*2)
case kindVar: case kindVar:
data = new(btfVariable) data = new(btfVariable)
case kindDatasec: case kindDatasec:
data = make([]btfVarSecinfo, header.Vlen()) data = make([]btfVarSecinfo, header.Vlen())
default: default:
return nil, xerrors.Errorf("type id %v: unknown kind: %v", id, header.Kind()) return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
} }
if data == nil { if data == nil {
@ -230,7 +251,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
} }
if err := binary.Read(r, bo, data); err != nil { if err := binary.Read(r, bo, data); err != nil {
return nil, xerrors.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err) return nil, fmt.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err)
} }
types = append(types, rawType{header, data}) types = append(types, rawType{header, data})

View File

@ -3,13 +3,13 @@ package btf
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"golang.org/x/xerrors"
) )
type btfExtHeader struct { type btfExtHeader struct {
@ -27,49 +27,49 @@ type btfExtHeader struct {
func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) { func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) {
var header btfExtHeader var header btfExtHeader
if err := binary.Read(r, bo, &header); err != nil { if err := binary.Read(r, bo, &header); err != nil {
return nil, nil, xerrors.Errorf("can't read header: %v", err) return nil, nil, fmt.Errorf("can't read header: %v", err)
} }
if header.Magic != btfMagic { if header.Magic != btfMagic {
return nil, nil, xerrors.Errorf("incorrect magic value %v", header.Magic) return nil, nil, fmt.Errorf("incorrect magic value %v", header.Magic)
} }
if header.Version != 1 { if header.Version != 1 {
return nil, nil, xerrors.Errorf("unexpected version %v", header.Version) return nil, nil, fmt.Errorf("unexpected version %v", header.Version)
} }
if header.Flags != 0 { if header.Flags != 0 {
return nil, nil, xerrors.Errorf("unsupported flags %v", header.Flags) return nil, nil, fmt.Errorf("unsupported flags %v", header.Flags)
} }
remainder := int64(header.HdrLen) - int64(binary.Size(&header)) remainder := int64(header.HdrLen) - int64(binary.Size(&header))
if remainder < 0 { if remainder < 0 {
return nil, nil, xerrors.New("header is too short") return nil, nil, errors.New("header is too short")
} }
// Of course, the .BTF.ext header has different semantics than the // Of course, the .BTF.ext header has different semantics than the
// .BTF ext header. We need to ignore non-null values. // .BTF ext header. We need to ignore non-null values.
_, err = io.CopyN(ioutil.Discard, r, remainder) _, err = io.CopyN(ioutil.Discard, r, remainder)
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("header padding: %v", err) return nil, nil, fmt.Errorf("header padding: %v", err)
} }
if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil { if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to function info section: %v", err) return nil, nil, fmt.Errorf("can't seek to function info section: %v", err)
} }
funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings) funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings)
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("function info: %w", err) return nil, nil, fmt.Errorf("function info: %w", err)
} }
if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil { if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil {
return nil, nil, xerrors.Errorf("can't seek to line info section: %v", err) return nil, nil, fmt.Errorf("can't seek to line info section: %v", err)
} }
lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings) lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings)
if err != nil { if err != nil {
return nil, nil, xerrors.Errorf("line info: %w", err) return nil, nil, fmt.Errorf("line info: %w", err)
} }
return funcInfo, lineInfo, nil return funcInfo, lineInfo, nil
@ -92,7 +92,7 @@ type extInfo struct {
func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
if other.recordSize != ei.recordSize { if other.recordSize != ei.recordSize {
return extInfo{}, xerrors.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) return extInfo{}, fmt.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 := make([]extInfoRecord, 0, len(ei.records)+len(other.records))
@ -117,7 +117,7 @@ func (ei extInfo) MarshalBinary() ([]byte, error) {
// while the ELF tracks it in bytes. // while the ELF tracks it in bytes.
insnOff := uint32(info.InsnOff / asm.InstructionSize) insnOff := uint32(info.InsnOff / asm.InstructionSize)
if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil { if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil {
return nil, xerrors.Errorf("can't write instruction offset: %v", err) return nil, fmt.Errorf("can't write instruction offset: %v", err)
} }
buf.Write(info.Opaque) buf.Write(info.Opaque)
@ -129,12 +129,12 @@ func (ei extInfo) MarshalBinary() ([]byte, error) {
func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) { func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) {
var recordSize uint32 var recordSize uint32
if err := binary.Read(r, bo, &recordSize); err != nil { if err := binary.Read(r, bo, &recordSize); err != nil {
return nil, xerrors.Errorf("can't read record size: %v", err) return nil, fmt.Errorf("can't read record size: %v", err)
} }
if recordSize < 4 { if recordSize < 4 {
// Need at least insnOff // Need at least insnOff
return nil, xerrors.New("record size too short") return nil, errors.New("record size too short")
} }
result := make(map[string]extInfo) result := make(map[string]extInfo)
@ -143,32 +143,32 @@ func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[st
if err := binary.Read(r, bo, &infoHeader); err == io.EOF { if err := binary.Read(r, bo, &infoHeader); err == io.EOF {
return result, nil return result, nil
} else if err != nil { } else if err != nil {
return nil, xerrors.Errorf("can't read ext info header: %v", err) return nil, fmt.Errorf("can't read ext info header: %v", err)
} }
secName, err := strings.Lookup(infoHeader.SecNameOff) secName, err := strings.Lookup(infoHeader.SecNameOff)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get section name: %w", err) return nil, fmt.Errorf("can't get section name: %w", err)
} }
if infoHeader.NumInfo == 0 { if infoHeader.NumInfo == 0 {
return nil, xerrors.Errorf("section %s has invalid number of records", secName) return nil, fmt.Errorf("section %s has invalid number of records", secName)
} }
var records []extInfoRecord var records []extInfoRecord
for i := uint32(0); i < infoHeader.NumInfo; i++ { for i := uint32(0); i < infoHeader.NumInfo; i++ {
var byteOff uint32 var byteOff uint32
if err := binary.Read(r, bo, &byteOff); err != nil { if err := binary.Read(r, bo, &byteOff); err != nil {
return nil, xerrors.Errorf("section %v: can't read extended info offset: %v", secName, err) return nil, fmt.Errorf("section %v: can't read extended info offset: %v", secName, err)
} }
buf := make([]byte, int(recordSize-4)) buf := make([]byte, int(recordSize-4))
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
return nil, xerrors.Errorf("section %v: can't read record: %v", secName, err) return nil, fmt.Errorf("section %v: can't read record: %v", secName, err)
} }
if byteOff%asm.InstructionSize != 0 { if byteOff%asm.InstructionSize != 0 {
return nil, xerrors.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff) return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff)
} }
records = append(records, extInfoRecord{uint64(byteOff), buf}) records = append(records, extInfoRecord{uint64(byteOff), buf})

View File

@ -2,10 +2,10 @@ package btf
import ( import (
"bytes" "bytes"
"errors"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"golang.org/x/xerrors"
) )
type stringTable []byte type stringTable []byte
@ -13,19 +13,19 @@ type stringTable []byte
func readStringTable(r io.Reader) (stringTable, error) { func readStringTable(r io.Reader) (stringTable, error) {
contents, err := ioutil.ReadAll(r) contents, err := ioutil.ReadAll(r)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't read string table: %v", err) return nil, fmt.Errorf("can't read string table: %v", err)
} }
if len(contents) < 1 { if len(contents) < 1 {
return nil, xerrors.New("string table is empty") return nil, errors.New("string table is empty")
} }
if contents[0] != '\x00' { if contents[0] != '\x00' {
return nil, xerrors.New("first item in string table is non-empty") return nil, errors.New("first item in string table is non-empty")
} }
if contents[len(contents)-1] != '\x00' { if contents[len(contents)-1] != '\x00' {
return nil, xerrors.New("string table isn't null terminated") return nil, errors.New("string table isn't null terminated")
} }
return stringTable(contents), nil return stringTable(contents), nil
@ -33,22 +33,22 @@ func readStringTable(r io.Reader) (stringTable, error) {
func (st stringTable) Lookup(offset uint32) (string, error) { func (st stringTable) Lookup(offset uint32) (string, error) {
if int64(offset) > int64(^uint(0)>>1) { if int64(offset) > int64(^uint(0)>>1) {
return "", xerrors.Errorf("offset %d overflows int", offset) return "", fmt.Errorf("offset %d overflows int", offset)
} }
pos := int(offset) pos := int(offset)
if pos >= len(st) { if pos >= len(st) {
return "", xerrors.Errorf("offset %d is out of bounds", offset) return "", fmt.Errorf("offset %d is out of bounds", offset)
} }
if pos > 0 && st[pos-1] != '\x00' { if pos > 0 && st[pos-1] != '\x00' {
return "", xerrors.Errorf("offset %d isn't start of a string", offset) return "", fmt.Errorf("offset %d isn't start of a string", offset)
} }
str := st[pos:] str := st[pos:]
end := bytes.IndexByte(str, '\x00') end := bytes.IndexByte(str, '\x00')
if end == -1 { if end == -1 {
return "", xerrors.Errorf("offset %d isn't null terminated", offset) return "", fmt.Errorf("offset %d isn't null terminated", offset)
} }
return string(str[:end]), nil return string(str[:end]), nil

View File

@ -1,9 +1,9 @@
package btf package btf
import ( import (
"errors"
"fmt"
"math" "math"
"golang.org/x/xerrors"
) )
const maxTypeDepth = 32 const maxTypeDepth = 32
@ -38,9 +38,10 @@ func (n Name) name() string {
// Void is the unit type of BTF. // Void is the unit type of BTF.
type Void struct{} type Void struct{}
func (v Void) ID() TypeID { return 0 } func (v *Void) ID() TypeID { return 0 }
func (v Void) copy() Type { return Void{} } func (v *Void) size() uint32 { return 0 }
func (v Void) walk(*copyStack) {} func (v *Void) copy() Type { return (*Void)(nil) }
func (v *Void) walk(*copyStack) {}
// Int is an integer of a given length. // Int is an integer of a given length.
type Int struct { type Int struct {
@ -310,7 +311,7 @@ func Sizeof(typ Type) (int, error) {
switch v := typ.(type) { switch v := typ.(type) {
case *Array: case *Array:
if n > 0 && int64(v.Nelems) > math.MaxInt64/n { if n > 0 && int64(v.Nelems) > math.MaxInt64/n {
return 0, xerrors.New("overflow") return 0, errors.New("overflow")
} }
// Arrays may be of zero length, which allows // Arrays may be of zero length, which allows
@ -336,22 +337,22 @@ func Sizeof(typ Type) (int, error) {
continue continue
default: default:
return 0, xerrors.Errorf("unrecognized type %T", typ) return 0, fmt.Errorf("unrecognized type %T", typ)
} }
if n > 0 && elem > math.MaxInt64/n { if n > 0 && elem > math.MaxInt64/n {
return 0, xerrors.New("overflow") return 0, errors.New("overflow")
} }
size := n * elem size := n * elem
if int64(int(size)) != size { if int64(int(size)) != size {
return 0, xerrors.New("overflow") return 0, errors.New("overflow")
} }
return int(size), nil return int(size), nil
} }
return 0, xerrors.New("exceeded type depth") return 0, errors.New("exceeded type depth")
} }
// copy a Type recursively. // copy a Type recursively.
@ -433,7 +434,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
for i, btfMember := range raw { for i, btfMember := range raw {
name, err := rawStrings.LookupName(btfMember.NameOff) name, err := rawStrings.LookupName(btfMember.NameOff)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get name for member %d: %w", i, err) return nil, fmt.Errorf("can't get name for member %d: %w", i, err)
} }
members = append(members, Member{ members = append(members, Member{
Name: name, Name: name,
@ -447,7 +448,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
} }
types := make([]Type, 0, len(rawTypes)) types := make([]Type, 0, len(rawTypes))
types = append(types, Void{}) types = append(types, (*Void)(nil))
namedTypes = make(map[string][]Type) namedTypes = make(map[string][]Type)
for i, raw := range rawTypes { for i, raw := range rawTypes {
@ -460,7 +461,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
name, err := rawStrings.LookupName(raw.NameOff) name, err := rawStrings.LookupName(raw.NameOff)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get name for type id %d: %w", id, err) return nil, fmt.Errorf("can't get name for type id %d: %w", id, err)
} }
switch raw.Kind() { switch raw.Kind() {
@ -484,14 +485,14 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
case kindStruct: case kindStruct:
members, err := convertMembers(raw.data.([]btfMember)) members, err := convertMembers(raw.data.([]btfMember))
if err != nil { if err != nil {
return nil, xerrors.Errorf("struct %s (id %d): %w", name, id, err) return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err)
} }
typ = &Struct{id, name, raw.Size(), members} typ = &Struct{id, name, raw.Size(), members}
case kindUnion: case kindUnion:
members, err := convertMembers(raw.data.([]btfMember)) members, err := convertMembers(raw.data.([]btfMember))
if err != nil { if err != nil {
return nil, xerrors.Errorf("union %s (id %d): %w", name, id, err) return nil, fmt.Errorf("union %s (id %d): %w", name, id, err)
} }
typ = &Union{id, name, raw.Size(), members} typ = &Union{id, name, raw.Size(), members}
@ -551,7 +552,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
typ = &Datasec{id, name, raw.SizeType, vars} typ = &Datasec{id, name, raw.SizeType, vars}
default: default:
return nil, xerrors.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) return nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
} }
types = append(types, typ) types = append(types, typ)
@ -566,7 +567,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
for _, fixup := range fixups { for _, fixup := range fixups {
i := int(fixup.id) i := int(fixup.id)
if i >= len(types) { if i >= len(types) {
return nil, xerrors.Errorf("reference to invalid type id: %d", fixup.id) return nil, fmt.Errorf("reference to invalid type id: %d", fixup.id)
} }
// Default void (id 0) to unknown // Default void (id 0) to unknown
@ -576,7 +577,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
} }
if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected { if expected := fixup.expectedKind; expected != kindUnknown && rawKind != expected {
return nil, xerrors.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind) return nil, fmt.Errorf("expected type id %d to have kind %s, found %s", fixup.id, expected, rawKind)
} }
*fixup.typ = types[i] *fixup.typ = types[i]

View File

@ -2,11 +2,11 @@ package internal
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"strings" "strings"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
) )
// ErrorWithLog returns an error that includes logs from the // ErrorWithLog returns an error that includes logs from the
@ -16,7 +16,7 @@ import (
// the log. It is used to check for truncation of the output. // the log. It is used to check for truncation of the output.
func ErrorWithLog(err error, log []byte, logErr error) error { func ErrorWithLog(err error, log []byte, logErr error) error {
logStr := strings.Trim(CString(log), "\t\r\n ") logStr := strings.Trim(CString(log), "\t\r\n ")
if xerrors.Is(logErr, unix.ENOSPC) { if errors.Is(logErr, unix.ENOSPC) {
logStr += " (truncated...)" logStr += " (truncated...)"
} }

View File

@ -1,15 +1,16 @@
package internal package internal
import ( import (
"errors"
"fmt"
"os"
"runtime" "runtime"
"strconv" "strconv"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
) )
var ErrClosedFd = xerrors.New("use of closed file descriptor") var ErrClosedFd = errors.New("use of closed file descriptor")
type FD struct { type FD struct {
raw int64 raw int64
@ -56,8 +57,13 @@ func (fd *FD) Dup() (*FD, error) {
dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0) dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't dup fd: %v", err) return nil, fmt.Errorf("can't dup fd: %v", err)
} }
return NewFD(uint32(dup)), nil return NewFD(uint32(dup)), nil
} }
func (fd *FD) File(name string) *os.File {
fd.Forget()
return os.NewFile(uintptr(fd.raw), name)
}

View File

@ -1,14 +1,13 @@
package internal package internal
import ( import (
"errors"
"fmt" "fmt"
"sync" "sync"
"golang.org/x/xerrors"
) )
// ErrNotSupported indicates that a feature is not supported by the current kernel. // ErrNotSupported indicates that a feature is not supported by the current kernel.
var ErrNotSupported = xerrors.New("not supported") var ErrNotSupported = errors.New("not supported")
// UnsupportedFeatureError is returned by FeatureTest() functions. // UnsupportedFeatureError is returned by FeatureTest() functions.
type UnsupportedFeatureError struct { type UnsupportedFeatureError struct {
@ -29,33 +28,63 @@ func (ufe *UnsupportedFeatureError) Is(target error) bool {
return target == ErrNotSupported return target == ErrNotSupported
} }
type featureTest struct {
sync.Mutex
successful bool
result error
}
// FeatureTestFn is used to determine whether the kernel supports
// a certain feature.
//
// The return values have the following semantics:
//
// err != nil: the test couldn't be executed
// err == nil && available: the feature is available
// err == nil && !available: the feature isn't available
type FeatureTestFn func() (available bool, err error)
// FeatureTest wraps a function so that it is run at most once. // FeatureTest wraps a function so that it is run at most once.
// //
// name should identify the tested feature, while version must be in the // name should identify the tested feature, while version must be in the
// form Major.Minor[.Patch]. // form Major.Minor[.Patch].
// //
// Returns a descriptive UnsupportedFeatureError if the feature is not available. // Returns an error wrapping ErrNotSupported if the feature is not supported.
func FeatureTest(name, version string, fn func() bool) func() error { func FeatureTest(name, version string, fn FeatureTestFn) func() error {
v, err := NewVersion(version) v, err := NewVersion(version)
if err != nil { if err != nil {
return func() error { return err } return func() error { return err }
} }
var ( ft := new(featureTest)
once sync.Once
result error
)
return func() error { return func() error {
once.Do(func() { ft.Lock()
if !fn() { defer ft.Unlock()
result = &UnsupportedFeatureError{
MinimumVersion: v, if ft.successful {
Name: name, return ft.result
} }
available, err := fn()
if errors.Is(err, ErrNotSupported) {
// The feature test aborted because a dependent feature
// is missing, which we should cache.
available = false
} else if err != nil {
// We couldn't execute the feature test to a point
// where it could make a determination.
// Don't cache the result, just return it.
return fmt.Errorf("can't detect support for %s: %w", name, err)
}
ft.successful = true
if !available {
ft.result = &UnsupportedFeatureError{
MinimumVersion: v,
Name: name,
} }
}) }
return result return ft.result
} }
} }
@ -69,7 +98,7 @@ func NewVersion(ver string) (Version, error) {
var major, minor, patch uint16 var major, minor, patch uint16
n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch) n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
if n < 2 { if n < 2 {
return Version{}, xerrors.Errorf("invalid version: %s", ver) return Version{}, fmt.Errorf("invalid version: %s", ver)
} }
return Version{major, minor, patch}, nil return Version{major, minor, patch}, nil
} }

View File

@ -1,6 +1,6 @@
package internal package internal
import "golang.org/x/xerrors" import "errors"
// DiscardZeroes makes sure that all written bytes are zero // DiscardZeroes makes sure that all written bytes are zero
// before discarding them. // before discarding them.
@ -9,7 +9,7 @@ type DiscardZeroes struct{}
func (DiscardZeroes) Write(p []byte) (int, error) { func (DiscardZeroes) Write(p []byte) (int, error) {
for _, b := range p { for _, b := range p {
if b != 0 { if b != 0 {
return 0, xerrors.New("encountered non-zero byte") return 0, errors.New("encountered non-zero byte")
} }
} }
return len(p), nil return len(p), nil

View File

@ -1,16 +1,61 @@
package internal package internal
import ( import (
"fmt"
"path/filepath"
"runtime" "runtime"
"unsafe" "unsafe"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
) )
//go:generate stringer -output syscall_string.go -type=BPFCmd
// BPFCmd identifies a subcommand of the bpf syscall.
type BPFCmd int
// Well known BPF commands.
const (
BPF_MAP_CREATE BPFCmd = iota
BPF_MAP_LOOKUP_ELEM
BPF_MAP_UPDATE_ELEM
BPF_MAP_DELETE_ELEM
BPF_MAP_GET_NEXT_KEY
BPF_PROG_LOAD
BPF_OBJ_PIN
BPF_OBJ_GET
BPF_PROG_ATTACH
BPF_PROG_DETACH
BPF_PROG_TEST_RUN
BPF_PROG_GET_NEXT_ID
BPF_MAP_GET_NEXT_ID
BPF_PROG_GET_FD_BY_ID
BPF_MAP_GET_FD_BY_ID
BPF_OBJ_GET_INFO_BY_FD
BPF_PROG_QUERY
BPF_RAW_TRACEPOINT_OPEN
BPF_BTF_LOAD
BPF_BTF_GET_FD_BY_ID
BPF_TASK_FD_QUERY
BPF_MAP_LOOKUP_AND_DELETE_ELEM
BPF_MAP_FREEZE
BPF_BTF_GET_NEXT_ID
BPF_MAP_LOOKUP_BATCH
BPF_MAP_LOOKUP_AND_DELETE_BATCH
BPF_MAP_UPDATE_BATCH
BPF_MAP_DELETE_BATCH
BPF_LINK_CREATE
BPF_LINK_UPDATE
BPF_LINK_GET_FD_BY_ID
BPF_LINK_GET_NEXT_ID
BPF_ENABLE_STATS
BPF_ITER_CREATE
)
// BPF wraps SYS_BPF. // BPF wraps SYS_BPF.
// //
// Any pointers contained in attr must use the Pointer type from this package. // Any pointers contained in attr must use the Pointer type from this package.
func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) { func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
runtime.KeepAlive(attr) runtime.KeepAlive(attr)
@ -21,3 +66,74 @@ func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) {
return r1, err return r1, err
} }
type BPFProgAttachAttr struct {
TargetFd uint32
AttachBpfFd uint32
AttachType uint32
AttachFlags uint32
ReplaceBpfFd uint32
}
func BPFProgAttach(attr *BPFProgAttachAttr) error {
_, err := BPF(BPF_PROG_ATTACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err
}
type BPFProgDetachAttr struct {
TargetFd uint32
AttachBpfFd uint32
AttachType uint32
}
func BPFProgDetach(attr *BPFProgDetachAttr) error {
_, err := BPF(BPF_PROG_DETACH, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err
}
type bpfObjAttr struct {
fileName Pointer
fd uint32
fileFlags uint32
}
const bpfFSType = 0xcafe4a11
// BPFObjPin wraps BPF_OBJ_PIN.
func BPFObjPin(fileName string, fd *FD) error {
dirName := filepath.Dir(fileName)
var statfs unix.Statfs_t
if err := unix.Statfs(dirName, &statfs); err != nil {
return err
}
if uint64(statfs.Type) != bpfFSType {
return fmt.Errorf("%s is not on a bpf filesystem", fileName)
}
value, err := fd.Value()
if err != nil {
return err
}
attr := bpfObjAttr{
fileName: NewStringPointer(fileName),
fd: value,
}
_, err = BPF(BPF_OBJ_PIN, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err != nil {
return fmt.Errorf("pin object %s: %w", fileName, err)
}
return nil
}
// BPFObjGet wraps BPF_OBJ_GET.
func BPFObjGet(fileName string) (*FD, error) {
attr := bpfObjAttr{
fileName: NewStringPointer(fileName),
}
ptr, err := BPF(BPF_OBJ_GET, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err != nil {
return nil, fmt.Errorf("get object %s: %w", fileName, err)
}
return NewFD(uint32(ptr)), nil
}

View File

@ -0,0 +1,56 @@
// Code generated by "stringer -output syscall_string.go -type=BPFCmd"; DO NOT EDIT.
package internal
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[BPF_MAP_CREATE-0]
_ = x[BPF_MAP_LOOKUP_ELEM-1]
_ = x[BPF_MAP_UPDATE_ELEM-2]
_ = x[BPF_MAP_DELETE_ELEM-3]
_ = x[BPF_MAP_GET_NEXT_KEY-4]
_ = x[BPF_PROG_LOAD-5]
_ = x[BPF_OBJ_PIN-6]
_ = x[BPF_OBJ_GET-7]
_ = x[BPF_PROG_ATTACH-8]
_ = x[BPF_PROG_DETACH-9]
_ = x[BPF_PROG_TEST_RUN-10]
_ = x[BPF_PROG_GET_NEXT_ID-11]
_ = x[BPF_MAP_GET_NEXT_ID-12]
_ = x[BPF_PROG_GET_FD_BY_ID-13]
_ = x[BPF_MAP_GET_FD_BY_ID-14]
_ = x[BPF_OBJ_GET_INFO_BY_FD-15]
_ = x[BPF_PROG_QUERY-16]
_ = x[BPF_RAW_TRACEPOINT_OPEN-17]
_ = x[BPF_BTF_LOAD-18]
_ = x[BPF_BTF_GET_FD_BY_ID-19]
_ = x[BPF_TASK_FD_QUERY-20]
_ = x[BPF_MAP_LOOKUP_AND_DELETE_ELEM-21]
_ = x[BPF_MAP_FREEZE-22]
_ = x[BPF_BTF_GET_NEXT_ID-23]
_ = x[BPF_MAP_LOOKUP_BATCH-24]
_ = x[BPF_MAP_LOOKUP_AND_DELETE_BATCH-25]
_ = x[BPF_MAP_UPDATE_BATCH-26]
_ = x[BPF_MAP_DELETE_BATCH-27]
_ = x[BPF_LINK_CREATE-28]
_ = x[BPF_LINK_UPDATE-29]
_ = x[BPF_LINK_GET_FD_BY_ID-30]
_ = x[BPF_LINK_GET_NEXT_ID-31]
_ = x[BPF_ENABLE_STATS-32]
_ = x[BPF_ITER_CREATE-33]
}
const _BPFCmd_name = "BPF_MAP_CREATEBPF_MAP_LOOKUP_ELEMBPF_MAP_UPDATE_ELEMBPF_MAP_DELETE_ELEMBPF_MAP_GET_NEXT_KEYBPF_PROG_LOADBPF_OBJ_PINBPF_OBJ_GETBPF_PROG_ATTACHBPF_PROG_DETACHBPF_PROG_TEST_RUNBPF_PROG_GET_NEXT_IDBPF_MAP_GET_NEXT_IDBPF_PROG_GET_FD_BY_IDBPF_MAP_GET_FD_BY_IDBPF_OBJ_GET_INFO_BY_FDBPF_PROG_QUERYBPF_RAW_TRACEPOINT_OPENBPF_BTF_LOADBPF_BTF_GET_FD_BY_IDBPF_TASK_FD_QUERYBPF_MAP_LOOKUP_AND_DELETE_ELEMBPF_MAP_FREEZEBPF_BTF_GET_NEXT_IDBPF_MAP_LOOKUP_BATCHBPF_MAP_LOOKUP_AND_DELETE_BATCHBPF_MAP_UPDATE_BATCHBPF_MAP_DELETE_BATCHBPF_LINK_CREATEBPF_LINK_UPDATEBPF_LINK_GET_FD_BY_IDBPF_LINK_GET_NEXT_IDBPF_ENABLE_STATSBPF_ITER_CREATE"
var _BPFCmd_index = [...]uint16{0, 14, 33, 52, 71, 91, 104, 115, 126, 141, 156, 173, 193, 212, 233, 253, 275, 289, 312, 324, 344, 361, 391, 405, 424, 444, 475, 495, 515, 530, 545, 566, 586, 602, 617}
func (i BPFCmd) String() string {
if i < 0 || i >= BPFCmd(len(_BPFCmd_index)-1) {
return "BPFCmd(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _BPFCmd_name[_BPFCmd_index[i]:_BPFCmd_index[i+1]]
}

View File

@ -10,11 +10,13 @@ import (
const ( const (
ENOENT = linux.ENOENT ENOENT = linux.ENOENT
EEXIST = linux.EEXIST
EAGAIN = linux.EAGAIN EAGAIN = linux.EAGAIN
ENOSPC = linux.ENOSPC ENOSPC = linux.ENOSPC
EINVAL = linux.EINVAL EINVAL = linux.EINVAL
EPOLLIN = linux.EPOLLIN EPOLLIN = linux.EPOLLIN
EINTR = linux.EINTR EINTR = linux.EINTR
EPERM = linux.EPERM
ESRCH = linux.ESRCH ESRCH = linux.ESRCH
ENODEV = linux.ENODEV ENODEV = linux.ENODEV
BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG

View File

@ -12,10 +12,12 @@ var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime
const ( const (
ENOENT = syscall.ENOENT ENOENT = syscall.ENOENT
EEXIST = syscall.EEXIST
EAGAIN = syscall.EAGAIN EAGAIN = syscall.EAGAIN
ENOSPC = syscall.ENOSPC ENOSPC = syscall.ENOSPC
EINVAL = syscall.EINVAL EINVAL = syscall.EINVAL
EINTR = syscall.EINTR EINTR = syscall.EINTR
EPERM = syscall.EPERM
ESRCH = syscall.ESRCH ESRCH = syscall.ESRCH
ENODEV = syscall.ENODEV ENODEV = syscall.ENODEV
BPF_F_RDONLY_PROG = 0 BPF_F_RDONLY_PROG = 0

View File

@ -1,10 +1,10 @@
package ebpf package ebpf
import ( import (
"fmt"
"github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/btf"
"golang.org/x/xerrors"
) )
// link resolves bpf-to-bpf calls. // link resolves bpf-to-bpf calls.
@ -28,7 +28,7 @@ func link(prog *ProgramSpec, libs []*ProgramSpec) error {
needed, err := needSection(insns, lib.Instructions) needed, err := needSection(insns, lib.Instructions)
if err != nil { if err != nil {
return xerrors.Errorf("linking %s: %w", lib.Name, err) return fmt.Errorf("linking %s: %w", lib.Name, err)
} }
if !needed { if !needed {
@ -41,7 +41,7 @@ func link(prog *ProgramSpec, libs []*ProgramSpec) error {
if prog.BTF != nil && lib.BTF != nil { if prog.BTF != nil && lib.BTF != nil {
if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil { if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil {
return xerrors.Errorf("linking BTF of %s: %w", lib.Name, err) return fmt.Errorf("linking BTF of %s: %w", lib.Name, err)
} }
} }
} }

108
vendor/github.com/cilium/ebpf/map.go generated vendored
View File

@ -1,20 +1,20 @@
package ebpf package ebpf
import ( import (
"errors"
"fmt" "fmt"
"strings" "strings"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
) )
// Errors returned by Map and MapIterator methods. // Errors returned by Map and MapIterator methods.
var ( var (
ErrKeyNotExist = xerrors.New("key does not exist") ErrKeyNotExist = errors.New("key does not exist")
ErrIterationAborted = xerrors.New("iteration aborted") ErrKeyExist = errors.New("key already exists")
ErrIterationAborted = errors.New("iteration aborted")
) )
// MapID represents the unique ID of an eBPF map // MapID represents the unique ID of an eBPF map
@ -91,7 +91,7 @@ type Map struct {
// You should not use fd after calling this function. // You should not use fd after calling this function.
func NewMapFromFD(fd int) (*Map, error) { func NewMapFromFD(fd int) (*Map, error) {
if fd < 0 { if fd < 0 {
return nil, xerrors.New("invalid fd") return nil, errors.New("invalid fd")
} }
bpfFd := internal.NewFD(uint32(fd)) bpfFd := internal.NewFD(uint32(fd))
@ -107,14 +107,18 @@ func NewMapFromFD(fd int) (*Map, error) {
// //
// Creating a map for the first time will perform feature detection // Creating a map for the first time will perform feature detection
// by creating small, temporary maps. // by creating small, temporary maps.
//
// The caller is responsible for ensuring the process' rlimit is set
// sufficiently high for locking memory during map creation. This can be done
// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMap.
func NewMap(spec *MapSpec) (*Map, error) { func NewMap(spec *MapSpec) (*Map, error) {
if spec.BTF == nil { if spec.BTF == nil {
return newMapWithBTF(spec, nil) return newMapWithBTF(spec, nil)
} }
handle, err := btf.NewHandle(btf.MapSpec(spec.BTF)) handle, err := btf.NewHandle(btf.MapSpec(spec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { if err != nil && !errors.Is(err, btf.ErrNotSupported) {
return nil, xerrors.Errorf("can't load BTF: %w", err) return nil, fmt.Errorf("can't load BTF: %w", err)
} }
return newMapWithBTF(spec, handle) return newMapWithBTF(spec, handle)
@ -126,7 +130,7 @@ func newMapWithBTF(spec *MapSpec, handle *btf.Handle) (*Map, error) {
} }
if spec.InnerMap == nil { if spec.InnerMap == nil {
return nil, xerrors.Errorf("%s requires InnerMap", spec.Type) return nil, fmt.Errorf("%s requires InnerMap", spec.Type)
} }
template, err := createMap(spec.InnerMap, nil, handle) template, err := createMap(spec.InnerMap, nil, handle)
@ -150,25 +154,25 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
} }
if abi.ValueSize != 0 && abi.ValueSize != 4 { if abi.ValueSize != 0 && abi.ValueSize != 4 {
return nil, xerrors.New("ValueSize must be zero or four for map of map") return nil, errors.New("ValueSize must be zero or four for map of map")
} }
abi.ValueSize = 4 abi.ValueSize = 4
case PerfEventArray: case PerfEventArray:
if abi.KeySize != 0 && abi.KeySize != 4 { if abi.KeySize != 0 && abi.KeySize != 4 {
return nil, xerrors.New("KeySize must be zero or four for perf event array") return nil, errors.New("KeySize must be zero or four for perf event array")
} }
abi.KeySize = 4 abi.KeySize = 4
if abi.ValueSize != 0 && abi.ValueSize != 4 { if abi.ValueSize != 0 && abi.ValueSize != 4 {
return nil, xerrors.New("ValueSize must be zero or four for perf event array") return nil, errors.New("ValueSize must be zero or four for perf event array")
} }
abi.ValueSize = 4 abi.ValueSize = 4
if abi.MaxEntries == 0 { if abi.MaxEntries == 0 {
n, err := internal.PossibleCPUs() n, err := internal.PossibleCPUs()
if err != nil { if err != nil {
return nil, xerrors.Errorf("perf event array: %w", err) return nil, fmt.Errorf("perf event array: %w", err)
} }
abi.MaxEntries = uint32(n) abi.MaxEntries = uint32(n)
} }
@ -176,7 +180,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
if abi.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze { if abi.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze {
if err := haveMapMutabilityModifiers(); err != nil { if err := haveMapMutabilityModifiers(); err != nil {
return nil, xerrors.Errorf("map create: %w", err) return nil, fmt.Errorf("map create: %w", err)
} }
} }
@ -192,7 +196,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
var err error var err error
attr.innerMapFd, err = inner.Value() attr.innerMapFd, err = inner.Value()
if err != nil { if err != nil {
return nil, xerrors.Errorf("map create: %w", err) return nil, fmt.Errorf("map create: %w", err)
} }
} }
@ -208,7 +212,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
fd, err := bpfMapCreate(&attr) fd, err := bpfMapCreate(&attr)
if err != nil { if err != nil {
return nil, xerrors.Errorf("map create: %w", err) return nil, fmt.Errorf("map create: %w", err)
} }
m, err := newMap(fd, spec.Name, abi) m, err := newMap(fd, spec.Name, abi)
@ -218,13 +222,13 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
if err := m.populate(spec.Contents); err != nil { if err := m.populate(spec.Contents); err != nil {
m.Close() m.Close()
return nil, xerrors.Errorf("map create: can't set initial contents: %w", err) return nil, fmt.Errorf("map create: can't set initial contents: %w", err)
} }
if spec.Freeze { if spec.Freeze {
if err := m.Freeze(); err != nil { if err := m.Freeze(); err != nil {
m.Close() m.Close()
return nil, xerrors.Errorf("can't freeze map: %w", err) return nil, fmt.Errorf("can't freeze map: %w", err)
} }
} }
@ -296,9 +300,9 @@ func (m *Map) Lookup(key, valueOut interface{}) error {
*value = m *value = m
return nil return nil
case *Map: case *Map:
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
case Map: case Map:
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil)) return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
case **Program: case **Program:
p, err := unmarshalProgram(valueBytes) p, err := unmarshalProgram(valueBytes)
@ -310,9 +314,9 @@ func (m *Map) Lookup(key, valueOut interface{}) error {
*value = p *value = p
return nil return nil
case *Program: case *Program:
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
case Program: case Program:
return xerrors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil)) return fmt.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
default: default:
return unmarshalBytes(valueOut, valueBytes) return unmarshalBytes(valueOut, valueBytes)
@ -327,11 +331,11 @@ func (m *Map) LookupAndDelete(key, valueOut interface{}) error {
keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil { if err != nil {
return xerrors.Errorf("can't marshal key: %w", err) return fmt.Errorf("can't marshal key: %w", err)
} }
if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil { if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil {
return xerrors.Errorf("lookup and delete failed: %w", err) return fmt.Errorf("lookup and delete failed: %w", err)
} }
return unmarshalBytes(valueOut, valueBytes) return unmarshalBytes(valueOut, valueBytes)
@ -345,7 +349,7 @@ func (m *Map) LookupBytes(key interface{}) ([]byte, error) {
valuePtr := internal.NewSlicePointer(valueBytes) valuePtr := internal.NewSlicePointer(valueBytes)
err := m.lookup(key, valuePtr) err := m.lookup(key, valuePtr)
if xerrors.Is(err, ErrKeyNotExist) { if errors.Is(err, ErrKeyNotExist) {
return nil, nil return nil, nil
} }
@ -355,11 +359,11 @@ func (m *Map) LookupBytes(key interface{}) ([]byte, error) {
func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error { func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error {
keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil { if err != nil {
return xerrors.Errorf("can't marshal key: %w", err) return fmt.Errorf("can't marshal key: %w", err)
} }
if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil { if err = bpfMapLookupElem(m.fd, keyPtr, valueOut); err != nil {
return xerrors.Errorf("lookup failed: %w", err) return fmt.Errorf("lookup failed: %w", err)
} }
return nil return nil
} }
@ -389,7 +393,7 @@ func (m *Map) Put(key, value interface{}) error {
func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error {
keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil { if err != nil {
return xerrors.Errorf("can't marshal key: %w", err) return fmt.Errorf("can't marshal key: %w", err)
} }
var valuePtr internal.Pointer var valuePtr internal.Pointer
@ -399,11 +403,11 @@ func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error {
valuePtr, err = marshalPtr(value, int(m.abi.ValueSize)) valuePtr, err = marshalPtr(value, int(m.abi.ValueSize))
} }
if err != nil { if err != nil {
return xerrors.Errorf("can't marshal value: %w", err) return fmt.Errorf("can't marshal value: %w", err)
} }
if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil { if err = bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags)); err != nil {
return xerrors.Errorf("update failed: %w", err) return fmt.Errorf("update failed: %w", err)
} }
return nil return nil
@ -415,11 +419,11 @@ func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error {
func (m *Map) Delete(key interface{}) error { func (m *Map) Delete(key interface{}) error {
keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
if err != nil { if err != nil {
return xerrors.Errorf("can't marshal key: %w", err) return fmt.Errorf("can't marshal key: %w", err)
} }
if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil { if err = bpfMapDeleteElem(m.fd, keyPtr); err != nil {
return xerrors.Errorf("delete failed: %w", err) return fmt.Errorf("delete failed: %w", err)
} }
return nil return nil
} }
@ -441,7 +445,7 @@ func (m *Map) NextKey(key, nextKeyOut interface{}) error {
} }
if err := unmarshalBytes(nextKeyOut, nextKeyBytes); err != nil { if err := unmarshalBytes(nextKeyOut, nextKeyBytes); err != nil {
return xerrors.Errorf("can't unmarshal next key: %w", err) return fmt.Errorf("can't unmarshal next key: %w", err)
} }
return nil return nil
} }
@ -458,7 +462,7 @@ func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) {
nextKeyPtr := internal.NewSlicePointer(nextKey) nextKeyPtr := internal.NewSlicePointer(nextKey)
err := m.nextKey(key, nextKeyPtr) err := m.nextKey(key, nextKeyPtr)
if xerrors.Is(err, ErrKeyNotExist) { if errors.Is(err, ErrKeyNotExist) {
return nil, nil return nil, nil
} }
@ -474,12 +478,12 @@ func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error {
if key != nil { if key != nil {
keyPtr, err = marshalPtr(key, int(m.abi.KeySize)) keyPtr, err = marshalPtr(key, int(m.abi.KeySize))
if err != nil { if err != nil {
return xerrors.Errorf("can't marshal key: %w", err) return fmt.Errorf("can't marshal key: %w", err)
} }
} }
if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil { if err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut); err != nil {
return xerrors.Errorf("next key failed: %w", err) return fmt.Errorf("next key failed: %w", err)
} }
return nil return nil
} }
@ -532,7 +536,7 @@ func (m *Map) Clone() (*Map, error) {
dup, err := m.fd.Dup() dup, err := m.fd.Dup()
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't clone map: %w", err) return nil, fmt.Errorf("can't clone map: %w", err)
} }
return newMap(dup, m.name, &m.abi) return newMap(dup, m.name, &m.abi)
@ -542,7 +546,7 @@ func (m *Map) Clone() (*Map, error) {
// //
// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional // This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
func (m *Map) Pin(fileName string) error { func (m *Map) Pin(fileName string) error {
return bpfPinObject(fileName, m.fd) return internal.BPFObjPin(fileName, m.fd)
} }
// Freeze prevents a map to be modified from user space. // Freeze prevents a map to be modified from user space.
@ -550,11 +554,11 @@ func (m *Map) Pin(fileName string) error {
// It makes no changes to kernel-side restrictions. // It makes no changes to kernel-side restrictions.
func (m *Map) Freeze() error { func (m *Map) Freeze() error {
if err := haveMapMutabilityModifiers(); err != nil { if err := haveMapMutabilityModifiers(); err != nil {
return xerrors.Errorf("can't freeze map: %w", err) return fmt.Errorf("can't freeze map: %w", err)
} }
if err := bpfMapFreeze(m.fd); err != nil { if err := bpfMapFreeze(m.fd); err != nil {
return xerrors.Errorf("can't freeze map: %w", err) return fmt.Errorf("can't freeze map: %w", err)
} }
return nil return nil
} }
@ -562,7 +566,7 @@ func (m *Map) Freeze() error {
func (m *Map) populate(contents []MapKV) error { func (m *Map) populate(contents []MapKV) error {
for _, kv := range contents { for _, kv := range contents {
if err := m.Put(kv.Key, kv.Value); err != nil { if err := m.Put(kv.Key, kv.Value); err != nil {
return xerrors.Errorf("key %v: %w", kv.Key, err) return fmt.Errorf("key %v: %w", kv.Key, err)
} }
} }
return nil return nil
@ -573,7 +577,7 @@ func (m *Map) populate(contents []MapKV) error {
// The function is not compatible with nested maps. // The function is not compatible with nested maps.
// Use LoadPinnedMapExplicit in these situations. // Use LoadPinnedMapExplicit in these situations.
func LoadPinnedMap(fileName string) (*Map, error) { func LoadPinnedMap(fileName string) (*Map, error) {
fd, err := bpfGetObject(fileName) fd, err := internal.BPFObjGet(fileName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -587,7 +591,7 @@ func LoadPinnedMap(fileName string) (*Map, error) {
// LoadPinnedMapExplicit loads a map with explicit parameters. // LoadPinnedMapExplicit loads a map with explicit parameters.
func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) { func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) {
fd, err := bpfGetObject(fileName) fd, err := internal.BPFObjGet(fileName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -596,7 +600,7 @@ func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) {
func unmarshalMap(buf []byte) (*Map, error) { func unmarshalMap(buf []byte) (*Map, error) {
if len(buf) != 4 { if len(buf) != 4 {
return nil, xerrors.New("map id requires 4 byte value") return nil, errors.New("map id requires 4 byte value")
} }
// Looking up an entry in a nested map or prog array returns an id, // Looking up an entry in a nested map or prog array returns an id,
@ -621,12 +625,12 @@ func patchValue(value []byte, typ btf.Type, replacements map[string]interface{})
replaced := make(map[string]bool) replaced := make(map[string]bool)
replace := func(name string, offset, size int, replacement interface{}) error { replace := func(name string, offset, size int, replacement interface{}) error {
if offset+size > len(value) { if offset+size > len(value) {
return xerrors.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size) return fmt.Errorf("%s: offset %d(+%d) is out of bounds", name, offset, size)
} }
buf, err := marshalBytes(replacement, size) buf, err := marshalBytes(replacement, size)
if err != nil { if err != nil {
return xerrors.Errorf("marshal %s: %w", name, err) return fmt.Errorf("marshal %s: %w", name, err)
} }
copy(value[offset:offset+size], buf) copy(value[offset:offset+size], buf)
@ -650,7 +654,7 @@ func patchValue(value []byte, typ btf.Type, replacements map[string]interface{})
} }
default: default:
return xerrors.Errorf("patching %T is not supported", typ) return fmt.Errorf("patching %T is not supported", typ)
} }
if len(replaced) == len(replacements) { if len(replaced) == len(replacements) {
@ -665,10 +669,10 @@ func patchValue(value []byte, typ btf.Type, replacements map[string]interface{})
} }
if len(missing) == 1 { if len(missing) == 1 {
return xerrors.Errorf("unknown field: %s", missing[0]) return fmt.Errorf("unknown field: %s", missing[0])
} }
return xerrors.Errorf("unknown fields: %s", strings.Join(missing, ",")) return fmt.Errorf("unknown fields: %s", strings.Join(missing, ","))
} }
// MapIterator iterates a Map. // MapIterator iterates a Map.
@ -726,7 +730,7 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
mi.prevKey = mi.prevBytes mi.prevKey = mi.prevBytes
mi.err = mi.target.Lookup(nextBytes, valueOut) mi.err = mi.target.Lookup(nextBytes, valueOut)
if xerrors.Is(mi.err, ErrKeyNotExist) { if errors.Is(mi.err, ErrKeyNotExist) {
// Even though the key should be valid, we couldn't look up // Even though the key should be valid, we couldn't look up
// its value. If we're iterating a hash map this is probably // its value. If we're iterating a hash map this is probably
// because a concurrent delete removed the value before we // because a concurrent delete removed the value before we
@ -745,7 +749,7 @@ func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
return mi.err == nil return mi.err == nil
} }
mi.err = xerrors.Errorf("%w", ErrIterationAborted) mi.err = fmt.Errorf("%w", ErrIterationAborted)
return false return false
} }
@ -762,7 +766,7 @@ func (mi *MapIterator) Err() error {
// //
// Returns ErrNotExist, if there is no next eBPF map. // Returns ErrNotExist, if there is no next eBPF map.
func MapGetNextID(startID MapID) (MapID, error) { func MapGetNextID(startID MapID) (MapID, error) {
id, err := objGetNextID(_MapGetNextID, uint32(startID)) id, err := objGetNextID(internal.BPF_MAP_GET_NEXT_ID, uint32(startID))
return MapID(id), err return MapID(id), err
} }
@ -770,7 +774,7 @@ func MapGetNextID(startID MapID) (MapID, error) {
// //
// Returns ErrNotExist, if there is no eBPF map with the given id. // Returns ErrNotExist, if there is no eBPF map with the given id.
func NewMapFromID(id MapID) (*Map, error) { func NewMapFromID(id MapID) (*Map, error) {
fd, err := bpfObjGetFDByID(_MapGetFDByID, uint32(id)) fd, err := bpfObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id))
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -4,13 +4,13 @@ import (
"bytes" "bytes"
"encoding" "encoding"
"encoding/binary" "encoding/binary"
"errors"
"fmt"
"reflect" "reflect"
"runtime" "runtime"
"unsafe" "unsafe"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"golang.org/x/xerrors"
) )
func marshalPtr(data interface{}, length int) (internal.Pointer, error) { func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
@ -18,7 +18,7 @@ func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
if length == 0 { if length == 0 {
return internal.NewPointer(nil), nil return internal.NewPointer(nil), nil
} }
return internal.Pointer{}, xerrors.New("can't use nil as key of map") return internal.Pointer{}, errors.New("can't use nil as key of map")
} }
if ptr, ok := data.(unsafe.Pointer); ok { if ptr, ok := data.(unsafe.Pointer); ok {
@ -42,12 +42,12 @@ func marshalBytes(data interface{}, length int) (buf []byte, err error) {
case []byte: case []byte:
buf = value buf = value
case unsafe.Pointer: case unsafe.Pointer:
err = xerrors.New("can't marshal from unsafe.Pointer") err = errors.New("can't marshal from unsafe.Pointer")
default: default:
var wr bytes.Buffer var wr bytes.Buffer
err = binary.Write(&wr, internal.NativeEndian, value) err = binary.Write(&wr, internal.NativeEndian, value)
if err != nil { if err != nil {
err = xerrors.Errorf("encoding %T: %v", value, err) err = fmt.Errorf("encoding %T: %v", value, err)
} }
buf = wr.Bytes() buf = wr.Bytes()
} }
@ -56,7 +56,7 @@ func marshalBytes(data interface{}, length int) (buf []byte, err error) {
} }
if len(buf) != length { if len(buf) != length {
return nil, xerrors.Errorf("%T doesn't marshal to %d bytes", data, length) return nil, fmt.Errorf("%T doesn't marshal to %d bytes", data, length)
} }
return buf, nil return buf, nil
} }
@ -92,13 +92,13 @@ func unmarshalBytes(data interface{}, buf []byte) error {
*value = buf *value = buf
return nil return nil
case string: case string:
return xerrors.New("require pointer to string") return errors.New("require pointer to string")
case []byte: case []byte:
return xerrors.New("require pointer to []byte") return errors.New("require pointer to []byte")
default: default:
rd := bytes.NewReader(buf) rd := bytes.NewReader(buf)
if err := binary.Read(rd, internal.NativeEndian, value); err != nil { if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
return xerrors.Errorf("decoding %T: %v", value, err) return fmt.Errorf("decoding %T: %v", value, err)
} }
return nil return nil
} }
@ -113,7 +113,7 @@ func unmarshalBytes(data interface{}, buf []byte) error {
func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) { func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) {
sliceType := reflect.TypeOf(slice) sliceType := reflect.TypeOf(slice)
if sliceType.Kind() != reflect.Slice { if sliceType.Kind() != reflect.Slice {
return internal.Pointer{}, xerrors.New("per-CPU value requires slice") return internal.Pointer{}, errors.New("per-CPU value requires slice")
} }
possibleCPUs, err := internal.PossibleCPUs() possibleCPUs, err := internal.PossibleCPUs()
@ -124,7 +124,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er
sliceValue := reflect.ValueOf(slice) sliceValue := reflect.ValueOf(slice)
sliceLen := sliceValue.Len() sliceLen := sliceValue.Len()
if sliceLen > possibleCPUs { if sliceLen > possibleCPUs {
return internal.Pointer{}, xerrors.Errorf("per-CPU value exceeds number of CPUs") return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
} }
alignedElemLength := align(elemLength, 8) alignedElemLength := align(elemLength, 8)
@ -151,7 +151,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er
func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error { func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
slicePtrType := reflect.TypeOf(slicePtr) slicePtrType := reflect.TypeOf(slicePtr)
if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice { if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
return xerrors.Errorf("per-cpu value requires pointer to slice") return fmt.Errorf("per-cpu value requires pointer to slice")
} }
possibleCPUs, err := internal.PossibleCPUs() possibleCPUs, err := internal.PossibleCPUs()
@ -170,7 +170,7 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro
step := len(buf) / possibleCPUs step := len(buf) / possibleCPUs
if step < elemLength { if step < elemLength {
return xerrors.Errorf("per-cpu element length is larger than available data") return fmt.Errorf("per-cpu element length is larger than available data")
} }
for i := 0; i < possibleCPUs; i++ { for i := 0; i < possibleCPUs; i++ {
var elem interface{} var elem interface{}
@ -188,7 +188,7 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro
err := unmarshalBytes(elem, elemBytes) err := unmarshalBytes(elem, elemBytes)
if err != nil { if err != nil {
return xerrors.Errorf("cpu %d: %w", i, err) return fmt.Errorf("cpu %d: %w", i, err)
} }
buf = buf[step:] buf = buf[step:]

174
vendor/github.com/cilium/ebpf/prog.go generated vendored
View File

@ -2,18 +2,17 @@ package ebpf
import ( import (
"bytes" "bytes"
"encoding/binary"
"errors"
"fmt" "fmt"
"math" "math"
"strings" "strings"
"time" "time"
"unsafe"
"github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
) )
// ErrNotSupported is returned whenever the kernel doesn't support a feature. // ErrNotSupported is returned whenever the kernel doesn't support a feature.
@ -47,17 +46,33 @@ type ProgramOptions struct {
type ProgramSpec struct { type ProgramSpec struct {
// Name is passed to the kernel as a debug aid. Must only contain // Name is passed to the kernel as a debug aid. Must only contain
// alpha numeric and '_' characters. // alpha numeric and '_' characters.
Name string Name string
Type ProgramType // Type determines at which hook in the kernel a program will run.
AttachType AttachType Type ProgramType
Instructions asm.Instructions AttachType AttachType
License string // Name of a kernel data structure to attach to. It's interpretation
// depends on Type and AttachType.
AttachTo string
Instructions asm.Instructions
// License of the program. Some helpers are only available if
// the license is deemed compatible with the GPL.
//
// See https://www.kernel.org/doc/html/latest/process/license-rules.html#id1
License string
// Version used by tracing programs.
//
// Deprecated: superseded by BTF.
KernelVersion uint32 KernelVersion uint32
// The BTF associated with this program. Changing Instructions // The BTF associated with this program. Changing Instructions
// will most likely invalidate the contained data, and may // will most likely invalidate the contained data, and may
// result in errors when attempting to load it into the kernel. // result in errors when attempting to load it into the kernel.
BTF *btf.Program BTF *btf.Program
// The byte order this program was compiled for, may be nil.
ByteOrder binary.ByteOrder
} }
// Copy returns a copy of the spec. // Copy returns a copy of the spec.
@ -80,9 +95,10 @@ type Program struct {
// otherwise it is empty. // otherwise it is empty.
VerifierLog string VerifierLog string
fd *internal.FD fd *internal.FD
name string name string
abi ProgramABI abi ProgramABI
attachType AttachType
} }
// NewProgram creates a new Program. // NewProgram creates a new Program.
@ -103,8 +119,8 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
} }
handle, err := btf.NewHandle(btf.ProgramSpec(spec.BTF)) handle, err := btf.NewHandle(btf.ProgramSpec(spec.BTF))
if err != nil && !xerrors.Is(err, btf.ErrNotSupported) { if err != nil && !errors.Is(err, btf.ErrNotSupported) {
return nil, xerrors.Errorf("can't load BTF: %w", err) return nil, fmt.Errorf("can't load BTF: %w", err)
} }
return newProgramWithBTF(spec, handle, opts) return newProgramWithBTF(spec, handle, opts)
@ -148,7 +164,7 @@ func newProgramWithBTF(spec *ProgramSpec, btf *btf.Handle, opts ProgramOptions)
} }
err = internal.ErrorWithLog(err, logBuf, logErr) err = internal.ErrorWithLog(err, logBuf, logErr)
return nil, xerrors.Errorf("can't load program: %w", err) return nil, fmt.Errorf("can't load program: %w", err)
} }
// NewProgramFromFD creates a program from a raw fd. // NewProgramFromFD creates a program from a raw fd.
@ -158,7 +174,7 @@ func newProgramWithBTF(spec *ProgramSpec, btf *btf.Handle, opts ProgramOptions)
// Requires at least Linux 4.11. // Requires at least Linux 4.11.
func NewProgramFromFD(fd int) (*Program, error) { func NewProgramFromFD(fd int) (*Program, error) {
if fd < 0 { if fd < 0 {
return nil, xerrors.New("invalid fd") return nil, errors.New("invalid fd")
} }
bpfFd := internal.NewFD(uint32(fd)) bpfFd := internal.NewFD(uint32(fd))
@ -181,11 +197,15 @@ func newProgram(fd *internal.FD, name string, abi *ProgramABI) *Program {
func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr, error) { func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr, error) {
if len(spec.Instructions) == 0 { if len(spec.Instructions) == 0 {
return nil, xerrors.New("Instructions cannot be empty") return nil, errors.New("Instructions cannot be empty")
} }
if len(spec.License) == 0 { if len(spec.License) == 0 {
return nil, xerrors.New("License cannot be empty") return nil, errors.New("License cannot be empty")
}
if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
} }
buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize)) buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
@ -214,7 +234,7 @@ func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr
recSize, bytes, err := btf.ProgramLineInfos(spec.BTF) recSize, bytes, err := btf.ProgramLineInfos(spec.BTF)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get BTF line infos: %w", err) return nil, fmt.Errorf("can't get BTF line infos: %w", err)
} }
attr.lineInfoRecSize = recSize attr.lineInfoRecSize = recSize
attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
@ -222,13 +242,23 @@ func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr
recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF) recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF)
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get BTF function infos: %w", err) return nil, fmt.Errorf("can't get BTF function infos: %w", err)
} }
attr.funcInfoRecSize = recSize attr.funcInfoRecSize = recSize
attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.funcInfo = internal.NewSlicePointer(bytes) attr.funcInfo = internal.NewSlicePointer(bytes)
} }
if spec.AttachTo != "" {
target, err := resolveBTFType(spec.AttachTo, spec.Type, spec.AttachType)
if err != nil {
return nil, err
}
if target != nil {
attr.attachBTFID = target.ID()
}
}
return attr, nil return attr, nil
} }
@ -270,7 +300,7 @@ func (p *Program) Clone() (*Program, error) {
dup, err := p.fd.Dup() dup, err := p.fd.Dup()
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't clone program: %w", err) return nil, fmt.Errorf("can't clone program: %w", err)
} }
return newProgram(dup, p.name, &p.abi), nil return newProgram(dup, p.name, &p.abi), nil
@ -280,8 +310,8 @@ func (p *Program) Clone() (*Program, error) {
// //
// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional // This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
func (p *Program) Pin(fileName string) error { func (p *Program) Pin(fileName string) error {
if err := bpfPinObject(fileName, p.fd); err != nil { if err := internal.BPFObjPin(fileName, p.fd); err != nil {
return xerrors.Errorf("can't pin program: %w", err) return fmt.Errorf("can't pin program: %w", err)
} }
return nil return nil
} }
@ -305,7 +335,7 @@ func (p *Program) Close() error {
func (p *Program) Test(in []byte) (uint32, []byte, error) { func (p *Program) Test(in []byte) (uint32, []byte, error) {
ret, out, _, err := p.testRun(in, 1, nil) ret, out, _, err := p.testRun(in, 1, nil)
if err != nil { if err != nil {
return ret, nil, xerrors.Errorf("can't test program: %w", err) return ret, nil, fmt.Errorf("can't test program: %w", err)
} }
return ret, out, nil return ret, out, nil
} }
@ -324,12 +354,12 @@ func (p *Program) Test(in []byte) (uint32, []byte, error) {
func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) { func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) {
ret, _, total, err := p.testRun(in, repeat, reset) ret, _, total, err := p.testRun(in, repeat, reset)
if err != nil { if err != nil {
return ret, total, xerrors.Errorf("can't benchmark program: %w", err) return ret, total, fmt.Errorf("can't benchmark program: %w", err)
} }
return ret, total, nil return ret, total, nil
} }
var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() bool { var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() (bool, error) {
prog, err := NewProgram(&ProgramSpec{ prog, err := NewProgram(&ProgramSpec{
Type: SocketFilter, Type: SocketFilter,
Instructions: asm.Instructions{ Instructions: asm.Instructions{
@ -340,28 +370,23 @@ var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() b
}) })
if err != nil { if err != nil {
// This may be because we lack sufficient permissions, etc. // This may be because we lack sufficient permissions, etc.
return false return false, err
} }
defer prog.Close() defer prog.Close()
fd, err := prog.fd.Value()
if err != nil {
return false
}
// Programs require at least 14 bytes input // Programs require at least 14 bytes input
in := make([]byte, 14) in := make([]byte, 14)
attr := bpfProgTestRunAttr{ attr := bpfProgTestRunAttr{
fd: fd, fd: uint32(prog.FD()),
dataSizeIn: uint32(len(in)), dataSizeIn: uint32(len(in)),
dataIn: internal.NewSlicePointer(in), dataIn: internal.NewSlicePointer(in),
} }
_, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) err = bpfProgTestRun(&attr)
// Check for EINVAL specifically, rather than err != nil since we // Check for EINVAL specifically, rather than err != nil since we
// otherwise misdetect due to insufficient permissions. // otherwise misdetect due to insufficient permissions.
return !xerrors.Is(err, unix.EINVAL) return !errors.Is(err, unix.EINVAL), nil
}) })
func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) { func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) {
@ -403,19 +428,19 @@ func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte,
} }
for { for {
_, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) err = bpfProgTestRun(&attr)
if err == nil { if err == nil {
break break
} }
if xerrors.Is(err, unix.EINTR) { if errors.Is(err, unix.EINTR) {
if reset != nil { if reset != nil {
reset() reset()
} }
continue continue
} }
return 0, nil, 0, xerrors.Errorf("can't run test: %w", err) return 0, nil, 0, fmt.Errorf("can't run test: %w", err)
} }
if int(attr.dataSizeOut) > cap(out) { if int(attr.dataSizeOut) > cap(out) {
@ -431,7 +456,7 @@ func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte,
func unmarshalProgram(buf []byte) (*Program, error) { func unmarshalProgram(buf []byte) (*Program, error) {
if len(buf) != 4 { if len(buf) != 4 {
return nil, xerrors.New("program id requires 4 byte value") return nil, errors.New("program id requires 4 byte value")
} }
// Looking up an entry in a nested map or prog array returns an id, // Looking up an entry in a nested map or prog array returns an id,
@ -452,10 +477,12 @@ func (p *Program) MarshalBinary() ([]byte, error) {
return buf, nil return buf, nil
} }
// Attach a Program to a container object fd // Attach a Program.
//
// Deprecated: use link.RawAttachProgram instead.
func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error { func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
if fd < 0 { if fd < 0 {
return xerrors.New("invalid fd") return errors.New("invalid fd")
} }
pfd, err := p.fd.Value() pfd, err := p.fd.Value()
@ -463,20 +490,26 @@ func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
return err return err
} }
attr := bpfProgAlterAttr{ attr := internal.BPFProgAttachAttr{
targetFd: uint32(fd), TargetFd: uint32(fd),
attachBpfFd: pfd, AttachBpfFd: pfd,
attachType: uint32(typ), AttachType: uint32(typ),
attachFlags: uint32(flags), AttachFlags: uint32(flags),
} }
return bpfProgAlter(_ProgAttach, &attr) return internal.BPFProgAttach(&attr)
} }
// Detach a Program from a container object fd // Detach a Program.
//
// Deprecated: use link.RawDetachProgram instead.
func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error { func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
if fd < 0 { if fd < 0 {
return xerrors.New("invalid fd") return errors.New("invalid fd")
}
if flags != 0 {
return errors.New("flags must be zero")
} }
pfd, err := p.fd.Value() pfd, err := p.fd.Value()
@ -484,21 +517,20 @@ func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
return err return err
} }
attr := bpfProgAlterAttr{ attr := internal.BPFProgDetachAttr{
targetFd: uint32(fd), TargetFd: uint32(fd),
attachBpfFd: pfd, AttachBpfFd: pfd,
attachType: uint32(typ), AttachType: uint32(typ),
attachFlags: uint32(flags),
} }
return bpfProgAlter(_ProgDetach, &attr) return internal.BPFProgDetach(&attr)
} }
// LoadPinnedProgram loads a Program from a BPF file. // LoadPinnedProgram loads a Program from a BPF file.
// //
// Requires at least Linux 4.11. // Requires at least Linux 4.11.
func LoadPinnedProgram(fileName string) (*Program, error) { func LoadPinnedProgram(fileName string) (*Program, error) {
fd, err := bpfGetObject(fileName) fd, err := internal.BPFObjGet(fileName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -506,7 +538,7 @@ func LoadPinnedProgram(fileName string) (*Program, error) {
name, abi, err := newProgramABIFromFd(fd) name, abi, err := newProgramABIFromFd(fd)
if err != nil { if err != nil {
_ = fd.Close() _ = fd.Close()
return nil, xerrors.Errorf("can't get ABI for %s: %w", fileName, err) return nil, fmt.Errorf("can't get ABI for %s: %w", fileName, err)
} }
return newProgram(fd, name, abi), nil return newProgram(fd, name, abi), nil
@ -532,7 +564,7 @@ func SanitizeName(name string, replacement rune) string {
// //
// Returns ErrNotExist, if there is no next eBPF program. // Returns ErrNotExist, if there is no next eBPF program.
func ProgramGetNextID(startID ProgramID) (ProgramID, error) { func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
id, err := objGetNextID(_ProgGetNextID, uint32(startID)) id, err := objGetNextID(internal.BPF_PROG_GET_NEXT_ID, uint32(startID))
return ProgramID(id), err return ProgramID(id), err
} }
@ -540,7 +572,7 @@ func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
// //
// Returns ErrNotExist, if there is no eBPF program with the given id. // Returns ErrNotExist, if there is no eBPF program with the given id.
func NewProgramFromID(id ProgramID) (*Program, error) { func NewProgramFromID(id ProgramID) (*Program, error) {
fd, err := bpfObjGetFDByID(_ProgGetFDByID, uint32(id)) fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -562,3 +594,29 @@ func (p *Program) ID() (ProgramID, error) {
} }
return ProgramID(info.id), nil return ProgramID(info.id), nil
} }
func resolveBTFType(name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
kernel, err := btf.LoadKernelSpec()
if err != nil {
return nil, fmt.Errorf("can't resolve BTF type %s: %w", name, err)
}
type match struct {
p ProgramType
a AttachType
}
target := match{progType, attachType}
switch target {
case match{Tracing, AttachTraceIter}:
var target btf.Func
if err := kernel.FindType("bpf_iter_"+name, &target); err != nil {
return nil, fmt.Errorf("can't resolve BTF for iterator %s: %w", name, err)
}
return &target, nil
default:
return nil, nil
}
}

View File

@ -15,8 +15,13 @@ if [[ "${1:-}" = "--in-vm" ]]; then
export GOPROXY=file:///run/go-root/pkg/mod/cache/download export GOPROXY=file:///run/go-root/pkg/mod/cache/download
export GOCACHE=/run/go-cache export GOCACHE=/run/go-cache
elfs=""
if [[ -d "/run/input/bpf" ]]; then
elfs="/run/input/bpf"
fi
echo Running tests... echo Running tests...
/usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v ./... /usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v -elfs "$elfs" ./...
touch "$1/success" touch "$1/success"
exit 0 exit 0
fi fi
@ -39,20 +44,34 @@ if [[ -z "${kernel_version}" ]]; then
fi fi
readonly kernel="linux-${kernel_version}.bz" readonly kernel="linux-${kernel_version}.bz"
readonly selftests="linux-${kernel_version}-selftests-bpf.bz"
readonly input="$(mktemp -d)"
readonly output="$(mktemp -d)" readonly output="$(mktemp -d)"
readonly tmp_dir="${TMPDIR:-$(mktemp -d)}" readonly tmp_dir="${TMPDIR:-/tmp}"
readonly branch="${BRANCH:-master}"
test -e "${tmp_dir}/${kernel}" || { fetch() {
echo Fetching "${kernel}" echo Fetching "${1}"
curl --fail -L "https://github.com/cilium/ci-kernels/blob/master/${kernel}?raw=true" -o "${tmp_dir}/${kernel}" wget -nv -N -P "${tmp_dir}" "https://github.com/cilium/ci-kernels/raw/${branch}/${1}"
} }
fetch "${kernel}"
if fetch "${selftests}"; then
mkdir "${input}/bpf"
tar --strip-components=4 -xjf "${tmp_dir}/${selftests}" -C "${input}/bpf"
else
echo "No selftests found, disabling"
fi
echo Testing on "${kernel_version}" echo Testing on "${kernel_version}"
$sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 512M --pwd \ $sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 512M --pwd \
--rwdir=/run/input="${input}" \
--rwdir=/run/output="${output}" \ --rwdir=/run/output="${output}" \
--rodir=/run/go-path="$(go env GOPATH)" \ --rodir=/run/go-path="$(go env GOPATH)" \
--rwdir=/run/go-cache="$(go env GOCACHE)" \ --rwdir=/run/go-cache="$(go env GOCACHE)" \
--script-sh "$(realpath "$0") --in-vm /run/output" --script-sh "$(realpath "$0") --in-vm /run/output" \
--qemu-opts -smp 2 # need at least two CPUs for some tests
if [[ ! -e "${output}/success" ]]; then if [[ ! -e "${output}/success" ]]; then
echo "Test failed on ${kernel_version}" echo "Test failed on ${kernel_version}"
@ -66,4 +85,5 @@ else
fi fi
fi fi
$sudo rm -r "${input}"
$sudo rm -r "${output}" $sudo rm -r "${output}"

View File

@ -1,19 +1,19 @@
package ebpf package ebpf
import ( import (
"path/filepath" "errors"
"fmt"
"os"
"unsafe" "unsafe"
"github.com/cilium/ebpf/internal" "github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix" "github.com/cilium/ebpf/internal/unix"
"golang.org/x/xerrors"
) )
// Generic errors returned by BPF syscalls. // Generic errors returned by BPF syscalls.
var ( var (
ErrNotExist = xerrors.New("requested object does not exit") ErrNotExist = errors.New("requested object does not exist")
) )
// bpfObjName is a null-terminated string made up of // bpfObjName is a null-terminated string made up of
@ -79,12 +79,6 @@ type bpfMapInfo struct {
mapName bpfObjName // since 4.15 ad5b177bd73f mapName bpfObjName // since 4.15 ad5b177bd73f
} }
type bpfPinObjAttr struct {
fileName internal.Pointer
fd uint32
padding uint32
}
type bpfProgLoadAttr struct { type bpfProgLoadAttr struct {
progType ProgramType progType ProgramType
insCount uint32 insCount uint32
@ -105,6 +99,8 @@ type bpfProgLoadAttr struct {
lineInfoRecSize uint32 lineInfoRecSize uint32
lineInfo internal.Pointer lineInfo internal.Pointer
lineInfoCnt uint32 lineInfoCnt uint32
attachBTFID btf.TypeID
attachProgFd uint32
} }
type bpfProgInfo struct { type bpfProgInfo struct {
@ -133,13 +129,6 @@ type bpfProgTestRunAttr struct {
duration uint32 duration uint32
} }
type bpfProgAlterAttr struct {
targetFd uint32
attachBpfFd uint32
attachType uint32
attachFlags uint32
}
type bpfObjGetInfoByFDAttr struct { type bpfObjGetInfoByFDAttr struct {
fd uint32 fd uint32
infoLen uint32 infoLen uint32
@ -163,7 +152,7 @@ type bpfObjGetNextIDAttr struct {
func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) { func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
for { for {
fd, err := internal.BPF(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
// As of ~4.20 the verifier can be interrupted by a signal, // As of ~4.20 the verifier can be interrupted by a signal,
// and returns EAGAIN in that case. // and returns EAGAIN in that case.
if err == unix.EAGAIN { if err == unix.EAGAIN {
@ -178,13 +167,17 @@ func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
} }
} }
func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error { func bpfProgTestRun(attr *bpfProgTestRunAttr) error {
_, err := internal.BPF(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) _, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err return err
} }
func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) { func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) {
fd, err := internal.BPF(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) fd, err := internal.BPF(internal.BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
if errors.Is(err, os.ErrPermission) {
return nil, errors.New("permission denied or insufficient rlimit to lock memory for map")
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -192,7 +185,7 @@ func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) {
return internal.NewFD(uint32(fd)), nil return internal.NewFD(uint32(fd)), nil
} }
var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() (bool, error) {
inner, err := bpfMapCreate(&bpfMapCreateAttr{ inner, err := bpfMapCreate(&bpfMapCreateAttr{
mapType: Array, mapType: Array,
keySize: 4, keySize: 4,
@ -200,7 +193,7 @@ var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool {
maxEntries: 1, maxEntries: 1,
}) })
if err != nil { if err != nil {
return false return false, err
} }
defer inner.Close() defer inner.Close()
@ -213,14 +206,14 @@ var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool {
innerMapFd: innerFd, innerMapFd: innerFd,
}) })
if err != nil { if err != nil {
return false return false, nil
} }
_ = nested.Close() _ = nested.Close()
return true return true, nil
}) })
var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() bool { var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() (bool, error) {
// This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since // This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since
// BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. // BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check.
m, err := bpfMapCreate(&bpfMapCreateAttr{ m, err := bpfMapCreate(&bpfMapCreateAttr{
@ -231,10 +224,10 @@ var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps
flags: unix.BPF_F_RDONLY_PROG, flags: unix.BPF_F_RDONLY_PROG,
}) })
if err != nil { if err != nil {
return false return false, nil
} }
_ = m.Close() _ = m.Close()
return true return true, nil
}) })
func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error { func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error {
@ -248,7 +241,7 @@ func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error {
key: key, key: key,
value: valueOut, value: valueOut,
} }
_, err = internal.BPF(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) _, err = internal.BPF(internal.BPF_MAP_LOOKUP_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err) return wrapMapError(err)
} }
@ -263,7 +256,7 @@ func bpfMapLookupAndDelete(m *internal.FD, key, valueOut internal.Pointer) error
key: key, key: key,
value: valueOut, value: valueOut,
} }
_, err = internal.BPF(_MapLookupAndDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) _, err = internal.BPF(internal.BPF_MAP_LOOKUP_AND_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err) return wrapMapError(err)
} }
@ -279,7 +272,7 @@ func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint
value: valueOut, value: valueOut,
flags: flags, flags: flags,
} }
_, err = internal.BPF(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) _, err = internal.BPF(internal.BPF_MAP_UPDATE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err) return wrapMapError(err)
} }
@ -293,7 +286,7 @@ func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error {
mapFd: fd, mapFd: fd,
key: key, key: key,
} }
_, err = internal.BPF(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) _, err = internal.BPF(internal.BPF_MAP_DELETE_ELEM, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err) return wrapMapError(err)
} }
@ -308,11 +301,11 @@ func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error {
key: key, key: key,
value: nextKeyOut, value: nextKeyOut,
} }
_, err = internal.BPF(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) _, err = internal.BPF(internal.BPF_MAP_GET_NEXT_KEY, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return wrapMapError(err) return wrapMapError(err)
} }
func objGetNextID(cmd int, start uint32) (uint32, error) { func objGetNextID(cmd internal.BPFCmd, start uint32) (uint32, error) {
attr := bpfObjGetNextIDAttr{ attr := bpfObjGetNextIDAttr{
startID: start, startID: start,
} }
@ -324,11 +317,11 @@ func wrapObjError(err error) error {
if err == nil { if err == nil {
return nil return nil
} }
if xerrors.Is(err, unix.ENOENT) { if errors.Is(err, unix.ENOENT) {
return xerrors.Errorf("%w", ErrNotExist) return fmt.Errorf("%w", ErrNotExist)
} }
return xerrors.New(err.Error()) return errors.New(err.Error())
} }
func wrapMapError(err error) error { func wrapMapError(err error) error {
@ -336,11 +329,15 @@ func wrapMapError(err error) error {
return nil return nil
} }
if xerrors.Is(err, unix.ENOENT) { if errors.Is(err, unix.ENOENT) {
return ErrKeyNotExist return ErrKeyNotExist
} }
return xerrors.New(err.Error()) if errors.Is(err, unix.EEXIST) {
return ErrKeyExist
}
return errors.New(err.Error())
} }
func bpfMapFreeze(m *internal.FD) error { func bpfMapFreeze(m *internal.FD) error {
@ -352,47 +349,10 @@ func bpfMapFreeze(m *internal.FD) error {
attr := bpfMapFreezeAttr{ attr := bpfMapFreezeAttr{
mapFd: fd, mapFd: fd,
} }
_, err = internal.BPF(_MapFreeze, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) _, err = internal.BPF(internal.BPF_MAP_FREEZE, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return err return err
} }
const bpfFSType = 0xcafe4a11
func bpfPinObject(fileName string, fd *internal.FD) error {
dirName := filepath.Dir(fileName)
var statfs unix.Statfs_t
if err := unix.Statfs(dirName, &statfs); err != nil {
return err
}
if uint64(statfs.Type) != bpfFSType {
return xerrors.Errorf("%s is not on a bpf filesystem", fileName)
}
value, err := fd.Value()
if err != nil {
return err
}
_, err = internal.BPF(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{
fileName: internal.NewStringPointer(fileName),
fd: value,
}), 16)
if err != nil {
return xerrors.Errorf("pin object %s: %w", fileName, err)
}
return nil
}
func bpfGetObject(fileName string) (*internal.FD, error) {
ptr, err := internal.BPF(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{
fileName: internal.NewStringPointer(fileName),
}), 16)
if err != nil {
return nil, xerrors.Errorf("get object %s: %w", fileName, err)
}
return internal.NewFD(uint32(ptr)), nil
}
func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error { func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error {
value, err := fd.Value() value, err := fd.Value()
if err != nil { if err != nil {
@ -405,9 +365,9 @@ func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) er
infoLen: uint32(size), infoLen: uint32(size),
info: internal.NewPointer(info), info: internal.NewPointer(info),
} }
_, err = internal.BPF(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) _, err = internal.BPF(internal.BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
if err != nil { if err != nil {
return xerrors.Errorf("fd %d: %w", fd, err) return fmt.Errorf("fd %d: %w", fd, err)
} }
return nil return nil
} }
@ -415,7 +375,7 @@ func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) er
func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) { func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) {
var info bpfProgInfo var info bpfProgInfo
if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil { if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
return nil, xerrors.Errorf("can't get program info: %w", err) return nil, fmt.Errorf("can't get program info: %w", err)
} }
return &info, nil return &info, nil
} }
@ -424,12 +384,12 @@ func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) {
var info bpfMapInfo var info bpfMapInfo
err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
if err != nil { if err != nil {
return nil, xerrors.Errorf("can't get map info: %w", err) return nil, fmt.Errorf("can't get map info: %w", err)
} }
return &info, nil return &info, nil
} }
var haveObjName = internal.FeatureTest("object names", "4.15", func() bool { var haveObjName = internal.FeatureTest("object names", "4.15", func() (bool, error) {
attr := bpfMapCreateAttr{ attr := bpfMapCreateAttr{
mapType: Array, mapType: Array,
keySize: 4, keySize: 4,
@ -440,16 +400,16 @@ var haveObjName = internal.FeatureTest("object names", "4.15", func() bool {
fd, err := bpfMapCreate(&attr) fd, err := bpfMapCreate(&attr)
if err != nil { if err != nil {
return false return false, nil
} }
_ = fd.Close() _ = fd.Close()
return true return true, nil
}) })
var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() bool { var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func() (bool, error) {
if err := haveObjName(); err != nil { if err := haveObjName(); err != nil {
return false return false, err
} }
attr := bpfMapCreateAttr{ attr := bpfMapCreateAttr{
@ -462,14 +422,14 @@ var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func()
fd, err := bpfMapCreate(&attr) fd, err := bpfMapCreate(&attr)
if err != nil { if err != nil {
return false return false, nil
} }
_ = fd.Close() _ = fd.Close()
return true return true, nil
}) })
func bpfObjGetFDByID(cmd int, id uint32) (*internal.FD, error) { func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) {
attr := bpfGetFDByIDAttr{ attr := bpfGetFDByIDAttr{
id: id, id: id,
} }

View File

@ -1,6 +1,6 @@
package ebpf package ebpf
//go:generate stringer -output types_string.go -type=MapType,ProgramType //go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType
// MapType indicates the type map structure // MapType indicates the type map structure
// that will be initialized in the kernel. // that will be initialized in the kernel.
@ -85,39 +85,12 @@ const (
// hasPerCPUValue returns true if the Map stores a value per CPU. // hasPerCPUValue returns true if the Map stores a value per CPU.
func (mt MapType) hasPerCPUValue() bool { func (mt MapType) hasPerCPUValue() bool {
if mt == PerCPUHash || mt == PerCPUArray { if mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash {
return true return true
} }
return false return false
} }
const (
_MapCreate = iota
_MapLookupElem
_MapUpdateElem
_MapDeleteElem
_MapGetNextKey
_ProgLoad
_ObjPin
_ObjGet
_ProgAttach
_ProgDetach
_ProgTestRun
_ProgGetNextID
_MapGetNextID
_ProgGetFDByID
_MapGetFDByID
_ObjGetInfoByFD
_ProgQuery
_RawTracepointOpen
_BTFLoad
_BTFGetFDByID
_TaskFDQuery
_MapLookupAndDeleteElem
_MapFreeze
_BTFGetNextID
)
// ProgramType of the eBPF program // ProgramType of the eBPF program
type ProgramType uint32 type ProgramType uint32
@ -214,6 +187,9 @@ const (
AttachTraceRawTp AttachTraceRawTp
AttachTraceFEntry AttachTraceFEntry
AttachTraceFExit AttachTraceFExit
AttachModifyReturn
AttachLSMMac
AttachTraceIter
) )
// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command

View File

@ -1,4 +1,4 @@
// Code generated by "stringer -output types_string.go -type=MapType,ProgramType"; DO NOT EDIT. // Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType"; DO NOT EDIT.
package ebpf package ebpf
@ -89,3 +89,49 @@ func (i ProgramType) String() string {
} }
return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]] return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]]
} }
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[AttachNone-0]
_ = x[AttachCGroupInetIngress-0]
_ = x[AttachCGroupInetEgress-1]
_ = x[AttachCGroupInetSockCreate-2]
_ = x[AttachCGroupSockOps-3]
_ = x[AttachSkSKBStreamParser-4]
_ = x[AttachSkSKBStreamVerdict-5]
_ = x[AttachCGroupDevice-6]
_ = x[AttachSkMsgVerdict-7]
_ = x[AttachCGroupInet4Bind-8]
_ = x[AttachCGroupInet6Bind-9]
_ = x[AttachCGroupInet4Connect-10]
_ = x[AttachCGroupInet6Connect-11]
_ = x[AttachCGroupInet4PostBind-12]
_ = x[AttachCGroupInet6PostBind-13]
_ = x[AttachCGroupUDP4Sendmsg-14]
_ = x[AttachCGroupUDP6Sendmsg-15]
_ = x[AttachLircMode2-16]
_ = x[AttachFlowDissector-17]
_ = x[AttachCGroupSysctl-18]
_ = x[AttachCGroupUDP4Recvmsg-19]
_ = x[AttachCGroupUDP6Recvmsg-20]
_ = x[AttachCGroupGetsockopt-21]
_ = x[AttachCGroupSetsockopt-22]
_ = x[AttachTraceRawTp-23]
_ = x[AttachTraceFEntry-24]
_ = x[AttachTraceFExit-25]
_ = x[AttachModifyReturn-26]
_ = x[AttachLSMMac-27]
_ = x[AttachTraceIter-28]
}
const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIter"
var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582}
func (i AttachType) String() string {
if i >= AttachType(len(_AttachType_index)-1) {
return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]]
}

27
vendor/golang.org/x/xerrors/LICENSE generated vendored
View File

@ -1,27 +0,0 @@
Copyright (c) 2019 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

22
vendor/golang.org/x/xerrors/PATENTS generated vendored
View File

@ -1,22 +0,0 @@
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.

2
vendor/golang.org/x/xerrors/README generated vendored
View File

@ -1,2 +0,0 @@
This repository holds the transition packages for the new Go 1.13 error values.
See golang.org/design/29934-error-values.

View File

@ -1,193 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xerrors
import (
"bytes"
"fmt"
"io"
"reflect"
"strconv"
)
// FormatError calls the FormatError method of f with an errors.Printer
// configured according to s and verb, and writes the result to s.
func FormatError(f Formatter, s fmt.State, verb rune) {
// Assuming this function is only called from the Format method, and given
// that FormatError takes precedence over Format, it cannot be called from
// any package that supports errors.Formatter. It is therefore safe to
// disregard that State may be a specific printer implementation and use one
// of our choice instead.
// limitations: does not support printing error as Go struct.
var (
sep = " " // separator before next error
p = &state{State: s}
direct = true
)
var err error = f
switch verb {
// Note that this switch must match the preference order
// for ordinary string printing (%#v before %+v, and so on).
case 'v':
if s.Flag('#') {
if stringer, ok := err.(fmt.GoStringer); ok {
io.WriteString(&p.buf, stringer.GoString())
goto exit
}
// proceed as if it were %v
} else if s.Flag('+') {
p.printDetail = true
sep = "\n - "
}
case 's':
case 'q', 'x', 'X':
// Use an intermediate buffer in the rare cases that precision,
// truncation, or one of the alternative verbs (q, x, and X) are
// specified.
direct = false
default:
p.buf.WriteString("%!")
p.buf.WriteRune(verb)
p.buf.WriteByte('(')
switch {
case err != nil:
p.buf.WriteString(reflect.TypeOf(f).String())
default:
p.buf.WriteString("<nil>")
}
p.buf.WriteByte(')')
io.Copy(s, &p.buf)
return
}
loop:
for {
switch v := err.(type) {
case Formatter:
err = v.FormatError((*printer)(p))
case fmt.Formatter:
v.Format(p, 'v')
break loop
default:
io.WriteString(&p.buf, v.Error())
break loop
}
if err == nil {
break
}
if p.needColon || !p.printDetail {
p.buf.WriteByte(':')
p.needColon = false
}
p.buf.WriteString(sep)
p.inDetail = false
p.needNewline = false
}
exit:
width, okW := s.Width()
prec, okP := s.Precision()
if !direct || (okW && width > 0) || okP {
// Construct format string from State s.
format := []byte{'%'}
if s.Flag('-') {
format = append(format, '-')
}
if s.Flag('+') {
format = append(format, '+')
}
if s.Flag(' ') {
format = append(format, ' ')
}
if okW {
format = strconv.AppendInt(format, int64(width), 10)
}
if okP {
format = append(format, '.')
format = strconv.AppendInt(format, int64(prec), 10)
}
format = append(format, string(verb)...)
fmt.Fprintf(s, string(format), p.buf.String())
} else {
io.Copy(s, &p.buf)
}
}
var detailSep = []byte("\n ")
// state tracks error printing state. It implements fmt.State.
type state struct {
fmt.State
buf bytes.Buffer
printDetail bool
inDetail bool
needColon bool
needNewline bool
}
func (s *state) Write(b []byte) (n int, err error) {
if s.printDetail {
if len(b) == 0 {
return 0, nil
}
if s.inDetail && s.needColon {
s.needNewline = true
if b[0] == '\n' {
b = b[1:]
}
}
k := 0
for i, c := range b {
if s.needNewline {
if s.inDetail && s.needColon {
s.buf.WriteByte(':')
s.needColon = false
}
s.buf.Write(detailSep)
s.needNewline = false
}
if c == '\n' {
s.buf.Write(b[k:i])
k = i + 1
s.needNewline = true
}
}
s.buf.Write(b[k:])
if !s.inDetail {
s.needColon = true
}
} else if !s.inDetail {
s.buf.Write(b)
}
return len(b), nil
}
// printer wraps a state to implement an xerrors.Printer.
type printer state
func (s *printer) Print(args ...interface{}) {
if !s.inDetail || s.printDetail {
fmt.Fprint((*state)(s), args...)
}
}
func (s *printer) Printf(format string, args ...interface{}) {
if !s.inDetail || s.printDetail {
fmt.Fprintf((*state)(s), format, args...)
}
}
func (s *printer) Detail() bool {
s.inDetail = true
return s.printDetail
}

View File

@ -1 +0,0 @@
issuerepo: golang/go

22
vendor/golang.org/x/xerrors/doc.go generated vendored
View File

@ -1,22 +0,0 @@
// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package xerrors implements functions to manipulate errors.
//
// This package is based on the Go 2 proposal for error values:
// https://golang.org/design/29934-error-values
//
// These functions were incorporated into the standard library's errors package
// in Go 1.13:
// - Is
// - As
// - Unwrap
//
// Also, Errorf's %w verb was incorporated into fmt.Errorf.
//
// Use this package to get equivalent behavior in all supported Go versions.
//
// No other features of this package were included in Go 1.13, and at present
// there are no plans to include any of them.
package xerrors // import "golang.org/x/xerrors"

View File

@ -1,33 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xerrors
import "fmt"
// errorString is a trivial implementation of error.
type errorString struct {
s string
frame Frame
}
// New returns an error that formats as the given text.
//
// The returned error contains a Frame set to the caller's location and
// implements Formatter to show this information when printed with details.
func New(text string) error {
return &errorString{text, Caller(1)}
}
func (e *errorString) Error() string {
return e.s
}
func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) }
func (e *errorString) FormatError(p Printer) (next error) {
p.Print(e.s)
e.frame.Format(p)
return nil
}

187
vendor/golang.org/x/xerrors/fmt.go generated vendored
View File

@ -1,187 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xerrors
import (
"fmt"
"strings"
"unicode"
"unicode/utf8"
"golang.org/x/xerrors/internal"
)
const percentBangString = "%!"
// Errorf formats according to a format specifier and returns the string as a
// value that satisfies error.
//
// The returned error includes the file and line number of the caller when
// formatted with additional detail enabled. If the last argument is an error
// the returned error's Format method will return it if the format string ends
// with ": %s", ": %v", or ": %w". If the last argument is an error and the
// format string ends with ": %w", the returned error implements an Unwrap
// method returning it.
//
// If the format specifier includes a %w verb with an error operand in a
// position other than at the end, the returned error will still implement an
// Unwrap method returning the operand, but the error's Format method will not
// return the wrapped error.
//
// It is invalid to include more than one %w verb or to supply it with an
// operand that does not implement the error interface. The %w verb is otherwise
// a synonym for %v.
func Errorf(format string, a ...interface{}) error {
format = formatPlusW(format)
// Support a ": %[wsv]" suffix, which works well with xerrors.Formatter.
wrap := strings.HasSuffix(format, ": %w")
idx, format2, ok := parsePercentW(format)
percentWElsewhere := !wrap && idx >= 0
if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) {
err := errorAt(a, len(a)-1)
if err == nil {
return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)}
}
// TODO: this is not entirely correct. The error value could be
// printed elsewhere in format if it mixes numbered with unnumbered
// substitutions. With relatively small changes to doPrintf we can
// have it optionally ignore extra arguments and pass the argument
// list in its entirety.
msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...)
frame := Frame{}
if internal.EnableTrace {
frame = Caller(1)
}
if wrap {
return &wrapError{msg, err, frame}
}
return &noWrapError{msg, err, frame}
}
// Support %w anywhere.
// TODO: don't repeat the wrapped error's message when %w occurs in the middle.
msg := fmt.Sprintf(format2, a...)
if idx < 0 {
return &noWrapError{msg, nil, Caller(1)}
}
err := errorAt(a, idx)
if !ok || err == nil {
// Too many %ws or argument of %w is not an error. Approximate the Go
// 1.13 fmt.Errorf message.
return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)}
}
frame := Frame{}
if internal.EnableTrace {
frame = Caller(1)
}
return &wrapError{msg, err, frame}
}
func errorAt(args []interface{}, i int) error {
if i < 0 || i >= len(args) {
return nil
}
err, ok := args[i].(error)
if !ok {
return nil
}
return err
}
// formatPlusW is used to avoid the vet check that will barf at %w.
func formatPlusW(s string) string {
return s
}
// Return the index of the only %w in format, or -1 if none.
// Also return a rewritten format string with %w replaced by %v, and
// false if there is more than one %w.
// TODO: handle "%[N]w".
func parsePercentW(format string) (idx int, newFormat string, ok bool) {
// Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go.
idx = -1
ok = true
n := 0
sz := 0
var isW bool
for i := 0; i < len(format); i += sz {
if format[i] != '%' {
sz = 1
continue
}
// "%%" is not a format directive.
if i+1 < len(format) && format[i+1] == '%' {
sz = 2
continue
}
sz, isW = parsePrintfVerb(format[i:])
if isW {
if idx >= 0 {
ok = false
} else {
idx = n
}
// "Replace" the last character, the 'w', with a 'v'.
p := i + sz - 1
format = format[:p] + "v" + format[p+1:]
}
n++
}
return idx, format, ok
}
// Parse the printf verb starting with a % at s[0].
// Return how many bytes it occupies and whether the verb is 'w'.
func parsePrintfVerb(s string) (int, bool) {
// Assume only that the directive is a sequence of non-letters followed by a single letter.
sz := 0
var r rune
for i := 1; i < len(s); i += sz {
r, sz = utf8.DecodeRuneInString(s[i:])
if unicode.IsLetter(r) {
return i + sz, r == 'w'
}
}
return len(s), false
}
type noWrapError struct {
msg string
err error
frame Frame
}
func (e *noWrapError) Error() string {
return fmt.Sprint(e)
}
func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
func (e *noWrapError) FormatError(p Printer) (next error) {
p.Print(e.msg)
e.frame.Format(p)
return e.err
}
type wrapError struct {
msg string
err error
frame Frame
}
func (e *wrapError) Error() string {
return fmt.Sprint(e)
}
func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) }
func (e *wrapError) FormatError(p Printer) (next error) {
p.Print(e.msg)
e.frame.Format(p)
return e.err
}
func (e *wrapError) Unwrap() error {
return e.err
}

View File

@ -1,34 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xerrors
// A Formatter formats error messages.
type Formatter interface {
error
// FormatError prints the receiver's first error and returns the next error in
// the error chain, if any.
FormatError(p Printer) (next error)
}
// A Printer formats error messages.
//
// The most common implementation of Printer is the one provided by package fmt
// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message
// typically provide their own implementations.
type Printer interface {
// Print appends args to the message output.
Print(args ...interface{})
// Printf writes a formatted string.
Printf(format string, args ...interface{})
// Detail reports whether error detail is requested.
// After the first call to Detail, all text written to the Printer
// is formatted as additional detail, or ignored when
// detail has not been requested.
// If Detail returns false, the caller can avoid printing the detail at all.
Detail() bool
}

56
vendor/golang.org/x/xerrors/frame.go generated vendored
View File

@ -1,56 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xerrors
import (
"runtime"
)
// A Frame contains part of a call stack.
type Frame struct {
// Make room for three PCs: the one we were asked for, what it called,
// and possibly a PC for skipPleaseUseCallersFrames. See:
// https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169
frames [3]uintptr
}
// Caller returns a Frame that describes a frame on the caller's stack.
// The argument skip is the number of frames to skip over.
// Caller(0) returns the frame for the caller of Caller.
func Caller(skip int) Frame {
var s Frame
runtime.Callers(skip+1, s.frames[:])
return s
}
// location reports the file, line, and function of a frame.
//
// The returned function may be "" even if file and line are not.
func (f Frame) location() (function, file string, line int) {
frames := runtime.CallersFrames(f.frames[:])
if _, ok := frames.Next(); !ok {
return "", "", 0
}
fr, ok := frames.Next()
if !ok {
return "", "", 0
}
return fr.Function, fr.File, fr.Line
}
// Format prints the stack as error detail.
// It should be called from an error's Format implementation
// after printing any other error detail.
func (f Frame) Format(p Printer) {
if p.Detail() {
function, file, line := f.location()
if function != "" {
p.Printf("%s\n ", function)
}
if file != "" {
p.Printf("%s:%d\n", file, line)
}
}
}

3
vendor/golang.org/x/xerrors/go.mod generated vendored
View File

@ -1,3 +0,0 @@
module golang.org/x/xerrors
go 1.11

View File

@ -1,8 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package internal
// EnableTrace indicates whether stack information should be recorded in errors.
var EnableTrace = true

106
vendor/golang.org/x/xerrors/wrap.go generated vendored
View File

@ -1,106 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xerrors
import (
"reflect"
)
// A Wrapper provides context around another error.
type Wrapper interface {
// Unwrap returns the next error in the error chain.
// If there is no next error, Unwrap returns nil.
Unwrap() error
}
// Opaque returns an error with the same error formatting as err
// but that does not match err and cannot be unwrapped.
func Opaque(err error) error {
return noWrapper{err}
}
type noWrapper struct {
error
}
func (e noWrapper) FormatError(p Printer) (next error) {
if f, ok := e.error.(Formatter); ok {
return f.FormatError(p)
}
p.Print(e.error)
return nil
}
// Unwrap returns the result of calling the Unwrap method on err, if err implements
// Unwrap. Otherwise, Unwrap returns nil.
func Unwrap(err error) error {
u, ok := err.(Wrapper)
if !ok {
return nil
}
return u.Unwrap()
}
// Is reports whether any error in err's chain matches target.
//
// An error is considered to match a target if it is equal to that target or if
// it implements a method Is(error) bool such that Is(target) returns true.
func Is(err, target error) bool {
if target == nil {
return err == target
}
isComparable := reflect.TypeOf(target).Comparable()
for {
if isComparable && err == target {
return true
}
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
return true
}
// TODO: consider supporing target.Is(err). This would allow
// user-definable predicates, but also may allow for coping with sloppy
// APIs, thereby making it easier to get away with them.
if err = Unwrap(err); err == nil {
return false
}
}
}
// As finds the first error in err's chain that matches the type to which target
// points, and if so, sets the target to its value and returns true. An error
// matches a type if it is assignable to the target type, or if it has a method
// As(interface{}) bool such that As(target) returns true. As will panic if target
// is not a non-nil pointer to a type which implements error or is of interface type.
//
// The As method should set the target to its value and return true if err
// matches the type to which target points.
func As(err error, target interface{}) bool {
if target == nil {
panic("errors: target cannot be nil")
}
val := reflect.ValueOf(target)
typ := val.Type()
if typ.Kind() != reflect.Ptr || val.IsNil() {
panic("errors: target must be a non-nil pointer")
}
if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) {
panic("errors: *target must be interface or implement error")
}
targetType := typ.Elem()
for err != nil {
if reflect.TypeOf(err).AssignableTo(targetType) {
val.Elem().Set(reflect.ValueOf(err))
return true
}
if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) {
return true
}
err = Unwrap(err)
}
return false
}
var errorType = reflect.TypeOf((*error)(nil)).Elem()

5
vendor/modules.txt vendored
View File

@ -2,7 +2,7 @@
## explicit ## explicit
github.com/checkpoint-restore/go-criu/v4 github.com/checkpoint-restore/go-criu/v4
github.com/checkpoint-restore/go-criu/v4/rpc github.com/checkpoint-restore/go-criu/v4/rpc
# github.com/cilium/ebpf v0.0.0-20200507155900-a9f01edf17e3 # github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775
## explicit ## explicit
github.com/cilium/ebpf github.com/cilium/ebpf
github.com/cilium/ebpf/asm github.com/cilium/ebpf/asm
@ -75,6 +75,3 @@ github.com/vishvananda/netns
## explicit ## explicit
golang.org/x/sys/unix golang.org/x/sys/unix
golang.org/x/sys/windows golang.org/x/sys/windows
# golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
golang.org/x/xerrors
golang.org/x/xerrors/internal