From 4fb3410f65d0ab6a8c72a4adcde15b21332c7a28 Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Sat, 4 Apr 2020 00:31:39 +0200 Subject: [PATCH] vendor: cilium/ebbf 4032b1d8aae306b7bb94a2a11002932caf88c644 full diff: https://github.com/cilium/ebpf/compare/60c3aa43f488292fe2ee50fb8b833b383ca8ebbb...4032b1d8aae306b7bb94a2a11002932caf88c644 Signed-off-by: Sebastiaan van Stijn --- vendor.conf | 2 +- vendor/github.com/cilium/ebpf/abi.go | 17 +- vendor/github.com/cilium/ebpf/collection.go | 63 +- vendor/github.com/cilium/ebpf/elf_reader.go | 297 +++++++--- .../cilium/ebpf/internal/btf/btf.go | 530 +++++++++++++++++ .../cilium/ebpf/internal/btf/btf_types.go | 190 ++++++ .../cilium/ebpf/internal/btf/doc.go | 8 + .../cilium/ebpf/internal/btf/ext_info.go | 184 ++++++ .../cilium/ebpf/internal/btf/strings.go | 60 ++ .../cilium/ebpf/internal/btf/types.go | 550 ++++++++++++++++++ .../github.com/cilium/ebpf/internal/errors.go | 50 ++ vendor/github.com/cilium/ebpf/internal/fd.go | 63 ++ vendor/github.com/cilium/ebpf/internal/io.go | 16 + vendor/github.com/cilium/ebpf/internal/ptr.go | 26 + .../cilium/ebpf/{ => internal}/ptr_32_be.go | 6 +- .../cilium/ebpf/{ => internal}/ptr_32_le.go | 6 +- .../cilium/ebpf/{ => internal}/ptr_64.go | 6 +- .../cilium/ebpf/internal/syscall.go | 23 + vendor/github.com/cilium/ebpf/linker.go | 28 +- vendor/github.com/cilium/ebpf/map.go | 83 ++- vendor/github.com/cilium/ebpf/marshalers.go | 33 +- vendor/github.com/cilium/ebpf/prog.go | 120 ++-- vendor/github.com/cilium/ebpf/syscalls.go | 235 +++----- vendor/github.com/cilium/ebpf/types.go | 36 ++ vendor/github.com/cilium/ebpf/types_string.go | 21 +- 25 files changed, 2304 insertions(+), 349 deletions(-) create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/btf.go create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/btf_types.go create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/doc.go create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/ext_info.go create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/strings.go create mode 100644 vendor/github.com/cilium/ebpf/internal/btf/types.go create mode 100644 vendor/github.com/cilium/ebpf/internal/errors.go create mode 100644 vendor/github.com/cilium/ebpf/internal/fd.go create mode 100644 vendor/github.com/cilium/ebpf/internal/io.go create mode 100644 vendor/github.com/cilium/ebpf/internal/ptr.go rename vendor/github.com/cilium/ebpf/{ => internal}/ptr_32_be.go (59%) rename vendor/github.com/cilium/ebpf/{ => internal}/ptr_32_le.go (61%) rename vendor/github.com/cilium/ebpf/{ => internal}/ptr_64.go (65%) create mode 100644 vendor/github.com/cilium/ebpf/internal/syscall.go diff --git a/vendor.conf b/vendor.conf index 12a0c35e4..720c5a4f7 100644 --- a/vendor.conf +++ b/vendor.conf @@ -53,7 +53,7 @@ google.golang.org/grpc f495f5b15ae7ccda3b38c53a1bfc gotest.tools/v3 bb0d8a963040ea5048dcef1a14d8f8b58a33d4b3 # v3.0.2 # cgroups dependencies -github.com/cilium/ebpf 60c3aa43f488292fe2ee50fb8b833b383ca8ebbb +github.com/cilium/ebpf 4032b1d8aae306b7bb94a2a11002932caf88c644 # cri dependencies github.com/containerd/cri 92cb4ed9786a6cd271152ba1f862183d84701003 # master diff --git a/vendor/github.com/cilium/ebpf/abi.go b/vendor/github.com/cilium/ebpf/abi.go index 3cde33f7c..77227ffda 100644 --- a/vendor/github.com/cilium/ebpf/abi.go +++ b/vendor/github.com/cilium/ebpf/abi.go @@ -9,6 +9,7 @@ import ( "syscall" "github.com/cilium/ebpf/internal" + "github.com/pkg/errors" ) @@ -31,7 +32,7 @@ func newMapABIFromSpec(spec *MapSpec) *MapABI { } } -func newMapABIFromFd(fd *bpfFD) (string, *MapABI, error) { +func newMapABIFromFd(fd *internal.FD) (string, *MapABI, error) { info, err := bpfGetMapInfoByFD(fd) if err != nil { if errors.Cause(err) == syscall.EINVAL { @@ -50,7 +51,7 @@ func newMapABIFromFd(fd *bpfFD) (string, *MapABI, error) { }, nil } -func newMapABIFromProc(fd *bpfFD) (*MapABI, error) { +func newMapABIFromProc(fd *internal.FD) (*MapABI, error) { var abi MapABI err := scanFdInfo(fd, map[string]interface{}{ "map_type": &abi.Type, @@ -94,7 +95,7 @@ func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI { } } -func newProgramABIFromFd(fd *bpfFD) (string, *ProgramABI, error) { +func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) { info, err := bpfGetProgInfoByFD(fd) if err != nil { if errors.Cause(err) == syscall.EINVAL { @@ -105,10 +106,10 @@ func newProgramABIFromFd(fd *bpfFD) (string, *ProgramABI, error) { } var name string - if bpfName := convertCString(info.name[:]); bpfName != "" { + if bpfName := internal.CString(info.name[:]); bpfName != "" { name = bpfName } else { - name = convertCString(info.tag[:]) + name = internal.CString(info.tag[:]) } return name, &ProgramABI{ @@ -116,7 +117,7 @@ func newProgramABIFromFd(fd *bpfFD) (string, *ProgramABI, error) { }, nil } -func newProgramABIFromProc(fd *bpfFD) (string, *ProgramABI, error) { +func newProgramABIFromProc(fd *internal.FD) (string, *ProgramABI, error) { var ( abi ProgramABI name string @@ -139,8 +140,8 @@ func newProgramABIFromProc(fd *bpfFD) (string, *ProgramABI, error) { return name, &abi, nil } -func scanFdInfo(fd *bpfFD, fields map[string]interface{}) error { - raw, err := fd.value() +func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error { + raw, err := fd.Value() if err != nil { return err } diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go index 5ad1a5ec4..bf6a96d4a 100644 --- a/vendor/github.com/cilium/ebpf/collection.go +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -2,6 +2,7 @@ package ebpf import ( "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal/btf" "github.com/pkg/errors" ) @@ -55,17 +56,61 @@ func NewCollection(spec *CollectionSpec) (*Collection, error) { // NewCollectionWithOptions creates a Collection from a specification. // // Only maps referenced by at least one of the programs are initialized. -func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) { - maps := make(map[string]*Map) +func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (coll *Collection, err error) { + var ( + maps = make(map[string]*Map) + progs = make(map[string]*Program) + btfs = make(map[*btf.Spec]*btf.Handle) + ) + + defer func() { + for _, btf := range btfs { + btf.Close() + } + + if err == nil { + return + } + + for _, m := range maps { + m.Close() + } + + for _, p := range progs { + p.Close() + } + }() + + loadBTF := func(spec *btf.Spec) (*btf.Handle, error) { + if btfs[spec] != nil { + return btfs[spec], nil + } + + handle, err := btf.NewHandle(spec) + if err != nil { + return nil, err + } + + btfs[spec] = handle + return handle, nil + } + for mapName, mapSpec := range spec.Maps { - m, err := NewMap(mapSpec) + var handle *btf.Handle + if mapSpec.BTF != nil { + handle, err = loadBTF(btf.MapSpec(mapSpec.BTF)) + if err != nil && !btf.IsNotSupported(err) { + return nil, err + } + } + + m, err := newMapWithBTF(mapSpec, handle) if err != nil { return nil, errors.Wrapf(err, "map %s", mapName) } maps[mapName] = m } - progs := make(map[string]*Program) for progName, origProgSpec := range spec.Programs { progSpec := origProgSpec.Copy() @@ -91,7 +136,15 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co } } - prog, err := NewProgramWithOptions(progSpec, opts.Programs) + var handle *btf.Handle + if progSpec.BTF != nil { + handle, err = loadBTF(btf.ProgramSpec(progSpec.BTF)) + if err != nil && !btf.IsNotSupported(err) { + return nil, err + } + } + + prog, err := newProgramWithBTF(progSpec, handle, opts.Programs) if err != nil { return nil, errors.Wrapf(err, "program %s", progName) } diff --git a/vendor/github.com/cilium/ebpf/elf_reader.go b/vendor/github.com/cilium/ebpf/elf_reader.go index 3bdc0849b..c33b744f8 100644 --- a/vendor/github.com/cilium/ebpf/elf_reader.go +++ b/vendor/github.com/cilium/ebpf/elf_reader.go @@ -4,12 +4,13 @@ import ( "bytes" "debug/elf" "encoding/binary" - "fmt" "io" "os" "strings" "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" "github.com/pkg/errors" ) @@ -18,6 +19,8 @@ type elfCode struct { *elf.File symbols []elf.Symbol symbolsPerSection map[elf.SectionIndex]map[uint64]string + license string + version uint32 } // LoadCollectionSpec parses an ELF file into a CollectionSpec. @@ -33,8 +36,8 @@ func LoadCollectionSpec(file string) (*CollectionSpec, error) { } // LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec. -func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) { - f, err := elf.NewFile(code) +func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) { + f, err := elf.NewFile(rd) if err != nil { return nil, err } @@ -45,12 +48,17 @@ func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) { return nil, errors.Wrap(err, "load symbols") } - ec := &elfCode{f, symbols, symbolsPerSection(symbols)} + ec := &elfCode{f, symbols, symbolsPerSection(symbols), "", 0} + + var ( + licenseSection *elf.Section + versionSection *elf.Section + btfMaps = make(map[elf.SectionIndex]*elf.Section) + progSections = make(map[elf.SectionIndex]*elf.Section) + relSections = make(map[elf.SectionIndex]*elf.Section) + mapSections = make(map[elf.SectionIndex]*elf.Section) + ) - var licenseSection, versionSection *elf.Section - progSections := make(map[elf.SectionIndex]*elf.Section) - relSections := make(map[elf.SectionIndex]*elf.Section) - mapSections := make(map[elf.SectionIndex]*elf.Section) for i, sec := range ec.Sections { switch { case strings.HasPrefix(sec.Name, "license"): @@ -59,6 +67,8 @@ func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) { versionSection = sec case strings.HasPrefix(sec.Name, "maps"): mapSections[elf.SectionIndex(i)] = sec + case sec.Name == ".maps": + btfMaps[elf.SectionIndex(i)] = sec case sec.Type == elf.SHT_REL: if int(sec.Info) >= len(ec.Sections) { return nil, errors.Errorf("found relocation section %v for missing section %v", i, sec.Info) @@ -67,7 +77,7 @@ func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) { // Store relocations under the section index of the target idx := elf.SectionIndex(sec.Info) if relSections[idx] != nil { - return nil, errors.Errorf("section %d has multiple relocation sections", idx) + return nil, errors.Errorf("section %d has multiple relocation sections", sec.Info) } relSections[idx] = sec case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0: @@ -75,33 +85,36 @@ func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) { } } - license, err := loadLicense(licenseSection) + ec.license, err = loadLicense(licenseSection) if err != nil { return nil, errors.Wrap(err, "load license") } - version, err := loadVersion(versionSection, ec.ByteOrder) + ec.version, err = loadVersion(versionSection, ec.ByteOrder) if err != nil { return nil, errors.Wrap(err, "load version") } - maps, err := ec.loadMaps(mapSections) + btf, err := btf.LoadSpecFromReader(rd) if err != nil { + return nil, errors.Wrap(err, "load BTF") + } + + maps := make(map[string]*MapSpec) + + if err := ec.loadMaps(maps, mapSections); err != nil { return nil, errors.Wrap(err, "load maps") } - progs, libs, err := ec.loadPrograms(progSections, relSections, license, version) - if err != nil { - return nil, errors.Wrap(err, "load programs") + if len(btfMaps) > 0 { + if err := ec.loadBTFMaps(maps, btfMaps, btf); err != nil { + return nil, errors.Wrap(err, "load BTF maps") + } } - if len(libs) > 0 { - for name, prog := range progs { - prog.Instructions, err = link(prog.Instructions, libs...) - if err != nil { - return nil, errors.Wrapf(err, "program %s", name) - } - } + progs, err := ec.loadPrograms(progSections, relSections, btf) + if err != nil { + return nil, errors.Wrap(err, "load programs") } return &CollectionSpec{maps, progs}, nil @@ -128,52 +141,74 @@ func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) { return version, errors.Wrapf(err, "section %s", sec.Name) } -func (ec *elfCode) loadPrograms(progSections, relSections map[elf.SectionIndex]*elf.Section, license string, version uint32) (map[string]*ProgramSpec, []asm.Instructions, error) { +func (ec *elfCode) loadPrograms(progSections, relSections map[elf.SectionIndex]*elf.Section, btf *btf.Spec) (map[string]*ProgramSpec, error) { var ( - progs = make(map[string]*ProgramSpec) - libs []asm.Instructions + progs []*ProgramSpec + libs []*ProgramSpec ) + for idx, prog := range progSections { syms := ec.symbolsPerSection[idx] if len(syms) == 0 { - return nil, nil, errors.Errorf("section %v: missing symbols", prog.Name) + return nil, errors.Errorf("section %v: missing symbols", prog.Name) } funcSym := syms[0] if funcSym == "" { - return nil, nil, errors.Errorf("section %v: no label at start", prog.Name) + return nil, errors.Errorf("section %v: no label at start", prog.Name) } rels, err := ec.loadRelocations(relSections[idx]) if err != nil { - return nil, nil, errors.Wrapf(err, "program %s: can't load relocations", funcSym) + return nil, errors.Wrapf(err, "program %s: can't load relocations", funcSym) } - insns, err := ec.loadInstructions(prog, syms, rels) + insns, length, err := ec.loadInstructions(prog, syms, rels) if err != nil { - return nil, nil, errors.Wrapf(err, "program %s: can't unmarshal instructions", funcSym) + return nil, errors.Wrapf(err, "program %s: can't unmarshal instructions", funcSym) } - if progType, attachType := getProgType(prog.Name); progType == UnspecifiedProgram { + progType, attachType := getProgType(prog.Name) + + spec := &ProgramSpec{ + Name: funcSym, + Type: progType, + AttachType: attachType, + License: ec.license, + KernelVersion: ec.version, + Instructions: insns, + } + + if btf != nil { + spec.BTF, err = btf.Program(prog.Name, length) + if err != nil { + return nil, errors.Wrapf(err, "BTF for section %s (program %s)", prog.Name, funcSym) + } + } + + if spec.Type == UnspecifiedProgram { // There is no single name we can use for "library" sections, // since they may contain multiple functions. We'll decode the // labels they contain later on, and then link sections that way. - libs = append(libs, insns) + libs = append(libs, spec) } else { - progs[funcSym] = &ProgramSpec{ - Name: funcSym, - Type: progType, - AttachType: attachType, - License: license, - KernelVersion: version, - Instructions: insns, - } + progs = append(progs, spec) } } - return progs, libs, nil + + res := make(map[string]*ProgramSpec, len(progs)) + for _, prog := range progs { + err := link(prog, libs) + if err != nil { + return nil, errors.Wrapf(err, "program %s", prog.Name) + } + res[prog.Name] = prog + } + + return res, nil } -func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]string) (asm.Instructions, error) { +func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]string) (asm.Instructions, uint64, error) { var ( r = section.Open() insns asm.Instructions @@ -183,10 +218,10 @@ func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations m for { n, err := ins.Unmarshal(r, ec.ByteOrder) if err == io.EOF { - return insns, nil + return insns, offset, nil } if err != nil { - return nil, errors.Wrapf(err, "offset %d", offset) + return nil, 0, errors.Wrapf(err, "offset %d", offset) } ins.Symbol = symbols[offset] @@ -197,19 +232,15 @@ func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations m } } -func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[string]*MapSpec, error) { - var ( - maps = make(map[string]*MapSpec) - b = make([]byte, 1) - ) +func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section) error { for idx, sec := range mapSections { syms := ec.symbolsPerSection[idx] if len(syms) == 0 { - return nil, errors.Errorf("section %v: no symbols", sec.Name) + return errors.Errorf("section %v: no symbols", sec.Name) } if sec.Size%uint64(len(syms)) != 0 { - return nil, errors.Errorf("section %v: map descriptors are not of equal size", sec.Name) + return errors.Errorf("section %v: map descriptors are not of equal size", sec.Name) } var ( @@ -219,12 +250,11 @@ func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[ for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size { mapSym := syms[offset] if mapSym == "" { - fmt.Println(syms) - return nil, errors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) + return errors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset) } if maps[mapSym] != nil { - return nil, errors.Errorf("section %v: map %v already exists", sec.Name, mapSym) + return errors.Errorf("section %v: map %v already exists", sec.Name, mapSym) } lr := io.LimitReader(r, int64(size)) @@ -232,51 +262,152 @@ func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[ var spec MapSpec switch { case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil: - return nil, errors.Errorf("map %v: missing type", mapSym) + return errors.Errorf("map %v: missing type", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil: - return nil, errors.Errorf("map %v: missing key size", mapSym) + return errors.Errorf("map %v: missing key size", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil: - return nil, errors.Errorf("map %v: missing value size", mapSym) + return errors.Errorf("map %v: missing value size", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil: - return nil, errors.Errorf("map %v: missing max entries", mapSym) + return errors.Errorf("map %v: missing max entries", mapSym) case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil: - return nil, errors.Errorf("map %v: missing flags", mapSym) + return errors.Errorf("map %v: missing flags", mapSym) } - for { - _, err := lr.Read(b) - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - if b[0] != 0 { - return nil, errors.Errorf("map %v: unknown and non-zero fields in definition", mapSym) - } + if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil { + return errors.Errorf("map %v: unknown and non-zero fields in definition", mapSym) } maps[mapSym] = &spec } } - return maps, nil + + return nil +} + +func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec, mapSections map[elf.SectionIndex]*elf.Section, spec *btf.Spec) error { + + if spec == nil { + return errors.Errorf("missing BTF") + } + + for idx, sec := range mapSections { + syms := ec.symbolsPerSection[idx] + if len(syms) == 0 { + return errors.Errorf("section %v: no symbols", sec.Name) + } + + for _, sym := range syms { + if maps[sym] != nil { + return errors.Errorf("section %v: map %v already exists", sec.Name, sym) + } + + btfMap, err := spec.Map(sym) + if err != nil { + return errors.Wrapf(err, "map %v: can't get BTF", sym) + } + + spec, err := mapSpecFromBTF(btfMap) + if err != nil { + return errors.Wrapf(err, "map %v", sym) + } + + maps[sym] = spec + } + } + + return nil +} + +func mapSpecFromBTF(btfMap *btf.Map) (*MapSpec, error) { + var ( + mapType, flags, maxEntries uint32 + err error + ) + for _, member := range btf.MapType(btfMap).Members { + switch member.Name { + case "type": + mapType, err = uintFromBTF(member.Type) + if err != nil { + return nil, errors.Wrap(err, "can't get type") + } + + case "map_flags": + flags, err = uintFromBTF(member.Type) + if err != nil { + return nil, errors.Wrap(err, "can't get BTF map flags") + } + + case "max_entries": + maxEntries, err = uintFromBTF(member.Type) + if err != nil { + return nil, errors.Wrap(err, "can't get BTF map max entries") + } + + case "key": + case "value": + default: + return nil, errors.Errorf("unrecognized field %s in BTF map definition", member.Name) + } + } + + keySize, err := btf.Sizeof(btf.MapKey(btfMap)) + if err != nil { + return nil, errors.Wrap(err, "can't get size of BTF key") + } + + valueSize, err := btf.Sizeof(btf.MapValue(btfMap)) + if err != nil { + return nil, errors.Wrap(err, "can't get size of BTF value") + } + + return &MapSpec{ + Type: MapType(mapType), + KeySize: uint32(keySize), + ValueSize: uint32(valueSize), + MaxEntries: maxEntries, + Flags: flags, + BTF: btfMap, + }, nil +} + +// uintFromBTF resolves the __uint macro, which is a pointer to a sized +// array, e.g. for int (*foo)[10], this function will return 10. +func uintFromBTF(typ btf.Type) (uint32, error) { + ptr, ok := typ.(*btf.Pointer) + if !ok { + return 0, errors.Errorf("not a pointer: %v", typ) + } + + arr, ok := ptr.Target.(*btf.Array) + if !ok { + return 0, errors.Errorf("not a pointer to array: %v", typ) + } + + return arr.Nelems, nil } func getProgType(v string) (ProgramType, AttachType) { types := map[string]ProgramType{ // From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568 - "socket": SocketFilter, - "seccomp": SocketFilter, - "kprobe/": Kprobe, - "kretprobe/": Kprobe, - "tracepoint/": TracePoint, - "xdp": XDP, - "perf_event": PerfEvent, - "sockops": SockOps, - "sk_skb": SkSKB, - "sk_msg": SkMsg, - "lirc_mode2": LircMode2, - "flow_dissector": FlowDissector, + "socket": SocketFilter, + "seccomp": SocketFilter, + "kprobe/": Kprobe, + "uprobe/": Kprobe, + "kretprobe/": Kprobe, + "uretprobe/": Kprobe, + "tracepoint/": TracePoint, + "raw_tracepoint/": RawTracepoint, + "xdp": XDP, + "perf_event": PerfEvent, + "lwt_in": LWTIn, + "lwt_out": LWTOut, + "lwt_xmit": LWTXmit, + "lwt_seg6local": LWTSeg6Local, + "sockops": SockOps, + "sk_skb": SkSKB, + "sk_msg": SkMsg, + "lirc_mode2": LircMode2, + "flow_dissector": FlowDissector, "cgroup_skb/": CGroupSKB, "cgroup/dev": CGroupDevice, diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf.go b/vendor/github.com/cilium/ebpf/internal/btf/btf.go new file mode 100644 index 000000000..b2122f37e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf.go @@ -0,0 +1,530 @@ +package btf + +import ( + "bytes" + "debug/elf" + "encoding/binary" + "io" + "io/ioutil" + "math" + "reflect" + "unsafe" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" + + "github.com/pkg/errors" +) + +const btfMagic = 0xeB9F + +// Spec represents decoded BTF. +type Spec struct { + rawTypes []rawType + strings stringTable + types map[string][]Type + funcInfos map[string]extInfo + lineInfos map[string]extInfo +} + +type btfHeader struct { + Magic uint16 + Version uint8 + Flags uint8 + HdrLen uint32 + + TypeOff uint32 + TypeLen uint32 + StringOff uint32 + StringLen uint32 +} + +// LoadSpecFromReader reads BTF sections from an ELF. +// +// Returns a nil Spec and no error if no BTF was present. +func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) { + file, err := elf.NewFile(rd) + if err != nil { + return nil, err + } + defer file.Close() + + var ( + btfSection *elf.Section + btfExtSection *elf.Section + ) + + for _, sec := range file.Sections { + switch sec.Name { + case ".BTF": + btfSection = sec + case ".BTF.ext": + btfExtSection = sec + } + } + + if btfSection == nil { + return nil, nil + } + + spec, err := parseBTF(btfSection.Open(), file.ByteOrder) + if err != nil { + return nil, err + } + + if btfExtSection != nil { + spec.funcInfos, spec.lineInfos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings) + if err != nil { + return nil, errors.Wrap(err, "can't read ext info") + } + } + + return spec, nil +} + +func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) (*Spec, error) { + rawBTF, err := ioutil.ReadAll(btf) + if err != nil { + return nil, errors.Wrap(err, "can't read BTF") + } + + rd := bytes.NewReader(rawBTF) + + var header btfHeader + if err := binary.Read(rd, bo, &header); err != nil { + return nil, errors.Wrap(err, "can't read header") + } + + if header.Magic != btfMagic { + return nil, errors.Errorf("incorrect magic value %v", header.Magic) + } + + if header.Version != 1 { + return nil, errors.Errorf("unexpected version %v", header.Version) + } + + if header.Flags != 0 { + return nil, errors.Errorf("unsupported flags %v", header.Flags) + } + + remainder := int64(header.HdrLen) - int64(binary.Size(&header)) + if remainder < 0 { + return nil, errors.New("header is too short") + } + + if _, err := io.CopyN(internal.DiscardZeroes{}, rd, remainder); err != nil { + return nil, errors.Wrap(err, "header padding") + } + + if _, err := rd.Seek(int64(header.HdrLen+header.StringOff), io.SeekStart); err != nil { + return nil, errors.Wrap(err, "can't seek to start of string section") + } + + strings, err := readStringTable(io.LimitReader(rd, int64(header.StringLen))) + if err != nil { + return nil, errors.Wrap(err, "can't read type names") + } + + if _, err := rd.Seek(int64(header.HdrLen+header.TypeOff), io.SeekStart); err != nil { + return nil, errors.Wrap(err, "can't seek to start of type section") + } + + rawTypes, err := readTypes(io.LimitReader(rd, int64(header.TypeLen)), bo) + if err != nil { + return nil, errors.Wrap(err, "can't read types") + } + + types, err := inflateRawTypes(rawTypes, strings) + if err != nil { + return nil, err + } + + return &Spec{ + rawTypes: rawTypes, + types: types, + strings: strings, + funcInfos: make(map[string]extInfo), + lineInfos: make(map[string]extInfo), + }, nil +} + +func (s *Spec) marshal(bo binary.ByteOrder) ([]byte, error) { + var ( + buf bytes.Buffer + header = new(btfHeader) + headerLen = binary.Size(header) + ) + + // Reserve space for the header. We have to write it last since + // we don't know the size of the type section yet. + _, _ = buf.Write(make([]byte, headerLen)) + + // Write type section, just after the header. + for _, typ := range s.rawTypes { + if typ.Kind() == kindDatasec { + // Datasec requires patching with information from the ELF + // file. We don't support this at the moment, so patch + // out any Datasec by turning it into a void*. + typ = rawType{} + typ.SetKind(kindPointer) + } + + if err := typ.Marshal(&buf, bo); err != nil { + return nil, errors.Wrap(err, "can't marshal BTF") + } + } + + typeLen := uint32(buf.Len() - headerLen) + + // Write string section after type section. + _, _ = buf.Write(s.strings) + + // Fill out the header, and write it out. + header = &btfHeader{ + Magic: btfMagic, + Version: 1, + Flags: 0, + HdrLen: uint32(headerLen), + TypeOff: 0, + TypeLen: typeLen, + StringOff: typeLen, + StringLen: uint32(len(s.strings)), + } + + raw := buf.Bytes() + err := binary.Write(sliceWriter(raw[:headerLen]), bo, header) + if err != nil { + return nil, errors.Wrap(err, "can't write header") + } + + return raw, nil +} + +type sliceWriter []byte + +func (sw sliceWriter) Write(p []byte) (int, error) { + if len(p) != len(sw) { + return 0, errors.New("size doesn't match") + } + + return copy(sw, p), nil +} + +// Program finds the BTF for a specific section. +// +// Length is the number of bytes in the raw BPF instruction stream. +// +// Returns an error if there is no BTF. +func (s *Spec) Program(name string, length uint64) (*Program, error) { + if length == 0 { + return nil, errors.New("length musn't be zero") + } + + funcInfos, funcOK := s.funcInfos[name] + lineInfos, lineOK := s.lineInfos[name] + + if !funcOK && !lineOK { + return nil, errors.Errorf("no BTF for program %s", name) + } + + return &Program{s, length, funcInfos, lineInfos}, nil +} + +// Map finds the BTF for a map. +// +// Returns an error if there is no BTF for the given name. +func (s *Spec) Map(name string) (*Map, error) { + var mapVar Var + if err := s.FindType(name, &mapVar); err != nil { + return nil, err + } + + mapStruct, ok := mapVar.Type.(*Struct) + if !ok { + return nil, errors.Errorf("expected struct, have %s", mapVar.Type) + } + + var key, value Type + for _, member := range mapStruct.Members { + switch member.Name { + case "key": + key = member.Type + + case "value": + value = member.Type + } + } + + if key == nil { + return nil, errors.Errorf("map %s: missing 'key' in type", name) + } + + if value == nil { + return nil, errors.Errorf("map %s: missing 'value' in type", name) + } + + return &Map{mapStruct, s, key, value}, nil +} + +var errNotFound = errors.New("not found") + +// FindType searches for a type with a specific name. +// +// hint determines the type of the returned Type. +// +// Returns an error if there is no or multiple matches. +func (s *Spec) FindType(name string, typ Type) error { + var ( + wanted = reflect.TypeOf(typ) + candidate Type + ) + + for _, typ := range s.types[name] { + if reflect.TypeOf(typ) != wanted { + continue + } + + if candidate != nil { + return errors.Errorf("type %s: multiple candidates for %T", name, typ) + } + + candidate = typ + } + + if candidate == nil { + return errors.WithMessagef(errNotFound, "type %s", name) + } + + value := reflect.Indirect(reflect.ValueOf(copyType(candidate))) + reflect.Indirect(reflect.ValueOf(typ)).Set(value) + return nil +} + +// Handle is a reference to BTF loaded into the kernel. +type Handle struct { + fd *internal.FD +} + +// NewHandle loads BTF into the kernel. +// +// Returns an error if BTF is not supported, which can +// be checked by IsNotSupported. +func NewHandle(spec *Spec) (*Handle, error) { + if err := haveBTF(); err != nil { + return nil, err + } + + btf, err := spec.marshal(internal.NativeEndian) + if err != nil { + return nil, errors.Wrap(err, "can't marshal BTF") + } + + if uint64(len(btf)) > math.MaxUint32 { + return nil, errors.New("BTF exceeds the maximum size") + } + + attr := &bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + } + + fd, err := bpfLoadBTF(attr) + if err != nil { + logBuf := make([]byte, 64*1024) + attr.logBuf = internal.NewSlicePointer(logBuf) + attr.btfLogSize = uint32(len(logBuf)) + attr.btfLogLevel = 1 + _, logErr := bpfLoadBTF(attr) + return nil, internal.ErrorWithLog(err, logBuf, logErr) + } + + return &Handle{fd}, nil +} + +// Close destroys the handle. +// +// Subsequent calls to FD will return an invalid value. +func (h *Handle) Close() error { + return h.fd.Close() +} + +// FD returns the file descriptor for the handle. +func (h *Handle) FD() int { + value, err := h.fd.Value() + if err != nil { + return -1 + } + + return int(value) +} + +// Map is the BTF for a map. +type Map struct { + definition *Struct + spec *Spec + key, value Type +} + +// MapSpec should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapSpec(m *Map) *Spec { + return m.spec +} + +// MapType should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapType(m *Map) *Struct { + return m.definition +} + +// MapKey should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapKey(m *Map) Type { + return m.key +} + +// MapValue should be a method on Map, but is a free function +// to hide it from users of the ebpf package. +func MapValue(m *Map) Type { + return m.value +} + +// Program is the BTF information for a stream of instructions. +type Program struct { + spec *Spec + length uint64 + funcInfos, lineInfos extInfo +} + +// ProgramSpec returns the Spec needed for loading function and line infos into the kernel. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramSpec(s *Program) *Spec { + return s.spec +} + +// ProgramAppend the information from other to the Program. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramAppend(s, other *Program) error { + funcInfos, err := s.funcInfos.append(other.funcInfos, s.length) + if err != nil { + return errors.Wrap(err, "func infos") + } + + lineInfos, err := s.lineInfos.append(other.lineInfos, s.length) + if err != nil { + return errors.Wrap(err, "line infos") + } + + s.length += other.length + s.funcInfos = funcInfos + s.lineInfos = lineInfos + return nil +} + +// ProgramFuncInfos returns the binary form of BTF function infos. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) { + bytes, err = s.funcInfos.MarshalBinary() + if err != nil { + return 0, nil, err + } + + return s.funcInfos.recordSize, bytes, nil +} + +// ProgramLineInfos returns the binary form of BTF line infos. +// +// This is a free function instead of a method to hide it from users +// of package ebpf. +func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) { + bytes, err = s.lineInfos.MarshalBinary() + if err != nil { + return 0, nil, err + } + + return s.lineInfos.recordSize, bytes, nil +} + +// IsNotSupported returns true if the error indicates that the kernel +// doesn't support BTF. +func IsNotSupported(err error) bool { + ufe, ok := errors.Cause(err).(*internal.UnsupportedFeatureError) + return ok && ufe.Name == "BTF" +} + +type bpfLoadBTFAttr struct { + btf internal.Pointer + logBuf internal.Pointer + btfSize uint32 + btfLogSize uint32 + btfLogLevel uint32 +} + +func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) { + const _BTFLoad = 18 + + fd, err := internal.BPF(_BTFLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + + return internal.NewFD(uint32(fd)), nil +} + +func minimalBTF(bo binary.ByteOrder) []byte { + const minHeaderLength = 24 + + var ( + types struct { + Integer btfType + Var btfType + btfVar struct{ Linkage uint32 } + } + typLen = uint32(binary.Size(&types)) + strings = []byte{0, 'a', 0} + header = btfHeader{ + Magic: btfMagic, + Version: 1, + HdrLen: minHeaderLength, + TypeOff: 0, + TypeLen: typLen, + StringOff: typLen, + StringLen: uint32(len(strings)), + } + ) + + // We use a BTF_KIND_VAR here, to make sure that + // the kernel understands BTF at least as well as we + // do. BTF_KIND_VAR was introduced ~5.1. + types.Integer.SetKind(kindPointer) + types.Var.NameOff = 1 + types.Var.SetKind(kindVar) + types.Var.SizeType = 1 + + buf := new(bytes.Buffer) + _ = binary.Write(buf, bo, &header) + _ = binary.Write(buf, bo, &types) + buf.Write(strings) + + return buf.Bytes() +} + +var haveBTF = internal.FeatureTest("BTF", "5.1", func() bool { + btf := minimalBTF(internal.NativeEndian) + fd, err := bpfLoadBTF(&bpfLoadBTFAttr{ + btf: internal.NewSlicePointer(btf), + btfSize: uint32(len(btf)), + }) + if err == nil { + fd.Close() + } + // Check for EINVAL specifically, rather than err != nil since we + // otherwise misdetect due to insufficient permissions. + return errors.Cause(err) != unix.EINVAL +}) diff --git a/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go new file mode 100644 index 000000000..6570fedff --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/btf_types.go @@ -0,0 +1,190 @@ +package btf + +import ( + "encoding/binary" + "io" + + "github.com/pkg/errors" +) + +// btfKind describes a Type. +type btfKind uint8 + +// Equivalents of the BTF_KIND_* constants. +const ( + kindUnknown btfKind = iota + kindInt + kindPointer + kindArray + kindStruct + kindUnion + kindEnum + kindForward + kindTypedef + kindVolatile + kindConst + kindRestrict + // Added ~4.20 + kindFunc + kindFuncProto + // Added ~5.1 + kindVar + kindDatasec +) + +const ( + btfTypeKindShift = 24 + btfTypeKindLen = 4 + btfTypeVlenShift = 0 + btfTypeVlenMask = 16 +) + +// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst. +type btfType struct { + NameOff uint32 + /* "info" bits arrangement + * bits 0-15: vlen (e.g. # of struct's members) + * bits 16-23: unused + * bits 24-27: kind (e.g. int, ptr, array...etc) + * bits 28-30: unused + * bit 31: kind_flag, currently used by + * struct, union and fwd + */ + Info uint32 + /* "size" is used by INT, ENUM, STRUCT and UNION. + * "size" tells the size of the type it is describing. + * + * "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT, + * FUNC and FUNC_PROTO. + * "type" is a type_id referring to another type. + */ + SizeType uint32 +} + +func mask(len uint32) uint32 { + return (1 << len) - 1 +} + +func (bt *btfType) info(len, shift uint32) uint32 { + return (bt.Info >> shift) & mask(len) +} + +func (bt *btfType) setInfo(value, len, shift uint32) { + bt.Info &^= mask(len) << shift + bt.Info |= (value & mask(len)) << shift +} + +func (bt *btfType) Kind() btfKind { + return btfKind(bt.info(btfTypeKindLen, btfTypeKindShift)) +} + +func (bt *btfType) SetKind(kind btfKind) { + bt.setInfo(uint32(kind), btfTypeKindLen, btfTypeKindShift) +} + +func (bt *btfType) Vlen() int { + return int(bt.info(btfTypeVlenMask, btfTypeVlenShift)) +} + +func (bt *btfType) SetVlen(vlen int) { + bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift) +} + +func (bt *btfType) Type() TypeID { + // TODO: Panic here if wrong kind? + return TypeID(bt.SizeType) +} + +func (bt *btfType) Size() uint32 { + // TODO: Panic here if wrong kind? + return bt.SizeType +} + +type rawType struct { + btfType + data interface{} +} + +func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error { + if err := binary.Write(w, bo, &rt.btfType); err != nil { + return err + } + + if rt.data == nil { + return nil + } + + return binary.Write(w, bo, rt.data) +} + +type btfArray struct { + Type TypeID + IndexType TypeID + Nelems uint32 +} + +type btfMember struct { + NameOff uint32 + Type TypeID + Offset uint32 +} + +func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) { + var ( + header btfType + types []rawType + ) + + for id := TypeID(1); ; id++ { + if err := binary.Read(r, bo, &header); err == io.EOF { + return types, nil + } else if err != nil { + return nil, errors.Wrapf(err, "can't read type info for id %v", id) + } + + var data interface{} + switch header.Kind() { + case kindInt: + // sizeof(uint32) + data = make([]byte, 4) + case kindPointer: + case kindArray: + data = new(btfArray) + case kindStruct: + fallthrough + case kindUnion: + data = make([]btfMember, header.Vlen()) + case kindEnum: + // sizeof(struct btf_enum) + data = make([]byte, header.Vlen()*4*2) + case kindForward: + case kindTypedef: + case kindVolatile: + case kindConst: + case kindRestrict: + case kindFunc: + case kindFuncProto: + // sizeof(struct btf_param) + data = make([]byte, header.Vlen()*4*2) + case kindVar: + // sizeof(struct btf_variable) + data = make([]byte, 4) + case kindDatasec: + // sizeof(struct btf_var_secinfo) + data = make([]byte, header.Vlen()*4*3) + default: + return nil, errors.Errorf("type id %v: unknown kind: %v", id, header.Kind()) + } + + if data == nil { + types = append(types, rawType{header, nil}) + continue + } + + if err := binary.Read(r, bo, data); err != nil { + return nil, errors.Wrapf(err, "type id %d: kind %v: can't read %T", id, header.Kind(), data) + } + + types = append(types, rawType{header, data}) + } +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/doc.go b/vendor/github.com/cilium/ebpf/internal/btf/doc.go new file mode 100644 index 000000000..ad2576cb2 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/doc.go @@ -0,0 +1,8 @@ +// Package btf handles data encoded according to the BPF Type Format. +// +// The canonical documentation lives in the Linux kernel repository and is +// available at https://www.kernel.org/doc/html/latest/bpf/btf.html +// +// The API is very much unstable. You should only use this via the main +// ebpf library. +package btf diff --git a/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go new file mode 100644 index 000000000..ab019cac7 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/ext_info.go @@ -0,0 +1,184 @@ +package btf + +import ( + "bytes" + "encoding/binary" + "io" + "io/ioutil" + + "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal" + + "github.com/pkg/errors" +) + +type btfExtHeader struct { + Magic uint16 + Version uint8 + Flags uint8 + HdrLen uint32 + + FuncInfoOff uint32 + FuncInfoLen uint32 + LineInfoOff uint32 + LineInfoLen uint32 +} + +func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, err error) { + const expectedMagic = 0xeB9F + + var header btfExtHeader + if err := binary.Read(r, bo, &header); err != nil { + return nil, nil, errors.Wrap(err, "can't read header") + } + + if header.Magic != expectedMagic { + return nil, nil, errors.Errorf("incorrect magic value %v", header.Magic) + } + + if header.Version != 1 { + return nil, nil, errors.Errorf("unexpected version %v", header.Version) + } + + if header.Flags != 0 { + return nil, nil, errors.Errorf("unsupported flags %v", header.Flags) + } + + remainder := int64(header.HdrLen) - int64(binary.Size(&header)) + if remainder < 0 { + return nil, nil, errors.New("header is too short") + } + + // Of course, the .BTF.ext header has different semantics than the + // .BTF ext header. We need to ignore non-null values. + _, err = io.CopyN(ioutil.Discard, r, remainder) + if err != nil { + return nil, nil, errors.Wrap(err, "header padding") + } + + if _, err := r.Seek(int64(header.HdrLen+header.FuncInfoOff), io.SeekStart); err != nil { + return nil, nil, errors.Wrap(err, "can't seek to function info section") + } + + funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings) + if err != nil { + return nil, nil, errors.Wrap(err, "function info") + } + + if _, err := r.Seek(int64(header.HdrLen+header.LineInfoOff), io.SeekStart); err != nil { + return nil, nil, errors.Wrap(err, "can't seek to line info section") + } + + lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings) + if err != nil { + return nil, nil, errors.Wrap(err, "line info") + } + + return funcInfo, lineInfo, nil +} + +type btfExtInfoSec struct { + SecNameOff uint32 + NumInfo uint32 +} + +type extInfoRecord struct { + InsnOff uint64 + Opaque []byte +} + +type extInfo struct { + recordSize uint32 + records []extInfoRecord +} + +func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) { + if other.recordSize != ei.recordSize { + return extInfo{}, errors.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize) + } + + records := make([]extInfoRecord, 0, len(ei.records)+len(other.records)) + records = append(records, ei.records...) + for _, info := range other.records { + records = append(records, extInfoRecord{ + InsnOff: info.InsnOff + offset, + Opaque: info.Opaque, + }) + } + return extInfo{ei.recordSize, records}, nil +} + +func (ei extInfo) MarshalBinary() ([]byte, error) { + if len(ei.records) == 0 { + return nil, nil + } + + buf := bytes.NewBuffer(make([]byte, 0, int(ei.recordSize)*len(ei.records))) + for _, info := range ei.records { + // The kernel expects offsets in number of raw bpf instructions, + // while the ELF tracks it in bytes. + insnOff := uint32(info.InsnOff / asm.InstructionSize) + if err := binary.Write(buf, internal.NativeEndian, insnOff); err != nil { + return nil, errors.Wrap(err, "can't write instruction offset") + } + + buf.Write(info.Opaque) + } + + return buf.Bytes(), nil +} + +func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) { + var recordSize uint32 + if err := binary.Read(r, bo, &recordSize); err != nil { + return nil, errors.Wrap(err, "can't read record size") + } + + if recordSize < 4 { + // Need at least insnOff + return nil, errors.New("record size too short") + } + + result := make(map[string]extInfo) + for { + var infoHeader btfExtInfoSec + if err := binary.Read(r, bo, &infoHeader); err == io.EOF { + return result, nil + } else if err != nil { + return nil, errors.Wrap(err, "can't read ext info header") + } + + secName, err := strings.Lookup(infoHeader.SecNameOff) + if err != nil { + return nil, errors.Wrap(err, "can't get section name") + } + + if infoHeader.NumInfo == 0 { + return nil, errors.Errorf("section %s has invalid number of records", secName) + } + + var records []extInfoRecord + for i := uint32(0); i < infoHeader.NumInfo; i++ { + var byteOff uint32 + if err := binary.Read(r, bo, &byteOff); err != nil { + return nil, errors.Wrapf(err, "section %v: can't read extended info offset", secName) + } + + buf := make([]byte, int(recordSize-4)) + if _, err := io.ReadFull(r, buf); err != nil { + return nil, errors.Wrapf(err, "section %v: can't read record", secName) + } + + if byteOff%asm.InstructionSize != 0 { + return nil, errors.Errorf("section %v: offset %v is not aligned with instruction size", secName, byteOff) + } + + records = append(records, extInfoRecord{uint64(byteOff), buf}) + } + + result[secName] = extInfo{ + recordSize, + records, + } + } +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/strings.go b/vendor/github.com/cilium/ebpf/internal/btf/strings.go new file mode 100644 index 000000000..c0337649e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/strings.go @@ -0,0 +1,60 @@ +package btf + +import ( + "bytes" + "io" + "io/ioutil" + + "github.com/pkg/errors" +) + +type stringTable []byte + +func readStringTable(r io.Reader) (stringTable, error) { + contents, err := ioutil.ReadAll(r) + if err != nil { + return nil, errors.Wrap(err, "can't read string table") + } + + if len(contents) < 1 { + return nil, errors.New("string table is empty") + } + + if contents[0] != '\x00' { + return nil, errors.New("first item in string table is non-empty") + } + + if contents[len(contents)-1] != '\x00' { + return nil, errors.New("string table isn't null terminated") + } + + return stringTable(contents), nil +} + +func (st stringTable) Lookup(offset uint32) (string, error) { + if int64(offset) > int64(^uint(0)>>1) { + return "", errors.Errorf("offset %d overflows int", offset) + } + + pos := int(offset) + if pos >= len(st) { + return "", errors.Errorf("offset %d is out of bounds", offset) + } + + if pos > 0 && st[pos-1] != '\x00' { + return "", errors.Errorf("offset %d isn't start of a string", offset) + } + + str := st[pos:] + end := bytes.IndexByte(str, '\x00') + if end == -1 { + return "", errors.Errorf("offset %d isn't null terminated", offset) + } + + return string(str[:end]), nil +} + +func (st stringTable) LookupName(offset uint32) (Name, error) { + str, err := st.Lookup(offset) + return Name(str), err +} diff --git a/vendor/github.com/cilium/ebpf/internal/btf/types.go b/vendor/github.com/cilium/ebpf/internal/btf/types.go new file mode 100644 index 000000000..c49cb8621 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/btf/types.go @@ -0,0 +1,550 @@ +package btf + +import ( + "math" + + "github.com/pkg/errors" +) + +const maxTypeDepth = 32 + +// TypeID identifies a type in a BTF section. +type TypeID uint32 + +// ID implements part of the Type interface. +func (tid TypeID) ID() TypeID { + return tid +} + +// Type represents a type described by BTF. +type Type interface { + ID() TypeID + + // Make a copy of the type, without copying Type members. + copy() Type + + walk(*copyStack) +} + +// Name identifies a type. +// +// Anonymous types have an empty name. +type Name string + +func (n Name) name() string { + return string(n) +} + +// Void is the unit type of BTF. +type Void struct{} + +func (v Void) ID() TypeID { return 0 } +func (v Void) copy() Type { return Void{} } +func (v Void) walk(*copyStack) {} + +// Int is an integer of a given length. +type Int struct { + TypeID + Name + + // The size of the integer in bytes. + Size uint32 +} + +func (i *Int) size() uint32 { return i.Size } +func (i *Int) walk(*copyStack) {} +func (i *Int) copy() Type { + cpy := *i + return &cpy +} + +// Pointer is a pointer to another type. +type Pointer struct { + TypeID + Target Type +} + +func (p *Pointer) size() uint32 { return 8 } +func (p *Pointer) walk(cs *copyStack) { cs.push(&p.Target) } +func (p *Pointer) copy() Type { + cpy := *p + return &cpy +} + +// Array is an array with a fixed number of elements. +type Array struct { + TypeID + Type Type + Nelems uint32 +} + +func (arr *Array) walk(cs *copyStack) { cs.push(&arr.Type) } +func (arr *Array) copy() Type { + cpy := *arr + return &cpy +} + +// Struct is a compound type of consecutive members. +type Struct struct { + TypeID + Name + // The size of the struct including padding, in bytes + Size uint32 + Members []Member +} + +func (s *Struct) size() uint32 { return s.Size } + +func (s *Struct) walk(cs *copyStack) { + for i := range s.Members { + cs.push(&s.Members[i].Type) + } +} + +func (s *Struct) copy() Type { + cpy := *s + cpy.Members = copyMembers(cpy.Members) + return &cpy +} + +// Union is a compound type where members occupy the same memory. +type Union struct { + TypeID + Name + // The size of the union including padding, in bytes. + Size uint32 + Members []Member +} + +func (u *Union) size() uint32 { return u.Size } + +func (u *Union) walk(cs *copyStack) { + for i := range u.Members { + cs.push(&u.Members[i].Type) + } +} + +func (u *Union) copy() Type { + cpy := *u + cpy.Members = copyMembers(cpy.Members) + return &cpy +} + +// Member is part of a Struct or Union. +// +// It is not a valid Type. +type Member struct { + Name + Type Type + Offset uint32 +} + +func copyMembers(in []Member) []Member { + cpy := make([]Member, 0, len(in)) + for _, member := range in { + cpy = append(cpy, member) + } + return cpy +} + +// Enum lists possible values. +type Enum struct { + TypeID + Name +} + +func (e *Enum) size() uint32 { return 4 } +func (e *Enum) walk(*copyStack) {} +func (e *Enum) copy() Type { + cpy := *e + return &cpy +} + +// Fwd is a forward declaration of a Type. +type Fwd struct { + TypeID + Name +} + +func (f *Fwd) walk(*copyStack) {} +func (f *Fwd) copy() Type { + cpy := *f + return &cpy +} + +// Typedef is an alias of a Type. +type Typedef struct { + TypeID + Name + Type Type +} + +func (td *Typedef) walk(cs *copyStack) { cs.push(&td.Type) } +func (td *Typedef) copy() Type { + cpy := *td + return &cpy +} + +// Volatile is a modifier. +type Volatile struct { + TypeID + Type Type +} + +func (v *Volatile) walk(cs *copyStack) { cs.push(&v.Type) } +func (v *Volatile) copy() Type { + cpy := *v + return &cpy +} + +// Const is a modifier. +type Const struct { + TypeID + Type Type +} + +func (c *Const) walk(cs *copyStack) { cs.push(&c.Type) } +func (c *Const) copy() Type { + cpy := *c + return &cpy +} + +// Restrict is a modifier. +type Restrict struct { + TypeID + Type Type +} + +func (r *Restrict) walk(cs *copyStack) { cs.push(&r.Type) } +func (r *Restrict) copy() Type { + cpy := *r + return &cpy +} + +// Func is a function definition. +type Func struct { + TypeID + Name + Type Type +} + +func (f *Func) walk(cs *copyStack) { cs.push(&f.Type) } +func (f *Func) copy() Type { + cpy := *f + return &cpy +} + +// FuncProto is a function declaration. +type FuncProto struct { + TypeID + Return Type + // Parameters not supported yet +} + +func (fp *FuncProto) walk(cs *copyStack) { cs.push(&fp.Return) } +func (fp *FuncProto) copy() Type { + cpy := *fp + return &cpy +} + +// Var is a global variable. +type Var struct { + TypeID + Name + Type Type +} + +func (v *Var) walk(cs *copyStack) { cs.push(&v.Type) } +func (v *Var) copy() Type { + cpy := *v + return &cpy +} + +// Datasec is a global program section containing data. +type Datasec struct { + TypeID + Name + Size uint32 +} + +func (ds *Datasec) size() uint32 { return ds.Size } +func (ds *Datasec) walk(*copyStack) {} +func (ds *Datasec) copy() Type { + cpy := *ds + return &cpy +} + +type sizer interface { + size() uint32 +} + +var ( + _ sizer = (*Int)(nil) + _ sizer = (*Pointer)(nil) + _ sizer = (*Struct)(nil) + _ sizer = (*Union)(nil) + _ sizer = (*Enum)(nil) + _ sizer = (*Datasec)(nil) +) + +// Sizeof returns the size of a type in bytes. +// +// Returns an error if the size can't be computed. +func Sizeof(typ Type) (int, error) { + var ( + n = int64(1) + elem int64 + ) + + for i := 0; i < maxTypeDepth; i++ { + switch v := typ.(type) { + case *Array: + if n > 0 && int64(v.Nelems) > math.MaxInt64/n { + return 0, errors.New("overflow") + } + + // Arrays may be of zero length, which allows + // n to be zero as well. + n *= int64(v.Nelems) + typ = v.Type + continue + + case sizer: + elem = int64(v.size()) + + case *Typedef: + typ = v.Type + continue + case *Volatile: + typ = v.Type + continue + case *Const: + typ = v.Type + continue + case *Restrict: + typ = v.Type + continue + + default: + return 0, errors.Errorf("unrecognized type %T", typ) + } + + if n > 0 && elem > math.MaxInt64/n { + return 0, errors.New("overflow") + } + + size := n * elem + if int64(int(size)) != size { + return 0, errors.New("overflow") + } + + return int(size), nil + } + + return 0, errors.New("exceeded type depth") +} + +// copy a Type recursively. +// +// typ may form a cycle. +func copyType(typ Type) Type { + var ( + copies = make(map[Type]Type) + work copyStack + ) + + for t := &typ; t != nil; t = work.pop() { + // *t is the identity of the type. + if cpy := copies[*t]; cpy != nil { + *t = cpy + continue + } + + cpy := (*t).copy() + copies[*t] = cpy + *t = cpy + + // Mark any nested types for copying. + cpy.walk(&work) + } + + return typ +} + +// copyStack keeps track of pointers to types which still +// need to be copied. +type copyStack []*Type + +// push adds a type to the stack. +func (cs *copyStack) push(t *Type) { + *cs = append(*cs, t) +} + +// pop returns the topmost Type, or nil. +func (cs *copyStack) pop() *Type { + n := len(*cs) + if n == 0 { + return nil + } + + t := (*cs)[n-1] + *cs = (*cs)[:n-1] + return t +} + +type namer interface { + name() string +} + +var _ namer = Name("") + +// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns +// it into a graph of Types connected via pointers. +// +// Returns a map of named types (so, where NameOff is non-zero). Since BTF ignores +// compilation units, multiple types may share the same name. A Type may form a +// cyclic graph by pointing at itself. +func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map[string][]Type, err error) { + type fixup struct { + id TypeID + typ *Type + } + + var fixups []fixup + convertMembers := func(raw []btfMember) ([]Member, error) { + // NB: The fixup below relies on pre-allocating this array to + // work, since otherwise append might re-allocate members. + members := make([]Member, 0, len(raw)) + for i, btfMember := range raw { + name, err := rawStrings.LookupName(btfMember.NameOff) + if err != nil { + return nil, errors.Wrapf(err, "can't get name for member %d", i) + } + members = append(members, Member{ + Name: name, + Offset: btfMember.Offset, + }) + } + for i := range members { + fixups = append(fixups, fixup{raw[i].Type, &members[i].Type}) + } + return members, nil + } + + types := make([]Type, 0, len(rawTypes)) + types = append(types, Void{}) + namedTypes = make(map[string][]Type) + + for i, raw := range rawTypes { + var ( + // Void is defined to always be type ID 0, and is thus + // omitted from BTF. + id = TypeID(i + 1) + typ Type + ) + + name, err := rawStrings.LookupName(raw.NameOff) + if err != nil { + return nil, errors.Wrapf(err, "can't get name for type id %d", id) + } + + switch raw.Kind() { + case kindInt: + typ = &Int{id, name, raw.Size()} + + case kindPointer: + ptr := &Pointer{id, nil} + fixups = append(fixups, fixup{raw.Type(), &ptr.Target}) + typ = ptr + + case kindArray: + btfArr := raw.data.(*btfArray) + + // IndexType is unused according to btf.rst. + // Don't make it available right now. + arr := &Array{id, nil, btfArr.Nelems} + fixups = append(fixups, fixup{btfArr.Type, &arr.Type}) + typ = arr + + case kindStruct: + members, err := convertMembers(raw.data.([]btfMember)) + if err != nil { + return nil, errors.Wrapf(err, "struct %s (id %d)", name, id) + } + typ = &Struct{id, name, raw.Size(), members} + + case kindUnion: + members, err := convertMembers(raw.data.([]btfMember)) + if err != nil { + return nil, errors.Wrapf(err, "union %s (id %d)", name, id) + } + typ = &Union{id, name, raw.Size(), members} + + case kindEnum: + typ = &Enum{id, name} + + case kindForward: + typ = &Fwd{id, name} + + case kindTypedef: + typedef := &Typedef{id, name, nil} + fixups = append(fixups, fixup{raw.Type(), &typedef.Type}) + typ = typedef + + case kindVolatile: + volatile := &Volatile{id, nil} + fixups = append(fixups, fixup{raw.Type(), &volatile.Type}) + typ = volatile + + case kindConst: + cnst := &Const{id, nil} + fixups = append(fixups, fixup{raw.Type(), &cnst.Type}) + typ = cnst + + case kindRestrict: + restrict := &Restrict{id, nil} + fixups = append(fixups, fixup{raw.Type(), &restrict.Type}) + typ = restrict + + case kindFunc: + fn := &Func{id, name, nil} + fixups = append(fixups, fixup{raw.Type(), &fn.Type}) + typ = fn + + case kindFuncProto: + fp := &FuncProto{id, nil} + fixups = append(fixups, fixup{raw.Type(), &fp.Return}) + typ = fp + + case kindVar: + v := &Var{id, name, nil} + fixups = append(fixups, fixup{raw.Type(), &v.Type}) + typ = v + + case kindDatasec: + typ = &Datasec{id, name, raw.SizeType} + + default: + return nil, errors.Errorf("type id %d: unknown kind: %v", id, raw.Kind()) + } + + types = append(types, typ) + + if namer, ok := typ.(namer); ok { + if name := namer.name(); name != "" { + namedTypes[name] = append(namedTypes[name], typ) + } + } + } + + for _, fixup := range fixups { + i := int(fixup.id) + if i >= len(types) { + return nil, errors.Errorf("reference to invalid type id: %d", fixup.id) + } + + *fixup.typ = types[i] + } + + return namedTypes, nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/errors.go b/vendor/github.com/cilium/ebpf/internal/errors.go new file mode 100644 index 000000000..9590fe84e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/errors.go @@ -0,0 +1,50 @@ +package internal + +import ( + "bytes" + "fmt" + "strings" + + "github.com/cilium/ebpf/internal/unix" + "github.com/pkg/errors" +) + +// ErrorWithLog returns an error that includes logs from the +// kernel verifier. +// +// logErr should be the error returned by the syscall that generated +// the log. It is used to check for truncation of the output. +func ErrorWithLog(err error, log []byte, logErr error) error { + logStr := strings.Trim(CString(log), "\t\r\n ") + if errors.Cause(logErr) == unix.ENOSPC { + logStr += " (truncated...)" + } + + return &loadError{err, logStr} +} + +type loadError struct { + cause error + log string +} + +func (le *loadError) Error() string { + if le.log == "" { + return le.cause.Error() + } + + return fmt.Sprintf("%s: %s", le.cause, le.log) +} + +func (le *loadError) Cause() error { + return le.cause +} + +// CString turns a NUL / zero terminated byte buffer into a string. +func CString(in []byte) string { + inLen := bytes.IndexByte(in, 0) + if inLen == -1 { + return "" + } + return string(in[:inLen]) +} diff --git a/vendor/github.com/cilium/ebpf/internal/fd.go b/vendor/github.com/cilium/ebpf/internal/fd.go new file mode 100644 index 000000000..6800c84aa --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/fd.go @@ -0,0 +1,63 @@ +package internal + +import ( + "runtime" + "strconv" + + "github.com/cilium/ebpf/internal/unix" + + "github.com/pkg/errors" +) + +var ErrClosedFd = errors.New("use of closed file descriptor") + +type FD struct { + raw int64 +} + +func NewFD(value uint32) *FD { + fd := &FD{int64(value)} + runtime.SetFinalizer(fd, (*FD).Close) + return fd +} + +func (fd *FD) String() string { + return strconv.FormatInt(fd.raw, 10) +} + +func (fd *FD) Value() (uint32, error) { + if fd.raw < 0 { + return 0, ErrClosedFd + } + + return uint32(fd.raw), nil +} + +func (fd *FD) Close() error { + if fd.raw < 0 { + return nil + } + + value := int(fd.raw) + fd.raw = -1 + + fd.Forget() + return unix.Close(value) +} + +func (fd *FD) Forget() { + runtime.SetFinalizer(fd, nil) +} + +func (fd *FD) Dup() (*FD, error) { + if fd.raw < 0 { + return nil, ErrClosedFd + } + + dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0) + if err != nil { + return nil, errors.Wrap(err, "can't dup fd") + } + + return NewFD(uint32(dup)), nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/io.go b/vendor/github.com/cilium/ebpf/internal/io.go new file mode 100644 index 000000000..756e86119 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/io.go @@ -0,0 +1,16 @@ +package internal + +import "github.com/pkg/errors" + +// DiscardZeroes makes sure that all written bytes are zero +// before discarding them. +type DiscardZeroes struct{} + +func (DiscardZeroes) Write(p []byte) (int, error) { + for _, b := range p { + if b != 0 { + return 0, errors.New("encountered non-zero byte") + } + } + return len(p), nil +} diff --git a/vendor/github.com/cilium/ebpf/internal/ptr.go b/vendor/github.com/cilium/ebpf/internal/ptr.go new file mode 100644 index 000000000..e79b5aa00 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/ptr.go @@ -0,0 +1,26 @@ +package internal + +import "unsafe" + +// NewPointer creates a 64-bit pointer from an unsafe Pointer. +func NewPointer(ptr unsafe.Pointer) Pointer { + return Pointer{ptr: ptr} +} + +// NewSlicePointer creates a 64-bit pointer from a byte slice. +func NewSlicePointer(buf []byte) Pointer { + if len(buf) == 0 { + return Pointer{} + } + + return Pointer{ptr: unsafe.Pointer(&buf[0])} +} + +// NewStringPointer creates a 64-bit pointer from a string. +func NewStringPointer(str string) Pointer { + if str == "" { + return Pointer{} + } + + return Pointer{ptr: unsafe.Pointer(&[]byte(str)[0])} +} diff --git a/vendor/github.com/cilium/ebpf/ptr_32_be.go b/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go similarity index 59% rename from vendor/github.com/cilium/ebpf/ptr_32_be.go rename to vendor/github.com/cilium/ebpf/internal/ptr_32_be.go index 775774436..a56fbcc8e 100644 --- a/vendor/github.com/cilium/ebpf/ptr_32_be.go +++ b/vendor/github.com/cilium/ebpf/internal/ptr_32_be.go @@ -1,14 +1,14 @@ // +build armbe mips mips64p32 -package ebpf +package internal import ( "unsafe" ) -// ptr wraps an unsafe.Pointer to be 64bit to +// Pointer wraps an unsafe.Pointer to be 64bit to // conform to the syscall specification. -type syscallPtr struct { +type Pointer struct { pad uint32 ptr unsafe.Pointer } diff --git a/vendor/github.com/cilium/ebpf/ptr_32_le.go b/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go similarity index 61% rename from vendor/github.com/cilium/ebpf/ptr_32_le.go rename to vendor/github.com/cilium/ebpf/internal/ptr_32_le.go index 14b805e92..be2ecfca7 100644 --- a/vendor/github.com/cilium/ebpf/ptr_32_le.go +++ b/vendor/github.com/cilium/ebpf/internal/ptr_32_le.go @@ -1,14 +1,14 @@ // +build 386 amd64p32 arm mipsle mips64p32le -package ebpf +package internal import ( "unsafe" ) -// ptr wraps an unsafe.Pointer to be 64bit to +// Pointer wraps an unsafe.Pointer to be 64bit to // conform to the syscall specification. -type syscallPtr struct { +type Pointer struct { ptr unsafe.Pointer pad uint32 } diff --git a/vendor/github.com/cilium/ebpf/ptr_64.go b/vendor/github.com/cilium/ebpf/internal/ptr_64.go similarity index 65% rename from vendor/github.com/cilium/ebpf/ptr_64.go rename to vendor/github.com/cilium/ebpf/internal/ptr_64.go index c897d7273..69452dceb 100644 --- a/vendor/github.com/cilium/ebpf/ptr_64.go +++ b/vendor/github.com/cilium/ebpf/internal/ptr_64.go @@ -1,14 +1,14 @@ // +build !386,!amd64p32,!arm,!mipsle,!mips64p32le // +build !armbe,!mips,!mips64p32 -package ebpf +package internal import ( "unsafe" ) -// ptr wraps an unsafe.Pointer to be 64bit to +// Pointer wraps an unsafe.Pointer to be 64bit to // conform to the syscall specification. -type syscallPtr struct { +type Pointer struct { ptr unsafe.Pointer } diff --git a/vendor/github.com/cilium/ebpf/internal/syscall.go b/vendor/github.com/cilium/ebpf/internal/syscall.go new file mode 100644 index 000000000..b32cf3bce --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/syscall.go @@ -0,0 +1,23 @@ +package internal + +import ( + "runtime" + "unsafe" + + "github.com/cilium/ebpf/internal/unix" +) + +// BPF wraps SYS_BPF. +// +// Any pointers contained in attr must use the Pointer type from this package. +func BPF(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) { + r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) + runtime.KeepAlive(attr) + + var err error + if errNo != 0 { + err = errNo + } + + return r1, err +} diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go index da556c226..25f2ab947 100644 --- a/vendor/github.com/cilium/ebpf/linker.go +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -2,23 +2,35 @@ package ebpf import ( "github.com/cilium/ebpf/asm" + "github.com/cilium/ebpf/internal/btf" + "github.com/pkg/errors" ) // link resolves bpf-to-bpf calls. // -// Each section may contain multiple functions / labels, and is only linked +// Each library may contain multiple functions / labels, and is only linked // if the program being edited references one of these functions. // -// Sections must not require linking themselves. -func link(insns asm.Instructions, sections ...asm.Instructions) (asm.Instructions, error) { - for _, section := range sections { - var err error - insns, err = linkSection(insns, section) +// Libraries must not require linking themselves. +func link(prog *ProgramSpec, libs []*ProgramSpec) error { + for _, lib := range libs { + insns, err := linkSection(prog.Instructions, lib.Instructions) if err != nil { - return nil, err + return errors.Wrapf(err, "linking %s", lib.Name) + } + + if len(insns) == len(prog.Instructions) { + continue + } + + prog.Instructions = insns + if prog.BTF != nil && lib.BTF != nil { + if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil { + return errors.Wrapf(err, "linking BTF of %s", lib.Name) + } } } - return insns, nil + return nil } func linkSection(insns, section asm.Instructions) (asm.Instructions, error) { diff --git a/vendor/github.com/cilium/ebpf/map.go b/vendor/github.com/cilium/ebpf/map.go index a8e1eab34..86eff1aef 100644 --- a/vendor/github.com/cilium/ebpf/map.go +++ b/vendor/github.com/cilium/ebpf/map.go @@ -2,9 +2,9 @@ package ebpf import ( "fmt" - "unsafe" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" "github.com/pkg/errors" @@ -20,8 +20,12 @@ type MapSpec struct { ValueSize uint32 MaxEntries uint32 Flags uint32 + // InnerMap is used as a template for ArrayOfMaps and HashOfMaps InnerMap *MapSpec + + // The BTF associated with this map. + BTF *btf.Map } func (ms *MapSpec) String() string { @@ -50,7 +54,7 @@ func (ms *MapSpec) Copy() *MapSpec { // if you require custom encoding. type Map struct { name string - fd *bpfFD + fd *internal.FD abi MapABI // Per CPU maps return values larger than the size in the spec fullValueSize int @@ -63,11 +67,11 @@ func NewMapFromFD(fd int) (*Map, error) { if fd < 0 { return nil, errors.New("invalid fd") } - bpfFd := newBPFFD(uint32(fd)) + bpfFd := internal.NewFD(uint32(fd)) name, abi, err := newMapABIFromFd(bpfFd) if err != nil { - bpfFd.forget() + bpfFd.Forget() return nil, err } return newMap(bpfFd, name, abi) @@ -78,24 +82,37 @@ func NewMapFromFD(fd int) (*Map, error) { // Creating a map for the first time will perform feature detection // by creating small, temporary maps. func NewMap(spec *MapSpec) (*Map, error) { + if spec.BTF == nil { + return newMapWithBTF(spec, nil) + } + + handle, err := btf.NewHandle(btf.MapSpec(spec.BTF)) + if err != nil && !btf.IsNotSupported(err) { + return nil, errors.Wrap(err, "can't load BTF") + } + + return newMapWithBTF(spec, handle) +} + +func newMapWithBTF(spec *MapSpec, handle *btf.Handle) (*Map, error) { if spec.Type != ArrayOfMaps && spec.Type != HashOfMaps { - return createMap(spec, nil) + return createMap(spec, nil, handle) } if spec.InnerMap == nil { return nil, errors.Errorf("%s requires InnerMap", spec.Type) } - template, err := createMap(spec.InnerMap, nil) + template, err := createMap(spec.InnerMap, nil, handle) if err != nil { return nil, err } defer template.Close() - return createMap(spec, template.fd) + return createMap(spec, template.fd, handle) } -func createMap(spec *MapSpec, inner *bpfFD) (*Map, error) { +func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, error) { spec = spec.Copy() switch spec.Type { @@ -140,12 +157,18 @@ func createMap(spec *MapSpec, inner *bpfFD) (*Map, error) { if inner != nil { var err error - attr.innerMapFd, err = inner.value() + attr.innerMapFd, err = inner.Value() if err != nil { return nil, errors.Wrap(err, "map create") } } + if handle != nil && spec.BTF != nil { + attr.btfFd = uint32(handle.FD()) + attr.btfKeyTypeID = btf.MapKey(spec.BTF).ID() + attr.btfValueTypeID = btf.MapValue(spec.BTF).ID() + } + name, err := newBPFObjName(spec.Name) if err != nil { return nil, errors.Wrap(err, "map create") @@ -163,7 +186,7 @@ func createMap(spec *MapSpec, inner *bpfFD) (*Map, error) { return newMap(fd, spec.Name, newMapABIFromSpec(spec)) } -func newMap(fd *bpfFD, name string, abi *MapABI) (*Map, error) { +func newMap(fd *internal.FD, name string, abi *MapABI) (*Map, error) { m := &Map{ name, fd, @@ -251,12 +274,28 @@ func (m *Map) Lookup(key, valueOut interface{}) error { } } +// LookupAndDelete retrieves and deletes a value from a Map. +func (m *Map) LookupAndDelete(key, valueOut interface{}) error { + valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize) + + keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) + if err != nil { + return errors.WithMessage(err, "can't marshal key") + } + + if err := bpfMapLookupAndDelete(m.fd, keyPtr, valuePtr); err != nil { + return errors.WithMessage(err, "lookup and delete and delete failed") + } + + return unmarshalBytes(valueOut, valueBytes) +} + // LookupBytes gets a value from Map. // // Returns a nil value if a key doesn't exist. func (m *Map) LookupBytes(key interface{}) ([]byte, error) { valueBytes := make([]byte, m.fullValueSize) - valuePtr := newPtr(unsafe.Pointer(&valueBytes[0])) + valuePtr := internal.NewSlicePointer(valueBytes) err := m.lookup(key, valuePtr) if IsNotExist(err) { @@ -266,7 +305,7 @@ func (m *Map) LookupBytes(key interface{}) ([]byte, error) { return valueBytes, err } -func (m *Map) lookup(key interface{}, valueOut syscallPtr) error { +func (m *Map) lookup(key interface{}, valueOut internal.Pointer) error { keyPtr, err := marshalPtr(key, int(m.abi.KeySize)) if err != nil { return errors.WithMessage(err, "can't marshal key") @@ -304,7 +343,7 @@ func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error { return errors.WithMessage(err, "can't marshal key") } - var valuePtr syscallPtr + var valuePtr internal.Pointer if m.abi.Type.hasPerCPUValue() { valuePtr, err = marshalPerCPUValue(value, int(m.abi.ValueSize)) } else { @@ -355,7 +394,7 @@ func (m *Map) NextKey(key, nextKeyOut interface{}) error { // Use Iterate if you want to traverse all entries in the map. func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { nextKey := make([]byte, m.abi.KeySize) - nextKeyPtr := newPtr(unsafe.Pointer(&nextKey[0])) + nextKeyPtr := internal.NewSlicePointer(nextKey) err := m.nextKey(key, nextKeyPtr) if IsNotExist(err) { @@ -365,9 +404,9 @@ func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) { return nextKey, err } -func (m *Map) nextKey(key interface{}, nextKeyOut syscallPtr) error { +func (m *Map) nextKey(key interface{}, nextKeyOut internal.Pointer) error { var ( - keyPtr syscallPtr + keyPtr internal.Pointer err error ) @@ -400,14 +439,14 @@ func (m *Map) Close() error { return nil } - return m.fd.close() + return m.fd.Close() } // FD gets the file descriptor of the Map. // // Calling this function is invalid after Close has been called. func (m *Map) FD() int { - fd, err := m.fd.value() + fd, err := m.fd.Value() if err != nil { // Best effort: -1 is the number most likely to be an // invalid file descriptor. @@ -428,7 +467,7 @@ func (m *Map) Clone() (*Map, error) { return nil, nil } - dup, err := m.fd.dup() + dup, err := m.fd.Dup() if err != nil { return nil, errors.Wrap(err, "can't clone map") } @@ -454,7 +493,7 @@ func LoadPinnedMap(fileName string) (*Map, error) { } name, abi, err := newMapABIFromFd(fd) if err != nil { - _ = fd.close() + _ = fd.Close() return nil, err } return newMap(fd, name, abi) @@ -484,7 +523,7 @@ func unmarshalMap(buf []byte) (*Map, error) { name, abi, err := newMapABIFromFd(fd) if err != nil { - _ = fd.close() + _ = fd.Close() return nil, err } @@ -493,7 +532,7 @@ func unmarshalMap(buf []byte) (*Map, error) { // MarshalBinary implements BinaryMarshaler. func (m *Map) MarshalBinary() ([]byte, error) { - fd, err := m.fd.value() + fd, err := m.fd.Value() if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/ebpf/marshalers.go b/vendor/github.com/cilium/ebpf/marshalers.go index 44ba27330..64b7d4de7 100644 --- a/vendor/github.com/cilium/ebpf/marshalers.go +++ b/vendor/github.com/cilium/ebpf/marshalers.go @@ -13,17 +13,24 @@ import ( "github.com/pkg/errors" ) -func marshalPtr(data interface{}, length int) (syscallPtr, error) { +func marshalPtr(data interface{}, length int) (internal.Pointer, error) { + if data == nil { + if length == 0 { + return internal.NewPointer(nil), nil + } + return internal.Pointer{}, errors.New("can't use nil as key of map") + } + if ptr, ok := data.(unsafe.Pointer); ok { - return newPtr(ptr), nil + return internal.NewPointer(ptr), nil } buf, err := marshalBytes(data, length) if err != nil { - return syscallPtr{}, err + return internal.Pointer{}, err } - return newPtr(unsafe.Pointer(&buf[0])), nil + return internal.NewSlicePointer(buf), nil } func marshalBytes(data interface{}, length int) (buf []byte, err error) { @@ -52,13 +59,13 @@ func marshalBytes(data interface{}, length int) (buf []byte, err error) { return buf, nil } -func makeBuffer(dst interface{}, length int) (syscallPtr, []byte) { +func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) { if ptr, ok := dst.(unsafe.Pointer); ok { - return newPtr(ptr), nil + return internal.NewPointer(ptr), nil } buf := make([]byte, length) - return newPtr(unsafe.Pointer(&buf[0])), buf + return internal.NewSlicePointer(buf), buf } func unmarshalBytes(data interface{}, buf []byte) error { @@ -99,21 +106,21 @@ func unmarshalBytes(data interface{}, buf []byte) error { // Values are initialized to zero if the slice has less elements than CPUs. // // slice must have a type like []elementType. -func marshalPerCPUValue(slice interface{}, elemLength int) (syscallPtr, error) { +func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, error) { sliceType := reflect.TypeOf(slice) if sliceType.Kind() != reflect.Slice { - return syscallPtr{}, errors.New("per-CPU value requires slice") + return internal.Pointer{}, errors.New("per-CPU value requires slice") } possibleCPUs, err := internal.PossibleCPUs() if err != nil { - return syscallPtr{}, err + return internal.Pointer{}, err } sliceValue := reflect.ValueOf(slice) sliceLen := sliceValue.Len() if sliceLen > possibleCPUs { - return syscallPtr{}, errors.Errorf("per-CPU value exceeds number of CPUs") + return internal.Pointer{}, errors.Errorf("per-CPU value exceeds number of CPUs") } alignedElemLength := align(elemLength, 8) @@ -123,14 +130,14 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (syscallPtr, error) { elem := sliceValue.Index(i).Interface() elemBytes, err := marshalBytes(elem, elemLength) if err != nil { - return syscallPtr{}, err + return internal.Pointer{}, err } offset := i * alignedElemLength copy(buf[offset:offset+elemLength], elemBytes) } - return newPtr(unsafe.Pointer(&buf[0])), nil + return internal.NewSlicePointer(buf), nil } // unmarshalPerCPUValue decodes a buffer into a slice containing one value per diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index c69cc2b05..08ef4fa46 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -10,6 +10,7 @@ import ( "github.com/cilium/ebpf/asm" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" "github.com/pkg/errors" @@ -46,6 +47,11 @@ type ProgramSpec struct { Instructions asm.Instructions License string KernelVersion uint32 + + // The BTF associated with this program. Changing Instructions + // will most likely invalidate the contained data, and may + // result in errors when attempting to load it into the kernel. + BTF *btf.Program } // Copy returns a copy of the spec. @@ -68,7 +74,7 @@ type Program struct { // otherwise it is empty. VerifierLog string - fd *bpfFD + fd *internal.FD name string abi ProgramABI } @@ -86,7 +92,20 @@ func NewProgram(spec *ProgramSpec) (*Program, error) { // Loading a program for the first time will perform // feature detection by loading small, temporary programs. func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) { - attr, err := convertProgramSpec(spec) + if spec.BTF == nil { + return newProgramWithBTF(spec, nil, opts) + } + + handle, err := btf.NewHandle(btf.ProgramSpec(spec.BTF)) + if err != nil && !btf.IsNotSupported(err) { + return nil, errors.Wrap(err, "can't load BTF") + } + + return newProgramWithBTF(spec, handle, opts) +} + +func newProgramWithBTF(spec *ProgramSpec, btf *btf.Handle, opts ProgramOptions) (*Program, error) { + attr, err := convertProgramSpec(spec, btf) if err != nil { return nil, err } @@ -101,34 +120,28 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er logBuf = make([]byte, logSize) attr.logLevel = opts.LogLevel attr.logSize = uint32(len(logBuf)) - attr.logBuf = newPtr(unsafe.Pointer(&logBuf[0])) + attr.logBuf = internal.NewSlicePointer(logBuf) } fd, err := bpfProgLoad(attr) if err == nil { prog := newProgram(fd, spec.Name, &ProgramABI{spec.Type}) - prog.VerifierLog = convertCString(logBuf) + prog.VerifierLog = internal.CString(logBuf) return prog, nil } - truncated := errors.Cause(err) == unix.ENOSPC if opts.LogLevel == 0 { // Re-run with the verifier enabled to get better error messages. logBuf = make([]byte, logSize) attr.logLevel = 1 attr.logSize = uint32(len(logBuf)) - attr.logBuf = newPtr(unsafe.Pointer(&logBuf[0])) + attr.logBuf = internal.NewSlicePointer(logBuf) - _, nerr := bpfProgLoad(attr) - truncated = errors.Cause(nerr) == unix.ENOSPC + _, logErr := bpfProgLoad(attr) + err = internal.ErrorWithLog(err, logBuf, logErr) } - logs := convertCString(logBuf) - if truncated { - logs += "\n(truncated...)" - } - - return nil, &loadError{err, logs} + return nil, errors.Wrap(err, "can't load program") } // NewProgramFromFD creates a program from a raw fd. @@ -140,18 +153,18 @@ func NewProgramFromFD(fd int) (*Program, error) { if fd < 0 { return nil, errors.New("invalid fd") } - bpfFd := newBPFFD(uint32(fd)) + bpfFd := internal.NewFD(uint32(fd)) name, abi, err := newProgramABIFromFd(bpfFd) if err != nil { - bpfFd.forget() + bpfFd.Forget() return nil, err } return newProgram(bpfFd, name, abi), nil } -func newProgram(fd *bpfFD, name string, abi *ProgramABI) *Program { +func newProgram(fd *internal.FD, name string, abi *ProgramABI) *Program { return &Program{ name: name, fd: fd, @@ -159,7 +172,7 @@ func newProgram(fd *bpfFD, name string, abi *ProgramABI) *Program { } } -func convertProgramSpec(spec *ProgramSpec) (*bpfProgLoadAttr, error) { +func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr, error) { if len(spec.Instructions) == 0 { return nil, errors.New("Instructions cannot be empty") } @@ -176,13 +189,12 @@ func convertProgramSpec(spec *ProgramSpec) (*bpfProgLoadAttr, error) { bytecode := buf.Bytes() insCount := uint32(len(bytecode) / asm.InstructionSize) - lic := []byte(spec.License) attr := &bpfProgLoadAttr{ progType: spec.Type, expectedAttachType: spec.AttachType, insCount: insCount, - instructions: newPtr(unsafe.Pointer(&bytecode[0])), - license: newPtr(unsafe.Pointer(&lic[0])), + instructions: internal.NewSlicePointer(bytecode), + license: internal.NewStringPointer(spec.License), } name, err := newBPFObjName(spec.Name) @@ -194,6 +206,26 @@ func convertProgramSpec(spec *ProgramSpec) (*bpfProgLoadAttr, error) { attr.progName = name } + if handle != nil && spec.BTF != nil { + attr.progBTFFd = uint32(handle.FD()) + + recSize, bytes, err := btf.ProgramLineInfos(spec.BTF) + if err != nil { + return nil, errors.Wrap(err, "can't get BTF line infos") + } + attr.lineInfoRecSize = recSize + attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.lineInfo = internal.NewSlicePointer(bytes) + + recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF) + if err != nil { + return nil, errors.Wrap(err, "can't get BTF function infos") + } + attr.funcInfoRecSize = recSize + attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize)) + attr.funcInfo = internal.NewSlicePointer(bytes) + } + return attr, nil } @@ -213,7 +245,7 @@ func (p *Program) ABI() ProgramABI { // // It is invalid to call this function after Close has been called. func (p *Program) FD() int { - fd, err := p.fd.value() + fd, err := p.fd.Value() if err != nil { // Best effort: -1 is the number most likely to be an // invalid file descriptor. @@ -233,7 +265,7 @@ func (p *Program) Clone() (*Program, error) { return nil, nil } - dup, err := p.fd.dup() + dup, err := p.fd.Dup() if err != nil { return nil, errors.Wrap(err, "can't clone program") } @@ -254,7 +286,7 @@ func (p *Program) Close() error { return nil } - return p.fd.close() + return p.fd.Close() } // Test runs the Program in the kernel with the given input and returns the @@ -296,7 +328,7 @@ var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() b } defer prog.Close() - fd, err := prog.fd.value() + fd, err := prog.fd.Value() if err != nil { return false } @@ -306,10 +338,10 @@ var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() b attr := bpfProgTestRunAttr{ fd: fd, dataSizeIn: uint32(len(in)), - dataIn: newPtr(unsafe.Pointer(&in[0])), + dataIn: internal.NewSlicePointer(in), } - _, err = bpfCall(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) // Check for EINVAL specifically, rather than err != nil since we // otherwise misdetect due to insufficient permissions. @@ -340,7 +372,7 @@ func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration, // See https://patchwork.ozlabs.org/cover/1006822/ out := make([]byte, len(in)+outputPad) - fd, err := p.fd.value() + fd, err := p.fd.Value() if err != nil { return 0, nil, 0, err } @@ -349,12 +381,12 @@ func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration, fd: fd, dataSizeIn: uint32(len(in)), dataSizeOut: uint32(len(out)), - dataIn: newPtr(unsafe.Pointer(&in[0])), - dataOut: newPtr(unsafe.Pointer(&out[0])), + dataIn: internal.NewSlicePointer(in), + dataOut: internal.NewSlicePointer(out), repeat: uint32(repeat), } - _, err = bpfCall(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) if err != nil { return 0, nil, 0, errors.Wrap(err, "can't run test") } @@ -385,7 +417,7 @@ func unmarshalProgram(buf []byte) (*Program, error) { name, abi, err := newProgramABIFromFd(fd) if err != nil { - _ = fd.close() + _ = fd.Close() return nil, err } @@ -394,7 +426,7 @@ func unmarshalProgram(buf []byte) (*Program, error) { // MarshalBinary implements BinaryMarshaler. func (p *Program) MarshalBinary() ([]byte, error) { - value, err := p.fd.value() + value, err := p.fd.Value() if err != nil { return nil, err } @@ -410,7 +442,7 @@ func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error { return errors.New("invalid fd") } - pfd, err := p.fd.value() + pfd, err := p.fd.Value() if err != nil { return err } @@ -431,7 +463,7 @@ func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error { return errors.New("invalid fd") } - pfd, err := p.fd.value() + pfd, err := p.fd.Value() if err != nil { return err } @@ -457,7 +489,7 @@ func LoadPinnedProgram(fileName string) (*Program, error) { name, abi, err := newProgramABIFromFd(fd) if err != nil { - _ = fd.close() + _ = fd.Close() return nil, errors.Wrapf(err, "can't get ABI for %s", fileName) } @@ -480,22 +512,6 @@ func SanitizeName(name string, replacement rune) string { }, name) } -type loadError struct { - cause error - verifierLog string -} - -func (le *loadError) Error() string { - if le.verifierLog == "" { - return fmt.Sprintf("failed to load program: %s", le.cause) - } - return fmt.Sprintf("failed to load program: %s: %s", le.cause, le.verifierLog) -} - -func (le *loadError) Cause() error { - return le.cause -} - // IsNotSupported returns true if an error occurred because // the kernel does not have support for a specific feature. func IsNotSupported(err error) bool { diff --git a/vendor/github.com/cilium/ebpf/syscalls.go b/vendor/github.com/cilium/ebpf/syscalls.go index d00a03fb2..70f8c127a 100644 --- a/vendor/github.com/cilium/ebpf/syscalls.go +++ b/vendor/github.com/cilium/ebpf/syscalls.go @@ -1,72 +1,17 @@ package ebpf import ( - "bytes" "path/filepath" - "runtime" - "strconv" "strings" "unsafe" "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/btf" "github.com/cilium/ebpf/internal/unix" "github.com/pkg/errors" ) -var errClosedFd = errors.New("use of closed file descriptor") - -type bpfFD struct { - raw int64 -} - -func newBPFFD(value uint32) *bpfFD { - fd := &bpfFD{int64(value)} - runtime.SetFinalizer(fd, (*bpfFD).close) - return fd -} - -func (fd *bpfFD) String() string { - return strconv.FormatInt(fd.raw, 10) -} - -func (fd *bpfFD) value() (uint32, error) { - if fd.raw < 0 { - return 0, errClosedFd - } - - return uint32(fd.raw), nil -} - -func (fd *bpfFD) close() error { - if fd.raw < 0 { - return nil - } - - value := int(fd.raw) - fd.raw = -1 - - fd.forget() - return unix.Close(value) -} - -func (fd *bpfFD) forget() { - runtime.SetFinalizer(fd, nil) -} - -func (fd *bpfFD) dup() (*bpfFD, error) { - if fd.raw < 0 { - return nil, errClosedFd - } - - dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0) - if err != nil { - return nil, errors.Wrap(err, "can't dup fd") - } - - return newBPFFD(uint32(dup)), nil -} - // bpfObjName is a null-terminated string made up of // 'A-Za-z0-9_' characters. type bpfObjName [unix.BPF_OBJ_NAME_LEN]byte @@ -99,21 +44,25 @@ func invalidBPFObjNameChar(char rune) bool { } type bpfMapCreateAttr struct { - mapType MapType - keySize uint32 - valueSize uint32 - maxEntries uint32 - flags uint32 - innerMapFd uint32 // since 4.12 56f668dfe00d - numaNode uint32 // since 4.14 96eabe7a40aa - mapName bpfObjName // since 4.15 ad5b177bd73f + mapType MapType + keySize uint32 + valueSize uint32 + maxEntries uint32 + flags uint32 + innerMapFd uint32 // since 4.12 56f668dfe00d + numaNode uint32 // since 4.14 96eabe7a40aa + mapName bpfObjName // since 4.15 ad5b177bd73f + mapIfIndex uint32 + btfFd uint32 + btfKeyTypeID btf.TypeID + btfValueTypeID btf.TypeID } type bpfMapOpAttr struct { mapFd uint32 padding uint32 - key syscallPtr - value syscallPtr + key internal.Pointer + value internal.Pointer flags uint64 } @@ -128,7 +77,7 @@ type bpfMapInfo struct { } type bpfPinObjAttr struct { - fileName syscallPtr + fileName internal.Pointer fd uint32 padding uint32 } @@ -136,16 +85,23 @@ type bpfPinObjAttr struct { type bpfProgLoadAttr struct { progType ProgramType insCount uint32 - instructions syscallPtr - license syscallPtr + instructions internal.Pointer + license internal.Pointer logLevel uint32 logSize uint32 - logBuf syscallPtr + logBuf internal.Pointer kernelVersion uint32 // since 4.1 2541517c32be progFlags uint32 // since 4.11 e07b98d9bffe progName bpfObjName // since 4.15 067cae47771c progIfIndex uint32 // since 4.15 1f6f4cb7ba21 expectedAttachType AttachType // since 4.17 5e43f899b03a + progBTFFd uint32 + funcInfoRecSize uint32 + funcInfo internal.Pointer + funcInfoCnt uint32 + lineInfoRecSize uint32 + lineInfo internal.Pointer + lineInfoCnt uint32 } type bpfProgInfo struct { @@ -154,12 +110,12 @@ type bpfProgInfo struct { tag [unix.BPF_TAG_SIZE]byte jitedLen uint32 xlatedLen uint32 - jited syscallPtr - xlated syscallPtr + jited internal.Pointer + xlated internal.Pointer loadTime uint64 // since 4.15 cb4d2b3f03d8 createdByUID uint32 nrMapIDs uint32 - mapIds syscallPtr + mapIds internal.Pointer name bpfObjName } @@ -168,8 +124,8 @@ type bpfProgTestRunAttr struct { retval uint32 dataSizeIn uint32 dataSizeOut uint32 - dataIn syscallPtr - dataOut syscallPtr + dataIn internal.Pointer + dataOut internal.Pointer repeat uint32 duration uint32 } @@ -184,7 +140,7 @@ type bpfProgAlterAttr struct { type bpfObjGetInfoByFDAttr struct { fd uint32 infoLen uint32 - info syscallPtr // May be either bpfMapInfo or bpfProgInfo + info internal.Pointer // May be either bpfMapInfo or bpfProgInfo } type bpfGetFDByIDAttr struct { @@ -192,13 +148,9 @@ type bpfGetFDByIDAttr struct { next uint32 } -func newPtr(ptr unsafe.Pointer) syscallPtr { - return syscallPtr{ptr: ptr} -} - -func bpfProgLoad(attr *bpfProgLoadAttr) (*bpfFD, error) { +func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) { for { - fd, err := bpfCall(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + fd, err := internal.BPF(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) // As of ~4.20 the verifier can be interrupted by a signal, // and returns EAGAIN in that case. if err == unix.EAGAIN { @@ -209,22 +161,22 @@ func bpfProgLoad(attr *bpfProgLoadAttr) (*bpfFD, error) { return nil, err } - return newBPFFD(uint32(fd)), nil + return internal.NewFD(uint32(fd)), nil } } func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error { - _, err := bpfCall(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + _, err := internal.BPF(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) return err } -func bpfMapCreate(attr *bpfMapCreateAttr) (*bpfFD, error) { - fd, err := bpfCall(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) +func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) { + fd, err := internal.BPF(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) if err != nil { return nil, err } - return newBPFFD(uint32(fd)), nil + return internal.NewFD(uint32(fd)), nil } var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { @@ -237,9 +189,9 @@ var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { if err != nil { return false } - defer inner.close() + defer inner.Close() - innerFd, _ := inner.value() + innerFd, _ := inner.Value() nested, err := bpfMapCreate(&bpfMapCreateAttr{ mapType: ArrayOfMaps, keySize: 4, @@ -251,12 +203,12 @@ var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool { return false } - _ = nested.close() + _ = nested.Close() return true }) -func bpfMapLookupElem(m *bpfFD, key, valueOut syscallPtr) error { - fd, err := m.value() +func bpfMapLookupElem(m *internal.FD, key, valueOut internal.Pointer) error { + fd, err := m.Value() if err != nil { return err } @@ -266,12 +218,27 @@ func bpfMapLookupElem(m *bpfFD, key, valueOut syscallPtr) error { key: key, value: valueOut, } - _, err = bpfCall(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return err } -func bpfMapUpdateElem(m *bpfFD, key, valueOut syscallPtr, flags uint64) error { - fd, err := m.value() +func bpfMapLookupAndDelete(m *internal.FD, key, valueOut internal.Pointer) error { + fd, err := m.Value() + if err != nil { + return err + } + + attr := bpfMapOpAttr{ + mapFd: fd, + key: key, + value: valueOut, + } + _, err = internal.BPF(_MapLookupAndDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return err +} + +func bpfMapUpdateElem(m *internal.FD, key, valueOut internal.Pointer, flags uint64) error { + fd, err := m.Value() if err != nil { return err } @@ -282,12 +249,12 @@ func bpfMapUpdateElem(m *bpfFD, key, valueOut syscallPtr, flags uint64) error { value: valueOut, flags: flags, } - _, err = bpfCall(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return err } -func bpfMapDeleteElem(m *bpfFD, key syscallPtr) error { - fd, err := m.value() +func bpfMapDeleteElem(m *internal.FD, key internal.Pointer) error { + fd, err := m.Value() if err != nil { return err } @@ -296,12 +263,12 @@ func bpfMapDeleteElem(m *bpfFD, key syscallPtr) error { mapFd: fd, key: key, } - _, err = bpfCall(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return err } -func bpfMapGetNextKey(m *bpfFD, key, nextKeyOut syscallPtr) error { - fd, err := m.value() +func bpfMapGetNextKey(m *internal.FD, key, nextKeyOut internal.Pointer) error { + fd, err := m.Value() if err != nil { return err } @@ -311,13 +278,13 @@ func bpfMapGetNextKey(m *bpfFD, key, nextKeyOut syscallPtr) error { key: key, value: nextKeyOut, } - _, err = bpfCall(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + _, err = internal.BPF(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) return err } const bpfFSType = 0xcafe4a11 -func bpfPinObject(fileName string, fd *bpfFD) error { +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 { @@ -327,30 +294,30 @@ func bpfPinObject(fileName string, fd *bpfFD) error { return errors.Errorf("%s is not on a bpf filesystem", fileName) } - value, err := fd.value() + value, err := fd.Value() if err != nil { return err } - _, err = bpfCall(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{ - fileName: newPtr(unsafe.Pointer(&[]byte(fileName)[0])), + _, err = internal.BPF(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{ + fileName: internal.NewStringPointer(fileName), fd: value, }), 16) return errors.Wrapf(err, "pin object %s", fileName) } -func bpfGetObject(fileName string) (*bpfFD, error) { - ptr, err := bpfCall(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{ - fileName: newPtr(unsafe.Pointer(&[]byte(fileName)[0])), +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, errors.Wrapf(err, "get object %s", fileName) } - return newBPFFD(uint32(ptr)), nil + return internal.NewFD(uint32(ptr)), nil } -func bpfGetObjectInfoByFD(fd *bpfFD, info unsafe.Pointer, size uintptr) error { - value, err := fd.value() +func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error { + value, err := fd.Value() if err != nil { return err } @@ -359,19 +326,19 @@ func bpfGetObjectInfoByFD(fd *bpfFD, info unsafe.Pointer, size uintptr) error { attr := bpfObjGetInfoByFDAttr{ fd: value, infoLen: uint32(size), - info: newPtr(info), + info: internal.NewPointer(info), } - _, err = bpfCall(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) - return errors.Wrapf(err, "fd %d", value) + _, err = internal.BPF(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + return errors.Wrapf(err, "fd %d", fd) } -func bpfGetProgInfoByFD(fd *bpfFD) (*bpfProgInfo, error) { +func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) { var info bpfProgInfo err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) return &info, errors.Wrap(err, "can't get program info") } -func bpfGetMapInfoByFD(fd *bpfFD) (*bpfMapInfo, error) { +func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) { var info bpfMapInfo err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)) return &info, errors.Wrap(err, "can't get map info") @@ -398,50 +365,30 @@ var haveObjName = internal.FeatureTest("object names", "4.15", func() bool { return false } - _ = fd.close() + _ = fd.Close() return true }) -func bpfGetMapFDByID(id uint32) (*bpfFD, error) { +func bpfGetMapFDByID(id uint32) (*internal.FD, error) { // available from 4.13 attr := bpfGetFDByIDAttr{ id: id, } - ptr, err := bpfCall(_MapGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + ptr, err := internal.BPF(_MapGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) if err != nil { return nil, errors.Wrapf(err, "can't get fd for map id %d", id) } - return newBPFFD(uint32(ptr)), nil + return internal.NewFD(uint32(ptr)), nil } -func bpfGetProgramFDByID(id uint32) (*bpfFD, error) { +func bpfGetProgramFDByID(id uint32) (*internal.FD, error) { // available from 4.13 attr := bpfGetFDByIDAttr{ id: id, } - ptr, err := bpfCall(_ProgGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) + ptr, err := internal.BPF(_ProgGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr)) if err != nil { return nil, errors.Wrapf(err, "can't get fd for program id %d", id) } - return newBPFFD(uint32(ptr)), nil -} - -func bpfCall(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) { - r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size) - runtime.KeepAlive(attr) - - var err error - if errNo != 0 { - err = errNo - } - - return r1, err -} - -func convertCString(in []byte) string { - inLen := bytes.IndexByte(in, 0) - if inLen == -1 { - return "" - } - return string(in[:inLen]) + return internal.NewFD(uint32(ptr)), nil } diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go index 0daf9a715..6a0228dc7 100644 --- a/vendor/github.com/cilium/ebpf/types.go +++ b/vendor/github.com/cilium/ebpf/types.go @@ -57,6 +57,30 @@ const ( // HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps // itself. HashOfMaps + // DevMap - Specialized map to store references to network devices. + DevMap + // SockMap - Specialized map to store references to sockets. + SockMap + // CPUMap - Specialized map to store references to CPUs. + CPUMap + // XSKMap - Specialized map for XDP programs to store references to open sockets. + XSKMap + // SockHash - Specialized hash to store references to sockets. + SockHash + // CGroupStorage - Special map for CGroups. + CGroupStorage + // ReusePortSockArray - Specialized map to store references to sockets that can be reused. + ReusePortSockArray + // PerCPUCGroupStorage - Special per CPU map for CGroups. + PerCPUCGroupStorage + // Queue - FIFO storage for BPF programs. + Queue + // Stack - LIFO storage for BPF programs. + Stack + // SkStorage - Specialized map for local storage at SK for BPF programs. + SkStorage + // DevMapHash - Hash-based indexing scheme for references to network devices. + DevMapHash ) // hasPerCPUValue returns true if the Map stores a value per CPU. @@ -84,6 +108,13 @@ const ( _ProgGetFDByID _MapGetFDByID _ObjGetInfoByFD + _ProgQuery + _RawTracepointOpen + _BTFLoad + _BTFGetFDByID + _TaskFDQuery + _MapLookupAndDeleteElem + _MapFreeze ) const ( @@ -149,6 +180,8 @@ const ( RawTracepointWritable // CGroupSockopt program CGroupSockopt + // Tracing program + Tracing ) // AttachType of the eBPF program, needed to differentiate allowed context accesses in @@ -183,6 +216,9 @@ const ( AttachCGroupUDP6Recvmsg AttachCGroupGetsockopt AttachCGroupSetsockopt + AttachTraceRawTp + AttachTraceFEntry + AttachTraceFExit ) // AttachFlags of the eBPF program used in BPF_PROG_ATTACH command diff --git a/vendor/github.com/cilium/ebpf/types_string.go b/vendor/github.com/cilium/ebpf/types_string.go index 4813437ec..f41ba77df 100644 --- a/vendor/github.com/cilium/ebpf/types_string.go +++ b/vendor/github.com/cilium/ebpf/types_string.go @@ -22,11 +22,23 @@ func _() { _ = x[LPMTrie-11] _ = x[ArrayOfMaps-12] _ = x[HashOfMaps-13] + _ = x[DevMap-14] + _ = x[SockMap-15] + _ = x[CPUMap-16] + _ = x[XSKMap-17] + _ = x[SockHash-18] + _ = x[CGroupStorage-19] + _ = x[ReusePortSockArray-20] + _ = x[PerCPUCGroupStorage-21] + _ = x[Queue-22] + _ = x[Stack-23] + _ = x[SkStorage-24] + _ = x[DevMapHash-25] } -const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMaps" +const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHash" -var _MapType_index = [...]uint8{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136} +var _MapType_index = [...]uint8{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248} func (i MapType) String() string { if i >= MapType(len(_MapType_index)-1) { @@ -64,11 +76,12 @@ func _() { _ = x[CGroupSysctl-23] _ = x[RawTracepointWritable-24] _ = x[CGroupSockopt-25] + _ = x[Tracing-26] } -const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockopt" +const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracing" -var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258} +var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265} func (i ProgramType) String() string { if i >= ProgramType(len(_ProgramType_index)-1) {