mirror of
https://github.com/opencontainers/runc.git
synced 2026-07-11 06:03:57 +08:00
Merge pull request #3530 from opencontainers/dependabot/go_modules/github.com/cilium/ebpf-0.9.1
build(deps): bump github.com/cilium/ebpf from 0.9.0 to 0.9.1
This commit is contained in:
+15
-5
@@ -3,8 +3,11 @@
|
||||
# Pin the default clang to a stable version.
|
||||
CLANG ?= clang-14
|
||||
STRIP ?= llvm-strip-14
|
||||
OBJCOPY ?= llvm-objcopy-14
|
||||
CFLAGS := -O2 -g -Wall -Werror $(CFLAGS)
|
||||
|
||||
CI_KERNEL_URL ?= https://github.com/cilium/ci-kernels/raw/master/
|
||||
|
||||
# Obtain an absolute path to the directory of the Makefile.
|
||||
# Assume the Makefile is in the root of the repository.
|
||||
REPODIR := $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
@@ -18,6 +21,7 @@ CONTAINER_RUN_ARGS ?= $(if $(filter ${CONTAINER_ENGINE}, podman), --log-driver=n
|
||||
IMAGE := $(shell cat ${REPODIR}/testdata/docker/IMAGE)
|
||||
VERSION := $(shell cat ${REPODIR}/testdata/docker/VERSION)
|
||||
|
||||
|
||||
# clang <8 doesn't tag relocs properly (STT_NOTYPE)
|
||||
# clang 9 is the first version emitting BTF
|
||||
TARGETS := \
|
||||
@@ -93,8 +97,14 @@ testdata/loader-%-eb.elf: testdata/loader.c
|
||||
$(CLANG) $(CFLAGS) -target bpfeb -c $< -o $@
|
||||
$(STRIP) -g $@
|
||||
|
||||
# Usage: make VMLINUX=/path/to/vmlinux vmlinux-btf
|
||||
.PHONY: vmlinux-btf
|
||||
vmlinux-btf: btf/testdata/vmlinux-btf.gz
|
||||
btf/testdata/vmlinux-btf.gz: $(VMLINUX)
|
||||
objcopy --dump-section .BTF=/dev/stdout "$<" /dev/null | gzip > "$@"
|
||||
.PHONY: generate-btf
|
||||
generate-btf: KERNEL_VERSION?=5.18
|
||||
generate-btf:
|
||||
$(eval TMP := $(shell mktemp -d))
|
||||
curl -fL "$(CI_KERNEL_URL)/linux-$(KERNEL_VERSION).bz" -o "$(TMP)/bzImage"
|
||||
./testdata/extract-vmlinux "$(TMP)/bzImage" > "$(TMP)/vmlinux"
|
||||
$(OBJCOPY) --dump-section .BTF=/dev/stdout "$(TMP)/vmlinux" /dev/null | gzip > "btf/testdata/vmlinux.btf.gz"
|
||||
curl -fL "$(CI_KERNEL_URL)/linux-$(KERNEL_VERSION)-selftests-bpf.tgz" -o "$(TMP)/selftests.tgz"
|
||||
tar -xf "$(TMP)/selftests.tgz" --to-stdout tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.ko | \
|
||||
$(OBJCOPY) --dump-section .BTF="btf/testdata/btf_testmod.btf" - /dev/null
|
||||
$(RM) -r "$(TMP)"
|
||||
|
||||
+2
-1
@@ -54,7 +54,8 @@ This library includes the following packages:
|
||||
|
||||
* A version of Go that is [supported by
|
||||
upstream](https://golang.org/doc/devel/release.html#policy)
|
||||
* Linux >= 4.4. CI is run against LTS releases.
|
||||
* Linux >= 4.9. CI is run against kernel.org LTS releases. 4.4 should work but is
|
||||
not tested against.
|
||||
|
||||
## Regenerating Testdata
|
||||
|
||||
|
||||
+28
@@ -201,6 +201,34 @@ const (
|
||||
FnGetFuncIp
|
||||
FnGetAttachCookie
|
||||
FnTaskPtRegs
|
||||
FnGetBranchSnapshot
|
||||
FnTraceVprintk
|
||||
FnSkcToUnixSock
|
||||
FnKallsymsLookupName
|
||||
FnFindVma
|
||||
FnLoop
|
||||
FnStrncmp
|
||||
FnGetFuncArg
|
||||
FnGetFuncRet
|
||||
FnGetFuncArgCnt
|
||||
FnGetRetval
|
||||
FnSetRetval
|
||||
FnXdpGetBuffLen
|
||||
FnXdpLoadBytes
|
||||
FnXdpStoreBytes
|
||||
FnCopyFromUserTask
|
||||
FnSkbSetTstamp
|
||||
FnImaFileHash
|
||||
FnKptrXchg
|
||||
FnMapLookupPercpuElem
|
||||
FnSkcToMptcpSock
|
||||
FnDynptrFromMem
|
||||
FnRingbufReserveDynptr
|
||||
FnRingbufSubmitDynptr
|
||||
FnRingbufDiscardDynptr
|
||||
FnDynptrRead
|
||||
FnDynptrWrite
|
||||
FnDynptrData
|
||||
|
||||
maxBuiltinFunc
|
||||
)
|
||||
|
||||
+31
-2
@@ -184,11 +184,40 @@ func _() {
|
||||
_ = x[FnGetFuncIp-173]
|
||||
_ = x[FnGetAttachCookie-174]
|
||||
_ = x[FnTaskPtRegs-175]
|
||||
_ = x[FnGetBranchSnapshot-176]
|
||||
_ = x[FnTraceVprintk-177]
|
||||
_ = x[FnSkcToUnixSock-178]
|
||||
_ = x[FnKallsymsLookupName-179]
|
||||
_ = x[FnFindVma-180]
|
||||
_ = x[FnLoop-181]
|
||||
_ = x[FnStrncmp-182]
|
||||
_ = x[FnGetFuncArg-183]
|
||||
_ = x[FnGetFuncRet-184]
|
||||
_ = x[FnGetFuncArgCnt-185]
|
||||
_ = x[FnGetRetval-186]
|
||||
_ = x[FnSetRetval-187]
|
||||
_ = x[FnXdpGetBuffLen-188]
|
||||
_ = x[FnXdpLoadBytes-189]
|
||||
_ = x[FnXdpStoreBytes-190]
|
||||
_ = x[FnCopyFromUserTask-191]
|
||||
_ = x[FnSkbSetTstamp-192]
|
||||
_ = x[FnImaFileHash-193]
|
||||
_ = x[FnKptrXchg-194]
|
||||
_ = x[FnMapLookupPercpuElem-195]
|
||||
_ = x[FnSkcToMptcpSock-196]
|
||||
_ = x[FnDynptrFromMem-197]
|
||||
_ = x[FnRingbufReserveDynptr-198]
|
||||
_ = x[FnRingbufSubmitDynptr-199]
|
||||
_ = x[FnRingbufDiscardDynptr-200]
|
||||
_ = x[FnDynptrRead-201]
|
||||
_ = x[FnDynptrWrite-202]
|
||||
_ = x[FnDynptrData-203]
|
||||
_ = x[maxBuiltinFunc-204]
|
||||
}
|
||||
|
||||
const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegs"
|
||||
const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDatamaxBuiltinFunc"
|
||||
|
||||
var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497, 2508, 2526, 2538, 2551, 2562, 2579, 2591}
|
||||
var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497, 2508, 2526, 2538, 2551, 2562, 2579, 2591, 2610, 2624, 2639, 2659, 2668, 2674, 2683, 2695, 2707, 2722, 2733, 2744, 2759, 2773, 2788, 2806, 2820, 2833, 2843, 2864, 2880, 2895, 2917, 2938, 2960, 2972, 2985, 2997, 3011}
|
||||
|
||||
func (i BuiltinFunc) String() string {
|
||||
if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) {
|
||||
|
||||
+13
-5
@@ -8,6 +8,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/cilium/ebpf/internal/sys"
|
||||
@@ -568,9 +569,8 @@ func (insns Instructions) SymbolOffsets() (map[string]int, error) {
|
||||
|
||||
// FunctionReferences returns a set of symbol names these Instructions make
|
||||
// bpf-to-bpf calls to.
|
||||
func (insns Instructions) FunctionReferences() map[string]bool {
|
||||
calls := make(map[string]bool)
|
||||
|
||||
func (insns Instructions) FunctionReferences() []string {
|
||||
calls := make(map[string]struct{})
|
||||
for _, ins := range insns {
|
||||
if ins.Constant != -1 {
|
||||
// BPF-to-BPF calls have -1 constants.
|
||||
@@ -585,10 +585,16 @@ func (insns Instructions) FunctionReferences() map[string]bool {
|
||||
continue
|
||||
}
|
||||
|
||||
calls[ins.Reference()] = true
|
||||
calls[ins.Reference()] = struct{}{}
|
||||
}
|
||||
|
||||
return calls
|
||||
result := make([]string, 0, len(calls))
|
||||
for call := range calls {
|
||||
result = append(result, call)
|
||||
}
|
||||
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// ReferenceOffsets returns the set of references and their offset in
|
||||
@@ -667,6 +673,8 @@ func (insns Instructions) Format(f fmt.State, c rune) {
|
||||
|
||||
// Marshal encodes a BPF program into the kernel format.
|
||||
//
|
||||
// insns may be modified if there are unresolved jumps or bpf2bpf calls.
|
||||
//
|
||||
// Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
|
||||
// without a matching Symbol Instruction within insns.
|
||||
func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
|
||||
|
||||
+86
-30
@@ -27,7 +27,7 @@ var (
|
||||
)
|
||||
|
||||
// ID represents the unique ID of a BTF object.
|
||||
type ID uint32
|
||||
type ID = sys.BTFID
|
||||
|
||||
// Spec represents decoded BTF.
|
||||
type Spec struct {
|
||||
@@ -35,8 +35,8 @@ type Spec struct {
|
||||
rawTypes []rawType
|
||||
strings *stringTable
|
||||
|
||||
// All types contained by the spec, the position of a type in the slice
|
||||
// is its ID.
|
||||
// All types contained by the spec. For the base type, the position of
|
||||
// a type in the slice is its ID.
|
||||
types types
|
||||
|
||||
// Type IDs indexed by type.
|
||||
@@ -199,26 +199,39 @@ func loadSpecFromELF(file *internal.SafeELFFile) (*Spec, error) {
|
||||
return nil, fmt.Errorf("compressed BTF is not supported")
|
||||
}
|
||||
|
||||
return loadRawSpec(btfSection.ReaderAt, file.ByteOrder, sectionSizes, vars)
|
||||
rawTypes, rawStrings, err := parseBTF(btfSection.ReaderAt, file.ByteOrder, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = fixupDatasec(rawTypes, rawStrings, sectionSizes, vars)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return inflateSpec(rawTypes, rawStrings, file.ByteOrder, nil)
|
||||
}
|
||||
|
||||
func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
|
||||
rawTypes, rawStrings, err := parseBTF(btf, bo)
|
||||
func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder,
|
||||
baseTypes types, baseStrings *stringTable) (*Spec, error) {
|
||||
|
||||
rawTypes, rawStrings, err := parseBTF(btf, bo, baseStrings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = fixupDatasec(rawTypes, rawStrings, sectionSizes, variableOffsets)
|
||||
return inflateSpec(rawTypes, rawStrings, bo, baseTypes)
|
||||
}
|
||||
|
||||
func inflateSpec(rawTypes []rawType, rawStrings *stringTable, bo binary.ByteOrder,
|
||||
baseTypes types) (*Spec, error) {
|
||||
|
||||
types, err := inflateRawTypes(rawTypes, baseTypes, rawStrings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
types, err := inflateRawTypes(rawTypes, rawStrings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
typeIDs, typesByName := indexTypes(types)
|
||||
typeIDs, typesByName := indexTypes(types, TypeID(len(baseTypes)))
|
||||
|
||||
return &Spec{
|
||||
rawTypes: rawTypes,
|
||||
@@ -230,7 +243,7 @@ func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, sectionSizes map[string]u
|
||||
}, nil
|
||||
}
|
||||
|
||||
func indexTypes(types []Type) (map[Type]TypeID, map[essentialName][]Type) {
|
||||
func indexTypes(types []Type, typeIDOffset TypeID) (map[Type]TypeID, map[essentialName][]Type) {
|
||||
namedTypes := 0
|
||||
for _, typ := range types {
|
||||
if typ.TypeName() != "" {
|
||||
@@ -248,7 +261,7 @@ func indexTypes(types []Type) (map[Type]TypeID, map[essentialName][]Type) {
|
||||
if name := newEssentialName(typ.TypeName()); name != "" {
|
||||
typesByName[name] = append(typesByName[name], typ)
|
||||
}
|
||||
typeIDs[typ] = TypeID(i)
|
||||
typeIDs[typ] = TypeID(i) + typeIDOffset
|
||||
}
|
||||
|
||||
return typeIDs, typesByName
|
||||
@@ -353,14 +366,15 @@ func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder {
|
||||
|
||||
// parseBTF reads a .BTF section into memory and parses it into a list of
|
||||
// raw types and a string table.
|
||||
func parseBTF(btf io.ReaderAt, bo binary.ByteOrder) ([]rawType, *stringTable, error) {
|
||||
func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([]rawType, *stringTable, error) {
|
||||
buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64)
|
||||
header, err := parseBTFHeader(buf, bo)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("parsing .BTF header: %v", err)
|
||||
}
|
||||
|
||||
rawStrings, err := readStringTable(io.NewSectionReader(btf, header.stringStart(), int64(header.StringLen)))
|
||||
rawStrings, err := readStringTable(io.NewSectionReader(btf, header.stringStart(), int64(header.StringLen)),
|
||||
baseStrings)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("can't read type names: %w", err)
|
||||
}
|
||||
@@ -433,7 +447,11 @@ func fixupDatasec(rawTypes []rawType, rawStrings *stringTable, sectionSizes map[
|
||||
func (s *Spec) Copy() *Spec {
|
||||
types := copyTypes(s.types, nil)
|
||||
|
||||
typeIDs, typesByName := indexTypes(types)
|
||||
typeIDOffset := TypeID(0)
|
||||
if len(s.types) != 0 {
|
||||
typeIDOffset = s.typeIDs[s.types[0]]
|
||||
}
|
||||
typeIDs, typesByName := indexTypes(types, typeIDOffset)
|
||||
|
||||
// NB: Other parts of spec are not copied since they are immutable.
|
||||
return &Spec{
|
||||
@@ -633,6 +651,14 @@ func (s *Spec) TypeByName(name string, typ interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadSplitSpecFromReader loads split BTF from a reader.
|
||||
//
|
||||
// Types from base are used to resolve references in the split BTF.
|
||||
// The returned Spec only contains types from the split BTF, not from the base.
|
||||
func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) {
|
||||
return loadRawSpec(r, internal.NativeEndian, base.types, base.strings)
|
||||
}
|
||||
|
||||
// TypesIterator iterates over types of a given spec.
|
||||
type TypesIterator struct {
|
||||
spec *Spec
|
||||
@@ -659,8 +685,10 @@ func (iter *TypesIterator) Next() bool {
|
||||
|
||||
// Handle is a reference to BTF loaded into the kernel.
|
||||
type Handle struct {
|
||||
spec *Spec
|
||||
fd *sys.FD
|
||||
fd *sys.FD
|
||||
|
||||
// Size of the raw BTF in bytes.
|
||||
size uint32
|
||||
}
|
||||
|
||||
// NewHandle loads BTF into the kernel.
|
||||
@@ -698,16 +726,18 @@ func NewHandle(spec *Spec) (*Handle, error) {
|
||||
attr.BtfLogBuf = sys.NewSlicePointer(logBuf)
|
||||
attr.BtfLogSize = uint32(len(logBuf))
|
||||
attr.BtfLogLevel = 1
|
||||
_, logErr := sys.BtfLoad(attr)
|
||||
// NB: The syscall will never return ENOSPC as of 5.18-rc4.
|
||||
return nil, internal.ErrorWithLog(err, logBuf, logErr)
|
||||
_, _ = sys.BtfLoad(attr)
|
||||
return nil, internal.ErrorWithLog(err, logBuf)
|
||||
}
|
||||
|
||||
return &Handle{spec.Copy(), fd}, nil
|
||||
return &Handle{fd, attr.BtfSize}, nil
|
||||
}
|
||||
|
||||
// NewHandleFromID returns the BTF handle for a given id.
|
||||
//
|
||||
// Prefer calling [ebpf.Program.Handle] or [ebpf.Map.Handle] if possible.
|
||||
//
|
||||
// Returns ErrNotExist, if there is no BTF with the given id.
|
||||
//
|
||||
// Requires CAP_SYS_ADMIN.
|
||||
@@ -716,27 +746,48 @@ func NewHandleFromID(id ID) (*Handle, error) {
|
||||
Id: uint32(id),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get BTF by id: %w", err)
|
||||
return nil, fmt.Errorf("get FD for ID %d: %w", id, err)
|
||||
}
|
||||
|
||||
info, err := newInfoFromFd(fd)
|
||||
info, err := newHandleInfoFromFD(fd)
|
||||
if err != nil {
|
||||
_ = fd.Close()
|
||||
return nil, fmt.Errorf("get BTF spec for handle: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Handle{info.BTF, fd}, nil
|
||||
return &Handle{fd, info.size}, nil
|
||||
}
|
||||
|
||||
// Spec returns the Spec that defined the BTF loaded into the kernel.
|
||||
func (h *Handle) Spec() *Spec {
|
||||
return h.spec
|
||||
// Spec parses the kernel BTF into Go types.
|
||||
//
|
||||
// base is used to decode split BTF and may be nil.
|
||||
func (h *Handle) Spec(base *Spec) (*Spec, error) {
|
||||
var btfInfo sys.BtfInfo
|
||||
btfBuffer := make([]byte, h.size)
|
||||
btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer)
|
||||
|
||||
if err := sys.ObjInfo(h.fd, &btfInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var baseTypes types
|
||||
var baseStrings *stringTable
|
||||
if base != nil {
|
||||
baseTypes = base.types
|
||||
baseStrings = base.strings
|
||||
}
|
||||
|
||||
return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, baseTypes, baseStrings)
|
||||
}
|
||||
|
||||
// Close destroys the handle.
|
||||
//
|
||||
// Subsequent calls to FD will return an invalid value.
|
||||
func (h *Handle) Close() error {
|
||||
if h == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return h.fd.Close()
|
||||
}
|
||||
|
||||
@@ -745,6 +796,11 @@ func (h *Handle) FD() int {
|
||||
return h.fd.Int()
|
||||
}
|
||||
|
||||
// Info returns metadata about the handle.
|
||||
func (h *Handle) Info() (*HandleInfo, error) {
|
||||
return newHandleInfoFromFD(h.fd)
|
||||
}
|
||||
|
||||
func marshalBTF(types interface{}, strings []byte, bo binary.ByteOrder) []byte {
|
||||
const minHeaderLength = 24
|
||||
|
||||
|
||||
+57
-8
@@ -130,13 +130,22 @@ func mask(len uint32) uint32 {
|
||||
return (1 << len) - 1
|
||||
}
|
||||
|
||||
func readBits(value, len, shift uint32) uint32 {
|
||||
return (value >> shift) & mask(len)
|
||||
}
|
||||
|
||||
func writeBits(value, len, shift, new uint32) uint32 {
|
||||
value &^= mask(len) << shift
|
||||
value |= (new & mask(len)) << shift
|
||||
return value
|
||||
}
|
||||
|
||||
func (bt *btfType) info(len, shift uint32) uint32 {
|
||||
return (bt.Info >> shift) & mask(len)
|
||||
return readBits(bt.Info, len, shift)
|
||||
}
|
||||
|
||||
func (bt *btfType) setInfo(value, len, shift uint32) {
|
||||
bt.Info &^= mask(len) << shift
|
||||
bt.Info |= (value & mask(len)) << shift
|
||||
bt.Info = writeBits(bt.Info, len, shift, value)
|
||||
}
|
||||
|
||||
func (bt *btfType) Kind() btfKind {
|
||||
@@ -198,6 +207,50 @@ func (rt *rawType) Marshal(w io.Writer, bo binary.ByteOrder) error {
|
||||
return binary.Write(w, bo, rt.data)
|
||||
}
|
||||
|
||||
// btfInt encodes additional data for integers.
|
||||
//
|
||||
// ? ? ? ? e e e e o o o o o o o o ? ? ? ? ? ? ? ? b b b b b b b b
|
||||
// ? = undefined
|
||||
// e = encoding
|
||||
// o = offset (bitfields?)
|
||||
// b = bits (bitfields)
|
||||
type btfInt struct {
|
||||
Raw uint32
|
||||
}
|
||||
|
||||
const (
|
||||
btfIntEncodingLen = 4
|
||||
btfIntEncodingShift = 24
|
||||
btfIntOffsetLen = 8
|
||||
btfIntOffsetShift = 16
|
||||
btfIntBitsLen = 8
|
||||
btfIntBitsShift = 0
|
||||
)
|
||||
|
||||
func (bi btfInt) Encoding() IntEncoding {
|
||||
return IntEncoding(readBits(bi.Raw, btfIntEncodingLen, btfIntEncodingShift))
|
||||
}
|
||||
|
||||
func (bi *btfInt) SetEncoding(e IntEncoding) {
|
||||
bi.Raw = writeBits(uint32(bi.Raw), btfIntEncodingLen, btfIntEncodingShift, uint32(e))
|
||||
}
|
||||
|
||||
func (bi btfInt) Offset() Bits {
|
||||
return Bits(readBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift))
|
||||
}
|
||||
|
||||
func (bi *btfInt) SetOffset(offset uint32) {
|
||||
bi.Raw = writeBits(bi.Raw, btfIntOffsetLen, btfIntOffsetShift, offset)
|
||||
}
|
||||
|
||||
func (bi btfInt) Bits() Bits {
|
||||
return Bits(readBits(bi.Raw, btfIntBitsLen, btfIntBitsShift))
|
||||
}
|
||||
|
||||
func (bi *btfInt) SetBits(bits byte) {
|
||||
bi.Raw = writeBits(bi.Raw, btfIntBitsLen, btfIntBitsShift, uint32(bits))
|
||||
}
|
||||
|
||||
type btfArray struct {
|
||||
Type TypeID
|
||||
IndexType TypeID
|
||||
@@ -249,7 +302,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, err
|
||||
var data interface{}
|
||||
switch header.Kind() {
|
||||
case kindInt:
|
||||
data = new(uint32)
|
||||
data = new(btfInt)
|
||||
case kindPointer:
|
||||
case kindArray:
|
||||
data = new(btfArray)
|
||||
@@ -288,7 +341,3 @@ func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, err
|
||||
types = append(types, rawType{header, data})
|
||||
}
|
||||
}
|
||||
|
||||
func intEncoding(raw uint32) (IntEncoding, Bits, Bits) {
|
||||
return IntEncoding((raw & 0x0f000000) >> 24), Bits(raw&0x00ff0000) >> 16, Bits(raw & 0x000000ff)
|
||||
}
|
||||
|
||||
+14
-1
@@ -65,7 +65,20 @@ func (gf *GoFormatter) writeTypeDecl(name string, typ Type) error {
|
||||
|
||||
switch v := skipQualifiers(typ).(type) {
|
||||
case *Enum:
|
||||
fmt.Fprintf(&gf.w, "type %s int32", name)
|
||||
fmt.Fprintf(&gf.w, "type %s ", name)
|
||||
switch v.Size {
|
||||
case 1:
|
||||
gf.w.WriteString("int8")
|
||||
case 2:
|
||||
gf.w.WriteString("int16")
|
||||
case 4:
|
||||
gf.w.WriteString("int32")
|
||||
case 8:
|
||||
gf.w.WriteString("int64")
|
||||
default:
|
||||
return fmt.Errorf("%s: invalid enum size %d", typ, v.Size)
|
||||
}
|
||||
|
||||
if len(v.Values) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cilium/ebpf/internal/sys"
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
// HandleInfo describes a Handle.
|
||||
type HandleInfo struct {
|
||||
// ID of this handle in the kernel. The ID is only valid as long as the
|
||||
// associated handle is kept alive.
|
||||
ID ID
|
||||
|
||||
// Name is an identifying name for the BTF, currently only used by the
|
||||
// kernel.
|
||||
Name string
|
||||
|
||||
// IsKernel is true if the BTF originated with the kernel and not
|
||||
// userspace.
|
||||
IsKernel bool
|
||||
|
||||
// Size of the raw BTF in bytes.
|
||||
size uint32
|
||||
}
|
||||
|
||||
func newHandleInfoFromFD(fd *sys.FD) (*HandleInfo, error) {
|
||||
// We invoke the syscall once with a empty BTF and name buffers to get size
|
||||
// information to allocate buffers. Then we invoke it a second time with
|
||||
// buffers to receive the data.
|
||||
var btfInfo sys.BtfInfo
|
||||
if err := sys.ObjInfo(fd, &btfInfo); err != nil {
|
||||
return nil, fmt.Errorf("get BTF info for fd %s: %w", fd, err)
|
||||
}
|
||||
|
||||
if btfInfo.NameLen > 0 {
|
||||
// NameLen doesn't account for the terminating NUL.
|
||||
btfInfo.NameLen++
|
||||
}
|
||||
|
||||
// Don't pull raw BTF by default, since it may be quite large.
|
||||
btfSize := btfInfo.BtfSize
|
||||
btfInfo.BtfSize = 0
|
||||
|
||||
nameBuffer := make([]byte, btfInfo.NameLen)
|
||||
btfInfo.Name, btfInfo.NameLen = sys.NewSlicePointerLen(nameBuffer)
|
||||
if err := sys.ObjInfo(fd, &btfInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &HandleInfo{
|
||||
ID: ID(btfInfo.Id),
|
||||
Name: unix.ByteSliceToString(nameBuffer),
|
||||
IsKernel: btfInfo.KernelBtf != 0,
|
||||
size: btfSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// IsModule returns true if the BTF is for the kernel itself.
|
||||
func (i *HandleInfo) IsVmlinux() bool {
|
||||
return i.IsKernel && i.Name == "vmlinux"
|
||||
}
|
||||
|
||||
// IsModule returns true if the BTF is for a kernel module.
|
||||
func (i *HandleInfo) IsModule() bool {
|
||||
return i.IsKernel && i.Name != "vmlinux"
|
||||
}
|
||||
|
||||
// HandleIterator allows enumerating BTF blobs loaded into the kernel.
|
||||
type HandleIterator struct {
|
||||
// The ID of the last retrieved handle. Only valid after a call to Next.
|
||||
ID ID
|
||||
err error
|
||||
}
|
||||
|
||||
// Next retrieves a handle for the next BTF blob.
|
||||
//
|
||||
// [Handle.Close] is called if *handle is non-nil to avoid leaking fds.
|
||||
//
|
||||
// Returns true if another BTF blob was found. Call [HandleIterator.Err] after
|
||||
// the function returns false.
|
||||
func (it *HandleIterator) Next(handle **Handle) bool {
|
||||
if *handle != nil {
|
||||
(*handle).Close()
|
||||
*handle = nil
|
||||
}
|
||||
|
||||
id := it.ID
|
||||
for {
|
||||
attr := &sys.BtfGetNextIdAttr{Id: id}
|
||||
err := sys.BtfGetNextId(attr)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// There are no more BTF objects.
|
||||
return false
|
||||
} else if err != nil {
|
||||
it.err = fmt.Errorf("get next BTF ID: %w", err)
|
||||
return false
|
||||
}
|
||||
|
||||
id = attr.NextId
|
||||
*handle, err = NewHandleFromID(id)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// Try again with the next ID.
|
||||
continue
|
||||
} else if err != nil {
|
||||
it.err = fmt.Errorf("retrieve handle for ID %d: %w", id, err)
|
||||
return false
|
||||
}
|
||||
|
||||
it.ID = id
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Err returns an error if iteration failed for some reason.
|
||||
func (it *HandleIterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package btf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/cilium/ebpf/internal"
|
||||
"github.com/cilium/ebpf/internal/sys"
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
// info describes a BTF object.
|
||||
type info struct {
|
||||
BTF *Spec
|
||||
ID ID
|
||||
// Name is an identifying name for the BTF, currently only used by the
|
||||
// kernel.
|
||||
Name string
|
||||
// KernelBTF is true if the BTf originated with the kernel and not
|
||||
// userspace.
|
||||
KernelBTF bool
|
||||
}
|
||||
|
||||
func newInfoFromFd(fd *sys.FD) (*info, error) {
|
||||
// We invoke the syscall once with a empty BTF and name buffers to get size
|
||||
// information to allocate buffers. Then we invoke it a second time with
|
||||
// buffers to receive the data.
|
||||
var btfInfo sys.BtfInfo
|
||||
if err := sys.ObjInfo(fd, &btfInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
btfBuffer := make([]byte, btfInfo.BtfSize)
|
||||
nameBuffer := make([]byte, btfInfo.NameLen)
|
||||
btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer)
|
||||
btfInfo.Name, btfInfo.NameLen = sys.NewSlicePointerLen(nameBuffer)
|
||||
if err := sys.ObjInfo(fd, &btfInfo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spec, err := loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &info{
|
||||
BTF: spec,
|
||||
ID: ID(btfInfo.Id),
|
||||
Name: unix.ByteSliceToString(nameBuffer),
|
||||
KernelBTF: btfInfo.KernelBtf != 0,
|
||||
}, nil
|
||||
}
|
||||
+20
-4
@@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
type stringTable struct {
|
||||
base *stringTable
|
||||
offsets []uint32
|
||||
strings []string
|
||||
}
|
||||
@@ -19,7 +20,15 @@ type sizedReader interface {
|
||||
Size() int64
|
||||
}
|
||||
|
||||
func readStringTable(r sizedReader) (*stringTable, error) {
|
||||
func readStringTable(r sizedReader, base *stringTable) (*stringTable, error) {
|
||||
// When parsing split BTF's string table, the first entry offset is derived
|
||||
// from the last entry offset of the base BTF.
|
||||
firstStringOffset := uint32(0)
|
||||
if base != nil {
|
||||
idx := len(base.offsets) - 1
|
||||
firstStringOffset = base.offsets[idx] + uint32(len(base.strings[idx])) + 1
|
||||
}
|
||||
|
||||
// Derived from vmlinux BTF.
|
||||
const averageStringLength = 16
|
||||
|
||||
@@ -27,7 +36,7 @@ func readStringTable(r sizedReader) (*stringTable, error) {
|
||||
offsets := make([]uint32, 0, n)
|
||||
strings := make([]string, 0, n)
|
||||
|
||||
offset := uint32(0)
|
||||
offset := firstStringOffset
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Split(splitNull)
|
||||
for scanner.Scan() {
|
||||
@@ -44,11 +53,11 @@ func readStringTable(r sizedReader) (*stringTable, error) {
|
||||
return nil, errors.New("string table is empty")
|
||||
}
|
||||
|
||||
if strings[0] != "" {
|
||||
if firstStringOffset == 0 && strings[0] != "" {
|
||||
return nil, errors.New("first item in string table is non-empty")
|
||||
}
|
||||
|
||||
return &stringTable{offsets, strings}, nil
|
||||
return &stringTable{base, offsets, strings}, nil
|
||||
}
|
||||
|
||||
func splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
@@ -64,6 +73,13 @@ func splitNull(data []byte, atEOF bool) (advance int, token []byte, err error) {
|
||||
}
|
||||
|
||||
func (st *stringTable) Lookup(offset uint32) (string, error) {
|
||||
if st.base != nil && offset <= st.base.offsets[len(st.base.offsets)-1] {
|
||||
return st.base.lookup(offset)
|
||||
}
|
||||
return st.lookup(offset)
|
||||
}
|
||||
|
||||
func (st *stringTable) lookup(offset uint32) (string, error) {
|
||||
i := search(st.offsets, offset)
|
||||
if i == len(st.offsets) || st.offsets[i] != offset {
|
||||
return "", fmt.Errorf("offset %d isn't start of a string", offset)
|
||||
|
||||
+58
-27
@@ -151,17 +151,22 @@ func (p *Pointer) copy() Type {
|
||||
|
||||
// Array is an array with a fixed number of elements.
|
||||
type Array struct {
|
||||
Index Type
|
||||
Type Type
|
||||
Nelems uint32
|
||||
}
|
||||
|
||||
func (arr *Array) Format(fs fmt.State, verb rune) {
|
||||
formatType(fs, verb, arr, "type=", arr.Type, "n=", arr.Nelems)
|
||||
formatType(fs, verb, arr, "index=", arr.Index, "type=", arr.Type, "n=", arr.Nelems)
|
||||
}
|
||||
|
||||
func (arr *Array) TypeName() string { return "" }
|
||||
|
||||
func (arr *Array) walk(tdq *typeDeque) { tdq.push(&arr.Type) }
|
||||
func (arr *Array) walk(tdq *typeDeque) {
|
||||
tdq.push(&arr.Index)
|
||||
tdq.push(&arr.Type)
|
||||
}
|
||||
|
||||
func (arr *Array) copy() Type {
|
||||
cpy := *arr
|
||||
return &cpy
|
||||
@@ -266,12 +271,14 @@ type Member struct {
|
||||
|
||||
// Enum lists possible values.
|
||||
type Enum struct {
|
||||
Name string
|
||||
Name string
|
||||
// Size of the enum value in bytes.
|
||||
Size uint32
|
||||
Values []EnumValue
|
||||
}
|
||||
|
||||
func (e *Enum) Format(fs fmt.State, verb rune) {
|
||||
formatType(fs, verb, e, "values=", len(e.Values))
|
||||
formatType(fs, verb, e, "size=", e.Size, "values=", len(e.Values))
|
||||
}
|
||||
|
||||
func (e *Enum) TypeName() string { return e.Name }
|
||||
@@ -284,7 +291,7 @@ type EnumValue struct {
|
||||
Value int32
|
||||
}
|
||||
|
||||
func (e *Enum) size() uint32 { return 4 }
|
||||
func (e *Enum) size() uint32 { return e.Size }
|
||||
func (e *Enum) walk(*typeDeque) {}
|
||||
func (e *Enum) copy() Type {
|
||||
cpy := *e
|
||||
@@ -771,12 +778,24 @@ func (dq *typeDeque) all() []*Type {
|
||||
// inflateRawTypes takes a list of raw btf types linked via type IDs, and turns
|
||||
// it into a graph of Types connected via pointers.
|
||||
//
|
||||
// Returns a map of named types (so, where NameOff is non-zero) and a slice of types
|
||||
// indexed by TypeID. Since BTF ignores compilation units, multiple types may share
|
||||
// the same name. A Type may form a cyclic graph by pointing at itself.
|
||||
func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable) ([]Type, error) {
|
||||
types := make([]Type, 0, len(rawTypes)+1)
|
||||
types = append(types, (*Void)(nil))
|
||||
// If baseTypes are provided, then the raw types are
|
||||
// considered to be of a split BTF (e.g., a kernel module).
|
||||
//
|
||||
// Returns a slice of types indexed by TypeID. Since BTF ignores compilation
|
||||
// units, multiple types may share the same name. A Type may form a cyclic graph
|
||||
// by pointing at itself.
|
||||
func inflateRawTypes(rawTypes []rawType, baseTypes types, rawStrings *stringTable) ([]Type, error) {
|
||||
types := make([]Type, 0, len(rawTypes)+1) // +1 for Void added to base types
|
||||
|
||||
typeIDOffset := TypeID(1) // Void is TypeID(0), so the rest starts from TypeID(1)
|
||||
|
||||
if baseTypes == nil {
|
||||
// Void is defined to always be type ID 0, and is thus omitted from BTF.
|
||||
types = append(types, (*Void)(nil))
|
||||
} else {
|
||||
// For split BTF, the next ID is max base BTF type ID + 1
|
||||
typeIDOffset = TypeID(len(baseTypes))
|
||||
}
|
||||
|
||||
type fixupDef struct {
|
||||
id TypeID
|
||||
@@ -785,9 +804,18 @@ func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable) ([]Type, error
|
||||
|
||||
var fixups []fixupDef
|
||||
fixup := func(id TypeID, typ *Type) {
|
||||
if id < TypeID(len(types)) {
|
||||
if id < TypeID(len(baseTypes)) {
|
||||
*typ = baseTypes[id]
|
||||
return
|
||||
}
|
||||
|
||||
idx := id
|
||||
if baseTypes != nil {
|
||||
idx = id - TypeID(len(baseTypes))
|
||||
}
|
||||
if idx < TypeID(len(types)) {
|
||||
// We've already inflated this type, fix it up immediately.
|
||||
*typ = types[id]
|
||||
*typ = types[idx]
|
||||
return
|
||||
}
|
||||
fixups = append(fixups, fixupDef{id, typ})
|
||||
@@ -877,9 +905,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable) ([]Type, error
|
||||
|
||||
for i, raw := range rawTypes {
|
||||
var (
|
||||
// Void is defined to always be type ID 0, and is thus
|
||||
// omitted from BTF.
|
||||
id = TypeID(i + 1)
|
||||
id = typeIDOffset + TypeID(i)
|
||||
typ Type
|
||||
)
|
||||
|
||||
@@ -891,11 +917,11 @@ func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable) ([]Type, error
|
||||
switch raw.Kind() {
|
||||
case kindInt:
|
||||
size := raw.Size()
|
||||
encoding, offset, bits := intEncoding(*raw.data.(*uint32))
|
||||
if offset > 0 || bits.Bytes() != size {
|
||||
legacyBitfields[id] = [2]Bits{offset, bits}
|
||||
bi := raw.data.(*btfInt)
|
||||
if bi.Offset() > 0 || bi.Bits().Bytes() != size {
|
||||
legacyBitfields[id] = [2]Bits{bi.Offset(), bi.Bits()}
|
||||
}
|
||||
typ = &Int{name, size, encoding}
|
||||
typ = &Int{name, raw.Size(), bi.Encoding()}
|
||||
|
||||
case kindPointer:
|
||||
ptr := &Pointer{nil}
|
||||
@@ -904,10 +930,8 @@ func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable) ([]Type, error
|
||||
|
||||
case kindArray:
|
||||
btfArr := raw.data.(*btfArray)
|
||||
|
||||
// IndexType is unused according to btf.rst.
|
||||
// Don't make it available right now.
|
||||
arr := &Array{nil, btfArr.Nelems}
|
||||
arr := &Array{nil, nil, btfArr.Nelems}
|
||||
fixup(btfArr.IndexType, &arr.Index)
|
||||
fixup(btfArr.Type, &arr.Type)
|
||||
typ = arr
|
||||
|
||||
@@ -938,7 +962,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable) ([]Type, error
|
||||
Value: btfVal.Val,
|
||||
})
|
||||
}
|
||||
typ = &Enum{name, vals}
|
||||
typ = &Enum{name, raw.Size(), vals}
|
||||
|
||||
case kindForward:
|
||||
if raw.KindFlag() {
|
||||
@@ -1030,14 +1054,21 @@ func inflateRawTypes(rawTypes []rawType, rawStrings *stringTable) ([]Type, error
|
||||
|
||||
for _, fixup := range fixups {
|
||||
i := int(fixup.id)
|
||||
if i >= len(types) {
|
||||
if i >= len(types)+len(baseTypes) {
|
||||
return nil, fmt.Errorf("reference to invalid type id: %d", fixup.id)
|
||||
}
|
||||
if i < len(baseTypes) {
|
||||
return nil, fmt.Errorf("fixup for base type id %d is not expected", i)
|
||||
}
|
||||
|
||||
*fixup.typ = types[i]
|
||||
*fixup.typ = types[i-len(baseTypes)]
|
||||
}
|
||||
|
||||
for _, bitfieldFixup := range bitfieldFixups {
|
||||
if bitfieldFixup.id < TypeID(len(baseTypes)) {
|
||||
return nil, fmt.Errorf("bitfield fixup from split to base types is not expected")
|
||||
}
|
||||
|
||||
data, ok := legacyBitfields[bitfieldFixup.id]
|
||||
if ok {
|
||||
// This is indeed a legacy bitfield, fix it up.
|
||||
|
||||
+16
-15
@@ -264,14 +264,17 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer loader.cleanup()
|
||||
defer loader.close()
|
||||
|
||||
// Support assigning Programs and Maps, lazy-loading the required objects.
|
||||
assignedMaps := make(map[string]bool)
|
||||
assignedProgs := make(map[string]bool)
|
||||
|
||||
getValue := func(typ reflect.Type, name string) (interface{}, error) {
|
||||
switch typ {
|
||||
|
||||
case reflect.TypeOf((*Program)(nil)):
|
||||
assignedProgs[name] = true
|
||||
return loader.loadProgram(name)
|
||||
|
||||
case reflect.TypeOf((*Map)(nil)):
|
||||
@@ -311,7 +314,13 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
|
||||
}
|
||||
}
|
||||
|
||||
loader.finalize()
|
||||
// Prevent loader.cleanup() from closing assigned Maps and Programs.
|
||||
for m := range assignedMaps {
|
||||
delete(loader.maps, m)
|
||||
}
|
||||
for p := range assignedProgs {
|
||||
delete(loader.programs, p)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -342,7 +351,7 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer loader.cleanup()
|
||||
defer loader.close()
|
||||
|
||||
// Create maps first, as their fds need to be linked into programs.
|
||||
for mapName := range spec.Maps {
|
||||
@@ -367,9 +376,9 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Prevent loader.cleanup from closing maps and programs.
|
||||
maps, progs := loader.maps, loader.programs
|
||||
|
||||
loader.finalize()
|
||||
loader.maps, loader.programs = nil, nil
|
||||
|
||||
return &Collection{
|
||||
progs,
|
||||
@@ -441,16 +450,8 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec
|
||||
}, nil
|
||||
}
|
||||
|
||||
// finalize should be called when all the collectionLoader's resources
|
||||
// have been successfully loaded into the kernel and populated with values.
|
||||
func (cl *collectionLoader) finalize() {
|
||||
cl.maps, cl.programs = nil, nil
|
||||
}
|
||||
|
||||
// cleanup cleans up all resources left over in the collectionLoader.
|
||||
// Call finalize() when Map and Program creation/population is successful
|
||||
// to prevent them from getting closed.
|
||||
func (cl *collectionLoader) cleanup() {
|
||||
// close all resources left over in the collectionLoader.
|
||||
func (cl *collectionLoader) close() {
|
||||
cl.handles.close()
|
||||
for _, m := range cl.maps {
|
||||
m.Close()
|
||||
|
||||
+6
-4
@@ -283,6 +283,7 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
|
||||
progs := make(map[string]*ProgramSpec)
|
||||
|
||||
// Generate a ProgramSpec for each function found in each program section.
|
||||
var export []string
|
||||
for _, sec := range ec.sections {
|
||||
if sec.kind != programSection {
|
||||
continue
|
||||
@@ -319,13 +320,14 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
|
||||
return nil, fmt.Errorf("duplicate program name %s", name)
|
||||
}
|
||||
progs[name] = spec
|
||||
|
||||
if spec.SectionName != ".text" {
|
||||
export = append(export, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate each prog's references with pointers to all of its callees.
|
||||
if err := populateReferences(progs); err != nil {
|
||||
return nil, fmt.Errorf("populating references: %w", err)
|
||||
}
|
||||
flattenPrograms(progs, export)
|
||||
|
||||
// Hide programs (e.g. library functions) that were not explicitly emitted
|
||||
// to an ELF section. These could be exposed in a separate CollectionSpec
|
||||
|
||||
+1
@@ -177,6 +177,7 @@ func (pi *ProgramInfo) ID() (ProgramID, bool) {
|
||||
|
||||
// BTFID returns the BTF ID associated with the program.
|
||||
//
|
||||
// The ID is only valid as long as the associated program is kept alive.
|
||||
// Available from 5.0.
|
||||
//
|
||||
// The bool return value indicates whether this optional field is available and
|
||||
|
||||
+179
-20
@@ -2,46 +2,205 @@ package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ErrorWithLog returns an error that includes logs from the
|
||||
// kernel verifier.
|
||||
// ErrorWithLog returns an error which includes logs from the kernel verifier.
|
||||
//
|
||||
// logErr should be the error returned by the syscall that generated
|
||||
// the log. It is used to check for truncation of the output.
|
||||
func ErrorWithLog(err error, log []byte, logErr error) error {
|
||||
// The default error output is a summary of the full log. The latter can be
|
||||
// accessed via VerifierError.Log or by formatting the error, see Format.
|
||||
//
|
||||
// A set of heuristics is used to determine whether the log has been truncated.
|
||||
func ErrorWithLog(err error, log []byte) *VerifierError {
|
||||
const whitespace = "\t\r\v\n "
|
||||
|
||||
// Convert verifier log C string by truncating it on the first 0 byte
|
||||
// and trimming trailing whitespace before interpreting as a Go string.
|
||||
truncated := false
|
||||
if i := bytes.IndexByte(log, 0); i != -1 {
|
||||
if i == len(log)-1 && !bytes.HasSuffix(log[:i], []byte{'\n'}) {
|
||||
// The null byte is at the end of the buffer and it's not preceded
|
||||
// by a newline character. Most likely the buffer was too short.
|
||||
truncated = true
|
||||
}
|
||||
|
||||
log = log[:i]
|
||||
}
|
||||
logStr := string(bytes.Trim(log, "\t\r\n "))
|
||||
|
||||
if errors.Is(logErr, unix.ENOSPC) {
|
||||
logStr += " (truncated...)"
|
||||
} else if len(log) > 0 {
|
||||
// No null byte? Dodgy!
|
||||
truncated = true
|
||||
}
|
||||
|
||||
return &VerifierError{err, logStr}
|
||||
log = bytes.Trim(log, whitespace)
|
||||
logLines := bytes.Split(log, []byte{'\n'})
|
||||
lines := make([]string, 0, len(logLines))
|
||||
for _, line := range logLines {
|
||||
// Don't remove leading white space on individual lines. We rely on it
|
||||
// when outputting logs.
|
||||
lines = append(lines, string(bytes.TrimRight(line, whitespace)))
|
||||
}
|
||||
|
||||
return &VerifierError{err, lines, truncated}
|
||||
}
|
||||
|
||||
// VerifierError includes information from the eBPF verifier.
|
||||
//
|
||||
// It summarises the log output, see Format if you want to output the full contents.
|
||||
type VerifierError struct {
|
||||
cause error
|
||||
log string
|
||||
// The error which caused this error.
|
||||
Cause error
|
||||
// The verifier output split into lines.
|
||||
Log []string
|
||||
// Whether the log output is truncated, based on several heuristics.
|
||||
Truncated bool
|
||||
}
|
||||
|
||||
func (le *VerifierError) Unwrap() error {
|
||||
return le.cause
|
||||
return le.Cause
|
||||
}
|
||||
|
||||
func (le *VerifierError) Error() string {
|
||||
if le.log == "" {
|
||||
return le.cause.Error()
|
||||
log := le.Log
|
||||
if n := len(log); n > 0 && strings.HasPrefix(log[n-1], "processed ") {
|
||||
// Get rid of "processed 39 insns (limit 1000000) ..." from summary.
|
||||
log = log[:n-1]
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s: %s", le.cause, le.log)
|
||||
n := len(log)
|
||||
if n == 0 {
|
||||
return le.Cause.Error()
|
||||
}
|
||||
|
||||
lines := log[n-1:]
|
||||
if n >= 2 && (includePreviousLine(log[n-1]) || le.Truncated) {
|
||||
// Add one more line of context if it aids understanding the error.
|
||||
lines = log[n-2:]
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
fmt.Fprintf(&b, "%s: ", le.Cause.Error())
|
||||
|
||||
for i, line := range lines {
|
||||
b.WriteString(strings.TrimSpace(line))
|
||||
if i != len(lines)-1 {
|
||||
b.WriteString(": ")
|
||||
}
|
||||
}
|
||||
|
||||
omitted := len(le.Log) - len(lines)
|
||||
if omitted == 0 && !le.Truncated {
|
||||
return b.String()
|
||||
}
|
||||
|
||||
b.WriteString(" (")
|
||||
if le.Truncated {
|
||||
b.WriteString("truncated")
|
||||
}
|
||||
|
||||
if omitted > 0 {
|
||||
if le.Truncated {
|
||||
b.WriteString(", ")
|
||||
}
|
||||
fmt.Fprintf(&b, "%d line(s) omitted", omitted)
|
||||
}
|
||||
b.WriteString(")")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// includePreviousLine returns true if the given line likely is better
|
||||
// understood with additional context from the preceding line.
|
||||
func includePreviousLine(line string) bool {
|
||||
// We need to find a good trade off between understandable error messages
|
||||
// and too much complexity here. Checking the string prefix is ok, requiring
|
||||
// regular expressions to do it is probably overkill.
|
||||
|
||||
if strings.HasPrefix(line, "\t") {
|
||||
// [13] STRUCT drm_rect size=16 vlen=4
|
||||
// \tx1 type_id=2
|
||||
return true
|
||||
}
|
||||
|
||||
if len(line) >= 2 && line[0] == 'R' && line[1] >= '0' && line[1] <= '9' {
|
||||
// 0: (95) exit
|
||||
// R0 !read_ok
|
||||
return true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(line, "invalid bpf_context access") {
|
||||
// 0: (79) r6 = *(u64 *)(r1 +0)
|
||||
// func '__x64_sys_recvfrom' arg0 type FWD is not a struct
|
||||
// invalid bpf_context access off=0 size=8
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Format the error.
|
||||
//
|
||||
// Understood verbs are %s and %v, which are equivalent to calling Error(). %v
|
||||
// allows outputting additional information using the following flags:
|
||||
//
|
||||
// + Output the first <width> lines, or all lines if no width is given.
|
||||
// - Output the last <width> lines, or all lines if no width is given.
|
||||
//
|
||||
// Use width to specify how many lines to output. Use the '-' flag to output
|
||||
// lines from the end of the log instead of the beginning.
|
||||
func (le *VerifierError) Format(f fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
_, _ = io.WriteString(f, le.Error())
|
||||
|
||||
case 'v':
|
||||
n, haveWidth := f.Width()
|
||||
if !haveWidth || n > len(le.Log) {
|
||||
n = len(le.Log)
|
||||
}
|
||||
|
||||
if !f.Flag('+') && !f.Flag('-') {
|
||||
if haveWidth {
|
||||
_, _ = io.WriteString(f, "%!v(BADWIDTH)")
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = io.WriteString(f, le.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if f.Flag('+') && f.Flag('-') {
|
||||
_, _ = io.WriteString(f, "%!v(BADFLAG)")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(f, "%s:", le.Cause.Error())
|
||||
|
||||
omitted := len(le.Log) - n
|
||||
lines := le.Log[:n]
|
||||
if f.Flag('-') {
|
||||
// Print last instead of first lines.
|
||||
lines = le.Log[len(le.Log)-n:]
|
||||
if omitted > 0 {
|
||||
fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted)
|
||||
}
|
||||
}
|
||||
|
||||
for _, line := range lines {
|
||||
fmt.Fprintf(f, "\n\t%s", line)
|
||||
}
|
||||
|
||||
if !f.Flag('-') {
|
||||
if omitted > 0 {
|
||||
fmt.Fprintf(f, "\n\t(%d line(s) omitted)", omitted)
|
||||
}
|
||||
}
|
||||
|
||||
if le.Truncated {
|
||||
fmt.Fprintf(f, "\n\t(truncated)")
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Fprintf(f, "%%!%c(BADVERB)", verb)
|
||||
}
|
||||
}
|
||||
|
||||
+12
-1
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/cilium/ebpf/internal/sys"
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
@@ -24,7 +25,17 @@ func Pin(currentPath, newPath string, fd *sys.FD) error {
|
||||
var statfs unix.Statfs_t
|
||||
if err := unix.Statfs(filepath.Dir(newPath), &statfs); err != nil {
|
||||
return err
|
||||
} else if uint64(statfs.Type) != bpfFSType {
|
||||
}
|
||||
|
||||
fsType := int64(statfs.Type)
|
||||
if unsafe.Sizeof(statfs.Type) == 4 {
|
||||
// We're on a 32 bit arch, where statfs.Type is int32. bpfFSType is a
|
||||
// negative number when interpreted as int32 so we need to cast via
|
||||
// uint32 to avoid sign extension.
|
||||
fsType = int64(uint32(statfs.Type))
|
||||
}
|
||||
|
||||
if fsType != bpfFSType {
|
||||
return fmt.Errorf("%s is not on a bpf filesystem", newPath)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3,4 +3,4 @@ package sys
|
||||
|
||||
// Regenerate types.go by invoking go generate in the current directory.
|
||||
|
||||
//go:generate go run github.com/cilium/ebpf/internal/cmd/gentypes ../../btf/testdata/vmlinux-btf.gz
|
||||
//go:generate go run github.com/cilium/ebpf/internal/cmd/gentypes ../../btf/testdata/vmlinux.btf.gz
|
||||
|
||||
+3
@@ -93,6 +93,9 @@ func NewObjName(name string) ObjName {
|
||||
// LinkID uniquely identifies a bpf_link.
|
||||
type LinkID uint32
|
||||
|
||||
// BTFID uniquely identifies a BTF blob loaded into the kernel.
|
||||
type BTFID uint32
|
||||
|
||||
// wrappedErrno wraps syscall.Errno to prevent direct comparisons with
|
||||
// syscall.E* or unix.E* constants.
|
||||
//
|
||||
|
||||
+53
-7
@@ -58,7 +58,8 @@ const (
|
||||
BPF_SK_REUSEPORT_SELECT AttachType = 39
|
||||
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE AttachType = 40
|
||||
BPF_PERF_EVENT AttachType = 41
|
||||
__MAX_BPF_ATTACH_TYPE AttachType = 42
|
||||
BPF_TRACE_KPROBE_MULTI AttachType = 42
|
||||
__MAX_BPF_ATTACH_TYPE AttachType = 43
|
||||
)
|
||||
|
||||
type Cmd int32
|
||||
@@ -292,7 +293,15 @@ const (
|
||||
BPF_FUNC_get_func_arg FunctionId = 183
|
||||
BPF_FUNC_get_func_ret FunctionId = 184
|
||||
BPF_FUNC_get_func_arg_cnt FunctionId = 185
|
||||
__BPF_FUNC_MAX_ID FunctionId = 186
|
||||
BPF_FUNC_get_retval FunctionId = 186
|
||||
BPF_FUNC_set_retval FunctionId = 187
|
||||
BPF_FUNC_xdp_get_buff_len FunctionId = 188
|
||||
BPF_FUNC_xdp_load_bytes FunctionId = 189
|
||||
BPF_FUNC_xdp_store_bytes FunctionId = 190
|
||||
BPF_FUNC_copy_from_user_task FunctionId = 191
|
||||
BPF_FUNC_skb_set_tstamp FunctionId = 192
|
||||
BPF_FUNC_ima_file_hash FunctionId = 193
|
||||
__BPF_FUNC_MAX_ID FunctionId = 194
|
||||
)
|
||||
|
||||
type HdrStartOff int32
|
||||
@@ -313,7 +322,8 @@ const (
|
||||
BPF_LINK_TYPE_NETNS LinkType = 5
|
||||
BPF_LINK_TYPE_XDP LinkType = 6
|
||||
BPF_LINK_TYPE_PERF_EVENT LinkType = 7
|
||||
MAX_BPF_LINK_TYPE LinkType = 8
|
||||
BPF_LINK_TYPE_KPROBE_MULTI LinkType = 8
|
||||
MAX_BPF_LINK_TYPE LinkType = 9
|
||||
)
|
||||
|
||||
type MapType int32
|
||||
@@ -432,7 +442,7 @@ const (
|
||||
type BtfInfo struct {
|
||||
Btf Pointer
|
||||
BtfSize uint32
|
||||
Id uint32
|
||||
Id BTFID
|
||||
Name Pointer
|
||||
NameLen uint32
|
||||
KernelBtf uint32
|
||||
@@ -517,6 +527,30 @@ type ProgInfo struct {
|
||||
_ [4]byte
|
||||
}
|
||||
|
||||
type SkLookup struct {
|
||||
Cookie uint64
|
||||
Family uint32
|
||||
Protocol uint32
|
||||
RemoteIp4 [4]uint8
|
||||
RemoteIp6 [16]uint8
|
||||
RemotePort uint16
|
||||
_ [2]byte
|
||||
LocalIp4 [4]uint8
|
||||
LocalIp6 [16]uint8
|
||||
LocalPort uint32
|
||||
IngressIfindex uint32
|
||||
_ [4]byte
|
||||
}
|
||||
|
||||
type XdpMd struct {
|
||||
Data uint32
|
||||
DataEnd uint32
|
||||
DataMeta uint32
|
||||
IngressIfindex uint32
|
||||
RxQueueIndex uint32
|
||||
EgressIfindex uint32
|
||||
}
|
||||
|
||||
type BtfGetFdByIdAttr struct{ Id uint32 }
|
||||
|
||||
func BtfGetFdById(attr *BtfGetFdByIdAttr) (*FD, error) {
|
||||
@@ -527,6 +561,16 @@ func BtfGetFdById(attr *BtfGetFdByIdAttr) (*FD, error) {
|
||||
return NewFD(int(fd))
|
||||
}
|
||||
|
||||
type BtfGetNextIdAttr struct {
|
||||
Id BTFID
|
||||
NextId BTFID
|
||||
}
|
||||
|
||||
func BtfGetNextId(attr *BtfGetNextIdAttr) error {
|
||||
_, err := BPF(BPF_BTF_GET_NEXT_ID, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||
return err
|
||||
}
|
||||
|
||||
type BtfLoadAttr struct {
|
||||
Btf Pointer
|
||||
BtfLogBuf Pointer
|
||||
@@ -573,7 +617,7 @@ type LinkCreateAttr struct {
|
||||
AttachType AttachType
|
||||
Flags uint32
|
||||
TargetBtfId uint32
|
||||
_ [12]byte
|
||||
_ [28]byte
|
||||
}
|
||||
|
||||
func LinkCreate(attr *LinkCreateAttr) (*FD, error) {
|
||||
@@ -591,7 +635,7 @@ type LinkCreateIterAttr struct {
|
||||
Flags uint32
|
||||
IterInfo Pointer
|
||||
IterInfoLen uint32
|
||||
_ [4]byte
|
||||
_ [20]byte
|
||||
}
|
||||
|
||||
func LinkCreateIter(attr *LinkCreateIterAttr) (*FD, error) {
|
||||
@@ -608,7 +652,7 @@ type LinkCreatePerfEventAttr struct {
|
||||
AttachType AttachType
|
||||
Flags uint32
|
||||
BpfCookie uint64
|
||||
_ [8]byte
|
||||
_ [24]byte
|
||||
}
|
||||
|
||||
func LinkCreatePerfEvent(attr *LinkCreatePerfEventAttr) (*FD, error) {
|
||||
@@ -954,6 +998,8 @@ type ProgRunAttr struct {
|
||||
CtxOut Pointer
|
||||
Flags uint32
|
||||
Cpu uint32
|
||||
BatchSize uint32
|
||||
_ [4]byte
|
||||
}
|
||||
|
||||
func ProgRun(attr *ProgRunAttr) error {
|
||||
|
||||
-12
@@ -56,18 +56,6 @@ func AttachCgroup(opts CgroupOptions) (Link, error) {
|
||||
return cg, nil
|
||||
}
|
||||
|
||||
// LoadPinnedCgroup loads a pinned cgroup from a bpffs.
|
||||
//
|
||||
// Deprecated: use LoadPinnedLink instead.
|
||||
func LoadPinnedCgroup(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
|
||||
link, err := LoadPinnedRawLink(fileName, CgroupType, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &linkCgroup{*link}, nil
|
||||
}
|
||||
|
||||
type progAttachCgroup struct {
|
||||
cgroup *os.File
|
||||
current *ebpf.Program
|
||||
|
||||
-12
@@ -58,18 +58,6 @@ func AttachIter(opts IterOptions) (*Iter, error) {
|
||||
return &Iter{RawLink{fd, ""}}, err
|
||||
}
|
||||
|
||||
// LoadPinnedIter loads a pinned iterator from a bpffs.
|
||||
//
|
||||
// Deprecated: use LoadPinnedLink instead.
|
||||
func LoadPinnedIter(fileName string, opts *ebpf.LoadPinOptions) (*Iter, error) {
|
||||
link, err := LoadPinnedRawLink(fileName, IterType, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Iter{*link}, err
|
||||
}
|
||||
|
||||
// Iter represents an attached bpf_iter.
|
||||
type Iter struct {
|
||||
RawLink
|
||||
|
||||
+10
-1
@@ -350,7 +350,7 @@ func tracefsKprobe(args probeArgs) (*perfEvent, error) {
|
||||
// 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, args probeArgs) (*perfEvent, error) {
|
||||
func tracefsProbe(typ probeType, args probeArgs) (_ *perfEvent, err 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.
|
||||
@@ -376,6 +376,15 @@ func tracefsProbe(typ probeType, args probeArgs) (*perfEvent, error) {
|
||||
if err := createTraceFSProbeEvent(typ, args); err != nil {
|
||||
return nil, fmt.Errorf("creating probe entry on tracefs: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// Make sure we clean up the created tracefs event when we return error.
|
||||
// If a livepatch handler is already active on the symbol, the write to
|
||||
// tracefs will succeed, a trace event will show up, but creating the
|
||||
// perf event will fail with EBUSY.
|
||||
_ = closeTraceFSProbeEvent(typ, args.group, args.symbol)
|
||||
}
|
||||
}()
|
||||
|
||||
// Get the newly-created trace event's id.
|
||||
tid, err := getTraceEventID(group, args.symbol)
|
||||
|
||||
-35
@@ -107,11 +107,6 @@ type Info struct {
|
||||
extra interface{}
|
||||
}
|
||||
|
||||
// RawLinkInfo contains information on a raw link.
|
||||
//
|
||||
// Deprecated: use Info instead.
|
||||
type RawLinkInfo = Info
|
||||
|
||||
type TracingInfo sys.TracingLinkInfo
|
||||
type CgroupInfo sys.CgroupLinkInfo
|
||||
type NetNsInfo sys.NetNsLinkInfo
|
||||
@@ -188,36 +183,6 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
|
||||
return &RawLink{fd, ""}, nil
|
||||
}
|
||||
|
||||
// LoadPinnedRawLink loads a persisted link from a bpffs.
|
||||
//
|
||||
// Returns an error if the pinned link type doesn't match linkType. Pass
|
||||
// UnspecifiedType to disable this behaviour.
|
||||
//
|
||||
// Deprecated: use LoadPinnedLink instead.
|
||||
func LoadPinnedRawLink(fileName string, linkType Type, opts *ebpf.LoadPinOptions) (*RawLink, error) {
|
||||
link, err := loadPinnedRawLink(fileName, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if linkType == UnspecifiedType {
|
||||
return link, nil
|
||||
}
|
||||
|
||||
info, err := link.Info()
|
||||
if err != nil {
|
||||
link.Close()
|
||||
return nil, fmt.Errorf("get pinned link info: %w", err)
|
||||
}
|
||||
|
||||
if info.Type != linkType {
|
||||
link.Close()
|
||||
return nil, fmt.Errorf("link type %v doesn't match %v", info.Type, linkType)
|
||||
}
|
||||
|
||||
return link, nil
|
||||
}
|
||||
|
||||
func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {
|
||||
fd, err := sys.ObjGet(&sys.ObjGetAttr{
|
||||
Pathname: sys.NewStringPointer(fileName),
|
||||
|
||||
-12
@@ -34,15 +34,3 @@ func AttachNetNs(ns int, prog *ebpf.Program) (*NetNsLink, error) {
|
||||
|
||||
return &NetNsLink{*link}, nil
|
||||
}
|
||||
|
||||
// LoadPinnedNetNs loads a network namespace link from bpffs.
|
||||
//
|
||||
// Deprecated: use LoadPinnedLink instead.
|
||||
func LoadPinnedNetNs(fileName string, opts *ebpf.LoadPinOptions) (*NetNsLink, error) {
|
||||
link, err := LoadPinnedRawLink(fileName, NetNsType, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &NetNsLink{*link}, nil
|
||||
}
|
||||
|
||||
+8
-23
@@ -41,27 +41,24 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (
|
||||
typeID btf.TypeID
|
||||
)
|
||||
if targetProg != nil {
|
||||
info, err := targetProg.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
btfID, ok := info.BTFID()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not get BTF ID for program %s: %w", info.Name, errInvalidInput)
|
||||
}
|
||||
btfHandle, err := btf.NewHandleFromID(btfID)
|
||||
btfHandle, err := targetProg.Handle()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer btfHandle.Close()
|
||||
|
||||
spec, err := btfHandle.Spec(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var function *btf.Func
|
||||
if err := btfHandle.Spec().TypeByName(name, &function); err != nil {
|
||||
if err := spec.TypeByName(name, &function); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
target = targetProg.FD()
|
||||
typeID, err = btfHandle.Spec().TypeID(function)
|
||||
typeID, err = spec.TypeID(function)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -80,18 +77,6 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (
|
||||
return &tracing{*link}, nil
|
||||
}
|
||||
|
||||
// LoadPinnedFreplace loads a pinned iterator from a bpffs.
|
||||
//
|
||||
// Deprecated: use LoadPinnedLink instead.
|
||||
func LoadPinnedFreplace(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
|
||||
link, err := LoadPinnedRawLink(fileName, TracingType, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &tracing{*link}, err
|
||||
}
|
||||
|
||||
type TracingOptions struct {
|
||||
// Program must be of type Tracing with attach type
|
||||
// AttachTraceFEntry/AttachTraceFExit/AttachModifyReturn or
|
||||
|
||||
+46
-30
@@ -42,15 +42,21 @@ var (
|
||||
type Executable struct {
|
||||
// Path of the executable on the filesystem.
|
||||
path string
|
||||
// Parsed ELF symbols and dynamic symbols offsets.
|
||||
offsets map[string]uint64
|
||||
// Parsed ELF and dynamic symbols' addresses.
|
||||
addresses map[string]uint64
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Symbol address. Must be provided in case of external symbols (shared libs).
|
||||
// If set, overrides the address eventually parsed from the executable.
|
||||
Address uint64
|
||||
// The offset relative to given symbol. Useful when tracing an arbitrary point
|
||||
// inside the frame of given symbol.
|
||||
//
|
||||
// Note: this field changed from being an absolute offset to being relative
|
||||
// to Address.
|
||||
Offset uint64
|
||||
// Only set the uprobe on the given process ID. Useful when tracing
|
||||
// shared library calls or programs that have many running instances.
|
||||
@@ -100,8 +106,8 @@ func OpenExecutable(path string) (*Executable, error) {
|
||||
}
|
||||
|
||||
ex := Executable{
|
||||
path: path,
|
||||
offsets: make(map[string]uint64),
|
||||
path: path,
|
||||
addresses: make(map[string]uint64),
|
||||
}
|
||||
|
||||
if err := ex.load(se); err != nil {
|
||||
@@ -130,7 +136,7 @@ func (ex *Executable) load(f *internal.SafeELFFile) error {
|
||||
continue
|
||||
}
|
||||
|
||||
off := s.Value
|
||||
address := s.Value
|
||||
|
||||
// Loop over ELF segments.
|
||||
for _, prog := range f.Progs {
|
||||
@@ -146,32 +152,42 @@ func (ex *Executable) load(f *internal.SafeELFFile) error {
|
||||
// fn symbol offset = fn symbol VA - .text VA + .text offset
|
||||
//
|
||||
// stackoverflow.com/a/40249502
|
||||
off = s.Value - prog.Vaddr + prog.Off
|
||||
address = s.Value - prog.Vaddr + prog.Off
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
ex.offsets[s.Name] = off
|
||||
ex.addresses[s.Name] = address
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ex *Executable) offset(symbol string) (uint64, error) {
|
||||
if off, ok := ex.offsets[symbol]; ok {
|
||||
// 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.
|
||||
//
|
||||
// Since only offset values are stored and not elf.Symbol, if the value is 0,
|
||||
// assume it's an external symbol.
|
||||
if off == 0 {
|
||||
return 0, fmt.Errorf("cannot resolve %s library call '%s', "+
|
||||
"consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported)
|
||||
}
|
||||
return off, nil
|
||||
// address calculates the address of a symbol in the executable.
|
||||
//
|
||||
// opts must not be nil.
|
||||
func (ex *Executable) address(symbol string, opts *UprobeOptions) (uint64, error) {
|
||||
if opts.Address > 0 {
|
||||
return opts.Address + opts.Offset, nil
|
||||
}
|
||||
return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
|
||||
|
||||
address, ok := ex.addresses[symbol]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
// Since only offset values are stored and not elf.Symbol, if the value is 0,
|
||||
// assume it's an external symbol.
|
||||
if address == 0 {
|
||||
return 0, fmt.Errorf("cannot resolve %s library call '%s': %w "+
|
||||
"(consider providing UprobeOptions.Address)", ex.path, symbol, ErrNotSupported)
|
||||
}
|
||||
|
||||
return address + opts.Offset, nil
|
||||
}
|
||||
|
||||
// Uprobe attaches the given eBPF program to a perf event that fires when the
|
||||
@@ -186,6 +202,8 @@ func (ex *Executable) offset(symbol string) (uint64, error) {
|
||||
//
|
||||
// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
|
||||
//
|
||||
// Note: Setting the Offset field in the options supersedes the symbol's offset.
|
||||
//
|
||||
// Losing the reference to the resulting Link (up) will close the Uprobe
|
||||
// and prevent further execution of prog. The Link must be Closed during
|
||||
// program shutdown to avoid leaking system resources.
|
||||
@@ -218,6 +236,8 @@ func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
|
||||
//
|
||||
// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
|
||||
//
|
||||
// Note: Setting the Offset field in the options supersedes the symbol's offset.
|
||||
//
|
||||
// Losing the reference to the resulting Link (up) will close the Uprobe
|
||||
// and prevent further execution of prog. The Link must be Closed during
|
||||
// program shutdown to avoid leaking system resources.
|
||||
@@ -252,13 +272,9 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
|
||||
opts = &UprobeOptions{}
|
||||
}
|
||||
|
||||
offset := opts.Offset
|
||||
if offset == 0 {
|
||||
off, err := ex.offset(symbol)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offset = off
|
||||
offset, err := ex.address(symbol, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pid := opts.PID
|
||||
|
||||
+73
-61
@@ -52,68 +52,9 @@ func splitSymbols(insns asm.Instructions) (map[string]asm.Instructions, error) {
|
||||
// Each function is denoted by an ELF symbol and the compiler takes care of
|
||||
// register setup before each jump instruction.
|
||||
|
||||
// populateReferences populates all of progs' Instructions and references
|
||||
// with their full dependency chains including transient dependencies.
|
||||
func populateReferences(progs map[string]*ProgramSpec) error {
|
||||
type props struct {
|
||||
insns asm.Instructions
|
||||
refs map[string]*ProgramSpec
|
||||
}
|
||||
|
||||
out := make(map[string]props)
|
||||
|
||||
// Resolve and store direct references between all progs.
|
||||
if err := findReferences(progs); err != nil {
|
||||
return fmt.Errorf("finding references: %w", err)
|
||||
}
|
||||
|
||||
// Flatten all progs' instruction streams.
|
||||
for name, prog := range progs {
|
||||
insns, refs := prog.flatten(nil)
|
||||
|
||||
prop := props{
|
||||
insns: insns,
|
||||
refs: refs,
|
||||
}
|
||||
|
||||
out[name] = prop
|
||||
}
|
||||
|
||||
// Replace all progs' instructions and references
|
||||
for name, props := range out {
|
||||
progs[name].Instructions = props.insns
|
||||
progs[name].references = props.refs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// findReferences finds bpf-to-bpf calls between progs and populates each
|
||||
// prog's references field with its direct neighbours.
|
||||
func findReferences(progs map[string]*ProgramSpec) error {
|
||||
// Check all ProgramSpecs in the collection against each other.
|
||||
for _, prog := range progs {
|
||||
prog.references = make(map[string]*ProgramSpec)
|
||||
|
||||
// Look up call targets in progs and store pointers to their corresponding
|
||||
// ProgramSpecs as direct references.
|
||||
for refname := range prog.Instructions.FunctionReferences() {
|
||||
ref := progs[refname]
|
||||
// Call targets are allowed to be missing from an ELF. This occurs when
|
||||
// a program calls into a forward function declaration that is left
|
||||
// unimplemented. This is caught at load time during fixups.
|
||||
if ref != nil {
|
||||
prog.references[refname] = ref
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// hasReferences returns true if insns contains one or more bpf2bpf
|
||||
// hasFunctionReferences returns true if insns contains one or more bpf2bpf
|
||||
// function references.
|
||||
func hasReferences(insns asm.Instructions) bool {
|
||||
func hasFunctionReferences(insns asm.Instructions) bool {
|
||||
for _, i := range insns {
|
||||
if i.IsFunctionReference() {
|
||||
return true
|
||||
@@ -160,6 +101,77 @@ func applyRelocations(insns asm.Instructions, local, target *btf.Spec) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// flattenPrograms resolves bpf-to-bpf calls for a set of programs.
|
||||
//
|
||||
// Links all programs in names by modifying their ProgramSpec in progs.
|
||||
func flattenPrograms(progs map[string]*ProgramSpec, names []string) {
|
||||
// Pre-calculate all function references.
|
||||
refs := make(map[*ProgramSpec][]string)
|
||||
for _, prog := range progs {
|
||||
refs[prog] = prog.Instructions.FunctionReferences()
|
||||
}
|
||||
|
||||
// Create a flattened instruction stream, but don't modify progs yet to
|
||||
// avoid linking multiple times.
|
||||
flattened := make([]asm.Instructions, 0, len(names))
|
||||
for _, name := range names {
|
||||
flattened = append(flattened, flattenInstructions(name, progs, refs))
|
||||
}
|
||||
|
||||
// Finally, assign the flattened instructions.
|
||||
for i, name := range names {
|
||||
progs[name].Instructions = flattened[i]
|
||||
}
|
||||
}
|
||||
|
||||
// flattenInstructions resolves bpf-to-bpf calls for a single program.
|
||||
//
|
||||
// Flattens the instructions of prog by concatenating the instructions of all
|
||||
// direct and indirect dependencies.
|
||||
//
|
||||
// progs contains all referenceable programs, while refs contain the direct
|
||||
// dependencies of each program.
|
||||
func flattenInstructions(name string, progs map[string]*ProgramSpec, refs map[*ProgramSpec][]string) asm.Instructions {
|
||||
prog := progs[name]
|
||||
|
||||
insns := make(asm.Instructions, len(prog.Instructions))
|
||||
copy(insns, prog.Instructions)
|
||||
|
||||
// Add all direct references of prog to the list of to be linked programs.
|
||||
pending := make([]string, len(refs[prog]))
|
||||
copy(pending, refs[prog])
|
||||
|
||||
// All references for which we've appended instructions.
|
||||
linked := make(map[string]bool)
|
||||
|
||||
// Iterate all pending references. We can't use a range since pending is
|
||||
// modified in the body below.
|
||||
for len(pending) > 0 {
|
||||
var ref string
|
||||
ref, pending = pending[0], pending[1:]
|
||||
|
||||
if linked[ref] {
|
||||
// We've already linked this ref, don't append instructions again.
|
||||
continue
|
||||
}
|
||||
|
||||
progRef := progs[ref]
|
||||
if progRef == nil {
|
||||
// We don't have instructions that go with this reference. This
|
||||
// happens when calling extern functions.
|
||||
continue
|
||||
}
|
||||
|
||||
insns = append(insns, progRef.Instructions...)
|
||||
linked[ref] = true
|
||||
|
||||
// Make sure we link indirect references.
|
||||
pending = append(pending, refs[progRef]...)
|
||||
}
|
||||
|
||||
return insns
|
||||
}
|
||||
|
||||
// fixupAndValidate is called by the ELF reader right before marshaling the
|
||||
// instruction stream. It performs last-minute adjustments to the program and
|
||||
// runs some sanity checks before sending it off to the kernel.
|
||||
|
||||
+15
-22
@@ -19,7 +19,6 @@ import (
|
||||
|
||||
// Errors returned by Map and MapIterator methods.
|
||||
var (
|
||||
errFirstKeyNotFound = errors.New("first key not found")
|
||||
ErrKeyNotExist = errors.New("key does not exist")
|
||||
ErrKeyExist = errors.New("key already exists")
|
||||
ErrIterationAborted = errors.New("iteration aborted")
|
||||
@@ -170,7 +169,8 @@ func (ms *MapSpec) checkCompatibility(m *Map) error {
|
||||
case m.valueSize != ms.ValueSize:
|
||||
return fmt.Errorf("expected value size %v, got %v: %w", ms.ValueSize, m.valueSize, ErrMapIncompatible)
|
||||
|
||||
case m.maxEntries != ms.MaxEntries:
|
||||
case !(ms.Type == PerfEventArray && ms.MaxEntries == 0) &&
|
||||
m.maxEntries != ms.MaxEntries:
|
||||
return fmt.Errorf("expected max entries %v, got %v: %w", ms.MaxEntries, m.maxEntries, ErrMapIncompatible)
|
||||
|
||||
case m.flags != ms.Flags:
|
||||
@@ -249,8 +249,8 @@ func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
|
||||
return nil, fmt.Errorf("creating map: %w", err)
|
||||
}
|
||||
|
||||
err = m.finalize(spec)
|
||||
if err != nil {
|
||||
if err := m.finalize(spec); err != nil {
|
||||
m.Close()
|
||||
return nil, fmt.Errorf("populating map: %w", err)
|
||||
}
|
||||
|
||||
@@ -456,6 +456,9 @@ func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions, handles *handleCa
|
||||
if !spec.hasBTF() {
|
||||
return nil, fmt.Errorf("map create without BTF: %w", err)
|
||||
}
|
||||
if errors.Is(err, unix.EINVAL) && attr.MaxEntries == 0 {
|
||||
return nil, fmt.Errorf("map create: %w (MaxEntries may be incorrectly set to zero)", err)
|
||||
}
|
||||
return nil, fmt.Errorf("map create: %w", err)
|
||||
}
|
||||
defer closeOnError(fd)
|
||||
@@ -776,14 +779,14 @@ func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error {
|
||||
// Kernels 4.4.131 and earlier return EFAULT instead of a pointer to the
|
||||
// first map element when a nil key pointer is specified.
|
||||
if key == nil && errors.Is(err, unix.EFAULT) {
|
||||
var guessKey sys.Pointer
|
||||
var guessKey []byte
|
||||
guessKey, err = m.guessNonExistentKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("can't guess starting key: %w", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Retry the syscall with a valid non-existing key.
|
||||
attr.Key = guessKey
|
||||
attr.Key = sys.NewSlicePointer(guessKey)
|
||||
if err = sys.MapGetNextKey(&attr); err == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -798,7 +801,7 @@ func (m *Map) nextKey(key interface{}, nextKeyOut sys.Pointer) error {
|
||||
// guessNonExistentKey attempts to perform a map lookup that returns ENOENT.
|
||||
// This is necessary on kernels before 4.4.132, since those don't support
|
||||
// iterating maps from the start by providing an invalid key pointer.
|
||||
func (m *Map) guessNonExistentKey() (startKey sys.Pointer, err error) {
|
||||
func (m *Map) guessNonExistentKey() ([]byte, error) {
|
||||
// Provide an invalid value pointer to prevent a copy on the kernel side.
|
||||
valuePtr := sys.NewPointer(unsafe.Pointer(^uintptr(0)))
|
||||
randKey := make([]byte, int(m.keySize))
|
||||
@@ -830,11 +833,11 @@ func (m *Map) guessNonExistentKey() (startKey sys.Pointer, err error) {
|
||||
|
||||
err := m.lookup(randKey, valuePtr, 0)
|
||||
if errors.Is(err, ErrKeyNotExist) {
|
||||
return sys.NewSlicePointer(randKey), nil
|
||||
return randKey, nil
|
||||
}
|
||||
}
|
||||
|
||||
return sys.Pointer{}, errFirstKeyNotFound
|
||||
return nil, errors.New("couldn't find non-existing key")
|
||||
}
|
||||
|
||||
// BatchLookup looks up many elements in a map at once.
|
||||
@@ -1036,7 +1039,8 @@ func (m *Map) Iterate() *MapIterator {
|
||||
return newMapIterator(m)
|
||||
}
|
||||
|
||||
// Close removes a Map
|
||||
// Close the Map's underlying file descriptor, which could unload the
|
||||
// Map from the kernel if it is not pinned or in use by a loaded Program.
|
||||
func (m *Map) Close() error {
|
||||
if m == nil {
|
||||
// This makes it easier to clean up when iterating maps
|
||||
@@ -1418,14 +1422,3 @@ func NewMapFromID(id MapID) (*Map, error) {
|
||||
|
||||
return newMapFromFD(fd)
|
||||
}
|
||||
|
||||
// ID returns the systemwide unique ID of the map.
|
||||
//
|
||||
// Deprecated: use MapInfo.ID() instead.
|
||||
func (m *Map) ID() (MapID, error) {
|
||||
var info sys.MapInfo
|
||||
if err := sys.ObjInfo(m.fd, &info); err != nil {
|
||||
return MapID(0), err
|
||||
}
|
||||
return MapID(info.Id), nil
|
||||
}
|
||||
|
||||
+159
-158
@@ -102,9 +102,6 @@ type ProgramSpec struct {
|
||||
|
||||
// The byte order this program was compiled for, may be nil.
|
||||
ByteOrder binary.ByteOrder
|
||||
|
||||
// Programs called by this ProgramSpec. Includes all dependencies.
|
||||
references map[string]*ProgramSpec
|
||||
}
|
||||
|
||||
// Copy returns a copy of the spec.
|
||||
@@ -126,42 +123,7 @@ func (ps *ProgramSpec) Tag() (string, error) {
|
||||
return ps.Instructions.Tag(internal.NativeEndian)
|
||||
}
|
||||
|
||||
// flatten returns spec's full instruction stream including all of its
|
||||
// dependencies and an expanded map of references that includes all symbols
|
||||
// appearing in the instruction stream.
|
||||
//
|
||||
// Returns nil, nil if spec was already visited.
|
||||
func (spec *ProgramSpec) flatten(visited map[*ProgramSpec]bool) (asm.Instructions, map[string]*ProgramSpec) {
|
||||
if visited == nil {
|
||||
visited = make(map[*ProgramSpec]bool)
|
||||
}
|
||||
|
||||
// This program and its dependencies were already collected.
|
||||
if visited[spec] {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
visited[spec] = true
|
||||
|
||||
// Start off with spec's direct references and instructions.
|
||||
progs := spec.references
|
||||
insns := spec.Instructions
|
||||
|
||||
// Recurse into each reference and append/merge its references into
|
||||
// a temporary buffer as to not interfere with the resolution process.
|
||||
for _, ref := range spec.references {
|
||||
if ri, rp := ref.flatten(visited); ri != nil || rp != nil {
|
||||
insns = append(insns, ri...)
|
||||
|
||||
// Merge nested references into the top-level scope.
|
||||
for n, p := range rp {
|
||||
progs[n] = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return insns, progs
|
||||
}
|
||||
type VerifierError = internal.VerifierError
|
||||
|
||||
// Program represents BPF program loaded into the kernel.
|
||||
//
|
||||
@@ -179,8 +141,7 @@ type Program struct {
|
||||
|
||||
// NewProgram creates a new Program.
|
||||
//
|
||||
// Loading a program for the first time will perform
|
||||
// feature detection by loading small, temporary programs.
|
||||
// See NewProgramWithOptions for details.
|
||||
func NewProgram(spec *ProgramSpec) (*Program, error) {
|
||||
return NewProgramWithOptions(spec, ProgramOptions{})
|
||||
}
|
||||
@@ -189,6 +150,9 @@ func NewProgram(spec *ProgramSpec) (*Program, error) {
|
||||
//
|
||||
// Loading a program for the first time will perform
|
||||
// feature detection by loading small, temporary programs.
|
||||
//
|
||||
// Returns an error wrapping VerifierError if the program or its BTF is rejected
|
||||
// by the kernel.
|
||||
func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) {
|
||||
if spec == nil {
|
||||
return nil, errors.New("can't load a program from a nil spec")
|
||||
@@ -329,35 +293,36 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
|
||||
return &Program{unix.ByteSliceToString(logBuf), fd, spec.Name, "", spec.Type}, nil
|
||||
}
|
||||
|
||||
logErr := err
|
||||
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
|
||||
attr.LogSize = uint32(len(logBuf))
|
||||
attr.LogBuf = sys.NewSlicePointer(logBuf)
|
||||
_, _ = sys.ProgLoad(attr)
|
||||
}
|
||||
|
||||
fd, logErr = sys.ProgLoad(attr)
|
||||
if logErr == nil {
|
||||
fd.Close()
|
||||
switch {
|
||||
case errors.Is(err, unix.EPERM):
|
||||
if len(logBuf) > 0 && logBuf[0] == 0 {
|
||||
// EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can
|
||||
// check that the log is empty to reduce false positives.
|
||||
return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err)
|
||||
}
|
||||
|
||||
fallthrough
|
||||
|
||||
case errors.Is(err, unix.EINVAL):
|
||||
if hasFunctionReferences(spec.Instructions) {
|
||||
if err := haveBPFToBPFCalls(); err != nil {
|
||||
return nil, fmt.Errorf("load program: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM)) && hasReferences(spec.Instructions) {
|
||||
if err := haveBPFToBPFCalls(); err != nil {
|
||||
return nil, fmt.Errorf("load program: %w", internal.ErrorWithLog(err, logBuf, logErr))
|
||||
}
|
||||
}
|
||||
|
||||
if errors.Is(logErr, unix.EPERM) && len(logBuf) > 0 && logBuf[0] == 0 {
|
||||
// EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can
|
||||
// check that the log is empty to reduce false positives.
|
||||
return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", logErr)
|
||||
}
|
||||
|
||||
err = internal.ErrorWithLog(err, logBuf, logErr)
|
||||
err = internal.ErrorWithLog(err, logBuf)
|
||||
if btfDisabled {
|
||||
return nil, fmt.Errorf("load program without BTF: %w", err)
|
||||
return nil, fmt.Errorf("load program: %w (BTF disabled)", err)
|
||||
}
|
||||
return nil, fmt.Errorf("load program: %w", err)
|
||||
}
|
||||
@@ -419,6 +384,24 @@ func (p *Program) Info() (*ProgramInfo, error) {
|
||||
return newProgramInfoFromFd(p.fd)
|
||||
}
|
||||
|
||||
// Handle returns a reference to the program's type information in the kernel.
|
||||
//
|
||||
// Returns ErrNotSupported if the kernel has no BTF support, or if there is no
|
||||
// BTF associated with the program.
|
||||
func (p *Program) Handle() (*btf.Handle, error) {
|
||||
info, err := p.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, ok := info.BTFID()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("program %s: retrieve BTF ID: %w", p, ErrNotSupported)
|
||||
}
|
||||
|
||||
return btf.NewHandleFromID(id)
|
||||
}
|
||||
|
||||
// FD gets the file descriptor of the Program.
|
||||
//
|
||||
// It is invalid to call this function after Close has been called.
|
||||
@@ -477,7 +460,9 @@ func (p *Program) IsPinned() bool {
|
||||
return p.pinnedPath != ""
|
||||
}
|
||||
|
||||
// Close unloads the program from the kernel.
|
||||
// Close the Program's underlying file descriptor, which could unload
|
||||
// the program from the kernel if it is not pinned or attached to a
|
||||
// kernel hook.
|
||||
func (p *Program) Close() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
@@ -486,6 +471,28 @@ func (p *Program) Close() error {
|
||||
return p.fd.Close()
|
||||
}
|
||||
|
||||
// Various options for Run'ing a Program
|
||||
type RunOptions struct {
|
||||
// Program's data input. Required field.
|
||||
Data []byte
|
||||
// Program's data after Program has run. Caller must allocate. Optional field.
|
||||
DataOut []byte
|
||||
// Program's context input. Optional field.
|
||||
Context interface{}
|
||||
// Program's context after Program has run. Must be a pointer or slice. Optional field.
|
||||
ContextOut interface{}
|
||||
// Number of times to run Program. Optional field. Defaults to 1.
|
||||
Repeat uint32
|
||||
// Optional flags.
|
||||
Flags uint32
|
||||
// CPU to run Program on. Optional field.
|
||||
// Note not all program types support this field.
|
||||
CPU uint32
|
||||
// Called whenever the syscall is interrupted, and should be set to testing.B.ResetTimer
|
||||
// or similar. Typically used during benchmarking. Optional field.
|
||||
Reset func()
|
||||
}
|
||||
|
||||
// Test runs the Program in the kernel with the given input and returns the
|
||||
// value returned by the eBPF program. outLen may be zero.
|
||||
//
|
||||
@@ -494,11 +501,38 @@ func (p *Program) Close() error {
|
||||
//
|
||||
// This function requires at least Linux 4.12.
|
||||
func (p *Program) Test(in []byte) (uint32, []byte, error) {
|
||||
ret, out, _, err := p.testRun(in, 1, nil)
|
||||
// Older kernels ignore the dataSizeOut argument when copying to user space.
|
||||
// Combined with things like bpf_xdp_adjust_head() we don't really know what the final
|
||||
// size will be. Hence we allocate an output buffer which we hope will always be large
|
||||
// enough, and panic if the kernel wrote past the end of the allocation.
|
||||
// See https://patchwork.ozlabs.org/cover/1006822/
|
||||
var out []byte
|
||||
if len(in) > 0 {
|
||||
out = make([]byte, len(in)+outputPad)
|
||||
}
|
||||
|
||||
opts := RunOptions{
|
||||
Data: in,
|
||||
DataOut: out,
|
||||
Repeat: 1,
|
||||
}
|
||||
|
||||
ret, _, err := p.testRun(&opts)
|
||||
if err != nil {
|
||||
return ret, nil, fmt.Errorf("can't test program: %w", err)
|
||||
}
|
||||
return ret, out, nil
|
||||
return ret, opts.DataOut, nil
|
||||
}
|
||||
|
||||
// Run runs the Program in kernel with given RunOptions.
|
||||
//
|
||||
// Note: the same restrictions from Test apply.
|
||||
func (p *Program) Run(opts *RunOptions) (uint32, error) {
|
||||
ret, _, err := p.testRun(opts)
|
||||
if err != nil {
|
||||
return ret, fmt.Errorf("can't test program: %w", err)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Benchmark runs the Program with the given input for a number of times
|
||||
@@ -513,7 +547,17 @@ func (p *Program) Test(in []byte) (uint32, []byte, error) {
|
||||
//
|
||||
// This function requires at least Linux 4.12.
|
||||
func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.Duration, error) {
|
||||
ret, _, total, err := p.testRun(in, repeat, reset)
|
||||
if uint(repeat) > math.MaxUint32 {
|
||||
return 0, 0, fmt.Errorf("repeat is too high")
|
||||
}
|
||||
|
||||
opts := RunOptions{
|
||||
Data: in,
|
||||
Repeat: uint32(repeat),
|
||||
Reset: reset,
|
||||
}
|
||||
|
||||
ret, total, err := p.testRun(&opts)
|
||||
if err != nil {
|
||||
return ret, total, fmt.Errorf("can't benchmark program: %w", err)
|
||||
}
|
||||
@@ -565,37 +609,42 @@ var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() e
|
||||
return err
|
||||
})
|
||||
|
||||
func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte, time.Duration, error) {
|
||||
if uint(repeat) > math.MaxUint32 {
|
||||
return 0, nil, 0, fmt.Errorf("repeat is too high")
|
||||
}
|
||||
|
||||
if len(in) == 0 {
|
||||
return 0, nil, 0, fmt.Errorf("missing input")
|
||||
}
|
||||
|
||||
if uint(len(in)) > math.MaxUint32 {
|
||||
return 0, nil, 0, fmt.Errorf("input is too long")
|
||||
func (p *Program) testRun(opts *RunOptions) (uint32, time.Duration, error) {
|
||||
if uint(len(opts.Data)) > math.MaxUint32 {
|
||||
return 0, 0, fmt.Errorf("input is too long")
|
||||
}
|
||||
|
||||
if err := haveProgTestRun(); err != nil {
|
||||
return 0, nil, 0, err
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// Older kernels ignore the dataSizeOut argument when copying to user space.
|
||||
// Combined with things like bpf_xdp_adjust_head() we don't really know what the final
|
||||
// size will be. Hence we allocate an output buffer which we hope will always be large
|
||||
// enough, and panic if the kernel wrote past the end of the allocation.
|
||||
// See https://patchwork.ozlabs.org/cover/1006822/
|
||||
out := make([]byte, len(in)+outputPad)
|
||||
var ctxBytes []byte
|
||||
if opts.Context != nil {
|
||||
ctx := new(bytes.Buffer)
|
||||
if err := binary.Write(ctx, internal.NativeEndian, opts.Context); err != nil {
|
||||
return 0, 0, fmt.Errorf("cannot serialize context: %v", err)
|
||||
}
|
||||
ctxBytes = ctx.Bytes()
|
||||
}
|
||||
|
||||
var ctxOut []byte
|
||||
if opts.ContextOut != nil {
|
||||
ctxOut = make([]byte, binary.Size(opts.ContextOut))
|
||||
}
|
||||
|
||||
attr := sys.ProgRunAttr{
|
||||
ProgFd: p.fd.Uint(),
|
||||
DataSizeIn: uint32(len(in)),
|
||||
DataSizeOut: uint32(len(out)),
|
||||
DataIn: sys.NewSlicePointer(in),
|
||||
DataOut: sys.NewSlicePointer(out),
|
||||
Repeat: uint32(repeat),
|
||||
DataSizeIn: uint32(len(opts.Data)),
|
||||
DataSizeOut: uint32(len(opts.DataOut)),
|
||||
DataIn: sys.NewSlicePointer(opts.Data),
|
||||
DataOut: sys.NewSlicePointer(opts.DataOut),
|
||||
Repeat: uint32(opts.Repeat),
|
||||
CtxSizeIn: uint32(len(ctxBytes)),
|
||||
CtxSizeOut: uint32(len(ctxOut)),
|
||||
CtxIn: sys.NewSlicePointer(ctxBytes),
|
||||
CtxOut: sys.NewSlicePointer(ctxOut),
|
||||
Flags: opts.Flags,
|
||||
Cpu: opts.CPU,
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -605,28 +654,37 @@ func (p *Program) testRun(in []byte, repeat int, reset func()) (uint32, []byte,
|
||||
}
|
||||
|
||||
if errors.Is(err, unix.EINTR) {
|
||||
if reset != nil {
|
||||
reset()
|
||||
if opts.Reset != nil {
|
||||
opts.Reset()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if errors.Is(err, unix.ENOTSUPP) {
|
||||
return 0, nil, 0, fmt.Errorf("kernel doesn't support testing program type %s: %w", p.Type(), ErrNotSupported)
|
||||
return 0, 0, fmt.Errorf("kernel doesn't support testing program type %s: %w", p.Type(), ErrNotSupported)
|
||||
}
|
||||
|
||||
return 0, nil, 0, fmt.Errorf("can't run test: %w", err)
|
||||
return 0, 0, fmt.Errorf("can't run test: %w", err)
|
||||
}
|
||||
|
||||
if int(attr.DataSizeOut) > cap(out) {
|
||||
// Houston, we have a problem. The program created more data than we allocated,
|
||||
// and the kernel wrote past the end of our buffer.
|
||||
panic("kernel wrote past end of output buffer")
|
||||
if opts.DataOut != nil {
|
||||
if int(attr.DataSizeOut) > cap(opts.DataOut) {
|
||||
// Houston, we have a problem. The program created more data than we allocated,
|
||||
// and the kernel wrote past the end of our buffer.
|
||||
panic("kernel wrote past end of output buffer")
|
||||
}
|
||||
opts.DataOut = opts.DataOut[:int(attr.DataSizeOut)]
|
||||
}
|
||||
|
||||
if len(ctxOut) != 0 {
|
||||
b := bytes.NewReader(ctxOut)
|
||||
if err := binary.Read(b, internal.NativeEndian, opts.ContextOut); err != nil {
|
||||
return 0, 0, fmt.Errorf("failed to decode ContextOut: %v", err)
|
||||
}
|
||||
}
|
||||
out = out[:int(attr.DataSizeOut)]
|
||||
|
||||
total := time.Duration(attr.Duration) * time.Nanosecond
|
||||
return attr.Retval, out, total, nil
|
||||
return attr.Retval, total, nil
|
||||
}
|
||||
|
||||
func unmarshalProgram(buf []byte) (*Program, error) {
|
||||
@@ -650,45 +708,6 @@ func marshalProgram(p *Program, length int) ([]byte, error) {
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// Attach a Program.
|
||||
//
|
||||
// Deprecated: use link.RawAttachProgram instead.
|
||||
func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
|
||||
if fd < 0 {
|
||||
return errors.New("invalid fd")
|
||||
}
|
||||
|
||||
attr := sys.ProgAttachAttr{
|
||||
TargetFd: uint32(fd),
|
||||
AttachBpfFd: p.fd.Uint(),
|
||||
AttachType: uint32(typ),
|
||||
AttachFlags: uint32(flags),
|
||||
}
|
||||
|
||||
return sys.ProgAttach(&attr)
|
||||
}
|
||||
|
||||
// Detach a Program.
|
||||
//
|
||||
// Deprecated: use link.RawDetachProgram instead.
|
||||
func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
|
||||
if fd < 0 {
|
||||
return errors.New("invalid fd")
|
||||
}
|
||||
|
||||
if flags != 0 {
|
||||
return errors.New("flags must be zero")
|
||||
}
|
||||
|
||||
attr := sys.ProgAttachAttr{
|
||||
TargetFd: uint32(fd),
|
||||
AttachBpfFd: p.fd.Uint(),
|
||||
AttachType: uint32(typ),
|
||||
}
|
||||
|
||||
return sys.ProgAttach(&attr)
|
||||
}
|
||||
|
||||
// LoadPinnedProgram loads a Program from a BPF file.
|
||||
//
|
||||
// Requires at least Linux 4.11.
|
||||
@@ -734,17 +753,6 @@ func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
|
||||
return ProgramID(attr.NextId), sys.ProgGetNextId(attr)
|
||||
}
|
||||
|
||||
// ID returns the systemwide unique ID of the program.
|
||||
//
|
||||
// Deprecated: use ProgramInfo.ID() instead.
|
||||
func (p *Program) ID() (ProgramID, error) {
|
||||
var info sys.ProgInfo
|
||||
if err := sys.ObjInfo(p.fd, &info); err != nil {
|
||||
return ProgramID(0), err
|
||||
}
|
||||
return ProgramID(info.Id), nil
|
||||
}
|
||||
|
||||
// BindMap binds map to the program and is only released once program is released.
|
||||
//
|
||||
// This may be used in cases where metadata should be associated with the program
|
||||
@@ -846,23 +854,16 @@ func findTargetInProgram(prog *Program, name string, progType ProgramType, attac
|
||||
return 0, errUnrecognizedAttachType
|
||||
}
|
||||
|
||||
info, err := prog.Info()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("load target BTF: %w", err)
|
||||
}
|
||||
|
||||
btfID, ok := info.BTFID()
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("load target BTF: no BTF info available")
|
||||
}
|
||||
|
||||
btfHandle, err := btf.NewHandleFromID(btfID)
|
||||
btfHandle, err := prog.Handle()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("load target BTF: %w", err)
|
||||
}
|
||||
defer btfHandle.Close()
|
||||
|
||||
spec := btfHandle.Spec()
|
||||
spec, err := btfHandle.Spec(nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var targetFunc *btf.Func
|
||||
err = spec.TypeByName(typeName, &targetFunc)
|
||||
|
||||
+6
-2
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Test the current package under a different kernel.
|
||||
# Requires virtme and qemu to be installed.
|
||||
# Examples:
|
||||
@@ -61,7 +61,7 @@ if [[ "${1:-}" = "--exec-vm" ]]; then
|
||||
if [[ -e "${output}/status" ]]; then
|
||||
break
|
||||
fi
|
||||
|
||||
|
||||
if [[ -v CI ]]; then
|
||||
echo "Retrying test run due to qemu crash"
|
||||
continue
|
||||
@@ -83,6 +83,10 @@ elif [[ "${1:-}" = "--exec-test" ]]; then
|
||||
export KERNEL_SELFTESTS="/run/input/bpf"
|
||||
fi
|
||||
|
||||
if [[ -f "/run/input/bpf/bpf_testmod/bpf_testmod.ko" ]]; then
|
||||
insmod "/run/input/bpf/bpf_testmod/bpf_testmod.ko"
|
||||
fi
|
||||
|
||||
dmesg --clear
|
||||
rc=0
|
||||
"$@" || rc=$?
|
||||
|
||||
-6
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
@@ -12,11 +11,6 @@ import (
|
||||
"github.com/cilium/ebpf/internal/unix"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func invalidBPFObjNameChar(char rune) bool {
|
||||
|
||||
Vendored
+1
-1
@@ -2,7 +2,7 @@
|
||||
## explicit; go 1.13
|
||||
github.com/checkpoint-restore/go-criu/v5
|
||||
github.com/checkpoint-restore/go-criu/v5/rpc
|
||||
# github.com/cilium/ebpf v0.9.0
|
||||
# github.com/cilium/ebpf v0.9.1
|
||||
## explicit; go 1.17
|
||||
github.com/cilium/ebpf
|
||||
github.com/cilium/ebpf/asm
|
||||
|
||||
Reference in New Issue
Block a user