Browse Source

Merge pull request #42654 from AkihiroSuda/runc-v1.0.1

update runc binary and libcontainer to v1.0.1
Justin Cormack 4 years ago
parent
commit
bde67dfc38
64 changed files with 2320 additions and 1050 deletions
  1. 1 1
      hack/dockerfile/install/runc.installer
  2. 4 4
      vendor.conf
  3. 52 0
      vendor/github.com/cilium/ebpf/asm/func.go
  4. 52 1
      vendor/github.com/cilium/ebpf/asm/func_string.go
  5. 21 11
      vendor/github.com/cilium/ebpf/asm/instruction.go
  6. 1 1
      vendor/github.com/cilium/ebpf/asm/load_store.go
  7. 2 2
      vendor/github.com/cilium/ebpf/asm/opcode.go
  8. 43 16
      vendor/github.com/cilium/ebpf/collection.go
  9. 35 15
      vendor/github.com/cilium/ebpf/elf_reader.go
  10. 8 1
      vendor/github.com/cilium/ebpf/examples/README.md
  11. 1 2
      vendor/github.com/cilium/ebpf/examples/go.mod
  12. 1 1
      vendor/github.com/cilium/ebpf/examples/kprobe/bpf/kprobe_example.c
  13. 2 2
      vendor/github.com/cilium/ebpf/examples/uretprobe/bpf/uretprobe_example.c
  14. 20 12
      vendor/github.com/cilium/ebpf/internal/btf/btf.go
  15. 19 8
      vendor/github.com/cilium/ebpf/internal/btf/btf_types.go
  16. 44 0
      vendor/github.com/cilium/ebpf/internal/btf/btf_types_string.go
  17. 631 132
      vendor/github.com/cilium/ebpf/internal/btf/core.go
  18. 34 12
      vendor/github.com/cilium/ebpf/internal/btf/ext_info.go
  19. 42 20
      vendor/github.com/cilium/ebpf/internal/btf/types.go
  20. 16 0
      vendor/github.com/cilium/ebpf/internal/elf.go
  21. 5 0
      vendor/github.com/cilium/ebpf/internal/endian.go
  22. 4 0
      vendor/github.com/cilium/ebpf/internal/errors.go
  23. 0 4
      vendor/github.com/cilium/ebpf/internal/ptr.go
  24. 66 1
      vendor/github.com/cilium/ebpf/internal/syscall.go
  25. 3 0
      vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
  26. 3 0
      vendor/github.com/cilium/ebpf/internal/unix/types_other.go
  27. 38 5
      vendor/github.com/cilium/ebpf/link/iter.go
  28. 215 73
      vendor/github.com/cilium/ebpf/link/kprobe.go
  29. 49 29
      vendor/github.com/cilium/ebpf/link/perf_event.go
  30. 25 0
      vendor/github.com/cilium/ebpf/link/platform.go
  31. 2 2
      vendor/github.com/cilium/ebpf/link/program.go
  32. 17 0
      vendor/github.com/cilium/ebpf/link/syscalls.go
  33. 2 2
      vendor/github.com/cilium/ebpf/link/tracepoint.go
  34. 237 0
      vendor/github.com/cilium/ebpf/link/uprobe.go
  35. 9 2
      vendor/github.com/cilium/ebpf/linker.go
  36. 58 28
      vendor/github.com/cilium/ebpf/map.go
  37. 82 69
      vendor/github.com/cilium/ebpf/prog.go
  38. 85 96
      vendor/github.com/cilium/ebpf/syscalls.go
  39. 4 0
      vendor/github.com/cilium/ebpf/types.go
  40. 6 2
      vendor/github.com/cilium/ebpf/types_string.go
  41. 16 20
      vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go
  42. 142 161
      vendor/github.com/coreos/go-systemd/v22/dbus/methods.go
  43. 3 1
      vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go
  44. 6 6
      vendor/github.com/opencontainers/runc/go.mod
  45. 53 7
      vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go
  46. 0 51
      vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/fscommon.go
  47. 0 122
      vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/utils.go
  48. 2 4
      vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go
  49. 5 5
      vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go
  50. 2 2
      vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go
  51. 6 4
      vendor/github.com/opencontainers/runc/libcontainer/configs/config.go
  52. 0 17
      vendor/github.com/opencontainers/runc/libcontainer/configs/devices.go
  53. 1 1
      vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go
  54. 1 2
      vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go
  55. 8 5
      vendor/github.com/opencontainers/runc/libcontainer/configs/network.go
  56. 5 6
      vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go
  57. 3 3
      vendor/github.com/opencontainers/runc/libcontainer/nsenter/test/escape.go
  58. 74 42
      vendor/github.com/opencontainers/runc/libcontainer/user/user.go
  59. 1 1
      vendor/github.com/sirupsen/logrus/README.md
  60. 41 32
      vendor/github.com/sirupsen/logrus/entry.go
  61. 4 1
      vendor/github.com/sirupsen/logrus/json_formatter.go
  62. 1 1
      vendor/github.com/sirupsen/logrus/logger.go
  63. 1 1
      vendor/github.com/sirupsen/logrus/terminal_check_unix.go
  64. 6 1
      vendor/github.com/sirupsen/logrus/text_formatter.go

+ 1 - 1
hack/dockerfile/install/runc.installer

@@ -4,7 +4,7 @@
 # The version of runc should match the version that is used by the containerd
 # version that is used. If you need to update runc, open a pull request in
 # the containerd project first, and update both after that is merged.
-: ${RUNC_COMMIT:=84113eef6fc27af1b01b3181f31bbaf708715301} # v1.0.0
+: ${RUNC_COMMIT:=4144b63817ebcc5b358fc2c8ef95f7cddd709aa7} # v1.0.1
 
 install_runc() {
 	# If using RHEL7 kernels (3.10.0 el7), disable kmem accounting/limiting

+ 4 - 4
vendor.conf

@@ -16,7 +16,7 @@ github.com/moby/term                                3f7ff695adc6a35abc925370dd0a
 github.com/moby/sys                                 b0f1fd7235275d01bd35cc4421e884e522395f45 # mountinfo/v0.4.1
 
 github.com/creack/pty                               2a38352e8b4d7ab6c336eef107e42a55e72e7fbc # v1.1.11
-github.com/sirupsen/logrus                          6699a89a232f3db797f2e280639854bbc4b89725 # v1.7.0
+github.com/sirupsen/logrus                          bdc0db8ead3853c56b7cd1ac2ba4e11b47d7da6b # v1.8.1
 github.com/tchap/go-patricia                        a7f0089c6f496e8e70402f61733606daa326cac5 # v2.3.0
 golang.org/x/net                                    e18ecbb051101a46fc263334b127c89bc7bff7ea
 golang.org/x/sys                                    d19ff857e887eacb631721f188c7d365c2331456
@@ -88,7 +88,7 @@ google.golang.org/grpc                              f495f5b15ae7ccda3b38c53a1bfc
 # the containerd project first, and update both after that is merged.
 # This commit does not need to match RUNC_COMMIT as it is used for helper
 # packages but should be newer or equal.
-github.com/opencontainers/runc                      b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7 # v1.0.0-rc95
+github.com/opencontainers/runc                      4144b63817ebcc5b358fc2c8ef95f7cddd709aa7 # v1.0.1
 github.com/opencontainers/runtime-spec              1c3f411f041711bbeecf35ff7e93461ea6789220 # v1.0.3-0.20210326190908-1c3f411f0417
 github.com/opencontainers/image-spec                d60099175f88c47cd379c4738d158884749ed235 # v1.0.1
 github.com/cyphar/filepath-securejoin               a261ee33d7a517f054effbf451841abaafe3e0fd # v0.2.2
@@ -97,7 +97,7 @@ github.com/cyphar/filepath-securejoin               a261ee33d7a517f054effbf45184
 github.com/coreos/go-systemd                        39ca1b05acc7ad1220e09f133283b8859a8b71ab # v17
 
 # systemd integration (journald, daemon/listeners, containerd/cgroups)
-github.com/coreos/go-systemd/v22                    256724e3db397c5ca4287b8f0c78e9e8492fdb01 # v22.3.1
+github.com/coreos/go-systemd/v22                    777e73a89cef78631ccaa97f53a9bae67e166186 # v22.3.2
 github.com/godbus/dbus/v5                           c88335c0b1d28a30e7fc76d526a06154b85e5d97 # v5.0.4
 
 # gelf logging driver deps
@@ -137,7 +137,7 @@ github.com/containerd/go-runc                       16b287bc67d069a60fa48db15f33
 github.com/containerd/typeurl                       5e43fb8b75ed2f2305fc04e6918c8d10636771bc # v1.0.2
 github.com/containerd/ttrpc                         bfba540dc45464586c106b1f31c8547933c1eb41 # v1.0.2
 github.com/gogo/googleapis                          01e0f9cca9b92166042241267ee2a5cdf5cff46c # v1.3.2
-github.com/cilium/ebpf                              ef54c303d1fff1e80a9bf20f00a378fde5419d61 # v0.5.0
+github.com/cilium/ebpf                              ca492085341e0e917f48ec30704d5054c5d42ca8 # v0.6.2
 github.com/klauspost/compress                       a3b7545c88eea469c2246bee0e6c130525d56190 # v1.11.13
 github.com/pelletier/go-toml                        65ca8064882c8c308e5c804c5d5443d409e0738c # v1.8.1
 

+ 52 - 0
vendor/github.com/cilium/ebpf/asm/func.go

@@ -132,6 +132,58 @@ const (
 	FnSkStorageDelete
 	FnSendSignal
 	FnTcpGenSyncookie
+	FnSkbOutput
+	FnProbeReadUser
+	FnProbeReadKernel
+	FnProbeReadUserStr
+	FnProbeReadKernelStr
+	FnTcpSendAck
+	FnSendSignalThread
+	FnJiffies64
+	FnReadBranchRecords
+	FnGetNsCurrentPidTgid
+	FnXdpOutput
+	FnGetNetnsCookie
+	FnGetCurrentAncestorCgroupId
+	FnSkAssign
+	FnKtimeGetBootNs
+	FnSeqPrintf
+	FnSeqWrite
+	FnSkCgroupId
+	FnSkAncestorCgroupId
+	FnRingbufOutput
+	FnRingbufReserve
+	FnRingbufSubmit
+	FnRingbufDiscard
+	FnRingbufQuery
+	FnCsumLevel
+	FnSkcToTcp6Sock
+	FnSkcToTcpSock
+	FnSkcToTcpTimewaitSock
+	FnSkcToTcpRequestSock
+	FnSkcToUdp6Sock
+	FnGetTaskStack
+	FnLoadHdrOpt
+	FnStoreHdrOpt
+	FnReserveHdrOpt
+	FnInodeStorageGet
+	FnInodeStorageDelete
+	FnDPath
+	FnCopyFromUser
+	FnSnprintfBtf
+	FnSeqPrintfBtf
+	FnSkbCgroupClassid
+	FnRedirectNeigh
+	FnPerCpuPtr
+	FnThisCpuPtr
+	FnRedirectPeer
+	FnTaskStorageGet
+	FnTaskStorageDelete
+	FnGetCurrentTaskBtf
+	FnBprmOptsSet
+	FnKtimeGetCoarseNs
+	FnImaInodeHash
+	FnSockFromFile
 )
 
 // Call emits a function call.

File diff suppressed because it is too large
+ 52 - 1
vendor/github.com/cilium/ebpf/asm/func_string.go


+ 21 - 11
vendor/github.com/cilium/ebpf/asm/instruction.go

@@ -57,7 +57,7 @@ func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, err
 		return 0, fmt.Errorf("can't unmarshal registers: %s", err)
 	}
 
-	if !bi.OpCode.isDWordLoad() {
+	if !bi.OpCode.IsDWordLoad() {
 		return InstructionSize, nil
 	}
 
@@ -80,7 +80,7 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
 		return 0, errors.New("invalid opcode")
 	}
 
