vendor: cadvisor v0.39.0
Main upgrades: - github.com/opencontainers/runc v1.0.0-rc93 - github.com/containerd/containerd v1.4.4 - github.com/docker/docker v20.10.2 - github.com/mrunalp/fileutils v0.5.0 - github.com/opencontainers/selinux v1.8.0 - github.com/cilium/ebpf v0.2.0
This commit is contained in:
17
vendor/github.com/cilium/ebpf/.clang-format
generated
vendored
Normal file
17
vendor/github.com/cilium/ebpf/.clang-format
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: true
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
BreakBeforeBraces: Attach
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
TabWidth: 4
|
||||
UseTab: ForContinuationAndIndentation
|
||||
ColumnLimit: 1000
|
||||
...
|
1
vendor/github.com/cilium/ebpf/.gitignore
generated
vendored
1
vendor/github.com/cilium/ebpf/.gitignore
generated
vendored
@@ -4,6 +4,7 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.o
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
6
vendor/github.com/cilium/ebpf/abi.go
generated
vendored
6
vendor/github.com/cilium/ebpf/abi.go
generated
vendored
@@ -88,12 +88,6 @@ type ProgramABI struct {
|
||||
Type ProgramType
|
||||
}
|
||||
|
||||
func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI {
|
||||
return &ProgramABI{
|
||||
spec.Type,
|
||||
}
|
||||
}
|
||||
|
||||
func newProgramABIFromFd(fd *internal.FD) (string, *ProgramABI, error) {
|
||||
info, err := bpfGetProgInfoByFD(fd)
|
||||
if err != nil {
|
||||
|
121
vendor/github.com/cilium/ebpf/asm/instruction.go
generated
vendored
121
vendor/github.com/cilium/ebpf/asm/instruction.go
generated
vendored
@@ -12,6 +12,14 @@ import (
|
||||
// InstructionSize is the size of a BPF instruction in bytes
|
||||
const InstructionSize = 8
|
||||
|
||||
// RawInstructionOffset is an offset in units of raw BPF instructions.
|
||||
type RawInstructionOffset uint64
|
||||
|
||||
// Bytes returns the offset of an instruction in bytes.
|
||||
func (rio RawInstructionOffset) Bytes() uint64 {
|
||||
return uint64(rio) * InstructionSize
|
||||
}
|
||||
|
||||
// Instruction is a single eBPF instruction.
|
||||
type Instruction struct {
|
||||
OpCode OpCode
|
||||
@@ -155,6 +163,13 @@ func (ins *Instruction) isLoadFromMap() bool {
|
||||
return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
|
||||
}
|
||||
|
||||
// IsFunctionCall returns true if the instruction calls another BPF function.
|
||||
//
|
||||
// This is not the same thing as a BPF helper call.
|
||||
func (ins *Instruction) IsFunctionCall() bool {
|
||||
return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
|
||||
}
|
||||
|
||||
// Format implements fmt.Formatter.
|
||||
func (ins Instruction) Format(f fmt.State, c rune) {
|
||||
if c != 'v' {
|
||||
@@ -310,28 +325,6 @@ func (insns Instructions) ReferenceOffsets() map[string][]int {
|
||||
return offsets
|
||||
}
|
||||
|
||||
func (insns Instructions) marshalledOffsets() (map[string]int, error) {
|
||||
symbols := make(map[string]int)
|
||||
|
||||
marshalledPos := 0
|
||||
for _, ins := range insns {
|
||||
currentPos := marshalledPos
|
||||
marshalledPos += ins.OpCode.marshalledInstructions()
|
||||
|
||||
if ins.Symbol == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := symbols[ins.Symbol]; ok {
|
||||
return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol)
|
||||
}
|
||||
|
||||
symbols[ins.Symbol] = currentPos
|
||||
}
|
||||
|
||||
return symbols, nil
|
||||
}
|
||||
|
||||
// Format implements fmt.Formatter.
|
||||
//
|
||||
// You can control indentation of symbols by
|
||||
@@ -370,21 +363,17 @@ func (insns Instructions) Format(f fmt.State, c rune) {
|
||||
symIndent = strings.Repeat(" ", symPadding)
|
||||
}
|
||||
|
||||
// Figure out how many digits we need to represent the highest
|
||||
// offset.
|
||||
highestOffset := 0
|
||||
for _, ins := range insns {
|
||||
highestOffset += ins.OpCode.marshalledInstructions()
|
||||
}
|
||||
// Guess how many digits we need at most, by assuming that all instructions
|
||||
// are double wide.
|
||||
highestOffset := len(insns) * 2
|
||||
offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset))))
|
||||
|
||||
offset := 0
|
||||
for _, ins := range insns {
|
||||
if ins.Symbol != "" {
|
||||
fmt.Fprintf(f, "%s%s:\n", symIndent, ins.Symbol)
|
||||
iter := insns.Iterate()
|
||||
for iter.Next() {
|
||||
if iter.Ins.Symbol != "" {
|
||||
fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol)
|
||||
}
|
||||
fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, offset, ins)
|
||||
offset += ins.OpCode.marshalledInstructions()
|
||||
fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -392,43 +381,49 @@ func (insns Instructions) Format(f fmt.State, c rune) {
|
||||
|
||||
// Marshal encodes a BPF program into the kernel format.
|
||||
func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
|
||||
absoluteOffsets, err := insns.marshalledOffsets()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
num := 0
|
||||
for i, ins := range insns {
|
||||
switch {
|
||||
case ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall && ins.Constant == -1:
|
||||
// Rewrite bpf to bpf call
|
||||
offset, ok := absoluteOffsets[ins.Reference]
|
||||
if !ok {
|
||||
return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
|
||||
}
|
||||
|
||||
ins.Constant = int64(offset - num - 1)
|
||||
|
||||
case ins.OpCode.Class() == JumpClass && ins.Offset == -1:
|
||||
// Rewrite jump to label
|
||||
offset, ok := absoluteOffsets[ins.Reference]
|
||||
if !ok {
|
||||
return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
|
||||
}
|
||||
|
||||
ins.Offset = int16(offset - num - 1)
|
||||
}
|
||||
|
||||
n, err := ins.Marshal(w, bo)
|
||||
_, err := ins.Marshal(w, bo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("instruction %d: %w", i, err)
|
||||
}
|
||||
|
||||
num += int(n / InstructionSize)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Iterate allows iterating a BPF program while keeping track of
|
||||
// various offsets.
|
||||
//
|
||||
// Modifying the instruction slice will lead to undefined behaviour.
|
||||
func (insns Instructions) Iterate() *InstructionIterator {
|
||||
return &InstructionIterator{insns: insns}
|
||||
}
|
||||
|
||||
// InstructionIterator iterates over a BPF program.
|
||||
type InstructionIterator struct {
|
||||
insns Instructions
|
||||
// The instruction in question.
|
||||
Ins *Instruction
|
||||
// The index of the instruction in the original instruction slice.
|
||||
Index int
|
||||
// The offset of the instruction in raw BPF instructions. This accounts
|
||||
// for double-wide instructions.
|
||||
Offset RawInstructionOffset
|
||||
}
|
||||
|
||||
// Next returns true as long as there are any instructions remaining.
|
||||
func (iter *InstructionIterator) Next() bool {
|
||||
if len(iter.insns) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if iter.Ins != nil {
|
||||
iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions())
|
||||
}
|
||||
iter.Ins = &iter.insns[0]
|
||||
iter.insns = iter.insns[1:]
|
||||
return true
|
||||
}
|
||||
|
||||
type bpfInstruction struct {
|
||||
OpCode OpCode
|
||||
Registers bpfRegisters
|
||||
|
6
vendor/github.com/cilium/ebpf/asm/opcode.go
generated
vendored
6
vendor/github.com/cilium/ebpf/asm/opcode.go
generated
vendored
@@ -66,10 +66,10 @@ type OpCode uint8
|
||||
// InvalidOpCode is returned by setters on OpCode
|
||||
const InvalidOpCode OpCode = 0xff
|
||||
|
||||
// marshalledInstructions returns the number of BPF instructions required
|
||||
// rawInstructions returns the number of BPF instructions required
|
||||
// to encode this opcode.
|
||||
func (op OpCode) marshalledInstructions() int {
|
||||
if op == LoadImmOp(DWord) {
|
||||
func (op OpCode) rawInstructions() int {
|
||||
if op.isDWordLoad() {
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
|
168
vendor/github.com/cilium/ebpf/collection.go
generated
vendored
168
vendor/github.com/cilium/ebpf/collection.go
generated
vendored
@@ -4,6 +4,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
@@ -11,7 +13,10 @@ import (
|
||||
)
|
||||
|
||||
// CollectionOptions control loading a collection into the kernel.
|
||||
//
|
||||
// Maps and Programs are passed to NewMapWithOptions and NewProgramsWithOptions.
|
||||
type CollectionOptions struct {
|
||||
Maps MapOptions
|
||||
Programs ProgramOptions
|
||||
}
|
||||
|
||||
@@ -126,6 +131,65 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
|
||||
return nil
|
||||
}
|
||||
|
||||
// Assign the contents of a collection spec to a struct.
|
||||
//
|
||||
// This function is a short-cut to manually checking the presence
|
||||
// of maps and programs in a collection spec.
|
||||
//
|
||||
// The argument to must be a pointer to a struct. A field of the
|
||||
// struct is updated with values from Programs or Maps if it
|
||||
// has an `ebpf` tag and its type is *ProgramSpec or *MapSpec.
|
||||
// The tag gives the name of the program or map as found in
|
||||
// the CollectionSpec.
|
||||
//
|
||||
// struct {
|
||||
// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"`
|
||||
// Bar *ebpf.MapSpec `ebpf:"bar_map"`
|
||||
// Ignored int
|
||||
// }
|
||||
//
|
||||
// Returns an error if any of the fields can't be found, or
|
||||
// if the same map or program is assigned multiple times.
|
||||
func (cs *CollectionSpec) Assign(to interface{}) error {
|
||||
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
|
||||
switch typ {
|
||||
case reflect.TypeOf((*ProgramSpec)(nil)):
|
||||
p := cs.Programs[name]
|
||||
if p == nil {
|
||||
return reflect.Value{}, fmt.Errorf("missing program %q", name)
|
||||
}
|
||||
return reflect.ValueOf(p), nil
|
||||
case reflect.TypeOf((*MapSpec)(nil)):
|
||||
m := cs.Maps[name]
|
||||
if m == nil {
|
||||
return reflect.Value{}, fmt.Errorf("missing map %q", name)
|
||||
}
|
||||
return reflect.ValueOf(m), nil
|
||||
default:
|
||||
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
|
||||
}
|
||||
}
|
||||
|
||||
return assignValues(to, valueOf)
|
||||
}
|
||||
|
||||
// LoadAndAssign creates a collection from a spec, and assigns it to a struct.
|
||||
//
|
||||
// See Collection.Assign for details.
|
||||
func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {
|
||||
if opts == nil {
|
||||
opts = &CollectionOptions{}
|
||||
}
|
||||
|
||||
coll, err := NewCollectionWithOptions(cs, *opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer coll.Close()
|
||||
|
||||
return coll.Assign(to)
|
||||
}
|
||||
|
||||
// Collection is a collection of Programs and Maps associated
|
||||
// with their symbols
|
||||
type Collection struct {
|
||||
@@ -191,7 +255,7 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (col
|
||||
}
|
||||
}
|
||||
|
||||
m, err := newMapWithBTF(mapSpec, handle)
|
||||
m, err := newMapWithBTF(mapSpec, handle, opts.Maps)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("map %s: %w", mapName, err)
|
||||
}
|
||||
@@ -292,3 +356,105 @@ func (coll *Collection) DetachProgram(name string) *Program {
|
||||
delete(coll.Programs, name)
|
||||
return p
|
||||
}
|
||||
|
||||
// Assign the contents of a collection to a struct.
|
||||
//
|
||||
// `to` must be a pointer to a struct like the following:
|
||||
//
|
||||
// struct {
|
||||
// Foo *ebpf.Program `ebpf:"xdp_foo"`
|
||||
// Bar *ebpf.Map `ebpf:"bar_map"`
|
||||
// Ignored int
|
||||
// }
|
||||
//
|
||||
// See CollectionSpec.Assign for the semantics of this function.
|
||||
//
|
||||
// DetachMap and DetachProgram is invoked for all assigned elements
|
||||
// if the function is successful.
|
||||
func (coll *Collection) Assign(to interface{}) error {
|
||||
assignedMaps := make(map[string]struct{})
|
||||
assignedPrograms := make(map[string]struct{})
|
||||
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
|
||||
switch typ {
|
||||
case reflect.TypeOf((*Program)(nil)):
|
||||
p := coll.Programs[name]
|
||||
if p == nil {
|
||||
return reflect.Value{}, fmt.Errorf("missing program %q", name)
|
||||
}
|
||||
assignedPrograms[name] = struct{}{}
|
||||
return reflect.ValueOf(p), nil
|
||||
case reflect.TypeOf((*Map)(nil)):
|
||||
m := coll.Maps[name]
|
||||
if m == nil {
|
||||
return reflect.Value{}, fmt.Errorf("missing map %q", name)
|
||||
}
|
||||
assignedMaps[name] = struct{}{}
|
||||
return reflect.ValueOf(m), nil
|
||||
default:
|
||||
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
|
||||
}
|
||||
}
|
||||
|
||||
if err := assignValues(to, valueOf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for name := range assignedPrograms {
|
||||
coll.DetachProgram(name)
|
||||
}
|
||||
|
||||
for name := range assignedMaps {
|
||||
coll.DetachMap(name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Value, error)) error {
|
||||
v := reflect.ValueOf(to)
|
||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
||||
return fmt.Errorf("%T is not a pointer to a struct", to)
|
||||
}
|
||||
|
||||
type elem struct {
|
||||
typ reflect.Type
|
||||
name string
|
||||
}
|
||||
|
||||
var (
|
||||
s = v.Elem()
|
||||
sT = s.Type()
|
||||
assignedTo = make(map[elem]string)
|
||||
)
|
||||
for i := 0; i < sT.NumField(); i++ {
|
||||
field := sT.Field(i)
|
||||
|
||||
name := field.Tag.Get("ebpf")
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(name, ",") {
|
||||
return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name)
|
||||
}
|
||||
|
||||
e := elem{field.Type, name}
|
||||
if assignedField := assignedTo[e]; assignedField != "" {
|
||||
return fmt.Errorf("field %s: %q was already assigned to %s", field.Name, name, assignedField)
|
||||
}
|
||||
|
||||
value, err := valueOf(field.Type, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("field %s: %w", field.Name, err)
|
||||
}
|
||||
|
||||
fieldValue := s.Field(i)
|
||||
if !fieldValue.CanSet() {
|
||||
return fmt.Errorf("can't set value of field %s", field.Name)
|
||||
}
|
||||
|
||||
fieldValue.Set(value)
|
||||
assignedTo[e] = field.Name
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
3
vendor/github.com/cilium/ebpf/doc.go
generated
vendored
3
vendor/github.com/cilium/ebpf/doc.go
generated
vendored
@@ -12,6 +12,5 @@
|
||||
// eBPF code should be compiled ahead of time using clang, and shipped with
|
||||
// your application as any other resource.
|
||||
//
|
||||
// This package doesn't include code required to attach eBPF to Linux
|
||||
// subsystems, since this varies per subsystem.
|
||||
// Use the link subpackage to attach a loaded program to a hook in the kernel.
|
||||
package ebpf
|
||||
|
16
vendor/github.com/cilium/ebpf/elf_reader.go
generated
vendored
16
vendor/github.com/cilium/ebpf/elf_reader.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
@@ -236,7 +237,7 @@ func (ec *elfCode) loadPrograms(progSections map[elf.SectionIndex]*elf.Section,
|
||||
|
||||
func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]elf.Symbol) (asm.Instructions, uint64, error) {
|
||||
var (
|
||||
r = section.Open()
|
||||
r = bufio.NewReader(section.Open())
|
||||
insns asm.Instructions
|
||||
offset uint64
|
||||
)
|
||||
@@ -389,7 +390,7 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec, mapSections map[elf.Sectio
|
||||
}
|
||||
|
||||
var (
|
||||
r = sec.Open()
|
||||
r = bufio.NewReader(sec.Open())
|
||||
size = sec.Size / uint64(len(syms))
|
||||
)
|
||||
for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size {
|
||||
@@ -482,6 +483,7 @@ func mapSpecFromBTF(spec *btf.Spec, name string) (*MapSpec, error) {
|
||||
|
||||
var (
|
||||
mapType, flags, maxEntries uint32
|
||||
pinType PinType
|
||||
)
|
||||
for _, member := range btfMapMembers {
|
||||
switch member.Name {
|
||||
@@ -529,9 +531,7 @@ func mapSpecFromBTF(spec *btf.Spec, name string) (*MapSpec, error) {
|
||||
return nil, fmt.Errorf("can't get pinning: %w", err)
|
||||
}
|
||||
|
||||
if pinning != 0 {
|
||||
return nil, fmt.Errorf("'pinning' attribute not supported: %w", ErrNotSupported)
|
||||
}
|
||||
pinType = PinType(pinning)
|
||||
|
||||
case "key", "value":
|
||||
default:
|
||||
@@ -540,12 +540,14 @@ func mapSpecFromBTF(spec *btf.Spec, name string) (*MapSpec, error) {
|
||||
}
|
||||
|
||||
return &MapSpec{
|
||||
Name: SanitizeName(name, -1),
|
||||
Type: MapType(mapType),
|
||||
KeySize: keySize,
|
||||
ValueSize: valueSize,
|
||||
MaxEntries: maxEntries,
|
||||
Flags: flags,
|
||||
BTF: btfMap,
|
||||
Pinning: pinType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -636,6 +638,8 @@ func getProgType(sectionName string) (ProgramType, AttachType, string) {
|
||||
"lirc_mode2": {LircMode2, AttachLircMode2},
|
||||
"flow_dissector": {FlowDissector, AttachFlowDissector},
|
||||
"iter/": {Tracing, AttachTraceIter},
|
||||
"sk_lookup/": {SkLookup, AttachSkLookup},
|
||||
"lsm/": {LSM, AttachLSMMac},
|
||||
|
||||
"cgroup_skb/ingress": {CGroupSKB, AttachCGroupInetIngress},
|
||||
"cgroup_skb/egress": {CGroupSKB, AttachCGroupInetEgress},
|
||||
@@ -684,7 +688,7 @@ func (ec *elfCode) loadRelocations(sections map[elf.SectionIndex]*elf.Section) (
|
||||
return nil, nil, fmt.Errorf("section %s: relocations are less than 16 bytes", sec.Name)
|
||||
}
|
||||
|
||||
r := sec.Open()
|
||||
r := bufio.NewReader(sec.Open())
|
||||
for off := uint64(0); off < sec.Size; off += sec.Entsize {
|
||||
ent := io.LimitReader(r, int64(sec.Entsize))
|
||||
|
||||
|
7
vendor/github.com/cilium/ebpf/go.mod
generated
vendored
7
vendor/github.com/cilium/ebpf/go.mod
generated
vendored
@@ -1,5 +1,8 @@
|
||||
module github.com/cilium/ebpf
|
||||
|
||||
go 1.13
|
||||
go 1.14
|
||||
|
||||
require golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
|
||||
require (
|
||||
github.com/google/go-cmp v0.5.2
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9
|
||||
)
|
||||
|
4
vendor/github.com/cilium/ebpf/go.sum
generated
vendored
4
vendor/github.com/cilium/ebpf/go.sum
generated
vendored
@@ -1,2 +1,6 @@
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9 h1:1/DFK4b7JH8DmkqhUk48onnSfrPzImPoVxuomtbT2nk=
|
||||
golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
135
vendor/github.com/cilium/ebpf/internal/btf/btf.go
generated
vendored
135
vendor/github.com/cilium/ebpf/internal/btf/btf.go
generated
vendored
@@ -31,7 +31,7 @@ var (
|
||||
type Spec struct {
|
||||
rawTypes []rawType
|
||||
strings stringTable
|
||||
types map[string][]Type
|
||||
types map[string][]namedType
|
||||
funcInfos map[string]extInfo
|
||||
lineInfos map[string]extInfo
|
||||
byteOrder binary.ByteOrder
|
||||
@@ -59,29 +59,9 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var (
|
||||
btfSection *elf.Section
|
||||
btfExtSection *elf.Section
|
||||
sectionSizes = make(map[string]uint32)
|
||||
)
|
||||
|
||||
for _, sec := range file.Sections {
|
||||
switch sec.Name {
|
||||
case ".BTF":
|
||||
btfSection = sec
|
||||
case ".BTF.ext":
|
||||
btfExtSection = sec
|
||||
default:
|
||||
if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS {
|
||||
break
|
||||
}
|
||||
|
||||
if sec.Size > math.MaxUint32 {
|
||||
return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
|
||||
}
|
||||
|
||||
sectionSizes[sec.Name] = uint32(sec.Size)
|
||||
}
|
||||
btfSection, btfExtSection, sectionSizes, err := findBtfSections(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if btfSection == nil {
|
||||
@@ -129,6 +109,51 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func findBtfSections(file *elf.File) (*elf.Section, *elf.Section, map[string]uint32, error) {
|
||||
var (
|
||||
btfSection *elf.Section
|
||||
btfExtSection *elf.Section
|
||||
sectionSizes = make(map[string]uint32)
|
||||
)
|
||||
|
||||
for _, sec := range file.Sections {
|
||||
switch sec.Name {
|
||||
case ".BTF":
|
||||
btfSection = sec
|
||||
case ".BTF.ext":
|
||||
btfExtSection = sec
|
||||
default:
|
||||
if sec.Type != elf.SHT_PROGBITS && sec.Type != elf.SHT_NOBITS {
|
||||
break
|
||||
}
|
||||
|
||||
if sec.Size > math.MaxUint32 {
|
||||
return nil, nil, nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
|
||||
}
|
||||
|
||||
sectionSizes[sec.Name] = uint32(sec.Size)
|
||||
}
|
||||
}
|
||||
return btfSection, btfExtSection, sectionSizes, nil
|
||||
}
|
||||
|
||||
func loadSpecFromVmlinux(rd io.ReaderAt) (*Spec, error) {
|
||||
file, err := elf.NewFile(rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
btfSection, _, _, err := findBtfSections(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(".BTF ELF section: %s", err)
|
||||
}
|
||||
if btfSection == nil {
|
||||
return nil, fmt.Errorf("unable to find .BTF ELF section")
|
||||
}
|
||||
return loadNakedSpec(btfSection.Open(), file.ByteOrder, nil, nil)
|
||||
}
|
||||
|
||||
func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
|
||||
rawTypes, rawStrings, err := parseBTF(btf, bo)
|
||||
if err != nil {
|
||||
@@ -176,16 +201,43 @@ func LoadKernelSpec() (*Spec, error) {
|
||||
}
|
||||
|
||||
func loadKernelSpec() (*Spec, error) {
|
||||
fh, err := os.Open("/sys/kernel/btf/vmlinux")
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("can't open kernel BTF at /sys/kernel/btf/vmlinux: %w", ErrNotFound)
|
||||
}
|
||||
release, err := unix.KernelRelease()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't read kernel BTF: %s", err)
|
||||
return nil, fmt.Errorf("can't read kernel release number: %w", err)
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
return loadNakedSpec(fh, internal.NativeEndian, nil, nil)
|
||||
fh, err := os.Open("/sys/kernel/btf/vmlinux")
|
||||
if err == nil {
|
||||
defer fh.Close()
|
||||
|
||||
return loadNakedSpec(fh, internal.NativeEndian, nil, nil)
|
||||
}
|
||||
|
||||
// use same list of locations as libbpf
|
||||
// https://github.com/libbpf/libbpf/blob/9a3a42608dbe3731256a5682a125ac1e23bced8f/src/btf.c#L3114-L3122
|
||||
locations := []string{
|
||||
"/boot/vmlinux-%s",
|
||||
"/lib/modules/%s/vmlinux-%[1]s",
|
||||
"/lib/modules/%s/build/vmlinux",
|
||||
"/usr/lib/modules/%s/kernel/vmlinux",
|
||||
"/usr/lib/debug/boot/vmlinux-%s",
|
||||
"/usr/lib/debug/boot/vmlinux-%s.debug",
|
||||
"/usr/lib/debug/lib/modules/%s/vmlinux",
|
||||
}
|
||||
|
||||
for _, loc := range locations {
|
||||
path := fmt.Sprintf(loc, release)
|
||||
|
||||
fh, err := os.Open(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
defer fh.Close()
|
||||
|
||||
return loadSpecFromVmlinux(fh)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported)
|
||||
}
|
||||
|
||||
func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) {
|
||||
@@ -402,9 +454,19 @@ func (s *Spec) Map(name string) (*Map, []Member, error) {
|
||||
switch member.Name {
|
||||
case "key":
|
||||
key = member.Type
|
||||
if pk, isPtr := key.(*Pointer); !isPtr {
|
||||
return nil, nil, fmt.Errorf("key type is not a pointer: %T", key)
|
||||
} else {
|
||||
key = pk.Target
|
||||
}
|
||||
|
||||
case "value":
|
||||
value = member.Type
|
||||
if vk, isPtr := value.(*Pointer); !isPtr {
|
||||
return nil, nil, fmt.Errorf("value type is not a pointer: %T", value)
|
||||
} else {
|
||||
value = vk.Target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,11 +503,16 @@ func (s *Spec) FindType(name string, typ Type) error {
|
||||
candidate Type
|
||||
)
|
||||
|
||||
for _, typ := range s.types[name] {
|
||||
for _, typ := range s.types[essentialName(name)] {
|
||||
if reflect.TypeOf(typ) != wanted {
|
||||
continue
|
||||
}
|
||||
|
||||
// Match against the full name, not just the essential one.
|
||||
if typ.name() != name {
|
||||
continue
|
||||
}
|
||||
|
||||
if candidate != nil {
|
||||
return fmt.Errorf("type %s: multiple candidates for %T", name, typ)
|
||||
}
|
||||
@@ -621,9 +688,7 @@ type bpfLoadBTFAttr struct {
|
||||
}
|
||||
|
||||
func bpfLoadBTF(attr *bpfLoadBTFAttr) (*internal.FD, error) {
|
||||
const _BTFLoad = 18
|
||||
|
||||
fd, err := internal.BPF(_BTFLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||
fd, err := internal.BPF(internal.BPF_BTF_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
18
vendor/github.com/cilium/ebpf/internal/btf/btf_types.go
generated
vendored
18
vendor/github.com/cilium/ebpf/internal/btf/btf_types.go
generated
vendored
@@ -40,10 +40,12 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
btfTypeKindShift = 24
|
||||
btfTypeKindLen = 4
|
||||
btfTypeVlenShift = 0
|
||||
btfTypeVlenMask = 16
|
||||
btfTypeKindShift = 24
|
||||
btfTypeKindLen = 4
|
||||
btfTypeVlenShift = 0
|
||||
btfTypeVlenMask = 16
|
||||
btfTypeKindFlagShift = 31
|
||||
btfTypeKindFlagMask = 1
|
||||
)
|
||||
|
||||
// btfType is equivalent to struct btf_type in Documentation/bpf/btf.rst.
|
||||
@@ -136,6 +138,10 @@ func (bt *btfType) SetVlen(vlen int) {
|
||||
bt.setInfo(uint32(vlen), btfTypeVlenMask, btfTypeVlenShift)
|
||||
}
|
||||
|
||||
func (bt *btfType) KindFlag() bool {
|
||||
return bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift) == 1
|
||||
}
|
||||
|
||||
func (bt *btfType) Linkage() btfFuncLinkage {
|
||||
return btfFuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift))
|
||||
}
|
||||
@@ -257,3 +263,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
|
||||
types = append(types, rawType{header, data})
|
||||
}
|
||||
}
|
||||
|
||||
func intEncoding(raw uint32) (IntEncoding, uint32, byte) {
|
||||
return IntEncoding((raw & 0x0f000000) >> 24), (raw & 0x00ff0000) >> 16, byte(raw & 0x000000ff)
|
||||
}
|
||||
|
12
vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
generated
vendored
12
vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
generated
vendored
@@ -1,6 +1,7 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
@@ -58,7 +59,8 @@ func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (f
|
||||
return nil, nil, fmt.Errorf("can't seek to function info section: %v", err)
|
||||
}
|
||||
|
||||
funcInfo, err = parseExtInfo(io.LimitReader(r, int64(header.FuncInfoLen)), bo, strings)
|
||||
buf := bufio.NewReader(io.LimitReader(r, int64(header.FuncInfoLen)))
|
||||
funcInfo, err = parseExtInfo(buf, bo, strings)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("function info: %w", err)
|
||||
}
|
||||
@@ -67,7 +69,8 @@ func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (f
|
||||
return nil, nil, fmt.Errorf("can't seek to line info section: %v", err)
|
||||
}
|
||||
|
||||
lineInfo, err = parseExtInfo(io.LimitReader(r, int64(header.LineInfoLen)), bo, strings)
|
||||
buf = bufio.NewReader(io.LimitReader(r, int64(header.LineInfoLen)))
|
||||
lineInfo, err = parseExtInfo(buf, bo, strings)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("line info: %w", err)
|
||||
}
|
||||
@@ -127,6 +130,8 @@ func (ei extInfo) MarshalBinary() ([]byte, error) {
|
||||
}
|
||||
|
||||
func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]extInfo, error) {
|
||||
const maxRecordSize = 256
|
||||
|
||||
var recordSize uint32
|
||||
if err := binary.Read(r, bo, &recordSize); err != nil {
|
||||
return nil, fmt.Errorf("can't read record size: %v", err)
|
||||
@@ -136,6 +141,9 @@ func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[st
|
||||
// Need at least insnOff
|
||||
return nil, errors.New("record size too short")
|
||||
}
|
||||
if recordSize > maxRecordSize {
|
||||
return nil, fmt.Errorf("record size %v exceeds %v", recordSize, maxRecordSize)
|
||||
}
|
||||
|
||||
result := make(map[string]extInfo)
|
||||
for {
|
||||
|
49
vendor/github.com/cilium/ebpf/internal/btf/fuzz.go
generated
vendored
Normal file
49
vendor/github.com/cilium/ebpf/internal/btf/fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
// +build gofuzz
|
||||
|
||||
// Use with https://github.com/dvyukov/go-fuzz
|
||||
|
||||
package btf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/cilium/ebpf/internal"
|
||||
)
|
||||
|
||||
func FuzzSpec(data []byte) int {
|
||||
if len(data) < binary.Size(btfHeader{}) {
|
||||
return -1
|
||||
}
|
||||
|
||||
spec, err := loadNakedSpec(bytes.NewReader(data), internal.NativeEndian, nil, nil)
|
||||
if err != nil {
|
||||
if spec != nil {
|
||||
panic("spec is not nil")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if spec == nil {
|
||||
panic("spec is nil")
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func FuzzExtInfo(data []byte) int {
|
||||
if len(data) < binary.Size(btfExtHeader{}) {
|
||||
return -1
|
||||
}
|
||||
|
||||
table := stringTable("\x00foo\x00barfoo\x00")
|
||||
info, err := parseExtInfo(bytes.NewReader(data), internal.NativeEndian, table)
|
||||
if err != nil {
|
||||
if info != nil {
|
||||
panic("info is not nil")
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if info == nil {
|
||||
panic("info is nil")
|
||||
}
|
||||
return 1
|
||||
}
|
185
vendor/github.com/cilium/ebpf/internal/btf/types.go
generated
vendored
185
vendor/github.com/cilium/ebpf/internal/btf/types.go
generated
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const maxTypeDepth = 32
|
||||
@@ -23,9 +24,19 @@ type Type interface {
|
||||
// Make a copy of the type, without copying Type members.
|
||||
copy() Type
|
||||
|
||||
// Enumerate all nested Types. Repeated calls must visit nested
|
||||
// types in the same order.
|
||||
walk(*copyStack)
|
||||
}
|
||||
|
||||
// namedType is a type with a name.
|
||||
//
|
||||
// Most named types simply embed Name.
|
||||
type namedType interface {
|
||||
Type
|
||||
name() string
|
||||
}
|
||||
|
||||
// Name identifies a type.
|
||||
//
|
||||
// Anonymous types have an empty name.
|
||||
@@ -43,15 +54,30 @@ func (v *Void) size() uint32 { return 0 }
|
||||
func (v *Void) copy() Type { return (*Void)(nil) }
|
||||
func (v *Void) walk(*copyStack) {}
|
||||
|
||||
type IntEncoding byte
|
||||
|
||||
const (
|
||||
Signed IntEncoding = 1 << iota
|
||||
Char
|
||||
Bool
|
||||
)
|
||||
|
||||
// Int is an integer of a given length.
|
||||
type Int struct {
|
||||
TypeID
|
||||
Name
|
||||
|
||||
// The size of the integer in bytes.
|
||||
Size uint32
|
||||
Size uint32
|
||||
Encoding IntEncoding
|
||||
// Offset is the starting bit offset. Currently always 0.
|
||||
// See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int
|
||||
Offset uint32
|
||||
Bits byte
|
||||
}
|
||||
|
||||
var _ namedType = (*Int)(nil)
|
||||
|
||||
func (i *Int) size() uint32 { return i.Size }
|
||||
func (i *Int) walk(*copyStack) {}
|
||||
func (i *Int) copy() Type {
|
||||
@@ -109,6 +135,10 @@ func (s *Struct) copy() Type {
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func (s *Struct) members() []Member {
|
||||
return s.Members
|
||||
}
|
||||
|
||||
// Union is a compound type where members occupy the same memory.
|
||||
type Union struct {
|
||||
TypeID
|
||||
@@ -133,25 +163,51 @@ func (u *Union) copy() Type {
|
||||
return &cpy
|
||||
}
|
||||
|
||||
func (u *Union) members() []Member {
|
||||
return u.Members
|
||||
}
|
||||
|
||||
type composite interface {
|
||||
members() []Member
|
||||
}
|
||||
|
||||
var (
|
||||
_ composite = (*Struct)(nil)
|
||||
_ composite = (*Union)(nil)
|
||||
)
|
||||
|
||||
// Member is part of a Struct or Union.
|
||||
//
|
||||
// It is not a valid Type.
|
||||
type Member struct {
|
||||
Name
|
||||
Type Type
|
||||
Offset uint32
|
||||
Type Type
|
||||
// Offset is the bit offset of this member
|
||||
Offset uint32
|
||||
BitfieldSize uint32
|
||||
}
|
||||
|
||||
// Enum lists possible values.
|
||||
type Enum struct {
|
||||
TypeID
|
||||
Name
|
||||
Values []EnumValue
|
||||
}
|
||||
|
||||
// EnumValue is part of an Enum
|
||||
//
|
||||
// Is is not a valid Type
|
||||
type EnumValue struct {
|
||||
Name
|
||||
Value int32
|
||||
}
|
||||
|
||||
func (e *Enum) size() uint32 { return 4 }
|
||||
func (e *Enum) walk(*copyStack) {}
|
||||
func (e *Enum) copy() Type {
|
||||
cpy := *e
|
||||
cpy.Values = make([]EnumValue, len(e.Values))
|
||||
copy(cpy.Values, e.Values)
|
||||
return &cpy
|
||||
}
|
||||
|
||||
@@ -180,36 +236,39 @@ func (td *Typedef) copy() Type {
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Volatile is a modifier.
|
||||
// Volatile is a qualifier.
|
||||
type Volatile struct {
|
||||
TypeID
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (v *Volatile) qualify() Type { return v.Type }
|
||||
func (v *Volatile) walk(cs *copyStack) { cs.push(&v.Type) }
|
||||
func (v *Volatile) copy() Type {
|
||||
cpy := *v
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Const is a modifier.
|
||||
// Const is a qualifier.
|
||||
type Const struct {
|
||||
TypeID
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (c *Const) qualify() Type { return c.Type }
|
||||
func (c *Const) walk(cs *copyStack) { cs.push(&c.Type) }
|
||||
func (c *Const) copy() Type {
|
||||
cpy := *c
|
||||
return &cpy
|
||||
}
|
||||
|
||||
// Restrict is a modifier.
|
||||
// Restrict is a qualifier.
|
||||
type Restrict struct {
|
||||
TypeID
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (r *Restrict) qualify() Type { return r.Type }
|
||||
func (r *Restrict) walk(cs *copyStack) { cs.push(&r.Type) }
|
||||
func (r *Restrict) copy() Type {
|
||||
cpy := *r
|
||||
@@ -233,15 +292,28 @@ func (f *Func) copy() Type {
|
||||
type FuncProto struct {
|
||||
TypeID
|
||||
Return Type
|
||||
// Parameters not supported yet
|
||||
Params []FuncParam
|
||||
}
|
||||
|
||||
func (fp *FuncProto) walk(cs *copyStack) {
|
||||
cs.push(&fp.Return)
|
||||
for i := range fp.Params {
|
||||
cs.push(&fp.Params[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
func (fp *FuncProto) walk(cs *copyStack) { cs.push(&fp.Return) }
|
||||
func (fp *FuncProto) copy() Type {
|
||||
cpy := *fp
|
||||
cpy.Params = make([]FuncParam, len(fp.Params))
|
||||
copy(cpy.Params, fp.Params)
|
||||
return &cpy
|
||||
}
|
||||
|
||||
type FuncParam struct {
|
||||
Name
|
||||
Type Type
|
||||
}
|
||||
|
||||
// Var is a global variable.
|
||||
type Var struct {
|
||||
TypeID
|
||||
@@ -298,6 +370,16 @@ var (
|
||||
_ sizer = (*Datasec)(nil)
|
||||
)
|
||||
|
||||
type qualifier interface {
|
||||
qualify() Type
|
||||
}
|
||||
|
||||
var (
|
||||
_ qualifier = (*Const)(nil)
|
||||
_ qualifier = (*Restrict)(nil)
|
||||
_ qualifier = (*Volatile)(nil)
|
||||
)
|
||||
|
||||
// Sizeof returns the size of a type in bytes.
|
||||
//
|
||||
// Returns an error if the size can't be computed.
|
||||
@@ -326,14 +408,9 @@ func Sizeof(typ Type) (int, error) {
|
||||
case *Typedef:
|
||||
typ = v.Type
|
||||
continue
|
||||
case *Volatile:
|
||||
typ = v.Type
|
||||
continue
|
||||
case *Const:
|
||||
typ = v.Type
|
||||
continue
|
||||
case *Restrict:
|
||||
typ = v.Type
|
||||
|
||||
case qualifier:
|
||||
typ = v.qualify()
|
||||
continue
|
||||
|
||||
default:
|
||||
@@ -383,7 +460,7 @@ func copyType(typ Type) Type {
|
||||
}
|
||||
|
||||
// copyStack keeps track of pointers to types which still
|
||||
// need to be copied.
|
||||
// need to be visited.
|
||||
type copyStack []*Type
|
||||
|
||||
// push adds a type to the stack.
|
||||
@@ -403,19 +480,13 @@ func (cs *copyStack) pop() *Type {
|
||||
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) {
|
||||
func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map[string][]namedType, err error) {
|
||||
type fixupDef struct {
|
||||
id TypeID
|
||||
expectedKind btfKind
|
||||
@@ -427,7 +498,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
fixups = append(fixups, fixupDef{id, expectedKind, typ})
|
||||
}
|
||||
|
||||
convertMembers := func(raw []btfMember) ([]Member, error) {
|
||||
convertMembers := func(raw []btfMember, kindFlag bool) ([]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))
|
||||
@@ -436,10 +507,15 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get name for member %d: %w", i, err)
|
||||
}
|
||||
members = append(members, Member{
|
||||
m := Member{
|
||||
Name: name,
|
||||
Offset: btfMember.Offset,
|
||||
})
|
||||
}
|
||||
if kindFlag {
|
||||
m.BitfieldSize = btfMember.Offset >> 24
|
||||
m.Offset &= 0xffffff
|
||||
}
|
||||
members = append(members, m)
|
||||
}
|
||||
for i := range members {
|
||||
fixup(raw[i].Type, kindUnknown, &members[i].Type)
|
||||
@@ -449,7 +525,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
|
||||
types := make([]Type, 0, len(rawTypes))
|
||||
types = append(types, (*Void)(nil))
|
||||
namedTypes = make(map[string][]Type)
|
||||
namedTypes = make(map[string][]namedType)
|
||||
|
||||
for i, raw := range rawTypes {
|
||||
var (
|
||||
@@ -466,7 +542,8 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
|
||||
switch raw.Kind() {
|
||||
case kindInt:
|
||||
typ = &Int{id, name, raw.Size()}
|
||||
encoding, offset, bits := intEncoding(*raw.data.(*uint32))
|
||||
typ = &Int{id, name, raw.Size(), encoding, offset, bits}
|
||||
|
||||
case kindPointer:
|
||||
ptr := &Pointer{id, nil}
|
||||
@@ -483,21 +560,33 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
typ = arr
|
||||
|
||||
case kindStruct:
|
||||
members, err := convertMembers(raw.data.([]btfMember))
|
||||
members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err)
|
||||
}
|
||||
typ = &Struct{id, name, raw.Size(), members}
|
||||
|
||||
case kindUnion:
|
||||
members, err := convertMembers(raw.data.([]btfMember))
|
||||
members, err := convertMembers(raw.data.([]btfMember), raw.KindFlag())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("union %s (id %d): %w", name, id, err)
|
||||
}
|
||||
typ = &Union{id, name, raw.Size(), members}
|
||||
|
||||
case kindEnum:
|
||||
typ = &Enum{id, name}
|
||||
rawvals := raw.data.([]btfEnum)
|
||||
vals := make([]EnumValue, 0, len(rawvals))
|
||||
for i, btfVal := range rawvals {
|
||||
name, err := rawStrings.LookupName(btfVal.NameOff)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get name for enum value %d: %s", i, err)
|
||||
}
|
||||
vals = append(vals, EnumValue{
|
||||
Name: name,
|
||||
Value: btfVal.Val,
|
||||
})
|
||||
}
|
||||
typ = &Enum{id, name, vals}
|
||||
|
||||
case kindForward:
|
||||
typ = &Fwd{id, name}
|
||||
@@ -528,7 +617,22 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
typ = fn
|
||||
|
||||
case kindFuncProto:
|
||||
fp := &FuncProto{id, nil}
|
||||
rawparams := raw.data.([]btfParam)
|
||||
params := make([]FuncParam, 0, len(rawparams))
|
||||
for i, param := range rawparams {
|
||||
name, err := rawStrings.LookupName(param.NameOff)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get name for func proto parameter %d: %s", i, err)
|
||||
}
|
||||
params = append(params, FuncParam{
|
||||
Name: name,
|
||||
})
|
||||
}
|
||||
for i := range params {
|
||||
fixup(rawparams[i].Type, kindUnknown, ¶ms[i].Type)
|
||||
}
|
||||
|
||||
fp := &FuncProto{id, nil, params}
|
||||
fixup(raw.Type(), kindUnknown, &fp.Return)
|
||||
typ = fp
|
||||
|
||||
@@ -557,9 +661,9 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
|
||||
types = append(types, typ)
|
||||
|
||||
if namer, ok := typ.(namer); ok {
|
||||
if name := namer.name(); name != "" {
|
||||
namedTypes[name] = append(namedTypes[name], typ)
|
||||
if named, ok := typ.(namedType); ok {
|
||||
if name := essentialName(named.name()); name != "" {
|
||||
namedTypes[name] = append(namedTypes[name], named)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -585,3 +689,12 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (namedTypes map
|
||||
|
||||
return namedTypes, nil
|
||||
}
|
||||
|
||||
// essentialName returns name without a ___ suffix.
|
||||
func essentialName(name string) string {
|
||||
lastIdx := strings.LastIndex(name, "___")
|
||||
if lastIdx > 0 {
|
||||
return name[:lastIdx]
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
8
vendor/github.com/cilium/ebpf/internal/feature.go
generated
vendored
8
vendor/github.com/cilium/ebpf/internal/feature.go
generated
vendored
@@ -20,6 +20,9 @@ type UnsupportedFeatureError struct {
|
||||
}
|
||||
|
||||
func (ufe *UnsupportedFeatureError) Error() string {
|
||||
if ufe.MinimumVersion.Unspecified() {
|
||||
return fmt.Sprintf("%s not supported", ufe.Name)
|
||||
}
|
||||
return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
|
||||
}
|
||||
|
||||
@@ -120,3 +123,8 @@ func (v Version) Less(other Version) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Unspecified returns true if the version is all zero.
|
||||
func (v Version) Unspecified() bool {
|
||||
return v[0] == 0 && v[1] == 0 && v[2] == 0
|
||||
}
|
||||
|
27
vendor/github.com/cilium/ebpf/internal/syscall.go
generated
vendored
27
vendor/github.com/cilium/ebpf/internal/syscall.go
generated
vendored
@@ -137,3 +137,30 @@ func BPFObjGet(fileName string) (*FD, error) {
|
||||
}
|
||||
return NewFD(uint32(ptr)), nil
|
||||
}
|
||||
|
||||
type bpfObjGetInfoByFDAttr struct {
|
||||
fd uint32
|
||||
infoLen uint32
|
||||
info Pointer
|
||||
}
|
||||
|
||||
// BPFObjGetInfoByFD wraps BPF_OBJ_GET_INFO_BY_FD.
|
||||
//
|
||||
// Available from 4.13.
|
||||
func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error {
|
||||
value, err := fd.Value()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
attr := bpfObjGetInfoByFDAttr{
|
||||
fd: value,
|
||||
infoLen: uint32(size),
|
||||
info: NewPointer(info),
|
||||
}
|
||||
_, err = BPF(BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("fd %v: %w", fd, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
14
vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
generated
vendored
14
vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
generated
vendored
@@ -3,6 +3,7 @@
|
||||
package unix
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"syscall"
|
||||
|
||||
linux "golang.org/x/sys/unix"
|
||||
@@ -19,6 +20,7 @@ const (
|
||||
EPERM = linux.EPERM
|
||||
ESRCH = linux.ESRCH
|
||||
ENODEV = linux.ENODEV
|
||||
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
|
||||
BPF_F_RDONLY_PROG = linux.BPF_F_RDONLY_PROG
|
||||
BPF_F_WRONLY_PROG = linux.BPF_F_WRONLY_PROG
|
||||
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
|
||||
@@ -148,3 +150,15 @@ func Gettid() int {
|
||||
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
|
||||
return linux.Tgkill(tgid, tid, sig)
|
||||
}
|
||||
|
||||
func KernelRelease() (string, error) {
|
||||
var uname Utsname
|
||||
err := Uname(&uname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
end := bytes.IndexByte(uname.Release[:], 0)
|
||||
release := string(uname.Release[:end])
|
||||
return release, nil
|
||||
}
|
||||
|
5
vendor/github.com/cilium/ebpf/internal/unix/types_other.go
generated
vendored
5
vendor/github.com/cilium/ebpf/internal/unix/types_other.go
generated
vendored
@@ -20,6 +20,7 @@ const (
|
||||
EPERM = syscall.EPERM
|
||||
ESRCH = syscall.ESRCH
|
||||
ENODEV = syscall.ENODEV
|
||||
BPF_F_NUMA_NODE = 0
|
||||
BPF_F_RDONLY_PROG = 0
|
||||
BPF_F_WRONLY_PROG = 0
|
||||
BPF_OBJ_NAME_LEN = 0x10
|
||||
@@ -215,3 +216,7 @@ func Gettid() int {
|
||||
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
|
||||
return errNonLinux
|
||||
}
|
||||
|
||||
func KernelRelease() (string, error) {
|
||||
return "", errNonLinux
|
||||
}
|
||||
|
47
vendor/github.com/cilium/ebpf/linker.go
generated
vendored
47
vendor/github.com/cilium/ebpf/linker.go
generated
vendored
@@ -84,3 +84,50 @@ func needSection(insns, section asm.Instructions) (bool, error) {
|
||||
// None of the functions in the section are called.
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func fixupJumpsAndCalls(insns asm.Instructions) error {
|
||||
symbolOffsets := make(map[string]asm.RawInstructionOffset)
|
||||
iter := insns.Iterate()
|
||||
for iter.Next() {
|
||||
ins := iter.Ins
|
||||
|
||||
if ins.Symbol == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := symbolOffsets[ins.Symbol]; ok {
|
||||
return fmt.Errorf("duplicate symbol %s", ins.Symbol)
|
||||
}
|
||||
|
||||
symbolOffsets[ins.Symbol] = iter.Offset
|
||||
}
|
||||
|
||||
iter = insns.Iterate()
|
||||
for iter.Next() {
|
||||
i := iter.Index
|
||||
offset := iter.Offset
|
||||
ins := iter.Ins
|
||||
|
||||
switch {
|
||||
case ins.IsFunctionCall() && ins.Constant == -1:
|
||||
// Rewrite bpf to bpf call
|
||||
callOffset, ok := symbolOffsets[ins.Reference]
|
||||
if !ok {
|
||||
return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
|
||||
}
|
||||
|
||||
ins.Constant = int64(callOffset - offset - 1)
|
||||
|
||||
case ins.OpCode.Class() == asm.JumpClass && ins.Offset == -1:
|
||||
// Rewrite jump to label
|
||||
jumpOffset, ok := symbolOffsets[ins.Reference]
|
||||
if !ok {
|
||||
return fmt.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
|
||||
}
|
||||
|
||||
ins.Offset = int16(jumpOffset - offset - 1)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
156
vendor/github.com/cilium/ebpf/map.go
generated
vendored
156
vendor/github.com/cilium/ebpf/map.go
generated
vendored
@@ -3,6 +3,8 @@ package ebpf
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/cilium/ebpf/internal"
|
||||
@@ -17,6 +19,14 @@ var (
|
||||
ErrIterationAborted = errors.New("iteration aborted")
|
||||
)
|
||||
|
||||
// MapOptions control loading a map into the kernel.
|
||||
type MapOptions struct {
|
||||
// The base path to pin maps in if requested via PinByName.
|
||||
// Existing maps will be re-used if they are compatible, otherwise an
|
||||
// error is returned.
|
||||
PinPath string
|
||||
}
|
||||
|
||||
// MapID represents the unique ID of an eBPF map
|
||||
type MapID uint32
|
||||
|
||||
@@ -31,6 +41,15 @@ type MapSpec struct {
|
||||
MaxEntries uint32
|
||||
Flags uint32
|
||||
|
||||
// Automatically pin and load a map from MapOptions.PinPath.
|
||||
// Generates an error if an existing pinned map is incompatible with the MapSpec.
|
||||
Pinning PinType
|
||||
|
||||
// Specify numa node during map creation
|
||||
// (effective only if unix.BPF_F_NUMA_NODE flag is set,
|
||||
// which can be imported from golang.org/x/sys/unix)
|
||||
NumaNode uint32
|
||||
|
||||
// The initial contents of the map. May be nil.
|
||||
Contents []MapKV
|
||||
|
||||
@@ -69,6 +88,26 @@ type MapKV struct {
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
func (ms *MapSpec) checkCompatibility(m *Map) error {
|
||||
switch {
|
||||
case m.abi.Type != ms.Type:
|
||||
return fmt.Errorf("expected type %v, got %v", ms.Type, m.abi.Type)
|
||||
|
||||
case m.abi.KeySize != ms.KeySize:
|
||||
return fmt.Errorf("expected key size %v, got %v", ms.KeySize, m.abi.KeySize)
|
||||
|
||||
case m.abi.ValueSize != ms.ValueSize:
|
||||
return fmt.Errorf("expected value size %v, got %v", ms.ValueSize, m.abi.ValueSize)
|
||||
|
||||
case m.abi.MaxEntries != ms.MaxEntries:
|
||||
return fmt.Errorf("expected max entries %v, got %v", ms.MaxEntries, m.abi.MaxEntries)
|
||||
|
||||
case m.abi.Flags != ms.Flags:
|
||||
return fmt.Errorf("expected flags %v, got %v", ms.Flags, m.abi.Flags)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Map represents a Map file descriptor.
|
||||
//
|
||||
// It is not safe to close a map which is used by other goroutines.
|
||||
@@ -105,15 +144,22 @@ func NewMapFromFD(fd int) (*Map, error) {
|
||||
|
||||
// NewMap creates a new Map.
|
||||
//
|
||||
// It's equivalent to calling NewMapWithOptions with default options.
|
||||
func NewMap(spec *MapSpec) (*Map, error) {
|
||||
return NewMapWithOptions(spec, MapOptions{})
|
||||
}
|
||||
|
||||
// NewMapWithOptions creates a new Map.
|
||||
//
|
||||
// Creating a map for the first time will perform feature detection
|
||||
// by creating small, temporary maps.
|
||||
//
|
||||
// The caller is responsible for ensuring the process' rlimit is set
|
||||
// sufficiently high for locking memory during map creation. This can be done
|
||||
// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMap.
|
||||
func NewMap(spec *MapSpec) (*Map, error) {
|
||||
// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMapWithOptions.
|
||||
func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
|
||||
if spec.BTF == nil {
|
||||
return newMapWithBTF(spec, nil)
|
||||
return newMapWithBTF(spec, nil, opts)
|
||||
}
|
||||
|
||||
handle, err := btf.NewHandle(btf.MapSpec(spec.BTF))
|
||||
@@ -121,28 +167,75 @@ func NewMap(spec *MapSpec) (*Map, error) {
|
||||
return nil, fmt.Errorf("can't load BTF: %w", err)
|
||||
}
|
||||
|
||||
return newMapWithBTF(spec, handle)
|
||||
return newMapWithBTF(spec, handle, opts)
|
||||
}
|
||||
|
||||
func newMapWithBTF(spec *MapSpec, handle *btf.Handle) (*Map, error) {
|
||||
if spec.Type != ArrayOfMaps && spec.Type != HashOfMaps {
|
||||
return createMap(spec, nil, handle)
|
||||
func newMapWithBTF(spec *MapSpec, handle *btf.Handle, opts MapOptions) (*Map, error) {
|
||||
switch spec.Pinning {
|
||||
case PinByName:
|
||||
if spec.Name == "" || opts.PinPath == "" {
|
||||
return nil, fmt.Errorf("pin by name: missing Name or PinPath")
|
||||
}
|
||||
|
||||
m, err := LoadPinnedMap(filepath.Join(opts.PinPath, spec.Name))
|
||||
if errors.Is(err, unix.ENOENT) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("load pinned map: %s", err)
|
||||
}
|
||||
|
||||
if err := spec.checkCompatibility(m); err != nil {
|
||||
m.Close()
|
||||
return nil, fmt.Errorf("use pinned map %s: %s", spec.Name, err)
|
||||
}
|
||||
|
||||
return m, nil
|
||||
|
||||
case PinNone:
|
||||
// Nothing to do here
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported pin type %d", int(spec.Pinning))
|
||||
}
|
||||
|
||||
if spec.InnerMap == nil {
|
||||
return nil, fmt.Errorf("%s requires InnerMap", spec.Type)
|
||||
var innerFd *internal.FD
|
||||
if spec.Type == ArrayOfMaps || spec.Type == HashOfMaps {
|
||||
if spec.InnerMap == nil {
|
||||
return nil, fmt.Errorf("%s requires InnerMap", spec.Type)
|
||||
}
|
||||
|
||||
template, err := createMap(spec.InnerMap, nil, handle, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer template.Close()
|
||||
|
||||
innerFd = template.fd
|
||||
}
|
||||
|
||||
template, err := createMap(spec.InnerMap, nil, handle)
|
||||
m, err := createMap(spec, innerFd, handle, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer template.Close()
|
||||
|
||||
return createMap(spec, template.fd, handle)
|
||||
if spec.Pinning == PinByName {
|
||||
if err := m.Pin(filepath.Join(opts.PinPath, spec.Name)); err != nil {
|
||||
m.Close()
|
||||
return nil, fmt.Errorf("pin map: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, error) {
|
||||
func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle, opts MapOptions) (_ *Map, err error) {
|
||||
closeOnError := func(closer io.Closer) {
|
||||
if err != nil {
|
||||
closer.Close()
|
||||
}
|
||||
}
|
||||
|
||||
abi := newMapABIFromSpec(spec)
|
||||
|
||||
switch spec.Type {
|
||||
@@ -190,6 +283,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
|
||||
valueSize: abi.ValueSize,
|
||||
maxEntries: abi.MaxEntries,
|
||||
flags: abi.Flags,
|
||||
numaNode: spec.NumaNode,
|
||||
}
|
||||
|
||||
if inner != nil {
|
||||
@@ -214,6 +308,7 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("map create: %w", err)
|
||||
}
|
||||
defer closeOnError(fd)
|
||||
|
||||
m, err := newMap(fd, spec.Name, abi)
|
||||
if err != nil {
|
||||
@@ -221,13 +316,11 @@ func createMap(spec *MapSpec, inner *internal.FD, handle *btf.Handle) (*Map, err
|
||||
}
|
||||
|
||||
if err := m.populate(spec.Contents); err != nil {
|
||||
m.Close()
|
||||
return nil, fmt.Errorf("map create: can't set initial contents: %w", err)
|
||||
}
|
||||
|
||||
if spec.Freeze {
|
||||
if err := m.Freeze(); err != nil {
|
||||
m.Close()
|
||||
return nil, fmt.Errorf("can't freeze map: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -263,7 +356,34 @@ func (m *Map) String() string {
|
||||
return fmt.Sprintf("%s#%v", m.abi.Type, m.fd)
|
||||
}
|
||||
|
||||
// ABI gets the ABI of the Map
|
||||
// Type returns the underlying type of the map.
|
||||
func (m *Map) Type() MapType {
|
||||
return m.abi.Type
|
||||
}
|
||||
|
||||
// KeySize returns the size of the map key in bytes.
|
||||
func (m *Map) KeySize() uint32 {
|
||||
return m.abi.KeySize
|
||||
}
|
||||
|
||||
// ValueSize returns the size of the map value in bytes.
|
||||
func (m *Map) ValueSize() uint32 {
|
||||
return m.abi.ValueSize
|
||||
}
|
||||
|
||||
// MaxEntries returns the maximum number of elements the map can hold.
|
||||
func (m *Map) MaxEntries() uint32 {
|
||||
return m.abi.MaxEntries
|
||||
}
|
||||
|
||||
// Flags returns the flags of the map.
|
||||
func (m *Map) Flags() uint32 {
|
||||
return m.abi.Flags
|
||||
}
|
||||
|
||||
// ABI gets the ABI of the Map.
|
||||
//
|
||||
// Deprecated: use Type, KeySize, ValueSize, MaxEntries and Flags instead.
|
||||
func (m *Map) ABI() MapABI {
|
||||
return m.abi
|
||||
}
|
||||
@@ -544,7 +664,7 @@ func (m *Map) Clone() (*Map, error) {
|
||||
|
||||
// Pin persists the map past the lifetime of the process that created it.
|
||||
//
|
||||
// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
|
||||
// This requires bpffs to be mounted above fileName. See https://docs.cilium.io/en/k8s-doc/admin/#admin-mount-bpffs
|
||||
func (m *Map) Pin(fileName string) error {
|
||||
return internal.BPFObjPin(fileName, m.fd)
|
||||
}
|
||||
@@ -789,6 +909,8 @@ func NewMapFromID(id MapID) (*Map, error) {
|
||||
}
|
||||
|
||||
// ID returns the systemwide unique ID of the map.
|
||||
//
|
||||
// Requires at least Linux 4.13.
|
||||
func (m *Map) ID() (MapID, error) {
|
||||
info, err := bpfGetMapInfoByFD(m.fd)
|
||||
if err != nil {
|
||||
|
52
vendor/github.com/cilium/ebpf/prog.go
generated
vendored
52
vendor/github.com/cilium/ebpf/prog.go
generated
vendored
@@ -208,8 +208,15 @@ func convertProgramSpec(spec *ProgramSpec, handle *btf.Handle) (*bpfProgLoadAttr
|
||||
return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
|
||||
}
|
||||
|
||||
insns := make(asm.Instructions, len(spec.Instructions))
|
||||
copy(insns, spec.Instructions)
|
||||
|
||||
if err := fixupJumpsAndCalls(insns); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
|
||||
err := spec.Instructions.Marshal(buf, internal.NativeEndian)
|
||||
err := insns.Marshal(buf, internal.NativeEndian)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -269,7 +276,14 @@ func (p *Program) String() string {
|
||||
return fmt.Sprintf("%s#%v", p.abi.Type, p.fd)
|
||||
}
|
||||
|
||||
// ABI gets the ABI of the Program
|
||||
// Type returns the underlying type of the program.
|
||||
func (p *Program) Type() ProgramType {
|
||||
return p.abi.Type
|
||||
}
|
||||
|
||||
// ABI gets the ABI of the Program.
|
||||
//
|
||||
// Deprecated: use Type instead.
|
||||
func (p *Program) ABI() ProgramABI {
|
||||
return p.abi
|
||||
}
|
||||
@@ -308,7 +322,7 @@ func (p *Program) Clone() (*Program, error) {
|
||||
|
||||
// Pin persists the Program past the lifetime of the process that created it
|
||||
//
|
||||
// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
|
||||
// This requires bpffs to be mounted above fileName. See https://docs.cilium.io/en/k8s-doc/admin/#admin-mount-bpffs
|
||||
func (p *Program) Pin(fileName string) error {
|
||||
if err := internal.BPFObjPin(fileName, p.fd); err != nil {
|
||||
return fmt.Errorf("can't pin program: %w", err)
|
||||
@@ -595,12 +609,16 @@ func (p *Program) ID() (ProgramID, error) {
|
||||
return ProgramID(info.id), nil
|
||||
}
|
||||
|
||||
func resolveBTFType(name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
|
||||
func findKernelType(name string, typ btf.Type) error {
|
||||
kernel, err := btf.LoadKernelSpec()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't resolve BTF type %s: %w", name, err)
|
||||
return fmt.Errorf("can't load kernel spec: %w", err)
|
||||
}
|
||||
|
||||
return kernel.FindType(name, typ)
|
||||
}
|
||||
|
||||
func resolveBTFType(name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
|
||||
type match struct {
|
||||
p ProgramType
|
||||
a AttachType
|
||||
@@ -608,10 +626,30 @@ func resolveBTFType(name string, progType ProgramType, attachType AttachType) (b
|
||||
|
||||
target := match{progType, attachType}
|
||||
switch target {
|
||||
case match{LSM, AttachLSMMac}:
|
||||
var target btf.Func
|
||||
err := findKernelType("bpf_lsm_"+name, &target)
|
||||
if errors.Is(err, btf.ErrNotFound) {
|
||||
return nil, &internal.UnsupportedFeatureError{
|
||||
Name: name + " LSM hook",
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve BTF for LSM hook %s: %w", name, err)
|
||||
}
|
||||
|
||||
return &target, nil
|
||||
|
||||
case match{Tracing, AttachTraceIter}:
|
||||
var target btf.Func
|
||||
if err := kernel.FindType("bpf_iter_"+name, &target); err != nil {
|
||||
return nil, fmt.Errorf("can't resolve BTF for iterator %s: %w", name, err)
|
||||
err := findKernelType("bpf_iter_"+name, &target)
|
||||
if errors.Is(err, btf.ErrNotFound) {
|
||||
return nil, &internal.UnsupportedFeatureError{
|
||||
Name: name + " iterator",
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve BTF for iterator %s: %w", name, err)
|
||||
}
|
||||
|
||||
return &target, nil
|
||||
|
10
vendor/github.com/cilium/ebpf/readme.md
generated
vendored
10
vendor/github.com/cilium/ebpf/readme.md
generated
vendored
@@ -1,10 +1,13 @@
|
||||
eBPF
|
||||
-------
|
||||
[](https://godoc.org/github.com/cilium/ebpf)
|
||||
[](https://pkg.go.dev/github.com/cilium/ebpf)
|
||||
|
||||
eBPF is a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs. It has minimal external dependencies and is intended to be used in long running processes.
|
||||
|
||||
[ebpf/asm](https://godoc.org/github.com/cilium/ebpf/asm) contains a basic assembler.
|
||||
* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic assembler.
|
||||
* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF to various hooks.
|
||||
* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a PERF_EVENT_ARRAY.
|
||||
* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows embedding eBPF in Go.
|
||||
|
||||
The library is maintained by [Cloudflare](https://www.cloudflare.com) and [Cilium](https://www.cilium.io). Feel free to [join](https://cilium.herokuapp.com/) the [libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack.
|
||||
|
||||
@@ -20,6 +23,7 @@ right now**. Expect to update your code if you want to follow along.
|
||||
|
||||
## Useful resources
|
||||
|
||||
* [Cilium eBPF documentation](https://cilium.readthedocs.io/en/latest/bpf/#bpf-guide) (recommended)
|
||||
* [eBPF.io](https://ebpf.io) (recommended)
|
||||
* [Cilium eBPF documentation](https://docs.cilium.io/en/latest/bpf/#bpf-guide) (recommended)
|
||||
* [Linux documentation on BPF](http://elixir.free-electrons.com/linux/latest/source/Documentation/networking/filter.txt)
|
||||
* [eBPF features by Linux version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md)
|
||||
|
20
vendor/github.com/cilium/ebpf/run-tests.sh
generated
vendored
20
vendor/github.com/cilium/ebpf/run-tests.sh
generated
vendored
@@ -12,7 +12,8 @@ if [[ "${1:-}" = "--in-vm" ]]; then
|
||||
export CGO_ENABLED=0
|
||||
export GOFLAGS=-mod=readonly
|
||||
export GOPATH=/run/go-path
|
||||
export GOPROXY=file:///run/go-root/pkg/mod/cache/download
|
||||
export GOPROXY=file:///run/go-path/pkg/mod/cache/download
|
||||
export GOSUMDB=off
|
||||
export GOCACHE=/run/go-cache
|
||||
|
||||
elfs=""
|
||||
@@ -21,7 +22,9 @@ if [[ "${1:-}" = "--in-vm" ]]; then
|
||||
fi
|
||||
|
||||
echo Running tests...
|
||||
/usr/local/bin/go test -coverprofile="$1/coverage.txt" -covermode=atomic -v -elfs "$elfs" ./...
|
||||
# TestLibBPFCompat runs separately to pass the "-elfs" flag only for it: https://github.com/cilium/ebpf/pull/119
|
||||
go test -v -count 1 -run TestLibBPFCompat -elfs "$elfs"
|
||||
go test -v -count 1 ./...
|
||||
touch "$1/success"
|
||||
exit 0
|
||||
fi
|
||||
@@ -66,11 +69,12 @@ fi
|
||||
|
||||
echo Testing on "${kernel_version}"
|
||||
$sudo virtme-run --kimg "${tmp_dir}/${kernel}" --memory 512M --pwd \
|
||||
--rw \
|
||||
--rwdir=/run/input="${input}" \
|
||||
--rwdir=/run/output="${output}" \
|
||||
--rodir=/run/go-path="$(go env GOPATH)" \
|
||||
--rwdir=/run/go-cache="$(go env GOCACHE)" \
|
||||
--script-sh "$(realpath "$0") --in-vm /run/output" \
|
||||
--script-sh "PATH=\"$PATH\" $(realpath "$0") --in-vm /run/output" \
|
||||
--qemu-opts -smp 2 # need at least two CPUs for some tests
|
||||
|
||||
if [[ ! -e "${output}/success" ]]; then
|
||||
@@ -78,11 +82,11 @@ if [[ ! -e "${output}/success" ]]; then
|
||||
exit 1
|
||||
else
|
||||
echo "Test successful on ${kernel_version}"
|
||||
if [[ -v CODECOV_TOKEN ]]; then
|
||||
curl --fail -s https://codecov.io/bash > "${tmp_dir}/codecov.sh"
|
||||
chmod +x "${tmp_dir}/codecov.sh"
|
||||
"${tmp_dir}/codecov.sh" -f "${output}/coverage.txt"
|
||||
fi
|
||||
# if [[ -v CODECOV_TOKEN ]]; then
|
||||
# curl --fail -s https://codecov.io/bash > "${tmp_dir}/codecov.sh"
|
||||
# chmod +x "${tmp_dir}/codecov.sh"
|
||||
# "${tmp_dir}/codecov.sh" -f "${output}/coverage.txt"
|
||||
# fi
|
||||
fi
|
||||
|
||||
$sudo rm -r "${input}"
|
||||
|
29
vendor/github.com/cilium/ebpf/syscalls.go
generated
vendored
29
vendor/github.com/cilium/ebpf/syscalls.go
generated
vendored
@@ -129,12 +129,6 @@ type bpfProgTestRunAttr struct {
|
||||
duration uint32
|
||||
}
|
||||
|
||||
type bpfObjGetInfoByFDAttr struct {
|
||||
fd uint32
|
||||
infoLen uint32
|
||||
info internal.Pointer // May be either bpfMapInfo or bpfProgInfo
|
||||
}
|
||||
|
||||
type bpfGetFDByIDAttr struct {
|
||||
id uint32
|
||||
next uint32
|
||||
@@ -353,28 +347,9 @@ func bpfMapFreeze(m *internal.FD) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func bpfGetObjectInfoByFD(fd *internal.FD, info unsafe.Pointer, size uintptr) error {
|
||||
value, err := fd.Value()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// available from 4.13
|
||||
attr := bpfObjGetInfoByFDAttr{
|
||||
fd: value,
|
||||
infoLen: uint32(size),
|
||||
info: internal.NewPointer(info),
|
||||
}
|
||||
_, err = internal.BPF(internal.BPF_OBJ_GET_INFO_BY_FD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||
if err != nil {
|
||||
return fmt.Errorf("fd %d: %w", fd, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) {
|
||||
var info bpfProgInfo
|
||||
if err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
|
||||
if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
|
||||
return nil, fmt.Errorf("can't get program info: %w", err)
|
||||
}
|
||||
return &info, nil
|
||||
@@ -382,7 +357,7 @@ func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) {
|
||||
|
||||
func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) {
|
||||
var info bpfMapInfo
|
||||
err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
|
||||
err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't get map info: %w", err)
|
||||
}
|
||||
|
54
vendor/github.com/cilium/ebpf/types.go
generated
vendored
54
vendor/github.com/cilium/ebpf/types.go
generated
vendored
@@ -1,6 +1,6 @@
|
||||
package ebpf
|
||||
|
||||
//go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType
|
||||
//go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType
|
||||
|
||||
// MapType indicates the type map structure
|
||||
// that will be initialized in the kernel.
|
||||
@@ -96,60 +96,37 @@ type ProgramType uint32
|
||||
|
||||
// eBPF program types
|
||||
const (
|
||||
// Unrecognized program type
|
||||
UnspecifiedProgram ProgramType = iota
|
||||
// SocketFilter socket or seccomp filter
|
||||
SocketFilter
|
||||
// Kprobe program
|
||||
Kprobe
|
||||
// SchedCLS traffic control shaper
|
||||
SchedCLS
|
||||
// SchedACT routing control shaper
|
||||
SchedACT
|
||||
// TracePoint program
|
||||
TracePoint
|
||||
// XDP program
|
||||
XDP
|
||||
// PerfEvent program
|
||||
PerfEvent
|
||||
// CGroupSKB program
|
||||
CGroupSKB
|
||||
// CGroupSock program
|
||||
CGroupSock
|
||||
// LWTIn program
|
||||
LWTIn
|
||||
// LWTOut program
|
||||
LWTOut
|
||||
// LWTXmit program
|
||||
LWTXmit
|
||||
// SockOps program
|
||||
SockOps
|
||||
// SkSKB program
|
||||
SkSKB
|
||||
// CGroupDevice program
|
||||
CGroupDevice
|
||||
// SkMsg program
|
||||
SkMsg
|
||||
// RawTracepoint program
|
||||
RawTracepoint
|
||||
// CGroupSockAddr program
|
||||
CGroupSockAddr
|
||||
// LWTSeg6Local program
|
||||
LWTSeg6Local
|
||||
// LircMode2 program
|
||||
LircMode2
|
||||
// SkReuseport program
|
||||
SkReuseport
|
||||
// FlowDissector program
|
||||
FlowDissector
|
||||
// CGroupSysctl program
|
||||
CGroupSysctl
|
||||
// RawTracepointWritable program
|
||||
RawTracepointWritable
|
||||
// CGroupSockopt program
|
||||
CGroupSockopt
|
||||
// Tracing program
|
||||
Tracing
|
||||
StructOps
|
||||
Extension
|
||||
LSM
|
||||
SkLookup
|
||||
)
|
||||
|
||||
// AttachType of the eBPF program, needed to differentiate allowed context accesses in
|
||||
@@ -190,7 +167,28 @@ const (
|
||||
AttachModifyReturn
|
||||
AttachLSMMac
|
||||
AttachTraceIter
|
||||
AttachCgroupInet4GetPeername
|
||||
AttachCgroupInet6GetPeername
|
||||
AttachCgroupInet4GetSockname
|
||||
AttachCgroupInet6GetSockname
|
||||
AttachXDPDevMap
|
||||
AttachCgroupInetSockRelease
|
||||
AttachXDPCPUMap
|
||||
AttachSkLookup
|
||||
AttachXDP
|
||||
)
|
||||
|
||||
// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command
|
||||
type AttachFlags uint32
|
||||
|
||||
// PinType determines whether a map is pinned into a BPFFS.
|
||||
type PinType int
|
||||
|
||||
// Valid pin types.
|
||||
//
|
||||
// Mirrors enum libbpf_pin_type.
|
||||
const (
|
||||
PinNone PinType = iota
|
||||
// Pin an object by using its name as the filename.
|
||||
PinByName
|
||||
)
|
||||
|
41
vendor/github.com/cilium/ebpf/types_string.go
generated
vendored
41
vendor/github.com/cilium/ebpf/types_string.go
generated
vendored
@@ -1,4 +1,4 @@
|
||||
// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType"; DO NOT EDIT.
|
||||
// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType"; DO NOT EDIT.
|
||||
|
||||
package ebpf
|
||||
|
||||
@@ -77,11 +77,15 @@ func _() {
|
||||
_ = x[RawTracepointWritable-24]
|
||||
_ = x[CGroupSockopt-25]
|
||||
_ = x[Tracing-26]
|
||||
_ = x[StructOps-27]
|
||||
_ = x[Extension-28]
|
||||
_ = x[LSM-29]
|
||||
_ = x[SkLookup-30]
|
||||
}
|
||||
|
||||
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracing"
|
||||
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookup"
|
||||
|
||||
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}
|
||||
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, 274, 283, 286, 294}
|
||||
|
||||
func (i ProgramType) String() string {
|
||||
if i >= ProgramType(len(_ProgramType_index)-1) {
|
||||
@@ -123,11 +127,20 @@ func _() {
|
||||
_ = x[AttachModifyReturn-26]
|
||||
_ = x[AttachLSMMac-27]
|
||||
_ = x[AttachTraceIter-28]
|
||||
_ = x[AttachCgroupInet4GetPeername-29]
|
||||
_ = x[AttachCgroupInet6GetPeername-30]
|
||||
_ = x[AttachCgroupInet4GetSockname-31]
|
||||
_ = x[AttachCgroupInet6GetSockname-32]
|
||||
_ = x[AttachXDPDevMap-33]
|
||||
_ = x[AttachCgroupInetSockRelease-34]
|
||||
_ = x[AttachXDPCPUMap-35]
|
||||
_ = x[AttachSkLookup-36]
|
||||
_ = x[AttachXDP-37]
|
||||
}
|
||||
|
||||
const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIter"
|
||||
const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIterAttachCgroupInet4GetPeernameAttachCgroupInet6GetPeernameAttachCgroupInet4GetSocknameAttachCgroupInet6GetSocknameAttachXDPDevMapAttachCgroupInetSockReleaseAttachXDPCPUMapAttachSkLookupAttachXDP"
|
||||
|
||||
var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582}
|
||||
var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582, 610, 638, 666, 694, 709, 736, 751, 765, 774}
|
||||
|
||||
func (i AttachType) String() string {
|
||||
if i >= AttachType(len(_AttachType_index)-1) {
|
||||
@@ -135,3 +148,21 @@ func (i AttachType) String() string {
|
||||
}
|
||||
return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]]
|
||||
}
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[PinNone-0]
|
||||
_ = x[PinByName-1]
|
||||
}
|
||||
|
||||
const _PinType_name = "PinNonePinByName"
|
||||
|
||||
var _PinType_index = [...]uint8{0, 7, 16}
|
||||
|
||||
func (i PinType) String() string {
|
||||
if i < 0 || i >= PinType(len(_PinType_index)-1) {
|
||||
return "PinType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _PinType_name[_PinType_index[i]:_PinType_index[i+1]]
|
||||
}
|
||||
|
Reference in New Issue
Block a user