build(deps): bump github.com/cilium/ebpf from 0.9.0 to 0.9.1

Bumps [github.com/cilium/ebpf](https://github.com/cilium/ebpf) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/cilium/ebpf/releases)
- [Commits](https://github.com/cilium/ebpf/compare/v0.9.0...v0.9.1)

---
updated-dependencies:
- dependency-name: github.com/cilium/ebpf
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot]
2022-07-28 19:05:50 +00:00
committed by GitHub
parent 6f8cb8b4c1
commit d9a3acb9fc
35 changed files with 1037 additions and 560 deletions
+15 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 {