-	isDWordLoad := ins.OpCode.isDWordLoad()
+	isDWordLoad := ins.OpCode.IsDWordLoad()
 
 	cons := int32(ins.Constant)
 	if isDWordLoad {
@@ -123,7 +123,7 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
 //
 // Returns an error if the instruction doesn't load a map.
 func (ins *Instruction) RewriteMapPtr(fd int) error {
-	if !ins.OpCode.isDWordLoad() {
+	if !ins.OpCode.IsDWordLoad() {
 		return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
 	}
 
@@ -138,15 +138,19 @@ func (ins *Instruction) RewriteMapPtr(fd int) error {
 	return nil
 }
 
-func (ins *Instruction) mapPtr() uint32 {
-	return uint32(uint64(ins.Constant) & math.MaxUint32)
+// MapPtr returns the map fd for this instruction.
+//
+// The result is undefined if the instruction is not a load from a map,
+// see IsLoadFromMap.
+func (ins *Instruction) MapPtr() int {
+	return int(int32(uint64(ins.Constant) & math.MaxUint32))
 }
 
 // RewriteMapOffset changes the offset of a direct load from a map.
 //
 // Returns an error if the instruction is not a direct load.
 func (ins *Instruction) RewriteMapOffset(offset uint32) error {
-	if !ins.OpCode.isDWordLoad() {
+	if !ins.OpCode.IsDWordLoad() {
 		return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
 	}
 
@@ -163,10 +167,10 @@ func (ins *Instruction) mapOffset() uint32 {
 	return uint32(uint64(ins.Constant) >> 32)
 }
 
-// isLoadFromMap returns true if the instruction loads from a map.
+// IsLoadFromMap returns true if the instruction loads from a map.
 //
 // This covers both loading the map pointer and direct map value loads.
-func (ins *Instruction) isLoadFromMap() bool {
+func (ins *Instruction) IsLoadFromMap() bool {
 	return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
 }
 
@@ -177,6 +181,12 @@ func (ins *Instruction) IsFunctionCall() bool {
 	return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
 }
 
+// IsConstantLoad returns true if the instruction loads a constant of the
+// given size.
+func (ins *Instruction) IsConstantLoad(size Size) bool {
+	return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0
+}
+
 // Format implements fmt.Formatter.
 func (ins Instruction) Format(f fmt.State, c rune) {
 	if c != 'v' {
@@ -197,8 +207,8 @@ func (ins Instruction) Format(f fmt.State, c rune) {
 		return
 	}
 
-	if ins.isLoadFromMap() {
-		fd := int32(ins.mapPtr())
+	if ins.IsLoadFromMap() {
+		fd := ins.MapPtr()
 		switch ins.Src {
 		case PseudoMapFD:
 			fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd)
@@ -403,7 +413,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
 func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
 	h := sha1.New()
 	for i, ins := range insns {
-		if ins.isLoadFromMap() {
+		if ins.IsLoadFromMap() {
 			ins.Constant = 0
 		}
 		_, err := ins.Marshal(h, bo)

+ 1 - 1
vendor/github.com/cilium/ebpf/asm/load_store.go

@@ -111,7 +111,7 @@ func LoadMapPtr(dst Register, fd int) Instruction {
 		OpCode:   LoadImmOp(DWord),
 		Dst:      dst,
 		Src:      PseudoMapFD,
-		Constant: int64(fd),
+		Constant: int64(uint32(fd)),
 	}
 }
 

+ 2 - 2
vendor/github.com/cilium/ebpf/asm/opcode.go

@@ -69,13 +69,13 @@ const InvalidOpCode OpCode = 0xff
 // rawInstructions returns the number of BPF instructions required
 // to encode this opcode.
 func (op OpCode) rawInstructions() int {
-	if op.isDWordLoad() {
+	if op.IsDWordLoad() {
 		return 2
 	}
 	return 1
 }
 
-func (op OpCode) isDWordLoad() bool {
+func (op OpCode) IsDWordLoad() bool {
 	return op == LoadImmOp(DWord)
 }
 

+ 43 - 16
vendor/github.com/cilium/ebpf/collection.go

@@ -3,6 +3,7 @@ package ebpf
 import (
 	"errors"
 	"fmt"
+	"io"
 	"math"
 	"reflect"
 	"strings"
@@ -89,8 +90,8 @@ func (cs *CollectionSpec) RewriteMaps(maps map[string]*Map) error {
 //
 // The constant must be defined like so in the C program:
 //
-//    static volatile const type foobar;
-//    static volatile const type foobar = default;
+//    volatile const type foobar;
+//    volatile const type foobar = default;
 //
 // Replacement values must be of the same length as the C sizeof(type).
 // If necessary, they are marshalled according to the same rules as
@@ -269,11 +270,21 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co
 	}, nil
 }
 
-type btfHandleCache map[*btf.Spec]*btf.Handle
+type handleCache struct {
+	btfHandles map[*btf.Spec]*btf.Handle
+	btfSpecs   map[io.ReaderAt]*btf.Spec
+}
+
+func newHandleCache() *handleCache {
+	return &handleCache{
+		btfHandles: make(map[*btf.Spec]*btf.Handle),
+		btfSpecs:   make(map[io.ReaderAt]*btf.Spec),
+	}
+}
 
-func (btfs btfHandleCache) load(spec *btf.Spec) (*btf.Handle, error) {
-	if btfs[spec] != nil {
-		return btfs[spec], nil
+func (hc handleCache) btfHandle(spec *btf.Spec) (*btf.Handle, error) {
+	if hc.btfHandles[spec] != nil {
+		return hc.btfHandles[spec], nil
 	}
 
 	handle, err := btf.NewHandle(spec)
@@ -281,14 +292,30 @@ func (btfs btfHandleCache) load(spec *btf.Spec) (*btf.Handle, error) {
 		return nil, err
 	}
 
-	btfs[spec] = handle
+	hc.btfHandles[spec] = handle
 	return handle, nil
 }
 
-func (btfs btfHandleCache) close() {
-	for _, handle := range btfs {
+func (hc handleCache) btfSpec(rd io.ReaderAt) (*btf.Spec, error) {
+	if hc.btfSpecs[rd] != nil {
+		return hc.btfSpecs[rd], nil
+	}
+
+	spec, err := btf.LoadSpecFromReader(rd)
+	if err != nil {
+		return nil, err
+	}
+
+	hc.btfSpecs[rd] = spec
+	return spec, nil
+}
+
+func (hc handleCache) close() {
+	for _, handle := range hc.btfHandles {
 		handle.Close()
 	}
+	hc.btfHandles = nil
+	hc.btfSpecs = nil
 }
 
 func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) (
@@ -300,12 +327,12 @@ func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) (
 	var (
 		maps             = make(map[string]*Map)
 		progs            = make(map[string]*Program)
-		btfs             = make(btfHandleCache)
+		handles          = newHandleCache()
 		skipMapsAndProgs = false
 	)
 
 	cleanup = func() {
-		btfs.close()
+		handles.close()
 
 		if skipMapsAndProgs {
 			return
@@ -335,7 +362,7 @@ func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) (
 			return nil, fmt.Errorf("missing map %s", mapName)
 		}
 
-		m, err := newMapWithOptions(mapSpec, opts.Maps, btfs)
+		m, err := newMapWithOptions(mapSpec, opts.Maps, handles)
 		if err != nil {
 			return nil, fmt.Errorf("map %s: %w", mapName, err)
 		}
@@ -360,7 +387,7 @@ func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) (
 		for i := range progSpec.Instructions {
 			ins := &progSpec.Instructions[i]
 
-			if ins.OpCode != asm.LoadImmOp(asm.DWord) || ins.Reference == "" {
+			if !ins.IsLoadFromMap() || ins.Reference == "" {
 				continue
 			}
 
@@ -372,7 +399,7 @@ func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) (
 
 			m, err := loadMap(ins.Reference)
 			if err != nil {
-				return nil, fmt.Errorf("program %s: %s", progName, err)
+				return nil, fmt.Errorf("program %s: %w", progName, err)
 			}
 
 			fd := m.FD()
@@ -384,7 +411,7 @@ func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) (
 			}
 		}
 
-		prog, err := newProgramWithOptions(progSpec, opts.Programs, btfs)
+		prog, err := newProgramWithOptions(progSpec, opts.Programs, handles)
 		if err != nil {
 			return nil, fmt.Errorf("program %s: %w", progName, err)
 		}
@@ -534,7 +561,7 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va
 			}
 
 			if err != nil {
-				return fmt.Errorf("field %s: %s", field.Name, err)
+				return fmt.Errorf("field %s: %w", field.Name, err)
 			}
 		}
 

+ 35 - 15
vendor/github.com/cilium/ebpf/elf_reader.go

@@ -96,7 +96,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
 	}
 
 	btfSpec, err := btf.LoadSpecFromReader(rd)
-	if err != nil {
+	if err != nil && !errors.Is(err, btf.ErrNotFound) {
 		return nil, fmt.Errorf("load BTF: %w", err)
 	}
 
@@ -159,7 +159,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
 			}
 
 			if target.Flags&elf.SHF_STRINGS > 0 {
-				return nil, fmt.Errorf("section %q: string %q is not stack allocated: %w", section.Name, rel.Name, ErrNotSupported)
+				return nil, fmt.Errorf("section %q: string is not stack allocated: %w", section.Name, ErrNotSupported)
 			}
 
 			target.references++
@@ -374,17 +374,25 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err
 		}
 
 	case dataSection:
+		var offset uint32
 		switch typ {
 		case elf.STT_SECTION:
 			if bind != elf.STB_LOCAL {
 				return fmt.Errorf("direct load: %s: unsupported relocation %s", name, bind)
 			}
 
+			// This is really a reference to a static symbol, which clang doesn't
+			// emit a symbol table entry for. Instead it encodes the offset in
+			// the instruction itself.
+			offset = uint32(uint64(ins.Constant))
+
 		case elf.STT_OBJECT:
 			if bind != elf.STB_GLOBAL {
 				return fmt.Errorf("direct load: %s: unsupported relocation %s", name, bind)
 			}
 
+			offset = uint32(rel.Value)
+
 		default:
 			return fmt.Errorf("incorrect relocation type %v for direct map load", typ)
 		}
@@ -394,10 +402,8 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err
 		// it's not clear how to encode that into Instruction.
 		name = target.Name
 
-		// For some reason, clang encodes the offset of the symbol its
-		// section in the first basic BPF instruction, while the kernel
-		// expects it in the second one.
-		ins.Constant <<= 32
+		// The kernel expects the offset in the second basic BPF instruction.
+		ins.Constant = int64(uint64(offset) << 32)
 		ins.Src = asm.PseudoMapValue
 
 		// Mark the instruction as needing an update when creating the
@@ -491,33 +497,38 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error {
 				return fmt.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
 			}
 
-			if maps[mapSym.Name] != nil {
+			mapName := mapSym.Name
+			if maps[mapName] != nil {
 				return fmt.Errorf("section %v: map %v already exists", sec.Name, mapSym)
 			}
 
 			lr := io.LimitReader(r, int64(size))
 
 			spec := MapSpec{
-				Name: SanitizeName(mapSym.Name, -1),
+				Name: SanitizeName(mapName, -1),
 			}
 			switch {
 			case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
-				return fmt.Errorf("map %v: missing type", mapSym)
+				return fmt.Errorf("map %s: missing type", mapName)
 			case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
-				return fmt.Errorf("map %v: missing key size", mapSym)
+				return fmt.Errorf("map %s: missing key size", mapName)
 			case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
-				return fmt.Errorf("map %v: missing value size", mapSym)
+				return fmt.Errorf("map %s: missing value size", mapName)
 			case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
-				return fmt.Errorf("map %v: missing max entries", mapSym)
+				return fmt.Errorf("map %s: missing max entries", mapName)
 			case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
-				return fmt.Errorf("map %v: missing flags", mapSym)
+				return fmt.Errorf("map %s: missing flags", mapName)
 			}
 
 			if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil {
-				return fmt.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
+				return fmt.Errorf("map %s: unknown and non-zero fields in definition", mapName)
+			}
+
+			if err := spec.clampPerfEventArraySize(); err != nil {
+				return fmt.Errorf("map %s: %w", mapName, err)
 			}
 
-			maps[mapSym.Name] = &spec
+			maps[mapName] = &spec
 		}
 	}
 
@@ -565,6 +576,10 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error {
 				return fmt.Errorf("map %v: %w", name, err)
 			}
 
+			if err := mapSpec.clampPerfEventArraySize(); err != nil {
+				return fmt.Errorf("map %v: %w", name, err)
+			}
+
 			maps[name] = mapSpec
 		}
 	}
@@ -847,6 +862,8 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
 		"uretprobe/":            {Kprobe, AttachNone, 0},
 		"tracepoint/":           {TracePoint, AttachNone, 0},
 		"raw_tracepoint/":       {RawTracepoint, AttachNone, 0},
+		"raw_tp/":               {RawTracepoint, AttachNone, 0},
+		"tp_btf/":               {Tracing, AttachTraceRawTp, 0},
 		"xdp":                   {XDP, AttachNone, 0},
 		"perf_event":            {PerfEvent, AttachNone, 0},
 		"lwt_in":                {LWTIn, AttachNone, 0},
@@ -860,6 +877,9 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
 		"lirc_mode2":            {LircMode2, AttachLircMode2, 0},
 		"flow_dissector":        {FlowDissector, AttachFlowDissector, 0},
 		"iter/":                 {Tracing, AttachTraceIter, 0},
+		"fentry/":               {Tracing, AttachTraceFEntry, 0},
+		"fmod_ret/":             {Tracing, AttachModifyReturn, 0},
+		"fexit/":                {Tracing, AttachTraceFExit, 0},
 		"fentry.s/":             {Tracing, AttachTraceFEntry, unix.BPF_F_SLEEPABLE},
 		"fmod_ret.s/":           {Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE},
 		"fexit.s/":              {Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE},

+ 8 - 1
vendor/github.com/cilium/ebpf/examples/README.md

@@ -1,6 +1,13 @@
 # eBPF Examples
 
 - [kprobe](kprobe/) - Attach a program to the entry or exit of an arbitrary kernel symbol (function).
-- [uprobe](uprobe/) - Like a kprobe, but for symbols in userspace binaries (e.g. `bash`).
+- [uretprobe](uretprobe/) - Like a kprobe, but for symbols in userspace binaries (e.g. `bash`).
 - [tracepoint](tracepoint/) - Attach a program to predetermined kernel tracepoints.
 - Add your use case(s) here!
+
+## How to run
+
+```bash
+cd ebpf/examples/
+go run -exec sudo [./kprobe, ./uretprobe, ./tracepoint, ...]
+```

+ 1 - 2
vendor/github.com/cilium/ebpf/examples/go.mod

@@ -3,7 +3,6 @@ module github.com/cilium/ebpf/examples
 go 1.15
 
 require (
-	github.com/cilium/ebpf v0.4.1-0.20210401155455-cb5b8b6084b4 // indirect
-	github.com/elastic/go-perf v0.0.0-20191212140718-9c656876f595
+	github.com/cilium/ebpf v0.6.1-0.20210610105443-1e7f01c7124c
 	golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
 )

+ 1 - 1
vendor/github.com/cilium/ebpf/examples/kprobe/bpf/kprobe_example.c

@@ -10,7 +10,7 @@ struct bpf_map_def SEC("maps") kprobe_map = {
     .max_entries = 1,
 };
 
-SEC("kprobe/__x64_sys_execve")
+SEC("kprobe/sys_execve")
 int kprobe_execve() {
     u32 key = 0;
     u64 initval = 1, *valp;

+ 2 - 2
vendor/github.com/cilium/ebpf/examples/uprobe/bpf/uprobe_example.c → vendor/github.com/cilium/ebpf/examples/uretprobe/bpf/uretprobe_example.c

@@ -12,8 +12,8 @@ struct {
 	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
 } events SEC(".maps");
 
-SEC("uprobe/bash_readline")
-int uprobe_bash_readline(struct pt_regs *ctx) {
+SEC("uretprobe/bash_readline")
+int uretprobe_bash_readline(struct pt_regs *ctx) {
 	struct event_t event;
 
 	event.pid = bpf_get_current_pid_tgid();

+ 20 - 12
vendor/github.com/cilium/ebpf/internal/btf/btf.go

@@ -35,7 +35,7 @@ type Spec struct {
 	namedTypes map[string][]namedType
 	funcInfos  map[string]extInfo
 	lineInfos  map[string]extInfo
-	coreRelos  map[string]bpfCoreRelos
+	coreRelos  map[string]coreRelos
 	byteOrder  binary.ByteOrder
 }
 
@@ -53,7 +53,7 @@ type btfHeader struct {
 
 // LoadSpecFromReader reads BTF sections from an ELF.
 //
-// Returns a nil Spec and no error if no BTF was present.
+// Returns ErrNotFound if the reader contains no BTF.
 func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
 	file, err := internal.NewSafeELFFile(rd)
 	if err != nil {
@@ -67,7 +67,7 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
 	}
 
 	if btfSection == nil {
-		return nil, nil
+		return nil, fmt.Errorf("btf: %w", ErrNotFound)
 	}
 
 	symbols, err := file.Symbols()
@@ -377,7 +377,7 @@ func (s *Spec) marshal(opts marshalOpts) ([]byte, error) {
 	for _, raw := range s.rawTypes {
 		switch {
 		case opts.StripFuncLinkage && raw.Kind() == kindFunc:
-			raw.SetLinkage(linkageStatic)
+			raw.SetLinkage(StaticFunc)
 		}
 
 		if err := raw.Marshal(&buf, opts.ByteOrder); err != nil {
@@ -438,13 +438,13 @@ func (s *Spec) Program(name string, length uint64) (*Program, error) {
 
 	funcInfos, funcOK := s.funcInfos[name]
 	lineInfos, lineOK := s.lineInfos[name]
-	coreRelos, coreOK := s.coreRelos[name]
+	relos, coreOK := s.coreRelos[name]
 
 	if !funcOK && !lineOK && !coreOK {
 		return nil, fmt.Errorf("no extended BTF info for section %s", name)
 	}
 
-	return &Program{s, length, funcInfos, lineInfos, coreRelos}, nil
+	return &Program{s, length, funcInfos, lineInfos, relos}, nil
 }
 
 // Datasec returns the BTF required to create maps which represent data sections.
@@ -491,7 +491,8 @@ func (s *Spec) FindType(name string, typ Type) error {
 		return fmt.Errorf("type %s: %w", name, ErrNotFound)
 	}
 
-	value := reflect.Indirect(reflect.ValueOf(copyType(candidate)))
+	cpy, _ := copyType(candidate, nil)
+	value := reflect.Indirect(reflect.ValueOf(cpy))
 	reflect.Indirect(reflect.ValueOf(typ)).Set(value)
 	return nil
 }
@@ -606,7 +607,7 @@ type Program struct {
 	spec                 *Spec
 	length               uint64
 	funcInfos, lineInfos extInfo
-	coreRelos            bpfCoreRelos
+	coreRelos            coreRelos
 }
 
 // ProgramSpec returns the Spec needed for loading function and line infos into the kernel.
@@ -665,16 +666,23 @@ func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
 	return s.lineInfos.recordSize, bytes, nil
 }
 
-// ProgramRelocations returns the CO-RE relocations required to adjust the
-// program to the target.
+// ProgramFixups returns the changes required to adjust the program to the target.
 //
 // This is a free function instead of a method to hide it from users
 // of package ebpf.
-func ProgramRelocations(s *Program, target *Spec) (map[uint64]Relocation, error) {
+func ProgramFixups(s *Program, target *Spec) (COREFixups, error) {
 	if len(s.coreRelos) == 0 {
 		return nil, nil
 	}
 
+	if target == nil {
+		var err error
+		target, err = LoadKernelSpec()
+		if err != nil {
+			return nil, err
+		}
+	}
+
 	return coreRelocate(s.spec, target, s.coreRelos)
 }
 
@@ -771,7 +779,7 @@ var haveFuncLinkage = internal.FeatureTest("BTF func linkage", "5.6", func() err
 	types.Func.SetKind(kindFunc)
 	types.Func.SizeType = 1 // aka FuncProto
 	types.Func.NameOff = 1
-	types.Func.SetLinkage(linkageGlobal)
+	types.Func.SetLinkage(GlobalFunc)
 
 	btf := marshalBTF(&types, strings, internal.NativeEndian)
 

+ 19 - 8
vendor/github.com/cilium/ebpf/internal/btf/btf_types.go

@@ -6,6 +6,8 @@ import (
 	"io"
 )
 
+//go:generate stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage
+
 // btfKind describes a Type.
 type btfKind uint8
 
@@ -31,14 +33,23 @@ const (
 	kindDatasec
 )
 
-// btfFuncLinkage describes BTF function linkage metadata.
-type btfFuncLinkage uint8
+// FuncLinkage describes BTF function linkage metadata.
+type FuncLinkage int
 
 // Equivalent of enum btf_func_linkage.
 const (
-	linkageStatic btfFuncLinkage = iota
-	linkageGlobal
-	// linkageExtern // Currently unused in libbpf.
+	StaticFunc FuncLinkage = iota // static
+	GlobalFunc                    // global
+	ExternFunc                    // extern
+)
+
+// VarLinkage describes BTF variable linkage metadata.
+type VarLinkage int
+
+const (
+	StaticVar VarLinkage = iota // static
+	GlobalVar                   // global
+	ExternVar                   // extern
 )
 
 const (
@@ -144,11 +155,11 @@ func (bt *btfType) KindFlag() bool {
 	return bt.info(btfTypeKindFlagMask, btfTypeKindFlagShift) == 1
 }
 
-func (bt *btfType) Linkage() btfFuncLinkage {
-	return btfFuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift))
+func (bt *btfType) Linkage() FuncLinkage {
+	return FuncLinkage(bt.info(btfTypeVlenMask, btfTypeVlenShift))
 }
 
-func (bt *btfType) SetLinkage(linkage btfFuncLinkage) {
+func (bt *btfType) SetLinkage(linkage FuncLinkage) {
 	bt.setInfo(uint32(linkage), btfTypeVlenMask, btfTypeVlenShift)
 }
 

+ 44 - 0
vendor/github.com/cilium/ebpf/internal/btf/btf_types_string.go

@@ -0,0 +1,44 @@
+// Code generated by "stringer -linecomment -output=btf_types_string.go -type=FuncLinkage,VarLinkage"; DO NOT EDIT.
+
+package btf
+
+import "strconv"
+
+func _() {
+	// An "invalid array index" compiler error signifies that the constant values have changed.
+	// Re-run the stringer command to generate them again.
+	var x [1]struct{}
+	_ = x[StaticFunc-0]
+	_ = x[GlobalFunc-1]
+	_ = x[ExternFunc-2]
+}
+
+const _FuncLinkage_name = "staticglobalextern"
+
+var _FuncLinkage_index = [...]uint8{0, 6, 12, 18}
+
+func (i FuncLinkage) String() string {
+	if i < 0 || i >= FuncLinkage(len(_FuncLinkage_index)-1) {
+		return "FuncLinkage(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	return _FuncLinkage_name[_FuncLinkage_index[i]:_FuncLinkage_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[StaticVar-0]
+	_ = x[GlobalVar-1]
+	_ = x[ExternVar-2]
+}
+
+const _VarLinkage_name = "staticglobalextern"
+
+var _VarLinkage_index = [...]uint8{0, 6, 12, 18}
+
+func (i VarLinkage) String() string {
+	if i < 0 || i >= VarLinkage(len(_VarLinkage_index)-1) {
+		return "VarLinkage(" + strconv.FormatInt(int64(i), 10) + ")"
+	}
+	return _VarLinkage_name[_VarLinkage_index[i]:_VarLinkage_index[i+1]]
+}

+ 631 - 132
vendor/github.com/cilium/ebpf/internal/btf/core.go

@@ -3,43 +3,160 @@ package btf
 import (
 	"errors"
 	"fmt"
+	"math"
 	"reflect"
+	"sort"
 	"strconv"
 	"strings"
+
+	"github.com/cilium/ebpf/asm"
 )
 
 // Code in this file is derived from libbpf, which is available under a BSD
 // 2-Clause license.
 
-// Relocation describes a CO-RE relocation.
-type Relocation struct {
-	Current uint32
-	New     uint32
+// COREFixup is the result of computing a CO-RE relocation for a target.
+type COREFixup struct {
+	Kind   COREKind
+	Local  uint32
+	Target uint32
+	Poison bool
+}
+
+func (f COREFixup) equal(other COREFixup) bool {
+	return f.Local == other.Local && f.Target == other.Target
+}
+
+func (f COREFixup) String() string {
+	if f.Poison {
+		return fmt.Sprintf("%s=poison", f.Kind)
+	}
+	return fmt.Sprintf("%s=%d->%d", f.Kind, f.Local, f.Target)
+}
+
+func (f COREFixup) apply(ins *asm.Instruction) error {
+	if f.Poison {
+		return errors.New("can't poison individual instruction")
+	}
+
+	switch class := ins.OpCode.Class(); class {
+	case asm.LdXClass, asm.StClass, asm.StXClass:
+		if want := int16(f.Local); want != ins.Offset {
+			return fmt.Errorf("invalid offset %d, expected %d", ins.Offset, want)
+		}
+
+		if f.Target > math.MaxInt16 {
+			return fmt.Errorf("offset %d exceeds MaxInt16", f.Target)
+		}
+
+		ins.Offset = int16(f.Target)
+
+	case asm.LdClass:
+		if !ins.IsConstantLoad(asm.DWord) {
+			return fmt.Errorf("not a dword-sized immediate load")
+		}
+
+		if want := int64(f.Local); want != ins.Constant {
+			return fmt.Errorf("invalid immediate %d, expected %d", ins.Constant, want)
+		}
+
+		ins.Constant = int64(f.Target)
+
+	case asm.ALUClass:
+		if ins.OpCode.ALUOp() == asm.Swap {
+			return fmt.Errorf("relocation against swap")
+		}
+
+		fallthrough
+
+	case asm.ALU64Class:
+		if src := ins.OpCode.Source(); src != asm.ImmSource {
+			return fmt.Errorf("invalid source %s", src)
+		}
+
+		if want := int64(f.Local); want != ins.Constant {
+			return fmt.Errorf("invalid immediate %d, expected %d", ins.Constant, want)
+		}
+
+		if f.Target > math.MaxInt32 {
+			return fmt.Errorf("immediate %d exceeds MaxInt32", f.Target)
+		}
+
+		ins.Constant = int64(f.Target)
+
+	default:
+		return fmt.Errorf("invalid class %s", class)
+	}
+
+	return nil
 }
 
-func (r Relocation) equal(other Relocation) bool {
-	return r.Current == other.Current && r.New == other.New
+func (f COREFixup) isNonExistant() bool {
+	return f.Kind.checksForExistence() && f.Target == 0
 }
 
-// coreReloKind is the type of CO-RE relocation
-type coreReloKind uint32
+type COREFixups map[uint64]COREFixup
+
+// Apply a set of CO-RE relocations to a BPF program.
+func (fs COREFixups) Apply(insns asm.Instructions) (asm.Instructions, error) {
+	if len(fs) == 0 {
+		cpy := make(asm.Instructions, len(insns))
+		copy(cpy, insns)
+		return insns, nil
+	}
+
+	cpy := make(asm.Instructions, 0, len(insns))
+	iter := insns.Iterate()
+	for iter.Next() {
+		fixup, ok := fs[iter.Offset.Bytes()]
+		if !ok {
+			cpy = append(cpy, *iter.Ins)
+			continue
+		}
+
+		ins := *iter.Ins
+		if fixup.Poison {
+			const badRelo = asm.BuiltinFunc(0xbad2310)
+
+			cpy = append(cpy, badRelo.Call())
+			if ins.OpCode.IsDWordLoad() {
+				// 64 bit constant loads occupy two raw bpf instructions, so
+				// we need to add another instruction as padding.
+				cpy = append(cpy, badRelo.Call())
+			}
+
+			continue
+		}
+
+		if err := fixup.apply(&ins); err != nil {
+			return nil, fmt.Errorf("instruction %d, offset %d: %s: %w", iter.Index, iter.Offset.Bytes(), fixup.Kind, err)
+		}
+
+		cpy = append(cpy, ins)
+	}
+
+	return cpy, nil
+}
+
+// COREKind is the type of CO-RE relocation
+type COREKind uint32
 
 const (
-	reloFieldByteOffset coreReloKind = iota /* field byte offset */
-	reloFieldByteSize                       /* field size in bytes */
-	reloFieldExists                         /* field existence in target kernel */
-	reloFieldSigned                         /* field signedness (0 - unsigned, 1 - signed) */
-	reloFieldLShiftU64                      /* bitfield-specific left bitshift */
-	reloFieldRShiftU64                      /* bitfield-specific right bitshift */
-	reloTypeIDLocal                         /* type ID in local BPF object */
-	reloTypeIDTarget                        /* type ID in target kernel */
-	reloTypeExists                          /* type existence in target kernel */
-	reloTypeSize                            /* type size in bytes */
-	reloEnumvalExists                       /* enum value existence in target kernel */
-	reloEnumvalValue                        /* enum value integer value */
+	reloFieldByteOffset COREKind = iota /* field byte offset */
+	reloFieldByteSize                   /* field size in bytes */
+	reloFieldExists                     /* field existence in target kernel */
+	reloFieldSigned                     /* field signedness (0 - unsigned, 1 - signed) */
+	reloFieldLShiftU64                  /* bitfield-specific left bitshift */
+	reloFieldRShiftU64                  /* bitfield-specific right bitshift */
+	reloTypeIDLocal                     /* type ID in local BPF object */
+	reloTypeIDTarget                    /* type ID in target kernel */
+	reloTypeExists                      /* type existence in target kernel */
+	reloTypeSize                        /* type size in bytes */
+	reloEnumvalExists                   /* enum value existence in target kernel */
+	reloEnumvalValue                    /* enum value integer value */
 )
 
-func (k coreReloKind) String() string {
+func (k COREKind) String() string {
 	switch k {
 	case reloFieldByteOffset:
 		return "byte_off"
@@ -70,103 +187,249 @@ func (k coreReloKind) String() string {
 	}
 }
 
-func coreRelocate(local, target *Spec, coreRelos bpfCoreRelos) (map[uint64]Relocation, error) {
-	if target == nil {
-		var err error
-		target, err = loadKernelSpec()
-		if err != nil {
-			return nil, err
-		}
-	}
+func (k COREKind) checksForExistence() bool {
+	return k == reloEnumvalExists || k == reloTypeExists || k == reloFieldExists
+}
 
+func coreRelocate(local, target *Spec, relos coreRelos) (COREFixups, error) {
 	if local.byteOrder != target.byteOrder {
 		return nil, fmt.Errorf("can't relocate %s against %s", local.byteOrder, target.byteOrder)
 	}
 
-	relocations := make(map[uint64]Relocation, len(coreRelos))
-	for _, relo := range coreRelos {
-		accessorStr, err := local.strings.Lookup(relo.AccessStrOff)
-		if err != nil {
-			return nil, err
+	var ids []TypeID
+	relosByID := make(map[TypeID]coreRelos)
+	result := make(COREFixups, len(relos))
+	for _, relo := range relos {
+		if relo.kind == reloTypeIDLocal {
+			// Filtering out reloTypeIDLocal here makes our lives a lot easier
+			// down the line, since it doesn't have a target at all.
+			if len(relo.accessor) > 1 || relo.accessor[0] != 0 {
+				return nil, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor)
+			}
+
+			result[uint64(relo.insnOff)] = COREFixup{
+				relo.kind,
+				uint32(relo.typeID),
+				uint32(relo.typeID),
+				false,
+			}
+			continue
 		}
 
-		accessor, err := parseCoreAccessor(accessorStr)
+		relos, ok := relosByID[relo.typeID]
+		if !ok {
+			ids = append(ids, relo.typeID)
+		}
+		relosByID[relo.typeID] = append(relos, relo)
+	}
+
+	// Ensure we work on relocations in a deterministic order.
+	sort.Slice(ids, func(i, j int) bool {
+		return ids[i] < ids[j]
+	})
+
+	for _, id := range ids {
+		if int(id) >= len(local.types) {
+			return nil, fmt.Errorf("invalid type id %d", id)
+		}
+
+		localType := local.types[id]
+		named, ok := localType.(namedType)
+		if !ok || named.name() == "" {
+			return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported)
+		}
+
+		relos := relosByID[id]
+		targets := target.namedTypes[named.essentialName()]
+		fixups, err := coreCalculateFixups(localType, targets, relos)
 		if err != nil {
-			return nil, fmt.Errorf("accessor %q: %s", accessorStr, err)
+			return nil, fmt.Errorf("relocate %s: %w", localType, err)
 		}
 
-		if int(relo.TypeID) >= len(local.types) {
-			return nil, fmt.Errorf("invalid type id %d", relo.TypeID)
+		for i, relo := range relos {
+			result[uint64(relo.insnOff)] = fixups[i]
 		}
+	}
 
-		typ := local.types[relo.TypeID]
+	return result, nil
+}
+
+var errAmbiguousRelocation = errors.New("ambiguous relocation")
+var errImpossibleRelocation = errors.New("impossible relocation")
+
+// coreCalculateFixups calculates the fixups for the given relocations using
+// the "best" target.
+//
+// The best target is determined by scoring: the less poisoning we have to do
+// the better the target is.
+func coreCalculateFixups(local Type, targets []namedType, relos coreRelos) ([]COREFixup, error) {
+	localID := local.ID()
+	local, err := copyType(local, skipQualifierAndTypedef)
+	if err != nil {
+		return nil, err
+	}
+
+	bestScore := len(relos)
+	var bestFixups []COREFixup
+	for i := range targets {
+		targetID := targets[i].ID()
+		target, err := copyType(targets[i], skipQualifierAndTypedef)
+		if err != nil {
+			return nil, err
+		}
 
-		if relo.ReloKind == reloTypeIDLocal {
-			relocations[uint64(relo.InsnOff)] = Relocation{
-				uint32(typ.ID()),
-				uint32(typ.ID()),
+		score := 0 // lower is better
+		fixups := make([]COREFixup, 0, len(relos))
+		for _, relo := range relos {
+			fixup, err := coreCalculateFixup(local, localID, target, targetID, relo)
+			if err != nil {
+				return nil, fmt.Errorf("target %s: %w", target, err)
+			}
+			if fixup.Poison || fixup.isNonExistant() {
+				score++
 			}
+			fixups = append(fixups, fixup)
+		}
+
+		if score > bestScore {
+			// We have a better target already, ignore this one.
 			continue
 		}
 
-		named, ok := typ.(namedType)
-		if !ok || named.name() == "" {
-			return nil, fmt.Errorf("relocate anonymous type %s: %w", typ.String(), ErrNotSupported)
+		if score < bestScore {
+			// This is the best target yet, use it.
+			bestScore = score
+			bestFixups = fixups
+			continue
 		}
 
-		name := essentialName(named.name())
-		res, err := coreCalculateRelocation(typ, target.namedTypes[name], relo.ReloKind, accessor)
-		if err != nil {
-			return nil, fmt.Errorf("relocate %s: %w", name, err)
+		// Some other target has the same score as the current one. Make sure
+		// the fixups agree with each other.
+		for i, fixup := range bestFixups {
+			if !fixup.equal(fixups[i]) {
+				return nil, fmt.Errorf("%s: multiple types match: %w", fixup.Kind, errAmbiguousRelocation)
+			}
 		}
+	}
 
-		relocations[uint64(relo.InsnOff)] = res
+	if bestFixups == nil {
+		// Nothing at all matched, probably because there are no suitable
+		// targets at all. Poison everything!
+		bestFixups = make([]COREFixup, len(relos))
+		for i, relo := range relos {
+			bestFixups[i] = COREFixup{Kind: relo.kind, Poison: true}
+		}
 	}
 
-	return relocations, nil
+	return bestFixups, nil
 }
 
-var errAmbiguousRelocation = errors.New("ambiguous relocation")
+// coreCalculateFixup calculates the fixup for a single local type, target type
+// and relocation.
+func coreCalculateFixup(local Type, localID TypeID, target Type, targetID TypeID, relo coreRelo) (COREFixup, error) {
+	fixup := func(local, target uint32) (COREFixup, error) {
+		return COREFixup{relo.kind, local, target, false}, nil
+	}
+	poison := func() (COREFixup, error) {
+		if relo.kind.checksForExistence() {
+			return fixup(1, 0)
+		}
+		return COREFixup{relo.kind, 0, 0, true}, nil
+	}
+	zero := COREFixup{}
+
+	switch relo.kind {
+	case reloTypeIDTarget, reloTypeSize, reloTypeExists:
+		if len(relo.accessor) > 1 || relo.accessor[0] != 0 {
+			return zero, fmt.Errorf("%s: unexpected accessor %v", relo.kind, relo.accessor)
+		}
+
+		err := coreAreTypesCompatible(local, target)
+		if errors.Is(err, errImpossibleRelocation) {
+			return poison()
+		}
+		if err != nil {
+			return zero, fmt.Errorf("relocation %s: %w", relo.kind, err)
+		}
+
+		switch relo.kind {
+		case reloTypeExists:
+			return fixup(1, 1)
 
-func coreCalculateRelocation(local Type, targets []namedType, kind coreReloKind, localAccessor coreAccessor) (Relocation, error) {
-	var relos []Relocation
-	var matches []Type
-	for _, target := range targets {
-		switch kind {
 		case reloTypeIDTarget:
-			if localAccessor[0] != 0 {
-				return Relocation{}, fmt.Errorf("%s: unexpected non-zero accessor", kind)
+			return fixup(uint32(localID), uint32(targetID))
+
+		case reloTypeSize:
+			localSize, err := Sizeof(local)
+			if err != nil {
+				return zero, err
 			}
 
-			if compat, err := coreAreTypesCompatible(local, target); err != nil {
-				return Relocation{}, fmt.Errorf("%s: %s", kind, err)
-			} else if !compat {
-				continue
+			targetSize, err := Sizeof(target)
+			if err != nil {
+				return zero, err
 			}
 
-			relos = append(relos, Relocation{uint32(target.ID()), uint32(target.ID())})
+			return fixup(uint32(localSize), uint32(targetSize))
+		}
 
-		default:
-			return Relocation{}, fmt.Errorf("relocation %s: %w", kind, ErrNotSupported)
+	case reloEnumvalValue, reloEnumvalExists:
+		localValue, targetValue, err := coreFindEnumValue(local, relo.accessor, target)
+		if errors.Is(err, errImpossibleRelocation) {
+			return poison()
+		}
+		if err != nil {
+			return zero, fmt.Errorf("relocation %s: %w", relo.kind, err)
 		}
-		matches = append(matches, target)
-	}
 
-	if len(relos) == 0 {
-		// TODO: Add switch for existence checks like reloEnumvalExists here.
+		switch relo.kind {
+		case reloEnumvalExists:
+			return fixup(1, 1)
 
-		// TODO: This might have to be poisoned.
-		return Relocation{}, fmt.Errorf("no relocation found, tried %v", targets)
-	}
+		case reloEnumvalValue:
+			return fixup(uint32(localValue.Value), uint32(targetValue.Value))
+		}
+
+	case reloFieldByteOffset, reloFieldByteSize, reloFieldExists:
+		if _, ok := target.(*Fwd); ok {
+			// We can't relocate fields using a forward declaration, so
+			// skip it. If a non-forward declaration is present in the BTF
+			// we'll find it in one of the other iterations.
+			return poison()
+		}
+
+		localField, targetField, err := coreFindField(local, relo.accessor, target)
+		if errors.Is(err, errImpossibleRelocation) {
+			return poison()
+		}
+		if err != nil {
+			return zero, fmt.Errorf("target %s: %w", target, err)
+		}
+
+		switch relo.kind {
+		case reloFieldExists:
+			return fixup(1, 1)
+
+		case reloFieldByteOffset:
+			return fixup(localField.offset/8, targetField.offset/8)
+
+		case reloFieldByteSize:
+			localSize, err := Sizeof(localField.Type)
+			if err != nil {
+				return zero, err
+			}
+
+			targetSize, err := Sizeof(targetField.Type)
+			if err != nil {
+				return zero, err
+			}
+
+			return fixup(uint32(localSize), uint32(targetSize))
 
-	relo := relos[0]
-	for _, altRelo := range relos[1:] {
-		if !altRelo.equal(relo) {
-			return Relocation{}, fmt.Errorf("multiple types %v match: %w", matches, errAmbiguousRelocation)
 		}
 	}
 
-	return relo, nil
+	return zero, fmt.Errorf("relocation %s: %w", relo.kind, ErrNotSupported)
 }
 
 /* coreAccessor contains a path through a struct. It contains at least one index.
@@ -219,6 +482,240 @@ func parseCoreAccessor(accessor string) (coreAccessor, error) {
 	return result, nil
 }
 
+func (ca coreAccessor) String() string {
+	strs := make([]string, 0, len(ca))
+	for _, i := range ca {
+		strs = append(strs, strconv.Itoa(i))
+	}
+	return strings.Join(strs, ":")
+}
+
+func (ca coreAccessor) enumValue(t Type) (*EnumValue, error) {
+	e, ok := t.(*Enum)
+	if !ok {
+		return nil, fmt.Errorf("not an enum: %s", t)
+	}
+
+	if len(ca) > 1 {
+		return nil, fmt.Errorf("invalid accessor %s for enum", ca)
+	}
+
+	i := ca[0]
+	if i >= len(e.Values) {
+		return nil, fmt.Errorf("invalid index %d for %s", i, e)
+	}
+
+	return &e.Values[i], nil
+}
+
+type coreField struct {
+	Type   Type
+	offset uint32
+}
+
+func adjustOffset(base uint32, t Type, n int) (uint32, error) {
+	size, err := Sizeof(t)
+	if err != nil {
+		return 0, err
+	}
+
+	return base + (uint32(n) * uint32(size) * 8), nil
+}
+
+// coreFindField descends into the local type using the accessor and tries to
+// find an equivalent field in target at each step.
+//
+// Returns the field and the offset of the field from the start of
+// target in bits.
+func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreField, _ error) {
+	// The first index is used to offset a pointer of the base type like
+	// when accessing an array.
+	localOffset, err := adjustOffset(0, local, localAcc[0])
+	if err != nil {
+		return coreField{}, coreField{}, err
+	}
+
+	targetOffset, err := adjustOffset(0, target, localAcc[0])
+	if err != nil {
+		return coreField{}, coreField{}, err
+	}
+
+	if err := coreAreMembersCompatible(local, target); err != nil {
+		return coreField{}, coreField{}, fmt.Errorf("fields: %w", err)
+	}
+
+	var localMaybeFlex, targetMaybeFlex bool
+	for _, acc := range localAcc[1:] {
+		switch localType := local.(type) {
+		case composite:
+			// For composite types acc is used to find the field in the local type,
+			// and then we try to find a field in target with the same name.
+			localMembers := localType.members()
+			if acc >= len(localMembers) {
+				return coreField{}, coreField{}, fmt.Errorf("invalid accessor %d for %s", acc, local)
+			}
+
+			localMember := localMembers[acc]
+			if localMember.Name == "" {
+				_, ok := localMember.Type.(composite)
+				if !ok {
+					return coreField{}, coreField{}, fmt.Errorf("unnamed field with type %s: %s", localMember.Type, ErrNotSupported)
+				}
+
+				// This is an anonymous struct or union, ignore it.
+				local = localMember.Type
+				localOffset += localMember.Offset
+				localMaybeFlex = false
+				continue
+			}
+
+			targetType, ok := target.(composite)
+			if !ok {
+				return coreField{}, coreField{}, fmt.Errorf("target not composite: %w", errImpossibleRelocation)
+			}
+
+			targetMember, last, err := coreFindMember(targetType, localMember.Name)
+			if err != nil {
+				return coreField{}, coreField{}, err
+			}
+
+			if targetMember.BitfieldSize > 0 {
+				return coreField{}, coreField{}, fmt.Errorf("field %q is a bitfield: %w", targetMember.Name, ErrNotSupported)
+			}
+
+			local = localMember.Type
+			localMaybeFlex = acc == len(localMembers)-1
+			localOffset += localMember.Offset
+			target = targetMember.Type
+			targetMaybeFlex = last
+			targetOffset += targetMember.Offset
+
+		case *Array:
+			// For arrays, acc is the index in the target.
+			targetType, ok := target.(*Array)
+			if !ok {
+				return coreField{}, coreField{}, fmt.Errorf("target not array: %w", errImpossibleRelocation)
+			}
+
+			if localType.Nelems == 0 && !localMaybeFlex {
+				return coreField{}, coreField{}, fmt.Errorf("local type has invalid flexible array")
+			}
+			if targetType.Nelems == 0 && !targetMaybeFlex {
+				return coreField{}, coreField{}, fmt.Errorf("target type has invalid flexible array")
+			}
+
+			if localType.Nelems > 0 && acc >= int(localType.Nelems) {
+				return coreField{}, coreField{}, fmt.Errorf("invalid access of %s at index %d", localType, acc)
+			}
+			if targetType.Nelems > 0 && acc >= int(targetType.Nelems) {
+				return coreField{}, coreField{}, fmt.Errorf("out of bounds access of target: %w", errImpossibleRelocation)
+			}
+
+			local = localType.Type
+			localMaybeFlex = false
+			localOffset, err = adjustOffset(localOffset, local, acc)
+			if err != nil {
+				return coreField{}, coreField{}, err
+			}
+
+			target = targetType.Type
+			targetMaybeFlex = false
+			targetOffset, err = adjustOffset(targetOffset, target, acc)
+			if err != nil {
+				return coreField{}, coreField{}, err
+			}
+
+		default:
+			return coreField{}, coreField{}, fmt.Errorf("relocate field of %T: %w", localType, ErrNotSupported)
+		}
+
+		if err := coreAreMembersCompatible(local, target); err != nil {
+			return coreField{}, coreField{}, err
+		}
+	}
+
+	return coreField{local, localOffset}, coreField{target, targetOffset}, nil
+}
+
+// coreFindMember finds a member in a composite type while handling anonymous
+// structs and unions.
+func coreFindMember(typ composite, name Name) (Member, bool, error) {
+	if name == "" {
+		return Member{}, false, errors.New("can't search for anonymous member")
+	}
+
+	type offsetTarget struct {
+		composite
+		offset uint32
+	}
+
+	targets := []offsetTarget{{typ, 0}}
+	visited := make(map[composite]bool)
+
+	for i := 0; i < len(targets); i++ {
+		target := targets[i]
+
+		// Only visit targets once to prevent infinite recursion.
+		if visited[target] {
+			continue
+		}
+		if len(visited) >= maxTypeDepth {
+			// This check is different than libbpf, which restricts the entire
+			// path to BPF_CORE_SPEC_MAX_LEN items.
+			return Member{}, false, fmt.Errorf("type is nested too deep")
+		}
+		visited[target] = true
+
+		members := target.members()
+		for j, member := range members {
+			if member.Name == name {
+				// NB: This is safe because member is a copy.
+				member.Offset += target.offset
+				return member, j == len(members)-1, nil
+			}
+
+			// The names don't match, but this member could be an anonymous struct
+			// or union.
+			if member.Name != "" {
+				continue
+			}
+
+			comp, ok := member.Type.(composite)
+			if !ok {
+				return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type)
+			}
+
+			targets = append(targets, offsetTarget{comp, target.offset + member.Offset})
+		}
+	}
+
+	return Member{}, false, fmt.Errorf("no matching member: %w", errImpossibleRelocation)
+}
+
+// coreFindEnumValue follows localAcc to find the equivalent enum value in target.
+func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localValue, targetValue *EnumValue, _ error) {
+	localValue, err := localAcc.enumValue(local)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	targetEnum, ok := target.(*Enum)
+	if !ok {
+		return nil, nil, errImpossibleRelocation
+	}
+
+	localName := localValue.Name.essentialName()
+	for i, targetValue := range targetEnum.Values {
+		if targetValue.Name.essentialName() != localName {
+			continue
+		}
+
+		return localValue, &targetEnum.Values[i], nil
+	}
+
+	return nil, nil, errImpossibleRelocation
+}
+
 /* The comment below is from bpf_core_types_are_compat in libbpf.c:
  *
  * Check local and target types for compatibility. This check is used for
@@ -239,8 +736,10 @@ func parseCoreAccessor(accessor string) (coreAccessor, error) {
  *     number of input args and compatible return and argument types.
  * These rules are not set in stone and probably will be adjusted as we get
  * more experience with using BPF CO-RE relocations.
+ *
+ * Returns errImpossibleRelocation if types are not compatible.
  */
-func coreAreTypesCompatible(localType Type, targetType Type) (bool, error) {
+func coreAreTypesCompatible(localType Type, targetType Type) error {
 	var (
 		localTs, targetTs typeDeque
 		l, t              = &localType, &targetType
@@ -249,14 +748,14 @@ func coreAreTypesCompatible(localType Type, targetType Type) (bool, error) {
 
 	for ; l != nil && t != nil; l, t = localTs.shift(), targetTs.shift() {
 		if depth >= maxTypeDepth {
-			return false, errors.New("types are nested too deep")
+			return errors.New("types are nested too deep")
 		}
 
-		localType = skipQualifierAndTypedef(*l)
-		targetType = skipQualifierAndTypedef(*t)
+		localType = *l
+		targetType = *t
 
 		if reflect.TypeOf(localType) != reflect.TypeOf(targetType) {
-			return false, nil
+			return fmt.Errorf("type mismatch: %w", errImpossibleRelocation)
 		}
 
 		switch lv := (localType).(type) {
@@ -266,7 +765,7 @@ func coreAreTypesCompatible(localType Type, targetType Type) (bool, error) {
 		case *Int:
 			tv := targetType.(*Int)
 			if lv.isBitfield() || tv.isBitfield() {
-				return false, nil
+				return fmt.Errorf("bitfield: %w", errImpossibleRelocation)
 			}
 
 		case *Pointer, *Array:
@@ -277,7 +776,7 @@ func coreAreTypesCompatible(localType Type, targetType Type) (bool, error) {
 		case *FuncProto:
 			tv := targetType.(*FuncProto)
 			if len(lv.Params) != len(tv.Params) {
-				return false, nil
+				return fmt.Errorf("function param mismatch: %w", errImpossibleRelocation)
 			}
 
 			depth++
@@ -285,22 +784,24 @@ func coreAreTypesCompatible(localType Type, targetType Type) (bool, error) {
 			targetType.walk(&targetTs)
 
 		default:
-			return false, fmt.Errorf("unsupported type %T", localType)
+			return fmt.Errorf("unsupported type %T", localType)
 		}
 	}
 
 	if l != nil {
-		return false, fmt.Errorf("dangling local type %T", *l)
+		return fmt.Errorf("dangling local type %T", *l)
 	}
 
 	if t != nil {
-		return false, fmt.Errorf("dangling target type %T", *t)
+		return fmt.Errorf("dangling target type %T", *t)
 	}
 
-	return true, nil
+	return nil
 }
 
-/* The comment below is from bpf_core_fields_are_compat in libbpf.c:
+/* coreAreMembersCompatible checks two types for field-based relocation compatibility.
+ *
+ * The comment below is from bpf_core_fields_are_compat in libbpf.c:
  *
  * Check two types for compatibility for the purpose of field access
  * relocation. const/volatile/restrict and typedefs are skipped to ensure we
@@ -314,65 +815,63 @@ func coreAreTypesCompatible(localType Type, targetType Type) (bool, error) {
  *   - for INT, size and signedness are ignored;
  *   - for ARRAY, dimensionality is ignored, element types are checked for
  *     compatibility recursively;
+ *     [ NB: coreAreMembersCompatible doesn't recurse, this check is done
+ *       by coreFindField. ]
  *   - everything else shouldn't be ever a target of relocation.
  * These rules are not set in stone and probably will be adjusted as we get
  * more experience with using BPF CO-RE relocations.
+ *
+ * Returns errImpossibleRelocation if the members are not compatible.
  */
-func coreAreMembersCompatible(localType Type, targetType Type) (bool, error) {
-	doNamesMatch := func(a, b string) bool {
+func coreAreMembersCompatible(localType Type, targetType Type) error {
+	doNamesMatch := func(a, b string) error {
 		if a == "" || b == "" {
 			// allow anonymous and named type to match
-			return true
-		}
-
-		return essentialName(a) == essentialName(b)
-	}
-
-	for depth := 0; depth <= maxTypeDepth; depth++ {
-		localType = skipQualifierAndTypedef(localType)
-		targetType = skipQualifierAndTypedef(targetType)
-
-		_, lok := localType.(composite)
-		_, tok := targetType.(composite)
-		if lok && tok {
-			return true, nil
+			return nil
 		}
 
-		if reflect.TypeOf(localType) != reflect.TypeOf(targetType) {
-			return false, nil
+		if essentialName(a) == essentialName(b) {
+			return nil
 		}
 
-		switch lv := localType.(type) {
-		case *Pointer:
-			return true, nil
+		return fmt.Errorf("names don't match: %w", errImpossibleRelocation)
+	}
 
-		case *Enum:
-			tv := targetType.(*Enum)
-			return doNamesMatch(lv.name(), tv.name()), nil
+	_, lok := localType.(composite)
+	_, tok := targetType.(composite)
+	if lok && tok {
+		return nil
+	}
 
-		case *Fwd:
-			tv := targetType.(*Fwd)
-			return doNamesMatch(lv.name(), tv.name()), nil
+	if reflect.TypeOf(localType) != reflect.TypeOf(targetType) {
+		return fmt.Errorf("type mismatch: %w", errImpossibleRelocation)
+	}
 
-		case *Int:
-			tv := targetType.(*Int)
-			return !lv.isBitfield() && !tv.isBitfield(), nil
+	switch lv := localType.(type) {
+	case *Array, *Pointer:
+		return nil
 
-		case *Array:
-			tv := targetType.(*Array)
+	case *Enum:
+		tv := targetType.(*Enum)
+		return doNamesMatch(lv.name(), tv.name())
 
-			localType = lv.Type
-			targetType = tv.Type
+	case *Fwd:
+		tv := targetType.(*Fwd)
+		return doNamesMatch(lv.name(), tv.name())
 
-		default:
-			return false, fmt.Errorf("unsupported type %T", localType)
+	case *Int:
+		tv := targetType.(*Int)
+		if lv.isBitfield() || tv.isBitfield() {
+			return fmt.Errorf("bitfield: %w", errImpossibleRelocation)
 		}
-	}
+		return nil
 
-	return false, errors.New("types are nested too deep")
+	default:
+		return fmt.Errorf("type %s: %w", localType, ErrNotSupported)
+	}
 }
 
-func skipQualifierAndTypedef(typ Type) Type {
+func skipQualifierAndTypedef(typ Type) (Type, error) {
 	result := typ
 	for depth := 0; depth <= maxTypeDepth; depth++ {
 		switch v := (result).(type) {
@@ -381,8 +880,8 @@ func skipQualifierAndTypedef(typ Type) Type {
 		case *Typedef:
 			result = v.Type
 		default:
-			return result
+			return result, nil
 		}
 	}
-	return typ
+	return nil, errors.New("exceeded type depth")
 }

+ 34 - 12
vendor/github.com/cilium/ebpf/internal/btf/ext_info.go

@@ -30,7 +30,7 @@ type btfExtCoreHeader struct {
 	CoreReloLen uint32
 }
 
-func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, coreRelos map[string]bpfCoreRelos, err error) {
+func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (funcInfo, lineInfo map[string]extInfo, relos map[string]coreRelos, err error) {
 	var header btfExtHeader
 	var coreHeader btfExtCoreHeader
 	if err := binary.Read(r, bo, &header); err != nil {
@@ -94,13 +94,13 @@ func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (f
 			return nil, nil, nil, fmt.Errorf("can't seek to CO-RE relocation section: %v", err)
 		}
 
-		coreRelos, err = parseExtInfoRelos(io.LimitReader(r, int64(coreHeader.CoreReloLen)), bo, strings)
+		relos, err = parseExtInfoRelos(io.LimitReader(r, int64(coreHeader.CoreReloLen)), bo, strings)
 		if err != nil {
 			return nil, nil, nil, fmt.Errorf("CO-RE relocation info: %w", err)
 		}
 	}
 
-	return funcInfo, lineInfo, coreRelos, nil
+	return funcInfo, lineInfo, relos, nil
 }
 
 type btfExtInfoSec struct {
@@ -208,18 +208,25 @@ type bpfCoreRelo struct {
 	InsnOff      uint32
 	TypeID       TypeID
 	AccessStrOff uint32
-	ReloKind     coreReloKind
+	Kind         COREKind
 }
 
-type bpfCoreRelos []bpfCoreRelo
+type coreRelo struct {
+	insnOff  uint32
+	typeID   TypeID
+	accessor coreAccessor
+	kind     COREKind
+}
+
+type coreRelos []coreRelo
 
 // append two slices of extInfoRelo to each other. The InsnOff of b are adjusted
 // by offset.
-func (r bpfCoreRelos) append(other bpfCoreRelos, offset uint64) bpfCoreRelos {
-	result := make([]bpfCoreRelo, 0, len(r)+len(other))
+func (r coreRelos) append(other coreRelos, offset uint64) coreRelos {
+	result := make([]coreRelo, 0, len(r)+len(other))
 	result = append(result, r...)
 	for _, relo := range other {
-		relo.InsnOff += uint32(offset)
+		relo.insnOff += uint32(offset)
 		result = append(result, relo)
 	}
 	return result
@@ -227,7 +234,7 @@ func (r bpfCoreRelos) append(other bpfCoreRelos, offset uint64) bpfCoreRelos {
 
 var extInfoReloSize = binary.Size(bpfCoreRelo{})
 
-func parseExtInfoRelos(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]bpfCoreRelos, error) {
+func parseExtInfoRelos(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[string]coreRelos, error) {
 	var recordSize uint32
 	if err := binary.Read(r, bo, &recordSize); err != nil {
 		return nil, fmt.Errorf("read record size: %v", err)
@@ -237,14 +244,14 @@ func parseExtInfoRelos(r io.Reader, bo binary.ByteOrder, strings stringTable) (m
 		return nil, fmt.Errorf("expected record size %d, got %d", extInfoReloSize, recordSize)
 	}
 
-	result := make(map[string]bpfCoreRelos)
+	result := make(map[string]coreRelos)
 	for {
 		secName, infoHeader, err := parseExtInfoHeader(r, bo, strings)
 		if errors.Is(err, io.EOF) {
 			return result, nil
 		}
 
-		var relos []bpfCoreRelo
+		var relos coreRelos
 		for i := uint32(0); i < infoHeader.NumInfo; i++ {
 			var relo bpfCoreRelo
 			if err := binary.Read(r, bo, &relo); err != nil {
@@ -255,7 +262,22 @@ func parseExtInfoRelos(r io.Reader, bo binary.ByteOrder, strings stringTable) (m
 				return nil, fmt.Errorf("section %v: offset %v is not aligned with instruction size", secName, relo.InsnOff)
 			}
 
-			relos = append(relos, relo)
+			accessorStr, err := strings.Lookup(relo.AccessStrOff)
+			if err != nil {
+				return nil, err
+			}
+
+			accessor, err := parseCoreAccessor(accessorStr)
+			if err != nil {
+				return nil, fmt.Errorf("accessor %q: %s", accessorStr, err)
+			}
+
+			relos = append(relos, coreRelo{
+				relo.InsnOff,
+				relo.TypeID,
+				accessor,
+				relo.Kind,
+			})
 		}
 
 		result[secName] = relos

+ 42 - 20
vendor/github.com/cilium/ebpf/internal/btf/types.go

@@ -1,7 +1,6 @@
 package btf
 
 import (
-	"errors"
 	"fmt"
 	"math"
 	"strings"
@@ -37,6 +36,7 @@ type Type interface {
 type namedType interface {
 	Type
 	name() string
+	essentialName() string
 }
 
 // Name identifies a type.
@@ -48,6 +48,10 @@ func (n Name) name() string {
 	return string(n)
 }
 
+func (n Name) essentialName() string {
+	return essentialName(string(n))
+}
+
 // Void is the unit type of BTF.
 type Void struct{}
 
@@ -174,8 +178,7 @@ func (s *Struct) walk(tdq *typeDeque) {
 
 func (s *Struct) copy() Type {
 	cpy := *s
-	cpy.Members = make([]Member, len(s.Members))
-	copy(cpy.Members, s.Members)
+	cpy.Members = copyMembers(s.Members)
 	return &cpy
 }
 
@@ -206,8 +209,7 @@ func (u *Union) walk(tdq *typeDeque) {
 
 func (u *Union) copy() Type {
 	cpy := *u
-	cpy.Members = make([]Member, len(u.Members))
-	copy(cpy.Members, u.Members)
+	cpy.Members = copyMembers(u.Members)
 	return &cpy
 }
 
@@ -215,6 +217,12 @@ func (u *Union) members() []Member {
 	return u.Members
 }
 
+func copyMembers(orig []Member) []Member {
+	cpy := make([]Member, len(orig))
+	copy(cpy, orig)
+	return cpy
+}
+
 type composite interface {
 	members() []Member
 }
@@ -372,11 +380,12 @@ func (r *Restrict) copy() Type {
 type Func struct {
 	TypeID
 	Name
-	Type Type
+	Type    Type
+	Linkage FuncLinkage
 }
 
 func (f *Func) String() string {
-	return fmt.Sprintf("func#%d[%q proto=#%d]", f.TypeID, f.Name, f.Type.ID())
+	return fmt.Sprintf("func#%d[%s %q proto=#%d]", f.TypeID, f.Linkage, f.Name, f.Type.ID())
 }
 
 func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) }
@@ -425,12 +434,12 @@ type FuncParam struct {
 type Var struct {
 	TypeID
 	Name
-	Type Type
+	Type    Type
+	Linkage VarLinkage
 }
 
 func (v *Var) String() string {
-	// TODO: Linkage
-	return fmt.Sprintf("var#%d[%q]", v.TypeID, v.Name)
+	return fmt.Sprintf("var#%d[%s %q]", v.TypeID, v.Linkage, v.Name)
 }
 
 func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) }
@@ -511,7 +520,7 @@ func Sizeof(typ Type) (int, error) {
 		switch v := typ.(type) {
 		case *Array:
 			if n > 0 && int64(v.Nelems) > math.MaxInt64/n {
-				return 0, errors.New("overflow")
+				return 0, fmt.Errorf("type %s: overflow", typ)
 			}
 
 			// Arrays may be of zero length, which allows
@@ -532,28 +541,30 @@ func Sizeof(typ Type) (int, error) {
 			continue
 
 		default:
-			return 0, fmt.Errorf("unrecognized type %T", typ)
+			return 0, fmt.Errorf("unsized type %T", typ)
 		}
 
 		if n > 0 && elem > math.MaxInt64/n {
-			return 0, errors.New("overflow")
+			return 0, fmt.Errorf("type %s: overflow", typ)
 		}
 
 		size := n * elem
 		if int64(int(size)) != size {
-			return 0, errors.New("overflow")
+			return 0, fmt.Errorf("type %s: overflow", typ)
 		}
 
 		return int(size), nil
 	}
 
-	return 0, errors.New("exceeded type depth")
+	return 0, fmt.Errorf("type %s: exceeded type depth", typ)
 }
 
 // copy a Type recursively.
 //
 // typ may form a cycle.
-func copyType(typ Type) Type {
+//
+// Returns any errors from transform verbatim.
+func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) {
 	var (
 		copies = make(map[Type]Type)
 		work   typeDeque
@@ -566,7 +577,17 @@ func copyType(typ Type) Type {
 			continue
 		}
 
-		cpy := (*t).copy()
+		var cpy Type
+		if transform != nil {
+			tf, err := transform(*t)
+			if err != nil {
+				return nil, fmt.Errorf("copy %s: %w", typ, err)
+			}
+			cpy = tf.copy()
+		} else {
+			cpy = (*t).copy()
+		}
+
 		copies[*t] = cpy
 		*t = cpy
 
@@ -574,7 +595,7 @@ func copyType(typ Type) Type {
 		cpy.walk(&work)
 	}
 
-	return typ
+	return typ, nil
 }
 
 // typeDeque keeps track of pointers to types which still
@@ -783,7 +804,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
 			typ = restrict
 
 		case kindFunc:
-			fn := &Func{id, name, nil}
+			fn := &Func{id, name, nil, raw.Linkage()}
 			fixup(raw.Type(), kindFuncProto, &fn.Type)
 			typ = fn
 
@@ -808,7 +829,8 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
 			typ = fp
 
 		case kindVar:
-			v := &Var{id, name, nil}
+			variable := raw.data.(*btfVariable)
+			v := &Var{id, name, nil, VarLinkage(variable.Linkage)}
 			fixup(raw.Type(), kindUnknown, &v.Type)
 			typ = v
 

+ 16 - 0
vendor/github.com/cilium/ebpf/internal/elf.go

@@ -50,3 +50,19 @@ func (se *SafeELFFile) Symbols() (syms []elf.Symbol, err error) {
 	syms, err = se.File.Symbols()
 	return
 }
+
+// DynamicSymbols is the safe version of elf.File.DynamicSymbols.
+func (se *SafeELFFile) DynamicSymbols() (syms []elf.Symbol, err error) {
+	defer func() {
+		r := recover()
+		if r == nil {
+			return
+		}
+
+		syms = nil
+		err = fmt.Errorf("reading ELF dynamic symbols panicked: %s", r)
+	}()
+
+	syms, err = se.File.DynamicSymbols()
+	return
+}

+ 5 - 0
vendor/github.com/cilium/ebpf/internal/endian.go

@@ -9,11 +9,16 @@ import (
 // depending on the host's endianness.
 var NativeEndian binary.ByteOrder
 
+// Clang is set to either "el" or "eb" depending on the host's endianness.
+var ClangEndian string
+
 func init() {
 	if isBigEndian() {
 		NativeEndian = binary.BigEndian
+		ClangEndian = "eb"
 	} else {
 		NativeEndian = binary.LittleEndian
+		ClangEndian = "el"
 	}
 }
 

+ 4 - 0
vendor/github.com/cilium/ebpf/internal/errors.go

@@ -29,6 +29,10 @@ type VerifierError struct {
 	log   string
 }
 
+func (le *VerifierError) Unwrap() error {
+	return le.cause
+}
+
 func (le *VerifierError) Error() string {
 	if le.log == "" {
 		return le.cause.Error()

+ 0 - 4
vendor/github.com/cilium/ebpf/internal/ptr.go

@@ -22,10 +22,6 @@ func NewSlicePointer(buf []byte) Pointer {
 
 // NewStringPointer creates a 64-bit pointer from a string.
 func NewStringPointer(str string) Pointer {
-	if str == "" {
-		return Pointer{}
-	}
-
 	p, err := unix.BytePtrFromString(str)
 	if err != nil {
 		return Pointer{}

+ 66 - 1
vendor/github.com/cilium/ebpf/internal/syscall.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"path/filepath"
 	"runtime"
+	"syscall"
 	"unsafe"
 
 	"github.com/cilium/ebpf/internal/unix"
@@ -61,7 +62,7 @@ func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
 
 	var err error
 	if errNo != 0 {
-		err = errNo
+		err = wrappedErrno{errNo}
 	}
 
 	return r1, err
@@ -178,3 +179,67 @@ func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error {
 	}
 	return nil
 }
+
+// BPFObjName is a null-terminated string made up of
+// 'A-Za-z0-9_' characters.
+type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte
+
+// NewBPFObjName truncates the result if it is too long.
+func NewBPFObjName(name string) BPFObjName {
+	var result BPFObjName
+	copy(result[:unix.BPF_OBJ_NAME_LEN-1], name)
+	return result
+}
+
+type BPFMapCreateAttr struct {
+	MapType        uint32
+	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   uint32
+	BTFValueTypeID uint32
+}
+
+func BPFMapCreate(attr *BPFMapCreateAttr) (*FD, error) {
+	fd, err := BPF(BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
+	if err != nil {
+		return nil, err
+	}
+
+	return NewFD(uint32(fd)), nil
+}
+
+// wrappedErrno wraps syscall.Errno to prevent direct comparisons with
+// syscall.E* or unix.E* constants.
+//
+// You should never export an error of this type.
+type wrappedErrno struct {
+	syscall.Errno
+}
+
+func (we wrappedErrno) Unwrap() error {
+	return we.Errno
+}
+
+type syscallError struct {
+	error
+	errno syscall.Errno
+}
+
+func SyscallError(err error, errno syscall.Errno) error {
+	return &syscallError{err, errno}
+}
+
+func (se *syscallError) Is(target error) bool {
+	return target == se.error
+}
+
+func (se *syscallError) Unwrap() error {
+	return se.errno
+}

+ 3 - 0
vendor/github.com/cilium/ebpf/internal/unix/types_linux.go

@@ -31,6 +31,8 @@ const (
 	BPF_F_RDONLY_PROG        = linux.BPF_F_RDONLY_PROG
 	BPF_F_WRONLY_PROG        = linux.BPF_F_WRONLY_PROG
 	BPF_F_SLEEPABLE          = linux.BPF_F_SLEEPABLE
+	BPF_F_MMAPABLE           = linux.BPF_F_MMAPABLE
+	BPF_F_INNER_MAP          = linux.BPF_F_INNER_MAP
 	BPF_OBJ_NAME_LEN         = linux.BPF_OBJ_NAME_LEN
 	BPF_TAG_SIZE             = linux.BPF_TAG_SIZE
 	SYS_BPF                  = linux.SYS_BPF
@@ -42,6 +44,7 @@ const (
 	PROT_READ                = linux.PROT_READ
 	PROT_WRITE               = linux.PROT_WRITE
 	MAP_SHARED               = linux.MAP_SHARED
+	PERF_ATTR_SIZE_VER1      = linux.PERF_ATTR_SIZE_VER1
 	PERF_TYPE_SOFTWARE       = linux.PERF_TYPE_SOFTWARE
 	PERF_TYPE_TRACEPOINT     = linux.PERF_TYPE_TRACEPOINT
 	PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT

+ 3 - 0
vendor/github.com/cilium/ebpf/internal/unix/types_other.go

@@ -31,6 +31,8 @@ const (
 	BPF_F_RDONLY_PROG        = 0
 	BPF_F_WRONLY_PROG        = 0
 	BPF_F_SLEEPABLE          = 0
+	BPF_F_MMAPABLE           = 0
+	BPF_F_INNER_MAP          = 0
 	BPF_OBJ_NAME_LEN         = 0x10
 	BPF_TAG_SIZE             = 0x8
 	SYS_BPF                  = 321
@@ -43,6 +45,7 @@ const (
 	PROT_READ                = 0x1
 	PROT_WRITE               = 0x2
 	MAP_SHARED               = 0x1
+	PERF_ATTR_SIZE_VER1      = 0
 	PERF_TYPE_SOFTWARE       = 0x1
 	PERF_TYPE_TRACEPOINT     = 0
 	PERF_COUNT_SW_BPF_OUTPUT = 0xa

+ 38 - 5
vendor/github.com/cilium/ebpf/link/iter.go

@@ -3,8 +3,10 @@ package link
 import (
 	"fmt"
 	"io"
+	"unsafe"
 
 	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal"
 )
 
 type IterOptions struct {
@@ -15,19 +17,45 @@ type IterOptions struct {
 	// AttachTo requires the kernel to include BTF of itself,
 	// and it to be compiled with a recent pahole (>= 1.16).
 	Program *ebpf.Program
+
+	// Map specifies the target map for bpf_map_elem and sockmap iterators.
+	// It may be nil.
+	Map *ebpf.Map
 }
 
 // AttachIter attaches a BPF seq_file iterator.
 func AttachIter(opts IterOptions) (*Iter, error) {
-	link, err := AttachRawLink(RawLinkOptions{
-		Program: opts.Program,
-		Attach:  ebpf.AttachTraceIter,
-	})
+	if err := haveBPFLink(); err != nil {
+		return nil, err
+	}
+
+	progFd := opts.Program.FD()
+	if progFd < 0 {
+		return nil, fmt.Errorf("invalid program: %s", internal.ErrClosedFd)
+	}
+
+	var info bpfIterLinkInfoMap
+	if opts.Map != nil {
+		mapFd := opts.Map.FD()
+		if mapFd < 0 {
+			return nil, fmt.Errorf("invalid map: %w", internal.ErrClosedFd)
+		}
+		info.map_fd = uint32(mapFd)
+	}
+
+	attr := bpfLinkCreateIterAttr{
+		prog_fd:       uint32(progFd),
+		attach_type:   ebpf.AttachTraceIter,
+		iter_info:     internal.NewPointer(unsafe.Pointer(&info)),
+		iter_info_len: uint32(unsafe.Sizeof(info)),
+	}
+
+	fd, err := bpfLinkCreateIter(&attr)
 	if err != nil {
 		return nil, fmt.Errorf("can't link iterator: %w", err)
 	}
 
-	return &Iter{*link}, err
+	return &Iter{RawLink{fd, ""}}, err
 }
 
 // LoadPinnedIter loads a pinned iterator from a bpffs.
@@ -65,3 +93,8 @@ func (it *Iter) Open() (io.ReadCloser, error) {
 
 	return fd.File("bpf_iter"), nil
 }
+
+// union bpf_iter_link_info.map
+type bpfIterLinkInfoMap struct {
+	map_fd uint32
+}

+ 215 - 73
vendor/github.com/cilium/ebpf/link/kprobe.go

@@ -1,12 +1,16 @@
 package link
 
 import (
+	"bytes"
 	"crypto/rand"
 	"errors"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
+	"sync"
+	"unsafe"
 
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/internal"
@@ -15,13 +19,60 @@ import (
 
 var (
 	kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events")
+
+	kprobeRetprobeBit = struct {
+		once  sync.Once
+		value uint64
+		err   error
+	}{}
+)
+
+type probeType uint8
+
+const (
+	kprobeType probeType = iota
+	uprobeType
 )
 
+func (pt probeType) String() string {
+	if pt == kprobeType {
+		return "kprobe"
+	}
+	return "uprobe"
+}
+
+func (pt probeType) EventsPath() string {
+	if pt == kprobeType {
+		return kprobeEventsPath
+	}
+	return uprobeEventsPath
+}
+
+func (pt probeType) PerfEventType(ret bool) perfEventType {
+	if pt == kprobeType {
+		if ret {
+			return kretprobeEvent
+		}
+		return kprobeEvent
+	}
+	if ret {
+		return uretprobeEvent
+	}
+	return uprobeEvent
+}
+
+func (pt probeType) RetprobeBit() (uint64, error) {
+	if pt == kprobeType {
+		return kretprobeBit()
+	}
+	return uretprobeBit()
+}
+
 // Kprobe attaches the given eBPF program to a perf event that fires when the
 // given kernel symbol starts executing. See /proc/kallsyms for available
 // symbols. For example, printk():
 //
-//	Kprobe("printk")
+//	Kprobe("printk", prog)
 //
 // The resulting Link must be Closed during program shutdown to avoid leaking
 // system resources.
@@ -44,7 +95,7 @@ func Kprobe(symbol string, prog *ebpf.Program) (Link, error) {
 // before the given kernel symbol exits, with the function stack left intact.
 // See /proc/kallsyms for available symbols. For example, printk():
 //
-//	Kretprobe("printk")
+//	Kretprobe("printk", prog)
 //
 // The resulting Link must be Closed during program shutdown to avoid leaking
 // system resources.
@@ -80,7 +131,10 @@ func kprobe(symbol string, prog *ebpf.Program, ret bool) (*perfEvent, error) {
 	}
 
 	// Use kprobe PMU if the kernel has it available.
-	tp, err := pmuKprobe(symbol, ret)
+	tp, err := pmuKprobe(platformPrefix(symbol), ret)
+	if errors.Is(err, os.ErrNotExist) {
+		tp, err = pmuKprobe(symbol, ret)
+	}
 	if err == nil {
 		return tp, nil
 	}
@@ -89,7 +143,10 @@ func kprobe(symbol string, prog *ebpf.Program, ret bool) (*perfEvent, error) {
 	}
 
 	// Use tracefs if kprobe PMU is missing.
-	tp, err = tracefsKprobe(symbol, ret)
+	tp, err = tracefsKprobe(platformPrefix(symbol), ret)
+	if errors.Is(err, os.ErrNotExist) {
+		tp, err = tracefsKprobe(symbol, ret)
+	}
 	if err != nil {
 		return nil, fmt.Errorf("creating trace event '%s' in tracefs: %w", symbol, err)
 	}
@@ -97,36 +154,70 @@ func kprobe(symbol string, prog *ebpf.Program, ret bool) (*perfEvent, error) {
 	return tp, nil
 }
 
-// pmuKprobe opens a perf event based on a Performance Monitoring Unit.
-// Requires at least 4.17 (e12f03d7031a "perf/core: Implement the
-// 'perf_kprobe' PMU").
-// Returns ErrNotSupported if the kernel doesn't support perf_kprobe PMU,
-// or os.ErrNotExist if the given symbol does not exist in the kernel.
+// pmuKprobe opens a perf event based on the kprobe PMU.
+// Returns os.ErrNotExist if the given symbol does not exist in the kernel.
 func pmuKprobe(symbol string, ret bool) (*perfEvent, error) {
+	return pmuProbe(kprobeType, symbol, "", 0, ret)
+}
 
+// pmuProbe opens a perf event based on a Performance Monitoring Unit.
+//
+// Requires at least a 4.17 kernel.
+// e12f03d7031a "perf/core: Implement the 'perf_kprobe' PMU"
+// 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU"
+//
+// Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU
+func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
 	// Getting the PMU type will fail if the kernel doesn't support
-	// the perf_kprobe PMU.
-	et, err := getPMUEventType("kprobe")
+	// the perf_[k,u]probe PMU.
+	et, err := getPMUEventType(typ)
 	if err != nil {
 		return nil, err
 	}
 
-	// Create a pointer to a NUL-terminated string for the kernel.
-	sp, err := unsafeStringPtr(symbol)
-	if err != nil {
-		return nil, err
-	}
-
-	// TODO: Parse the position of the bit from /sys/bus/event_source/devices/%s/format/retprobe.
-	config := 0
+	var config uint64
 	if ret {
-		config = 1
-	}
-
-	attr := unix.PerfEventAttr{
-		Type:   uint32(et),          // PMU event type read from sysfs
-		Ext1:   uint64(uintptr(sp)), // Kernel symbol to trace
-		Config: uint64(config),      // perf_kprobe PMU treats config as flags
+		bit, err := typ.RetprobeBit()
+		if err != nil {
+			return nil, err
+		}
+		config |= 1 << bit
+	}
+
+	var (
+		attr unix.PerfEventAttr
+		sp   unsafe.Pointer
+	)
+	switch typ {
+	case kprobeType:
+		// Create a pointer to a NUL-terminated string for the kernel.
+		sp, err := unsafeStringPtr(symbol)
+		if err != nil {
+			return nil, err
+		}
+
+		attr = unix.PerfEventAttr{
+			Type:   uint32(et),          // PMU event type read from sysfs
+			Ext1:   uint64(uintptr(sp)), // Kernel symbol to trace
+			Config: config,              // Retprobe flag
+		}
+	case uprobeType:
+		sp, err := unsafeStringPtr(path)
+		if err != nil {
+			return nil, err
+		}
+
+		attr = unix.PerfEventAttr{
+			// The minimum size required for PMU uprobes is PERF_ATTR_SIZE_VER1,
+			// since it added the config2 (Ext2) field. The Size field controls the
+			// size of the internal buffer the kernel allocates for reading the
+			// perf_event_attr argument from userspace.
+			Size:   unix.PERF_ATTR_SIZE_VER1,
+			Type:   uint32(et),          // PMU event type read from sysfs
+			Ext1:   uint64(uintptr(sp)), // Uprobe path
+			Ext2:   offset,              // Uprobe offset
+			Config: config,              // Retprobe flag
+		}
 	}
 
 	fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
@@ -144,22 +235,27 @@ func pmuKprobe(symbol string, ret bool) (*perfEvent, error) {
 	// Ensure the string pointer is not collected before PerfEventOpen returns.
 	runtime.KeepAlive(sp)
 
-	// Kernel has perf_kprobe PMU available, initialize perf event.
+	// Kernel has perf_[k,u]probe PMU available, initialize perf event.
 	return &perfEvent{
-		fd:       internal.NewFD(uint32(fd)),
-		pmuID:    et,
-		name:     symbol,
-		ret:      ret,
-		progType: ebpf.Kprobe,
+		fd:    internal.NewFD(uint32(fd)),
+		pmuID: et,
+		name:  symbol,
+		typ:   typ.PerfEventType(ret),
 	}, nil
 }
 
-// tracefsKprobe creates a trace event by writing an entry to <tracefs>/kprobe_events.
-// A new trace event group name is generated on every call to support creating
-// multiple trace events for the same kernel symbol. A perf event is then opened
-// on the newly-created trace event and returned to the caller.
+// tracefsKprobe creates a Kprobe tracefs entry.
 func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) {
+	return tracefsProbe(kprobeType, symbol, "", 0, ret)
+}
 
+// tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events.
+// A new trace event group name is generated on every call to support creating
+// multiple trace events for the same kernel or userspace symbol.
+// Path and offset are only set in the case of uprobe(s) and are used to set
+// the executable/library path on the filesystem and the offset where the probe is inserted.
+// A perf event is then opened on the newly-created trace event and returned to the caller.
+func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
 	// Generate a random string for each trace event we attempt to create.
 	// This value is used as the 'group' token in tracefs to allow creating
 	// multiple kprobe trace events with the same name.
@@ -176,14 +272,13 @@ func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) {
 	if err == nil {
 		return nil, fmt.Errorf("trace event already exists: %s/%s", group, symbol)
 	}
-	// The read is expected to fail with ErrNotSupported due to a non-existing event.
-	if err != nil && !errors.Is(err, ErrNotSupported) {
+	if err != nil && !errors.Is(err, os.ErrNotExist) {
 		return nil, fmt.Errorf("checking trace event %s/%s: %w", group, symbol, err)
 	}
 
-	// Create the kprobe trace event using tracefs.
-	if err := createTraceFSKprobeEvent(group, symbol, ret); err != nil {
-		return nil, fmt.Errorf("creating kprobe event on tracefs: %w", err)
+	// Create the [k,u]probe trace event using tracefs.
+	if err := createTraceFSProbeEvent(typ, group, symbol, path, offset, ret); err != nil {
+		return nil, fmt.Errorf("creating probe entry on tracefs: %w", err)
 	}
 
 	// Get the newly-created trace event's id.
@@ -202,65 +297,83 @@ func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) {
 		fd:        fd,
 		group:     group,
 		name:      symbol,
-		ret:       ret,
 		tracefsID: tid,
-		progType:  ebpf.Kprobe, // kernel only allows attaching kprobe programs to kprobe events
+		typ:       typ.PerfEventType(ret),
 	}, nil
 }
 
-// createTraceFSKprobeEvent creates a new ephemeral trace event by writing to
-// <tracefs>/kprobe_events. Returns ErrNotSupported if symbol is not a valid
-// kernel symbol, or if it is not traceable with kprobes.
-func createTraceFSKprobeEvent(group, symbol string, ret bool) error {
+// createTraceFSProbeEvent creates a new ephemeral trace event by writing to
+// <tracefs>/[k,u]probe_events. Returns os.ErrNotExist if symbol is not a valid
+// kernel symbol, or if it is not traceable with kprobes. Returns os.ErrExist
+// if a probe with the same group and symbol already exists.
+func createTraceFSProbeEvent(typ probeType, group, symbol, path string, offset uint64, ret bool) error {
 	// Open the kprobe_events file in tracefs.
-	f, err := os.OpenFile(kprobeEventsPath, os.O_APPEND|os.O_WRONLY, 0666)
+	f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666)
 	if err != nil {
-		return fmt.Errorf("error opening kprobe_events: %w", err)
+		return fmt.Errorf("error opening '%s': %w", typ.EventsPath(), err)
 	}
 	defer f.Close()
 
-	// The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt):
-	// p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
-	// r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
-	// -:[GRP/]EVENT                                        : Clear a probe
-	//
-	// Some examples:
-	// r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy
-	// p:ebpf_5678/p_my_kprobe __x64_sys_execve
-	//
-	// Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the
-	// kernel default to NR_CPUS. This is desired in most eBPF cases since
-	// subsampling or rate limiting logic can be more accurately implemented in
-	// the eBPF program itself. See Documentation/kprobes.txt for more details.
-	pe := fmt.Sprintf("%s:%s/%s %s", kprobePrefix(ret), group, symbol, symbol)
+	var pe string
+	switch typ {
+	case kprobeType:
+		// The kprobe_events syntax is as follows (see Documentation/trace/kprobetrace.txt):
+		// p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe
+		// r[MAXACTIVE][:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe
+		// -:[GRP/]EVENT                                        : Clear a probe
+		//
+		// Some examples:
+		// r:ebpf_1234/r_my_kretprobe nf_conntrack_destroy
+		// p:ebpf_5678/p_my_kprobe __x64_sys_execve
+		//
+		// Leaving the kretprobe's MAXACTIVE set to 0 (or absent) will make the
+		// kernel default to NR_CPUS. This is desired in most eBPF cases since
+		// subsampling or rate limiting logic can be more accurately implemented in
+		// the eBPF program itself.
+		// See Documentation/kprobes.txt for more details.
+		pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(ret), group, symbol, symbol)
+	case uprobeType:
+		// The uprobe_events syntax is as follows:
+		// p[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a probe
+		// r[:[GRP/]EVENT] PATH:OFFSET [FETCHARGS] : Set a return probe
+		// -:[GRP/]EVENT                           : Clear a probe
+		//
+		// Some examples:
+		// r:ebpf_1234/readline /bin/bash:0x12345
+		// p:ebpf_5678/main_mySymbol /bin/mybin:0x12345
+		//
+		// See Documentation/trace/uprobetracer.txt for more details.
+		pathOffset := uprobePathOffset(path, offset)
+		pe = fmt.Sprintf("%s:%s/%s %s", probePrefix(ret), group, symbol, pathOffset)
+	}
 	_, err = f.WriteString(pe)
 	// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
 	// when trying to create a kretprobe for a missing symbol. Make sure ENOENT
 	// is returned to the caller.
 	if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
-		return fmt.Errorf("kernel symbol %s not found: %w", symbol, os.ErrNotExist)
+		return fmt.Errorf("symbol %s not found: %w", symbol, os.ErrNotExist)
 	}
 	if err != nil {
-		return fmt.Errorf("writing '%s' to kprobe_events: %w", pe, err)
+		return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err)
 	}
 
 	return nil
 }
 
-// closeTraceFSKprobeEvent removes the kprobe with the given group, symbol and kind
-// from <tracefs>/kprobe_events.
-func closeTraceFSKprobeEvent(group, symbol string) error {
-	f, err := os.OpenFile(kprobeEventsPath, os.O_APPEND|os.O_WRONLY, 0666)
+// closeTraceFSProbeEvent removes the [k,u]probe with the given type, group and symbol
+// from <tracefs>/[k,u]probe_events.
+func closeTraceFSProbeEvent(typ probeType, group, symbol string) error {
+	f, err := os.OpenFile(typ.EventsPath(), os.O_APPEND|os.O_WRONLY, 0666)
 	if err != nil {
-		return fmt.Errorf("error opening kprobe_events: %w", err)
+		return fmt.Errorf("error opening %s: %w", typ.EventsPath(), err)
 	}
 	defer f.Close()
 
-	// See kprobe_events syntax above. Kprobe type does not need to be specified
+	// See [k,u]probe_events syntax above. The probe type does not need to be specified
 	// for removals.
 	pe := fmt.Sprintf("-:%s/%s", group, symbol)
 	if _, err = f.WriteString(pe); err != nil {
-		return fmt.Errorf("writing '%s' to kprobe_events: %w", pe, err)
+		return fmt.Errorf("writing '%s' to '%s': %w", pe, typ.EventsPath(), err)
 	}
 
 	return nil
@@ -288,9 +401,38 @@ func randomGroup(prefix string) (string, error) {
 	return group, nil
 }
 
-func kprobePrefix(ret bool) string {
+func probePrefix(ret bool) string {
 	if ret {
 		return "r"
 	}
 	return "p"
 }
+
+// determineRetprobeBit reads a Performance Monitoring Unit's retprobe bit
+// from /sys/bus/event_source/devices/<pmu>/format/retprobe.
+func determineRetprobeBit(typ probeType) (uint64, error) {
+	p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe")
+
+	data, err := ioutil.ReadFile(p)
+	if err != nil {
+		return 0, err
+	}
+
+	var rp uint64
+	n, err := fmt.Sscanf(string(bytes.TrimSpace(data)), "config:%d", &rp)
+	if err != nil {
+		return 0, fmt.Errorf("parse retprobe bit: %w", err)
+	}
+	if n != 1 {
+		return 0, fmt.Errorf("parse retprobe bit: expected 1 item, got %d", n)
+	}
+
+	return rp, nil
+}
+
+func kretprobeBit() (uint64, error) {
+	kprobeRetprobeBit.once.Do(func() {
+		kprobeRetprobeBit.value, kprobeRetprobeBit.err = determineRetprobeBit(kprobeType)
+	})
+	return kprobeRetprobeBit.value, kprobeRetprobeBit.err
+}

+ 49 - 29
vendor/github.com/cilium/ebpf/link/perf_event.go

@@ -31,6 +31,10 @@ import (
 //   exported kernel symbols. kprobe-based (tracefs) trace events can be
 //   created system-wide by writing to the <tracefs>/kprobe_events file, or
 //   they can be scoped to the current process by creating PMU perf events.
+// - u(ret)probe: Ephemeral trace events based on user provides ELF binaries
+//   and offsets. uprobe-based (tracefs) trace events can be
+//   created system-wide by writing to the <tracefs>/uprobe_events file, or
+//   they can be scoped to the current process by creating PMU perf events.
 // - perf event: An object instantiated based on an existing trace event or
 //   kernel symbol. Referred to by fd in userspace.
 //   Exactly one eBPF program can be attached to a perf event. Multiple perf
@@ -52,6 +56,16 @@ const (
 	perfAllThreads = -1
 )
 
+type perfEventType uint8
+
+const (
+	tracepointEvent perfEventType = iota
+	kprobeEvent
+	kretprobeEvent
+	uprobeEvent
+	uretprobeEvent
+)
+
 // A perfEvent represents a perf event kernel object. Exactly one eBPF program
 // can be attached to it. It is created based on a tracefs trace event or a
 // Performance Monitoring Unit (PMU).
@@ -66,11 +80,10 @@ type perfEvent struct {
 	// ID of the trace event read from tracefs. Valid IDs are non-zero.
 	tracefsID uint64
 
-	// True for kretprobes/uretprobes.
-	ret bool
+	// The event type determines the types of programs that can be attached.
+	typ perfEventType
 
-	fd       *internal.FD
-	progType ebpf.ProgramType
+	fd *internal.FD
 }
 
 func (pe *perfEvent) isLink() {}
@@ -117,13 +130,18 @@ func (pe *perfEvent) Close() error {
 		return fmt.Errorf("closing perf event fd: %w", err)
 	}
 
-	switch t := pe.progType; t {
-	case ebpf.Kprobe:
-		// For kprobes created using tracefs, clean up the <tracefs>/kprobe_events entry.
+	switch pe.typ {
+	case kprobeEvent, kretprobeEvent:
+		// Clean up kprobe tracefs entry.
+		if pe.tracefsID != 0 {
+			return closeTraceFSProbeEvent(kprobeType, pe.group, pe.name)
+		}
+	case uprobeEvent, uretprobeEvent:
+		// Clean up uprobe tracefs entry.
 		if pe.tracefsID != 0 {
-			return closeTraceFSKprobeEvent(pe.group, pe.name)
+			return closeTraceFSProbeEvent(uprobeType, pe.group, pe.name)
 		}
-	case ebpf.TracePoint:
+	case tracepointEvent:
 		// Tracepoint trace events don't hold any extra resources.
 		return nil
 	}
@@ -141,12 +159,21 @@ func (pe *perfEvent) attach(prog *ebpf.Program) error {
 	if pe.fd == nil {
 		return errors.New("cannot attach to nil perf event")
 	}
-	if t := prog.Type(); t != pe.progType {
-		return fmt.Errorf("invalid program type (expected %s): %s", pe.progType, t)
-	}
 	if prog.FD() < 0 {
 		return fmt.Errorf("invalid program: %w", internal.ErrClosedFd)
 	}
+	switch pe.typ {
+	case kprobeEvent, kretprobeEvent, uprobeEvent, uretprobeEvent:
+		if t := prog.Type(); t != ebpf.Kprobe {
+			return fmt.Errorf("invalid program type (expected %s): %s", ebpf.Kprobe, t)
+		}
+	case tracepointEvent:
+		if t := prog.Type(); t != ebpf.TracePoint {
+			return fmt.Errorf("invalid program type (expected %s): %s", ebpf.TracePoint, t)
+		}
+	default:
+		return fmt.Errorf("unknown perf event type: %d", pe.typ)
+	}
 
 	// The ioctl below will fail when the fd is invalid.
 	kfd, _ := pe.fd.Value()
@@ -180,8 +207,8 @@ func unsafeStringPtr(str string) (unsafe.Pointer, error) {
 // group and name must be alphanumeric or underscore, as required by the kernel.
 func getTraceEventID(group, name string) (uint64, error) {
 	tid, err := uint64FromFile(tracefsPath, "events", group, name, "id")
-	if errors.Is(err, ErrNotSupported) {
-		return 0, fmt.Errorf("trace event %s/%s: %w", group, name, ErrNotSupported)
+	if errors.Is(err, os.ErrNotExist) {
+		return 0, fmt.Errorf("trace event %s/%s: %w", group, name, os.ErrNotExist)
 	}
 	if err != nil {
 		return 0, fmt.Errorf("reading trace event ID of %s/%s: %w", group, name, err)
@@ -192,20 +219,22 @@ func getTraceEventID(group, name string) (uint64, error) {
 
 // getPMUEventType reads a Performance Monitoring Unit's type (numeric identifier)
 // from /sys/bus/event_source/devices/<pmu>/type.
-func getPMUEventType(pmu string) (uint64, error) {
-	et, err := uint64FromFile("/sys/bus/event_source/devices", pmu, "type")
-	if errors.Is(err, ErrNotSupported) {
-		return 0, fmt.Errorf("pmu type %s: %w", pmu, ErrNotSupported)
+//
+// Returns ErrNotSupported if the pmu type is not supported.
+func getPMUEventType(typ probeType) (uint64, error) {
+	et, err := uint64FromFile("/sys/bus/event_source/devices", typ.String(), "type")
+	if errors.Is(err, os.ErrNotExist) {
+		return 0, fmt.Errorf("pmu type %s: %w", typ, ErrNotSupported)
 	}
 	if err != nil {
-		return 0, fmt.Errorf("reading pmu type %s: %w", pmu, err)
+		return 0, fmt.Errorf("reading pmu type %s: %w", typ, err)
 	}
 
 	return et, nil
 }
 
 // openTracepointPerfEvent opens a tracepoint-type perf event. System-wide
-// kprobes created by writing to <tracefs>/kprobe_events are tracepoints
+// [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints
 // behind the scenes, and can be attached to using these perf events.
 func openTracepointPerfEvent(tid uint64) (*internal.FD, error) {
 	attr := unix.PerfEventAttr{
@@ -228,22 +257,13 @@ func openTracepointPerfEvent(tid uint64) (*internal.FD, error) {
 // and joined onto base. Returns error if base no longer prefixes the path after
 // joining all components.
 func uint64FromFile(base string, path ...string) (uint64, error) {
-
-	// Resolve leaf path separately for error feedback. Makes the join onto
-	// base more readable (can't mix with variadic args).
 	l := filepath.Join(path...)
-
 	p := filepath.Join(base, l)
 	if !strings.HasPrefix(p, base) {
 		return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
 	}
 
 	data, err := ioutil.ReadFile(p)
-	if os.IsNotExist(err) {
-		// Only echo leaf path, the base path can be prepended at the call site
-		// if more verbosity is required.
-		return 0, fmt.Errorf("symbol %s: %w", l, ErrNotSupported)
-	}
 	if err != nil {
 		return 0, fmt.Errorf("reading file %s: %w", p, err)
 	}

+ 25 - 0
vendor/github.com/cilium/ebpf/link/platform.go

@@ -0,0 +1,25 @@
+package link
+
+import (
+	"fmt"
+	"runtime"
+)
+
+func platformPrefix(symbol string) string {
+
+	prefix := runtime.GOARCH
+
+	// per https://github.com/golang/go/blob/master/src/go/build/syslist.go
+	switch prefix {
+	case "386":
+		prefix = "ia32"
+	case "amd64", "amd64p32":
+		prefix = "x64"
+	case "arm64", "arm64be":
+		prefix = "arm64"
+	default:
+		return symbol
+	}
+
+	return fmt.Sprintf("__%s_%s", prefix, symbol)
+}

+ 2 - 2
vendor/github.com/cilium/ebpf/link/program.go

@@ -43,7 +43,7 @@ func RawAttachProgram(opts RawAttachProgramOptions) error {
 	}
 
 	if err := internal.BPFProgAttach(&attr); err != nil {
-		return fmt.Errorf("can't attach program: %s", err)
+		return fmt.Errorf("can't attach program: %w", err)
 	}
 	return nil
 }
@@ -69,7 +69,7 @@ func RawDetachProgram(opts RawDetachProgramOptions) error {
 		AttachType:  uint32(opts.Attach),
 	}
 	if err := internal.BPFProgDetach(&attr); err != nil {
-		return fmt.Errorf("can't detach program: %s", err)
+		return fmt.Errorf("can't detach program: %w", err)
 	}
 
 	return nil

+ 17 - 0
vendor/github.com/cilium/ebpf/link/syscalls.go

@@ -102,6 +102,23 @@ func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) {
 	return internal.NewFD(uint32(ptr)), nil
 }
 
+type bpfLinkCreateIterAttr struct {
+	prog_fd       uint32
+	target_fd     uint32
+	attach_type   ebpf.AttachType
+	flags         uint32
+	iter_info     internal.Pointer
+	iter_info_len uint32
+}
+
+func bpfLinkCreateIter(attr *bpfLinkCreateIterAttr) (*internal.FD, error) {
+	ptr, err := internal.BPF(internal.BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
+	if err != nil {
+		return nil, err
+	}
+	return internal.NewFD(uint32(ptr)), nil
+}
+
 type bpfLinkUpdateAttr struct {
 	linkFd    uint32
 	newProgFd uint32

+ 2 - 2
vendor/github.com/cilium/ebpf/link/tracepoint.go

@@ -11,7 +11,7 @@ import (
 // tracepoints. The top-level directory is the group, the event's subdirectory
 // is the name. Example:
 //
-//	Tracepoint("syscalls", "sys_enter_fork")
+//	Tracepoint("syscalls", "sys_enter_fork", prog)
 //
 // Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is
 // only possible as of kernel 4.14 (commit cf5f5ce).
@@ -44,7 +44,7 @@ func Tracepoint(group, name string, prog *ebpf.Program) (Link, error) {
 		tracefsID: tid,
 		group:     group,
 		name:      name,
-		progType:  ebpf.TracePoint,
+		typ:       tracepointEvent,
 	}
 
 	if err := pe.attach(prog); err != nil {

+ 237 - 0
vendor/github.com/cilium/ebpf/link/uprobe.go

@@ -0,0 +1,237 @@
+package link
+
+import (
+	"debug/elf"
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+	"sync"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/internal"
+)
+
+var (
+	uprobeEventsPath = filepath.Join(tracefsPath, "uprobe_events")
+
+	// rgxUprobeSymbol is used to strip invalid characters from the uprobe symbol
+	// as they are not allowed to be used as the EVENT token in tracefs.
+	rgxUprobeSymbol = regexp.MustCompile("[^a-zA-Z0-9]+")
+
+	uprobeRetprobeBit = struct {
+		once  sync.Once
+		value uint64
+		err   error
+	}{}
+)
+
+// Executable defines an executable program on the filesystem.
+type Executable struct {
+	// Path of the executable on the filesystem.
+	path string
+	// Parsed ELF symbols and dynamic symbols.
+	symbols map[string]elf.Symbol
+}
+
+// UprobeOptions defines additional parameters that will be used
+// when loading Uprobes.
+type UprobeOptions struct {
+	// Symbol offset. Must be provided in case of external symbols (shared libs).
+	// If set, overrides the offset eventually parsed from the executable.
+	Offset uint64
+}
+
+// To open a new Executable, use:
+//
+//	OpenExecutable("/bin/bash")
+//
+// The returned value can then be used to open Uprobe(s).
+func OpenExecutable(path string) (*Executable, error) {
+	if path == "" {
+		return nil, fmt.Errorf("path cannot be empty")
+	}
+
+	f, err := os.Open(path)
+	if err != nil {
+		return nil, fmt.Errorf("open file '%s': %w", path, err)
+	}
+	defer f.Close()
+
+	se, err := internal.NewSafeELFFile(f)
+	if err != nil {
+		return nil, fmt.Errorf("parse ELF file: %w", err)
+	}
+
+	var ex = Executable{
+		path:    path,
+		symbols: make(map[string]elf.Symbol),
+	}
+	if err := ex.addSymbols(se.Symbols); err != nil {
+		return nil, err
+	}
+
+	if err := ex.addSymbols(se.DynamicSymbols); err != nil {
+		return nil, err
+	}
+
+	return &ex, nil
+}
+
+func (ex *Executable) addSymbols(f func() ([]elf.Symbol, error)) error {
+	// elf.Symbols and elf.DynamicSymbols return ErrNoSymbols if the section is not found.
+	syms, err := f()
+	if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
+		return err
+	}
+	for _, s := range syms {
+		if elf.ST_TYPE(s.Info) != elf.STT_FUNC {
+			// Symbol not associated with a function or other executable code.
+			continue
+		}
+		ex.symbols[s.Name] = s
+	}
+	return nil
+}
+
+func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) {
+	if s, ok := ex.symbols[symbol]; ok {
+		return &s, nil
+	}
+	return nil, fmt.Errorf("symbol %s not found", symbol)
+}
+
+// Uprobe attaches the given eBPF program to a perf event that fires when the
+// given symbol starts executing in the given Executable.
+// For example, /bin/bash::main():
+//
+//  ex, _ = OpenExecutable("/bin/bash")
+//  ex.Uprobe("main", prog, nil)
+//
+// When using symbols which belongs to shared libraries,
+// an offset must be provided via options:
+//
+//  ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
+//
+// The resulting Link must be Closed during program shutdown to avoid leaking
+// system resources. Functions provided by shared libraries can currently not
+// be traced and will result in an ErrNotSupported.
+func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
+	u, err := ex.uprobe(symbol, prog, opts, false)
+	if err != nil {
+		return nil, err
+	}
+
+	err = u.attach(prog)
+	if err != nil {
+		u.Close()
+		return nil, err
+	}
+
+	return u, nil
+}
+
+// Uretprobe attaches the given eBPF program to a perf event that fires right
+// before the given symbol exits. For example, /bin/bash::main():
+//
+//  ex, _ = OpenExecutable("/bin/bash")
+//  ex.Uretprobe("main", prog, nil)
+//
+// When using symbols which belongs to shared libraries,
+// an offset must be provided via options:
+//
+//  ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
+//
+// The resulting Link must be Closed during program shutdown to avoid leaking
+// system resources. Functions provided by shared libraries can currently not
+// be traced and will result in an ErrNotSupported.
+func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
+	u, err := ex.uprobe(symbol, prog, opts, true)
+	if err != nil {
+		return nil, err
+	}
+
+	err = u.attach(prog)
+	if err != nil {
+		u.Close()
+		return nil, err
+	}
+
+	return u, nil
+}
+
+// uprobe opens a perf event for the given binary/symbol and attaches prog to it.
+// If ret is true, create a uretprobe.
+func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions, ret bool) (*perfEvent, error) {
+	if prog == nil {
+		return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
+	}
+	if prog.Type() != ebpf.Kprobe {
+		return nil, fmt.Errorf("eBPF program type %s is not Kprobe: %w", prog.Type(), errInvalidInput)
+	}
+
+	var offset uint64
+	if opts != nil && opts.Offset != 0 {
+		offset = opts.Offset
+	} else {
+		sym, err := ex.symbol(symbol)
+		if err != nil {
+			return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, err)
+		}
+
+		// Symbols with location 0 from section undef are shared library calls and
+		// are relocated before the binary is executed. Dynamic linking is not
+		// implemented by the library, so mark this as unsupported for now.
+		if sym.Section == elf.SHN_UNDEF && sym.Value == 0 {
+			return nil, fmt.Errorf("cannot resolve %s library call '%s', "+
+				"consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported)
+		}
+
+		offset = sym.Value
+	}
+
+	// Use uprobe PMU if the kernel has it available.
+	tp, err := pmuUprobe(symbol, ex.path, offset, ret)
+	if err == nil {
+		return tp, nil
+	}
+	if err != nil && !errors.Is(err, ErrNotSupported) {
+		return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err)
+	}
+
+	// Use tracefs if uprobe PMU is missing.
+	tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, ret)
+	if err != nil {
+		return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err)
+	}
+
+	return tp, nil
+}
+
+// pmuUprobe opens a perf event based on the uprobe PMU.
+func pmuUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
+	return pmuProbe(uprobeType, symbol, path, offset, ret)
+}
+
+// tracefsUprobe creates a Uprobe tracefs entry.
+func tracefsUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
+	return tracefsProbe(uprobeType, symbol, path, offset, ret)
+}
+
+// uprobeSanitizedSymbol replaces every invalid characted for the tracefs api with an underscore.
+func uprobeSanitizedSymbol(symbol string) string {
+	return rgxUprobeSymbol.ReplaceAllString(symbol, "_")
+}
+
+// uprobePathOffset creates the PATH:OFFSET token for the tracefs api.
+func uprobePathOffset(path string, offset uint64) string {
+	return fmt.Sprintf("%s:%#x", path, offset)
+}
+
+func uretprobeBit() (uint64, error) {
+	uprobeRetprobeBit.once.Do(func() {
+		uprobeRetprobeBit.value, uprobeRetprobeBit.err = determineRetprobeBit(uprobeType)
+	})
+	return uprobeRetprobeBit.value, uprobeRetprobeBit.err
+}

+ 9 - 2
vendor/github.com/cilium/ebpf/linker.go

@@ -108,12 +108,16 @@ func fixupJumpsAndCalls(insns asm.Instructions) error {
 		offset := iter.Offset
 		ins := iter.Ins
 
+		if ins.Reference == "" {
+			continue
+		}
+
 		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 %q", i, ins.Reference)
+				return fmt.Errorf("call at %d: reference to missing symbol %q", i, ins.Reference)
 			}
 
 			ins.Constant = int64(callOffset - offset - 1)
@@ -122,10 +126,13 @@ func fixupJumpsAndCalls(insns asm.Instructions) error {
 			// Rewrite jump to label
 			jumpOffset, ok := symbolOffsets[ins.Reference]
 			if !ok {
-				return fmt.Errorf("instruction %d: reference to missing symbol %q", i, ins.Reference)
+				return fmt.Errorf("jump at %d: reference to missing symbol %q", i, ins.Reference)
 			}
 
 			ins.Offset = int16(jumpOffset - offset - 1)
+
+		case ins.IsLoadFromMap() && ins.MapPtr() == -1:
+			return fmt.Errorf("map %s: %w", ins.Reference, errUnsatisfiedReference)
 		}
 	}
 

+ 58 - 28
vendor/github.com/cilium/ebpf/map.go

@@ -18,6 +18,7 @@ var (
 	ErrKeyNotExist      = errors.New("key does not exist")
 	ErrKeyExist         = errors.New("key already exists")
 	ErrIterationAborted = errors.New("iteration aborted")
+	ErrMapIncompatible  = errors.New("map's spec is incompatible with pinned map")
 )
 
 // MapOptions control loading a map into the kernel.
@@ -87,6 +88,23 @@ func (ms *MapSpec) Copy() *MapSpec {
 	return &cpy
 }
 
+func (ms *MapSpec) clampPerfEventArraySize() error {
+	if ms.Type != PerfEventArray {
+		return nil
+	}
+
+	n, err := internal.PossibleCPUs()
+	if err != nil {
+		return fmt.Errorf("perf event array: %w", err)
+	}
+
+	if n := uint32(n); ms.MaxEntries > n {
+		ms.MaxEntries = n
+	}
+
+	return nil
+}
+
 // MapKV is used to initialize the contents of a Map.
 type MapKV struct {
 	Key   interface{}
@@ -96,19 +114,19 @@ type MapKV struct {
 func (ms *MapSpec) checkCompatibility(m *Map) error {
 	switch {
 	case m.typ != ms.Type:
-		return fmt.Errorf("expected type %v, got %v", ms.Type, m.typ)
+		return fmt.Errorf("expected type %v, got %v: %w", ms.Type, m.typ, ErrMapIncompatible)
 
 	case m.keySize != ms.KeySize:
-		return fmt.Errorf("expected key size %v, got %v", ms.KeySize, m.keySize)
+		return fmt.Errorf("expected key size %v, got %v: %w", ms.KeySize, m.keySize, ErrMapIncompatible)
 
 	case m.valueSize != ms.ValueSize:
-		return fmt.Errorf("expected value size %v, got %v", ms.ValueSize, m.valueSize)
+		return fmt.Errorf("expected value size %v, got %v: %w", ms.ValueSize, m.valueSize, ErrMapIncompatible)
 
 	case m.maxEntries != ms.MaxEntries:
-		return fmt.Errorf("expected max entries %v, got %v", ms.MaxEntries, m.maxEntries)
+		return fmt.Errorf("expected max entries %v, got %v: %w", ms.MaxEntries, m.maxEntries, ErrMapIncompatible)
 
 	case m.flags != ms.Flags:
-		return fmt.Errorf("expected flags %v, got %v", ms.Flags, m.flags)
+		return fmt.Errorf("expected flags %v, got %v: %w", ms.Flags, m.flags, ErrMapIncompatible)
 	}
 	return nil
 }
@@ -171,14 +189,16 @@ func NewMap(spec *MapSpec) (*Map, error) {
 // 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 NewMapWithOptions.
+//
+// May return an error wrapping ErrMapIncompatible.
 func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
-	btfs := make(btfHandleCache)
-	defer btfs.close()
+	handles := newHandleCache()
+	defer handles.close()
 
-	return newMapWithOptions(spec, opts, btfs)
+	return newMapWithOptions(spec, opts, handles)
 }
 
-func newMapWithOptions(spec *MapSpec, opts MapOptions, btfs btfHandleCache) (_ *Map, err error) {
+func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) {
 	closeOnError := func(c io.Closer) {
 		if err != nil {
 			c.Close()
@@ -202,7 +222,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, btfs btfHandleCache) (_ *
 		defer closeOnError(m)
 
 		if err := spec.checkCompatibility(m); err != nil {
-			return nil, fmt.Errorf("use pinned map %s: %s", spec.Name, err)
+			return nil, fmt.Errorf("use pinned map %s: %w", spec.Name, err)
 		}
 
 		return m, nil
@@ -211,7 +231,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, btfs btfHandleCache) (_ *
 		// Nothing to do here
 
 	default:
-		return nil, fmt.Errorf("unsupported pin type %d", int(spec.Pinning))
+		return nil, fmt.Errorf("pin type %d: %w", int(spec.Pinning), ErrNotSupported)
 	}
 
 	var innerFd *internal.FD
@@ -224,7 +244,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, btfs btfHandleCache) (_ *
 			return nil, errors.New("inner maps cannot be pinned")
 		}
 
-		template, err := createMap(spec.InnerMap, nil, opts, btfs)
+		template, err := createMap(spec.InnerMap, nil, opts, handles)
 		if err != nil {
 			return nil, err
 		}
@@ -233,7 +253,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, btfs btfHandleCache) (_ *
 		innerFd = template.fd
 	}
 
-	m, err := createMap(spec, innerFd, opts, btfs)
+	m, err := createMap(spec, innerFd, opts, handles)
 	if err != nil {
 		return nil, err
 	}
@@ -249,7 +269,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, btfs btfHandleCache) (_ *
 	return m, nil
 }
 
-func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, btfs btfHandleCache) (_ *Map, err error) {
+func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) {
 	closeOnError := func(closer io.Closer) {
 		if err != nil {
 			closer.Close()
@@ -296,44 +316,54 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, btfs btfHandl
 			return nil, fmt.Errorf("map create: %w", err)
 		}
 	}
+	if spec.Flags&unix.BPF_F_MMAPABLE > 0 {
+		if err := haveMmapableMaps(); err != nil {
+			return nil, fmt.Errorf("map create: %w", err)
+		}
+	}
+	if spec.Flags&unix.BPF_F_INNER_MAP > 0 {
+		if err := haveInnerMaps(); err != nil {
+			return nil, fmt.Errorf("map create: %w", err)
+		}
+	}
 
-	attr := bpfMapCreateAttr{
-		mapType:    spec.Type,
-		keySize:    spec.KeySize,
-		valueSize:  spec.ValueSize,
-		maxEntries: spec.MaxEntries,
-		flags:      spec.Flags,
-		numaNode:   spec.NumaNode,
+	attr := internal.BPFMapCreateAttr{
+		MapType:    uint32(spec.Type),
+		KeySize:    spec.KeySize,
+		ValueSize:  spec.ValueSize,
+		MaxEntries: spec.MaxEntries,
+		Flags:      spec.Flags,
+		NumaNode:   spec.NumaNode,
 	}
 
 	if inner != nil {
 		var err error
-		attr.innerMapFd, err = inner.Value()
+		attr.InnerMapFd, err = inner.Value()
 		if err != nil {
 			return nil, fmt.Errorf("map create: %w", err)
 		}
 	}
 
 	if haveObjName() == nil {
-		attr.mapName = newBPFObjName(spec.Name)
+		attr.MapName = internal.NewBPFObjName(spec.Name)
 	}
 
 	var btfDisabled bool
 	if spec.BTF != nil {
-		handle, err := btfs.load(btf.MapSpec(spec.BTF))
+		handle, err := handles.btfHandle(btf.MapSpec(spec.BTF))
 		btfDisabled = errors.Is(err, btf.ErrNotSupported)
 		if err != nil && !btfDisabled {
 			return nil, fmt.Errorf("load BTF: %w", err)
 		}
 
 		if handle != nil {
-			attr.btfFd = uint32(handle.FD())
-			attr.btfKeyTypeID = btf.MapKey(spec.BTF).ID()
-			attr.btfValueTypeID = btf.MapValue(spec.BTF).ID()
+			attr.BTFFd = uint32(handle.FD())
+			attr.BTFKeyTypeID = uint32(btf.MapKey(spec.BTF).ID())
+			attr.BTFValueTypeID = uint32(btf.MapValue(spec.BTF).ID())
 		}
 	}
 
-	fd, err := bpfMapCreate(&attr)
+	fd, err := internal.BPFMapCreate(&attr)
 	if err != nil {
 		if errors.Is(err, unix.EPERM) {
 			return nil, fmt.Errorf("map create: RLIMIT_MEMLOCK may be too low: %w", err)

+ 82 - 69
vendor/github.com/cilium/ebpf/prog.go

@@ -5,6 +5,7 @@ import (
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"io"
 	"math"
 	"path/filepath"
 	"strings"
@@ -19,6 +20,8 @@ import (
 // ErrNotSupported is returned whenever the kernel doesn't support a feature.
 var ErrNotSupported = internal.ErrNotSupported
 
+var errUnsatisfiedReference = errors.New("unsatisfied reference")
+
 // ProgramID represents the unique ID of an eBPF program.
 type ProgramID uint32
 
@@ -41,6 +44,12 @@ type ProgramOptions struct {
 	// Controls the output buffer size for the verifier. Defaults to
 	// DefaultVerifierLogSize.
 	LogSize int
+	// An ELF containing the target BTF for this program. It is used both to
+	// find the correct function to trace and to apply CO-RE relocations.
+	// This is useful in environments where the kernel BTF is not available
+	// (containers) or where it is in a non-standard location. Defaults to
+	// use the kernel BTF from a well-known location.
+	TargetBTF io.ReaderAt
 }
 
 // ProgramSpec defines a Program.
@@ -125,21 +134,21 @@ 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) {
-	btfs := make(btfHandleCache)
-	defer btfs.close()
+	handles := newHandleCache()
+	defer handles.close()
 
-	return newProgramWithOptions(spec, opts, btfs)
+	prog, err := newProgramWithOptions(spec, opts, handles)
+	if errors.Is(err, errUnsatisfiedReference) {
+		return nil, fmt.Errorf("cannot load program without loading its whole collection: %w", err)
+	}
+	return prog, err
 }
 
-func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, btfs btfHandleCache) (*Program, error) {
+func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) {
 	if len(spec.Instructions) == 0 {
 		return nil, errors.New("Instructions cannot be empty")
 	}
 
-	if len(spec.License) == 0 {
-		return nil, errors.New("License cannot be empty")
-	}
-
 	if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
 		return nil, fmt.Errorf("can't load %s program on %s", spec.ByteOrder, internal.NativeEndian)
 	}
@@ -157,44 +166,36 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, btfs btfHandl
 		kv = v.Kernel()
 	}
 
-	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 := insns.Marshal(buf, internal.NativeEndian)
-	if err != nil {
-		return nil, err
-	}
-
-	bytecode := buf.Bytes()
-	insCount := uint32(len(bytecode) / asm.InstructionSize)
 	attr := &bpfProgLoadAttr{
 		progType:           spec.Type,
 		progFlags:          spec.Flags,
 		expectedAttachType: spec.AttachType,
-		insCount:           insCount,
-		instructions:       internal.NewSlicePointer(bytecode),
 		license:            internal.NewStringPointer(spec.License),
 		kernelVersion:      kv,
 	}
 
 	if haveObjName() == nil {
-		attr.progName = newBPFObjName(spec.Name)
+		attr.progName = internal.NewBPFObjName(spec.Name)
+	}
+
+	var err error
+	var targetBTF *btf.Spec
+	if opts.TargetBTF != nil {
+		targetBTF, err = handles.btfSpec(opts.TargetBTF)
+		if err != nil {
+			return nil, fmt.Errorf("load target BTF: %w", err)
+		}
 	}
 
 	var btfDisabled bool
+	var core btf.COREFixups
 	if spec.BTF != nil {
-		if relos, err := btf.ProgramRelocations(spec.BTF, nil); err != nil {
-			return nil, fmt.Errorf("CO-RE relocations: %s", err)
-		} else if len(relos) > 0 {
-			return nil, fmt.Errorf("applying CO-RE relocations: %w", ErrNotSupported)
+		core, err = btf.ProgramFixups(spec.BTF, targetBTF)
+		if err != nil {
+			return nil, fmt.Errorf("CO-RE relocations: %w", err)
 		}
 
-		handle, err := btfs.load(btf.ProgramSpec(spec.BTF))
+		handle, err := handles.btfHandle(btf.ProgramSpec(spec.BTF))
 		btfDisabled = errors.Is(err, btf.ErrNotSupported)
 		if err != nil && !btfDisabled {
 			return nil, fmt.Errorf("load BTF: %w", err)
@@ -221,8 +222,27 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, btfs btfHandl
 		}
 	}
 
+	insns, err := core.Apply(spec.Instructions)
+	if err != nil {
+		return nil, fmt.Errorf("CO-RE fixup: %w", err)
+	}
+
+	if err := fixupJumpsAndCalls(insns); err != nil {
+		return nil, err
+	}
+
+	buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
+	err = insns.Marshal(buf, internal.NativeEndian)
+	if err != nil {
+		return nil, err
+	}
+
+	bytecode := buf.Bytes()
+	attr.instructions = internal.NewSlicePointer(bytecode)
+	attr.insCount = uint32(len(bytecode) / asm.InstructionSize)
+
 	if spec.AttachTo != "" {
-		target, err := resolveBTFType(spec.AttachTo, spec.Type, spec.AttachType)
+		target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType)
 		if err != nil {
 			return nil, err
 		}
@@ -250,7 +270,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, btfs btfHandl
 	}
 
 	logErr := err
-	if opts.LogLevel == 0 {
+	if opts.LogLevel == 0 && opts.LogSize >= 0 {
 		// Re-run with the verifier enabled to get better error messages.
 		logBuf = make([]byte, logSize)
 		attr.logLevel = 1
@@ -664,52 +684,45 @@ func (p *Program) ID() (ProgramID, error) {
 	return ProgramID(info.id), nil
 }
 
-func findKernelType(name string, typ btf.Type) error {
-	kernel, err := btf.LoadKernelSpec()
-	if err != nil {
-		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) {
+func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
 	type match struct {
 		p ProgramType
 		a AttachType
 	}
 
-	target := match{progType, attachType}
-	switch target {
+	var target btf.Type
+	var typeName, featureName string
+	switch (match{progType, attachType}) {
 	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
+		target = new(btf.Func)
+		typeName = "bpf_lsm_" + name
+		featureName = name + " LSM hook"
 
 	case match{Tracing, AttachTraceIter}:
-		var target btf.Func
-		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
+		target = new(btf.Func)
+		typeName = "bpf_iter_" + name
+		featureName = name + " iterator"
 
 	default:
 		return nil, nil
 	}
+
+	if kernel == nil {
+		var err error
+		kernel, err = btf.LoadKernelSpec()
+		if err != nil {
+			return nil, fmt.Errorf("load kernel spec: %w", err)
+		}
+	}
+
+	err := kernel.FindType(typeName, target)
+	if errors.Is(err, btf.ErrNotFound) {
+		return nil, &internal.UnsupportedFeatureError{
+			Name: featureName,
+		}
+	}
+	if err != nil {
+		return nil, fmt.Errorf("resolve BTF for %s: %w", featureName, err)
+	}
+	return target, nil
 }

+ 85 - 96
vendor/github.com/cilium/ebpf/syscalls.go

@@ -3,6 +3,7 @@ package ebpf
 import (
 	"errors"
 	"fmt"
+	"os"
 	"unsafe"
 
 	"github.com/cilium/ebpf/internal"
@@ -10,19 +11,10 @@ import (
 	"github.com/cilium/ebpf/internal/unix"
 )
 
-// Generic errors returned by BPF syscalls.
-var ErrNotExist = errors.New("requested object does not exist")
-
-// bpfObjName is a null-terminated string made up of
-// 'A-Za-z0-9_' characters.
-type bpfObjName [unix.BPF_OBJ_NAME_LEN]byte
-
-// newBPFObjName truncates the result if it is too long.
-func newBPFObjName(name string) bpfObjName {
-	var result bpfObjName
-	copy(result[:unix.BPF_OBJ_NAME_LEN-1], name)
-	return result
-}
+// ErrNotExist is returned when loading a non-existing map or program.
+//
+// Deprecated: use os.ErrNotExist instead.
+var ErrNotExist = os.ErrNotExist
 
 // invalidBPFObjNameChar returns true if char may not appear in
 // a BPF object name.
@@ -45,21 +37,6 @@ 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
-	mapIfIndex     uint32
-	btfFd          uint32
-	btfKeyTypeID   btf.TypeID
-	btfValueTypeID btf.TypeID
-}
-
 type bpfMapOpAttr struct {
 	mapFd   uint32
 	padding uint32
@@ -86,10 +63,10 @@ type bpfMapInfo struct {
 	value_size                uint32
 	max_entries               uint32
 	map_flags                 uint32
-	name                      bpfObjName // since 4.15 ad5b177bd73f
-	ifindex                   uint32     // since 4.16 52775b33bb50
-	btf_vmlinux_value_type_id uint32     // since 5.6  85d33df357b6
-	netns_dev                 uint64     // since 4.16 52775b33bb50
+	name                      internal.BPFObjName // since 4.15 ad5b177bd73f
+	ifindex                   uint32              // since 4.16 52775b33bb50
+	btf_vmlinux_value_type_id uint32              // since 5.6  85d33df357b6
+	netns_dev                 uint64              // since 4.16 52775b33bb50
 	netns_ino                 uint64
 	btf_id                    uint32 // since 4.18 78958fca7ead
 	btf_key_type_id           uint32 // since 4.18 9b2cf328b2ec
@@ -104,11 +81,11 @@ type bpfProgLoadAttr struct {
 	logLevel           uint32
 	logSize            uint32
 	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
+	kernelVersion      uint32              // since 4.1  2541517c32be
+	progFlags          uint32              // since 4.11 e07b98d9bffe
+	progName           internal.BPFObjName // since 4.15 067cae47771c
+	progIfIndex        uint32              // since 4.15 1f6f4cb7ba21
+	expectedAttachType AttachType          // since 4.17 5e43f899b03a
 	progBTFFd          uint32
 	funcInfoRecSize    uint32
 	funcInfo           internal.Pointer
@@ -132,7 +109,7 @@ type bpfProgInfo struct {
 	created_by_uid           uint32
 	nr_map_ids               uint32
 	map_ids                  internal.Pointer
-	name                     bpfObjName // since 4.15 067cae47771c
+	name                     internal.BPFObjName // since 4.15 067cae47771c
 	ifindex                  uint32
 	gpl_compatible           uint32
 	netns_dev                uint64
@@ -188,7 +165,7 @@ func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
 		fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
 		// As of ~4.20 the verifier can be interrupted by a signal,
 		// and returns EAGAIN in that case.
-		if err == unix.EAGAIN {
+		if errors.Is(err, unix.EAGAIN) {
 			continue
 		}
 
@@ -205,23 +182,14 @@ func bpfProgTestRun(attr *bpfProgTestRunAttr) error {
 	return err
 }
 
-func bpfMapCreate(attr *bpfMapCreateAttr) (*internal.FD, error) {
-	fd, err := internal.BPF(internal.BPF_MAP_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
-	if err != nil {
-		return nil, err
-	}
-
-	return internal.NewFD(uint32(fd)), nil
-}
-
 var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() error {
-	_, err := bpfMapCreate(&bpfMapCreateAttr{
-		mapType:    ArrayOfMaps,
-		keySize:    4,
-		valueSize:  4,
-		maxEntries: 1,
+	_, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{
+		MapType:    uint32(ArrayOfMaps),
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: 1,
 		// Invalid file descriptor.
-		innerMapFd: ^uint32(0),
+		InnerMapFd: ^uint32(0),
 	})
 	if errors.Is(err, unix.EINVAL) {
 		return internal.ErrNotSupported
@@ -235,12 +203,44 @@ var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() error {
 var haveMapMutabilityModifiers = internal.FeatureTest("read- and write-only maps", "5.2", func() error {
 	// This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since
 	// BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check.
-	m, err := bpfMapCreate(&bpfMapCreateAttr{
-		mapType:    Array,
-		keySize:    4,
-		valueSize:  4,
-		maxEntries: 1,
-		flags:      unix.BPF_F_RDONLY_PROG,
+	m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{
+		MapType:    uint32(Array),
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: 1,
+		Flags:      unix.BPF_F_RDONLY_PROG,
+	})
+	if err != nil {
+		return internal.ErrNotSupported
+	}
+	_ = m.Close()
+	return nil
+})
+
+var haveMmapableMaps = internal.FeatureTest("mmapable maps", "5.5", func() error {
+	// This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps.
+	m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{
+		MapType:    uint32(Array),
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: 1,
+		Flags:      unix.BPF_F_MMAPABLE,
+	})
+	if err != nil {
+		return internal.ErrNotSupported
+	}
+	_ = m.Close()
+	return nil
+})
+
+var haveInnerMaps = internal.FeatureTest("inner maps", "5.10", func() error {
+	// This checks BPF_F_INNER_MAP, which appeared in 5.10.
+	m, err := internal.BPFMapCreate(&internal.BPFMapCreateAttr{
+		MapType:    uint32(Array),
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: 1,
+		Flags:      unix.BPF_F_INNER_MAP,
 	})
 	if err != nil {
 		return internal.ErrNotSupported
@@ -329,7 +329,7 @@ func objGetNextID(cmd internal.BPFCmd, start uint32) (uint32, error) {
 		startID: start,
 	}
 	_, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
-	return attr.nextID, wrapObjError(err)
+	return attr.nextID, err
 }
 
 func bpfMapBatch(cmd internal.BPFCmd, m *internal.FD, inBatch, outBatch, keys, values internal.Pointer, count uint32, opts *BatchOptions) (uint32, error) {
@@ -355,32 +355,21 @@ func bpfMapBatch(cmd internal.BPFCmd, m *internal.FD, inBatch, outBatch, keys, v
 	return attr.count, wrapMapError(err)
 }
 
-func wrapObjError(err error) error {
-	if err == nil {
-		return nil
-	}
-	if errors.Is(err, unix.ENOENT) {
-		return fmt.Errorf("%w", ErrNotExist)
-	}
-
-	return errors.New(err.Error())
-}
-
 func wrapMapError(err error) error {
 	if err == nil {
 		return nil
 	}
 
 	if errors.Is(err, unix.ENOENT) {
-		return ErrKeyNotExist
+		return internal.SyscallError(ErrKeyNotExist, unix.ENOENT)
 	}
 
 	if errors.Is(err, unix.EEXIST) {
-		return ErrKeyExist
+		return internal.SyscallError(ErrKeyExist, unix.EEXIST)
 	}
 
 	if errors.Is(err, unix.ENOTSUPP) {
-		return ErrNotSupported
+		return internal.SyscallError(ErrNotSupported, unix.ENOTSUPP)
 	}
 
 	return err
@@ -417,15 +406,15 @@ func bpfGetMapInfoByFD(fd *internal.FD) (*bpfMapInfo, error) {
 }
 
 var haveObjName = internal.FeatureTest("object names", "4.15", func() error {
-	attr := bpfMapCreateAttr{
-		mapType:    Array,
-		keySize:    4,
-		valueSize:  4,
-		maxEntries: 1,
-		mapName:    newBPFObjName("feature_test"),
+	attr := internal.BPFMapCreateAttr{
+		MapType:    uint32(Array),
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: 1,
+		MapName:    internal.NewBPFObjName("feature_test"),
 	}
 
-	fd, err := bpfMapCreate(&attr)
+	fd, err := internal.BPFMapCreate(&attr)
 	if err != nil {
 		return internal.ErrNotSupported
 	}
@@ -439,15 +428,15 @@ var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func()
 		return err
 	}
 
-	attr := bpfMapCreateAttr{
-		mapType:    Array,
-		keySize:    4,
-		valueSize:  4,
-		maxEntries: 1,
-		mapName:    newBPFObjName(".test"),
+	attr := internal.BPFMapCreateAttr{
+		MapType:    uint32(Array),
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: 1,
+		MapName:    internal.NewBPFObjName(".test"),
 	}
 
-	fd, err := bpfMapCreate(&attr)
+	fd, err := internal.BPFMapCreate(&attr)
 	if err != nil {
 		return internal.ErrNotSupported
 	}
@@ -458,14 +447,14 @@ var objNameAllowsDot = internal.FeatureTest("dot in object names", "5.2", func()
 
 var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error {
 	var maxEntries uint32 = 2
-	attr := bpfMapCreateAttr{
-		mapType:    Hash,
-		keySize:    4,
-		valueSize:  4,
-		maxEntries: maxEntries,
+	attr := internal.BPFMapCreateAttr{
+		MapType:    uint32(Hash),
+		KeySize:    4,
+		ValueSize:  4,
+		MaxEntries: maxEntries,
 	}
 
-	fd, err := bpfMapCreate(&attr)
+	fd, err := internal.BPFMapCreate(&attr)
 	if err != nil {
 		return internal.ErrNotSupported
 	}
@@ -487,5 +476,5 @@ func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) {
 		id: id,
 	}
 	ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
-	return internal.NewFD(uint32(ptr)), wrapObjError(err)
+	return internal.NewFD(uint32(ptr)), err
 }

+ 4 - 0
vendor/github.com/cilium/ebpf/types.go

@@ -85,6 +85,10 @@ const (
 	SkStorage
 	// DevMapHash - Hash-based indexing scheme for references to network devices.
 	DevMapHash
+	StructOpts
+	RingBuf
+	InodeStorage
+	TaskStorage
 )
 
 // hasPerCPUValue returns true if the Map stores a value per CPU.

+ 6 - 2
vendor/github.com/cilium/ebpf/types_string.go

@@ -34,11 +34,15 @@ func _() {
 	_ = x[Stack-23]
 	_ = x[SkStorage-24]
 	_ = x[DevMapHash-25]
+	_ = x[StructOpts-26]
+	_ = x[RingBuf-27]
+	_ = x[InodeStorage-28]
+	_ = x[TaskStorage-29]
 }
 
-const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHash"
+const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOptsRingBufInodeStorageTaskStorage"
 
-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}
+var _MapType_index = [...]uint16{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, 258, 265, 277, 288}
 
 func (i MapType) String() string {
 	if i >= MapType(len(_MapType_index)-1) {

+ 16 - 20
vendor/github.com/coreos/go-systemd/v22/dbus/dbus.go

@@ -111,14 +111,13 @@ type Conn struct {
 	}
 }
 
-// New establishes a connection to any available bus and authenticates.
-// Callers should call Close() when done with the connection.
-// Deprecated: use NewWithContext instead
+// Deprecated: use NewWithContext instead.
 func New() (*Conn, error) {
 	return NewWithContext(context.Background())
 }
 
-// NewWithContext same as New with context
+// NewWithContext establishes a connection to any available bus and authenticates.
+// Callers should call Close() when done with the connection.
 func NewWithContext(ctx context.Context) (*Conn, error) {
 	conn, err := NewSystemConnectionContext(ctx)
 	if err != nil && os.Geteuid() == 0 {
@@ -127,44 +126,41 @@ func NewWithContext(ctx context.Context) (*Conn, error) {
 	return conn, err
 }
 
-// NewSystemConnection establishes a connection to the system bus and authenticates.
-// Callers should call Close() when done with the connection
-// Deprecated: use NewSystemConnectionContext instead
+// Deprecated: use NewSystemConnectionContext instead.
 func NewSystemConnection() (*Conn, error) {
 	return NewSystemConnectionContext(context.Background())
 }
 
-// NewSystemConnectionContext same as NewSystemConnection with context
+// NewSystemConnectionContext establishes a connection to the system bus and authenticates.
+// Callers should call Close() when done with the connection.
 func NewSystemConnectionContext(ctx context.Context) (*Conn, error) {
 	return NewConnection(func() (*dbus.Conn, error) {
 		return dbusAuthHelloConnection(ctx, dbus.SystemBusPrivate)
 	})
 }
 
-// NewUserConnection establishes a connection to the session bus and
-// authenticates. This can be used to connect to systemd user instances.
-// Callers should call Close() when done with the connection.
-// Deprecated: use NewUserConnectionContext instead
+// Deprecated: use NewUserConnectionContext instead.
 func NewUserConnection() (*Conn, error) {
 	return NewUserConnectionContext(context.Background())
 }
 
-// NewUserConnectionContext same as NewUserConnection with context
+// NewUserConnectionContext establishes a connection to the session bus and
+// authenticates. This can be used to connect to systemd user instances.
+// Callers should call Close() when done with the connection.
 func NewUserConnectionContext(ctx context.Context) (*Conn, error) {
 	return NewConnection(func() (*dbus.Conn, error) {
 		return dbusAuthHelloConnection(ctx, dbus.SessionBusPrivate)
 	})
 }
 
-// NewSystemdConnection establishes a private, direct connection to systemd.
-// This can be used for communicating with systemd without a dbus daemon.
-// Callers should call Close() when done with the connection.
-// Deprecated: use NewSystemdConnectionContext instead
+// Deprecated: use NewSystemdConnectionContext instead.
 func NewSystemdConnection() (*Conn, error) {
 	return NewSystemdConnectionContext(context.Background())
 }
 
-// NewSystemdConnectionContext same as NewSystemdConnection with context
+// NewSystemdConnectionContext establishes a private, direct connection to systemd.
+// This can be used for communicating with systemd without a dbus daemon.
+// Callers should call Close() when done with the connection.
 func NewSystemdConnectionContext(ctx context.Context) (*Conn, error) {
 	return NewConnection(func() (*dbus.Conn, error) {
 		// We skip Hello when talking directly to systemd.
@@ -174,7 +170,7 @@ func NewSystemdConnectionContext(ctx context.Context) (*Conn, error) {
 	})
 }
 
-// Close closes an established connection
+// Close closes an established connection.
 func (c *Conn) Close() {
 	c.sysconn.Close()
 	c.sigconn.Close()
@@ -217,7 +213,7 @@ func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) {
 
 // GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager
 // interface. The value is returned in its string representation, as defined at
-// https://developer.gnome.org/glib/unstable/gvariant-text.html
+// https://developer.gnome.org/glib/unstable/gvariant-text.html.
 func (c *Conn) GetManagerProperty(prop string) (string, error) {
 	variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop)
 	if err != nil {

+ 142 - 161
vendor/github.com/coreos/go-systemd/v22/dbus/methods.go

@@ -73,7 +73,12 @@ func (c *Conn) startJob(ctx context.Context, ch chan<- string, job string, args
 	return jobID, nil
 }
 
-// StartUnit enqueues a start job and depending jobs, if any (unless otherwise
+// Deprecated: use StartUnitContext instead.
+func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
+	return c.StartUnitContext(context.Background(), name, mode, ch)
+}
+
+// StartUnitContext enqueues a start job and depending jobs, if any (unless otherwise
 // specified by the mode string).
 //
 // Takes the unit to activate, plus a mode string. The mode needs to be one of
@@ -103,137 +108,124 @@ func (c *Conn) startJob(ctx context.Context, ch chan<- string, job string, args
 // should not be considered authoritative.
 //
 // If an error does occur, it will be returned to the user alongside a job ID of 0.
-// Deprecated: use StartUnitContext instead
-func (c *Conn) StartUnit(name string, mode string, ch chan<- string) (int, error) {
-	return c.StartUnitContext(context.Background(), name, mode, ch)
-}
-
-// StartUnitContext same as StartUnit with context
 func (c *Conn) StartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StartUnit", name, mode)
 }
 
-// StopUnit is similar to StartUnit but stops the specified unit rather
-// than starting it.
-// Deprecated: use StopUnitContext instead
+// Deprecated: use StopUnitContext instead.
 func (c *Conn) StopUnit(name string, mode string, ch chan<- string) (int, error) {
 	return c.StopUnitContext(context.Background(), name, mode, ch)
 }
 
-// StopUnitContext same as StopUnit with context
+// StopUnitContext is similar to StartUnitContext, but stops the specified unit
+// rather than starting it.
 func (c *Conn) StopUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StopUnit", name, mode)
 }
 
-// ReloadUnit reloads a unit.  Reloading is done only if the unit is already running and fails otherwise.
-// Deprecated: use ReloadUnitContext instead
+// Deprecated: use ReloadUnitContext instead.
 func (c *Conn) ReloadUnit(name string, mode string, ch chan<- string) (int, error) {
 	return c.ReloadUnitContext(context.Background(), name, mode, ch)
 }
 
-// ReloadUnitContext same as ReloadUnit with context
+// ReloadUnitContext reloads a unit. Reloading is done only if the unit
+// is already running, and fails otherwise.
 func (c *Conn) ReloadUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadUnit", name, mode)
 }
 
-// RestartUnit restarts a service.  If a service is restarted that isn't
-// running it will be started.
-// Deprecated: use RestartUnitContext instead
+// Deprecated: use RestartUnitContext instead.
 func (c *Conn) RestartUnit(name string, mode string, ch chan<- string) (int, error) {
 	return c.RestartUnitContext(context.Background(), name, mode, ch)
 }
 
-// RestartUnitContext same as RestartUnit with context
+// RestartUnitContext restarts a service. If a service is restarted that isn't
+// running it will be started.
 func (c *Conn) RestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.RestartUnit", name, mode)
 }
 
-// TryRestartUnit is like RestartUnit, except that a service that isn't running
-// is not affected by the restart.
-// Deprecated: use TryRestartUnitContext instead
+// Deprecated: use TryRestartUnitContext instead.
 func (c *Conn) TryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
 	return c.TryRestartUnitContext(context.Background(), name, mode, ch)
 }
 
-// TryRestartUnitContext same as TryRestartUnit with context
+// TryRestartUnitContext is like RestartUnitContext, except that a service that
+// isn't running is not affected by the restart.
 func (c *Conn) TryRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.TryRestartUnit", name, mode)
 }
 
-// ReloadOrRestartUnit attempts a reload if the unit supports it and use a restart
-// otherwise.
-// Deprecated: use ReloadOrRestartUnitContext instead
+// Deprecated: use ReloadOrRestartUnitContext instead.
 func (c *Conn) ReloadOrRestartUnit(name string, mode string, ch chan<- string) (int, error) {
 	return c.ReloadOrRestartUnitContext(context.Background(), name, mode, ch)
 }
 
-// ReloadOrRestartUnitContext same as ReloadOrRestartUnit with context
+// ReloadOrRestartUnitContext attempts a reload if the unit supports it and use
+// a restart otherwise.
 func (c *Conn) ReloadOrRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadOrRestartUnit", name, mode)
 }
 
-// ReloadOrTryRestartUnit attempts a reload if the unit supports it and use a "Try"
-// flavored restart otherwise.
-// Deprecated: use ReloadOrTryRestartUnitContext instead
+// Deprecated: use ReloadOrTryRestartUnitContext instead.
 func (c *Conn) ReloadOrTryRestartUnit(name string, mode string, ch chan<- string) (int, error) {
 	return c.ReloadOrTryRestartUnitContext(context.Background(), name, mode, ch)
 }
 
-// ReloadOrTryRestartUnitContext same as ReloadOrTryRestartUnit with context
+// ReloadOrTryRestartUnitContext attempts a reload if the unit supports it,
+// and use a "Try" flavored restart otherwise.
 func (c *Conn) ReloadOrTryRestartUnitContext(ctx context.Context, name string, mode string, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.ReloadOrTryRestartUnit", name, mode)
 }
 
-// StartTransientUnit() may be used to create and start a transient unit, which
-// will be released as soon as it is not running or referenced anymore or the
-// system is rebooted. name is the unit name including suffix, and must be
-// unique. mode is the same as in StartUnit(), properties contains properties
-// of the unit.
-// Deprecated: use StartTransientUnitContext instead
+// Deprecated: use StartTransientUnitContext instead.
 func (c *Conn) StartTransientUnit(name string, mode string, properties []Property, ch chan<- string) (int, error) {
 	return c.StartTransientUnitContext(context.Background(), name, mode, properties, ch)
 }
 
-// StartTransientUnitContext same as StartTransientUnit with context
+// StartTransientUnitContext may be used to create and start a transient unit, which
+// will be released as soon as it is not running or referenced anymore or the
+// system is rebooted. name is the unit name including suffix, and must be
+// unique. mode is the same as in StartUnitContext, properties contains properties
+// of the unit.
 func (c *Conn) StartTransientUnitContext(ctx context.Context, name string, mode string, properties []Property, ch chan<- string) (int, error) {
 	return c.startJob(ctx, ch, "org.freedesktop.systemd1.Manager.StartTransientUnit", name, mode, properties, make([]PropertyCollection, 0))
 }
 
-// KillUnit takes the unit name and a UNIX signal number to send.  All of the unit's
-// processes are killed.
-// Deprecated: use KillUnitContext instead
+// Deprecated: use KillUnitContext instead.
 func (c *Conn) KillUnit(name string, signal int32) {
 	c.KillUnitContext(context.Background(), name, signal)
 }
 
-// KillUnitContext same as KillUnit with context
+// KillUnitContext takes the unit name and a UNIX signal number to send.
+// All of the unit's processes are killed.
 func (c *Conn) KillUnitContext(ctx context.Context, name string, signal int32) {
 	c.KillUnitWithTarget(ctx, name, All, signal)
 }
 
-// KillUnitWithTarget is like KillUnitContext, but allows you to specify which process in the unit to send the signal to
+// KillUnitWithTarget is like KillUnitContext, but allows you to specify which
+// process in the unit to send the signal to.
 func (c *Conn) KillUnitWithTarget(ctx context.Context, name string, target Who, signal int32) error {
 	return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.KillUnit", 0, name, string(target), signal).Store()
 }
 
-// ResetFailedUnit resets the "failed" state of a specific unit.
-// Deprecated: use ResetFailedUnitContext instead
+// Deprecated: use ResetFailedUnitContext instead.
 func (c *Conn) ResetFailedUnit(name string) error {
 	return c.ResetFailedUnitContext(context.Background(), name)
 }
 
-// ResetFailedUnitContext same as ResetFailedUnit with context
+// ResetFailedUnitContext resets the "failed" state of a specific unit.
 func (c *Conn) ResetFailedUnitContext(ctx context.Context, name string) error {
 	return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
 }
 
-// SystemState returns the systemd state. Equivalent to `systemctl is-system-running`.
-// Deprecated: use SystemStateContext instead
+// Deprecated: use SystemStateContext instead.
 func (c *Conn) SystemState() (*Property, error) {
 	return c.SystemStateContext(context.Background())
 }
 
-// SystemStateContext same as SystemState with context
+// SystemStateContext returns the systemd state. Equivalent to
+// systemctl is-system-running.
 func (c *Conn) SystemStateContext(ctx context.Context) (*Property, error) {
 	var err error
 	var prop dbus.Variant
@@ -247,7 +239,7 @@ func (c *Conn) SystemStateContext(ctx context.Context) (*Property, error) {
 	return &Property{Name: "SystemState", Value: prop}, nil
 }
 
-// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface
+// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface.
 func (c *Conn) getProperties(ctx context.Context, path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) {
 	var err error
 	var props map[string]dbus.Variant
@@ -270,36 +262,36 @@ func (c *Conn) getProperties(ctx context.Context, path dbus.ObjectPath, dbusInte
 	return out, nil
 }
 
-// GetUnitProperties takes the (unescaped) unit name and returns all of its dbus object properties.
-// Deprecated: use GetUnitPropertiesContext instead
+// Deprecated: use GetUnitPropertiesContext instead.
 func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
 	return c.GetUnitPropertiesContext(context.Background(), unit)
 }
 
-// GetUnitPropertiesContext same as GetUnitPropertiesContext with context
+// GetUnitPropertiesContext takes the (unescaped) unit name and returns all of
+// its dbus object properties.
 func (c *Conn) GetUnitPropertiesContext(ctx context.Context, unit string) (map[string]interface{}, error) {
 	path := unitPath(unit)
 	return c.getProperties(ctx, path, "org.freedesktop.systemd1.Unit")
 }
 
-// GetUnitPathProperties takes the (escaped) unit path and returns all of its dbus object properties.
-// Deprecated: use GetUnitPathPropertiesContext instead
+// Deprecated: use GetUnitPathPropertiesContext instead.
 func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) {
 	return c.GetUnitPathPropertiesContext(context.Background(), path)
 }
 
-// GetUnitPathPropertiesContext same as GetUnitPathProperties with context
+// GetUnitPathPropertiesContext takes the (escaped) unit path and returns all
+// of its dbus object properties.
 func (c *Conn) GetUnitPathPropertiesContext(ctx context.Context, path dbus.ObjectPath) (map[string]interface{}, error) {
 	return c.getProperties(ctx, path, "org.freedesktop.systemd1.Unit")
 }
 
-// GetAllProperties takes the (unescaped) unit name and returns all of its dbus object properties.
-// Deprecated: use GetAllPropertiesContext instead
+// Deprecated: use GetAllPropertiesContext instead.
 func (c *Conn) GetAllProperties(unit string) (map[string]interface{}, error) {
 	return c.GetAllPropertiesContext(context.Background(), unit)
 }
 
-// GetAllPropertiesContext same as GetAllProperties with context
+// GetAllPropertiesContext takes the (unescaped) unit name and returns all of
+// its dbus object properties.
 func (c *Conn) GetAllPropertiesContext(ctx context.Context, unit string) (map[string]interface{}, error) {
 	path := unitPath(unit)
 	return c.getProperties(ctx, path, "")
@@ -323,64 +315,63 @@ func (c *Conn) getProperty(ctx context.Context, unit string, dbusInterface strin
 	return &Property{Name: propertyName, Value: prop}, nil
 }
 
-// Deprecated: use GetUnitPropertyContext instead
+// Deprecated: use GetUnitPropertyContext instead.
 func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, error) {
 	return c.GetUnitPropertyContext(context.Background(), unit, propertyName)
 }
 
-// GetUnitPropertyContext same as GetUnitProperty with context
+// GetUnitPropertyContext takes an (unescaped) unit name, and a property name,
+// and returns the property value.
 func (c *Conn) GetUnitPropertyContext(ctx context.Context, unit string, propertyName string) (*Property, error) {
 	return c.getProperty(ctx, unit, "org.freedesktop.systemd1.Unit", propertyName)
 }
 
-// GetServiceProperty returns property for given service name and property name
-// Deprecated: use GetServicePropertyContext instead
+// Deprecated: use GetServicePropertyContext instead.
 func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) {
 	return c.GetServicePropertyContext(context.Background(), service, propertyName)
 }
 
-// GetServicePropertyContext same as GetServiceProperty with context
+// GetServiceProperty returns property for given service name and property name.
 func (c *Conn) GetServicePropertyContext(ctx context.Context, service string, propertyName string) (*Property, error) {
 	return c.getProperty(ctx, service, "org.freedesktop.systemd1.Service", propertyName)
 }
 
-// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
-// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
-// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
-// Deprecated: use GetUnitTypePropertiesContext instead
+// Deprecated: use GetUnitTypePropertiesContext instead.
 func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
 	return c.GetUnitTypePropertiesContext(context.Background(), unit, unitType)
 }
 
-// GetUnitTypePropertiesContext same as GetUnitTypeProperties with context
+// GetUnitTypePropertiesContext returns the extra properties for a unit, specific to the unit type.
+// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope.
+// Returns "dbus.Error: Unknown interface" error if the unitType is not the correct type of the unit.
 func (c *Conn) GetUnitTypePropertiesContext(ctx context.Context, unit string, unitType string) (map[string]interface{}, error) {
 	path := unitPath(unit)
 	return c.getProperties(ctx, path, "org.freedesktop.systemd1."+unitType)
 }
 
-// SetUnitProperties() may be used to modify certain unit properties at runtime.
+// Deprecated: use SetUnitPropertiesContext instead.
+func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
+	return c.SetUnitPropertiesContext(context.Background(), name, runtime, properties...)
+}
+
+// SetUnitPropertiesContext may be used to modify certain unit properties at runtime.
 // Not all properties may be changed at runtime, but many resource management
 // settings (primarily those in systemd.cgroup(5)) may. The changes are applied
 // instantly, and stored on disk for future boots, unless runtime is true, in which
 // case the settings only apply until the next reboot. name is the name of the unit
 // to modify. properties are the settings to set, encoded as an array of property
 // name and value pairs.
-// Deprecated: use SetUnitPropertiesContext instead
-func (c *Conn) SetUnitProperties(name string, runtime bool, properties ...Property) error {
-	return c.SetUnitPropertiesContext(context.Background(), name, runtime, properties...)
-}
-
-// SetUnitPropertiesContext same as SetUnitProperties with context
 func (c *Conn) SetUnitPropertiesContext(ctx context.Context, name string, runtime bool, properties ...Property) error {
 	return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.SetUnitProperties", 0, name, runtime, properties).Store()
 }
 
-// Deprecated: use GetUnitTypePropertyContext instead
+// Deprecated: use GetUnitTypePropertyContext instead.
 func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName string) (*Property, error) {
 	return c.GetUnitTypePropertyContext(context.Background(), unit, unitType, propertyName)
 }
 
-// GetUnitTypePropertyContext same as GetUnitTypeProperty with context
+// GetUnitTypePropertyContext takes a property name, a unit name, and a unit type,
+// and returns a property value. For valid values of unitType, see GetUnitTypePropertiesContext.
 func (c *Conn) GetUnitTypePropertyContext(ctx context.Context, unit string, unitType string, propertyName string) (*Property, error) {
 	return c.getProperty(ctx, unit, "org.freedesktop.systemd1."+unitType, propertyName)
 }
@@ -426,58 +417,55 @@ func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) {
 	return status, nil
 }
 
-// ListUnits returns an array with all currently loaded units. Note that
-// units may be known by multiple names at the same time, and hence there might
-// be more unit names loaded than actual units behind them.
-// Also note that a unit is only loaded if it is active and/or enabled.
-// Units that are both disabled and inactive will thus not be returned.
-// Deprecated: use ListUnitsContext instead
+// Deprecated: use ListUnitsContext instead.
 func (c *Conn) ListUnits() ([]UnitStatus, error) {
 	return c.ListUnitsContext(context.Background())
 }
 
-// ListUnitsContext same as ListUnits with context
+// ListUnitsContext returns an array with all currently loaded units. Note that
+// units may be known by multiple names at the same time, and hence there might
+// be more unit names loaded than actual units behind them.
+// Also note that a unit is only loaded if it is active and/or enabled.
+// Units that are both disabled and inactive will thus not be returned.
 func (c *Conn) ListUnitsContext(ctx context.Context) ([]UnitStatus, error) {
 	return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnits", 0).Store)
 }
 
-// ListUnitsFiltered returns an array with units filtered by state.
-// It takes a list of units' statuses to filter.
-// Deprecated: use ListUnitsFilteredContext instead
+// Deprecated: use ListUnitsFilteredContext instead.
 func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) {
 	return c.ListUnitsFilteredContext(context.Background(), states)
 }
 
-// ListUnitsFilteredContext same as ListUnitsFiltered with context
+// ListUnitsFilteredContext returns an array with units filtered by state.
+// It takes a list of units' statuses to filter.
 func (c *Conn) ListUnitsFilteredContext(ctx context.Context, states []string) ([]UnitStatus, error) {
 	return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store)
 }
 
-// ListUnitsByPatterns returns an array with units.
-// It takes a list of units' statuses and names to filter.
-// Note that units may be known by multiple names at the same time,
-// and hence there might be more unit names loaded than actual units behind them.
-// Deprecated: use ListUnitsByPatternsContext instead
+// Deprecated: use ListUnitsByPatternsContext instead.
 func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) {
 	return c.ListUnitsByPatternsContext(context.Background(), states, patterns)
 }
 
-// ListUnitsByPatternsContext same as ListUnitsByPatterns with context
+// ListUnitsByPatternsContext returns an array with units.
+// It takes a list of units' statuses and names to filter.
+// Note that units may be known by multiple names at the same time,
+// and hence there might be more unit names loaded than actual units behind them.
 func (c *Conn) ListUnitsByPatternsContext(ctx context.Context, states []string, patterns []string) ([]UnitStatus, error) {
 	return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store)
 }
 
-// ListUnitsByNames returns an array with units. It takes a list of units'
-// names and returns an UnitStatus array. Comparing to ListUnitsByPatterns
-// method, this method returns statuses even for inactive or non-existing
-// units. Input array should contain exact unit names, but not patterns.
-// Note: Requires systemd v230 or higher
-// Deprecated: use ListUnitsByNamesContext instead
+// Deprecated: use ListUnitsByNamesContext instead.
 func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) {
 	return c.ListUnitsByNamesContext(context.Background(), units)
 }
 
-// ListUnitsByNamesContext same as ListUnitsByNames with context
+// ListUnitsByNamesContext returns an array with units. It takes a list of units'
+// names and returns an UnitStatus array. Comparing to ListUnitsByPatternsContext
+// method, this method returns statuses even for inactive or non-existing
+// units. Input array should contain exact unit names, but not patterns.
+//
+// Requires systemd v230 or higher.
 func (c *Conn) ListUnitsByNamesContext(ctx context.Context, units []string) ([]UnitStatus, error) {
 	return c.listUnitsInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store)
 }
@@ -513,37 +501,43 @@ func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) {
 	return files, nil
 }
 
-// ListUnitFiles returns an array of all available units on disk.
-// Deprecated: use ListUnitFilesContext instead
+// Deprecated: use ListUnitFilesContext instead.
 func (c *Conn) ListUnitFiles() ([]UnitFile, error) {
 	return c.ListUnitFilesContext(context.Background())
 }
 
-// ListUnitFilesContext same as ListUnitFiles with context
+// ListUnitFiles returns an array of all available units on disk.
 func (c *Conn) ListUnitFilesContext(ctx context.Context) ([]UnitFile, error) {
 	return c.listUnitFilesInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store)
 }
 
-// ListUnitFilesByPatterns returns an array of all available units on disk matched the patterns.
-// Deprecated: use ListUnitFilesByPatternsContext instead
+// Deprecated: use ListUnitFilesByPatternsContext instead.
 func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) {
 	return c.ListUnitFilesByPatternsContext(context.Background(), states, patterns)
 }
 
-// ListUnitFilesByPatternsContext same as ListUnitFilesByPatterns with context
+// ListUnitFilesByPatternsContext returns an array of all available units on disk matched the patterns.
 func (c *Conn) ListUnitFilesByPatternsContext(ctx context.Context, states []string, patterns []string) ([]UnitFile, error) {
 	return c.listUnitFilesInternal(c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store)
 }
 
 type LinkUnitFileChange EnableUnitFileChange
 
-// LinkUnitFiles() links unit files (that are located outside of the
+// Deprecated: use LinkUnitFilesContext instead.
+func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
+	return c.LinkUnitFilesContext(context.Background(), files, runtime, force)
+}
+
+// LinkUnitFilesContext links unit files (that are located outside of the
 // usual unit search paths) into the unit search path.
 //
 // It takes a list of absolute paths to unit files to link and two
-// booleans. The first boolean controls whether the unit shall be
+// booleans.
+//
+// The first boolean controls whether the unit shall be
 // enabled for runtime only (true, /run), or persistently (false,
 // /etc).
+//
 // The second controls whether symlinks pointing to other units shall
 // be replaced if necessary.
 //
@@ -551,12 +545,6 @@ type LinkUnitFileChange EnableUnitFileChange
 // structures with three strings: the type of the change (one of symlink
 // or unlink), the file name of the symlink and the destination of the
 // symlink.
-// Deprecated: use LinkUnitFilesContext instead
-func (c *Conn) LinkUnitFiles(files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
-	return c.LinkUnitFilesContext(context.Background(), files, runtime, force)
-}
-
-// LinkUnitFilesContext same as LinkUnitFiles with context
 func (c *Conn) LinkUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) ([]LinkUnitFileChange, error) {
 	result := make([][]interface{}, 0)
 	err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.LinkUnitFiles", 0, files, runtime, force).Store(&result)
@@ -583,8 +571,13 @@ func (c *Conn) LinkUnitFilesContext(ctx context.Context, files []string, runtime
 	return changes, nil
 }
 
-// EnableUnitFiles() may be used to enable one or more units in the system (by
-// creating symlinks to them in /etc or /run).
+// Deprecated: use EnableUnitFilesContext instead.
+func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
+	return c.EnableUnitFilesContext(context.Background(), files, runtime, force)
+}
+
+// EnableUnitFilesContext may be used to enable one or more units in the system
+// (by creating symlinks to them in /etc or /run).
 //
 // It takes a list of unit files to enable (either just file names or full
 // absolute paths if the unit files are residing outside the usual unit
@@ -599,12 +592,6 @@ func (c *Conn) LinkUnitFilesContext(ctx context.Context, files []string, runtime
 // structures with three strings: the type of the change (one of symlink
 // or unlink), the file name of the symlink and the destination of the
 // symlink.
-// Deprecated: use EnableUnitFilesContext instead
-func (c *Conn) EnableUnitFiles(files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
-	return c.EnableUnitFilesContext(context.Background(), files, runtime, force)
-}
-
-// EnableUnitFilesContext same as EnableUnitFiles with context
 func (c *Conn) EnableUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) (bool, []EnableUnitFileChange, error) {
 	var carries_install_info bool
 
@@ -639,8 +626,13 @@ type EnableUnitFileChange struct {
 	Destination string // Destination of the symlink
 }
 
-// DisableUnitFiles() may be used to disable one or more units in the system (by
-// removing symlinks to them from /etc or /run).
+// Deprecated: use DisableUnitFilesContext instead.
+func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
+	return c.DisableUnitFilesContext(context.Background(), files, runtime)
+}
+
+// DisableUnitFilesContext may be used to disable one or more units in the
+// system (by removing symlinks to them from /etc or /run).
 //
 // It takes a list of unit files to disable (either just file names or full
 // absolute paths if the unit files are residing outside the usual unit
@@ -651,12 +643,6 @@ type EnableUnitFileChange struct {
 // consists of structures with three strings: the type of the change (one of
 // symlink or unlink), the file name of the symlink and the destination of the
 // symlink.
-// Deprecated: use DisableUnitFilesContext instead
-func (c *Conn) DisableUnitFiles(files []string, runtime bool) ([]DisableUnitFileChange, error) {
-	return c.DisableUnitFilesContext(context.Background(), files, runtime)
-}
-
-// DisableUnitFilesContext same as DisableUnitFiles with context
 func (c *Conn) DisableUnitFilesContext(ctx context.Context, files []string, runtime bool) ([]DisableUnitFileChange, error) {
 	result := make([][]interface{}, 0)
 	err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.DisableUnitFiles", 0, files, runtime).Store(&result)
@@ -689,21 +675,20 @@ type DisableUnitFileChange struct {
 	Destination string // Destination of the symlink
 }
 
-// MaskUnitFiles masks one or more units in the system
-//
-// It takes three arguments:
-//   * list of units to mask (either just file names or full
-//     absolute paths if the unit files are residing outside
-//     the usual unit search paths)
-//   * runtime to specify whether the unit was enabled for runtime
-//     only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
-//   * force flag
-// Deprecated: use MaskUnitFilesContext instead
+// Deprecated: use MaskUnitFilesContext instead.
 func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) {
 	return c.MaskUnitFilesContext(context.Background(), files, runtime, force)
 }
 
-// MaskUnitFilesContext same as MaskUnitFiles with context
+// MaskUnitFilesContext masks one or more units in the system.
+//
+// The files argument contains a  list of units to mask (either just file names
+// or full absolute paths if the unit files are residing outside the usual unit
+// search paths).
+//
+// The runtime argument is used to specify whether the unit was enabled for
+// runtime only (true, /run/systemd/..), or persistently (false,
+// /etc/systemd/..).
 func (c *Conn) MaskUnitFilesContext(ctx context.Context, files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) {
 	result := make([][]interface{}, 0)
 	err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result)
@@ -736,20 +721,18 @@ type MaskUnitFileChange struct {
 	Destination string // Destination of the symlink
 }
 
-// UnmaskUnitFiles unmasks one or more units in the system
-//
-// It takes two arguments:
-//   * list of unit files to mask (either just file names or full
-//     absolute paths if the unit files are residing outside
-//     the usual unit search paths)
-//   * runtime to specify whether the unit was enabled for runtime
-//     only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
-// Deprecated: use UnmaskUnitFilesContext instead
+// Deprecated: use UnmaskUnitFilesContext instead.
 func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) {
 	return c.UnmaskUnitFilesContext(context.Background(), files, runtime)
 }
 
-// UnmaskUnitFilesContext same as UnmaskUnitFiles with context
+// UnmaskUnitFilesContext unmasks one or more units in the system.
+//
+// It takes the list of unit files to mask (either just file names or full
+// absolute paths if the unit files are residing outside the usual unit search
+// paths), and a boolean runtime flag to specify whether the unit was enabled
+// for runtime only (true, /run/systemd/..), or persistently (false,
+// /etc/systemd/..).
 func (c *Conn) UnmaskUnitFilesContext(ctx context.Context, files []string, runtime bool) ([]UnmaskUnitFileChange, error) {
 	result := make([][]interface{}, 0)
 	err := c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result)
@@ -782,14 +765,13 @@ type UnmaskUnitFileChange struct {
 	Destination string // Destination of the symlink
 }
 
-// Reload instructs systemd to scan for and reload unit files. This is
-// equivalent to a 'systemctl daemon-reload'.
-// Deprecated: use ReloadContext instead
+// Deprecated: use ReloadContext instead.
 func (c *Conn) Reload() error {
 	return c.ReloadContext(context.Background())
 }
 
-// ReloadContext same as Reload with context
+// ReloadContext instructs systemd to scan for and reload unit files. This is
+// an equivalent to systemctl daemon-reload.
 func (c *Conn) ReloadContext(ctx context.Context) error {
 	return c.sysobj.CallWithContext(ctx, "org.freedesktop.systemd1.Manager.Reload", 0).Store()
 }
@@ -798,12 +780,12 @@ func unitPath(name string) dbus.ObjectPath {
 	return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name))
 }
 
-// unitName returns the unescaped base element of the supplied escaped path
+// unitName returns the unescaped base element of the supplied escaped path.
 func unitName(dpath dbus.ObjectPath) string {
 	return pathBusUnescape(path.Base(string(dpath)))
 }
 
-// Currently queued job definition
+// JobStatus holds a currently queued job definition.
 type JobStatus struct {
 	Id       uint32          // The numeric job id
 	Unit     string          // The primary unit name for this job
@@ -813,13 +795,12 @@ type JobStatus struct {
 	UnitPath dbus.ObjectPath // The unit object path
 }
 
-// ListJobs returns an array with all currently queued jobs
-// Deprecated: use ListJobsContext instead
+// Deprecated: use ListJobsContext instead.
 func (c *Conn) ListJobs() ([]JobStatus, error) {
 	return c.ListJobsContext(context.Background())
 }
 
-// ListJobsContext same as ListJobs with context
+// ListJobsContext returns an array with all currently queued jobs.
 func (c *Conn) ListJobsContext(ctx context.Context) ([]JobStatus, error) {
 	return c.listJobsInternal(ctx)
 }

+ 3 - 1
vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go

@@ -65,9 +65,11 @@ func Enabled() bool {
 		return false
 	}
 
-	if _, err := net.Dial("unixgram", journalSocket); err != nil {
+	conn, err := net.Dial("unixgram", journalSocket)
+	if err != nil {
 		return false
 	}
+	defer conn.Close()
 
 	return true
 }

+ 6 - 6
vendor/github.com/opencontainers/runc/go.mod

@@ -3,26 +3,26 @@ module github.com/opencontainers/runc
 go 1.13
 
 require (
+	github.com/bits-and-blooms/bitset v1.2.0
 	github.com/checkpoint-restore/go-criu/v5 v5.0.0
-	github.com/cilium/ebpf v0.5.0
+	github.com/cilium/ebpf v0.6.2
 	github.com/containerd/console v1.0.2
-	github.com/coreos/go-systemd/v22 v22.3.1
+	github.com/coreos/go-systemd/v22 v22.3.2
 	github.com/cyphar/filepath-securejoin v0.2.2
 	github.com/docker/go-units v0.4.0
 	github.com/godbus/dbus/v5 v5.0.4
 	github.com/moby/sys/mountinfo v0.4.1
 	github.com/mrunalp/fileutils v0.5.0
 	github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
-	github.com/opencontainers/selinux v1.8.0
+	github.com/opencontainers/selinux v1.8.2
 	github.com/pkg/errors v0.9.1
 	github.com/seccomp/libseccomp-golang v0.9.1
-	github.com/sirupsen/logrus v1.7.0
+	github.com/sirupsen/logrus v1.8.1
 	github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
 	// NOTE: urfave/cli must be <= v1.22.1 due to a regression: https://github.com/urfave/cli/issues/1092
 	github.com/urfave/cli v1.22.1
 	github.com/vishvananda/netlink v1.1.0
-	github.com/willf/bitset v1.1.11
 	golang.org/x/net v0.0.0-20201224014010-6772e930b67b
 	golang.org/x/sys v0.0.0-20210426230700-d19ff857e887
-	google.golang.org/protobuf v1.25.0
+	google.golang.org/protobuf v1.26.0
 )

+ 53 - 7
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/open.go → vendor/github.com/opencontainers/runc/libcontainer/cgroups/file.go

@@ -1,6 +1,7 @@
-package fscommon
+package cgroups
 
 import (
+	"bytes"
 	"os"
 	"strings"
 	"sync"
@@ -10,6 +11,54 @@ import (
 	"golang.org/x/sys/unix"
 )
 
+// OpenFile opens a cgroup file in a given dir with given flags.
+// It is supposed to be used for cgroup files only.
+func OpenFile(dir, file string, flags int) (*os.File, error) {
+	if dir == "" {
+		return nil, errors.Errorf("no directory specified for %s", file)
+	}
+	return openFile(dir, file, flags)
+}
+
+// ReadFile reads data from a cgroup file in dir.
+// It is supposed to be used for cgroup files only.
+func ReadFile(dir, file string) (string, error) {
+	fd, err := OpenFile(dir, file, unix.O_RDONLY)
+	if err != nil {
+		return "", err
+	}
+	defer fd.Close()
+	var buf bytes.Buffer
+
+	_, err = buf.ReadFrom(fd)
+	return buf.String(), err
+}
+
+// WriteFile writes data to a cgroup file in dir.
+// It is supposed to be used for cgroup files only.
+func WriteFile(dir, file, data string) error {
+	fd, err := OpenFile(dir, file, unix.O_WRONLY)
+	if err != nil {
+		return err
+	}
+	defer fd.Close()
+	if err := retryingWriteFile(fd, data); err != nil {
+		return errors.Wrapf(err, "failed to write %q", data)
+	}
+	return nil
+}
+
+func retryingWriteFile(fd *os.File, data string) error {
+	for {
+		_, err := fd.Write([]byte(data))
+		if errors.Is(err, unix.EINTR) {
+			logrus.Infof("interrupted while writing %s to %s", data, fd.Name())
+			continue
+		}
+		return err
+	}
+}
+
 const (
 	cgroupfsDir    = "/sys/fs/cgroup"
 	cgroupfsPrefix = cgroupfsDir + "/"
@@ -28,7 +77,8 @@ var (
 func prepareOpenat2() error {
 	prepOnce.Do(func() {
 		fd, err := unix.Openat2(-1, cgroupfsDir, &unix.OpenHow{
-			Flags: unix.O_DIRECTORY | unix.O_PATH})
+			Flags: unix.O_DIRECTORY | unix.O_PATH,
+		})
 		if err != nil {
 			prepErr = &os.PathError{Op: "openat2", Path: cgroupfsDir, Err: err}
 			if err != unix.ENOSYS {
@@ -52,7 +102,6 @@ func prepareOpenat2() error {
 			// cgroupv2 has a single mountpoint and no "cpu,cpuacct" symlinks
 			resolveFlags |= unix.RESOLVE_NO_XDEV | unix.RESOLVE_NO_SYMLINKS
 		}
-
 	})
 
 	return prepErr
@@ -60,10 +109,7 @@ func prepareOpenat2() error {
 
 // OpenFile opens a cgroup file in a given dir with given flags.
 // It is supposed to be used for cgroup files only.
-func OpenFile(dir, file string, flags int) (*os.File, error) {
-	if dir == "" {
-		return nil, errors.Errorf("no directory specified for %s", file)
-	}
+func openFile(dir, file string, flags int) (*os.File, error) {
 	mode := os.FileMode(0)
 	if TestMode && flags&os.O_WRONLY != 0 {
 		// "emulate" cgroup fs for unit tests

+ 0 - 51
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/fscommon.go

@@ -1,51 +0,0 @@
-// +build linux
-
-package fscommon
-
-import (
-	"bytes"
-	"os"
-
-	"github.com/pkg/errors"
-	"github.com/sirupsen/logrus"
-	"golang.org/x/sys/unix"
-)
-
-// WriteFile writes data to a cgroup file in dir.
-// It is supposed to be used for cgroup files only.
-func WriteFile(dir, file, data string) error {
-	fd, err := OpenFile(dir, file, unix.O_WRONLY)
-	if err != nil {
-		return err
-	}
-	defer fd.Close()
-	if err := retryingWriteFile(fd, data); err != nil {
-		return errors.Wrapf(err, "failed to write %q", data)
-	}
-	return nil
-}
-
-// ReadFile reads data from a cgroup file in dir.
-// It is supposed to be used for cgroup files only.
-func ReadFile(dir, file string) (string, error) {
-	fd, err := OpenFile(dir, file, unix.O_RDONLY)
-	if err != nil {
-		return "", err
-	}
-	defer fd.Close()
-	var buf bytes.Buffer
-
-	_, err = buf.ReadFrom(fd)
-	return buf.String(), err
-}
-
-func retryingWriteFile(fd *os.File, data string) error {
-	for {
-		_, err := fd.Write([]byte(data))
-		if errors.Is(err, unix.EINTR) {
-			logrus.Infof("interrupted while writing %s to %s", data, fd.Name())
-			continue
-		}
-		return err
-	}
-}

+ 0 - 122
vendor/github.com/opencontainers/runc/libcontainer/cgroups/fscommon/utils.go

@@ -1,122 +0,0 @@
-// +build linux
-
-package fscommon
-
-import (
-	"errors"
-	"fmt"
-	"math"
-	"strconv"
-	"strings"
-)
-
-var (
-	ErrNotValidFormat = errors.New("line is not a valid key value format")
-)
-
-// ParseUint converts a string to an uint64 integer.
-// Negative values are returned at zero as, due to kernel bugs,
-// some of the memory cgroup stats can be negative.
-func ParseUint(s string, base, bitSize int) (uint64, error) {
-	value, err := strconv.ParseUint(s, base, bitSize)
-	if err != nil {
-		intValue, intErr := strconv.ParseInt(s, base, bitSize)
-		// 1. Handle negative values greater than MinInt64 (and)
-		// 2. Handle negative values lesser than MinInt64
-		if intErr == nil && intValue < 0 {
-			return 0, nil
-		} else if intErr != nil && intErr.(*strconv.NumError).Err == strconv.ErrRange && intValue < 0 {
-			return 0, nil
-		}
-
-		return value, err
-	}
-
-	return value, nil
-}
-
-// ParseKeyValue parses a space-separated "name value" kind of cgroup
-// parameter and returns its key as a string, and its value as uint64
-// (ParseUint is used to convert the value). For example,
-// "io_service_bytes 1234" will be returned as "io_service_bytes", 1234.
-func ParseKeyValue(t string) (string, uint64, error) {
-	parts := strings.SplitN(t, " ", 3)
-	if len(parts) != 2 {
-		return "", 0, fmt.Errorf("line %q is not in key value format", t)
-	}
-
-	value, err := ParseUint(parts[1], 10, 64)
-	if err != nil {
-		return "", 0, fmt.Errorf("unable to convert to uint64: %v", err)
-	}
-
-	return parts[0], value, nil
-}
-
-// GetValueByKey reads a key-value pairs from the specified cgroup file,
-// and returns a value of the specified key. ParseUint is used for value
-// conversion.
-func GetValueByKey(path, file, key string) (uint64, error) {
-	content, err := ReadFile(path, file)
-	if err != nil {
-		return 0, err
-	}
-
-	lines := strings.Split(string(content), "\n")
-	for _, line := range lines {
-		arr := strings.Split(line, " ")
-		if len(arr) == 2 && arr[0] == key {
-			return ParseUint(arr[1], 10, 64)
-		}
-	}
-
-	return 0, nil
-}
-
-// GetCgroupParamUint reads a single uint64 value from the specified cgroup file.
-// If the value read is "max", the math.MaxUint64 is returned.
-func GetCgroupParamUint(path, file string) (uint64, error) {
-	contents, err := GetCgroupParamString(path, file)
-	if err != nil {
-		return 0, err
-	}
-	contents = strings.TrimSpace(contents)
-	if contents == "max" {
-		return math.MaxUint64, nil
-	}
-
-	res, err := ParseUint(contents, 10, 64)
-	if err != nil {
-		return res, fmt.Errorf("unable to parse file %q", path+"/"+file)
-	}
-	return res, nil
-}
-
-// GetCgroupParamInt reads a single int64 value from specified cgroup file.
-// If the value read is "max", the math.MaxInt64 is returned.
-func GetCgroupParamInt(path, file string) (int64, error) {
-	contents, err := ReadFile(path, file)
-	if err != nil {
-		return 0, err
-	}
-	contents = strings.TrimSpace(contents)
-	if contents == "max" {
-		return math.MaxInt64, nil
-	}
-
-	res, err := strconv.ParseInt(contents, 10, 64)
-	if err != nil {
-		return res, fmt.Errorf("unable to parse %q as a int from Cgroup file %q", contents, path+"/"+file)
-	}
-	return res, nil
-}
-
-// GetCgroupParamString reads a string from the specified cgroup file.
-func GetCgroupParamString(path, file string) (string, error) {
-	contents, err := ReadFile(path, file)
-	if err != nil {
-		return "", err
-	}
-
-	return strings.TrimSpace(contents), nil
-}

+ 2 - 4
vendor/github.com/opencontainers/runc/libcontainer/cgroups/utils.go

@@ -15,7 +15,6 @@ import (
 	"sync"
 	"time"
 
-	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
 	"github.com/opencontainers/runc/libcontainer/userns"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
@@ -88,7 +87,7 @@ func GetAllSubsystems() ([]string, error) {
 		// - freezer: implemented in kernel 5.2
 		// We assume these are always available, as it is hard to detect availability.
 		pseudo := []string{"devices", "freezer"}
-		data, err := fscommon.ReadFile("/sys/fs/cgroup", "cgroup.controllers")
+		data, err := ReadFile("/sys/fs/cgroup", "cgroup.controllers")
 		if err != nil {
 			return nil, err
 		}
@@ -267,7 +266,6 @@ func RemovePaths(paths map[string]string) (err error) {
 				case retries - 1:
 					logrus.WithError(err).Error("Failed to remove cgroup")
 				}
-
 			}
 			_, err := os.Stat(p)
 			// We need this strange way of checking cgroups existence because
@@ -376,7 +374,7 @@ func WriteCgroupProc(dir string, pid int) error {
 		return nil
 	}
 
-	file, err := fscommon.OpenFile(dir, CgroupProcesses, os.O_WRONLY)
+	file, err := OpenFile(dir, CgroupProcesses, os.O_WRONLY)
 	if err != nil {
 		return fmt.Errorf("failed to write %v to %v: %v", pid, CgroupProcesses, err)
 	}

+ 5 - 5
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_linux.go

@@ -13,12 +13,12 @@ const (
 	Thawed    FreezerState = "THAWED"
 )
 
+// Cgroup holds properties of a cgroup on Linux.
 type Cgroup struct {
-	// Deprecated, use Path instead
+	// Name specifies the name of the cgroup
 	Name string `json:"name,omitempty"`
 
-	// name of parent of cgroup or slice
-	// Deprecated, use Path instead
+	// Parent specifies the name of parent of cgroup or slice
 	Parent string `json:"parent,omitempty"`
 
 	// Path specifies the path to cgroups that are created and/or joined by the container.
@@ -127,8 +127,8 @@ type Resources struct {
 
 	// SkipDevices allows to skip configuring device permissions.
 	// Used by e.g. kubelet while creating a parent cgroup (kubepods)
-	// common for many containers.
+	// common for many containers, and by runc update.
 	//
 	// NOTE it is impossible to start a container which has this flag set.
-	SkipDevices bool `json:"skip_devices"`
+	SkipDevices bool `json:"-"`
 }

+ 2 - 2
vendor/github.com/opencontainers/runc/libcontainer/configs/cgroup_unsupported.go

@@ -2,7 +2,7 @@
 
 package configs
 
+// Cgroup holds properties of a cgroup on Linux
 // TODO Windows: This can ultimately be entirely factored out on Windows as
 // cgroups are a Unix-specific construct.
-type Cgroup struct {
-}
+type Cgroup struct{}

+ 6 - 4
vendor/github.com/opencontainers/runc/libcontainer/configs/config.go

@@ -208,9 +208,11 @@ type Config struct {
 	RootlessCgroups bool `json:"rootless_cgroups,omitempty"`
 }
 
-type HookName string
-type HookList []Hook
-type Hooks map[HookName]HookList
+type (
+	HookName string
+	HookList []Hook
+	Hooks    map[HookName]HookList
+)
 
 const (
 	// Prestart commands are executed after the container namespaces are created,
@@ -387,7 +389,7 @@ func (c Command) Run(s *specs.State) error {
 	case err := <-errC:
 		return err
 	case <-timerCh:
-		cmd.Process.Kill()
+		_ = cmd.Process.Kill()
 		<-errC
 		return fmt.Errorf("hook ran past specified timeout of %.1fs", c.Timeout.Seconds())
 	}

+ 0 - 17
vendor/github.com/opencontainers/runc/libcontainer/configs/devices.go

@@ -1,17 +0,0 @@
-package configs
-
-import "github.com/opencontainers/runc/libcontainer/devices"
-
-type (
-	// Deprecated: use libcontainer/devices.Device
-	Device = devices.Device
-
-	// Deprecated: use libcontainer/devices.Rule
-	DeviceRule = devices.Rule
-
-	// Deprecated: use libcontainer/devices.Type
-	DeviceType = devices.Type
-
-	// Deprecated: use libcontainer/devices.Permissions
-	DevicePermissions = devices.Permissions
-)

+ 1 - 1
vendor/github.com/opencontainers/runc/libcontainer/configs/mount.go

@@ -3,7 +3,7 @@ package configs
 const (
 	// EXT_COPYUP is a directive to copy up the contents of a directory when
 	// a tmpfs is mounted over it.
-	EXT_COPYUP = 1 << iota
+	EXT_COPYUP = 1 << iota //nolint:golint // ignore "don't use ALL_CAPS" warning
 )
 
 type Mount struct {

+ 1 - 2
vendor/github.com/opencontainers/runc/libcontainer/configs/namespaces_unsupported.go

@@ -4,5 +4,4 @@ package configs
 
 // Namespace defines configuration for each namespace.  It specifies an
 // alternate path that is able to be joined via setns.
-type Namespace struct {
-}
+type Namespace struct{}

+ 8 - 5
vendor/github.com/opencontainers/runc/libcontainer/configs/network.go

@@ -50,7 +50,10 @@ type Network struct {
 	HairpinMode bool `json:"hairpin_mode"`
 }
 
-// Routes can be specified to create entries in the route table as the container is started
+// Route defines a routing table entry.
+//
+// Routes can be specified to create entries in the routing table as the container
+// is started.
 //
 // All of destination, source, and gateway should be either IPv4 or IPv6.
 // One of the three options must be present, and omitted entries will use their
@@ -58,15 +61,15 @@ type Network struct {
 // gateway to 1.2.3.4 and the interface to eth0 will set up a standard
 // destination of 0.0.0.0(or *) when viewed in the route table.
 type Route struct {
-	// Sets the destination and mask, should be a CIDR.  Accepts IPv4 and IPv6
+	// Destination specifies the destination IP address and mask in the CIDR form.
 	Destination string `json:"destination"`
 
-	// Sets the source and mask, should be a CIDR.  Accepts IPv4 and IPv6
+	// Source specifies the source IP address and mask in the CIDR form.
 	Source string `json:"source"`
 
-	// Sets the gateway.  Accepts IPv4 and IPv6
+	// Gateway specifies the gateway IP address.
 	Gateway string `json:"gateway"`
 
-	// The device to set this route up for, for example: eth0
+	// InterfaceName specifies the device to set this route up for, for example eth0.
 	InterfaceName string `json:"interface_name"`
 }

+ 5 - 6
vendor/github.com/opencontainers/runc/libcontainer/devices/device_unix.go

@@ -11,10 +11,8 @@ import (
 	"golang.org/x/sys/unix"
 )
 
-var (
-	// ErrNotADevice denotes that a file is not a valid linux device.
-	ErrNotADevice = errors.New("not a device node")
-)
+// ErrNotADevice denotes that a file is not a valid linux device.
+var ErrNotADevice = errors.New("not a device node")
 
 // Testing dependencies
 var (
@@ -29,8 +27,9 @@ func mkDev(d *Rule) (uint64, error) {
 	return unix.Mkdev(uint32(d.Major), uint32(d.Minor)), nil
 }
 
-// Given the path to a device and its cgroup_permissions(which cannot be easily queried) look up the
-// information about a linux device and return that information as a Device struct.
+// DeviceFromPath takes the path to a device and its cgroup_permissions (which
+// cannot be easily queried) to look up the information about a linux device
+// and returns that information as a Device struct.
 func DeviceFromPath(path, permissions string) (*Device, error) {
 	var stat unix.Stat_t
 	err := unixLstat(path, &stat)

+ 3 - 3
vendor/github.com/opencontainers/runc/libcontainer/nsenter/test/escape.go

@@ -14,7 +14,7 @@ import (
 	"unsafe"
 )
 
-func testEscapeJsonString(t *testing.T, input, want string) {
+func testEscapeJSONString(t *testing.T, input, want string) {
 	in := C.CString(input)
 	out := C.escape_json_string(in)
 	got := C.GoString(out)
@@ -25,7 +25,7 @@ func testEscapeJsonString(t *testing.T, input, want string) {
 	}
 }
 
-func testEscapeJson(t *testing.T) {
+func testEscapeJSON(t *testing.T) {
 	testCases := []struct {
 		input, output string
 	}{
@@ -48,6 +48,6 @@ func testEscapeJson(t *testing.T) {
 	}
 
 	for _, tc := range testCases {
-		testEscapeJsonString(t, tc.input, tc.output)
+		testEscapeJSONString(t, tc.input, tc.output)
 	}
 }

+ 74 - 42
vendor/github.com/opencontainers/runc/libcontainer/user/user.go

@@ -2,6 +2,7 @@ package user
 
 import (
 	"bufio"
+	"bytes"
 	"errors"
 	"fmt"
 	"io"
@@ -11,19 +12,17 @@ import (
 )
 
 const (
-	minId = 0
-	maxId = 1<<31 - 1 //for 32-bit systems compatibility
+	minID = 0
+	maxID = 1<<31 - 1 // for 32-bit systems compatibility
 )
 
 var (
-	// The current operating system does not provide the required data for user lookups.
-	ErrUnsupported = errors.New("user lookup: operating system does not provide passwd-formatted data")
-
-	// No matching entries found in file.
+	// ErrNoPasswdEntries is returned if no matching entries were found in /etc/group.
 	ErrNoPasswdEntries = errors.New("no matching entries in passwd file")
-	ErrNoGroupEntries  = errors.New("no matching entries in group file")
-
-	ErrRange = fmt.Errorf("uids and gids must be in range %d-%d", minId, maxId)
+	// ErrNoGroupEntries is returned if no matching entries were found in /etc/passwd.
+	ErrNoGroupEntries = errors.New("no matching entries in group file")
+	// ErrRange is returned if a UID or GID is outside of the valid range.
+	ErrRange = fmt.Errorf("uids and gids must be in range %d-%d", minID, maxID)
 )
 
 type User struct {
@@ -57,11 +56,11 @@ type IDMap struct {
 	Count    int64
 }
 
-func parseLine(line string, v ...interface{}) {
-	parseParts(strings.Split(line, ":"), v...)
+func parseLine(line []byte, v ...interface{}) {
+	parseParts(bytes.Split(line, []byte(":")), v...)
 }
 
-func parseParts(parts []string, v ...interface{}) {
+func parseParts(parts [][]byte, v ...interface{}) {
 	if len(parts) == 0 {
 		return
 	}
@@ -77,16 +76,16 @@ func parseParts(parts []string, v ...interface{}) {
 		// This is legit.
 		switch e := v[i].(type) {
 		case *string:
-			*e = p
+			*e = string(p)
 		case *int:
 			// "numbers", with conversion errors ignored because of some misbehaving configuration files.
-			*e, _ = strconv.Atoi(p)
+			*e, _ = strconv.Atoi(string(p))
 		case *int64:
-			*e, _ = strconv.ParseInt(p, 10, 64)
+			*e, _ = strconv.ParseInt(string(p), 10, 64)
 		case *[]string:
 			// Comma-separated lists.
-			if p != "" {
-				*e = strings.Split(p, ",")
+			if len(p) != 0 {
+				*e = strings.Split(string(p), ",")
 			} else {
 				*e = []string{}
 			}
@@ -130,8 +129,8 @@ func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
 	)
 
 	for s.Scan() {
-		line := strings.TrimSpace(s.Text())
-		if line == "" {
+		line := bytes.TrimSpace(s.Bytes())
+		if len(line) == 0 {
 			continue
 		}
 
@@ -181,15 +180,53 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
 	if r == nil {
 		return nil, fmt.Errorf("nil source for group-formatted data")
 	}
+	rd := bufio.NewReader(r)
+	out := []Group{}
 
-	var (
-		s   = bufio.NewScanner(r)
-		out = []Group{}
-	)
+	// Read the file line-by-line.
+	for {
+		var (
+			isPrefix  bool
+			wholeLine []byte
+			err       error
+		)
 
-	for s.Scan() {
-		text := s.Text()
-		if text == "" {
+		// Read the next line. We do so in chunks (as much as reader's
+		// buffer is able to keep), check if we read enough columns
+		// already on each step and store final result in wholeLine.
+		for {
+			var line []byte
+			line, isPrefix, err = rd.ReadLine()
+
+			if err != nil {
+				// We should return no error if EOF is reached
+				// without a match.
+				if err == io.EOF { //nolint:errorlint // comparison with io.EOF is legit, https://github.com/polyfloyd/go-errorlint/pull/12
+					err = nil
+				}
+				return out, err
+			}
+
+			// Simple common case: line is short enough to fit in a
+			// single reader's buffer.
+			if !isPrefix && len(wholeLine) == 0 {
+				wholeLine = line
+				break
+			}
+
+			wholeLine = append(wholeLine, line...)
+
+			// Check if we read the whole line already.
+			if !isPrefix {
+				break
+			}
+		}
+
+		// There's no spec for /etc/passwd or /etc/group, but we try to follow
+		// the same rules as the glibc parser, which allows comments and blank
+		// space at the beginning of a line.
+		wholeLine = bytes.TrimSpace(wholeLine)
+		if len(wholeLine) == 0 || wholeLine[0] == '#' {
 			continue
 		}
 
@@ -199,17 +236,12 @@ func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
 		//  root:x:0:root
 		//  adm:x:4:root,adm,daemon
 		p := Group{}
-		parseLine(text, &p.Name, &p.Pass, &p.Gid, &p.List)
+		parseLine(wholeLine, &p.Name, &p.Pass, &p.Gid, &p.List)
 
 		if filter == nil || filter(p) {
 			out = append(out, p)
 		}
 	}
-	if err := s.Err(); err != nil {
-		return nil, err
-	}
-
-	return out, nil
 }
 
 type ExecUser struct {
@@ -280,7 +312,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
 
 	// Allow for userArg to have either "user" syntax, or optionally "user:group" syntax
 	var userArg, groupArg string
-	parseLine(userSpec, &userArg, &groupArg)
+	parseLine([]byte(userSpec), &userArg, &groupArg)
 
 	// Convert userArg and groupArg to be numeric, so we don't have to execute
 	// Atoi *twice* for each iteration over lines.
@@ -328,7 +360,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
 		user.Uid = uidArg
 
 		// Must be inside valid uid range.
-		if user.Uid < minId || user.Uid > maxId {
+		if user.Uid < minID || user.Uid > maxID {
 			return nil, ErrRange
 		}
 
@@ -377,7 +409,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
 				user.Gid = gidArg
 
 				// Must be inside valid gid range.
-				if user.Gid < minId || user.Gid > maxId {
+				if user.Gid < minID || user.Gid > maxID {
 					return nil, ErrRange
 				}
 
@@ -401,7 +433,7 @@ func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (
 // or the given group data is nil, the id will be returned as-is
 // provided it is in the legal range.
 func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) {
-	var groups = []Group{}
+	groups := []Group{}
 	if group != nil {
 		var err error
 		groups, err = ParseGroupFilter(group, func(g Group) bool {
@@ -439,7 +471,7 @@ func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, err
 				return nil, fmt.Errorf("Unable to find group %s", ag)
 			}
 			// Ensure gid is inside gid range.
-			if gid < minId || gid > maxId {
+			if gid < minID || gid > maxID {
 				return nil, ErrRange
 			}
 			gidMap[int(gid)] = struct{}{}
@@ -498,8 +530,8 @@ func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) {
 	)
 
 	for s.Scan() {
-		line := strings.TrimSpace(s.Text())
-		if line == "" {
+		line := bytes.TrimSpace(s.Bytes())
+		if len(line) == 0 {
 			continue
 		}
 
@@ -551,14 +583,14 @@ func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) {
 	)
 
 	for s.Scan() {
-		line := strings.TrimSpace(s.Text())
-		if line == "" {
+		line := bytes.TrimSpace(s.Bytes())
+		if len(line) == 0 {
 			continue
 		}
 
 		// see: man 7 user_namespaces
 		p := IDMap{}
-		parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count)
+		parseParts(bytes.Fields(line), &p.ID, &p.ParentID, &p.Count)
 
 		if filter == nil || filter(p) {
 			out = append(out, p)

+ 1 - 1
vendor/github.com/sirupsen/logrus/README.md

@@ -402,7 +402,7 @@ func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
   // source of the official loggers.
   serialized, err := json.Marshal(entry.Data)
     if err != nil {
-      return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
+      return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
     }
   return append(serialized, '\n'), nil
 }

+ 41 - 32
vendor/github.com/sirupsen/logrus/entry.go

@@ -78,6 +78,14 @@ func NewEntry(logger *Logger) *Entry {
 	}
 }
 
+func (entry *Entry) Dup() *Entry {
+	data := make(Fields, len(entry.Data))
+	for k, v := range entry.Data {
+		data[k] = v
+	}
+	return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
+}
+
 // Returns the bytes representation of this entry from the formatter.
 func (entry *Entry) Bytes() ([]byte, error) {
 	return entry.Logger.Formatter.Format(entry)
@@ -123,11 +131,9 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
 	for k, v := range fields {
 		isErrField := false
 		if t := reflect.TypeOf(v); t != nil {
-			switch t.Kind() {
-			case reflect.Func:
+			switch {
+			case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
 				isErrField = true
-			case reflect.Ptr:
-				isErrField = t.Elem().Kind() == reflect.Func
 			}
 		}
 		if isErrField {
@@ -212,68 +218,72 @@ func (entry Entry) HasCaller() (has bool) {
 		entry.Caller != nil
 }
 
-// This function is not declared with a pointer value because otherwise
-// race conditions will occur when using multiple goroutines
-func (entry Entry) log(level Level, msg string) {
+func (entry *Entry) log(level Level, msg string) {
 	var buffer *bytes.Buffer
 
-	// Default to now, but allow users to override if they want.
-	//
-	// We don't have to worry about polluting future calls to Entry#log()
-	// with this assignment because this function is declared with a
-	// non-pointer receiver.
-	if entry.Time.IsZero() {
-		entry.Time = time.Now()
+	newEntry := entry.Dup()
+
+	if newEntry.Time.IsZero() {
+		newEntry.Time = time.Now()
 	}
 
-	entry.Level = level
-	entry.Message = msg
-	entry.Logger.mu.Lock()
-	if entry.Logger.ReportCaller {
-		entry.Caller = getCaller()
+	newEntry.Level = level
+	newEntry.Message = msg
+
+	newEntry.Logger.mu.Lock()
+	reportCaller := newEntry.Logger.ReportCaller
+	newEntry.Logger.mu.Unlock()
+
+	if reportCaller {
+		newEntry.Caller = getCaller()
 	}
-	entry.Logger.mu.Unlock()
 
-	entry.fireHooks()
+	newEntry.fireHooks()
 
 	buffer = getBuffer()
 	defer func() {
-		entry.Buffer = nil
+		newEntry.Buffer = nil
 		putBuffer(buffer)
 	}()
 	buffer.Reset()
-	entry.Buffer = buffer
+	newEntry.Buffer = buffer
 
-	entry.write()
+	newEntry.write()
 
-	entry.Buffer = nil
+	newEntry.Buffer = nil
 
 	// To avoid Entry#log() returning a value that only would make sense for
 	// panic() to use in Entry#Panic(), we avoid the allocation by checking
 	// directly here.
 	if level <= PanicLevel {
-		panic(&entry)
+		panic(newEntry)
 	}
 }
 
 func (entry *Entry) fireHooks() {
+	var tmpHooks LevelHooks
 	entry.Logger.mu.Lock()
-	defer entry.Logger.mu.Unlock()
-	err := entry.Logger.Hooks.Fire(entry.Level, entry)
+	tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
+	for k, v := range entry.Logger.Hooks {
+		tmpHooks[k] = v
+	}
+	entry.Logger.mu.Unlock()
+
+	err := tmpHooks.Fire(entry.Level, entry)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
 	}
 }
 
 func (entry *Entry) write() {
-	entry.Logger.mu.Lock()
-	defer entry.Logger.mu.Unlock()
 	serialized, err := entry.Logger.Formatter.Format(entry)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
 		return
 	}
-	if _, err = entry.Logger.Out.Write(serialized); err != nil {
+	entry.Logger.mu.Lock()
+	defer entry.Logger.mu.Unlock()
+	if _, err := entry.Logger.Out.Write(serialized); err != nil {
 		fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
 	}
 }
@@ -319,7 +329,6 @@ func (entry *Entry) Fatal(args ...interface{}) {
 
 func (entry *Entry) Panic(args ...interface{}) {
 	entry.Log(PanicLevel, args...)
-	panic(fmt.Sprint(args...))
 }
 
 // Entry Printf family functions

+ 4 - 1
vendor/github.com/sirupsen/logrus/json_formatter.go

@@ -23,6 +23,9 @@ func (f FieldMap) resolve(key fieldKey) string {
 // JSONFormatter formats logs into parsable json
 type JSONFormatter struct {
 	// TimestampFormat sets the format used for marshaling timestamps.
+	// The format to use is the same than for time.Format or time.Parse from the standard
+	// library.
+	// The standard Library already provides a set of predefined format.
 	TimestampFormat string
 
 	// DisableTimestamp allows disabling automatic timestamps in output
@@ -118,7 +121,7 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
 		encoder.SetIndent("", "  ")
 	}
 	if err := encoder.Encode(data); err != nil {
-		return nil, fmt.Errorf("failed to marshal fields to JSON, %v", err)
+		return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
 	}
 
 	return b.Bytes(), nil

+ 1 - 1
vendor/github.com/sirupsen/logrus/logger.go

@@ -12,7 +12,7 @@ import (
 // LogFunction For big messages, it can be more efficient to pass a function
 // and only call it if the log level is actually enables rather than
 // generating the log message and then checking if the level is enabled
-type LogFunction func()[]interface{}
+type LogFunction func() []interface{}
 
 type Logger struct {
 	// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a

+ 1 - 1
vendor/github.com/sirupsen/logrus/terminal_check_unix.go

@@ -1,4 +1,4 @@
-// +build linux aix
+// +build linux aix zos
 // +build !js
 
 package logrus

+ 6 - 1
vendor/github.com/sirupsen/logrus/text_formatter.go

@@ -53,7 +53,10 @@ type TextFormatter struct {
 	// the time passed since beginning of execution.
 	FullTimestamp bool
 
-	// TimestampFormat to use for display when a full timestamp is printed
+	// TimestampFormat to use for display when a full timestamp is printed.
+	// The format to use is the same than for time.Format or time.Parse from the standard
+	// library.
+	// The standard Library already provides a set of predefined format.
 	TimestampFormat string
 
 	// The fields are sorted by default for a consistent output. For applications
@@ -235,6 +238,8 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
 		levelColor = yellow
 	case ErrorLevel, FatalLevel, PanicLevel:
 		levelColor = red
+	case InfoLevel:
+		levelColor = blue
 	default:
 		levelColor = blue
 	}

Some files were not shown because too many files changed in this diff