full diff: https://github.com/cilium/ebpf/compare/v0.16.0...v0.17.3
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -142,7 +142,7 @@ require ( |
| 142 | 142 |
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect |
| 143 | 143 |
github.com/cenkalti/backoff/v4 v4.3.0 // indirect |
| 144 | 144 |
github.com/cespare/xxhash/v2 v2.3.0 // indirect |
| 145 |
- github.com/cilium/ebpf v0.16.0 // indirect |
|
| 145 |
+ github.com/cilium/ebpf v0.17.3 // indirect |
|
| 146 | 146 |
github.com/container-storage-interface/spec v1.5.0 // indirect |
| 147 | 147 |
github.com/containerd/console v1.0.4 // indirect |
| 148 | 148 |
github.com/containerd/errdefs/pkg v0.3.0 // indirect |
| ... | ... |
@@ -230,7 +230,6 @@ require ( |
| 230 | 230 |
go.uber.org/multierr v1.8.0 // indirect |
| 231 | 231 |
go.uber.org/zap v1.21.0 // indirect |
| 232 | 232 |
golang.org/x/crypto v0.35.0 // indirect |
| 233 |
- golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect |
|
| 234 | 233 |
golang.org/x/oauth2 v0.27.0 // indirect |
| 235 | 234 |
golang.org/x/tools v0.27.0 // indirect |
| 236 | 235 |
google.golang.org/api v0.155.0 // indirect |
| ... | ... |
@@ -105,8 +105,8 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA |
| 105 | 105 |
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
| 106 | 106 |
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= |
| 107 | 107 |
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= |
| 108 |
-github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok= |
|
| 109 |
-github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= |
|
| 108 |
+github.com/cilium/ebpf v0.17.3 h1:FnP4r16PWYSE4ux6zN+//jMcW4nMVRvuTLVTvCjyyjg= |
|
| 109 |
+github.com/cilium/ebpf v0.17.3/go.mod h1:G5EDHij8yiLzaqn0WjyfJHvRa+3aDlReIaLVRMvOyJk= |
|
| 110 | 110 |
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= |
| 111 | 111 |
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= |
| 112 | 112 |
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= |
| ... | ... |
@@ -11,3 +11,21 @@ linters: |
| 11 | 11 |
- typecheck |
| 12 | 12 |
- unused |
| 13 | 13 |
- gofmt |
| 14 |
+ - depguard |
|
| 15 |
+linters-settings: |
|
| 16 |
+ goimports: |
|
| 17 |
+ # A comma-separated list of prefixes, which, if set, checks import paths |
|
| 18 |
+ # with the given prefixes are grouped after 3rd-party packages. |
|
| 19 |
+ # Default: "" |
|
| 20 |
+ local-prefixes: github.com/cilium/ebpf |
|
| 21 |
+ depguard: |
|
| 22 |
+ rules: |
|
| 23 |
+ no-x-sys-unix: |
|
| 24 |
+ files: |
|
| 25 |
+ # Filenames are matched against absolute paths, include **/ at the start. |
|
| 26 |
+ - '!**/internal/unix/*.go' |
|
| 27 |
+ - '!**/examples/**/*.go' |
|
| 28 |
+ - '!**/docs/**/*.go' |
|
| 29 |
+ deny: |
|
| 30 |
+ - pkg: golang.org/x/sys/unix |
|
| 31 |
+ desc: use internal/unix instead |
| ... | ... |
@@ -39,16 +39,18 @@ TARGETS := \ |
| 39 | 39 |
testdata/subprog_reloc \ |
| 40 | 40 |
testdata/fwd_decl \ |
| 41 | 41 |
testdata/kconfig \ |
| 42 |
- testdata/kconfig_config \ |
|
| 42 |
+ testdata/ksym \ |
|
| 43 | 43 |
testdata/kfunc \ |
| 44 | 44 |
testdata/invalid-kfunc \ |
| 45 | 45 |
testdata/kfunc-kmod \ |
| 46 | 46 |
testdata/constants \ |
| 47 | 47 |
testdata/errors \ |
| 48 |
+ testdata/variables \ |
|
| 48 | 49 |
btf/testdata/relocs \ |
| 49 | 50 |
btf/testdata/relocs_read \ |
| 50 | 51 |
btf/testdata/relocs_read_tgt \ |
| 51 | 52 |
btf/testdata/relocs_enum \ |
| 53 |
+ btf/testdata/tags \ |
|
| 52 | 54 |
cmd/bpf2go/testdata/minimal |
| 53 | 55 |
|
| 54 | 56 |
.PHONY: all clean container-all container-shell generate |
| ... | ... |
@@ -57,7 +59,7 @@ TARGETS := \ |
| 57 | 57 |
|
| 58 | 58 |
# Build all ELF binaries using a containerized LLVM toolchain. |
| 59 | 59 |
container-all: |
| 60 |
- +${CONTAINER_ENGINE} run --rm -t ${CONTAINER_RUN_ARGS} \
|
|
| 60 |
+ +${CONTAINER_ENGINE} run --rm -ti ${CONTAINER_RUN_ARGS} \
|
|
| 61 | 61 |
-v "${REPODIR}":/ebpf -w /ebpf --env MAKEFLAGS \
|
| 62 | 62 |
--env HOME="/tmp" \ |
| 63 | 63 |
--env BPF2GO_CC="$(CLANG)" \ |
| ... | ... |
@@ -53,6 +53,7 @@ This library includes the following packages: |
| 53 | 53 |
* [rlimit](https://pkg.go.dev/github.com/cilium/ebpf/rlimit) provides a convenient API to lift |
| 54 | 54 |
the `RLIMIT_MEMLOCK` constraint on kernels before 5.11. |
| 55 | 55 |
* [btf](https://pkg.go.dev/github.com/cilium/ebpf/btf) allows reading the BPF Type Format. |
| 56 |
+* [pin](https://pkg.go.dev/github.com/cilium/ebpf/pin) provides APIs for working with pinned objects on bpffs. |
|
| 56 | 57 |
|
| 57 | 58 |
## Requirements |
| 58 | 59 |
|
| ... | ... |
@@ -5,10 +5,6 @@ package asm |
| 5 | 5 |
// BuiltinFunc is a built-in eBPF function. |
| 6 | 6 |
type BuiltinFunc int32 |
| 7 | 7 |
|
| 8 |
-func (_ BuiltinFunc) Max() BuiltinFunc {
|
|
| 9 |
- return maxBuiltinFunc - 1 |
|
| 10 |
-} |
|
| 11 |
- |
|
| 12 | 8 |
// eBPF built-in functions |
| 13 | 9 |
// |
| 14 | 10 |
// You can regenerate this list using the following gawk script: |
| ... | ... |
@@ -237,8 +233,6 @@ const ( |
| 237 | 237 |
FnUserRingbufDrain |
| 238 | 238 |
FnCgrpStorageGet |
| 239 | 239 |
FnCgrpStorageDelete |
| 240 |
- |
|
| 241 |
- maxBuiltinFunc |
|
| 242 | 240 |
) |
| 243 | 241 |
|
| 244 | 242 |
// Call emits a function call. |
| ... | ... |
@@ -220,12 +220,11 @@ func _() {
|
| 220 | 220 |
_ = x[FnUserRingbufDrain-209] |
| 221 | 221 |
_ = x[FnCgrpStorageGet-210] |
| 222 | 222 |
_ = x[FnCgrpStorageDelete-211] |
| 223 |
- _ = x[maxBuiltinFunc-212] |
|
| 224 | 223 |
} |
| 225 | 224 |
|
| 226 |
-const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDeletemaxBuiltinFunc" |
|
| 225 |
+const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDelete" |
|
| 227 | 226 |
|
| 228 |
-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, 3021, 3045, 3071, 3097, 3112, 3130, 3146, 3165, 3179}
|
|
| 227 |
+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, 3021, 3045, 3071, 3097, 3112, 3130, 3146, 3165}
|
|
| 229 | 228 |
|
| 230 | 229 |
func (i BuiltinFunc) String() string {
|
| 231 | 230 |
if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) {
|
| ... | ... |
@@ -12,7 +12,6 @@ import ( |
| 12 | 12 |
"strings" |
| 13 | 13 |
|
| 14 | 14 |
"github.com/cilium/ebpf/internal/sys" |
| 15 |
- "github.com/cilium/ebpf/internal/unix" |
|
| 16 | 15 |
) |
| 17 | 16 |
|
| 18 | 17 |
// InstructionSize is the size of a BPF instruction in bytes |
| ... | ... |
@@ -804,7 +803,7 @@ func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
|
| 804 | 804 |
return "", fmt.Errorf("instruction %d: %w", i, err)
|
| 805 | 805 |
} |
| 806 | 806 |
} |
| 807 |
- return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil |
|
| 807 |
+ return hex.EncodeToString(h.Sum(nil)[:sys.BPF_TAG_SIZE]), nil |
|
| 808 | 808 |
} |
| 809 | 809 |
|
| 810 | 810 |
// encodeFunctionReferences populates the Offset (or Constant, depending on |
| ... | ... |
@@ -52,6 +52,7 @@ func _() {
|
| 52 | 52 |
_ = x[AttachSkReuseportSelectOrMigrate-40] |
| 53 | 53 |
_ = x[AttachPerfEvent-41] |
| 54 | 54 |
_ = x[AttachTraceKprobeMulti-42] |
| 55 |
+ _ = x[AttachTraceKprobeSession-56] |
|
| 55 | 56 |
_ = x[AttachLSMCgroup-43] |
| 56 | 57 |
_ = x[AttachStructOps-44] |
| 57 | 58 |
_ = x[AttachNetfilter-45] |
| ... | ... |
@@ -67,9 +68,9 @@ func _() {
|
| 67 | 67 |
_ = x[AttachNetkitPeer-55] |
| 68 | 68 |
} |
| 69 | 69 |
|
| 70 |
-const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMultiLSMCgroupStructOpsNetfilterTCXIngressTCXEgressTraceUprobeMultiCgroupUnixConnectCgroupUnixSendmsgCgroupUnixRecvmsgCgroupUnixGetpeernameCgroupUnixGetsocknameNetkitPrimaryNetkitPeer" |
|
| 70 |
+const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMultiLSMCgroupStructOpsNetfilterTCXIngressTCXEgressTraceUprobeMultiCgroupUnixConnectCgroupUnixSendmsgCgroupUnixRecvmsgCgroupUnixGetpeernameCgroupUnixGetsocknameNetkitPrimaryNetkitPeerTraceKprobeSession" |
|
| 71 | 71 |
|
| 72 |
-var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610, 626, 635, 644, 653, 663, 672, 688, 705, 722, 739, 760, 781, 794, 804}
|
|
| 72 |
+var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610, 626, 635, 644, 653, 663, 672, 688, 705, 722, 739, 760, 781, 794, 804, 822}
|
|
| 73 | 73 |
|
| 74 | 74 |
func (i AttachType) String() string {
|
| 75 | 75 |
if i >= AttachType(len(_AttachType_index)-1) {
|
| ... | ... |
@@ -99,6 +99,10 @@ func (mt *mutableTypes) copy() *mutableTypes {
|
| 99 | 99 |
return nil |
| 100 | 100 |
} |
| 101 | 101 |
|
| 102 |
+ // Prevent concurrent modification of mt.copiedTypeIDs. |
|
| 103 |
+ mt.mu.RLock() |
|
| 104 |
+ defer mt.mu.RUnlock() |
|
| 105 |
+ |
|
| 102 | 106 |
mtCopy := &mutableTypes{
|
| 103 | 107 |
mt.imm, |
| 104 | 108 |
sync.RWMutex{},
|
| ... | ... |
@@ -106,10 +110,6 @@ func (mt *mutableTypes) copy() *mutableTypes {
|
| 106 | 106 |
make(map[Type]TypeID, len(mt.copiedTypeIDs)), |
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 |
- // Prevent concurrent modification of mt.copiedTypeIDs. |
|
| 110 |
- mt.mu.RLock() |
|
| 111 |
- defer mt.mu.RUnlock() |
|
| 112 |
- |
|
| 113 | 109 |
copiesOfCopies := make(map[Type]Type, len(mt.copies)) |
| 114 | 110 |
for orig, copy := range mt.copies {
|
| 115 | 111 |
// NB: We make a copy of copy, not orig, so that changes to mutable types |
| ... | ... |
@@ -443,13 +443,19 @@ func fixupDatasec(types []Type, sectionSizes map[string]uint32, offsets map[symb |
| 443 | 443 |
// Some Datasecs are virtual and don't have corresponding ELF sections. |
| 444 | 444 |
switch name {
|
| 445 | 445 |
case ".ksyms": |
| 446 |
- // .ksyms describes forward declarations of kfunc signatures. |
|
| 446 |
+ // .ksyms describes forward declarations of kfunc signatures, as well as |
|
| 447 |
+ // references to kernel symbols. |
|
| 447 | 448 |
// Nothing to fix up, all sizes and offsets are 0. |
| 448 | 449 |
for _, vsi := range ds.Vars {
|
| 449 |
- _, ok := vsi.Type.(*Func) |
|
| 450 |
- if !ok {
|
|
| 451 |
- // Only Funcs are supported in the .ksyms Datasec. |
|
| 452 |
- return fmt.Errorf("data section %s: expected *btf.Func, not %T: %w", name, vsi.Type, ErrNotSupported)
|
|
| 450 |
+ switch t := vsi.Type.(type) {
|
|
| 451 |
+ case *Func: |
|
| 452 |
+ continue |
|
| 453 |
+ case *Var: |
|
| 454 |
+ if _, ok := t.Type.(*Void); !ok {
|
|
| 455 |
+ return fmt.Errorf("data section %s: expected %s to be *Void, not %T: %w", name, vsi.Type.TypeName(), vsi.Type, ErrNotSupported)
|
|
| 456 |
+ } |
|
| 457 |
+ default: |
|
| 458 |
+ return fmt.Errorf("data section %s: expected to be either *btf.Func or *btf.Var, not %T: %w", name, vsi.Type, ErrNotSupported)
|
|
| 453 | 459 |
} |
| 454 | 460 |
} |
| 455 | 461 |
|
| ... | ... |
@@ -695,5 +701,13 @@ func (iter *TypesIterator) Next() bool {
|
| 695 | 695 |
iter.Type, ok = iter.spec.typeByID(iter.id) |
| 696 | 696 |
iter.id++ |
| 697 | 697 |
iter.done = !ok |
| 698 |
+ if !iter.done {
|
|
| 699 |
+ // Skip declTags, during unmarshaling declTags become `Tags` fields of other types. |
|
| 700 |
+ // We keep them in the spec to avoid holes in the ID space, but for the purposes of |
|
| 701 |
+ // iteration, they are not useful to the user. |
|
| 702 |
+ if _, ok := iter.Type.(*declTag); ok {
|
|
| 703 |
+ return iter.Next() |
|
| 704 |
+ } |
|
| 705 |
+ } |
|
| 698 | 706 |
return !iter.done |
| 699 | 707 |
} |
| ... | ... |
@@ -16,8 +16,8 @@ import ( |
| 16 | 16 |
// ExtInfos contains ELF section metadata. |
| 17 | 17 |
type ExtInfos struct {
|
| 18 | 18 |
// The slices are sorted by offset in ascending order. |
| 19 |
- funcInfos map[string]FuncInfos |
|
| 20 |
- lineInfos map[string]LineInfos |
|
| 19 |
+ funcInfos map[string]FuncOffsets |
|
| 20 |
+ lineInfos map[string]LineOffsets |
|
| 21 | 21 |
relocationInfos map[string]CORERelocationInfos |
| 22 | 22 |
} |
| 23 | 23 |
|
| ... | ... |
@@ -58,9 +58,9 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er |
| 58 | 58 |
return nil, fmt.Errorf("parsing BTF function info: %w", err)
|
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 |
- funcInfos := make(map[string]FuncInfos, len(btfFuncInfos)) |
|
| 61 |
+ funcInfos := make(map[string]FuncOffsets, len(btfFuncInfos)) |
|
| 62 | 62 |
for section, bfis := range btfFuncInfos {
|
| 63 |
- funcInfos[section], err = newFuncInfos(bfis, spec) |
|
| 63 |
+ funcInfos[section], err = newFuncOffsets(bfis, spec) |
|
| 64 | 64 |
if err != nil {
|
| 65 | 65 |
return nil, fmt.Errorf("section %s: func infos: %w", section, err)
|
| 66 | 66 |
} |
| ... | ... |
@@ -72,7 +72,7 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er |
| 72 | 72 |
return nil, fmt.Errorf("parsing BTF line info: %w", err)
|
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 |
- lineInfos := make(map[string]LineInfos, len(btfLineInfos)) |
|
| 75 |
+ lineInfos := make(map[string]LineOffsets, len(btfLineInfos)) |
|
| 76 | 76 |
for section, blis := range btfLineInfos {
|
| 77 | 77 |
lineInfos[section], err = newLineInfos(blis, spec.strings) |
| 78 | 78 |
if err != nil {
|
| ... | ... |
@@ -102,8 +102,10 @@ func loadExtInfos(r io.ReaderAt, bo binary.ByteOrder, spec *Spec) (*ExtInfos, er |
| 102 | 102 |
return &ExtInfos{funcInfos, lineInfos, coreRelos}, nil
|
| 103 | 103 |
} |
| 104 | 104 |
|
| 105 |
-type funcInfoMeta struct{}
|
|
| 106 |
-type coreRelocationMeta struct{}
|
|
| 105 |
+type ( |
|
| 106 |
+ funcInfoMeta struct{}
|
|
| 107 |
+ coreRelocationMeta struct{}
|
|
| 108 |
+) |
|
| 107 | 109 |
|
| 108 | 110 |
// Assign per-section metadata from BTF to a section's instructions. |
| 109 | 111 |
func (ei *ExtInfos) Assign(insns asm.Instructions, section string) {
|
| ... | ... |
@@ -117,20 +119,20 @@ func (ei *ExtInfos) Assign(insns asm.Instructions, section string) {
|
| 117 | 117 |
// Assign per-instruction metadata to the instructions in insns. |
| 118 | 118 |
func AssignMetadataToInstructions( |
| 119 | 119 |
insns asm.Instructions, |
| 120 |
- funcInfos FuncInfos, |
|
| 121 |
- lineInfos LineInfos, |
|
| 120 |
+ funcInfos FuncOffsets, |
|
| 121 |
+ lineInfos LineOffsets, |
|
| 122 | 122 |
reloInfos CORERelocationInfos, |
| 123 | 123 |
) {
|
| 124 | 124 |
iter := insns.Iterate() |
| 125 | 125 |
for iter.Next() {
|
| 126 |
- if len(funcInfos.infos) > 0 && funcInfos.infos[0].offset == iter.Offset {
|
|
| 127 |
- *iter.Ins = WithFuncMetadata(*iter.Ins, funcInfos.infos[0].fn) |
|
| 128 |
- funcInfos.infos = funcInfos.infos[1:] |
|
| 126 |
+ if len(funcInfos) > 0 && funcInfos[0].Offset == iter.Offset {
|
|
| 127 |
+ *iter.Ins = WithFuncMetadata(*iter.Ins, funcInfos[0].Func) |
|
| 128 |
+ funcInfos = funcInfos[1:] |
|
| 129 | 129 |
} |
| 130 | 130 |
|
| 131 |
- if len(lineInfos.infos) > 0 && lineInfos.infos[0].offset == iter.Offset {
|
|
| 132 |
- *iter.Ins = iter.Ins.WithSource(lineInfos.infos[0].line) |
|
| 133 |
- lineInfos.infos = lineInfos.infos[1:] |
|
| 131 |
+ if len(lineInfos) > 0 && lineInfos[0].Offset == iter.Offset {
|
|
| 132 |
+ *iter.Ins = iter.Ins.WithSource(lineInfos[0].Line) |
|
| 133 |
+ lineInfos = lineInfos[1:] |
|
| 134 | 134 |
} |
| 135 | 135 |
|
| 136 | 136 |
if len(reloInfos.infos) > 0 && reloInfos.infos[0].offset == iter.Offset {
|
| ... | ... |
@@ -159,9 +161,9 @@ marshal: |
| 159 | 159 |
var fiBuf, liBuf bytes.Buffer |
| 160 | 160 |
for {
|
| 161 | 161 |
if fn := FuncMetadata(iter.Ins); fn != nil {
|
| 162 |
- fi := &funcInfo{
|
|
| 163 |
- fn: fn, |
|
| 164 |
- offset: iter.Offset, |
|
| 162 |
+ fi := &FuncOffset{
|
|
| 163 |
+ Func: fn, |
|
| 164 |
+ Offset: iter.Offset, |
|
| 165 | 165 |
} |
| 166 | 166 |
if err := fi.marshal(&fiBuf, b); err != nil {
|
| 167 | 167 |
return nil, nil, fmt.Errorf("write func info: %w", err)
|
| ... | ... |
@@ -178,9 +180,9 @@ marshal: |
| 178 | 178 |
} |
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 |
- li := &lineInfo{
|
|
| 182 |
- line: line, |
|
| 183 |
- offset: iter.Offset, |
|
| 181 |
+ li := &LineOffset{
|
|
| 182 |
+ Offset: iter.Offset, |
|
| 183 |
+ Line: line, |
|
| 184 | 184 |
} |
| 185 | 185 |
if err := li.marshal(&liBuf, b); err != nil {
|
| 186 | 186 |
return nil, nil, fmt.Errorf("write line info: %w", err)
|
| ... | ... |
@@ -333,17 +335,17 @@ func parseExtInfoRecordSize(r io.Reader, bo binary.ByteOrder) (uint32, error) {
|
| 333 | 333 |
return recordSize, nil |
| 334 | 334 |
} |
| 335 | 335 |
|
| 336 |
-// FuncInfos contains a sorted list of func infos. |
|
| 337 |
-type FuncInfos struct {
|
|
| 338 |
- infos []funcInfo |
|
| 339 |
-} |
|
| 336 |
+// FuncOffsets is a sorted slice of FuncOffset. |
|
| 337 |
+type FuncOffsets []FuncOffset |
|
| 340 | 338 |
|
| 341 | 339 |
// The size of a FuncInfo in BTF wire format. |
| 342 | 340 |
var FuncInfoSize = uint32(binary.Size(bpfFuncInfo{}))
|
| 343 | 341 |
|
| 344 |
-type funcInfo struct {
|
|
| 345 |
- fn *Func |
|
| 346 |
- offset asm.RawInstructionOffset |
|
| 342 |
+// FuncOffset represents a [btf.Func] and its raw instruction offset within a |
|
| 343 |
+// BPF program. |
|
| 344 |
+type FuncOffset struct {
|
|
| 345 |
+ Offset asm.RawInstructionOffset |
|
| 346 |
+ Func *Func |
|
| 347 | 347 |
} |
| 348 | 348 |
|
| 349 | 349 |
type bpfFuncInfo struct {
|
| ... | ... |
@@ -352,7 +354,7 @@ type bpfFuncInfo struct {
|
| 352 | 352 |
TypeID TypeID |
| 353 | 353 |
} |
| 354 | 354 |
|
| 355 |
-func newFuncInfo(fi bpfFuncInfo, spec *Spec) (*funcInfo, error) {
|
|
| 355 |
+func newFuncOffset(fi bpfFuncInfo, spec *Spec) (*FuncOffset, error) {
|
|
| 356 | 356 |
typ, err := spec.TypeByID(fi.TypeID) |
| 357 | 357 |
if err != nil {
|
| 358 | 358 |
return nil, err |
| ... | ... |
@@ -368,31 +370,32 @@ func newFuncInfo(fi bpfFuncInfo, spec *Spec) (*funcInfo, error) {
|
| 368 | 368 |
return nil, fmt.Errorf("func with type ID %d doesn't have a name", fi.TypeID)
|
| 369 | 369 |
} |
| 370 | 370 |
|
| 371 |
- return &funcInfo{
|
|
| 372 |
- fn, |
|
| 371 |
+ return &FuncOffset{
|
|
| 373 | 372 |
asm.RawInstructionOffset(fi.InsnOff), |
| 373 |
+ fn, |
|
| 374 | 374 |
}, nil |
| 375 | 375 |
} |
| 376 | 376 |
|
| 377 |
-func newFuncInfos(bfis []bpfFuncInfo, spec *Spec) (FuncInfos, error) {
|
|
| 378 |
- fis := FuncInfos{
|
|
| 379 |
- infos: make([]funcInfo, 0, len(bfis)), |
|
| 380 |
- } |
|
| 377 |
+func newFuncOffsets(bfis []bpfFuncInfo, spec *Spec) (FuncOffsets, error) {
|
|
| 378 |
+ fos := make(FuncOffsets, 0, len(bfis)) |
|
| 379 |
+ |
|
| 381 | 380 |
for _, bfi := range bfis {
|
| 382 |
- fi, err := newFuncInfo(bfi, spec) |
|
| 381 |
+ fi, err := newFuncOffset(bfi, spec) |
|
| 383 | 382 |
if err != nil {
|
| 384 |
- return FuncInfos{}, fmt.Errorf("offset %d: %w", bfi.InsnOff, err)
|
|
| 383 |
+ return FuncOffsets{}, fmt.Errorf("offset %d: %w", bfi.InsnOff, err)
|
|
| 385 | 384 |
} |
| 386 |
- fis.infos = append(fis.infos, *fi) |
|
| 385 |
+ fos = append(fos, *fi) |
|
| 387 | 386 |
} |
| 388 |
- sort.Slice(fis.infos, func(i, j int) bool {
|
|
| 389 |
- return fis.infos[i].offset <= fis.infos[j].offset |
|
| 387 |
+ sort.Slice(fos, func(i, j int) bool {
|
|
| 388 |
+ return fos[i].Offset <= fos[j].Offset |
|
| 390 | 389 |
}) |
| 391 |
- return fis, nil |
|
| 390 |
+ return fos, nil |
|
| 392 | 391 |
} |
| 393 | 392 |
|
| 394 |
-// LoadFuncInfos parses BTF func info in kernel wire format. |
|
| 395 |
-func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncInfos, error) {
|
|
| 393 |
+// LoadFuncInfos parses BTF func info from kernel wire format into a |
|
| 394 |
+// [FuncOffsets], a sorted slice of [btf.Func]s of (sub)programs within a BPF |
|
| 395 |
+// program with their corresponding raw instruction offsets. |
|
| 396 |
+func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (FuncOffsets, error) {
|
|
| 396 | 397 |
fis, err := parseFuncInfoRecords( |
| 397 | 398 |
reader, |
| 398 | 399 |
bo, |
| ... | ... |
@@ -401,20 +404,20 @@ func LoadFuncInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec |
| 401 | 401 |
false, |
| 402 | 402 |
) |
| 403 | 403 |
if err != nil {
|
| 404 |
- return FuncInfos{}, fmt.Errorf("parsing BTF func info: %w", err)
|
|
| 404 |
+ return FuncOffsets{}, fmt.Errorf("parsing BTF func info: %w", err)
|
|
| 405 | 405 |
} |
| 406 | 406 |
|
| 407 |
- return newFuncInfos(fis, spec) |
|
| 407 |
+ return newFuncOffsets(fis, spec) |
|
| 408 | 408 |
} |
| 409 | 409 |
|
| 410 | 410 |
// marshal into the BTF wire format. |
| 411 |
-func (fi *funcInfo) marshal(w *bytes.Buffer, b *Builder) error {
|
|
| 412 |
- id, err := b.Add(fi.fn) |
|
| 411 |
+func (fi *FuncOffset) marshal(w *bytes.Buffer, b *Builder) error {
|
|
| 412 |
+ id, err := b.Add(fi.Func) |
|
| 413 | 413 |
if err != nil {
|
| 414 | 414 |
return err |
| 415 | 415 |
} |
| 416 | 416 |
bfi := bpfFuncInfo{
|
| 417 |
- InsnOff: uint32(fi.offset), |
|
| 417 |
+ InsnOff: uint32(fi.Offset), |
|
| 418 | 418 |
TypeID: id, |
| 419 | 419 |
} |
| 420 | 420 |
buf := make([]byte, FuncInfoSize) |
| ... | ... |
@@ -515,14 +518,13 @@ func (li *Line) String() string {
|
| 515 | 515 |
return li.line |
| 516 | 516 |
} |
| 517 | 517 |
|
| 518 |
-// LineInfos contains a sorted list of line infos. |
|
| 519 |
-type LineInfos struct {
|
|
| 520 |
- infos []lineInfo |
|
| 521 |
-} |
|
| 518 |
+// LineOffsets contains a sorted list of line infos. |
|
| 519 |
+type LineOffsets []LineOffset |
|
| 522 | 520 |
|
| 523 |
-type lineInfo struct {
|
|
| 524 |
- line *Line |
|
| 525 |
- offset asm.RawInstructionOffset |
|
| 521 |
+// LineOffset represents a line info and its raw instruction offset. |
|
| 522 |
+type LineOffset struct {
|
|
| 523 |
+ Offset asm.RawInstructionOffset |
|
| 524 |
+ Line *Line |
|
| 526 | 525 |
} |
| 527 | 526 |
|
| 528 | 527 |
// Constants for the format of bpfLineInfo.LineCol. |
| ... | ... |
@@ -541,7 +543,7 @@ type bpfLineInfo struct {
|
| 541 | 541 |
} |
| 542 | 542 |
|
| 543 | 543 |
// LoadLineInfos parses BTF line info in kernel wire format. |
| 544 |
-func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineInfos, error) {
|
|
| 544 |
+func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec *Spec) (LineOffsets, error) {
|
|
| 545 | 545 |
lis, err := parseLineInfoRecords( |
| 546 | 546 |
reader, |
| 547 | 547 |
bo, |
| ... | ... |
@@ -550,57 +552,55 @@ func LoadLineInfos(reader io.Reader, bo binary.ByteOrder, recordNum uint32, spec |
| 550 | 550 |
false, |
| 551 | 551 |
) |
| 552 | 552 |
if err != nil {
|
| 553 |
- return LineInfos{}, fmt.Errorf("parsing BTF line info: %w", err)
|
|
| 553 |
+ return LineOffsets{}, fmt.Errorf("parsing BTF line info: %w", err)
|
|
| 554 | 554 |
} |
| 555 | 555 |
|
| 556 | 556 |
return newLineInfos(lis, spec.strings) |
| 557 | 557 |
} |
| 558 | 558 |
|
| 559 |
-func newLineInfo(li bpfLineInfo, strings *stringTable) (lineInfo, error) {
|
|
| 559 |
+func newLineInfo(li bpfLineInfo, strings *stringTable) (LineOffset, error) {
|
|
| 560 | 560 |
line, err := strings.Lookup(li.LineOff) |
| 561 | 561 |
if err != nil {
|
| 562 |
- return lineInfo{}, fmt.Errorf("lookup of line: %w", err)
|
|
| 562 |
+ return LineOffset{}, fmt.Errorf("lookup of line: %w", err)
|
|
| 563 | 563 |
} |
| 564 | 564 |
|
| 565 | 565 |
fileName, err := strings.Lookup(li.FileNameOff) |
| 566 | 566 |
if err != nil {
|
| 567 |
- return lineInfo{}, fmt.Errorf("lookup of filename: %w", err)
|
|
| 567 |
+ return LineOffset{}, fmt.Errorf("lookup of filename: %w", err)
|
|
| 568 | 568 |
} |
| 569 | 569 |
|
| 570 | 570 |
lineNumber := li.LineCol >> bpfLineShift |
| 571 | 571 |
lineColumn := li.LineCol & bpfColumnMax |
| 572 | 572 |
|
| 573 |
- return lineInfo{
|
|
| 573 |
+ return LineOffset{
|
|
| 574 |
+ asm.RawInstructionOffset(li.InsnOff), |
|
| 574 | 575 |
&Line{
|
| 575 | 576 |
fileName, |
| 576 | 577 |
line, |
| 577 | 578 |
lineNumber, |
| 578 | 579 |
lineColumn, |
| 579 | 580 |
}, |
| 580 |
- asm.RawInstructionOffset(li.InsnOff), |
|
| 581 | 581 |
}, nil |
| 582 | 582 |
} |
| 583 | 583 |
|
| 584 |
-func newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineInfos, error) {
|
|
| 585 |
- lis := LineInfos{
|
|
| 586 |
- infos: make([]lineInfo, 0, len(blis)), |
|
| 587 |
- } |
|
| 584 |
+func newLineInfos(blis []bpfLineInfo, strings *stringTable) (LineOffsets, error) {
|
|
| 585 |
+ lis := make([]LineOffset, 0, len(blis)) |
|
| 588 | 586 |
for _, bli := range blis {
|
| 589 | 587 |
li, err := newLineInfo(bli, strings) |
| 590 | 588 |
if err != nil {
|
| 591 |
- return LineInfos{}, fmt.Errorf("offset %d: %w", bli.InsnOff, err)
|
|
| 589 |
+ return LineOffsets{}, fmt.Errorf("offset %d: %w", bli.InsnOff, err)
|
|
| 592 | 590 |
} |
| 593 |
- lis.infos = append(lis.infos, li) |
|
| 591 |
+ lis = append(lis, li) |
|
| 594 | 592 |
} |
| 595 |
- sort.Slice(lis.infos, func(i, j int) bool {
|
|
| 596 |
- return lis.infos[i].offset <= lis.infos[j].offset |
|
| 593 |
+ sort.Slice(lis, func(i, j int) bool {
|
|
| 594 |
+ return lis[i].Offset <= lis[j].Offset |
|
| 597 | 595 |
}) |
| 598 | 596 |
return lis, nil |
| 599 | 597 |
} |
| 600 | 598 |
|
| 601 | 599 |
// marshal writes the binary representation of the LineInfo to w. |
| 602 |
-func (li *lineInfo) marshal(w *bytes.Buffer, b *Builder) error {
|
|
| 603 |
- line := li.line |
|
| 600 |
+func (li *LineOffset) marshal(w *bytes.Buffer, b *Builder) error {
|
|
| 601 |
+ line := li.Line |
|
| 604 | 602 |
if line.lineNumber > bpfLineMax {
|
| 605 | 603 |
return fmt.Errorf("line %d exceeds %d", line.lineNumber, bpfLineMax)
|
| 606 | 604 |
} |
| ... | ... |
@@ -620,7 +620,7 @@ func (li *lineInfo) marshal(w *bytes.Buffer, b *Builder) error {
|
| 620 | 620 |
} |
| 621 | 621 |
|
| 622 | 622 |
bli := bpfLineInfo{
|
| 623 |
- uint32(li.offset), |
|
| 623 |
+ uint32(li.Offset), |
|
| 624 | 624 |
fileNameOff, |
| 625 | 625 |
lineOff, |
| 626 | 626 |
(line.lineNumber << bpfLineShift) | line.lineColumn, |
| ... | ... |
@@ -666,20 +666,19 @@ func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map |
| 666 | 666 |
// These records appear after a btf_ext_info_sec header in the line_info |
| 667 | 667 |
// sub-section of .BTF.ext. |
| 668 | 668 |
func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, offsetInBytes bool) ([]bpfLineInfo, error) {
|
| 669 |
- var li bpfLineInfo |
|
| 670 |
- |
|
| 671 |
- if exp, got := uint32(binary.Size(li)), recordSize; exp != got {
|
|
| 669 |
+ if exp, got := uint32(binary.Size(bpfLineInfo{})), recordSize; exp != got {
|
|
| 672 | 670 |
// BTF blob's record size is longer than we know how to parse. |
| 673 | 671 |
return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got)
|
| 674 | 672 |
} |
| 675 | 673 |
|
| 676 |
- out := make([]bpfLineInfo, 0, recordNum) |
|
| 677 |
- for i := uint32(0); i < recordNum; i++ {
|
|
| 678 |
- if err := binary.Read(r, bo, &li); err != nil {
|
|
| 679 |
- return nil, fmt.Errorf("can't read line info: %v", err)
|
|
| 680 |
- } |
|
| 674 |
+ out := make([]bpfLineInfo, recordNum) |
|
| 675 |
+ if err := binary.Read(r, bo, out); err != nil {
|
|
| 676 |
+ return nil, fmt.Errorf("can't read line info: %v", err)
|
|
| 677 |
+ } |
|
| 681 | 678 |
|
| 682 |
- if offsetInBytes {
|
|
| 679 |
+ if offsetInBytes {
|
|
| 680 |
+ for i := range out {
|
|
| 681 |
+ li := &out[i] |
|
| 683 | 682 |
if li.InsnOff%asm.InstructionSize != 0 {
|
| 684 | 683 |
return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff)
|
| 685 | 684 |
} |
| ... | ... |
@@ -688,8 +687,6 @@ func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r |
| 688 | 688 |
// Convert as early as possible. |
| 689 | 689 |
li.InsnOff /= asm.InstructionSize |
| 690 | 690 |
} |
| 691 |
- |
|
| 692 |
- out = append(out, li) |
|
| 693 | 691 |
} |
| 694 | 692 |
|
| 695 | 693 |
return out, nil |
| ... | ... |
@@ -799,7 +796,7 @@ func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map |
| 799 | 799 |
return nil, err |
| 800 | 800 |
} |
| 801 | 801 |
|
| 802 |
- records, err := parseCOREReloRecords(r, bo, recordSize, infoHeader.NumInfo) |
|
| 802 |
+ records, err := parseCOREReloRecords(r, bo, infoHeader.NumInfo) |
|
| 803 | 803 |
if err != nil {
|
| 804 | 804 |
return nil, fmt.Errorf("section %v: %w", secName, err)
|
| 805 | 805 |
} |
| ... | ... |
@@ -811,7 +808,7 @@ func parseCORERelos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map |
| 811 | 811 |
// parseCOREReloRecords parses a stream of CO-RE relocation entries into a |
| 812 | 812 |
// coreRelos. These records appear after a btf_ext_info_sec header in the |
| 813 | 813 |
// core_relos sub-section of .BTF.ext. |
| 814 |
-func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32) ([]bpfCORERelo, error) {
|
|
| 814 |
+func parseCOREReloRecords(r io.Reader, bo binary.ByteOrder, recordNum uint32) ([]bpfCORERelo, error) {
|
|
| 815 | 815 |
var out []bpfCORERelo |
| 816 | 816 |
|
| 817 | 817 |
var relo bpfCORERelo |
| ... | ... |
@@ -11,19 +11,19 @@ import ( |
| 11 | 11 |
|
| 12 | 12 |
// haveBTF attempts to load a BTF blob containing an Int. It should pass on any |
| 13 | 13 |
// kernel that supports BPF_BTF_LOAD. |
| 14 |
-var haveBTF = internal.NewFeatureTest("BTF", "4.18", func() error {
|
|
| 14 |
+var haveBTF = internal.NewFeatureTest("BTF", func() error {
|
|
| 15 | 15 |
// 0-length anonymous integer |
| 16 | 16 |
err := probeBTF(&Int{})
|
| 17 | 17 |
if errors.Is(err, unix.EINVAL) || errors.Is(err, unix.EPERM) {
|
| 18 | 18 |
return internal.ErrNotSupported |
| 19 | 19 |
} |
| 20 | 20 |
return err |
| 21 |
-}) |
|
| 21 |
+}, "4.18") |
|
| 22 | 22 |
|
| 23 | 23 |
// haveMapBTF attempts to load a minimal BTF blob containing a Var. It is |
| 24 | 24 |
// used as a proxy for .bss, .data and .rodata map support, which generally |
| 25 | 25 |
// come with a Var and Datasec. These were introduced in Linux 5.2. |
| 26 |
-var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", "5.2", func() error {
|
|
| 26 |
+var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", func() error {
|
|
| 27 | 27 |
if err := haveBTF(); err != nil {
|
| 28 | 28 |
return err |
| 29 | 29 |
} |
| ... | ... |
@@ -40,12 +40,12 @@ var haveMapBTF = internal.NewFeatureTest("Map BTF (Var/Datasec)", "5.2", func()
|
| 40 | 40 |
return internal.ErrNotSupported |
| 41 | 41 |
} |
| 42 | 42 |
return err |
| 43 |
-}) |
|
| 43 |
+}, "5.2") |
|
| 44 | 44 |
|
| 45 | 45 |
// haveProgBTF attempts to load a BTF blob containing a Func and FuncProto. It |
| 46 | 46 |
// is used as a proxy for ext_info (func_info) support, which depends on |
| 47 | 47 |
// Func(Proto) by definition. |
| 48 |
-var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", "5.0", func() error {
|
|
| 48 |
+var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", func() error {
|
|
| 49 | 49 |
if err := haveBTF(); err != nil {
|
| 50 | 50 |
return err |
| 51 | 51 |
} |
| ... | ... |
@@ -60,9 +60,9 @@ var haveProgBTF = internal.NewFeatureTest("Program BTF (func/line_info)", "5.0",
|
| 60 | 60 |
return internal.ErrNotSupported |
| 61 | 61 |
} |
| 62 | 62 |
return err |
| 63 |
-}) |
|
| 63 |
+}, "5.0") |
|
| 64 | 64 |
|
| 65 |
-var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", "5.6", func() error {
|
|
| 65 |
+var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", func() error {
|
|
| 66 | 66 |
if err := haveProgBTF(); err != nil {
|
| 67 | 67 |
return err |
| 68 | 68 |
} |
| ... | ... |
@@ -78,9 +78,44 @@ var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", "5.6", func()
|
| 78 | 78 |
return internal.ErrNotSupported |
| 79 | 79 |
} |
| 80 | 80 |
return err |
| 81 |
-}) |
|
| 81 |
+}, "5.6") |
|
| 82 | 82 |
|
| 83 |
-var haveEnum64 = internal.NewFeatureTest("ENUM64", "6.0", func() error {
|
|
| 83 |
+var haveDeclTags = internal.NewFeatureTest("BTF decl tags", func() error {
|
|
| 84 |
+ if err := haveBTF(); err != nil {
|
|
| 85 |
+ return err |
|
| 86 |
+ } |
|
| 87 |
+ |
|
| 88 |
+ t := &Typedef{
|
|
| 89 |
+ Name: "a", |
|
| 90 |
+ Type: &Int{},
|
|
| 91 |
+ Tags: []string{"a"},
|
|
| 92 |
+ } |
|
| 93 |
+ |
|
| 94 |
+ err := probeBTF(t) |
|
| 95 |
+ if errors.Is(err, unix.EINVAL) {
|
|
| 96 |
+ return internal.ErrNotSupported |
|
| 97 |
+ } |
|
| 98 |
+ return err |
|
| 99 |
+}, "5.16") |
|
| 100 |
+ |
|
| 101 |
+var haveTypeTags = internal.NewFeatureTest("BTF type tags", func() error {
|
|
| 102 |
+ if err := haveBTF(); err != nil {
|
|
| 103 |
+ return err |
|
| 104 |
+ } |
|
| 105 |
+ |
|
| 106 |
+ t := &TypeTag{
|
|
| 107 |
+ Type: &Int{},
|
|
| 108 |
+ Value: "a", |
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ err := probeBTF(t) |
|
| 112 |
+ if errors.Is(err, unix.EINVAL) {
|
|
| 113 |
+ return internal.ErrNotSupported |
|
| 114 |
+ } |
|
| 115 |
+ return err |
|
| 116 |
+}, "5.17") |
|
| 117 |
+ |
|
| 118 |
+var haveEnum64 = internal.NewFeatureTest("ENUM64", func() error {
|
|
| 84 | 119 |
if err := haveBTF(); err != nil {
|
| 85 | 120 |
return err |
| 86 | 121 |
} |
| ... | ... |
@@ -97,7 +132,7 @@ var haveEnum64 = internal.NewFeatureTest("ENUM64", "6.0", func() error {
|
| 97 | 97 |
return internal.ErrNotSupported |
| 98 | 98 |
} |
| 99 | 99 |
return err |
| 100 |
-}) |
|
| 100 |
+}, "6.0") |
|
| 101 | 101 |
|
| 102 | 102 |
func probeBTF(typ Type) error {
|
| 103 | 103 |
b, err := NewBuilder([]Type{typ})
|
| ... | ... |
@@ -161,6 +161,9 @@ func (gf *GoFormatter) writeTypeLit(typ Type, depth int) error {
|
| 161 | 161 |
case *Datasec: |
| 162 | 162 |
err = gf.writeDatasecLit(v, depth) |
| 163 | 163 |
|
| 164 |
+ case *Var: |
|
| 165 |
+ err = gf.writeTypeLit(v.Type, depth) |
|
| 166 |
+ |
|
| 164 | 167 |
default: |
| 165 | 168 |
return fmt.Errorf("type %T: %w", v, ErrNotSupported)
|
| 166 | 169 |
} |
| ... | ... |
@@ -8,7 +8,7 @@ import ( |
| 8 | 8 |
"sync" |
| 9 | 9 |
|
| 10 | 10 |
"github.com/cilium/ebpf/internal" |
| 11 |
- "github.com/cilium/ebpf/internal/kallsyms" |
|
| 11 |
+ "github.com/cilium/ebpf/internal/linux" |
|
| 12 | 12 |
) |
| 13 | 13 |
|
| 14 | 14 |
var kernelBTF = struct {
|
| ... | ... |
@@ -21,8 +21,6 @@ var kernelBTF = struct {
|
| 21 | 21 |
|
| 22 | 22 |
// FlushKernelSpec removes any cached kernel type information. |
| 23 | 23 |
func FlushKernelSpec() {
|
| 24 |
- kallsyms.FlushKernelModuleCache() |
|
| 25 |
- |
|
| 26 | 24 |
kernelBTF.Lock() |
| 27 | 25 |
defer kernelBTF.Unlock() |
| 28 | 26 |
|
| ... | ... |
@@ -130,7 +128,7 @@ func loadKernelModuleSpec(module string, base *Spec) (*Spec, error) {
|
| 130 | 130 |
|
| 131 | 131 |
// findVMLinux scans multiple well-known paths for vmlinux kernel images. |
| 132 | 132 |
func findVMLinux() (*os.File, error) {
|
| 133 |
- release, err := internal.KernelRelease() |
|
| 133 |
+ release, err := linux.KernelRelease() |
|
| 134 | 134 |
if err != nil {
|
| 135 | 135 |
return nil, err |
| 136 | 136 |
} |
| ... | ... |
@@ -18,6 +18,10 @@ type MarshalOptions struct {
|
| 18 | 18 |
Order binary.ByteOrder |
| 19 | 19 |
// Remove function linkage information for compatibility with <5.6 kernels. |
| 20 | 20 |
StripFuncLinkage bool |
| 21 |
+ // Replace decl tags with a placeholder for compatibility with <5.16 kernels. |
|
| 22 |
+ ReplaceDeclTags bool |
|
| 23 |
+ // Replace TypeTags with a placeholder for compatibility with <5.17 kernels. |
|
| 24 |
+ ReplaceTypeTags bool |
|
| 21 | 25 |
// Replace Enum64 with a placeholder for compatibility with <6.0 kernels. |
| 22 | 26 |
ReplaceEnum64 bool |
| 23 | 27 |
// Prevent the "No type found" error when loading BTF without any types. |
| ... | ... |
@@ -29,6 +33,8 @@ func KernelMarshalOptions() *MarshalOptions {
|
| 29 | 29 |
return &MarshalOptions{
|
| 30 | 30 |
Order: internal.NativeEndian, |
| 31 | 31 |
StripFuncLinkage: haveFuncLinkage() != nil, |
| 32 |
+ ReplaceDeclTags: haveDeclTags() != nil, |
|
| 33 |
+ ReplaceTypeTags: haveTypeTags() != nil, |
|
| 32 | 34 |
ReplaceEnum64: haveEnum64() != nil, |
| 33 | 35 |
PreventNoTypeFound: true, // All current kernels require this. |
| 34 | 36 |
} |
| ... | ... |
@@ -318,15 +324,7 @@ func (e *encoder) deflateType(typ Type) (err error) {
|
| 318 | 318 |
return errors.New("Void is implicit in BTF wire format")
|
| 319 | 319 |
|
| 320 | 320 |
case *Int: |
| 321 |
- raw.SetKind(kindInt) |
|
| 322 |
- raw.SetSize(v.Size) |
|
| 323 |
- |
|
| 324 |
- var bi btfInt |
|
| 325 |
- bi.SetEncoding(v.Encoding) |
|
| 326 |
- // We need to set bits in addition to size, since btf_type_int_is_regular |
|
| 327 |
- // otherwise flags this as a bitfield. |
|
| 328 |
- bi.SetBits(byte(v.Size) * 8) |
|
| 329 |
- raw.data = bi |
|
| 321 |
+ e.deflateInt(&raw, v) |
|
| 330 | 322 |
|
| 331 | 323 |
case *Pointer: |
| 332 | 324 |
raw.SetKind(kindPointer) |
| ... | ... |
@@ -368,8 +366,7 @@ func (e *encoder) deflateType(typ Type) (err error) {
|
| 368 | 368 |
raw.SetType(e.id(v.Type)) |
| 369 | 369 |
|
| 370 | 370 |
case *Const: |
| 371 |
- raw.SetKind(kindConst) |
|
| 372 |
- raw.SetType(e.id(v.Type)) |
|
| 371 |
+ e.deflateConst(&raw, v) |
|
| 373 | 372 |
|
| 374 | 373 |
case *Restrict: |
| 375 | 374 |
raw.SetKind(kindRestrict) |
| ... | ... |
@@ -404,15 +401,10 @@ func (e *encoder) deflateType(typ Type) (err error) {
|
| 404 | 404 |
raw.SetSize(v.Size) |
| 405 | 405 |
|
| 406 | 406 |
case *declTag: |
| 407 |
- raw.SetKind(kindDeclTag) |
|
| 408 |
- raw.SetType(e.id(v.Type)) |
|
| 409 |
- raw.data = &btfDeclTag{uint32(v.Index)}
|
|
| 410 |
- raw.NameOff, err = e.strings.Add(v.Value) |
|
| 407 |
+ err = e.deflateDeclTag(&raw, v) |
|
| 411 | 408 |
|
| 412 |
- case *typeTag: |
|
| 413 |
- raw.SetKind(kindTypeTag) |
|
| 414 |
- raw.SetType(e.id(v.Type)) |
|
| 415 |
- raw.NameOff, err = e.strings.Add(v.Value) |
|
| 409 |
+ case *TypeTag: |
|
| 410 |
+ err = e.deflateTypeTag(&raw, v) |
|
| 416 | 411 |
|
| 417 | 412 |
default: |
| 418 | 413 |
return fmt.Errorf("don't know how to deflate %T", v)
|
| ... | ... |
@@ -425,6 +417,57 @@ func (e *encoder) deflateType(typ Type) (err error) {
|
| 425 | 425 |
return raw.Marshal(e.buf, e.Order) |
| 426 | 426 |
} |
| 427 | 427 |
|
| 428 |
+func (e *encoder) deflateInt(raw *rawType, i *Int) {
|
|
| 429 |
+ raw.SetKind(kindInt) |
|
| 430 |
+ raw.SetSize(i.Size) |
|
| 431 |
+ |
|
| 432 |
+ var bi btfInt |
|
| 433 |
+ bi.SetEncoding(i.Encoding) |
|
| 434 |
+ // We need to set bits in addition to size, since btf_type_int_is_regular |
|
| 435 |
+ // otherwise flags this as a bitfield. |
|
| 436 |
+ bi.SetBits(byte(i.Size) * 8) |
|
| 437 |
+ raw.data = bi |
|
| 438 |
+} |
|
| 439 |
+ |
|
| 440 |
+func (e *encoder) deflateDeclTag(raw *rawType, tag *declTag) (err error) {
|
|
| 441 |
+ // Replace a decl tag with an integer for compatibility with <5.16 kernels, |
|
| 442 |
+ // following libbpf behaviour. |
|
| 443 |
+ if e.ReplaceDeclTags {
|
|
| 444 |
+ typ := &Int{"decl_tag_placeholder", 1, Unsigned}
|
|
| 445 |
+ e.deflateInt(raw, typ) |
|
| 446 |
+ |
|
| 447 |
+ // Add the placeholder type name to the string table. The encoder added the |
|
| 448 |
+ // original type name before this call. |
|
| 449 |
+ raw.NameOff, err = e.strings.Add(typ.TypeName()) |
|
| 450 |
+ return |
|
| 451 |
+ } |
|
| 452 |
+ |
|
| 453 |
+ raw.SetKind(kindDeclTag) |
|
| 454 |
+ raw.SetType(e.id(tag.Type)) |
|
| 455 |
+ raw.data = &btfDeclTag{uint32(tag.Index)}
|
|
| 456 |
+ raw.NameOff, err = e.strings.Add(tag.Value) |
|
| 457 |
+ return |
|
| 458 |
+} |
|
| 459 |
+ |
|
| 460 |
+func (e *encoder) deflateConst(raw *rawType, c *Const) {
|
|
| 461 |
+ raw.SetKind(kindConst) |
|
| 462 |
+ raw.SetType(e.id(c.Type)) |
|
| 463 |
+} |
|
| 464 |
+ |
|
| 465 |
+func (e *encoder) deflateTypeTag(raw *rawType, tag *TypeTag) (err error) {
|
|
| 466 |
+ // Replace a type tag with a const qualifier for compatibility with <5.17 |
|
| 467 |
+ // kernels, following libbpf behaviour. |
|
| 468 |
+ if e.ReplaceTypeTags {
|
|
| 469 |
+ e.deflateConst(raw, &Const{tag.Type})
|
|
| 470 |
+ return |
|
| 471 |
+ } |
|
| 472 |
+ |
|
| 473 |
+ raw.SetKind(kindTypeTag) |
|
| 474 |
+ raw.SetType(e.id(tag.Type)) |
|
| 475 |
+ raw.NameOff, err = e.strings.Add(tag.Value) |
|
| 476 |
+ return |
|
| 477 |
+} |
|
| 478 |
+ |
|
| 428 | 479 |
func (e *encoder) deflateUnion(raw *rawType, union *Union) (err error) {
|
| 429 | 480 |
raw.SetKind(kindUnion) |
| 430 | 481 |
raw.SetSize(union.Size) |
| ... | ... |
@@ -521,7 +564,7 @@ func (e *encoder) deflateEnum64(raw *rawType, enum *Enum) (err error) {
|
| 521 | 521 |
}) |
| 522 | 522 |
} |
| 523 | 523 |
|
| 524 |
- return e.deflateUnion(raw, &Union{enum.Name, enum.Size, members})
|
|
| 524 |
+ return e.deflateUnion(raw, &Union{enum.Name, enum.Size, members, nil})
|
|
| 525 | 525 |
} |
| 526 | 526 |
|
| 527 | 527 |
raw.SetKind(kindEnum64) |
| ... | ... |
@@ -40,9 +40,12 @@ func children(typ Type, yield func(child *Type) bool) bool {
|
| 40 | 40 |
// Explicitly type switch on the most common types to allow the inliner to |
| 41 | 41 |
// do its work. This avoids allocating intermediate slices from walk() on |
| 42 | 42 |
// the heap. |
| 43 |
+ var tags []string |
|
| 43 | 44 |
switch v := typ.(type) {
|
| 44 |
- case *Void, *Int, *Enum, *Fwd, *Float: |
|
| 45 |
+ case *Void, *Int, *Enum, *Fwd, *Float, *declTag: |
|
| 45 | 46 |
// No children to traverse. |
| 47 |
+ // declTags is declared as a leaf type since it's parsed into .Tags fields of other types |
|
| 48 |
+ // during unmarshaling. |
|
| 46 | 49 |
case *Pointer: |
| 47 | 50 |
if !yield(&v.Target) {
|
| 48 | 51 |
return false |
| ... | ... |
@@ -59,17 +62,32 @@ func children(typ Type, yield func(child *Type) bool) bool {
|
| 59 | 59 |
if !yield(&v.Members[i].Type) {
|
| 60 | 60 |
return false |
| 61 | 61 |
} |
| 62 |
+ for _, t := range v.Members[i].Tags {
|
|
| 63 |
+ var tag Type = &declTag{v, t, i}
|
|
| 64 |
+ if !yield(&tag) {
|
|
| 65 |
+ return false |
|
| 66 |
+ } |
|
| 67 |
+ } |
|
| 62 | 68 |
} |
| 69 |
+ tags = v.Tags |
|
| 63 | 70 |
case *Union: |
| 64 | 71 |
for i := range v.Members {
|
| 65 | 72 |
if !yield(&v.Members[i].Type) {
|
| 66 | 73 |
return false |
| 67 | 74 |
} |
| 75 |
+ for _, t := range v.Members[i].Tags {
|
|
| 76 |
+ var tag Type = &declTag{v, t, i}
|
|
| 77 |
+ if !yield(&tag) {
|
|
| 78 |
+ return false |
|
| 79 |
+ } |
|
| 80 |
+ } |
|
| 68 | 81 |
} |
| 82 |
+ tags = v.Tags |
|
| 69 | 83 |
case *Typedef: |
| 70 | 84 |
if !yield(&v.Type) {
|
| 71 | 85 |
return false |
| 72 | 86 |
} |
| 87 |
+ tags = v.Tags |
|
| 73 | 88 |
case *Volatile: |
| 74 | 89 |
if !yield(&v.Type) {
|
| 75 | 90 |
return false |
| ... | ... |
@@ -86,6 +104,20 @@ func children(typ Type, yield func(child *Type) bool) bool {
|
| 86 | 86 |
if !yield(&v.Type) {
|
| 87 | 87 |
return false |
| 88 | 88 |
} |
| 89 |
+ if fp, ok := v.Type.(*FuncProto); ok {
|
|
| 90 |
+ for i := range fp.Params {
|
|
| 91 |
+ if len(v.ParamTags) <= i {
|
|
| 92 |
+ continue |
|
| 93 |
+ } |
|
| 94 |
+ for _, t := range v.ParamTags[i] {
|
|
| 95 |
+ var tag Type = &declTag{v, t, i}
|
|
| 96 |
+ if !yield(&tag) {
|
|
| 97 |
+ return false |
|
| 98 |
+ } |
|
| 99 |
+ } |
|
| 100 |
+ } |
|
| 101 |
+ } |
|
| 102 |
+ tags = v.Tags |
|
| 89 | 103 |
case *FuncProto: |
| 90 | 104 |
if !yield(&v.Return) {
|
| 91 | 105 |
return false |
| ... | ... |
@@ -99,17 +131,14 @@ func children(typ Type, yield func(child *Type) bool) bool {
|
| 99 | 99 |
if !yield(&v.Type) {
|
| 100 | 100 |
return false |
| 101 | 101 |
} |
| 102 |
+ tags = v.Tags |
|
| 102 | 103 |
case *Datasec: |
| 103 | 104 |
for i := range v.Vars {
|
| 104 | 105 |
if !yield(&v.Vars[i].Type) {
|
| 105 | 106 |
return false |
| 106 | 107 |
} |
| 107 | 108 |
} |
| 108 |
- case *declTag: |
|
| 109 |
- if !yield(&v.Type) {
|
|
| 110 |
- return false |
|
| 111 |
- } |
|
| 112 |
- case *typeTag: |
|
| 109 |
+ case *TypeTag: |
|
| 113 | 110 |
if !yield(&v.Type) {
|
| 114 | 111 |
return false |
| 115 | 112 |
} |
| ... | ... |
@@ -119,5 +148,12 @@ func children(typ Type, yield func(child *Type) bool) bool {
|
| 119 | 119 |
panic(fmt.Sprintf("don't know how to walk Type %T", v))
|
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 |
+ for _, t := range tags {
|
|
| 123 |
+ var tag Type = &declTag{typ, t, -1}
|
|
| 124 |
+ if !yield(&tag) {
|
|
| 125 |
+ return false |
|
| 126 |
+ } |
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 122 | 129 |
return true |
| 123 | 130 |
} |
| ... | ... |
@@ -67,7 +67,7 @@ var ( |
| 67 | 67 |
_ Type = (*Datasec)(nil) |
| 68 | 68 |
_ Type = (*Float)(nil) |
| 69 | 69 |
_ Type = (*declTag)(nil) |
| 70 |
- _ Type = (*typeTag)(nil) |
|
| 70 |
+ _ Type = (*TypeTag)(nil) |
|
| 71 | 71 |
_ Type = (*cycle)(nil) |
| 72 | 72 |
) |
| 73 | 73 |
|
| ... | ... |
@@ -169,6 +169,7 @@ type Struct struct {
|
| 169 | 169 |
// The size of the struct including padding, in bytes |
| 170 | 170 |
Size uint32 |
| 171 | 171 |
Members []Member |
| 172 |
+ Tags []string |
|
| 172 | 173 |
} |
| 173 | 174 |
|
| 174 | 175 |
func (s *Struct) Format(fs fmt.State, verb rune) {
|
| ... | ... |
@@ -182,6 +183,7 @@ func (s *Struct) size() uint32 { return s.Size }
|
| 182 | 182 |
func (s *Struct) copy() Type {
|
| 183 | 183 |
cpy := *s |
| 184 | 184 |
cpy.Members = copyMembers(s.Members) |
| 185 |
+ cpy.Tags = copyTags(cpy.Tags) |
|
| 185 | 186 |
return &cpy |
| 186 | 187 |
} |
| 187 | 188 |
|
| ... | ... |
@@ -195,6 +197,7 @@ type Union struct {
|
| 195 | 195 |
// The size of the union including padding, in bytes. |
| 196 | 196 |
Size uint32 |
| 197 | 197 |
Members []Member |
| 198 |
+ Tags []string |
|
| 198 | 199 |
} |
| 199 | 200 |
|
| 200 | 201 |
func (u *Union) Format(fs fmt.State, verb rune) {
|
| ... | ... |
@@ -208,6 +211,7 @@ func (u *Union) size() uint32 { return u.Size }
|
| 208 | 208 |
func (u *Union) copy() Type {
|
| 209 | 209 |
cpy := *u |
| 210 | 210 |
cpy.Members = copyMembers(u.Members) |
| 211 |
+ cpy.Tags = copyTags(cpy.Tags) |
|
| 211 | 212 |
return &cpy |
| 212 | 213 |
} |
| 213 | 214 |
|
| ... | ... |
@@ -218,6 +222,18 @@ func (u *Union) members() []Member {
|
| 218 | 218 |
func copyMembers(orig []Member) []Member {
|
| 219 | 219 |
cpy := make([]Member, len(orig)) |
| 220 | 220 |
copy(cpy, orig) |
| 221 |
+ for i, member := range cpy {
|
|
| 222 |
+ cpy[i].Tags = copyTags(member.Tags) |
|
| 223 |
+ } |
|
| 224 |
+ return cpy |
|
| 225 |
+} |
|
| 226 |
+ |
|
| 227 |
+func copyTags(orig []string) []string {
|
|
| 228 |
+ if orig == nil { // preserve nil vs zero-len slice distinction
|
|
| 229 |
+ return nil |
|
| 230 |
+ } |
|
| 231 |
+ cpy := make([]string, len(orig)) |
|
| 232 |
+ copy(cpy, orig) |
|
| 221 | 233 |
return cpy |
| 222 | 234 |
} |
| 223 | 235 |
|
| ... | ... |
@@ -247,6 +263,7 @@ type Member struct {
|
| 247 | 247 |
Type Type |
| 248 | 248 |
Offset Bits |
| 249 | 249 |
BitfieldSize Bits |
| 250 |
+ Tags []string |
|
| 250 | 251 |
} |
| 251 | 252 |
|
| 252 | 253 |
// Enum lists possible values. |
| ... | ... |
@@ -334,6 +351,7 @@ func (f *Fwd) matches(typ Type) bool {
|
| 334 | 334 |
type Typedef struct {
|
| 335 | 335 |
Name string |
| 336 | 336 |
Type Type |
| 337 |
+ Tags []string |
|
| 337 | 338 |
} |
| 338 | 339 |
|
| 339 | 340 |
func (td *Typedef) Format(fs fmt.State, verb rune) {
|
| ... | ... |
@@ -344,6 +362,7 @@ func (td *Typedef) TypeName() string { return td.Name }
|
| 344 | 344 |
|
| 345 | 345 |
func (td *Typedef) copy() Type {
|
| 346 | 346 |
cpy := *td |
| 347 |
+ cpy.Tags = copyTags(td.Tags) |
|
| 347 | 348 |
return &cpy |
| 348 | 349 |
} |
| 349 | 350 |
|
| ... | ... |
@@ -403,6 +422,12 @@ type Func struct {
|
| 403 | 403 |
Name string |
| 404 | 404 |
Type Type |
| 405 | 405 |
Linkage FuncLinkage |
| 406 |
+ Tags []string |
|
| 407 |
+ // ParamTags holds a list of tags for each parameter of the FuncProto to which `Type` points. |
|
| 408 |
+ // If no tags are present for any param, the outer slice will be nil/len(ParamTags)==0. |
|
| 409 |
+ // If at least 1 param has a tag, the outer slice will have the same length as the number of params. |
|
| 410 |
+ // The inner slice contains the tags and may be nil/len(ParamTags[i])==0 if no tags are present for that param. |
|
| 411 |
+ ParamTags [][]string |
|
| 406 | 412 |
} |
| 407 | 413 |
|
| 408 | 414 |
func FuncMetadata(ins *asm.Instruction) *Func {
|
| ... | ... |
@@ -424,6 +449,14 @@ func (f *Func) TypeName() string { return f.Name }
|
| 424 | 424 |
|
| 425 | 425 |
func (f *Func) copy() Type {
|
| 426 | 426 |
cpy := *f |
| 427 |
+ cpy.Tags = copyTags(f.Tags) |
|
| 428 |
+ if f.ParamTags != nil { // preserve nil vs zero-len slice distinction
|
|
| 429 |
+ ptCopy := make([][]string, len(f.ParamTags)) |
|
| 430 |
+ for i, tags := range f.ParamTags {
|
|
| 431 |
+ ptCopy[i] = copyTags(tags) |
|
| 432 |
+ } |
|
| 433 |
+ cpy.ParamTags = ptCopy |
|
| 434 |
+ } |
|
| 427 | 435 |
return &cpy |
| 428 | 436 |
} |
| 429 | 437 |
|
| ... | ... |
@@ -456,6 +489,7 @@ type Var struct {
|
| 456 | 456 |
Name string |
| 457 | 457 |
Type Type |
| 458 | 458 |
Linkage VarLinkage |
| 459 |
+ Tags []string |
|
| 459 | 460 |
} |
| 460 | 461 |
|
| 461 | 462 |
func (v *Var) Format(fs fmt.State, verb rune) {
|
| ... | ... |
@@ -466,6 +500,7 @@ func (v *Var) TypeName() string { return v.Name }
|
| 466 | 466 |
|
| 467 | 467 |
func (v *Var) copy() Type {
|
| 468 | 468 |
cpy := *v |
| 469 |
+ cpy.Tags = copyTags(v.Tags) |
|
| 469 | 470 |
return &cpy |
| 470 | 471 |
} |
| 471 | 472 |
|
| ... | ... |
@@ -540,19 +575,25 @@ func (dt *declTag) copy() Type {
|
| 540 | 540 |
return &cpy |
| 541 | 541 |
} |
| 542 | 542 |
|
| 543 |
-// typeTag associates metadata with a type. |
|
| 544 |
-type typeTag struct {
|
|
| 543 |
+// TypeTag associates metadata with a pointer type. Tag types act as a custom |
|
| 544 |
+// modifier(const, restrict, volatile) for the target type. Unlike declTags, |
|
| 545 |
+// TypeTags are ordered so the order in which they are added matters. |
|
| 546 |
+// |
|
| 547 |
+// One of their uses is to mark pointers as `__kptr` meaning a pointer points |
|
| 548 |
+// to kernel memory. Adding a `__kptr` to pointers in map values allows you |
|
| 549 |
+// to store pointers to kernel memory in maps. |
|
| 550 |
+type TypeTag struct {
|
|
| 545 | 551 |
Type Type |
| 546 | 552 |
Value string |
| 547 | 553 |
} |
| 548 | 554 |
|
| 549 |
-func (tt *typeTag) Format(fs fmt.State, verb rune) {
|
|
| 555 |
+func (tt *TypeTag) Format(fs fmt.State, verb rune) {
|
|
| 550 | 556 |
formatType(fs, verb, tt, "type=", tt.Type, "value=", tt.Value) |
| 551 | 557 |
} |
| 552 | 558 |
|
| 553 |
-func (tt *typeTag) TypeName() string { return "" }
|
|
| 554 |
-func (tt *typeTag) qualify() Type { return tt.Type }
|
|
| 555 |
-func (tt *typeTag) copy() Type {
|
|
| 559 |
+func (tt *TypeTag) TypeName() string { return "" }
|
|
| 560 |
+func (tt *TypeTag) qualify() Type { return tt.Type }
|
|
| 561 |
+func (tt *TypeTag) copy() Type {
|
|
| 556 | 562 |
cpy := *tt |
| 557 | 563 |
return &cpy |
| 558 | 564 |
} |
| ... | ... |
@@ -591,7 +632,7 @@ var ( |
| 591 | 591 |
_ qualifier = (*Const)(nil) |
| 592 | 592 |
_ qualifier = (*Restrict)(nil) |
| 593 | 593 |
_ qualifier = (*Volatile)(nil) |
| 594 |
- _ qualifier = (*typeTag)(nil) |
|
| 594 |
+ _ qualifier = (*TypeTag)(nil) |
|
| 595 | 595 |
) |
| 596 | 596 |
|
| 597 | 597 |
var errUnsizedType = errors.New("type is unsized")
|
| ... | ... |
@@ -918,7 +959,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt |
| 918 | 918 |
if err != nil {
|
| 919 | 919 |
return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err)
|
| 920 | 920 |
} |
| 921 |
- typ = &Struct{name, header.Size(), members}
|
|
| 921 |
+ typ = &Struct{name, header.Size(), members, nil}
|
|
| 922 | 922 |
|
| 923 | 923 |
case kindUnion: |
| 924 | 924 |
vlen := header.Vlen() |
| ... | ... |
@@ -935,7 +976,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt |
| 935 | 935 |
if err != nil {
|
| 936 | 936 |
return nil, fmt.Errorf("union %s (id %d): %w", name, id, err)
|
| 937 | 937 |
} |
| 938 |
- typ = &Union{name, header.Size(), members}
|
|
| 938 |
+ typ = &Union{name, header.Size(), members, nil}
|
|
| 939 | 939 |
|
| 940 | 940 |
case kindEnum: |
| 941 | 941 |
vlen := header.Vlen() |
| ... | ... |
@@ -968,7 +1009,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt |
| 968 | 968 |
typ = &Fwd{name, header.FwdKind()}
|
| 969 | 969 |
|
| 970 | 970 |
case kindTypedef: |
| 971 |
- typedef := &Typedef{name, nil}
|
|
| 971 |
+ typedef := &Typedef{name, nil, nil}
|
|
| 972 | 972 |
fixup(header.Type(), &typedef.Type) |
| 973 | 973 |
typ = typedef |
| 974 | 974 |
|
| ... | ... |
@@ -988,7 +1029,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt |
| 988 | 988 |
typ = restrict |
| 989 | 989 |
|
| 990 | 990 |
case kindFunc: |
| 991 |
- fn := &Func{name, nil, header.Linkage()}
|
|
| 991 |
+ fn := &Func{name, nil, header.Linkage(), nil, nil}
|
|
| 992 | 992 |
fixup(header.Type(), &fn.Type) |
| 993 | 993 |
typ = fn |
| 994 | 994 |
|
| ... | ... |
@@ -1030,7 +1071,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt |
| 1030 | 1030 |
return nil, fmt.Errorf("can't read btfVariable, id: %d: %w", id, err)
|
| 1031 | 1031 |
} |
| 1032 | 1032 |
|
| 1033 |
- v := &Var{name, nil, VarLinkage(bVariable.Linkage)}
|
|
| 1033 |
+ v := &Var{name, nil, VarLinkage(bVariable.Linkage), nil}
|
|
| 1034 | 1034 |
fixup(header.Type(), &v.Type) |
| 1035 | 1035 |
typ = v |
| 1036 | 1036 |
|
| ... | ... |
@@ -1081,7 +1122,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt |
| 1081 | 1081 |
declTags = append(declTags, dt) |
| 1082 | 1082 |
|
| 1083 | 1083 |
case kindTypeTag: |
| 1084 |
- tt := &typeTag{nil, name}
|
|
| 1084 |
+ tt := &TypeTag{nil, name}
|
|
| 1085 | 1085 |
fixup(header.Type(), &tt.Type) |
| 1086 | 1086 |
typ = tt |
| 1087 | 1087 |
|
| ... | ... |
@@ -1142,26 +1183,69 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt |
| 1142 | 1142 |
|
| 1143 | 1143 |
for _, dt := range declTags {
|
| 1144 | 1144 |
switch t := dt.Type.(type) {
|
| 1145 |
- case *Var, *Typedef: |
|
| 1145 |
+ case *Var: |
|
| 1146 |
+ if dt.Index != -1 {
|
|
| 1147 |
+ return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index)
|
|
| 1148 |
+ } |
|
| 1149 |
+ t.Tags = append(t.Tags, dt.Value) |
|
| 1150 |
+ |
|
| 1151 |
+ case *Typedef: |
|
| 1146 | 1152 |
if dt.Index != -1 {
|
| 1147 |
- return nil, fmt.Errorf("type %s: index %d is not -1", dt, dt.Index)
|
|
| 1153 |
+ return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index)
|
|
| 1148 | 1154 |
} |
| 1155 |
+ t.Tags = append(t.Tags, dt.Value) |
|
| 1149 | 1156 |
|
| 1150 | 1157 |
case composite: |
| 1151 |
- if dt.Index >= len(t.members()) {
|
|
| 1152 |
- return nil, fmt.Errorf("type %s: index %d exceeds members of %s", dt, dt.Index, t)
|
|
| 1158 |
+ if dt.Index >= 0 {
|
|
| 1159 |
+ members := t.members() |
|
| 1160 |
+ if dt.Index >= len(members) {
|
|
| 1161 |
+ return nil, fmt.Errorf("type %s: component idx %d exceeds members of %s", dt, dt.Index, t)
|
|
| 1162 |
+ } |
|
| 1163 |
+ |
|
| 1164 |
+ members[dt.Index].Tags = append(members[dt.Index].Tags, dt.Value) |
|
| 1165 |
+ continue |
|
| 1166 |
+ } |
|
| 1167 |
+ |
|
| 1168 |
+ if dt.Index == -1 {
|
|
| 1169 |
+ switch t2 := t.(type) {
|
|
| 1170 |
+ case *Struct: |
|
| 1171 |
+ t2.Tags = append(t2.Tags, dt.Value) |
|
| 1172 |
+ case *Union: |
|
| 1173 |
+ t2.Tags = append(t2.Tags, dt.Value) |
|
| 1174 |
+ } |
|
| 1175 |
+ |
|
| 1176 |
+ continue |
|
| 1153 | 1177 |
} |
| 1154 | 1178 |
|
| 1179 |
+ return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t)
|
|
| 1180 |
+ |
|
| 1155 | 1181 |
case *Func: |
| 1156 | 1182 |
fp, ok := t.Type.(*FuncProto) |
| 1157 | 1183 |
if !ok {
|
| 1158 | 1184 |
return nil, fmt.Errorf("type %s: %s is not a FuncProto", dt, t.Type)
|
| 1159 | 1185 |
} |
| 1160 | 1186 |
|
| 1161 |
- if dt.Index >= len(fp.Params) {
|
|
| 1162 |
- return nil, fmt.Errorf("type %s: index %d exceeds params of %s", dt, dt.Index, t)
|
|
| 1187 |
+ // Ensure the number of argument tag lists equals the number of arguments |
|
| 1188 |
+ if len(t.ParamTags) == 0 {
|
|
| 1189 |
+ t.ParamTags = make([][]string, len(fp.Params)) |
|
| 1190 |
+ } |
|
| 1191 |
+ |
|
| 1192 |
+ if dt.Index >= 0 {
|
|
| 1193 |
+ if dt.Index >= len(fp.Params) {
|
|
| 1194 |
+ return nil, fmt.Errorf("type %s: component idx %d exceeds params of %s", dt, dt.Index, t)
|
|
| 1195 |
+ } |
|
| 1196 |
+ |
|
| 1197 |
+ t.ParamTags[dt.Index] = append(t.ParamTags[dt.Index], dt.Value) |
|
| 1198 |
+ continue |
|
| 1163 | 1199 |
} |
| 1164 | 1200 |
|
| 1201 |
+ if dt.Index == -1 {
|
|
| 1202 |
+ t.Tags = append(t.Tags, dt.Value) |
|
| 1203 |
+ continue |
|
| 1204 |
+ } |
|
| 1205 |
+ |
|
| 1206 |
+ return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t)
|
|
| 1207 |
+ |
|
| 1165 | 1208 |
default: |
| 1166 | 1209 |
return nil, fmt.Errorf("type %s: decl tag for type %s is not supported", dt, t)
|
| 1167 | 1210 |
} |
| ... | ... |
@@ -1207,6 +1291,20 @@ func UnderlyingType(typ Type) Type {
|
| 1207 | 1207 |
return &cycle{typ}
|
| 1208 | 1208 |
} |
| 1209 | 1209 |
|
| 1210 |
+// QualifiedType returns the type with all qualifiers removed. |
|
| 1211 |
+func QualifiedType(typ Type) Type {
|
|
| 1212 |
+ result := typ |
|
| 1213 |
+ for depth := 0; depth <= maxResolveDepth; depth++ {
|
|
| 1214 |
+ switch v := (result).(type) {
|
|
| 1215 |
+ case qualifier: |
|
| 1216 |
+ result = v.qualify() |
|
| 1217 |
+ default: |
|
| 1218 |
+ return result |
|
| 1219 |
+ } |
|
| 1220 |
+ } |
|
| 1221 |
+ return &cycle{typ}
|
|
| 1222 |
+} |
|
| 1223 |
+ |
|
| 1210 | 1224 |
// As returns typ if is of type T. Otherwise it peels qualifiers and Typedefs |
| 1211 | 1225 |
// until it finds a T. |
| 1212 | 1226 |
// |
| ... | ... |
@@ -12,7 +12,7 @@ func datasecResolveWorkaround(b *Builder, ds *Datasec) error {
|
| 12 | 12 |
} |
| 13 | 13 |
|
| 14 | 14 |
switch v.Type.(type) {
|
| 15 |
- case *Typedef, *Volatile, *Const, *Restrict, *typeTag: |
|
| 15 |
+ case *Typedef, *Volatile, *Const, *Restrict, *TypeTag: |
|
| 16 | 16 |
// NB: We must never call Add on a Datasec, otherwise we risk |
| 17 | 17 |
// infinite recursion. |
| 18 | 18 |
_, err := b.Add(v.Type) |
| ... | ... |
@@ -10,8 +10,10 @@ import ( |
| 10 | 10 |
"github.com/cilium/ebpf/asm" |
| 11 | 11 |
"github.com/cilium/ebpf/btf" |
| 12 | 12 |
"github.com/cilium/ebpf/internal" |
| 13 |
+ "github.com/cilium/ebpf/internal/kallsyms" |
|
| 13 | 14 |
"github.com/cilium/ebpf/internal/kconfig" |
| 14 |
- "github.com/cilium/ebpf/internal/sysenc" |
|
| 15 |
+ "github.com/cilium/ebpf/internal/linux" |
|
| 16 |
+ "github.com/cilium/ebpf/internal/sys" |
|
| 15 | 17 |
) |
| 16 | 18 |
|
| 17 | 19 |
// CollectionOptions control loading a collection into the kernel. |
| ... | ... |
@@ -38,6 +40,11 @@ type CollectionSpec struct {
|
| 38 | 38 |
Maps map[string]*MapSpec |
| 39 | 39 |
Programs map[string]*ProgramSpec |
| 40 | 40 |
|
| 41 |
+ // Variables refer to global variables declared in the ELF. They can be read |
|
| 42 |
+ // and modified freely before loading the Collection. Modifying them after |
|
| 43 |
+ // loading has no effect on a running eBPF program. |
|
| 44 |
+ Variables map[string]*VariableSpec |
|
| 45 |
+ |
|
| 41 | 46 |
// Types holds type information about Maps and Programs. |
| 42 | 47 |
// Modifications to Types are currently undefined behaviour. |
| 43 | 48 |
Types *btf.Spec |
| ... | ... |
@@ -56,6 +63,7 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {
|
| 56 | 56 |
cpy := CollectionSpec{
|
| 57 | 57 |
Maps: make(map[string]*MapSpec, len(cs.Maps)), |
| 58 | 58 |
Programs: make(map[string]*ProgramSpec, len(cs.Programs)), |
| 59 |
+ Variables: make(map[string]*VariableSpec, len(cs.Variables)), |
|
| 59 | 60 |
ByteOrder: cs.ByteOrder, |
| 60 | 61 |
Types: cs.Types.Copy(), |
| 61 | 62 |
} |
| ... | ... |
@@ -68,6 +76,10 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {
|
| 68 | 68 |
cpy.Programs[name] = spec.Copy() |
| 69 | 69 |
} |
| 70 | 70 |
|
| 71 |
+ for name, spec := range cs.Variables {
|
|
| 72 |
+ cpy.Variables[name] = spec.copy(&cpy) |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 71 | 75 |
return &cpy |
| 72 | 76 |
} |
| 73 | 77 |
|
| ... | ... |
@@ -134,65 +146,24 @@ func (m *MissingConstantsError) Error() string {
|
| 134 | 134 |
// From Linux 5.5 the verifier will use constants to eliminate dead code. |
| 135 | 135 |
// |
| 136 | 136 |
// Returns an error wrapping [MissingConstantsError] if a constant doesn't exist. |
| 137 |
+// |
|
| 138 |
+// Deprecated: Use [CollectionSpec.Variables] to interact with constants instead. |
|
| 139 |
+// RewriteConstants is now a wrapper around the VariableSpec API. |
|
| 137 | 140 |
func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error {
|
| 138 |
- replaced := make(map[string]bool) |
|
| 139 |
- |
|
| 140 |
- for name, spec := range cs.Maps {
|
|
| 141 |
- if !strings.HasPrefix(name, ".rodata") {
|
|
| 141 |
+ var missing []string |
|
| 142 |
+ for n, c := range consts {
|
|
| 143 |
+ v, ok := cs.Variables[n] |
|
| 144 |
+ if !ok {
|
|
| 145 |
+ missing = append(missing, n) |
|
| 142 | 146 |
continue |
| 143 | 147 |
} |
| 144 | 148 |
|
| 145 |
- b, ds, err := spec.dataSection() |
|
| 146 |
- if errors.Is(err, errMapNoBTFValue) {
|
|
| 147 |
- // Data sections without a BTF Datasec are valid, but don't support |
|
| 148 |
- // constant replacements. |
|
| 149 |
- continue |
|
| 150 |
- } |
|
| 151 |
- if err != nil {
|
|
| 152 |
- return fmt.Errorf("map %s: %w", name, err)
|
|
| 149 |
+ if !v.Constant() {
|
|
| 150 |
+ return fmt.Errorf("variable %s is not a constant", n)
|
|
| 153 | 151 |
} |
| 154 | 152 |
|
| 155 |
- // MapSpec.Copy() performs a shallow copy. Fully copy the byte slice |
|
| 156 |
- // to avoid any changes affecting other copies of the MapSpec. |
|
| 157 |
- cpy := make([]byte, len(b)) |
|
| 158 |
- copy(cpy, b) |
|
| 159 |
- |
|
| 160 |
- for _, v := range ds.Vars {
|
|
| 161 |
- vname := v.Type.TypeName() |
|
| 162 |
- replacement, ok := consts[vname] |
|
| 163 |
- if !ok {
|
|
| 164 |
- continue |
|
| 165 |
- } |
|
| 166 |
- |
|
| 167 |
- if _, ok := v.Type.(*btf.Var); !ok {
|
|
| 168 |
- return fmt.Errorf("section %s: unexpected type %T for variable %s", name, v.Type, vname)
|
|
| 169 |
- } |
|
| 170 |
- |
|
| 171 |
- if replaced[vname] {
|
|
| 172 |
- return fmt.Errorf("section %s: duplicate variable %s", name, vname)
|
|
| 173 |
- } |
|
| 174 |
- |
|
| 175 |
- if int(v.Offset+v.Size) > len(cpy) {
|
|
| 176 |
- return fmt.Errorf("section %s: offset %d(+%d) for variable %s is out of bounds", name, v.Offset, v.Size, vname)
|
|
| 177 |
- } |
|
| 178 |
- |
|
| 179 |
- b, err := sysenc.Marshal(replacement, int(v.Size)) |
|
| 180 |
- if err != nil {
|
|
| 181 |
- return fmt.Errorf("marshaling constant replacement %s: %w", vname, err)
|
|
| 182 |
- } |
|
| 183 |
- |
|
| 184 |
- b.CopyTo(cpy[v.Offset : v.Offset+v.Size]) |
|
| 185 |
- |
|
| 186 |
- replaced[vname] = true |
|
| 187 |
- } |
|
| 188 |
- |
|
| 189 |
- spec.Contents[0] = MapKV{Key: uint32(0), Value: cpy}
|
|
| 190 |
- } |
|
| 191 |
- |
|
| 192 |
- var missing []string |
|
| 193 |
- for c := range consts {
|
|
| 194 |
- if !replaced[c] {
|
|
| 195 |
- missing = append(missing, c) |
|
| 153 |
+ if err := v.Set(c); err != nil {
|
|
| 154 |
+ return fmt.Errorf("rewriting constant %s: %w", n, err)
|
|
| 196 | 155 |
} |
| 197 | 156 |
} |
| 198 | 157 |
|
| ... | ... |
@@ -210,25 +181,23 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
|
| 210 | 210 |
// if this sounds useful. |
| 211 | 211 |
// |
| 212 | 212 |
// 'to' must be a pointer to a struct. A field of the |
| 213 |
-// struct is updated with values from Programs or Maps if it |
|
| 214 |
-// has an `ebpf` tag and its type is *ProgramSpec or *MapSpec. |
|
| 213 |
+// struct is updated with values from Programs, Maps or Variables if it |
|
| 214 |
+// has an `ebpf` tag and its type is *ProgramSpec, *MapSpec or *VariableSpec. |
|
| 215 | 215 |
// The tag's value specifies the name of the program or map as |
| 216 | 216 |
// found in the CollectionSpec. |
| 217 | 217 |
// |
| 218 | 218 |
// struct {
|
| 219 |
-// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` |
|
| 220 |
-// Bar *ebpf.MapSpec `ebpf:"bar_map"` |
|
| 219 |
+// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"` |
|
| 220 |
+// Bar *ebpf.MapSpec `ebpf:"bar_map"` |
|
| 221 |
+// Var *ebpf.VariableSpec `ebpf:"some_var"` |
|
| 221 | 222 |
// Ignored int |
| 222 | 223 |
// } |
| 223 | 224 |
// |
| 224 | 225 |
// Returns an error if any of the eBPF objects can't be found, or |
| 225 |
-// if the same MapSpec or ProgramSpec is assigned multiple times. |
|
| 226 |
+// if the same Spec is assigned multiple times. |
|
| 226 | 227 |
func (cs *CollectionSpec) Assign(to interface{}) error {
|
| 227 |
- // Assign() only supports assigning ProgramSpecs and MapSpecs, |
|
| 228 |
- // so doesn't load any resources into the kernel. |
|
| 229 | 228 |
getValue := func(typ reflect.Type, name string) (interface{}, error) {
|
| 230 | 229 |
switch typ {
|
| 231 |
- |
|
| 232 | 230 |
case reflect.TypeOf((*ProgramSpec)(nil)): |
| 233 | 231 |
if p := cs.Programs[name]; p != nil {
|
| 234 | 232 |
return p, nil |
| ... | ... |
@@ -241,6 +210,12 @@ func (cs *CollectionSpec) Assign(to interface{}) error {
|
| 241 | 241 |
} |
| 242 | 242 |
return nil, fmt.Errorf("missing map %q", name)
|
| 243 | 243 |
|
| 244 |
+ case reflect.TypeOf((*VariableSpec)(nil)): |
|
| 245 |
+ if v := cs.Variables[name]; v != nil {
|
|
| 246 |
+ return v, nil |
|
| 247 |
+ } |
|
| 248 |
+ return nil, fmt.Errorf("missing variable %q", name)
|
|
| 249 |
+ |
|
| 244 | 250 |
default: |
| 245 | 251 |
return nil, fmt.Errorf("unsupported type %s", typ)
|
| 246 | 252 |
} |
| ... | ... |
@@ -286,6 +261,7 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
|
| 286 | 286 |
// Support assigning Programs and Maps, lazy-loading the required objects. |
| 287 | 287 |
assignedMaps := make(map[string]bool) |
| 288 | 288 |
assignedProgs := make(map[string]bool) |
| 289 |
+ assignedVars := make(map[string]bool) |
|
| 289 | 290 |
|
| 290 | 291 |
getValue := func(typ reflect.Type, name string) (interface{}, error) {
|
| 291 | 292 |
switch typ {
|
| ... | ... |
@@ -298,6 +274,10 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
|
| 298 | 298 |
assignedMaps[name] = true |
| 299 | 299 |
return loader.loadMap(name) |
| 300 | 300 |
|
| 301 |
+ case reflect.TypeOf((*Variable)(nil)): |
|
| 302 |
+ assignedVars[name] = true |
|
| 303 |
+ return loader.loadVariable(name) |
|
| 304 |
+ |
|
| 301 | 305 |
default: |
| 302 | 306 |
return nil, fmt.Errorf("unsupported type %s", typ)
|
| 303 | 307 |
} |
| ... | ... |
@@ -338,15 +318,22 @@ func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions)
|
| 338 | 338 |
for p := range assignedProgs {
|
| 339 | 339 |
delete(loader.programs, p) |
| 340 | 340 |
} |
| 341 |
+ for p := range assignedVars {
|
|
| 342 |
+ delete(loader.vars, p) |
|
| 343 |
+ } |
|
| 341 | 344 |
|
| 342 | 345 |
return nil |
| 343 | 346 |
} |
| 344 | 347 |
|
| 345 |
-// Collection is a collection of Programs and Maps associated |
|
| 346 |
-// with their symbols |
|
| 348 |
+// Collection is a collection of live BPF resources present in the kernel. |
|
| 347 | 349 |
type Collection struct {
|
| 348 | 350 |
Programs map[string]*Program |
| 349 | 351 |
Maps map[string]*Map |
| 352 |
+ |
|
| 353 |
+ // Variables contains global variables used by the Collection's program(s). On |
|
| 354 |
+ // kernels older than 5.5, most interactions with Variables return |
|
| 355 |
+ // [ErrNotSupported]. |
|
| 356 |
+ Variables map[string]*Variable |
|
| 350 | 357 |
} |
| 351 | 358 |
|
| 352 | 359 |
// NewCollection creates a Collection from the given spec, creating and |
| ... | ... |
@@ -387,19 +374,26 @@ func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Co |
| 387 | 387 |
} |
| 388 | 388 |
} |
| 389 | 389 |
|
| 390 |
+ for varName := range spec.Variables {
|
|
| 391 |
+ if _, err := loader.loadVariable(varName); err != nil {
|
|
| 392 |
+ return nil, err |
|
| 393 |
+ } |
|
| 394 |
+ } |
|
| 395 |
+ |
|
| 390 | 396 |
// Maps can contain Program and Map stubs, so populate them after |
| 391 | 397 |
// all Maps and Programs have been successfully loaded. |
| 392 | 398 |
if err := loader.populateDeferredMaps(); err != nil {
|
| 393 | 399 |
return nil, err |
| 394 | 400 |
} |
| 395 | 401 |
|
| 396 |
- // Prevent loader.cleanup from closing maps and programs. |
|
| 397 |
- maps, progs := loader.maps, loader.programs |
|
| 398 |
- loader.maps, loader.programs = nil, nil |
|
| 402 |
+ // Prevent loader.cleanup from closing maps, programs and vars. |
|
| 403 |
+ maps, progs, vars := loader.maps, loader.programs, loader.vars |
|
| 404 |
+ loader.maps, loader.programs, loader.vars = nil, nil, nil |
|
| 399 | 405 |
|
| 400 | 406 |
return &Collection{
|
| 401 | 407 |
progs, |
| 402 | 408 |
maps, |
| 409 |
+ vars, |
|
| 403 | 410 |
}, nil |
| 404 | 411 |
} |
| 405 | 412 |
|
| ... | ... |
@@ -408,6 +402,7 @@ type collectionLoader struct {
|
| 408 | 408 |
opts *CollectionOptions |
| 409 | 409 |
maps map[string]*Map |
| 410 | 410 |
programs map[string]*Program |
| 411 |
+ vars map[string]*Variable |
|
| 411 | 412 |
} |
| 412 | 413 |
|
| 413 | 414 |
func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collectionLoader, error) {
|
| ... | ... |
@@ -416,15 +411,14 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec |
| 416 | 416 |
} |
| 417 | 417 |
|
| 418 | 418 |
// Check for existing MapSpecs in the CollectionSpec for all provided replacement maps. |
| 419 |
- for name, m := range opts.MapReplacements {
|
|
| 420 |
- spec, ok := coll.Maps[name] |
|
| 421 |
- if !ok {
|
|
| 419 |
+ for name := range opts.MapReplacements {
|
|
| 420 |
+ if _, ok := coll.Maps[name]; !ok {
|
|
| 422 | 421 |
return nil, fmt.Errorf("replacement map %s not found in CollectionSpec", name)
|
| 423 | 422 |
} |
| 423 |
+ } |
|
| 424 | 424 |
|
| 425 |
- if err := spec.Compatible(m); err != nil {
|
|
| 426 |
- return nil, fmt.Errorf("using replacement map %s: %w", spec.Name, err)
|
|
| 427 |
- } |
|
| 425 |
+ if err := populateKallsyms(coll.Programs); err != nil {
|
|
| 426 |
+ return nil, fmt.Errorf("populating kallsyms caches: %w", err)
|
|
| 428 | 427 |
} |
| 429 | 428 |
|
| 430 | 429 |
return &collectionLoader{
|
| ... | ... |
@@ -432,9 +426,49 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec |
| 432 | 432 |
opts, |
| 433 | 433 |
make(map[string]*Map), |
| 434 | 434 |
make(map[string]*Program), |
| 435 |
+ make(map[string]*Variable), |
|
| 435 | 436 |
}, nil |
| 436 | 437 |
} |
| 437 | 438 |
|
| 439 |
+// populateKallsyms populates kallsyms caches, making lookups cheaper later on |
|
| 440 |
+// during individual program loading. Since we have less context available |
|
| 441 |
+// at those stages, we batch the lookups here instead to avoid redundant work. |
|
| 442 |
+func populateKallsyms(progs map[string]*ProgramSpec) error {
|
|
| 443 |
+ // Look up associated kernel modules for all symbols referenced by |
|
| 444 |
+ // ProgramSpec.AttachTo for program types that support attaching to kmods. |
|
| 445 |
+ mods := make(map[string]string) |
|
| 446 |
+ for _, p := range progs {
|
|
| 447 |
+ if p.AttachTo != "" && p.targetsKernelModule() {
|
|
| 448 |
+ mods[p.AttachTo] = "" |
|
| 449 |
+ } |
|
| 450 |
+ } |
|
| 451 |
+ if len(mods) != 0 {
|
|
| 452 |
+ if err := kallsyms.AssignModules(mods); err != nil {
|
|
| 453 |
+ return fmt.Errorf("getting modules from kallsyms: %w", err)
|
|
| 454 |
+ } |
|
| 455 |
+ } |
|
| 456 |
+ |
|
| 457 |
+ // Look up addresses of all kernel symbols referenced by all programs. |
|
| 458 |
+ addrs := make(map[string]uint64) |
|
| 459 |
+ for _, p := range progs {
|
|
| 460 |
+ iter := p.Instructions.Iterate() |
|
| 461 |
+ for iter.Next() {
|
|
| 462 |
+ ins := iter.Ins |
|
| 463 |
+ meta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta)
|
|
| 464 |
+ if meta != nil {
|
|
| 465 |
+ addrs[meta.Name] = 0 |
|
| 466 |
+ } |
|
| 467 |
+ } |
|
| 468 |
+ } |
|
| 469 |
+ if len(addrs) != 0 {
|
|
| 470 |
+ if err := kallsyms.AssignAddresses(addrs); err != nil {
|
|
| 471 |
+ return fmt.Errorf("getting addresses from kallsyms: %w", err)
|
|
| 472 |
+ } |
|
| 473 |
+ } |
|
| 474 |
+ |
|
| 475 |
+ return nil |
|
| 476 |
+} |
|
| 477 |
+ |
|
| 438 | 478 |
// close all resources left over in the collectionLoader. |
| 439 | 479 |
func (cl *collectionLoader) close() {
|
| 440 | 480 |
for _, m := range cl.maps {
|
| ... | ... |
@@ -455,7 +489,22 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
|
| 455 | 455 |
return nil, fmt.Errorf("missing map %s", mapName)
|
| 456 | 456 |
} |
| 457 | 457 |
|
| 458 |
+ mapSpec = mapSpec.Copy() |
|
| 459 |
+ |
|
| 460 |
+ // Defer setting the mmapable flag on maps until load time. This avoids the |
|
| 461 |
+ // MapSpec having different flags on some kernel versions. Also avoid running |
|
| 462 |
+ // syscalls during ELF loading, so platforms like wasm can also parse an ELF. |
|
| 463 |
+ if isDataSection(mapSpec.Name) && haveMmapableMaps() == nil {
|
|
| 464 |
+ mapSpec.Flags |= sys.BPF_F_MMAPABLE |
|
| 465 |
+ } |
|
| 466 |
+ |
|
| 458 | 467 |
if replaceMap, ok := cl.opts.MapReplacements[mapName]; ok {
|
| 468 |
+ // Check compatibility with the replacement map after setting |
|
| 469 |
+ // feature-dependent map flags. |
|
| 470 |
+ if err := mapSpec.Compatible(replaceMap); err != nil {
|
|
| 471 |
+ return nil, fmt.Errorf("using replacement map %s: %w", mapSpec.Name, err)
|
|
| 472 |
+ } |
|
| 473 |
+ |
|
| 459 | 474 |
// Clone the map to avoid closing user's map later on. |
| 460 | 475 |
m, err := replaceMap.Clone() |
| 461 | 476 |
if err != nil {
|
| ... | ... |
@@ -537,6 +586,58 @@ func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
|
| 537 | 537 |
return prog, nil |
| 538 | 538 |
} |
| 539 | 539 |
|
| 540 |
+func (cl *collectionLoader) loadVariable(varName string) (*Variable, error) {
|
|
| 541 |
+ if v := cl.vars[varName]; v != nil {
|
|
| 542 |
+ return v, nil |
|
| 543 |
+ } |
|
| 544 |
+ |
|
| 545 |
+ varSpec := cl.coll.Variables[varName] |
|
| 546 |
+ if varSpec == nil {
|
|
| 547 |
+ return nil, fmt.Errorf("unknown variable %s", varName)
|
|
| 548 |
+ } |
|
| 549 |
+ |
|
| 550 |
+ // Get the key of the VariableSpec's MapSpec in the CollectionSpec. |
|
| 551 |
+ var mapName string |
|
| 552 |
+ for n, ms := range cl.coll.Maps {
|
|
| 553 |
+ if ms == varSpec.m {
|
|
| 554 |
+ mapName = n |
|
| 555 |
+ break |
|
| 556 |
+ } |
|
| 557 |
+ } |
|
| 558 |
+ if mapName == "" {
|
|
| 559 |
+ return nil, fmt.Errorf("variable %s: underlying MapSpec %s was removed from CollectionSpec", varName, varSpec.m.Name)
|
|
| 560 |
+ } |
|
| 561 |
+ |
|
| 562 |
+ m, err := cl.loadMap(mapName) |
|
| 563 |
+ if err != nil {
|
|
| 564 |
+ return nil, fmt.Errorf("variable %s: %w", varName, err)
|
|
| 565 |
+ } |
|
| 566 |
+ |
|
| 567 |
+ // If the kernel is too old or the underlying map was created without |
|
| 568 |
+ // BPF_F_MMAPABLE, [Map.Memory] will return ErrNotSupported. In this case, |
|
| 569 |
+ // emit a Variable with a nil Memory. This keeps Collection{Spec}.Variables
|
|
| 570 |
+ // consistent across systems with different feature sets without breaking |
|
| 571 |
+ // LoadAndAssign. |
|
| 572 |
+ mm, err := m.Memory() |
|
| 573 |
+ if err != nil && !errors.Is(err, ErrNotSupported) {
|
|
| 574 |
+ return nil, fmt.Errorf("variable %s: getting memory for map %s: %w", varName, mapName, err)
|
|
| 575 |
+ } |
|
| 576 |
+ |
|
| 577 |
+ v, err := newVariable( |
|
| 578 |
+ varSpec.name, |
|
| 579 |
+ varSpec.offset, |
|
| 580 |
+ varSpec.size, |
|
| 581 |
+ varSpec.t, |
|
| 582 |
+ mm, |
|
| 583 |
+ ) |
|
| 584 |
+ if err != nil {
|
|
| 585 |
+ return nil, fmt.Errorf("variable %s: %w", varName, err)
|
|
| 586 |
+ } |
|
| 587 |
+ |
|
| 588 |
+ cl.vars[varName] = v |
|
| 589 |
+ return v, nil |
|
| 590 |
+} |
|
| 591 |
+ |
|
| 540 | 592 |
// populateDeferredMaps iterates maps holding programs or other maps and loads |
| 541 | 593 |
// any dependencies. Populates all maps in cl and freezes them if specified. |
| 542 | 594 |
func (cl *collectionLoader) populateDeferredMaps() error {
|
| ... | ... |
@@ -603,6 +704,7 @@ func resolveKconfig(m *MapSpec) error {
|
| 603 | 603 |
|
| 604 | 604 |
type configInfo struct {
|
| 605 | 605 |
offset uint32 |
| 606 |
+ size uint32 |
|
| 606 | 607 |
typ btf.Type |
| 607 | 608 |
} |
| 608 | 609 |
|
| ... | ... |
@@ -619,7 +721,7 @@ func resolveKconfig(m *MapSpec) error {
|
| 619 | 619 |
return fmt.Errorf("variable %s must be a 32 bits integer, got %s", n, v.Type)
|
| 620 | 620 |
} |
| 621 | 621 |
|
| 622 |
- kv, err := internal.KernelVersion() |
|
| 622 |
+ kv, err := linux.KernelVersion() |
|
| 623 | 623 |
if err != nil {
|
| 624 | 624 |
return fmt.Errorf("getting kernel version: %w", err)
|
| 625 | 625 |
} |
| ... | ... |
@@ -644,6 +746,7 @@ func resolveKconfig(m *MapSpec) error {
|
| 644 | 644 |
default: // Catch CONFIG_*. |
| 645 | 645 |
configs[n] = configInfo{
|
| 646 | 646 |
offset: vsi.Offset, |
| 647 |
+ size: vsi.Size, |
|
| 647 | 648 |
typ: v.Type, |
| 648 | 649 |
} |
| 649 | 650 |
} |
| ... | ... |
@@ -651,7 +754,7 @@ func resolveKconfig(m *MapSpec) error {
|
| 651 | 651 |
|
| 652 | 652 |
// We only parse kconfig file if a CONFIG_* variable was found. |
| 653 | 653 |
if len(configs) > 0 {
|
| 654 |
- f, err := kconfig.Find() |
|
| 654 |
+ f, err := linux.FindKConfig() |
|
| 655 | 655 |
if err != nil {
|
| 656 | 656 |
return fmt.Errorf("cannot find a kconfig file: %w", err)
|
| 657 | 657 |
} |
| ... | ... |
@@ -670,10 +773,10 @@ func resolveKconfig(m *MapSpec) error {
|
| 670 | 670 |
for n, info := range configs {
|
| 671 | 671 |
value, ok := kernelConfig[n] |
| 672 | 672 |
if !ok {
|
| 673 |
- return fmt.Errorf("config option %q does not exists for this kernel", n)
|
|
| 673 |
+ return fmt.Errorf("config option %q does not exist on this kernel", n)
|
|
| 674 | 674 |
} |
| 675 | 675 |
|
| 676 |
- err := kconfig.PutValue(data[info.offset:], info.typ, value) |
|
| 676 |
+ err := kconfig.PutValue(data[info.offset:info.offset+info.size], info.typ, value) |
|
| 677 | 677 |
if err != nil {
|
| 678 | 678 |
return fmt.Errorf("problem adding value for %s: %w", n, err)
|
| 679 | 679 |
} |
| ... | ... |
@@ -723,6 +826,7 @@ func LoadCollection(file string) (*Collection, error) {
|
| 723 | 723 |
func (coll *Collection) Assign(to interface{}) error {
|
| 724 | 724 |
assignedMaps := make(map[string]bool) |
| 725 | 725 |
assignedProgs := make(map[string]bool) |
| 726 |
+ assignedVars := make(map[string]bool) |
|
| 726 | 727 |
|
| 727 | 728 |
// Assign() only transfers already-loaded Maps and Programs. No extra |
| 728 | 729 |
// loading is done. |
| ... | ... |
@@ -743,6 +847,13 @@ func (coll *Collection) Assign(to interface{}) error {
|
| 743 | 743 |
} |
| 744 | 744 |
return nil, fmt.Errorf("missing map %q", name)
|
| 745 | 745 |
|
| 746 |
+ case reflect.TypeOf((*Variable)(nil)): |
|
| 747 |
+ if v := coll.Variables[name]; v != nil {
|
|
| 748 |
+ assignedVars[name] = true |
|
| 749 |
+ return v, nil |
|
| 750 |
+ } |
|
| 751 |
+ return nil, fmt.Errorf("missing variable %q", name)
|
|
| 752 |
+ |
|
| 746 | 753 |
default: |
| 747 | 754 |
return nil, fmt.Errorf("unsupported type %s", typ)
|
| 748 | 755 |
} |
| ... | ... |
@@ -759,6 +870,9 @@ func (coll *Collection) Assign(to interface{}) error {
|
| 759 | 759 |
for m := range assignedMaps {
|
| 760 | 760 |
delete(coll.Maps, m) |
| 761 | 761 |
} |
| 762 |
+ for s := range assignedVars {
|
|
| 763 |
+ delete(coll.Variables, s) |
|
| 764 |
+ } |
|
| 762 | 765 |
|
| 763 | 766 |
return nil |
| 764 | 767 |
} |
| ... | ... |
@@ -16,7 +16,6 @@ import ( |
| 16 | 16 |
"github.com/cilium/ebpf/btf" |
| 17 | 17 |
"github.com/cilium/ebpf/internal" |
| 18 | 18 |
"github.com/cilium/ebpf/internal/sys" |
| 19 |
- "github.com/cilium/ebpf/internal/unix" |
|
| 20 | 19 |
) |
| 21 | 20 |
|
| 22 | 21 |
type kconfigMetaKey struct{}
|
| ... | ... |
@@ -33,6 +32,13 @@ type kfuncMeta struct {
|
| 33 | 33 |
Func *btf.Func |
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 |
+type ksymMetaKey struct{}
|
|
| 37 |
+ |
|
| 38 |
+type ksymMeta struct {
|
|
| 39 |
+ Binding elf.SymBind |
|
| 40 |
+ Name string |
|
| 41 |
+} |
|
| 42 |
+ |
|
| 36 | 43 |
// elfCode is a convenience to reduce the amount of arguments that have to |
| 37 | 44 |
// be passed around explicitly. You should treat its contents as immutable. |
| 38 | 45 |
type elfCode struct {
|
| ... | ... |
@@ -43,7 +49,9 @@ type elfCode struct {
|
| 43 | 43 |
btf *btf.Spec |
| 44 | 44 |
extInfo *btf.ExtInfos |
| 45 | 45 |
maps map[string]*MapSpec |
| 46 |
+ vars map[string]*VariableSpec |
|
| 46 | 47 |
kfuncs map[string]*btf.Func |
| 48 |
+ ksyms map[string]struct{}
|
|
| 47 | 49 |
kconfig *MapSpec |
| 48 | 50 |
} |
| 49 | 51 |
|
| ... | ... |
@@ -71,7 +79,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
|
| 71 | 71 |
|
| 72 | 72 |
// Checks if the ELF file is for BPF data. |
| 73 | 73 |
// Old LLVM versions set e_machine to EM_NONE. |
| 74 |
- if f.File.Machine != unix.EM_NONE && f.File.Machine != elf.EM_BPF {
|
|
| 74 |
+ if f.File.Machine != elf.EM_NONE && f.File.Machine != elf.EM_BPF {
|
|
| 75 | 75 |
return nil, fmt.Errorf("unexpected machine type for BPF ELF: %s", f.File.Machine)
|
| 76 | 76 |
} |
| 77 | 77 |
|
| ... | ... |
@@ -101,7 +109,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
|
| 101 | 101 |
sections[idx] = newElfSection(sec, mapSection) |
| 102 | 102 |
case sec.Name == ".maps": |
| 103 | 103 |
sections[idx] = newElfSection(sec, btfMapSection) |
| 104 |
- case sec.Name == ".bss" || sec.Name == ".data" || strings.HasPrefix(sec.Name, ".rodata"): |
|
| 104 |
+ case isDataSection(sec.Name): |
|
| 105 | 105 |
sections[idx] = newElfSection(sec, dataSection) |
| 106 | 106 |
case sec.Type == elf.SHT_REL: |
| 107 | 107 |
// Store relocations under the section index of the target |
| ... | ... |
@@ -134,7 +142,9 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
|
| 134 | 134 |
btf: btfSpec, |
| 135 | 135 |
extInfo: btfExtInfo, |
| 136 | 136 |
maps: make(map[string]*MapSpec), |
| 137 |
+ vars: make(map[string]*VariableSpec), |
|
| 137 | 138 |
kfuncs: make(map[string]*btf.Func), |
| 139 |
+ ksyms: make(map[string]struct{}),
|
|
| 138 | 140 |
} |
| 139 | 141 |
|
| 140 | 142 |
symbols, err := f.Symbols() |
| ... | ... |
@@ -174,7 +184,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
|
| 174 | 174 |
return nil, fmt.Errorf("load programs: %w", err)
|
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 |
- return &CollectionSpec{ec.maps, progs, btfSpec, ec.ByteOrder}, nil
|
|
| 177 |
+ return &CollectionSpec{ec.maps, progs, ec.vars, btfSpec, ec.ByteOrder}, nil
|
|
| 178 | 178 |
} |
| 179 | 179 |
|
| 180 | 180 |
func loadLicense(sec *elf.Section) (string, error) {
|
| ... | ... |
@@ -201,6 +211,18 @@ func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) {
|
| 201 | 201 |
return version, nil |
| 202 | 202 |
} |
| 203 | 203 |
|
| 204 |
+func isDataSection(name string) bool {
|
|
| 205 |
+ return name == ".bss" || strings.HasPrefix(name, ".data") || strings.HasPrefix(name, ".rodata") |
|
| 206 |
+} |
|
| 207 |
+ |
|
| 208 |
+func isConstantDataSection(name string) bool {
|
|
| 209 |
+ return strings.HasPrefix(name, ".rodata") |
|
| 210 |
+} |
|
| 211 |
+ |
|
| 212 |
+func isKconfigSection(name string) bool {
|
|
| 213 |
+ return name == ".kconfig" |
|
| 214 |
+} |
|
| 215 |
+ |
|
| 204 | 216 |
type elfSectionKind int |
| 205 | 217 |
|
| 206 | 218 |
const ( |
| ... | ... |
@@ -506,7 +528,7 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err |
| 506 | 506 |
|
| 507 | 507 |
case elf.STT_OBJECT: |
| 508 | 508 |
// LLVM 9 emits OBJECT-LOCAL symbols for anonymous constants. |
| 509 |
- if bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL {
|
|
| 509 |
+ if bind != elf.STB_GLOBAL && bind != elf.STB_LOCAL && bind != elf.STB_WEAK {
|
|
| 510 | 510 |
return fmt.Errorf("direct load: %s: %w: %s", name, errUnsupportedBinding, bind)
|
| 511 | 511 |
} |
| 512 | 512 |
|
| ... | ... |
@@ -614,6 +636,8 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err |
| 614 | 614 |
} |
| 615 | 615 |
|
| 616 | 616 |
kf := ec.kfuncs[name] |
| 617 |
+ _, ks := ec.ksyms[name] |
|
| 618 |
+ |
|
| 617 | 619 |
switch {
|
| 618 | 620 |
// If a Call / DWordLoad instruction is found and the datasec has a btf.Func with a Name |
| 619 | 621 |
// that matches the symbol name we mark the instruction as a referencing a kfunc. |
| ... | ... |
@@ -634,6 +658,15 @@ func (ec *elfCode) relocateInstruction(ins *asm.Instruction, rel elf.Symbol) err |
| 634 | 634 |
|
| 635 | 635 |
ins.Constant = 0 |
| 636 | 636 |
|
| 637 |
+ case ks && ins.OpCode.IsDWordLoad(): |
|
| 638 |
+ if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK {
|
|
| 639 |
+ return fmt.Errorf("asm relocation: %s: %w: %s", name, errUnsupportedBinding, bind)
|
|
| 640 |
+ } |
|
| 641 |
+ ins.Metadata.Set(ksymMetaKey{}, &ksymMeta{
|
|
| 642 |
+ Binding: bind, |
|
| 643 |
+ Name: name, |
|
| 644 |
+ }) |
|
| 645 |
+ |
|
| 637 | 646 |
// If no kconfig map is found, this must be a symbol reference from inline |
| 638 | 647 |
// asm (see testdata/loader.c:asm_relocation()) or a call to a forward |
| 639 | 648 |
// function declaration (see testdata/fwd_decl.c). Don't interfere, These |
| ... | ... |
@@ -980,6 +1013,13 @@ func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *b |
| 980 | 980 |
} |
| 981 | 981 |
} |
| 982 | 982 |
|
| 983 |
+ // Some maps don't support value sizes, but annotating their map definitions |
|
| 984 |
+ // with __type macros can still be useful, especially to let bpf2go generate |
|
| 985 |
+ // type definitions for them. |
|
| 986 |
+ if value != nil && !mapType.canHaveValueSize() {
|
|
| 987 |
+ valueSize = 0 |
|
| 988 |
+ } |
|
| 989 |
+ |
|
| 983 | 990 |
return &MapSpec{
|
| 984 | 991 |
Name: SanitizeName(name, -1), |
| 985 | 992 |
Type: MapType(mapType), |
| ... | ... |
@@ -1092,12 +1132,21 @@ func (ec *elfCode) loadDataSections() error {
|
| 1092 | 1092 |
continue |
| 1093 | 1093 |
} |
| 1094 | 1094 |
|
| 1095 |
- if sec.references == 0 {
|
|
| 1096 |
- // Prune data sections which are not referenced by any |
|
| 1097 |
- // instructions. |
|
| 1095 |
+ // If a section has no references, it will be freed as soon as the |
|
| 1096 |
+ // Collection closes, so creating and populating it is wasteful. If it has |
|
| 1097 |
+ // no symbols, it is likely an ephemeral section used during compilation |
|
| 1098 |
+ // that wasn't sanitized by the bpf linker. (like .rodata.str1.1) |
|
| 1099 |
+ // |
|
| 1100 |
+ // No symbols means no VariableSpecs can be generated from it, making it |
|
| 1101 |
+ // pointless to emit a data section for. |
|
| 1102 |
+ if sec.references == 0 && len(sec.symbols) == 0 {
|
|
| 1098 | 1103 |
continue |
| 1099 | 1104 |
} |
| 1100 | 1105 |
|
| 1106 |
+ if sec.Size > math.MaxUint32 {
|
|
| 1107 |
+ return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name)
|
|
| 1108 |
+ } |
|
| 1109 |
+ |
|
| 1101 | 1110 |
mapSpec := &MapSpec{
|
| 1102 | 1111 |
Name: SanitizeName(sec.Name, -1), |
| 1103 | 1112 |
Type: Array, |
| ... | ... |
@@ -1106,6 +1155,10 @@ func (ec *elfCode) loadDataSections() error {
|
| 1106 | 1106 |
MaxEntries: 1, |
| 1107 | 1107 |
} |
| 1108 | 1108 |
|
| 1109 |
+ if isConstantDataSection(sec.Name) {
|
|
| 1110 |
+ mapSpec.Flags = sys.BPF_F_RDONLY_PROG |
|
| 1111 |
+ } |
|
| 1112 |
+ |
|
| 1109 | 1113 |
switch sec.Type {
|
| 1110 | 1114 |
// Only open the section if we know there's actual data to be read. |
| 1111 | 1115 |
case elf.SHT_PROGBITS: |
| ... | ... |
@@ -1113,20 +1166,56 @@ func (ec *elfCode) loadDataSections() error {
|
| 1113 | 1113 |
if err != nil {
|
| 1114 | 1114 |
return fmt.Errorf("data section %s: can't get contents: %w", sec.Name, err)
|
| 1115 | 1115 |
} |
| 1116 |
- |
|
| 1117 |
- if uint64(len(data)) > math.MaxUint32 {
|
|
| 1118 |
- return fmt.Errorf("data section %s: contents exceed maximum size", sec.Name)
|
|
| 1119 |
- } |
|
| 1120 | 1116 |
mapSpec.Contents = []MapKV{{uint32(0), data}}
|
| 1121 | 1117 |
|
| 1122 | 1118 |
case elf.SHT_NOBITS: |
| 1123 |
- // NOBITS sections like .bss contain only zeroes, and since data sections |
|
| 1124 |
- // are Arrays, the kernel already preallocates them. Skip reading zeroes |
|
| 1125 |
- // from the ELF. |
|
| 1119 |
+ // NOBITS sections like .bss contain only zeroes and are not allocated in |
|
| 1120 |
+ // the ELF. Since data sections are Arrays, the kernel can preallocate |
|
| 1121 |
+ // them. Don't attempt reading zeroes from the ELF, instead allocate the |
|
| 1122 |
+ // zeroed memory to support getting and setting VariableSpecs for sections |
|
| 1123 |
+ // like .bss. |
|
| 1124 |
+ mapSpec.Contents = []MapKV{{uint32(0), make([]byte, sec.Size)}}
|
|
| 1125 |
+ |
|
| 1126 | 1126 |
default: |
| 1127 | 1127 |
return fmt.Errorf("data section %s: unknown section type %s", sec.Name, sec.Type)
|
| 1128 | 1128 |
} |
| 1129 | 1129 |
|
| 1130 |
+ for off, sym := range sec.symbols {
|
|
| 1131 |
+ // Skip symbols marked with the 'hidden' attribute. |
|
| 1132 |
+ if elf.ST_VISIBILITY(sym.Other) == elf.STV_HIDDEN || |
|
| 1133 |
+ elf.ST_VISIBILITY(sym.Other) == elf.STV_INTERNAL {
|
|
| 1134 |
+ continue |
|
| 1135 |
+ } |
|
| 1136 |
+ |
|
| 1137 |
+ // Only accept symbols with global or weak bindings. The common |
|
| 1138 |
+ // alternative is STB_LOCAL, which are either function-scoped or declared |
|
| 1139 |
+ // 'static'. |
|
| 1140 |
+ if elf.ST_BIND(sym.Info) != elf.STB_GLOBAL && |
|
| 1141 |
+ elf.ST_BIND(sym.Info) != elf.STB_WEAK {
|
|
| 1142 |
+ continue |
|
| 1143 |
+ } |
|
| 1144 |
+ |
|
| 1145 |
+ if ec.vars[sym.Name] != nil {
|
|
| 1146 |
+ return fmt.Errorf("data section %s: duplicate variable %s", sec.Name, sym.Name)
|
|
| 1147 |
+ } |
|
| 1148 |
+ |
|
| 1149 |
+ // Skip symbols starting with a dot, they are compiler-internal symbols |
|
| 1150 |
+ // emitted by clang 11 and earlier and are not cleaned up by the bpf |
|
| 1151 |
+ // compiler backend (e.g. symbols named .Lconstinit.1 in sections like |
|
| 1152 |
+ // .rodata.cst32). Variables in C cannot start with a dot, so filter these |
|
| 1153 |
+ // out. |
|
| 1154 |
+ if strings.HasPrefix(sym.Name, ".") {
|
|
| 1155 |
+ continue |
|
| 1156 |
+ } |
|
| 1157 |
+ |
|
| 1158 |
+ ec.vars[sym.Name] = &VariableSpec{
|
|
| 1159 |
+ name: sym.Name, |
|
| 1160 |
+ offset: off, |
|
| 1161 |
+ size: sym.Size, |
|
| 1162 |
+ m: mapSpec, |
|
| 1163 |
+ } |
|
| 1164 |
+ } |
|
| 1165 |
+ |
|
| 1130 | 1166 |
// It is possible for a data section to exist without a corresponding BTF Datasec |
| 1131 | 1167 |
// if it only contains anonymous values like macro-defined arrays. |
| 1132 | 1168 |
if ec.btf != nil {
|
| ... | ... |
@@ -1135,12 +1224,38 @@ func (ec *elfCode) loadDataSections() error {
|
| 1135 | 1135 |
// Assign the spec's key and BTF only if the Datasec lookup was successful. |
| 1136 | 1136 |
mapSpec.Key = &btf.Void{}
|
| 1137 | 1137 |
mapSpec.Value = ds |
| 1138 |
- } |
|
| 1139 |
- } |
|
| 1140 | 1138 |
|
| 1141 |
- if strings.HasPrefix(sec.Name, ".rodata") {
|
|
| 1142 |
- mapSpec.Flags = unix.BPF_F_RDONLY_PROG |
|
| 1143 |
- mapSpec.Freeze = true |
|
| 1139 |
+ // Populate VariableSpecs with type information, if available. |
|
| 1140 |
+ for _, v := range ds.Vars {
|
|
| 1141 |
+ name := v.Type.TypeName() |
|
| 1142 |
+ if name == "" {
|
|
| 1143 |
+ return fmt.Errorf("data section %s: anonymous variable %v", sec.Name, v)
|
|
| 1144 |
+ } |
|
| 1145 |
+ |
|
| 1146 |
+ vt, ok := v.Type.(*btf.Var) |
|
| 1147 |
+ if !ok {
|
|
| 1148 |
+ return fmt.Errorf("data section %s: unexpected type %T for variable %s", sec.Name, v.Type, name)
|
|
| 1149 |
+ } |
|
| 1150 |
+ |
|
| 1151 |
+ ev := ec.vars[name] |
|
| 1152 |
+ if ev == nil {
|
|
| 1153 |
+ // Hidden symbols appear in the BTF Datasec but don't receive a VariableSpec. |
|
| 1154 |
+ continue |
|
| 1155 |
+ } |
|
| 1156 |
+ |
|
| 1157 |
+ if uint64(v.Offset) != ev.offset {
|
|
| 1158 |
+ return fmt.Errorf("data section %s: variable %s datasec offset (%d) doesn't match ELF symbol offset (%d)", sec.Name, name, v.Offset, ev.offset)
|
|
| 1159 |
+ } |
|
| 1160 |
+ |
|
| 1161 |
+ if uint64(v.Size) != ev.size {
|
|
| 1162 |
+ return fmt.Errorf("data section %s: variable %s size in datasec (%d) doesn't match ELF symbol size (%d)", sec.Name, name, v.Size, ev.size)
|
|
| 1163 |
+ } |
|
| 1164 |
+ |
|
| 1165 |
+ // Decouple the Var in the VariableSpec from the underlying DataSec in |
|
| 1166 |
+ // the MapSpec to avoid modifications from affecting map loads later on. |
|
| 1167 |
+ ev.t = btf.Copy(vt).(*btf.Var) |
|
| 1168 |
+ } |
|
| 1169 |
+ } |
|
| 1144 | 1170 |
} |
| 1145 | 1171 |
|
| 1146 | 1172 |
ec.maps[sec.Name] = mapSpec |
| ... | ... |
@@ -1175,8 +1290,7 @@ func (ec *elfCode) loadKconfigSection() error {
|
| 1175 | 1175 |
KeySize: uint32(4), |
| 1176 | 1176 |
ValueSize: ds.Size, |
| 1177 | 1177 |
MaxEntries: 1, |
| 1178 |
- Flags: unix.BPF_F_RDONLY_PROG, |
|
| 1179 |
- Freeze: true, |
|
| 1178 |
+ Flags: sys.BPF_F_RDONLY_PROG, |
|
| 1180 | 1179 |
Key: &btf.Int{Size: 4},
|
| 1181 | 1180 |
Value: ds, |
| 1182 | 1181 |
} |
| ... | ... |
@@ -1201,8 +1315,14 @@ func (ec *elfCode) loadKsymsSection() error {
|
| 1201 | 1201 |
} |
| 1202 | 1202 |
|
| 1203 | 1203 |
for _, v := range ds.Vars {
|
| 1204 |
- // we have already checked the .ksyms Datasec to only contain Func Vars. |
|
| 1205 |
- ec.kfuncs[v.Type.TypeName()] = v.Type.(*btf.Func) |
|
| 1204 |
+ switch t := v.Type.(type) {
|
|
| 1205 |
+ case *btf.Func: |
|
| 1206 |
+ ec.kfuncs[t.TypeName()] = t |
|
| 1207 |
+ case *btf.Var: |
|
| 1208 |
+ ec.ksyms[t.TypeName()] = struct{}{}
|
|
| 1209 |
+ default: |
|
| 1210 |
+ return fmt.Errorf("unexpected variable type in .ksyms: %T", v)
|
|
| 1211 |
+ } |
|
| 1206 | 1212 |
} |
| 1207 | 1213 |
|
| 1208 | 1214 |
return nil |
| ... | ... |
@@ -1266,10 +1386,10 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
|
| 1266 | 1266 |
|
| 1267 | 1267 |
var flags uint32 |
| 1268 | 1268 |
if t.flags&_SEC_SLEEPABLE > 0 {
|
| 1269 |
- flags |= unix.BPF_F_SLEEPABLE |
|
| 1269 |
+ flags |= sys.BPF_F_SLEEPABLE |
|
| 1270 | 1270 |
} |
| 1271 | 1271 |
if t.flags&_SEC_XDP_FRAGS > 0 {
|
| 1272 |
- flags |= unix.BPF_F_XDP_HAS_FRAGS |
|
| 1272 |
+ flags |= sys.BPF_F_XDP_HAS_FRAGS |
|
| 1273 | 1273 |
} |
| 1274 | 1274 |
if t.flags&_SEC_EXP_ATTACH_OPT > 0 {
|
| 1275 | 1275 |
if programType == XDP {
|
| ... | ... |
@@ -18,6 +18,7 @@ var elfSectionDefs = []libbpfElfSectionDef{
|
| 18 | 18 |
{"uretprobe.s+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_SLEEPABLE},
|
| 19 | 19 |
{"kprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE},
|
| 20 | 20 |
{"kretprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE},
|
| 21 |
+ {"kprobe.session+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_SESSION, _SEC_NONE},
|
|
| 21 | 22 |
{"uprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE},
|
| 22 | 23 |
{"uretprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE},
|
| 23 | 24 |
{"uprobe.multi.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE},
|
| ... | ... |
@@ -69,6 +70,7 @@ var elfSectionDefs = []libbpfElfSectionDef{
|
| 69 | 69 |
{"sockops", sys.BPF_PROG_TYPE_SOCK_OPS, sys.BPF_CGROUP_SOCK_OPS, _SEC_ATTACHABLE_OPT},
|
| 70 | 70 |
{"sk_skb/stream_parser", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_PARSER, _SEC_ATTACHABLE_OPT},
|
| 71 | 71 |
{"sk_skb/stream_verdict", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_VERDICT, _SEC_ATTACHABLE_OPT},
|
| 72 |
+ {"sk_skb/verdict", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_VERDICT, _SEC_ATTACHABLE_OPT},
|
|
| 72 | 73 |
{"sk_skb", sys.BPF_PROG_TYPE_SK_SKB, 0, _SEC_NONE},
|
| 73 | 74 |
{"sk_msg", sys.BPF_PROG_TYPE_SK_MSG, sys.BPF_SK_MSG_VERDICT, _SEC_ATTACHABLE_OPT},
|
| 74 | 75 |
{"lirc_mode2", sys.BPF_PROG_TYPE_LIRC_MODE2, sys.BPF_LIRC_MODE2, _SEC_ATTACHABLE_OPT},
|
| ... | ... |
@@ -8,10 +8,10 @@ import ( |
| 8 | 8 |
"fmt" |
| 9 | 9 |
"io" |
| 10 | 10 |
"os" |
| 11 |
+ "reflect" |
|
| 11 | 12 |
"strings" |
| 12 | 13 |
"syscall" |
| 13 | 14 |
"time" |
| 14 |
- "unsafe" |
|
| 15 | 15 |
|
| 16 | 16 |
"github.com/cilium/ebpf/asm" |
| 17 | 17 |
"github.com/cilium/ebpf/btf" |
| ... | ... |
@@ -39,53 +39,83 @@ import ( |
| 39 | 39 |
|
| 40 | 40 |
// MapInfo describes a map. |
| 41 | 41 |
type MapInfo struct {
|
| 42 |
- Type MapType |
|
| 43 |
- id MapID |
|
| 44 |
- KeySize uint32 |
|
| 45 |
- ValueSize uint32 |
|
| 42 |
+ // Type of the map. |
|
| 43 |
+ Type MapType |
|
| 44 |
+ // KeySize is the size of the map key in bytes. |
|
| 45 |
+ KeySize uint32 |
|
| 46 |
+ // ValueSize is the size of the map value in bytes. |
|
| 47 |
+ ValueSize uint32 |
|
| 48 |
+ // MaxEntries is the maximum number of entries the map can hold. Its meaning |
|
| 49 |
+ // is map-specific. |
|
| 46 | 50 |
MaxEntries uint32 |
| 47 |
- Flags uint32 |
|
| 51 |
+ // Flags used during map creation. |
|
| 52 |
+ Flags uint32 |
|
| 48 | 53 |
// Name as supplied by user space at load time. Available from 4.15. |
| 49 | 54 |
Name string |
| 50 | 55 |
|
| 51 |
- btf btf.ID |
|
| 56 |
+ id MapID |
|
| 57 |
+ btf btf.ID |
|
| 58 |
+ mapExtra uint64 |
|
| 59 |
+ memlock uint64 |
|
| 60 |
+ frozen bool |
|
| 52 | 61 |
} |
| 53 | 62 |
|
| 63 |
+// newMapInfoFromFd queries map information about the given fd. [sys.ObjInfo] is |
|
| 64 |
+// attempted first, supplementing any missing values with information from |
|
| 65 |
+// /proc/self/fdinfo. Ignores EINVAL from ObjInfo as well as ErrNotSupported |
|
| 66 |
+// from reading fdinfo (indicating the file exists, but no fields of interest |
|
| 67 |
+// were found). If both fail, an error is always returned. |
|
| 54 | 68 |
func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) {
|
| 55 | 69 |
var info sys.MapInfo |
| 56 |
- err := sys.ObjInfo(fd, &info) |
|
| 57 |
- if errors.Is(err, syscall.EINVAL) {
|
|
| 58 |
- return newMapInfoFromProc(fd) |
|
| 59 |
- } |
|
| 60 |
- if err != nil {
|
|
| 61 |
- return nil, err |
|
| 70 |
+ err1 := sys.ObjInfo(fd, &info) |
|
| 71 |
+ // EINVAL means the kernel doesn't support BPF_OBJ_GET_INFO_BY_FD. Continue |
|
| 72 |
+ // with fdinfo if that's the case. |
|
| 73 |
+ if err1 != nil && !errors.Is(err1, unix.EINVAL) {
|
|
| 74 |
+ return nil, fmt.Errorf("getting object info: %w", err1)
|
|
| 62 | 75 |
} |
| 63 | 76 |
|
| 64 |
- return &MapInfo{
|
|
| 77 |
+ mi := &MapInfo{
|
|
| 65 | 78 |
MapType(info.Type), |
| 66 |
- MapID(info.Id), |
|
| 67 | 79 |
info.KeySize, |
| 68 | 80 |
info.ValueSize, |
| 69 | 81 |
info.MaxEntries, |
| 70 | 82 |
uint32(info.MapFlags), |
| 71 | 83 |
unix.ByteSliceToString(info.Name[:]), |
| 84 |
+ MapID(info.Id), |
|
| 72 | 85 |
btf.ID(info.BtfId), |
| 73 |
- }, nil |
|
| 86 |
+ info.MapExtra, |
|
| 87 |
+ 0, |
|
| 88 |
+ false, |
|
| 89 |
+ } |
|
| 90 |
+ |
|
| 91 |
+ // Supplement OBJ_INFO with data from /proc/self/fdinfo. It contains fields |
|
| 92 |
+ // like memlock and frozen that are not present in OBJ_INFO. |
|
| 93 |
+ err2 := readMapInfoFromProc(fd, mi) |
|
| 94 |
+ if err2 != nil && !errors.Is(err2, ErrNotSupported) {
|
|
| 95 |
+ return nil, fmt.Errorf("getting map info from fdinfo: %w", err2)
|
|
| 96 |
+ } |
|
| 97 |
+ |
|
| 98 |
+ if err1 != nil && err2 != nil {
|
|
| 99 |
+ return nil, fmt.Errorf("ObjInfo and fdinfo both failed: objinfo: %w, fdinfo: %w", err1, err2)
|
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 102 |
+ return mi, nil |
|
| 74 | 103 |
} |
| 75 | 104 |
|
| 76 |
-func newMapInfoFromProc(fd *sys.FD) (*MapInfo, error) {
|
|
| 77 |
- var mi MapInfo |
|
| 78 |
- err := scanFdInfo(fd, map[string]interface{}{
|
|
| 105 |
+// readMapInfoFromProc queries map information about the given fd from |
|
| 106 |
+// /proc/self/fdinfo. It only writes data into fields that have a zero value. |
|
| 107 |
+func readMapInfoFromProc(fd *sys.FD, mi *MapInfo) error {
|
|
| 108 |
+ return scanFdInfo(fd, map[string]interface{}{
|
|
| 79 | 109 |
"map_type": &mi.Type, |
| 110 |
+ "map_id": &mi.id, |
|
| 80 | 111 |
"key_size": &mi.KeySize, |
| 81 | 112 |
"value_size": &mi.ValueSize, |
| 82 | 113 |
"max_entries": &mi.MaxEntries, |
| 83 | 114 |
"map_flags": &mi.Flags, |
| 115 |
+ "map_extra": &mi.mapExtra, |
|
| 116 |
+ "memlock": &mi.memlock, |
|
| 117 |
+ "frozen": &mi.frozen, |
|
| 84 | 118 |
}) |
| 85 |
- if err != nil {
|
|
| 86 |
- return nil, err |
|
| 87 |
- } |
|
| 88 |
- return &mi, nil |
|
| 89 | 119 |
} |
| 90 | 120 |
|
| 91 | 121 |
// ID returns the map ID. |
| ... | ... |
@@ -109,6 +139,35 @@ func (mi *MapInfo) BTFID() (btf.ID, bool) {
|
| 109 | 109 |
return mi.btf, mi.btf > 0 |
| 110 | 110 |
} |
| 111 | 111 |
|
| 112 |
+// MapExtra returns an opaque field whose meaning is map-specific. |
|
| 113 |
+// |
|
| 114 |
+// Available from 5.16. |
|
| 115 |
+// |
|
| 116 |
+// The bool return value indicates whether this optional field is available and |
|
| 117 |
+// populated, if it was specified during Map creation. |
|
| 118 |
+func (mi *MapInfo) MapExtra() (uint64, bool) {
|
|
| 119 |
+ return mi.mapExtra, mi.mapExtra > 0 |
|
| 120 |
+} |
|
| 121 |
+ |
|
| 122 |
+// Memlock returns an approximate number of bytes allocated to this map. |
|
| 123 |
+// |
|
| 124 |
+// Available from 4.10. |
|
| 125 |
+// |
|
| 126 |
+// The bool return value indicates whether this optional field is available. |
|
| 127 |
+func (mi *MapInfo) Memlock() (uint64, bool) {
|
|
| 128 |
+ return mi.memlock, mi.memlock > 0 |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+// Frozen indicates whether [Map.Freeze] was called on this map. If true, |
|
| 132 |
+// modifications from user space are not allowed. |
|
| 133 |
+// |
|
| 134 |
+// Available from 5.2. Requires access to procfs. |
|
| 135 |
+// |
|
| 136 |
+// If the kernel doesn't support map freezing, this field will always be false. |
|
| 137 |
+func (mi *MapInfo) Frozen() bool {
|
|
| 138 |
+ return mi.frozen |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 112 | 141 |
// programStats holds statistics of a program. |
| 113 | 142 |
type programStats struct {
|
| 114 | 143 |
// Total accumulated runtime of the program ins ns. |
| ... | ... |
@@ -120,6 +179,40 @@ type programStats struct {
|
| 120 | 120 |
recursionMisses uint64 |
| 121 | 121 |
} |
| 122 | 122 |
|
| 123 |
+// programJitedInfo holds information about JITed info of a program. |
|
| 124 |
+type programJitedInfo struct {
|
|
| 125 |
+ // ksyms holds the ksym addresses of the BPF program, including those of its |
|
| 126 |
+ // subprograms. |
|
| 127 |
+ // |
|
| 128 |
+ // Available from 4.18. |
|
| 129 |
+ ksyms []uint64 |
|
| 130 |
+ numKsyms uint32 |
|
| 131 |
+ |
|
| 132 |
+ // insns holds the JITed machine native instructions of the program, |
|
| 133 |
+ // including those of its subprograms. |
|
| 134 |
+ // |
|
| 135 |
+ // Available from 4.13. |
|
| 136 |
+ insns []byte |
|
| 137 |
+ numInsns uint32 |
|
| 138 |
+ |
|
| 139 |
+ // lineInfos holds the JITed line infos, which are kernel addresses. |
|
| 140 |
+ // |
|
| 141 |
+ // Available from 5.0. |
|
| 142 |
+ lineInfos []uint64 |
|
| 143 |
+ numLineInfos uint32 |
|
| 144 |
+ |
|
| 145 |
+ // lineInfoRecSize is the size of a single line info record. |
|
| 146 |
+ // |
|
| 147 |
+ // Available from 5.0. |
|
| 148 |
+ lineInfoRecSize uint32 |
|
| 149 |
+ |
|
| 150 |
+ // funcLens holds the insns length of each function. |
|
| 151 |
+ // |
|
| 152 |
+ // Available from 4.18. |
|
| 153 |
+ funcLens []uint32 |
|
| 154 |
+ numFuncLens uint32 |
|
| 155 |
+} |
|
| 156 |
+ |
|
| 123 | 157 |
// ProgramInfo describes a program. |
| 124 | 158 |
type ProgramInfo struct {
|
| 125 | 159 |
Type ProgramType |
| ... | ... |
@@ -133,9 +226,14 @@ type ProgramInfo struct {
|
| 133 | 133 |
haveCreatedByUID bool |
| 134 | 134 |
btf btf.ID |
| 135 | 135 |
stats *programStats |
| 136 |
+ loadTime time.Duration |
|
| 137 |
+ |
|
| 138 |
+ maps []MapID |
|
| 139 |
+ insns []byte |
|
| 140 |
+ jitedSize uint32 |
|
| 141 |
+ verifiedInstructions uint32 |
|
| 136 | 142 |
|
| 137 |
- maps []MapID |
|
| 138 |
- insns []byte |
|
| 143 |
+ jitedInfo programJitedInfo |
|
| 139 | 144 |
|
| 140 | 145 |
lineInfos []byte |
| 141 | 146 |
numLineInfos uint32 |
| ... | ... |
@@ -164,6 +262,9 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
|
| 164 | 164 |
runCount: info.RunCnt, |
| 165 | 165 |
recursionMisses: info.RecursionMisses, |
| 166 | 166 |
}, |
| 167 |
+ jitedSize: info.JitedProgLen, |
|
| 168 |
+ loadTime: time.Duration(info.LoadTime), |
|
| 169 |
+ verifiedInstructions: info.VerifiedInsns, |
|
| 167 | 170 |
} |
| 168 | 171 |
|
| 169 | 172 |
// Start with a clean struct for the second call, otherwise we may get EFAULT. |
| ... | ... |
@@ -174,7 +275,7 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
|
| 174 | 174 |
if info.NrMapIds > 0 {
|
| 175 | 175 |
pi.maps = make([]MapID, info.NrMapIds) |
| 176 | 176 |
info2.NrMapIds = info.NrMapIds |
| 177 |
- info2.MapIds = sys.NewPointer(unsafe.Pointer(&pi.maps[0])) |
|
| 177 |
+ info2.MapIds = sys.NewSlicePointer(pi.maps) |
|
| 178 | 178 |
makeSecondCall = true |
| 179 | 179 |
} else if haveProgramInfoMapIDs() == nil {
|
| 180 | 180 |
// This program really has no associated maps. |
| ... | ... |
@@ -215,6 +316,40 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
|
| 215 | 215 |
makeSecondCall = true |
| 216 | 216 |
} |
| 217 | 217 |
|
| 218 |
+ pi.jitedInfo.lineInfoRecSize = info.JitedLineInfoRecSize |
|
| 219 |
+ if info.JitedProgLen > 0 {
|
|
| 220 |
+ pi.jitedInfo.numInsns = info.JitedProgLen |
|
| 221 |
+ pi.jitedInfo.insns = make([]byte, info.JitedProgLen) |
|
| 222 |
+ info2.JitedProgLen = info.JitedProgLen |
|
| 223 |
+ info2.JitedProgInsns = sys.NewSlicePointer(pi.jitedInfo.insns) |
|
| 224 |
+ makeSecondCall = true |
|
| 225 |
+ } |
|
| 226 |
+ |
|
| 227 |
+ if info.NrJitedFuncLens > 0 {
|
|
| 228 |
+ pi.jitedInfo.numFuncLens = info.NrJitedFuncLens |
|
| 229 |
+ pi.jitedInfo.funcLens = make([]uint32, info.NrJitedFuncLens) |
|
| 230 |
+ info2.NrJitedFuncLens = info.NrJitedFuncLens |
|
| 231 |
+ info2.JitedFuncLens = sys.NewSlicePointer(pi.jitedInfo.funcLens) |
|
| 232 |
+ makeSecondCall = true |
|
| 233 |
+ } |
|
| 234 |
+ |
|
| 235 |
+ if info.NrJitedLineInfo > 0 {
|
|
| 236 |
+ pi.jitedInfo.numLineInfos = info.NrJitedLineInfo |
|
| 237 |
+ pi.jitedInfo.lineInfos = make([]uint64, info.NrJitedLineInfo) |
|
| 238 |
+ info2.NrJitedLineInfo = info.NrJitedLineInfo |
|
| 239 |
+ info2.JitedLineInfo = sys.NewSlicePointer(pi.jitedInfo.lineInfos) |
|
| 240 |
+ info2.JitedLineInfoRecSize = info.JitedLineInfoRecSize |
|
| 241 |
+ makeSecondCall = true |
|
| 242 |
+ } |
|
| 243 |
+ |
|
| 244 |
+ if info.NrJitedKsyms > 0 {
|
|
| 245 |
+ pi.jitedInfo.numKsyms = info.NrJitedKsyms |
|
| 246 |
+ pi.jitedInfo.ksyms = make([]uint64, info.NrJitedKsyms) |
|
| 247 |
+ info2.JitedKsyms = sys.NewSlicePointer(pi.jitedInfo.ksyms) |
|
| 248 |
+ info2.NrJitedKsyms = info.NrJitedKsyms |
|
| 249 |
+ makeSecondCall = true |
|
| 250 |
+ } |
|
| 251 |
+ |
|
| 218 | 252 |
if makeSecondCall {
|
| 219 | 253 |
if err := sys.ObjInfo(fd, &info2); err != nil {
|
| 220 | 254 |
return nil, err |
| ... | ... |
@@ -230,7 +365,7 @@ func newProgramInfoFromProc(fd *sys.FD) (*ProgramInfo, error) {
|
| 230 | 230 |
"prog_type": &info.Type, |
| 231 | 231 |
"prog_tag": &info.Tag, |
| 232 | 232 |
}) |
| 233 |
- if errors.Is(err, errMissingFields) {
|
|
| 233 |
+ if errors.Is(err, ErrNotSupported) {
|
|
| 234 | 234 |
return nil, &internal.UnsupportedFeatureError{
|
| 235 | 235 |
Name: "reading program info from /proc/self/fdinfo", |
| 236 | 236 |
MinimumVersion: internal.Version{4, 10, 0},
|
| ... | ... |
@@ -305,6 +440,52 @@ func (pi *ProgramInfo) RecursionMisses() (uint64, bool) {
|
| 305 | 305 |
return 0, false |
| 306 | 306 |
} |
| 307 | 307 |
|
| 308 |
+// btfSpec returns the BTF spec associated with the program. |
|
| 309 |
+func (pi *ProgramInfo) btfSpec() (*btf.Spec, error) {
|
|
| 310 |
+ id, ok := pi.BTFID() |
|
| 311 |
+ if !ok {
|
|
| 312 |
+ return nil, fmt.Errorf("program created without BTF or unsupported kernel: %w", ErrNotSupported)
|
|
| 313 |
+ } |
|
| 314 |
+ |
|
| 315 |
+ h, err := btf.NewHandleFromID(id) |
|
| 316 |
+ if err != nil {
|
|
| 317 |
+ return nil, fmt.Errorf("get BTF handle: %w", err)
|
|
| 318 |
+ } |
|
| 319 |
+ defer h.Close() |
|
| 320 |
+ |
|
| 321 |
+ spec, err := h.Spec(nil) |
|
| 322 |
+ if err != nil {
|
|
| 323 |
+ return nil, fmt.Errorf("get BTF spec: %w", err)
|
|
| 324 |
+ } |
|
| 325 |
+ |
|
| 326 |
+ return spec, nil |
|
| 327 |
+} |
|
| 328 |
+ |
|
| 329 |
+// LineInfos returns the BTF line information of the program. |
|
| 330 |
+// |
|
| 331 |
+// Available from 5.0. |
|
| 332 |
+// |
|
| 333 |
+// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns |
|
| 334 |
+// ErrNotSupported if the program was created without BTF or if the kernel |
|
| 335 |
+// doesn't support the field. |
|
| 336 |
+func (pi *ProgramInfo) LineInfos() (btf.LineOffsets, error) {
|
|
| 337 |
+ if len(pi.lineInfos) == 0 {
|
|
| 338 |
+ return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
|
|
| 339 |
+ } |
|
| 340 |
+ |
|
| 341 |
+ spec, err := pi.btfSpec() |
|
| 342 |
+ if err != nil {
|
|
| 343 |
+ return nil, err |
|
| 344 |
+ } |
|
| 345 |
+ |
|
| 346 |
+ return btf.LoadLineInfos( |
|
| 347 |
+ bytes.NewReader(pi.lineInfos), |
|
| 348 |
+ internal.NativeEndian, |
|
| 349 |
+ pi.numLineInfos, |
|
| 350 |
+ spec, |
|
| 351 |
+ ) |
|
| 352 |
+} |
|
| 353 |
+ |
|
| 308 | 354 |
// Instructions returns the 'xlated' instruction stream of the program |
| 309 | 355 |
// after it has been verified and rewritten by the kernel. These instructions |
| 310 | 356 |
// cannot be loaded back into the kernel as-is, this is mainly used for |
| ... | ... |
@@ -391,6 +572,29 @@ func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
|
| 391 | 391 |
return insns, nil |
| 392 | 392 |
} |
| 393 | 393 |
|
| 394 |
+// JitedSize returns the size of the program's JIT-compiled machine code in bytes, which is the |
|
| 395 |
+// actual code executed on the host's CPU. This field requires the BPF JIT compiler to be enabled. |
|
| 396 |
+// |
|
| 397 |
+// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent. |
|
| 398 |
+func (pi *ProgramInfo) JitedSize() (uint32, error) {
|
|
| 399 |
+ if pi.jitedSize == 0 {
|
|
| 400 |
+ return 0, fmt.Errorf("insufficient permissions, unsupported kernel, or JIT compiler disabled: %w", ErrNotSupported)
|
|
| 401 |
+ } |
|
| 402 |
+ return pi.jitedSize, nil |
|
| 403 |
+} |
|
| 404 |
+ |
|
| 405 |
+// TranslatedSize returns the size of the program's translated instructions in bytes, after it has |
|
| 406 |
+// been verified and rewritten by the kernel. |
|
| 407 |
+// |
|
| 408 |
+// Available from 4.13. Reading this metadata requires CAP_BPF or equivalent. |
|
| 409 |
+func (pi *ProgramInfo) TranslatedSize() (int, error) {
|
|
| 410 |
+ insns := len(pi.insns) |
|
| 411 |
+ if insns == 0 {
|
|
| 412 |
+ return 0, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
|
|
| 413 |
+ } |
|
| 414 |
+ return insns, nil |
|
| 415 |
+} |
|
| 416 |
+ |
|
| 394 | 417 |
// MapIDs returns the maps related to the program. |
| 395 | 418 |
// |
| 396 | 419 |
// Available from 4.15. |
| ... | ... |
@@ -400,6 +604,106 @@ func (pi *ProgramInfo) MapIDs() ([]MapID, bool) {
|
| 400 | 400 |
return pi.maps, pi.maps != nil |
| 401 | 401 |
} |
| 402 | 402 |
|
| 403 |
+// LoadTime returns when the program was loaded since boot time. |
|
| 404 |
+// |
|
| 405 |
+// Available from 4.15. |
|
| 406 |
+// |
|
| 407 |
+// The bool return value indicates whether this optional field is available. |
|
| 408 |
+func (pi *ProgramInfo) LoadTime() (time.Duration, bool) {
|
|
| 409 |
+ // loadTime and NrMapIds were introduced in the same kernel version. |
|
| 410 |
+ return pi.loadTime, pi.loadTime > 0 |
|
| 411 |
+} |
|
| 412 |
+ |
|
| 413 |
+// VerifiedInstructions returns the number verified instructions in the program. |
|
| 414 |
+// |
|
| 415 |
+// Available from 5.16. |
|
| 416 |
+// |
|
| 417 |
+// The bool return value indicates whether this optional field is available. |
|
| 418 |
+func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) {
|
|
| 419 |
+ return pi.verifiedInstructions, pi.verifiedInstructions > 0 |
|
| 420 |
+} |
|
| 421 |
+ |
|
| 422 |
+// JitedKsymAddrs returns the ksym addresses of the BPF program, including its |
|
| 423 |
+// subprograms. The addresses correspond to their symbols in /proc/kallsyms. |
|
| 424 |
+// |
|
| 425 |
+// Available from 4.18. Note that before 5.x, this field can be empty for |
|
| 426 |
+// programs without subprograms (bpf2bpf calls). |
|
| 427 |
+// |
|
| 428 |
+// The bool return value indicates whether this optional field is available. |
|
| 429 |
+// |
|
| 430 |
+// When a kernel address can't fit into uintptr (which is usually the case when |
|
| 431 |
+// running 32 bit program on a 64 bit kernel), this returns an empty slice and |
|
| 432 |
+// a false. |
|
| 433 |
+func (pi *ProgramInfo) JitedKsymAddrs() ([]uintptr, bool) {
|
|
| 434 |
+ ksyms := make([]uintptr, 0, len(pi.jitedInfo.ksyms)) |
|
| 435 |
+ if cap(ksyms) == 0 {
|
|
| 436 |
+ return ksyms, false |
|
| 437 |
+ } |
|
| 438 |
+ // Check if a kernel address fits into uintptr (it might not when |
|
| 439 |
+ // using a 32 bit binary on a 64 bit kernel). This check should work |
|
| 440 |
+ // with any kernel address, since they have 1s at the highest bits. |
|
| 441 |
+ if a := pi.jitedInfo.ksyms[0]; uint64(uintptr(a)) != a {
|
|
| 442 |
+ return nil, false |
|
| 443 |
+ } |
|
| 444 |
+ for _, ksym := range pi.jitedInfo.ksyms {
|
|
| 445 |
+ ksyms = append(ksyms, uintptr(ksym)) |
|
| 446 |
+ } |
|
| 447 |
+ return ksyms, true |
|
| 448 |
+} |
|
| 449 |
+ |
|
| 450 |
+// JitedInsns returns the JITed machine native instructions of the program. |
|
| 451 |
+// |
|
| 452 |
+// Available from 4.13. |
|
| 453 |
+// |
|
| 454 |
+// The bool return value indicates whether this optional field is available. |
|
| 455 |
+func (pi *ProgramInfo) JitedInsns() ([]byte, bool) {
|
|
| 456 |
+ return pi.jitedInfo.insns, len(pi.jitedInfo.insns) > 0 |
|
| 457 |
+} |
|
| 458 |
+ |
|
| 459 |
+// JitedLineInfos returns the JITed line infos of the program. |
|
| 460 |
+// |
|
| 461 |
+// Available from 5.0. |
|
| 462 |
+// |
|
| 463 |
+// The bool return value indicates whether this optional field is available. |
|
| 464 |
+func (pi *ProgramInfo) JitedLineInfos() ([]uint64, bool) {
|
|
| 465 |
+ return pi.jitedInfo.lineInfos, len(pi.jitedInfo.lineInfos) > 0 |
|
| 466 |
+} |
|
| 467 |
+ |
|
| 468 |
+// JitedFuncLens returns the insns length of each function in the JITed program. |
|
| 469 |
+// |
|
| 470 |
+// Available from 4.18. |
|
| 471 |
+// |
|
| 472 |
+// The bool return value indicates whether this optional field is available. |
|
| 473 |
+func (pi *ProgramInfo) JitedFuncLens() ([]uint32, bool) {
|
|
| 474 |
+ return pi.jitedInfo.funcLens, len(pi.jitedInfo.funcLens) > 0 |
|
| 475 |
+} |
|
| 476 |
+ |
|
| 477 |
+// FuncInfos returns the offset and function information of all (sub)programs in |
|
| 478 |
+// a BPF program. |
|
| 479 |
+// |
|
| 480 |
+// Available from 5.0. |
|
| 481 |
+// |
|
| 482 |
+// Requires CAP_SYS_ADMIN or equivalent for reading BTF information. Returns |
|
| 483 |
+// ErrNotSupported if the program was created without BTF or if the kernel |
|
| 484 |
+// doesn't support the field. |
|
| 485 |
+func (pi *ProgramInfo) FuncInfos() (btf.FuncOffsets, error) {
|
|
| 486 |
+ if len(pi.funcInfos) == 0 {
|
|
| 487 |
+ return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
|
|
| 488 |
+ } |
|
| 489 |
+ |
|
| 490 |
+ spec, err := pi.btfSpec() |
|
| 491 |
+ if err != nil {
|
|
| 492 |
+ return nil, err |
|
| 493 |
+ } |
|
| 494 |
+ |
|
| 495 |
+ return btf.LoadFuncInfos( |
|
| 496 |
+ bytes.NewReader(pi.funcInfos), |
|
| 497 |
+ internal.NativeEndian, |
|
| 498 |
+ pi.numFuncInfos, |
|
| 499 |
+ spec, |
|
| 500 |
+ ) |
|
| 501 |
+} |
|
| 502 |
+ |
|
| 403 | 503 |
func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {
|
| 404 | 504 |
fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int()))
|
| 405 | 505 |
if err != nil {
|
| ... | ... |
@@ -413,8 +717,6 @@ func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {
|
| 413 | 413 |
return nil |
| 414 | 414 |
} |
| 415 | 415 |
|
| 416 |
-var errMissingFields = errors.New("missing fields")
|
|
| 417 |
- |
|
| 418 | 416 |
func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
|
| 419 | 417 |
var ( |
| 420 | 418 |
scanner = bufio.NewScanner(r) |
| ... | ... |
@@ -433,26 +735,37 @@ func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
|
| 433 | 433 |
continue |
| 434 | 434 |
} |
| 435 | 435 |
|
| 436 |
- if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 {
|
|
| 437 |
- return fmt.Errorf("can't parse field %s: %v", name, err)
|
|
| 436 |
+ // If field already contains a non-zero value, don't overwrite it with fdinfo. |
|
| 437 |
+ if zero(field) {
|
|
| 438 |
+ if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 {
|
|
| 439 |
+ return fmt.Errorf("can't parse field %s: %v", name, err)
|
|
| 440 |
+ } |
|
| 438 | 441 |
} |
| 439 | 442 |
|
| 440 | 443 |
scanned++ |
| 441 | 444 |
} |
| 442 | 445 |
|
| 443 | 446 |
if err := scanner.Err(); err != nil {
|
| 444 |
- return err |
|
| 447 |
+ return fmt.Errorf("scanning fdinfo: %w", err)
|
|
| 445 | 448 |
} |
| 446 | 449 |
|
| 447 | 450 |
if len(fields) > 0 && scanned == 0 {
|
| 448 | 451 |
return ErrNotSupported |
| 449 | 452 |
} |
| 450 | 453 |
|
| 451 |
- if scanned != len(fields) {
|
|
| 452 |
- return errMissingFields |
|
| 454 |
+ return nil |
|
| 455 |
+} |
|
| 456 |
+ |
|
| 457 |
+func zero(arg any) bool {
|
|
| 458 |
+ v := reflect.ValueOf(arg) |
|
| 459 |
+ |
|
| 460 |
+ // Unwrap pointers and interfaces. |
|
| 461 |
+ for v.Kind() == reflect.Pointer || |
|
| 462 |
+ v.Kind() == reflect.Interface {
|
|
| 463 |
+ v = v.Elem() |
|
| 453 | 464 |
} |
| 454 | 465 |
|
| 455 |
- return nil |
|
| 466 |
+ return v.IsZero() |
|
| 456 | 467 |
} |
| 457 | 468 |
|
| 458 | 469 |
// EnableStats starts the measuring of the runtime |
| ... | ... |
@@ -471,7 +784,7 @@ func EnableStats(which uint32) (io.Closer, error) {
|
| 471 | 471 |
return fd, nil |
| 472 | 472 |
} |
| 473 | 473 |
|
| 474 |
-var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", "4.15", func() error {
|
|
| 474 |
+var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", func() error {
|
|
| 475 | 475 |
prog, err := progLoad(asm.Instructions{
|
| 476 | 476 |
asm.LoadImm(asm.R0, 0, asm.DWord), |
| 477 | 477 |
asm.Return(), |
| ... | ... |
@@ -496,4 +809,4 @@ var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", "
|
| 496 | 496 |
} |
| 497 | 497 |
|
| 498 | 498 |
return err |
| 499 |
-}) |
|
| 499 |
+}, "4.15") |
| 500 | 500 |
deleted file mode 100644 |
| ... | ... |
@@ -1,60 +0,0 @@ |
| 1 |
-package internal |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "io" |
|
| 6 |
- _ "unsafe" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-type auxvPairReader interface {
|
|
| 10 |
- Close() error |
|
| 11 |
- ReadAuxvPair() (uint64, uint64, error) |
|
| 12 |
-} |
|
| 13 |
- |
|
| 14 |
-// See https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/auxvec.h |
|
| 15 |
-const ( |
|
| 16 |
- _AT_NULL = 0 // End of vector |
|
| 17 |
- _AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image |
|
| 18 |
-) |
|
| 19 |
- |
|
| 20 |
-//go:linkname runtime_getAuxv runtime.getAuxv |
|
| 21 |
-func runtime_getAuxv() []uintptr |
|
| 22 |
- |
|
| 23 |
-type auxvRuntimeReader struct {
|
|
| 24 |
- data []uintptr |
|
| 25 |
- index int |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-func (r *auxvRuntimeReader) Close() error {
|
|
| 29 |
- return nil |
|
| 30 |
-} |
|
| 31 |
- |
|
| 32 |
-func (r *auxvRuntimeReader) ReadAuxvPair() (uint64, uint64, error) {
|
|
| 33 |
- if r.index >= len(r.data)+2 {
|
|
| 34 |
- return 0, 0, io.EOF |
|
| 35 |
- } |
|
| 36 |
- |
|
| 37 |
- // we manually add the (_AT_NULL, _AT_NULL) pair at the end |
|
| 38 |
- // that is not provided by the go runtime |
|
| 39 |
- var tag, value uintptr |
|
| 40 |
- if r.index+1 < len(r.data) {
|
|
| 41 |
- tag, value = r.data[r.index], r.data[r.index+1] |
|
| 42 |
- } else {
|
|
| 43 |
- tag, value = _AT_NULL, _AT_NULL |
|
| 44 |
- } |
|
| 45 |
- r.index += 2 |
|
| 46 |
- return uint64(tag), uint64(value), nil |
|
| 47 |
-} |
|
| 48 |
- |
|
| 49 |
-func newAuxvRuntimeReader() (auxvPairReader, error) {
|
|
| 50 |
- data := runtime_getAuxv() |
|
| 51 |
- |
|
| 52 |
- if len(data)%2 != 0 {
|
|
| 53 |
- return nil, errors.New("malformed auxv passed from runtime")
|
|
| 54 |
- } |
|
| 55 |
- |
|
| 56 |
- return &auxvRuntimeReader{
|
|
| 57 |
- data: data, |
|
| 58 |
- index: 0, |
|
| 59 |
- }, nil |
|
| 60 |
-} |
| ... | ... |
@@ -23,7 +23,7 @@ func ErrorWithLog(source string, err error, log []byte) *VerifierError {
|
| 23 | 23 |
|
| 24 | 24 |
log = bytes.Trim(log, whitespace) |
| 25 | 25 |
if len(log) == 0 {
|
| 26 |
- return &VerifierError{source, err, nil, false}
|
|
| 26 |
+ return &VerifierError{source, err, nil}
|
|
| 27 | 27 |
} |
| 28 | 28 |
|
| 29 | 29 |
logLines := bytes.Split(log, []byte{'\n'})
|
| ... | ... |
@@ -34,7 +34,7 @@ func ErrorWithLog(source string, err error, log []byte) *VerifierError {
|
| 34 | 34 |
lines = append(lines, string(bytes.TrimRight(line, whitespace))) |
| 35 | 35 |
} |
| 36 | 36 |
|
| 37 |
- return &VerifierError{source, err, lines, false}
|
|
| 37 |
+ return &VerifierError{source, err, lines}
|
|
| 38 | 38 |
} |
| 39 | 39 |
|
| 40 | 40 |
// VerifierError includes information from the eBPF verifier. |
| ... | ... |
@@ -46,8 +46,6 @@ type VerifierError struct {
|
| 46 | 46 |
Cause error |
| 47 | 47 |
// The verifier output split into lines. |
| 48 | 48 |
Log []string |
| 49 |
- // Deprecated: the log is never truncated anymore. |
|
| 50 |
- Truncated bool |
|
| 51 | 49 |
} |
| 52 | 50 |
|
| 53 | 51 |
func (le *VerifierError) Unwrap() error {
|
| ... | ... |
@@ -3,15 +3,25 @@ package internal |
| 3 | 3 |
import ( |
| 4 | 4 |
"errors" |
| 5 | 5 |
"fmt" |
| 6 |
+ "runtime" |
|
| 7 |
+ "strings" |
|
| 6 | 8 |
"sync" |
| 7 | 9 |
) |
| 8 | 10 |
|
| 9 |
-// ErrNotSupported indicates that a feature is not supported by the current kernel. |
|
| 11 |
+// ErrNotSupported indicates that a feature is not supported. |
|
| 10 | 12 |
var ErrNotSupported = errors.New("not supported")
|
| 11 | 13 |
|
| 14 |
+// ErrNotSupportedOnOS indicates that a feature is not supported on the current |
|
| 15 |
+// operating system. |
|
| 16 |
+var ErrNotSupportedOnOS = fmt.Errorf("%w on %s", ErrNotSupported, runtime.GOOS)
|
|
| 17 |
+ |
|
| 12 | 18 |
// UnsupportedFeatureError is returned by FeatureTest() functions. |
| 13 | 19 |
type UnsupportedFeatureError struct {
|
| 14 |
- // The minimum Linux mainline version required for this feature. |
|
| 20 |
+ // The minimum version required for this feature. |
|
| 21 |
+ // |
|
| 22 |
+ // On Linux this refers to the mainline kernel version, on other platforms |
|
| 23 |
+ // to the version of the runtime. |
|
| 24 |
+ // |
|
| 15 | 25 |
// Used for the error string, and for sanity checking during testing. |
| 16 | 26 |
MinimumVersion Version |
| 17 | 27 |
|
| ... | ... |
@@ -58,11 +68,44 @@ type FeatureTest struct {
|
| 58 | 58 |
type FeatureTestFn func() error |
| 59 | 59 |
|
| 60 | 60 |
// NewFeatureTest is a convenient way to create a single [FeatureTest]. |
| 61 |
-func NewFeatureTest(name, version string, fn FeatureTestFn) func() error {
|
|
| 61 |
+// |
|
| 62 |
+// versions specifies in which version of a BPF runtime a feature appeared. |
|
| 63 |
+// The format is "GOOS:Major.Minor[.Patch]". GOOS may be omitted when targeting |
|
| 64 |
+// Linux. Returns [ErrNotSupportedOnOS] if there is no version specified for the |
|
| 65 |
+// current OS. |
|
| 66 |
+func NewFeatureTest(name string, fn FeatureTestFn, versions ...string) func() error {
|
|
| 67 |
+ const nativePrefix = runtime.GOOS + ":" |
|
| 68 |
+ |
|
| 69 |
+ if len(versions) == 0 {
|
|
| 70 |
+ return func() error {
|
|
| 71 |
+ return fmt.Errorf("feature test %q: no versions specified", name)
|
|
| 72 |
+ } |
|
| 73 |
+ } |
|
| 74 |
+ |
|
| 62 | 75 |
ft := &FeatureTest{
|
| 63 |
- Name: name, |
|
| 64 |
- Version: version, |
|
| 65 |
- Fn: fn, |
|
| 76 |
+ Name: name, |
|
| 77 |
+ Fn: fn, |
|
| 78 |
+ } |
|
| 79 |
+ |
|
| 80 |
+ for _, version := range versions {
|
|
| 81 |
+ if strings.HasPrefix(version, nativePrefix) {
|
|
| 82 |
+ ft.Version = strings.TrimPrefix(version, nativePrefix) |
|
| 83 |
+ break |
|
| 84 |
+ } |
|
| 85 |
+ |
|
| 86 |
+ if OnLinux && !strings.ContainsRune(version, ':') {
|
|
| 87 |
+ // Allow version numbers without a GOOS prefix on Linux. |
|
| 88 |
+ ft.Version = version |
|
| 89 |
+ break |
|
| 90 |
+ } |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ if ft.Version == "" {
|
|
| 94 |
+ return func() error {
|
|
| 95 |
+ // We don't return an UnsupportedFeatureError here, since that will |
|
| 96 |
+ // trigger version checks which don't make sense. |
|
| 97 |
+ return fmt.Errorf("%s: %w", name, ErrNotSupportedOnOS)
|
|
| 98 |
+ } |
|
| 66 | 99 |
} |
| 67 | 100 |
|
| 68 | 101 |
return ft.execute |
| 0 | 7 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,20 @@ |
| 0 |
+package kallsyms |
|
| 1 |
+ |
|
| 2 |
+import "sync" |
|
| 3 |
+ |
|
| 4 |
+type cache[K, V comparable] struct {
|
|
| 5 |
+ m sync.Map |
|
| 6 |
+} |
|
| 7 |
+ |
|
| 8 |
+func (c *cache[K, V]) Load(key K) (value V, _ bool) {
|
|
| 9 |
+ v, ok := c.m.Load(key) |
|
| 10 |
+ if !ok {
|
|
| 11 |
+ return value, false |
|
| 12 |
+ } |
|
| 13 |
+ value = v.(V) |
|
| 14 |
+ return value, true |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+func (c *cache[K, V]) Store(key K, value V) {
|
|
| 18 |
+ c.m.Store(key, value) |
|
| 19 |
+} |
| ... | ... |
@@ -1,74 +1,289 @@ |
| 1 | 1 |
package kallsyms |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "bufio" |
|
| 5 |
- "bytes" |
|
| 4 |
+ "errors" |
|
| 5 |
+ "fmt" |
|
| 6 | 6 |
"io" |
| 7 | 7 |
"os" |
| 8 |
- "sync" |
|
| 8 |
+ "slices" |
|
| 9 |
+ "strconv" |
|
| 10 |
+ "strings" |
|
| 11 |
+ |
|
| 12 |
+ "github.com/cilium/ebpf/internal" |
|
| 9 | 13 |
) |
| 10 | 14 |
|
| 11 |
-var kernelModules struct {
|
|
| 12 |
- sync.RWMutex |
|
| 13 |
- // function to kernel module mapping |
|
| 14 |
- kmods map[string]string |
|
| 15 |
+var errAmbiguousKsym = errors.New("multiple kernel symbols with the same name")
|
|
| 16 |
+ |
|
| 17 |
+var symAddrs cache[string, uint64] |
|
| 18 |
+var symModules cache[string, string] |
|
| 19 |
+ |
|
| 20 |
+// Module returns the kernel module providing the given symbol in the kernel, if |
|
| 21 |
+// any. Returns an empty string and no error if the symbol is not present in the |
|
| 22 |
+// kernel. Only function symbols are considered. Returns an error if multiple |
|
| 23 |
+// symbols with the same name were found. |
|
| 24 |
+// |
|
| 25 |
+// Consider [AssignModules] if you need to resolve multiple symbols, as it will |
|
| 26 |
+// only perform one iteration over /proc/kallsyms. |
|
| 27 |
+func Module(name string) (string, error) {
|
|
| 28 |
+ if name == "" {
|
|
| 29 |
+ return "", nil |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ if mod, ok := symModules.Load(name); ok {
|
|
| 33 |
+ return mod, nil |
|
| 34 |
+ } |
|
| 35 |
+ |
|
| 36 |
+ request := map[string]string{name: ""}
|
|
| 37 |
+ if err := AssignModules(request); err != nil {
|
|
| 38 |
+ return "", err |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return request[name], nil |
|
| 15 | 42 |
} |
| 16 | 43 |
|
| 17 |
-// KernelModule returns the kernel module, if any, a probe-able function is contained in. |
|
| 18 |
-func KernelModule(fn string) (string, error) {
|
|
| 19 |
- kernelModules.RLock() |
|
| 20 |
- kmods := kernelModules.kmods |
|
| 21 |
- kernelModules.RUnlock() |
|
| 44 |
+// AssignModules looks up the kernel module providing each given symbol, if any, |
|
| 45 |
+// and assigns them to their corresponding values in the symbols map. Only |
|
| 46 |
+// function symbols are considered. Results of all lookups are cached, |
|
| 47 |
+// successful or otherwise. |
|
| 48 |
+// |
|
| 49 |
+// Any symbols missing in the kernel are ignored. Returns an error if multiple |
|
| 50 |
+// symbols with a given name were found. |
|
| 51 |
+func AssignModules(symbols map[string]string) error {
|
|
| 52 |
+ if !internal.OnLinux {
|
|
| 53 |
+ return fmt.Errorf("read /proc/kallsyms: %w", internal.ErrNotSupportedOnOS)
|
|
| 54 |
+ } |
|
| 22 | 55 |
|
| 23 |
- if kmods == nil {
|
|
| 24 |
- kernelModules.Lock() |
|
| 25 |
- defer kernelModules.Unlock() |
|
| 26 |
- kmods = kernelModules.kmods |
|
| 56 |
+ if len(symbols) == 0 {
|
|
| 57 |
+ return nil |
|
| 27 | 58 |
} |
| 28 | 59 |
|
| 29 |
- if kmods != nil {
|
|
| 30 |
- return kmods[fn], nil |
|
| 60 |
+ // Attempt to fetch symbols from cache. |
|
| 61 |
+ request := make(map[string]string) |
|
| 62 |
+ for name := range symbols {
|
|
| 63 |
+ if mod, ok := symModules.Load(name); ok {
|
|
| 64 |
+ symbols[name] = mod |
|
| 65 |
+ continue |
|
| 66 |
+ } |
|
| 67 |
+ |
|
| 68 |
+ // Mark the symbol to be read from /proc/kallsyms. |
|
| 69 |
+ request[name] = "" |
|
| 70 |
+ } |
|
| 71 |
+ if len(request) == 0 {
|
|
| 72 |
+ // All symbols satisfied from cache. |
|
| 73 |
+ return nil |
|
| 31 | 74 |
} |
| 32 | 75 |
|
| 33 | 76 |
f, err := os.Open("/proc/kallsyms")
|
| 34 | 77 |
if err != nil {
|
| 35 |
- return "", err |
|
| 78 |
+ return err |
|
| 36 | 79 |
} |
| 37 | 80 |
defer f.Close() |
| 38 |
- kmods, err = loadKernelModuleMapping(f) |
|
| 39 |
- if err != nil {
|
|
| 40 |
- return "", err |
|
| 81 |
+ |
|
| 82 |
+ if err := assignModules(f, request); err != nil {
|
|
| 83 |
+ return fmt.Errorf("assigning symbol modules: %w", err)
|
|
| 41 | 84 |
} |
| 42 | 85 |
|
| 43 |
- kernelModules.kmods = kmods |
|
| 44 |
- return kmods[fn], nil |
|
| 86 |
+ // Update the cache with the new symbols. Cache all requested symbols, even if |
|
| 87 |
+ // they're missing or don't belong to a module. |
|
| 88 |
+ for name, mod := range request {
|
|
| 89 |
+ symModules.Store(name, mod) |
|
| 90 |
+ symbols[name] = mod |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ return nil |
|
| 45 | 94 |
} |
| 46 | 95 |
|
| 47 |
-// FlushKernelModuleCache removes any cached information about function to kernel module mapping. |
|
| 48 |
-func FlushKernelModuleCache() {
|
|
| 49 |
- kernelModules.Lock() |
|
| 50 |
- defer kernelModules.Unlock() |
|
| 96 |
+// assignModules assigns kernel symbol modules read from f to values requested |
|
| 97 |
+// by symbols. Always scans the whole input to make sure the user didn't request |
|
| 98 |
+// an ambiguous symbol. |
|
| 99 |
+func assignModules(f io.Reader, symbols map[string]string) error {
|
|
| 100 |
+ if len(symbols) == 0 {
|
|
| 101 |
+ return nil |
|
| 102 |
+ } |
|
| 103 |
+ |
|
| 104 |
+ found := make(map[string]struct{})
|
|
| 105 |
+ r := newReader(f) |
|
| 106 |
+ for r.Line() {
|
|
| 107 |
+ // Only look for function symbols in the kernel's text section (tT). |
|
| 108 |
+ s, err, skip := parseSymbol(r, []rune{'t', 'T'})
|
|
| 109 |
+ if err != nil {
|
|
| 110 |
+ return fmt.Errorf("parsing kallsyms line: %w", err)
|
|
| 111 |
+ } |
|
| 112 |
+ if skip {
|
|
| 113 |
+ continue |
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ if _, requested := symbols[s.name]; !requested {
|
|
| 117 |
+ continue |
|
| 118 |
+ } |
|
| 119 |
+ |
|
| 120 |
+ if _, ok := found[s.name]; ok {
|
|
| 121 |
+ // We've already seen this symbol. Return an error to avoid silently |
|
| 122 |
+ // attaching to a symbol in the wrong module. libbpf also rejects |
|
| 123 |
+ // referring to ambiguous symbols. |
|
| 124 |
+ // |
|
| 125 |
+ // We can't simply check if we already have a value for the given symbol, |
|
| 126 |
+ // since many won't have an associated kernel module. |
|
| 127 |
+ return fmt.Errorf("symbol %s: duplicate found at address 0x%x (module %q): %w",
|
|
| 128 |
+ s.name, s.addr, s.mod, errAmbiguousKsym) |
|
| 129 |
+ } |
|
| 130 |
+ |
|
| 131 |
+ symbols[s.name] = s.mod |
|
| 132 |
+ found[s.name] = struct{}{}
|
|
| 133 |
+ } |
|
| 134 |
+ if err := r.Err(); err != nil {
|
|
| 135 |
+ return fmt.Errorf("reading kallsyms: %w", err)
|
|
| 136 |
+ } |
|
| 137 |
+ |
|
| 138 |
+ return nil |
|
| 139 |
+} |
|
| 140 |
+ |
|
| 141 |
+// Address returns the address of the given symbol in the kernel. Returns 0 and |
|
| 142 |
+// no error if the symbol is not present. Returns an error if multiple addresses |
|
| 143 |
+// were found for a symbol. |
|
| 144 |
+// |
|
| 145 |
+// Consider [AssignAddresses] if you need to resolve multiple symbols, as it |
|
| 146 |
+// will only perform one iteration over /proc/kallsyms. |
|
| 147 |
+func Address(symbol string) (uint64, error) {
|
|
| 148 |
+ if symbol == "" {
|
|
| 149 |
+ return 0, nil |
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ if addr, ok := symAddrs.Load(symbol); ok {
|
|
| 153 |
+ return addr, nil |
|
| 154 |
+ } |
|
| 51 | 155 |
|
| 52 |
- kernelModules.kmods = nil |
|
| 156 |
+ request := map[string]uint64{symbol: 0}
|
|
| 157 |
+ if err := AssignAddresses(request); err != nil {
|
|
| 158 |
+ return 0, err |
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 161 |
+ return request[symbol], nil |
|
| 53 | 162 |
} |
| 54 | 163 |
|
| 55 |
-func loadKernelModuleMapping(f io.Reader) (map[string]string, error) {
|
|
| 56 |
- mods := make(map[string]string) |
|
| 57 |
- scanner := bufio.NewScanner(f) |
|
| 58 |
- for scanner.Scan() {
|
|
| 59 |
- fields := bytes.Fields(scanner.Bytes()) |
|
| 60 |
- if len(fields) < 4 {
|
|
| 164 |
+// AssignAddresses looks up the addresses of the requested symbols in the kernel |
|
| 165 |
+// and assigns them to their corresponding values in the symbols map. Results |
|
| 166 |
+// of all lookups are cached, successful or otherwise. |
|
| 167 |
+// |
|
| 168 |
+// Any symbols missing in the kernel are ignored. Returns an error if multiple |
|
| 169 |
+// addresses were found for a symbol. |
|
| 170 |
+func AssignAddresses(symbols map[string]uint64) error {
|
|
| 171 |
+ if !internal.OnLinux {
|
|
| 172 |
+ return fmt.Errorf("read /proc/kallsyms: %w", internal.ErrNotSupportedOnOS)
|
|
| 173 |
+ } |
|
| 174 |
+ |
|
| 175 |
+ if len(symbols) == 0 {
|
|
| 176 |
+ return nil |
|
| 177 |
+ } |
|
| 178 |
+ |
|
| 179 |
+ // Attempt to fetch symbols from cache. |
|
| 180 |
+ request := make(map[string]uint64) |
|
| 181 |
+ for name := range symbols {
|
|
| 182 |
+ if addr, ok := symAddrs.Load(name); ok {
|
|
| 183 |
+ symbols[name] = addr |
|
| 61 | 184 |
continue |
| 62 | 185 |
} |
| 63 |
- switch string(fields[1]) {
|
|
| 64 |
- case "t", "T": |
|
| 65 |
- mods[string(fields[2])] = string(bytes.Trim(fields[3], "[]")) |
|
| 66 |
- default: |
|
| 186 |
+ |
|
| 187 |
+ // Mark the symbol to be read from /proc/kallsyms. |
|
| 188 |
+ request[name] = 0 |
|
| 189 |
+ } |
|
| 190 |
+ if len(request) == 0 {
|
|
| 191 |
+ // All symbols satisfied from cache. |
|
| 192 |
+ return nil |
|
| 193 |
+ } |
|
| 194 |
+ |
|
| 195 |
+ f, err := os.Open("/proc/kallsyms")
|
|
| 196 |
+ if err != nil {
|
|
| 197 |
+ return err |
|
| 198 |
+ } |
|
| 199 |
+ defer f.Close() |
|
| 200 |
+ |
|
| 201 |
+ if err := assignAddresses(f, request); err != nil {
|
|
| 202 |
+ return fmt.Errorf("loading symbol addresses: %w", err)
|
|
| 203 |
+ } |
|
| 204 |
+ |
|
| 205 |
+ // Update the cache with the new symbols. Cache all requested symbols even if |
|
| 206 |
+ // they weren't found, to avoid repeated lookups. |
|
| 207 |
+ for name, addr := range request {
|
|
| 208 |
+ symAddrs.Store(name, addr) |
|
| 209 |
+ symbols[name] = addr |
|
| 210 |
+ } |
|
| 211 |
+ |
|
| 212 |
+ return nil |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 215 |
+// assignAddresses assigns kernel symbol addresses read from f to values |
|
| 216 |
+// requested by symbols. Always scans the whole input to make sure the user |
|
| 217 |
+// didn't request an ambiguous symbol. |
|
| 218 |
+func assignAddresses(f io.Reader, symbols map[string]uint64) error {
|
|
| 219 |
+ if len(symbols) == 0 {
|
|
| 220 |
+ return nil |
|
| 221 |
+ } |
|
| 222 |
+ r := newReader(f) |
|
| 223 |
+ for r.Line() {
|
|
| 224 |
+ s, err, skip := parseSymbol(r, nil) |
|
| 225 |
+ if err != nil {
|
|
| 226 |
+ return fmt.Errorf("parsing kallsyms line: %w", err)
|
|
| 227 |
+ } |
|
| 228 |
+ if skip {
|
|
| 67 | 229 |
continue |
| 68 | 230 |
} |
| 231 |
+ |
|
| 232 |
+ existing, requested := symbols[s.name] |
|
| 233 |
+ if existing != 0 {
|
|
| 234 |
+ // Multiple addresses for a symbol have been found. Return a friendly |
|
| 235 |
+ // error to avoid silently attaching to the wrong symbol. libbpf also |
|
| 236 |
+ // rejects referring to ambiguous symbols. |
|
| 237 |
+ return fmt.Errorf("symbol %s(0x%x): duplicate found at address 0x%x: %w", s.name, existing, s.addr, errAmbiguousKsym)
|
|
| 238 |
+ } |
|
| 239 |
+ if requested {
|
|
| 240 |
+ symbols[s.name] = s.addr |
|
| 241 |
+ } |
|
| 69 | 242 |
} |
| 70 |
- if scanner.Err() != nil {
|
|
| 71 |
- return nil, scanner.Err() |
|
| 243 |
+ if err := r.Err(); err != nil {
|
|
| 244 |
+ return fmt.Errorf("reading kallsyms: %w", err)
|
|
| 72 | 245 |
} |
| 73 |
- return mods, nil |
|
| 246 |
+ |
|
| 247 |
+ return nil |
|
| 248 |
+} |
|
| 249 |
+ |
|
| 250 |
+type ksym struct {
|
|
| 251 |
+ addr uint64 |
|
| 252 |
+ name string |
|
| 253 |
+ mod string |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+// parseSymbol parses a line from /proc/kallsyms into an address, type, name and |
|
| 257 |
+// module. Skip will be true if the symbol doesn't match any of the given symbol |
|
| 258 |
+// types. See `man 1 nm` for all available types. |
|
| 259 |
+// |
|
| 260 |
+// Example line: `ffffffffc1682010 T nf_nat_init [nf_nat]` |
|
| 261 |
+func parseSymbol(r *reader, types []rune) (s ksym, err error, skip bool) {
|
|
| 262 |
+ for i := 0; r.Word(); i++ {
|
|
| 263 |
+ switch i {
|
|
| 264 |
+ // Address of the symbol. |
|
| 265 |
+ case 0: |
|
| 266 |
+ s.addr, err = strconv.ParseUint(r.Text(), 16, 64) |
|
| 267 |
+ if err != nil {
|
|
| 268 |
+ return s, fmt.Errorf("parsing address: %w", err), false
|
|
| 269 |
+ } |
|
| 270 |
+ // Type of the symbol. Assume the character is ASCII-encoded by converting |
|
| 271 |
+ // it directly to a rune, since it's a fixed field controlled by the kernel. |
|
| 272 |
+ case 1: |
|
| 273 |
+ if len(types) > 0 && !slices.Contains(types, rune(r.Bytes()[0])) {
|
|
| 274 |
+ return s, nil, true |
|
| 275 |
+ } |
|
| 276 |
+ // Name of the symbol. |
|
| 277 |
+ case 2: |
|
| 278 |
+ s.name = r.Text() |
|
| 279 |
+ // Kernel module the symbol is provided by. |
|
| 280 |
+ case 3: |
|
| 281 |
+ s.mod = strings.Trim(r.Text(), "[]") |
|
| 282 |
+ // Ignore any future fields. |
|
| 283 |
+ default: |
|
| 284 |
+ break |
|
| 285 |
+ } |
|
| 286 |
+ } |
|
| 287 |
+ |
|
| 288 |
+ return |
|
| 74 | 289 |
} |
| 75 | 290 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,118 @@ |
| 0 |
+package kallsyms |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bufio" |
|
| 4 |
+ "io" |
|
| 5 |
+ "unicode" |
|
| 6 |
+ "unicode/utf8" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+// reader is a line and word-oriented reader built for reading /proc/kallsyms. |
|
| 10 |
+// It takes an io.Reader and iterates its contents line by line, then word by |
|
| 11 |
+// word. |
|
| 12 |
+// |
|
| 13 |
+// It's designed to allow partial reading of lines without paying the cost of |
|
| 14 |
+// allocating objects that will never be accessed, resulting in less work for |
|
| 15 |
+// the garbage collector. |
|
| 16 |
+type reader struct {
|
|
| 17 |
+ s *bufio.Scanner |
|
| 18 |
+ line []byte |
|
| 19 |
+ word []byte |
|
| 20 |
+ |
|
| 21 |
+ err error |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+func newReader(r io.Reader) *reader {
|
|
| 25 |
+ return &reader{
|
|
| 26 |
+ s: bufio.NewScanner(r), |
|
| 27 |
+ } |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// Bytes returns the current word as a byte slice. |
|
| 31 |
+func (r *reader) Bytes() []byte {
|
|
| 32 |
+ return r.word |
|
| 33 |
+} |
|
| 34 |
+ |
|
| 35 |
+// Text returns the output of Bytes as a string. |
|
| 36 |
+func (r *reader) Text() string {
|
|
| 37 |
+ return string(r.Bytes()) |
|
| 38 |
+} |
|
| 39 |
+ |
|
| 40 |
+// Line advances the reader to the next line in the input. Calling Line resets |
|
| 41 |
+// the current word, making [reader.Bytes] and [reader.Text] return empty |
|
| 42 |
+// values. Follow this up with a call to [reader.Word]. |
|
| 43 |
+// |
|
| 44 |
+// Like [bufio.Scanner], [reader.Err] needs to be checked after Line returns |
|
| 45 |
+// false to determine if an error occurred during reading. |
|
| 46 |
+// |
|
| 47 |
+// Returns true if Line can be called again. Returns false if all lines in the |
|
| 48 |
+// input have been read. |
|
| 49 |
+func (r *reader) Line() bool {
|
|
| 50 |
+ for r.s.Scan() {
|
|
| 51 |
+ line := r.s.Bytes() |
|
| 52 |
+ if len(line) == 0 {
|
|
| 53 |
+ continue |
|
| 54 |
+ } |
|
| 55 |
+ |
|
| 56 |
+ r.line = line |
|
| 57 |
+ r.word = nil |
|
| 58 |
+ |
|
| 59 |
+ return true |
|
| 60 |
+ } |
|
| 61 |
+ if err := r.s.Err(); err != nil {
|
|
| 62 |
+ r.err = err |
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ return false |
|
| 66 |
+} |
|
| 67 |
+ |
|
| 68 |
+// Word advances the reader to the next word in the current line. |
|
| 69 |
+// |
|
| 70 |
+// Returns true if a word is found and Word should be called again. Returns |
|
| 71 |
+// false when all words on the line have been read. |
|
| 72 |
+func (r *reader) Word() bool {
|
|
| 73 |
+ if len(r.line) == 0 {
|
|
| 74 |
+ return false |
|
| 75 |
+ } |
|
| 76 |
+ |
|
| 77 |
+ // Find next word start, skipping leading spaces. |
|
| 78 |
+ start := 0 |
|
| 79 |
+ for width := 0; start < len(r.line); start += width {
|
|
| 80 |
+ var c rune |
|
| 81 |
+ c, width = utf8.DecodeRune(r.line[start:]) |
|
| 82 |
+ if !unicode.IsSpace(c) {
|
|
| 83 |
+ break |
|
| 84 |
+ } |
|
| 85 |
+ } |
|
| 86 |
+ |
|
| 87 |
+ // Whitespace scanning reached the end of the line due to trailing whitespace, |
|
| 88 |
+ // meaning there are no more words to read |
|
| 89 |
+ if start == len(r.line) {
|
|
| 90 |
+ return false |
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ // Find next word end. |
|
| 94 |
+ for width, i := 0, start; i < len(r.line); i += width {
|
|
| 95 |
+ var c rune |
|
| 96 |
+ c, width = utf8.DecodeRune(r.line[i:]) |
|
| 97 |
+ if unicode.IsSpace(c) {
|
|
| 98 |
+ r.word = r.line[start:i] |
|
| 99 |
+ r.line = r.line[i:] |
|
| 100 |
+ return true |
|
| 101 |
+ } |
|
| 102 |
+ } |
|
| 103 |
+ |
|
| 104 |
+ // The line contains data, but no end-of-word boundary was found. This is the |
|
| 105 |
+ // last, unterminated word in the line. |
|
| 106 |
+ if len(r.line) > start {
|
|
| 107 |
+ r.word = r.line[start:] |
|
| 108 |
+ r.line = nil |
|
| 109 |
+ return true |
|
| 110 |
+ } |
|
| 111 |
+ |
|
| 112 |
+ return false |
|
| 113 |
+} |
|
| 114 |
+ |
|
| 115 |
+func (r *reader) Err() error {
|
|
| 116 |
+ return r.err |
|
| 117 |
+} |
| ... | ... |
@@ -1,3 +1,4 @@ |
| 1 |
+// Package kconfig implements a parser for the format of Linux's .config file. |
|
| 1 | 2 |
package kconfig |
| 2 | 3 |
|
| 3 | 4 |
import ( |
| ... | ... |
@@ -7,7 +8,6 @@ import ( |
| 7 | 7 |
"fmt" |
| 8 | 8 |
"io" |
| 9 | 9 |
"math" |
| 10 |
- "os" |
|
| 11 | 10 |
"strconv" |
| 12 | 11 |
"strings" |
| 13 | 12 |
|
| ... | ... |
@@ -15,30 +15,6 @@ import ( |
| 15 | 15 |
"github.com/cilium/ebpf/internal" |
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 |
-// Find find a kconfig file on the host. |
|
| 19 |
-// It first reads from /boot/config- of the current running kernel and tries |
|
| 20 |
-// /proc/config.gz if nothing was found in /boot. |
|
| 21 |
-// If none of the file provide a kconfig, it returns an error. |
|
| 22 |
-func Find() (*os.File, error) {
|
|
| 23 |
- kernelRelease, err := internal.KernelRelease() |
|
| 24 |
- if err != nil {
|
|
| 25 |
- return nil, fmt.Errorf("cannot get kernel release: %w", err)
|
|
| 26 |
- } |
|
| 27 |
- |
|
| 28 |
- path := "/boot/config-" + kernelRelease |
|
| 29 |
- f, err := os.Open(path) |
|
| 30 |
- if err == nil {
|
|
| 31 |
- return f, nil |
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- f, err = os.Open("/proc/config.gz")
|
|
| 35 |
- if err == nil {
|
|
| 36 |
- return f, nil |
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path)
|
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 | 18 |
// Parse parses the kconfig file for which a reader is given. |
| 43 | 19 |
// All the CONFIG_* which are in filter and which are set set will be |
| 44 | 20 |
// put in the returned map as key with their corresponding value as map value. |
| ... | ... |
@@ -127,12 +103,13 @@ func PutValue(data []byte, typ btf.Type, value string) error {
|
| 127 | 127 |
switch value {
|
| 128 | 128 |
case "y", "n", "m": |
| 129 | 129 |
return putValueTri(data, typ, value) |
| 130 |
- default: |
|
| 131 |
- if strings.HasPrefix(value, `"`) {
|
|
| 132 |
- return putValueString(data, typ, value) |
|
| 133 |
- } |
|
| 134 |
- return putValueNumber(data, typ, value) |
|
| 135 | 130 |
} |
| 131 |
+ |
|
| 132 |
+ if strings.HasPrefix(value, `"`) {
|
|
| 133 |
+ return putValueString(data, typ, value) |
|
| 134 |
+ } |
|
| 135 |
+ |
|
| 136 |
+ return putValueNumber(data, typ, value) |
|
| 136 | 137 |
} |
| 137 | 138 |
|
| 138 | 139 |
// Golang translation of libbpf_tristate enum: |
| ... | ... |
@@ -169,6 +146,10 @@ func putValueTri(data []byte, typ btf.Type, value string) error {
|
| 169 | 169 |
return fmt.Errorf("cannot use enum %q, only libbpf_tristate is supported", v.Name)
|
| 170 | 170 |
} |
| 171 | 171 |
|
| 172 |
+ if len(data) != 4 {
|
|
| 173 |
+ return fmt.Errorf("expected enum value to occupy 4 bytes in datasec, got: %d", len(data))
|
|
| 174 |
+ } |
|
| 175 |
+ |
|
| 172 | 176 |
var tri triState |
| 173 | 177 |
switch value {
|
| 174 | 178 |
case "y": |
| ... | ... |
@@ -178,10 +159,10 @@ func putValueTri(data []byte, typ btf.Type, value string) error {
|
| 178 | 178 |
case "n": |
| 179 | 179 |
tri = TriNo |
| 180 | 180 |
default: |
| 181 |
- return fmt.Errorf("value %q is not support for libbpf_tristate", value)
|
|
| 181 |
+ return fmt.Errorf("value %q is not supported for libbpf_tristate", value)
|
|
| 182 | 182 |
} |
| 183 | 183 |
|
| 184 |
- internal.NativeEndian.PutUint64(data, uint64(tri)) |
|
| 184 |
+ internal.NativeEndian.PutUint32(data, uint32(tri)) |
|
| 185 | 185 |
default: |
| 186 | 186 |
return fmt.Errorf("cannot add number value, expected btf.Int or btf.Enum, got: %T", v)
|
| 187 | 187 |
} |
| 188 | 188 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,62 @@ |
| 0 |
+package linux |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/cilium/ebpf/internal" |
|
| 7 |
+ "github.com/cilium/ebpf/internal/unix" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type auxvPairReader interface {
|
|
| 11 |
+ Close() error |
|
| 12 |
+ ReadAuxvPair() (uint64, uint64, error) |
|
| 13 |
+} |
|
| 14 |
+ |
|
| 15 |
+// See https://elixir.bootlin.com/linux/v6.5.5/source/include/uapi/linux/auxvec.h |
|
| 16 |
+const ( |
|
| 17 |
+ _AT_NULL = 0 // End of vector |
|
| 18 |
+ _AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+type auxvRuntimeReader struct {
|
|
| 22 |
+ data [][2]uintptr |
|
| 23 |
+ index int |
|
| 24 |
+} |
|
| 25 |
+ |
|
| 26 |
+func (r *auxvRuntimeReader) Close() error {
|
|
| 27 |
+ return nil |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func (r *auxvRuntimeReader) ReadAuxvPair() (uint64, uint64, error) {
|
|
| 31 |
+ if r.index >= len(r.data)+2 {
|
|
| 32 |
+ return 0, 0, io.EOF |
|
| 33 |
+ } |
|
| 34 |
+ |
|
| 35 |
+ // we manually add the (_AT_NULL, _AT_NULL) pair at the end |
|
| 36 |
+ // that is not provided by the go runtime |
|
| 37 |
+ var tag, value uintptr |
|
| 38 |
+ if r.index < len(r.data) {
|
|
| 39 |
+ tag, value = r.data[r.index][0], r.data[r.index][1] |
|
| 40 |
+ } else {
|
|
| 41 |
+ tag, value = _AT_NULL, _AT_NULL |
|
| 42 |
+ } |
|
| 43 |
+ r.index += 1 |
|
| 44 |
+ return uint64(tag), uint64(value), nil |
|
| 45 |
+} |
|
| 46 |
+ |
|
| 47 |
+func newAuxvRuntimeReader() (auxvPairReader, error) {
|
|
| 48 |
+ if !internal.OnLinux {
|
|
| 49 |
+ return nil, fmt.Errorf("read auxv from runtime: %w", internal.ErrNotSupportedOnOS)
|
|
| 50 |
+ } |
|
| 51 |
+ |
|
| 52 |
+ data, err := unix.Auxv() |
|
| 53 |
+ if err != nil {
|
|
| 54 |
+ return nil, fmt.Errorf("read auxv from runtime: %w", err)
|
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ return &auxvRuntimeReader{
|
|
| 58 |
+ data: data, |
|
| 59 |
+ index: 0, |
|
| 60 |
+ }, nil |
|
| 61 |
+} |
| 0 | 2 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,31 @@ |
| 0 |
+package linux |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "os" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+// FindKConfig searches for a kconfig file on the host. |
|
| 8 |
+// |
|
| 9 |
+// It first reads from /boot/config- of the current running kernel and tries |
|
| 10 |
+// /proc/config.gz if nothing was found in /boot. |
|
| 11 |
+// If none of the file provide a kconfig, it returns an error. |
|
| 12 |
+func FindKConfig() (*os.File, error) {
|
|
| 13 |
+ kernelRelease, err := KernelRelease() |
|
| 14 |
+ if err != nil {
|
|
| 15 |
+ return nil, fmt.Errorf("cannot get kernel release: %w", err)
|
|
| 16 |
+ } |
|
| 17 |
+ |
|
| 18 |
+ path := "/boot/config-" + kernelRelease |
|
| 19 |
+ f, err := os.Open(path) |
|
| 20 |
+ if err == nil {
|
|
| 21 |
+ return f, nil |
|
| 22 |
+ } |
|
| 23 |
+ |
|
| 24 |
+ f, err = os.Open("/proc/config.gz")
|
|
| 25 |
+ if err == nil {
|
|
| 26 |
+ return f, nil |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path)
|
|
| 30 |
+} |
| 0 | 31 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,43 @@ |
| 0 |
+package linux |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "runtime" |
|
| 4 |
+) |
|
| 5 |
+ |
|
| 6 |
+// PlatformPrefix returns the platform-dependent syscall wrapper prefix used by |
|
| 7 |
+// the linux kernel. |
|
| 8 |
+// |
|
| 9 |
+// Based on https://github.com/golang/go/blob/master/src/go/build/syslist.go |
|
| 10 |
+// and https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L10047 |
|
| 11 |
+func PlatformPrefix() string {
|
|
| 12 |
+ switch runtime.GOARCH {
|
|
| 13 |
+ case "386": |
|
| 14 |
+ return "__ia32_" |
|
| 15 |
+ case "amd64", "amd64p32": |
|
| 16 |
+ return "__x64_" |
|
| 17 |
+ |
|
| 18 |
+ case "arm", "armbe": |
|
| 19 |
+ return "__arm_" |
|
| 20 |
+ case "arm64", "arm64be": |
|
| 21 |
+ return "__arm64_" |
|
| 22 |
+ |
|
| 23 |
+ case "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le": |
|
| 24 |
+ return "__mips_" |
|
| 25 |
+ |
|
| 26 |
+ case "s390": |
|
| 27 |
+ return "__s390_" |
|
| 28 |
+ case "s390x": |
|
| 29 |
+ return "__s390x_" |
|
| 30 |
+ |
|
| 31 |
+ case "riscv", "riscv64": |
|
| 32 |
+ return "__riscv_" |
|
| 33 |
+ |
|
| 34 |
+ case "ppc": |
|
| 35 |
+ return "__powerpc_" |
|
| 36 |
+ case "ppc64", "ppc64le": |
|
| 37 |
+ return "__powerpc64_" |
|
| 38 |
+ |
|
| 39 |
+ default: |
|
| 40 |
+ return "" |
|
| 41 |
+ } |
|
| 42 |
+} |
| 0 | 43 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,23 @@ |
| 0 |
+package linux |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "unsafe" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/cilium/ebpf/internal/unix" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func FSType(path string) (int64, error) {
|
|
| 9 |
+ var statfs unix.Statfs_t |
|
| 10 |
+ if err := unix.Statfs(path, &statfs); err != nil {
|
|
| 11 |
+ return 0, err |
|
| 12 |
+ } |
|
| 13 |
+ |
|
| 14 |
+ fsType := int64(statfs.Type) |
|
| 15 |
+ if unsafe.Sizeof(statfs.Type) == 4 {
|
|
| 16 |
+ // We're on a 32 bit arch, where statfs.Type is int32. bpfFSType is a |
|
| 17 |
+ // negative number when interpreted as int32 so we need to cast via |
|
| 18 |
+ // uint32 to avoid sign extension. |
|
| 19 |
+ fsType = int64(uint32(statfs.Type)) |
|
| 20 |
+ } |
|
| 21 |
+ return fsType, nil |
|
| 22 |
+} |
| 0 | 23 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,144 @@ |
| 0 |
+package linux |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "debug/elf" |
|
| 4 |
+ "encoding/binary" |
|
| 5 |
+ "errors" |
|
| 6 |
+ "fmt" |
|
| 7 |
+ "io" |
|
| 8 |
+ "math" |
|
| 9 |
+ "os" |
|
| 10 |
+ |
|
| 11 |
+ "github.com/cilium/ebpf/internal" |
|
| 12 |
+ "github.com/cilium/ebpf/internal/unix" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+var ( |
|
| 16 |
+ errAuxvNoVDSO = errors.New("no vdso address found in auxv")
|
|
| 17 |
+) |
|
| 18 |
+ |
|
| 19 |
+// vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library |
|
| 20 |
+// linked into the current process image. |
|
| 21 |
+func vdsoVersion() (uint32, error) {
|
|
| 22 |
+ av, err := newAuxvRuntimeReader() |
|
| 23 |
+ if err != nil {
|
|
| 24 |
+ return 0, err |
|
| 25 |
+ } |
|
| 26 |
+ |
|
| 27 |
+ defer av.Close() |
|
| 28 |
+ |
|
| 29 |
+ vdsoAddr, err := vdsoMemoryAddress(av) |
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ return 0, fmt.Errorf("finding vDSO memory address: %w", err)
|
|
| 32 |
+ } |
|
| 33 |
+ |
|
| 34 |
+ // Use /proc/self/mem rather than unsafe.Pointer tricks. |
|
| 35 |
+ mem, err := os.Open("/proc/self/mem")
|
|
| 36 |
+ if err != nil {
|
|
| 37 |
+ return 0, fmt.Errorf("opening mem: %w", err)
|
|
| 38 |
+ } |
|
| 39 |
+ defer mem.Close() |
|
| 40 |
+ |
|
| 41 |
+ // Open ELF at provided memory address, as offset into /proc/self/mem. |
|
| 42 |
+ c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64)) |
|
| 43 |
+ if err != nil {
|
|
| 44 |
+ return 0, fmt.Errorf("reading linux version code: %w", err)
|
|
| 45 |
+ } |
|
| 46 |
+ |
|
| 47 |
+ return c, nil |
|
| 48 |
+} |
|
| 49 |
+ |
|
| 50 |
+// vdsoMemoryAddress returns the memory address of the vDSO library |
|
| 51 |
+// linked into the current process image. r is an io.Reader into an auxv blob. |
|
| 52 |
+func vdsoMemoryAddress(r auxvPairReader) (uintptr, error) {
|
|
| 53 |
+ // Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`, |
|
| 54 |
+ // the address of a page containing the virtual Dynamic Shared Object (vDSO). |
|
| 55 |
+ for {
|
|
| 56 |
+ tag, value, err := r.ReadAuxvPair() |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ return 0, err |
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ switch tag {
|
|
| 62 |
+ case _AT_SYSINFO_EHDR: |
|
| 63 |
+ if value != 0 {
|
|
| 64 |
+ return uintptr(value), nil |
|
| 65 |
+ } |
|
| 66 |
+ return 0, fmt.Errorf("invalid vDSO address in auxv")
|
|
| 67 |
+ // _AT_NULL is always the last tag/val pair in the aux vector |
|
| 68 |
+ // and can be treated like EOF. |
|
| 69 |
+ case _AT_NULL: |
|
| 70 |
+ return 0, errAuxvNoVDSO |
|
| 71 |
+ } |
|
| 72 |
+ } |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+// format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)' |
|
| 76 |
+type elfNoteHeader struct {
|
|
| 77 |
+ NameSize int32 |
|
| 78 |
+ DescSize int32 |
|
| 79 |
+ Type int32 |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+// vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in |
|
| 83 |
+// the ELF notes section of the binary provided by the reader. |
|
| 84 |
+func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
|
|
| 85 |
+ hdr, err := internal.NewSafeELFFile(r) |
|
| 86 |
+ if err != nil {
|
|
| 87 |
+ return 0, fmt.Errorf("reading vDSO ELF: %w", err)
|
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 90 |
+ sections := hdr.SectionsByType(elf.SHT_NOTE) |
|
| 91 |
+ if len(sections) == 0 {
|
|
| 92 |
+ return 0, fmt.Errorf("no note section found in vDSO ELF")
|
|
| 93 |
+ } |
|
| 94 |
+ |
|
| 95 |
+ for _, sec := range sections {
|
|
| 96 |
+ sr := sec.Open() |
|
| 97 |
+ var n elfNoteHeader |
|
| 98 |
+ |
|
| 99 |
+ // Read notes until we find one named 'Linux'. |
|
| 100 |
+ for {
|
|
| 101 |
+ if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil {
|
|
| 102 |
+ if errors.Is(err, io.EOF) {
|
|
| 103 |
+ // We looked at all the notes in this section |
|
| 104 |
+ break |
|
| 105 |
+ } |
|
| 106 |
+ return 0, fmt.Errorf("reading note header: %w", err)
|
|
| 107 |
+ } |
|
| 108 |
+ |
|
| 109 |
+ // If a note name is defined, it follows the note header. |
|
| 110 |
+ var name string |
|
| 111 |
+ if n.NameSize > 0 {
|
|
| 112 |
+ // Read the note name, aligned to 4 bytes. |
|
| 113 |
+ buf := make([]byte, internal.Align(n.NameSize, 4)) |
|
| 114 |
+ if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil {
|
|
| 115 |
+ return 0, fmt.Errorf("reading note name: %w", err)
|
|
| 116 |
+ } |
|
| 117 |
+ |
|
| 118 |
+ // Read nul-terminated string. |
|
| 119 |
+ name = unix.ByteSliceToString(buf[:n.NameSize]) |
|
| 120 |
+ } |
|
| 121 |
+ |
|
| 122 |
+ // If a note descriptor is defined, it follows the name. |
|
| 123 |
+ // It is possible for a note to have a descriptor but not a name. |
|
| 124 |
+ if n.DescSize > 0 {
|
|
| 125 |
+ // LINUX_VERSION_CODE is a uint32 value. |
|
| 126 |
+ if name == "Linux" && n.DescSize == 4 && n.Type == 0 {
|
|
| 127 |
+ var version uint32 |
|
| 128 |
+ if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil {
|
|
| 129 |
+ return 0, fmt.Errorf("reading note descriptor: %w", err)
|
|
| 130 |
+ } |
|
| 131 |
+ return version, nil |
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ // Discard the note descriptor if it exists but we're not interested in it. |
|
| 135 |
+ if _, err := io.CopyN(io.Discard, sr, int64(internal.Align(n.DescSize, 4))); err != nil {
|
|
| 136 |
+ return 0, err |
|
| 137 |
+ } |
|
| 138 |
+ } |
|
| 139 |
+ } |
|
| 140 |
+ } |
|
| 141 |
+ |
|
| 142 |
+ return 0, fmt.Errorf("no Linux note in ELF")
|
|
| 143 |
+} |
| 0 | 144 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,34 @@ |
| 0 |
+package linux |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "sync" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/cilium/ebpf/internal" |
|
| 7 |
+ "github.com/cilium/ebpf/internal/unix" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// KernelVersion returns the version of the currently running kernel. |
|
| 11 |
+var KernelVersion = sync.OnceValues(detectKernelVersion) |
|
| 12 |
+ |
|
| 13 |
+// detectKernelVersion returns the version of the running kernel. |
|
| 14 |
+func detectKernelVersion() (internal.Version, error) {
|
|
| 15 |
+ vc, err := vdsoVersion() |
|
| 16 |
+ if err != nil {
|
|
| 17 |
+ return internal.Version{}, err
|
|
| 18 |
+ } |
|
| 19 |
+ return internal.NewVersionFromCode(vc), nil |
|
| 20 |
+} |
|
| 21 |
+ |
|
| 22 |
+// KernelRelease returns the release string of the running kernel. |
|
| 23 |
+// Its format depends on the Linux distribution and corresponds to directory |
|
| 24 |
+// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and |
|
| 25 |
+// 4.19.0-16-amd64. |
|
| 26 |
+func KernelRelease() (string, error) {
|
|
| 27 |
+ var uname unix.Utsname |
|
| 28 |
+ if err := unix.Uname(&uname); err != nil {
|
|
| 29 |
+ return "", fmt.Errorf("uname failed: %w", err)
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ return unix.ByteSliceToString(uname.Release[:]), nil |
|
| 33 |
+} |
| ... | ... |
@@ -1,13 +1,33 @@ |
| 1 | 1 |
package internal |
| 2 | 2 |
|
| 3 |
-import "golang.org/x/exp/constraints" |
|
| 4 |
- |
|
| 5 | 3 |
// Align returns 'n' updated to 'alignment' boundary. |
| 6 |
-func Align[I constraints.Integer](n, alignment I) I {
|
|
| 4 |
+func Align[I Integer](n, alignment I) I {
|
|
| 7 | 5 |
return (n + alignment - 1) / alignment * alignment |
| 8 | 6 |
} |
| 9 | 7 |
|
| 10 | 8 |
// IsPow returns true if n is a power of two. |
| 11 |
-func IsPow[I constraints.Integer](n I) bool {
|
|
| 9 |
+func IsPow[I Integer](n I) bool {
|
|
| 12 | 10 |
return n != 0 && (n&(n-1)) == 0 |
| 13 | 11 |
} |
| 12 |
+ |
|
| 13 |
+// Between returns the value clamped between a and b. |
|
| 14 |
+func Between[I Integer](val, a, b I) I {
|
|
| 15 |
+ lower, upper := a, b |
|
| 16 |
+ if lower > upper {
|
|
| 17 |
+ upper, lower = a, b |
|
| 18 |
+ } |
|
| 19 |
+ |
|
| 20 |
+ val = min(val, upper) |
|
| 21 |
+ return max(val, lower) |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+// Integer represents all possible integer types. |
|
| 25 |
+// Remove when x/exp/constraints is moved to the standard library. |
|
| 26 |
+type Integer interface {
|
|
| 27 |
+ ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+// List of integer types known by the Go compiler. Used by TestIntegerConstraint |
|
| 31 |
+// to warn if a new integer type is introduced. Remove when x/exp/constraints |
|
| 32 |
+// is moved to the standard library. |
|
| 33 |
+var integers = []string{"int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr"}
|
| 14 | 34 |
deleted file mode 100644 |
| ... | ... |
@@ -1,65 +0,0 @@ |
| 1 |
-package internal |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "errors" |
|
| 5 |
- "fmt" |
|
| 6 |
- "os" |
|
| 7 |
- "path/filepath" |
|
| 8 |
- "runtime" |
|
| 9 |
- |
|
| 10 |
- "github.com/cilium/ebpf/internal/sys" |
|
| 11 |
- "github.com/cilium/ebpf/internal/unix" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-func Pin(currentPath, newPath string, fd *sys.FD) error {
|
|
| 15 |
- if newPath == "" {
|
|
| 16 |
- return errors.New("given pinning path cannot be empty")
|
|
| 17 |
- } |
|
| 18 |
- if currentPath == newPath {
|
|
| 19 |
- return nil |
|
| 20 |
- } |
|
| 21 |
- |
|
| 22 |
- fsType, err := FSType(filepath.Dir(newPath)) |
|
| 23 |
- if err != nil {
|
|
| 24 |
- return err |
|
| 25 |
- } |
|
| 26 |
- if fsType != unix.BPF_FS_MAGIC {
|
|
| 27 |
- return fmt.Errorf("%s is not on a bpf filesystem", newPath)
|
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- defer runtime.KeepAlive(fd) |
|
| 31 |
- |
|
| 32 |
- if currentPath == "" {
|
|
| 33 |
- return sys.ObjPin(&sys.ObjPinAttr{
|
|
| 34 |
- Pathname: sys.NewStringPointer(newPath), |
|
| 35 |
- BpfFd: fd.Uint(), |
|
| 36 |
- }) |
|
| 37 |
- } |
|
| 38 |
- |
|
| 39 |
- // Renameat2 is used instead of os.Rename to disallow the new path replacing |
|
| 40 |
- // an existing path. |
|
| 41 |
- err = unix.Renameat2(unix.AT_FDCWD, currentPath, unix.AT_FDCWD, newPath, unix.RENAME_NOREPLACE) |
|
| 42 |
- if err == nil {
|
|
| 43 |
- // Object is now moved to the new pinning path. |
|
| 44 |
- return nil |
|
| 45 |
- } |
|
| 46 |
- if !os.IsNotExist(err) {
|
|
| 47 |
- return fmt.Errorf("unable to move pinned object to new path %v: %w", newPath, err)
|
|
| 48 |
- } |
|
| 49 |
- // Internal state not in sync with the file system so let's fix it. |
|
| 50 |
- return sys.ObjPin(&sys.ObjPinAttr{
|
|
| 51 |
- Pathname: sys.NewStringPointer(newPath), |
|
| 52 |
- BpfFd: fd.Uint(), |
|
| 53 |
- }) |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-func Unpin(pinnedPath string) error {
|
|
| 57 |
- if pinnedPath == "" {
|
|
| 58 |
- return nil |
|
| 59 |
- } |
|
| 60 |
- err := os.Remove(pinnedPath) |
|
| 61 |
- if err == nil || os.IsNotExist(err) {
|
|
| 62 |
- return nil |
|
| 63 |
- } |
|
| 64 |
- return err |
|
| 65 |
-} |
| 66 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,43 +0,0 @@ |
| 1 |
-package internal |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "runtime" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 |
-// PlatformPrefix returns the platform-dependent syscall wrapper prefix used by |
|
| 8 |
-// the linux kernel. |
|
| 9 |
-// |
|
| 10 |
-// Based on https://github.com/golang/go/blob/master/src/go/build/syslist.go |
|
| 11 |
-// and https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L10047 |
|
| 12 |
-func PlatformPrefix() string {
|
|
| 13 |
- switch runtime.GOARCH {
|
|
| 14 |
- case "386": |
|
| 15 |
- return "__ia32_" |
|
| 16 |
- case "amd64", "amd64p32": |
|
| 17 |
- return "__x64_" |
|
| 18 |
- |
|
| 19 |
- case "arm", "armbe": |
|
| 20 |
- return "__arm_" |
|
| 21 |
- case "arm64", "arm64be": |
|
| 22 |
- return "__arm64_" |
|
| 23 |
- |
|
| 24 |
- case "mips", "mipsle", "mips64", "mips64le", "mips64p32", "mips64p32le": |
|
| 25 |
- return "__mips_" |
|
| 26 |
- |
|
| 27 |
- case "s390": |
|
| 28 |
- return "__s390_" |
|
| 29 |
- case "s390x": |
|
| 30 |
- return "__s390x_" |
|
| 31 |
- |
|
| 32 |
- case "riscv", "riscv64": |
|
| 33 |
- return "__riscv_" |
|
| 34 |
- |
|
| 35 |
- case "ppc": |
|
| 36 |
- return "__powerpc_" |
|
| 37 |
- case "ppc64", "ppc64le": |
|
| 38 |
- return "__powerpc64_" |
|
| 39 |
- |
|
| 40 |
- default: |
|
| 41 |
- return "" |
|
| 42 |
- } |
|
| 43 |
-} |
| 44 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,23 +0,0 @@ |
| 1 |
-package internal |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "unsafe" |
|
| 5 |
- |
|
| 6 |
- "github.com/cilium/ebpf/internal/unix" |
|
| 7 |
-) |
|
| 8 |
- |
|
| 9 |
-func FSType(path string) (int64, error) {
|
|
| 10 |
- var statfs unix.Statfs_t |
|
| 11 |
- if err := unix.Statfs(path, &statfs); err != nil {
|
|
| 12 |
- return 0, err |
|
| 13 |
- } |
|
| 14 |
- |
|
| 15 |
- fsType := int64(statfs.Type) |
|
| 16 |
- if unsafe.Sizeof(statfs.Type) == 4 {
|
|
| 17 |
- // We're on a 32 bit arch, where statfs.Type is int32. bpfFSType is a |
|
| 18 |
- // negative number when interpreted as int32 so we need to cast via |
|
| 19 |
- // uint32 to avoid sign extension. |
|
| 20 |
- fsType = int64(uint32(statfs.Type)) |
|
| 21 |
- } |
|
| 22 |
- return fsType, nil |
|
| 23 |
-} |
| ... | ... |
@@ -4,9 +4,12 @@ import ( |
| 4 | 4 |
"fmt" |
| 5 | 5 |
"math" |
| 6 | 6 |
"os" |
| 7 |
+ "path/filepath" |
|
| 7 | 8 |
"runtime" |
| 8 | 9 |
"strconv" |
| 10 |
+ "strings" |
|
| 9 | 11 |
|
| 12 |
+ "github.com/cilium/ebpf/internal/testutils/fdtrace" |
|
| 10 | 13 |
"github.com/cilium/ebpf/internal/unix" |
| 11 | 14 |
) |
| 12 | 15 |
|
| ... | ... |
@@ -17,15 +20,7 @@ type FD struct {
|
| 17 | 17 |
} |
| 18 | 18 |
|
| 19 | 19 |
func newFD(value int) *FD {
|
| 20 |
- if onLeakFD != nil {
|
|
| 21 |
- // Attempt to store the caller's stack for the given fd value. |
|
| 22 |
- // Panic if fds contains an existing stack for the fd. |
|
| 23 |
- old, exist := fds.LoadOrStore(value, callersFrames()) |
|
| 24 |
- if exist {
|
|
| 25 |
- f := old.(*runtime.Frames) |
|
| 26 |
- panic(fmt.Sprintf("found existing stack for fd %d:\n%s", value, FormatFrames(f)))
|
|
| 27 |
- } |
|
| 28 |
- } |
|
| 20 |
+ fdtrace.TraceFD(value, 1) |
|
| 29 | 21 |
|
| 30 | 22 |
fd := &FD{value}
|
| 31 | 23 |
runtime.SetFinalizer(fd, (*FD).finalize) |
| ... | ... |
@@ -39,13 +34,7 @@ func (fd *FD) finalize() {
|
| 39 | 39 |
return |
| 40 | 40 |
} |
| 41 | 41 |
|
| 42 |
- // Invoke the fd leak callback. Calls LoadAndDelete to guarantee the callback |
|
| 43 |
- // is invoked at most once for one sys.FD allocation, runtime.Frames can only |
|
| 44 |
- // be unwound once. |
|
| 45 |
- f, ok := fds.LoadAndDelete(fd.Int()) |
|
| 46 |
- if ok && onLeakFD != nil {
|
|
| 47 |
- onLeakFD(f.(*runtime.Frames)) |
|
| 48 |
- } |
|
| 42 |
+ fdtrace.LeakFD(fd.raw) |
|
| 49 | 43 |
|
| 50 | 44 |
_ = fd.Close() |
| 51 | 45 |
} |
| ... | ... |
@@ -92,12 +81,15 @@ func (fd *FD) Close() error {
|
| 92 | 92 |
return nil |
| 93 | 93 |
} |
| 94 | 94 |
|
| 95 |
- return unix.Close(fd.disown()) |
|
| 95 |
+ return unix.Close(fd.Disown()) |
|
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
-func (fd *FD) disown() int {
|
|
| 99 |
- value := int(fd.raw) |
|
| 100 |
- fds.Delete(int(value)) |
|
| 98 |
+// Disown destroys the FD and returns its raw file descriptor without closing |
|
| 99 |
+// it. After this call, the underlying fd is no longer tied to the FD's |
|
| 100 |
+// lifecycle. |
|
| 101 |
+func (fd *FD) Disown() int {
|
|
| 102 |
+ value := fd.raw |
|
| 103 |
+ fdtrace.ForgetFD(value) |
|
| 101 | 104 |
fd.raw = -1 |
| 102 | 105 |
|
| 103 | 106 |
runtime.SetFinalizer(fd, nil) |
| ... | ... |
@@ -129,5 +121,45 @@ func (fd *FD) File(name string) *os.File {
|
| 129 | 129 |
return nil |
| 130 | 130 |
} |
| 131 | 131 |
|
| 132 |
- return os.NewFile(uintptr(fd.disown()), name) |
|
| 132 |
+ return os.NewFile(uintptr(fd.Disown()), name) |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+// ObjGetTyped wraps [ObjGet] with a readlink call to extract the type of the |
|
| 136 |
+// underlying bpf object. |
|
| 137 |
+func ObjGetTyped(attr *ObjGetAttr) (*FD, ObjType, error) {
|
|
| 138 |
+ fd, err := ObjGet(attr) |
|
| 139 |
+ if err != nil {
|
|
| 140 |
+ return nil, 0, err |
|
| 141 |
+ } |
|
| 142 |
+ |
|
| 143 |
+ typ, err := readType(fd) |
|
| 144 |
+ if err != nil {
|
|
| 145 |
+ _ = fd.Close() |
|
| 146 |
+ return nil, 0, fmt.Errorf("reading fd type: %w", err)
|
|
| 147 |
+ } |
|
| 148 |
+ |
|
| 149 |
+ return fd, typ, nil |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// readType returns the bpf object type of the file descriptor by calling |
|
| 153 |
+// readlink(3). Returns an error if the file descriptor does not represent a bpf |
|
| 154 |
+// object. |
|
| 155 |
+func readType(fd *FD) (ObjType, error) {
|
|
| 156 |
+ s, err := os.Readlink(filepath.Join("/proc/self/fd/", fd.String()))
|
|
| 157 |
+ if err != nil {
|
|
| 158 |
+ return 0, fmt.Errorf("readlink fd %d: %w", fd.Int(), err)
|
|
| 159 |
+ } |
|
| 160 |
+ |
|
| 161 |
+ s = strings.TrimPrefix(s, "anon_inode:") |
|
| 162 |
+ |
|
| 163 |
+ switch s {
|
|
| 164 |
+ case "bpf-map": |
|
| 165 |
+ return BPF_TYPE_MAP, nil |
|
| 166 |
+ case "bpf-prog": |
|
| 167 |
+ return BPF_TYPE_PROG, nil |
|
| 168 |
+ case "bpf-link": |
|
| 169 |
+ return BPF_TYPE_LINK, nil |
|
| 170 |
+ } |
|
| 171 |
+ |
|
| 172 |
+ return 0, fmt.Errorf("unknown type %s of fd %d", s, fd.Int())
|
|
| 133 | 173 |
} |
| 134 | 174 |
deleted file mode 100644 |
| ... | ... |
@@ -1,93 +0,0 @@ |
| 1 |
-package sys |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bytes" |
|
| 5 |
- "fmt" |
|
| 6 |
- "runtime" |
|
| 7 |
- "sync" |
|
| 8 |
-) |
|
| 9 |
- |
|
| 10 |
-// OnLeakFD controls tracing [FD] lifetime to detect resources that are not |
|
| 11 |
-// closed by Close(). |
|
| 12 |
-// |
|
| 13 |
-// If fn is not nil, tracing is enabled for all FDs created going forward. fn is |
|
| 14 |
-// invoked for all FDs that are closed by the garbage collector instead of an |
|
| 15 |
-// explicit Close() by a caller. Calling OnLeakFD twice with a non-nil fn |
|
| 16 |
-// (without disabling tracing in the meantime) will cause a panic. |
|
| 17 |
-// |
|
| 18 |
-// If fn is nil, tracing will be disabled. Any FDs that have not been closed are |
|
| 19 |
-// considered to be leaked, fn will be invoked for them, and the process will be |
|
| 20 |
-// terminated. |
|
| 21 |
-// |
|
| 22 |
-// fn will be invoked at most once for every unique sys.FD allocation since a |
|
| 23 |
-// runtime.Frames can only be unwound once. |
|
| 24 |
-func OnLeakFD(fn func(*runtime.Frames)) {
|
|
| 25 |
- // Enable leak tracing if new fn is provided. |
|
| 26 |
- if fn != nil {
|
|
| 27 |
- if onLeakFD != nil {
|
|
| 28 |
- panic("OnLeakFD called twice with non-nil fn")
|
|
| 29 |
- } |
|
| 30 |
- |
|
| 31 |
- onLeakFD = fn |
|
| 32 |
- return |
|
| 33 |
- } |
|
| 34 |
- |
|
| 35 |
- // fn is nil past this point. |
|
| 36 |
- |
|
| 37 |
- if onLeakFD == nil {
|
|
| 38 |
- return |
|
| 39 |
- } |
|
| 40 |
- |
|
| 41 |
- // Call onLeakFD for all open fds. |
|
| 42 |
- if fs := flushFrames(); len(fs) != 0 {
|
|
| 43 |
- for _, f := range fs {
|
|
| 44 |
- onLeakFD(f) |
|
| 45 |
- } |
|
| 46 |
- } |
|
| 47 |
- |
|
| 48 |
- onLeakFD = nil |
|
| 49 |
-} |
|
| 50 |
- |
|
| 51 |
-var onLeakFD func(*runtime.Frames) |
|
| 52 |
- |
|
| 53 |
-// fds is a registry of all file descriptors wrapped into sys.fds that were |
|
| 54 |
-// created while an fd tracer was active. |
|
| 55 |
-var fds sync.Map // map[int]*runtime.Frames |
|
| 56 |
- |
|
| 57 |
-// flushFrames removes all elements from fds and returns them as a slice. This |
|
| 58 |
-// deals with the fact that a runtime.Frames can only be unwound once using |
|
| 59 |
-// Next(). |
|
| 60 |
-func flushFrames() []*runtime.Frames {
|
|
| 61 |
- var frames []*runtime.Frames |
|
| 62 |
- fds.Range(func(key, value any) bool {
|
|
| 63 |
- frames = append(frames, value.(*runtime.Frames)) |
|
| 64 |
- fds.Delete(key) |
|
| 65 |
- return true |
|
| 66 |
- }) |
|
| 67 |
- return frames |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-func callersFrames() *runtime.Frames {
|
|
| 71 |
- c := make([]uintptr, 32) |
|
| 72 |
- |
|
| 73 |
- // Skip runtime.Callers and this function. |
|
| 74 |
- i := runtime.Callers(2, c) |
|
| 75 |
- if i == 0 {
|
|
| 76 |
- return nil |
|
| 77 |
- } |
|
| 78 |
- |
|
| 79 |
- return runtime.CallersFrames(c) |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-// FormatFrames formats a runtime.Frames as a human-readable string. |
|
| 83 |
-func FormatFrames(fs *runtime.Frames) string {
|
|
| 84 |
- var b bytes.Buffer |
|
| 85 |
- for {
|
|
| 86 |
- f, more := fs.Next() |
|
| 87 |
- b.WriteString(fmt.Sprintf("\t%s+%#x\n\t\t%s:%d\n", f.Function, f.PC-f.Entry, f.File, f.Line))
|
|
| 88 |
- if !more {
|
|
| 89 |
- break |
|
| 90 |
- } |
|
| 91 |
- } |
|
| 92 |
- return b.String() |
|
| 93 |
-} |
| 94 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,53 +0,0 @@ |
| 1 |
-// Code generated by "stringer -type MapFlags"; DO NOT EDIT. |
|
| 2 |
- |
|
| 3 |
-package sys |
|
| 4 |
- |
|
| 5 |
-import "strconv" |
|
| 6 |
- |
|
| 7 |
-func _() {
|
|
| 8 |
- // An "invalid array index" compiler error signifies that the constant values have changed. |
|
| 9 |
- // Re-run the stringer command to generate them again. |
|
| 10 |
- var x [1]struct{}
|
|
| 11 |
- _ = x[BPF_F_NO_PREALLOC-1] |
|
| 12 |
- _ = x[BPF_F_NO_COMMON_LRU-2] |
|
| 13 |
- _ = x[BPF_F_NUMA_NODE-4] |
|
| 14 |
- _ = x[BPF_F_RDONLY-8] |
|
| 15 |
- _ = x[BPF_F_WRONLY-16] |
|
| 16 |
- _ = x[BPF_F_STACK_BUILD_ID-32] |
|
| 17 |
- _ = x[BPF_F_ZERO_SEED-64] |
|
| 18 |
- _ = x[BPF_F_RDONLY_PROG-128] |
|
| 19 |
- _ = x[BPF_F_WRONLY_PROG-256] |
|
| 20 |
- _ = x[BPF_F_CLONE-512] |
|
| 21 |
- _ = x[BPF_F_MMAPABLE-1024] |
|
| 22 |
- _ = x[BPF_F_PRESERVE_ELEMS-2048] |
|
| 23 |
- _ = x[BPF_F_INNER_MAP-4096] |
|
| 24 |
- _ = x[BPF_F_LINK-8192] |
|
| 25 |
- _ = x[BPF_F_PATH_FD-16384] |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-const _MapFlags_name = "BPF_F_NO_PREALLOCBPF_F_NO_COMMON_LRUBPF_F_NUMA_NODEBPF_F_RDONLYBPF_F_WRONLYBPF_F_STACK_BUILD_IDBPF_F_ZERO_SEEDBPF_F_RDONLY_PROGBPF_F_WRONLY_PROGBPF_F_CLONEBPF_F_MMAPABLEBPF_F_PRESERVE_ELEMSBPF_F_INNER_MAPBPF_F_LINKBPF_F_PATH_FD" |
|
| 29 |
- |
|
| 30 |
-var _MapFlags_map = map[MapFlags]string{
|
|
| 31 |
- 1: _MapFlags_name[0:17], |
|
| 32 |
- 2: _MapFlags_name[17:36], |
|
| 33 |
- 4: _MapFlags_name[36:51], |
|
| 34 |
- 8: _MapFlags_name[51:63], |
|
| 35 |
- 16: _MapFlags_name[63:75], |
|
| 36 |
- 32: _MapFlags_name[75:95], |
|
| 37 |
- 64: _MapFlags_name[95:110], |
|
| 38 |
- 128: _MapFlags_name[110:127], |
|
| 39 |
- 256: _MapFlags_name[127:144], |
|
| 40 |
- 512: _MapFlags_name[144:155], |
|
| 41 |
- 1024: _MapFlags_name[155:169], |
|
| 42 |
- 2048: _MapFlags_name[169:189], |
|
| 43 |
- 4096: _MapFlags_name[189:204], |
|
| 44 |
- 8192: _MapFlags_name[204:214], |
|
| 45 |
- 16384: _MapFlags_name[214:227], |
|
| 46 |
-} |
|
| 47 |
- |
|
| 48 |
-func (i MapFlags) String() string {
|
|
| 49 |
- if str, ok := _MapFlags_map[i]; ok {
|
|
| 50 |
- return str |
|
| 51 |
- } |
|
| 52 |
- return "MapFlags(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
| 53 |
-} |
| 54 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,65 @@ |
| 0 |
+package sys |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "os" |
|
| 6 |
+ "path/filepath" |
|
| 7 |
+ "runtime" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/cilium/ebpf/internal/linux" |
|
| 10 |
+ "github.com/cilium/ebpf/internal/unix" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func Pin(currentPath, newPath string, fd *FD) error {
|
|
| 14 |
+ if newPath == "" {
|
|
| 15 |
+ return errors.New("given pinning path cannot be empty")
|
|
| 16 |
+ } |
|
| 17 |
+ if currentPath == newPath {
|
|
| 18 |
+ return nil |
|
| 19 |
+ } |
|
| 20 |
+ |
|
| 21 |
+ fsType, err := linux.FSType(filepath.Dir(newPath)) |
|
| 22 |
+ if err != nil {
|
|
| 23 |
+ return err |
|
| 24 |
+ } |
|
| 25 |
+ if fsType != unix.BPF_FS_MAGIC {
|
|
| 26 |
+ return fmt.Errorf("%s is not on a bpf filesystem", newPath)
|
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ defer runtime.KeepAlive(fd) |
|
| 30 |
+ |
|
| 31 |
+ if currentPath == "" {
|
|
| 32 |
+ return ObjPin(&ObjPinAttr{
|
|
| 33 |
+ Pathname: NewStringPointer(newPath), |
|
| 34 |
+ BpfFd: fd.Uint(), |
|
| 35 |
+ }) |
|
| 36 |
+ } |
|
| 37 |
+ |
|
| 38 |
+ // Renameat2 is used instead of os.Rename to disallow the new path replacing |
|
| 39 |
+ // an existing path. |
|
| 40 |
+ err = unix.Renameat2(unix.AT_FDCWD, currentPath, unix.AT_FDCWD, newPath, unix.RENAME_NOREPLACE) |
|
| 41 |
+ if err == nil {
|
|
| 42 |
+ // Object is now moved to the new pinning path. |
|
| 43 |
+ return nil |
|
| 44 |
+ } |
|
| 45 |
+ if !os.IsNotExist(err) {
|
|
| 46 |
+ return fmt.Errorf("unable to move pinned object to new path %v: %w", newPath, err)
|
|
| 47 |
+ } |
|
| 48 |
+ // Internal state not in sync with the file system so let's fix it. |
|
| 49 |
+ return ObjPin(&ObjPinAttr{
|
|
| 50 |
+ Pathname: NewStringPointer(newPath), |
|
| 51 |
+ BpfFd: fd.Uint(), |
|
| 52 |
+ }) |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+func Unpin(pinnedPath string) error {
|
|
| 56 |
+ if pinnedPath == "" {
|
|
| 57 |
+ return nil |
|
| 58 |
+ } |
|
| 59 |
+ err := os.Remove(pinnedPath) |
|
| 60 |
+ if err == nil || os.IsNotExist(err) {
|
|
| 61 |
+ return nil |
|
| 62 |
+ } |
|
| 63 |
+ return err |
|
| 64 |
+} |
| ... | ... |
@@ -11,13 +11,13 @@ func NewPointer(ptr unsafe.Pointer) Pointer {
|
| 11 | 11 |
return Pointer{ptr: ptr}
|
| 12 | 12 |
} |
| 13 | 13 |
|
| 14 |
-// NewSlicePointer creates a 64-bit pointer from a byte slice. |
|
| 15 |
-func NewSlicePointer(buf []byte) Pointer {
|
|
| 14 |
+// NewSlicePointer creates a 64-bit pointer from a slice. |
|
| 15 |
+func NewSlicePointer[T comparable](buf []T) Pointer {
|
|
| 16 | 16 |
if len(buf) == 0 {
|
| 17 | 17 |
return Pointer{}
|
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 |
- return Pointer{ptr: unsafe.Pointer(&buf[0])}
|
|
| 20 |
+ return Pointer{ptr: unsafe.Pointer(unsafe.SliceData(buf))}
|
|
| 21 | 21 |
} |
| 22 | 22 |
|
| 23 | 23 |
// NewSlicePointerLen creates a 64-bit pointer from a byte slice. |
| ... | ... |
@@ -2,7 +2,6 @@ package sys |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"runtime" |
| 5 |
- "syscall" |
|
| 6 | 5 |
"unsafe" |
| 7 | 6 |
|
| 8 | 7 |
"github.com/cilium/ebpf/internal/unix" |
| ... | ... |
@@ -11,7 +10,7 @@ import ( |
| 11 | 11 |
// ENOTSUPP is a Linux internal error code that has leaked into UAPI. |
| 12 | 12 |
// |
| 13 | 13 |
// It is not the same as ENOTSUP or EOPNOTSUPP. |
| 14 |
-const ENOTSUPP = syscall.Errno(524) |
|
| 14 |
+const ENOTSUPP = unix.Errno(524) |
|
| 15 | 15 |
|
| 16 | 16 |
// BPF wraps SYS_BPF. |
| 17 | 17 |
// |
| ... | ... |
@@ -133,12 +132,12 @@ func ObjInfo(fd *FD, info Info) error {
|
| 133 | 133 |
|
| 134 | 134 |
// BPFObjName is a null-terminated string made up of |
| 135 | 135 |
// 'A-Za-z0-9_' characters. |
| 136 |
-type ObjName [unix.BPF_OBJ_NAME_LEN]byte |
|
| 136 |
+type ObjName [BPF_OBJ_NAME_LEN]byte |
|
| 137 | 137 |
|
| 138 | 138 |
// NewObjName truncates the result if it is too long. |
| 139 | 139 |
func NewObjName(name string) ObjName {
|
| 140 | 140 |
var result ObjName |
| 141 |
- copy(result[:unix.BPF_OBJ_NAME_LEN-1], name) |
|
| 141 |
+ copy(result[:BPF_OBJ_NAME_LEN-1], name) |
|
| 142 | 142 |
return result |
| 143 | 143 |
} |
| 144 | 144 |
|
| ... | ... |
@@ -160,29 +159,6 @@ type BTFID uint32 |
| 160 | 160 |
// TypeID identifies a type in a BTF blob. |
| 161 | 161 |
type TypeID uint32 |
| 162 | 162 |
|
| 163 |
-// MapFlags control map behaviour. |
|
| 164 |
-type MapFlags uint32 |
|
| 165 |
- |
|
| 166 |
-//go:generate go run golang.org/x/tools/cmd/stringer@latest -type MapFlags |
|
| 167 |
- |
|
| 168 |
-const ( |
|
| 169 |
- BPF_F_NO_PREALLOC MapFlags = 1 << iota |
|
| 170 |
- BPF_F_NO_COMMON_LRU |
|
| 171 |
- BPF_F_NUMA_NODE |
|
| 172 |
- BPF_F_RDONLY |
|
| 173 |
- BPF_F_WRONLY |
|
| 174 |
- BPF_F_STACK_BUILD_ID |
|
| 175 |
- BPF_F_ZERO_SEED |
|
| 176 |
- BPF_F_RDONLY_PROG |
|
| 177 |
- BPF_F_WRONLY_PROG |
|
| 178 |
- BPF_F_CLONE |
|
| 179 |
- BPF_F_MMAPABLE |
|
| 180 |
- BPF_F_PRESERVE_ELEMS |
|
| 181 |
- BPF_F_INNER_MAP |
|
| 182 |
- BPF_F_LINK |
|
| 183 |
- BPF_F_PATH_FD |
|
| 184 |
-) |
|
| 185 |
- |
|
| 186 | 163 |
// Flags used by bpf_mprog. |
| 187 | 164 |
const ( |
| 188 | 165 |
BPF_F_REPLACE = 1 << (iota + 2) |
| ... | ... |
@@ -192,12 +168,22 @@ const ( |
| 192 | 192 |
BPF_F_LINK_MPROG = 1 << 13 // aka BPF_F_LINK |
| 193 | 193 |
) |
| 194 | 194 |
|
| 195 |
-// wrappedErrno wraps syscall.Errno to prevent direct comparisons with |
|
| 195 |
+// Flags used by BPF_PROG_LOAD. |
|
| 196 |
+const ( |
|
| 197 |
+ BPF_F_SLEEPABLE = 1 << 4 |
|
| 198 |
+ BPF_F_XDP_HAS_FRAGS = 1 << 5 |
|
| 199 |
+ BPF_F_XDP_DEV_BOUND_ONLY = 1 << 6 |
|
| 200 |
+) |
|
| 201 |
+ |
|
| 202 |
+const BPF_TAG_SIZE = 8 |
|
| 203 |
+const BPF_OBJ_NAME_LEN = 16 |
|
| 204 |
+ |
|
| 205 |
+// wrappedErrno wraps [unix.Errno] to prevent direct comparisons with |
|
| 196 | 206 |
// syscall.E* or unix.E* constants. |
| 197 | 207 |
// |
| 198 | 208 |
// You should never export an error of this type. |
| 199 | 209 |
type wrappedErrno struct {
|
| 200 |
- syscall.Errno |
|
| 210 |
+ unix.Errno |
|
| 201 | 211 |
} |
| 202 | 212 |
|
| 203 | 213 |
func (we wrappedErrno) Unwrap() error {
|
| ... | ... |
@@ -213,10 +199,10 @@ func (we wrappedErrno) Error() string {
|
| 213 | 213 |
|
| 214 | 214 |
type syscallError struct {
|
| 215 | 215 |
error |
| 216 |
- errno syscall.Errno |
|
| 216 |
+ errno unix.Errno |
|
| 217 | 217 |
} |
| 218 | 218 |
|
| 219 |
-func Error(err error, errno syscall.Errno) error {
|
|
| 219 |
+func Error(err error, errno unix.Errno) error {
|
|
| 220 | 220 |
return &syscallError{err, errno}
|
| 221 | 221 |
} |
| 222 | 222 |
|
| ... | ... |
@@ -6,6 +6,176 @@ import ( |
| 6 | 6 |
"unsafe" |
| 7 | 7 |
) |
| 8 | 8 |
|
| 9 |
+const ( |
|
| 10 |
+ BPF_ADJ_ROOM_ENCAP_L2_MASK = 255 |
|
| 11 |
+ BPF_ADJ_ROOM_ENCAP_L2_SHIFT = 56 |
|
| 12 |
+ BPF_ANY = 0 |
|
| 13 |
+ BPF_CSUM_LEVEL_DEC = 2 |
|
| 14 |
+ BPF_CSUM_LEVEL_INC = 1 |
|
| 15 |
+ BPF_CSUM_LEVEL_QUERY = 0 |
|
| 16 |
+ BPF_CSUM_LEVEL_RESET = 3 |
|
| 17 |
+ BPF_EXIST = 2 |
|
| 18 |
+ BPF_FIB_LKUP_RET_BLACKHOLE = 1 |
|
| 19 |
+ BPF_FIB_LKUP_RET_FRAG_NEEDED = 8 |
|
| 20 |
+ BPF_FIB_LKUP_RET_FWD_DISABLED = 5 |
|
| 21 |
+ BPF_FIB_LKUP_RET_NOT_FWDED = 4 |
|
| 22 |
+ BPF_FIB_LKUP_RET_NO_NEIGH = 7 |
|
| 23 |
+ BPF_FIB_LKUP_RET_NO_SRC_ADDR = 9 |
|
| 24 |
+ BPF_FIB_LKUP_RET_PROHIBIT = 3 |
|
| 25 |
+ BPF_FIB_LKUP_RET_SUCCESS = 0 |
|
| 26 |
+ BPF_FIB_LKUP_RET_UNREACHABLE = 2 |
|
| 27 |
+ BPF_FIB_LKUP_RET_UNSUPP_LWT = 6 |
|
| 28 |
+ BPF_FIB_LOOKUP_DIRECT = 1 |
|
| 29 |
+ BPF_FIB_LOOKUP_MARK = 32 |
|
| 30 |
+ BPF_FIB_LOOKUP_OUTPUT = 2 |
|
| 31 |
+ BPF_FIB_LOOKUP_SKIP_NEIGH = 4 |
|
| 32 |
+ BPF_FIB_LOOKUP_SRC = 16 |
|
| 33 |
+ BPF_FIB_LOOKUP_TBID = 8 |
|
| 34 |
+ BPF_FLOW_DISSECTOR_F_PARSE_1ST_FRAG = 1 |
|
| 35 |
+ BPF_FLOW_DISSECTOR_F_STOP_AT_ENCAP = 4 |
|
| 36 |
+ BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL = 2 |
|
| 37 |
+ BPF_F_ADJ_ROOM_DECAP_L3_IPV4 = 128 |
|
| 38 |
+ BPF_F_ADJ_ROOM_DECAP_L3_IPV6 = 256 |
|
| 39 |
+ BPF_F_ADJ_ROOM_ENCAP_L2_ETH = 64 |
|
| 40 |
+ BPF_F_ADJ_ROOM_ENCAP_L3_IPV4 = 2 |
|
| 41 |
+ BPF_F_ADJ_ROOM_ENCAP_L3_IPV6 = 4 |
|
| 42 |
+ BPF_F_ADJ_ROOM_ENCAP_L4_GRE = 8 |
|
| 43 |
+ BPF_F_ADJ_ROOM_ENCAP_L4_UDP = 16 |
|
| 44 |
+ BPF_F_ADJ_ROOM_FIXED_GSO = 1 |
|
| 45 |
+ BPF_F_ADJ_ROOM_NO_CSUM_RESET = 32 |
|
| 46 |
+ BPF_F_BPRM_SECUREEXEC = 1 |
|
| 47 |
+ BPF_F_BROADCAST = 8 |
|
| 48 |
+ BPF_F_CLONE = 512 |
|
| 49 |
+ BPF_F_CTXLEN_MASK = 4503595332403200 |
|
| 50 |
+ BPF_F_CURRENT_CPU = 4294967295 |
|
| 51 |
+ BPF_F_CURRENT_NETNS = 18446744073709551615 |
|
| 52 |
+ BPF_F_DONT_FRAGMENT = 4 |
|
| 53 |
+ BPF_F_EXCLUDE_INGRESS = 16 |
|
| 54 |
+ BPF_F_FAST_STACK_CMP = 512 |
|
| 55 |
+ BPF_F_GET_BRANCH_RECORDS_SIZE = 1 |
|
| 56 |
+ BPF_F_HDR_FIELD_MASK = 15 |
|
| 57 |
+ BPF_F_INDEX_MASK = 4294967295 |
|
| 58 |
+ BPF_F_INGRESS = 1 |
|
| 59 |
+ BPF_F_INNER_MAP = 4096 |
|
| 60 |
+ BPF_F_INVALIDATE_HASH = 2 |
|
| 61 |
+ BPF_F_KPROBE_MULTI_RETURN = 1 |
|
| 62 |
+ BPF_F_LINK = 8192 |
|
| 63 |
+ BPF_F_LOCK = 4 |
|
| 64 |
+ BPF_F_MARK_ENFORCE = 64 |
|
| 65 |
+ BPF_F_MARK_MANGLED_0 = 32 |
|
| 66 |
+ BPF_F_MMAPABLE = 1024 |
|
| 67 |
+ BPF_F_NEIGH = 2 |
|
| 68 |
+ BPF_F_NEXTHOP = 8 |
|
| 69 |
+ BPF_F_NO_COMMON_LRU = 2 |
|
| 70 |
+ BPF_F_NO_PREALLOC = 1 |
|
| 71 |
+ BPF_F_NO_TUNNEL_KEY = 16 |
|
| 72 |
+ BPF_F_NO_USER_CONV = 262144 |
|
| 73 |
+ BPF_F_NUMA_NODE = 4 |
|
| 74 |
+ BPF_F_PATH_FD = 16384 |
|
| 75 |
+ BPF_F_PEER = 4 |
|
| 76 |
+ BPF_F_PRESERVE_ELEMS = 2048 |
|
| 77 |
+ BPF_F_PSEUDO_HDR = 16 |
|
| 78 |
+ BPF_F_RDONLY = 8 |
|
| 79 |
+ BPF_F_RDONLY_PROG = 128 |
|
| 80 |
+ BPF_F_RECOMPUTE_CSUM = 1 |
|
| 81 |
+ BPF_F_REUSE_STACKID = 1024 |
|
| 82 |
+ BPF_F_SEGV_ON_FAULT = 131072 |
|
| 83 |
+ BPF_F_SEQ_NUMBER = 8 |
|
| 84 |
+ BPF_F_SKIP_FIELD_MASK = 255 |
|
| 85 |
+ BPF_F_STACK_BUILD_ID = 32 |
|
| 86 |
+ BPF_F_SYSCTL_BASE_NAME = 1 |
|
| 87 |
+ BPF_F_TIMER_ABS = 1 |
|
| 88 |
+ BPF_F_TIMER_CPU_PIN = 2 |
|
| 89 |
+ BPF_F_TOKEN_FD = 65536 |
|
| 90 |
+ BPF_F_TUNINFO_FLAGS = 16 |
|
| 91 |
+ BPF_F_TUNINFO_IPV6 = 1 |
|
| 92 |
+ BPF_F_UPROBE_MULTI_RETURN = 1 |
|
| 93 |
+ BPF_F_USER_BUILD_ID = 2048 |
|
| 94 |
+ BPF_F_USER_STACK = 256 |
|
| 95 |
+ BPF_F_VTYPE_BTF_OBJ_FD = 32768 |
|
| 96 |
+ BPF_F_WRONLY = 16 |
|
| 97 |
+ BPF_F_WRONLY_PROG = 256 |
|
| 98 |
+ BPF_F_ZERO_CSUM_TX = 2 |
|
| 99 |
+ BPF_F_ZERO_SEED = 64 |
|
| 100 |
+ BPF_LOAD_HDR_OPT_TCP_SYN = 1 |
|
| 101 |
+ BPF_LOCAL_STORAGE_GET_F_CREATE = 1 |
|
| 102 |
+ BPF_MAX_LOOPS = 8388608 |
|
| 103 |
+ BPF_MAX_TRAMP_LINKS = 38 |
|
| 104 |
+ BPF_NOEXIST = 1 |
|
| 105 |
+ BPF_RB_AVAIL_DATA = 0 |
|
| 106 |
+ BPF_RB_CONS_POS = 2 |
|
| 107 |
+ BPF_RB_FORCE_WAKEUP = 2 |
|
| 108 |
+ BPF_RB_NO_WAKEUP = 1 |
|
| 109 |
+ BPF_RB_PROD_POS = 3 |
|
| 110 |
+ BPF_RB_RING_SIZE = 1 |
|
| 111 |
+ BPF_REG_0 = 0 |
|
| 112 |
+ BPF_REG_1 = 1 |
|
| 113 |
+ BPF_REG_10 = 10 |
|
| 114 |
+ BPF_REG_2 = 2 |
|
| 115 |
+ BPF_REG_3 = 3 |
|
| 116 |
+ BPF_REG_4 = 4 |
|
| 117 |
+ BPF_REG_5 = 5 |
|
| 118 |
+ BPF_REG_6 = 6 |
|
| 119 |
+ BPF_REG_7 = 7 |
|
| 120 |
+ BPF_REG_8 = 8 |
|
| 121 |
+ BPF_REG_9 = 9 |
|
| 122 |
+ BPF_RINGBUF_BUSY_BIT = 2147483648 |
|
| 123 |
+ BPF_RINGBUF_DISCARD_BIT = 1073741824 |
|
| 124 |
+ BPF_RINGBUF_HDR_SZ = 8 |
|
| 125 |
+ BPF_SKB_CLOCK_MONOTONIC = 1 |
|
| 126 |
+ BPF_SKB_CLOCK_REALTIME = 0 |
|
| 127 |
+ BPF_SKB_CLOCK_TAI = 2 |
|
| 128 |
+ BPF_SKB_TSTAMP_DELIVERY_MONO = 1 |
|
| 129 |
+ BPF_SKB_TSTAMP_UNSPEC = 0 |
|
| 130 |
+ BPF_SK_LOOKUP_F_NO_REUSEPORT = 2 |
|
| 131 |
+ BPF_SK_LOOKUP_F_REPLACE = 1 |
|
| 132 |
+ BPF_SK_STORAGE_GET_F_CREATE = 1 |
|
| 133 |
+ BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB = 4 |
|
| 134 |
+ BPF_SOCK_OPS_ALL_CB_FLAGS = 127 |
|
| 135 |
+ BPF_SOCK_OPS_BASE_RTT = 7 |
|
| 136 |
+ BPF_SOCK_OPS_HDR_OPT_LEN_CB = 14 |
|
| 137 |
+ BPF_SOCK_OPS_NEEDS_ECN = 6 |
|
| 138 |
+ BPF_SOCK_OPS_PARSE_ALL_HDR_OPT_CB_FLAG = 16 |
|
| 139 |
+ BPF_SOCK_OPS_PARSE_HDR_OPT_CB = 13 |
|
| 140 |
+ BPF_SOCK_OPS_PARSE_UNKNOWN_HDR_OPT_CB_FLAG = 32 |
|
| 141 |
+ BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB = 5 |
|
| 142 |
+ BPF_SOCK_OPS_RETRANS_CB = 9 |
|
| 143 |
+ BPF_SOCK_OPS_RETRANS_CB_FLAG = 2 |
|
| 144 |
+ BPF_SOCK_OPS_RTO_CB = 8 |
|
| 145 |
+ BPF_SOCK_OPS_RTO_CB_FLAG = 1 |
|
| 146 |
+ BPF_SOCK_OPS_RTT_CB = 12 |
|
| 147 |
+ BPF_SOCK_OPS_RTT_CB_FLAG = 8 |
|
| 148 |
+ BPF_SOCK_OPS_RWND_INIT = 2 |
|
| 149 |
+ BPF_SOCK_OPS_STATE_CB = 10 |
|
| 150 |
+ BPF_SOCK_OPS_STATE_CB_FLAG = 4 |
|
| 151 |
+ BPF_SOCK_OPS_TCP_CONNECT_CB = 3 |
|
| 152 |
+ BPF_SOCK_OPS_TCP_LISTEN_CB = 11 |
|
| 153 |
+ BPF_SOCK_OPS_TIMEOUT_INIT = 1 |
|
| 154 |
+ BPF_SOCK_OPS_VOID = 0 |
|
| 155 |
+ BPF_SOCK_OPS_WRITE_HDR_OPT_CB = 15 |
|
| 156 |
+ BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG = 64 |
|
| 157 |
+ BPF_TASK_ITER_ALL_PROCS = 0 |
|
| 158 |
+ BPF_TASK_ITER_ALL_THREADS = 1 |
|
| 159 |
+ BPF_TASK_ITER_PROC_THREADS = 2 |
|
| 160 |
+ BPF_TCP_BOUND_INACTIVE = 13 |
|
| 161 |
+ BPF_TCP_CLOSE = 7 |
|
| 162 |
+ BPF_TCP_CLOSE_WAIT = 8 |
|
| 163 |
+ BPF_TCP_CLOSING = 11 |
|
| 164 |
+ BPF_TCP_ESTABLISHED = 1 |
|
| 165 |
+ BPF_TCP_FIN_WAIT1 = 4 |
|
| 166 |
+ BPF_TCP_FIN_WAIT2 = 5 |
|
| 167 |
+ BPF_TCP_LAST_ACK = 9 |
|
| 168 |
+ BPF_TCP_LISTEN = 10 |
|
| 169 |
+ BPF_TCP_MAX_STATES = 14 |
|
| 170 |
+ BPF_TCP_NEW_SYN_RECV = 12 |
|
| 171 |
+ BPF_TCP_SYN_RECV = 3 |
|
| 172 |
+ BPF_TCP_SYN_SENT = 2 |
|
| 173 |
+ BPF_TCP_TIME_WAIT = 6 |
|
| 174 |
+ BPF_WRITE_HDR_TCP_CURRENT_MSS = 1 |
|
| 175 |
+ BPF_WRITE_HDR_TCP_SYNACK_COOKIE = 2 |
|
| 176 |
+ BPF_XFRM_STATE_OPTS_SZ = 36 |
|
| 177 |
+) |
|
| 178 |
+ |
|
| 9 | 179 |
type AdjRoomMode uint32 |
| 10 | 180 |
|
| 11 | 181 |
const ( |
| ... | ... |
@@ -72,7 +242,8 @@ const ( |
| 72 | 72 |
BPF_CGROUP_UNIX_GETSOCKNAME AttachType = 53 |
| 73 | 73 |
BPF_NETKIT_PRIMARY AttachType = 54 |
| 74 | 74 |
BPF_NETKIT_PEER AttachType = 55 |
| 75 |
- __MAX_BPF_ATTACH_TYPE AttachType = 56 |
|
| 75 |
+ BPF_TRACE_KPROBE_SESSION AttachType = 56 |
|
| 76 |
+ __MAX_BPF_ATTACH_TYPE AttachType = 57 |
|
| 76 | 77 |
) |
| 77 | 78 |
|
| 78 | 79 |
type Cmd uint32 |
| ... | ... |
@@ -115,6 +286,8 @@ const ( |
| 115 | 115 |
BPF_ITER_CREATE Cmd = 33 |
| 116 | 116 |
BPF_LINK_DETACH Cmd = 34 |
| 117 | 117 |
BPF_PROG_BIND_MAP Cmd = 35 |
| 118 |
+ BPF_TOKEN_CREATE Cmd = 36 |
|
| 119 |
+ __MAX_BPF_CMD Cmd = 37 |
|
| 118 | 120 |
) |
| 119 | 121 |
|
| 120 | 122 |
type FunctionId uint32 |
| ... | ... |
@@ -359,7 +532,8 @@ const ( |
| 359 | 359 |
BPF_LINK_TYPE_TCX LinkType = 11 |
| 360 | 360 |
BPF_LINK_TYPE_UPROBE_MULTI LinkType = 12 |
| 361 | 361 |
BPF_LINK_TYPE_NETKIT LinkType = 13 |
| 362 |
- __MAX_BPF_LINK_TYPE LinkType = 14 |
|
| 362 |
+ BPF_LINK_TYPE_SOCKMAP LinkType = 14 |
|
| 363 |
+ __MAX_BPF_LINK_TYPE LinkType = 15 |
|
| 363 | 364 |
) |
| 364 | 365 |
|
| 365 | 366 |
type MapType uint32 |
| ... | ... |
@@ -400,6 +574,17 @@ const ( |
| 400 | 400 |
BPF_MAP_TYPE_BLOOM_FILTER MapType = 30 |
| 401 | 401 |
BPF_MAP_TYPE_USER_RINGBUF MapType = 31 |
| 402 | 402 |
BPF_MAP_TYPE_CGRP_STORAGE MapType = 32 |
| 403 |
+ BPF_MAP_TYPE_ARENA MapType = 33 |
|
| 404 |
+ __MAX_BPF_MAP_TYPE MapType = 34 |
|
| 405 |
+) |
|
| 406 |
+ |
|
| 407 |
+type ObjType uint32 |
|
| 408 |
+ |
|
| 409 |
+const ( |
|
| 410 |
+ BPF_TYPE_UNSPEC ObjType = 0 |
|
| 411 |
+ BPF_TYPE_PROG ObjType = 1 |
|
| 412 |
+ BPF_TYPE_MAP ObjType = 2 |
|
| 413 |
+ BPF_TYPE_LINK ObjType = 3 |
|
| 403 | 414 |
) |
| 404 | 415 |
|
| 405 | 416 |
type PerfEventType uint32 |
| ... | ... |
@@ -450,6 +635,7 @@ const ( |
| 450 | 450 |
BPF_PROG_TYPE_SK_LOOKUP ProgType = 30 |
| 451 | 451 |
BPF_PROG_TYPE_SYSCALL ProgType = 31 |
| 452 | 452 |
BPF_PROG_TYPE_NETFILTER ProgType = 32 |
| 453 |
+ __MAX_BPF_PROG_TYPE ProgType = 33 |
|
| 453 | 454 |
) |
| 454 | 455 |
|
| 455 | 456 |
type RetCode uint32 |
| ... | ... |
@@ -537,7 +723,7 @@ type MapInfo struct {
|
| 537 | 537 |
KeySize uint32 |
| 538 | 538 |
ValueSize uint32 |
| 539 | 539 |
MaxEntries uint32 |
| 540 |
- MapFlags MapFlags |
|
| 540 |
+ MapFlags uint32 |
|
| 541 | 541 |
Name ObjName |
| 542 | 542 |
Ifindex uint32 |
| 543 | 543 |
BtfVmlinuxValueTypeId TypeID |
| ... | ... |
@@ -546,7 +732,7 @@ type MapInfo struct {
|
| 546 | 546 |
BtfId uint32 |
| 547 | 547 |
BtfKeyTypeId TypeID |
| 548 | 548 |
BtfValueTypeId TypeID |
| 549 |
- _ [4]byte |
|
| 549 |
+ BtfVmlinuxId uint32 |
|
| 550 | 550 |
MapExtra uint64 |
| 551 | 551 |
} |
| 552 | 552 |
|
| ... | ... |
@@ -556,7 +742,7 @@ type ProgInfo struct {
|
| 556 | 556 |
Tag [8]uint8 |
| 557 | 557 |
JitedProgLen uint32 |
| 558 | 558 |
XlatedProgLen uint32 |
| 559 |
- JitedProgInsns uint64 |
|
| 559 |
+ JitedProgInsns Pointer |
|
| 560 | 560 |
XlatedProgInsns Pointer |
| 561 | 561 |
LoadTime uint64 |
| 562 | 562 |
CreatedByUid uint32 |
| ... | ... |
@@ -569,15 +755,15 @@ type ProgInfo struct {
|
| 569 | 569 |
NetnsIno uint64 |
| 570 | 570 |
NrJitedKsyms uint32 |
| 571 | 571 |
NrJitedFuncLens uint32 |
| 572 |
- JitedKsyms uint64 |
|
| 573 |
- JitedFuncLens uint64 |
|
| 572 |
+ JitedKsyms Pointer |
|
| 573 |
+ JitedFuncLens Pointer |
|
| 574 | 574 |
BtfId BTFID |
| 575 | 575 |
FuncInfoRecSize uint32 |
| 576 | 576 |
FuncInfo Pointer |
| 577 | 577 |
NrFuncInfo uint32 |
| 578 | 578 |
NrLineInfo uint32 |
| 579 | 579 |
LineInfo Pointer |
| 580 |
- JitedLineInfo uint64 |
|
| 580 |
+ JitedLineInfo Pointer |
|
| 581 | 581 |
NrJitedLineInfo uint32 |
| 582 | 582 |
LineInfoRecSize uint32 |
| 583 | 583 |
JitedLineInfoRecSize uint32 |
| ... | ... |
@@ -643,6 +829,8 @@ type BtfLoadAttr struct {
|
| 643 | 643 |
BtfLogSize uint32 |
| 644 | 644 |
BtfLogLevel uint32 |
| 645 | 645 |
BtfLogTrueSize uint32 |
| 646 |
+ BtfFlags uint32 |
|
| 647 |
+ BtfTokenFd int32 |
|
| 646 | 648 |
} |
| 647 | 649 |
|
| 648 | 650 |
func BtfLoad(attr *BtfLoadAttr) (*FD, error) {
|
| ... | ... |
@@ -886,7 +1074,7 @@ type MapCreateAttr struct {
|
| 886 | 886 |
KeySize uint32 |
| 887 | 887 |
ValueSize uint32 |
| 888 | 888 |
MaxEntries uint32 |
| 889 |
- MapFlags MapFlags |
|
| 889 |
+ MapFlags uint32 |
|
| 890 | 890 |
InnerMapFd uint32 |
| 891 | 891 |
NumaNode uint32 |
| 892 | 892 |
MapName ObjName |
| ... | ... |
@@ -896,6 +1084,8 @@ type MapCreateAttr struct {
|
| 896 | 896 |
BtfValueTypeId TypeID |
| 897 | 897 |
BtfVmlinuxValueTypeId TypeID |
| 898 | 898 |
MapExtra uint64 |
| 899 |
+ ValueTypeBtfObjFd int32 |
|
| 900 |
+ MapTokenFd int32 |
|
| 899 | 901 |
} |
| 900 | 902 |
|
| 901 | 903 |
func MapCreate(attr *MapCreateAttr) (*FD, error) {
|
| ... | ... |
@@ -1189,6 +1379,8 @@ type ProgLoadAttr struct {
|
| 1189 | 1189 |
CoreRelos Pointer |
| 1190 | 1190 |
CoreReloRecSize uint32 |
| 1191 | 1191 |
LogTrueSize uint32 |
| 1192 |
+ ProgTokenFd int32 |
|
| 1193 |
+ _ [4]byte |
|
| 1192 | 1194 |
} |
| 1193 | 1195 |
|
| 1194 | 1196 |
func ProgLoad(attr *ProgLoadAttr) (*FD, error) {
|
| ... | ... |
@@ -1246,6 +1438,7 @@ type RawTracepointOpenAttr struct {
|
| 1246 | 1246 |
Name Pointer |
| 1247 | 1247 |
ProgFd uint32 |
| 1248 | 1248 |
_ [4]byte |
| 1249 |
+ Cookie uint64 |
|
| 1249 | 1250 |
} |
| 1250 | 1251 |
|
| 1251 | 1252 |
func RawTracepointOpen(attr *RawTracepointOpenAttr) (*FD, error) {
|
| ... | ... |
@@ -1287,19 +1480,20 @@ type KprobeLinkInfo struct {
|
| 1287 | 1287 |
Offset uint32 |
| 1288 | 1288 |
Addr uint64 |
| 1289 | 1289 |
Missed uint64 |
| 1290 |
- _ [8]byte |
|
| 1290 |
+ Cookie uint64 |
|
| 1291 | 1291 |
} |
| 1292 | 1292 |
|
| 1293 | 1293 |
type KprobeMultiLinkInfo struct {
|
| 1294 |
- Type LinkType |
|
| 1295 |
- Id LinkID |
|
| 1296 |
- ProgId uint32 |
|
| 1297 |
- _ [4]byte |
|
| 1298 |
- Addrs Pointer |
|
| 1299 |
- Count uint32 |
|
| 1300 |
- Flags uint32 |
|
| 1301 |
- Missed uint64 |
|
| 1302 |
- _ [24]byte |
|
| 1294 |
+ Type LinkType |
|
| 1295 |
+ Id LinkID |
|
| 1296 |
+ ProgId uint32 |
|
| 1297 |
+ _ [4]byte |
|
| 1298 |
+ Addrs Pointer |
|
| 1299 |
+ Count uint32 |
|
| 1300 |
+ Flags uint32 |
|
| 1301 |
+ Missed uint64 |
|
| 1302 |
+ Cookies uint64 |
|
| 1303 |
+ _ [16]byte |
|
| 1303 | 1304 |
} |
| 1304 | 1305 |
|
| 1305 | 1306 |
type NetNsLinkInfo struct {
|
| ... | ... |
@@ -51,12 +51,12 @@ func SyscallOutput(dst any, size int) Buffer {
|
| 51 | 51 |
// |
| 52 | 52 |
// Returns the number of copied bytes. |
| 53 | 53 |
func (b Buffer) CopyTo(dst []byte) int {
|
| 54 |
- return copy(dst, b.unsafeBytes()) |
|
| 54 |
+ return copy(dst, b.Bytes()) |
|
| 55 | 55 |
} |
| 56 | 56 |
|
| 57 | 57 |
// AppendTo appends the buffer onto dst. |
| 58 | 58 |
func (b Buffer) AppendTo(dst []byte) []byte {
|
| 59 |
- return append(dst, b.unsafeBytes()...) |
|
| 59 |
+ return append(dst, b.Bytes()...) |
|
| 60 | 60 |
} |
| 61 | 61 |
|
| 62 | 62 |
// Pointer returns the location where a syscall should write. |
| ... | ... |
@@ -72,10 +72,12 @@ func (b Buffer) Unmarshal(data any) error {
|
| 72 | 72 |
return nil |
| 73 | 73 |
} |
| 74 | 74 |
|
| 75 |
- return Unmarshal(data, b.unsafeBytes()) |
|
| 75 |
+ return Unmarshal(data, b.Bytes()) |
|
| 76 | 76 |
} |
| 77 | 77 |
|
| 78 |
-func (b Buffer) unsafeBytes() []byte {
|
|
| 78 |
+// Bytes returns the buffer as a byte slice. Returns nil if the Buffer was |
|
| 79 |
+// created using UnsafeBuffer or by zero-copy unmarshaling. |
|
| 80 |
+func (b Buffer) Bytes() []byte {
|
|
| 79 | 81 |
if b.size == syscallPointerOnly {
|
| 80 | 82 |
return nil |
| 81 | 83 |
} |
| 82 | 84 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,103 @@ |
| 0 |
+package fdtrace |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "os" |
|
| 6 |
+ "runtime" |
|
| 7 |
+ "sync" |
|
| 8 |
+ "sync/atomic" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+// foundLeak is atomic since the GC may collect objects in parallel. |
|
| 12 |
+var foundLeak atomic.Bool |
|
| 13 |
+ |
|
| 14 |
+func onLeakFD(fs *runtime.Frames) {
|
|
| 15 |
+ foundLeak.Store(true) |
|
| 16 |
+ fmt.Fprintln(os.Stderr, "leaked fd created at:") |
|
| 17 |
+ fmt.Fprintln(os.Stderr, formatFrames(fs)) |
|
| 18 |
+} |
|
| 19 |
+ |
|
| 20 |
+// fds is a registry of all file descriptors wrapped into sys.fds that were |
|
| 21 |
+// created while an fd tracer was active. |
|
| 22 |
+var fds *sync.Map // map[int]*runtime.Frames |
|
| 23 |
+ |
|
| 24 |
+// TraceFD associates raw with the current execution stack. |
|
| 25 |
+// |
|
| 26 |
+// skip controls how many entries of the stack the function should skip. |
|
| 27 |
+func TraceFD(raw int, skip int) {
|
|
| 28 |
+ if fds == nil {
|
|
| 29 |
+ return |
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ // Attempt to store the caller's stack for the given fd value. |
|
| 33 |
+ // Panic if fds contains an existing stack for the fd. |
|
| 34 |
+ old, exist := fds.LoadOrStore(raw, callersFrames(skip)) |
|
| 35 |
+ if exist {
|
|
| 36 |
+ f := old.(*runtime.Frames) |
|
| 37 |
+ panic(fmt.Sprintf("found existing stack for fd %d:\n%s", raw, formatFrames(f)))
|
|
| 38 |
+ } |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// ForgetFD removes any existing association for raw. |
|
| 42 |
+func ForgetFD(raw int) {
|
|
| 43 |
+ if fds != nil {
|
|
| 44 |
+ fds.Delete(raw) |
|
| 45 |
+ } |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// LeakFD indicates that raw was leaked. |
|
| 49 |
+// |
|
| 50 |
+// Calling the function with a value that was not passed to [TraceFD] before |
|
| 51 |
+// is undefined. |
|
| 52 |
+func LeakFD(raw int) {
|
|
| 53 |
+ if fds == nil {
|
|
| 54 |
+ return |
|
| 55 |
+ } |
|
| 56 |
+ |
|
| 57 |
+ // Invoke the fd leak callback. Calls LoadAndDelete to guarantee the callback |
|
| 58 |
+ // is invoked at most once for one sys.FD allocation, runtime.Frames can only |
|
| 59 |
+ // be unwound once. |
|
| 60 |
+ f, ok := fds.LoadAndDelete(raw) |
|
| 61 |
+ if ok {
|
|
| 62 |
+ onLeakFD(f.(*runtime.Frames)) |
|
| 63 |
+ } |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+// flushFrames removes all elements from fds and returns them as a slice. This |
|
| 67 |
+// deals with the fact that a runtime.Frames can only be unwound once using |
|
| 68 |
+// Next(). |
|
| 69 |
+func flushFrames() []*runtime.Frames {
|
|
| 70 |
+ var frames []*runtime.Frames |
|
| 71 |
+ fds.Range(func(key, value any) bool {
|
|
| 72 |
+ frames = append(frames, value.(*runtime.Frames)) |
|
| 73 |
+ fds.Delete(key) |
|
| 74 |
+ return true |
|
| 75 |
+ }) |
|
| 76 |
+ return frames |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+func callersFrames(skip int) *runtime.Frames {
|
|
| 80 |
+ c := make([]uintptr, 32) |
|
| 81 |
+ |
|
| 82 |
+ // Skip runtime.Callers and this function. |
|
| 83 |
+ i := runtime.Callers(skip+2, c) |
|
| 84 |
+ if i == 0 {
|
|
| 85 |
+ return nil |
|
| 86 |
+ } |
|
| 87 |
+ |
|
| 88 |
+ return runtime.CallersFrames(c) |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// formatFrames formats a runtime.Frames as a human-readable string. |
|
| 92 |
+func formatFrames(fs *runtime.Frames) string {
|
|
| 93 |
+ var b bytes.Buffer |
|
| 94 |
+ for {
|
|
| 95 |
+ f, more := fs.Next() |
|
| 96 |
+ b.WriteString(fmt.Sprintf("\t%s+%#x\n\t\t%s:%d\n", f.Function, f.PC-f.Entry, f.File, f.Line))
|
|
| 97 |
+ if !more {
|
|
| 98 |
+ break |
|
| 99 |
+ } |
|
| 100 |
+ } |
|
| 101 |
+ return b.String() |
|
| 102 |
+} |
| 0 | 103 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,31 @@ |
| 0 |
+package fdtrace |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "os" |
|
| 4 |
+ "sync" |
|
| 5 |
+) |
|
| 6 |
+ |
|
| 7 |
+type testingM interface {
|
|
| 8 |
+ Run() int |
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+// TestMain runs m with fd tracing enabled. |
|
| 12 |
+// |
|
| 13 |
+// The function calls [os.Exit] and does not return. |
|
| 14 |
+func TestMain(m testingM) {
|
|
| 15 |
+ fds = new(sync.Map) |
|
| 16 |
+ |
|
| 17 |
+ ret := m.Run() |
|
| 18 |
+ |
|
| 19 |
+ if fs := flushFrames(); len(fs) != 0 {
|
|
| 20 |
+ for _, f := range fs {
|
|
| 21 |
+ onLeakFD(f) |
|
| 22 |
+ } |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ if foundLeak.Load() {
|
|
| 26 |
+ ret = 99 |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ os.Exit(ret) |
|
| 30 |
+} |
| ... | ... |
@@ -12,6 +12,7 @@ import ( |
| 12 | 12 |
"syscall" |
| 13 | 13 |
|
| 14 | 14 |
"github.com/cilium/ebpf/internal" |
| 15 |
+ "github.com/cilium/ebpf/internal/linux" |
|
| 15 | 16 |
"github.com/cilium/ebpf/internal/unix" |
| 16 | 17 |
) |
| 17 | 18 |
|
| ... | ... |
@@ -112,6 +113,10 @@ func sanitizeTracefsPath(path ...string) (string, error) {
|
| 112 | 112 |
// but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted. |
| 113 | 113 |
// The available tracefs paths will depends on distribution choices. |
| 114 | 114 |
var getTracefsPath = sync.OnceValues(func() (string, error) {
|
| 115 |
+ if !internal.OnLinux {
|
|
| 116 |
+ return "", fmt.Errorf("tracefs: %w", internal.ErrNotSupportedOnOS)
|
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 115 | 119 |
for _, p := range []struct {
|
| 116 | 120 |
path string |
| 117 | 121 |
fsType int64 |
| ... | ... |
@@ -121,7 +126,7 @@ var getTracefsPath = sync.OnceValues(func() (string, error) {
|
| 121 | 121 |
// RHEL/CentOS |
| 122 | 122 |
{"/sys/kernel/debug/tracing", unix.DEBUGFS_MAGIC},
|
| 123 | 123 |
} {
|
| 124 |
- if fsType, err := internal.FSType(p.path); err == nil && fsType == p.fsType {
|
|
| 124 |
+ if fsType, err := linux.FSType(p.path); err == nil && fsType == p.fsType {
|
|
| 125 | 125 |
return p.path, nil |
| 126 | 126 |
} |
| 127 | 127 |
} |
| ... | ... |
@@ -213,7 +218,10 @@ func NewEvent(args ProbeArgs) (*Event, error) {
|
| 213 | 213 |
if err == nil {
|
| 214 | 214 |
return nil, fmt.Errorf("trace event %s/%s: %w", args.Group, eventName, os.ErrExist)
|
| 215 | 215 |
} |
| 216 |
- if err != nil && !errors.Is(err, os.ErrNotExist) {
|
|
| 216 |
+ if errors.Is(err, unix.EINVAL) {
|
|
| 217 |
+ return nil, fmt.Errorf("trace event %s/%s: %w (unknown symbol?)", args.Group, eventName, err)
|
|
| 218 |
+ } |
|
| 219 |
+ if !errors.Is(err, os.ErrNotExist) {
|
|
| 217 | 220 |
return nil, fmt.Errorf("checking trace event %s/%s: %w", args.Group, eventName, err)
|
| 218 | 221 |
} |
| 219 | 222 |
|
| 220 | 223 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+package unix |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "syscall" |
|
| 4 |
+ |
|
| 5 |
+ linux "golang.org/x/sys/unix" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+type Errno = syscall.Errno |
|
| 9 |
+ |
|
| 10 |
+const ( |
|
| 11 |
+ E2BIG = linux.E2BIG |
|
| 12 |
+ EACCES = linux.EACCES |
|
| 13 |
+ EAGAIN = linux.EAGAIN |
|
| 14 |
+ EBADF = linux.EBADF |
|
| 15 |
+ EEXIST = linux.EEXIST |
|
| 16 |
+ EFAULT = linux.EFAULT |
|
| 17 |
+ EILSEQ = linux.EILSEQ |
|
| 18 |
+ EINTR = linux.EINTR |
|
| 19 |
+ EINVAL = linux.EINVAL |
|
| 20 |
+ ENODEV = linux.ENODEV |
|
| 21 |
+ ENOENT = linux.ENOENT |
|
| 22 |
+ ENOSPC = linux.ENOSPC |
|
| 23 |
+ EOPNOTSUPP = linux.EOPNOTSUPP |
|
| 24 |
+ EPERM = linux.EPERM |
|
| 25 |
+ EPOLLIN = linux.EPOLLIN |
|
| 26 |
+ ESRCH = linux.ESRCH |
|
| 27 |
+ ESTALE = linux.ESTALE |
|
| 28 |
+) |
| 0 | 29 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 0 |
+//go:build !linux && !windows |
|
| 1 |
+ |
|
| 2 |
+package unix |
|
| 3 |
+ |
|
| 4 |
+import "syscall" |
|
| 5 |
+ |
|
| 6 |
+type Errno = syscall.Errno |
|
| 7 |
+ |
|
| 8 |
+// Errnos are distinct and non-zero. |
|
| 9 |
+const ( |
|
| 10 |
+ E2BIG Errno = iota + 1 |
|
| 11 |
+ EACCES |
|
| 12 |
+ EAGAIN |
|
| 13 |
+ EBADF |
|
| 14 |
+ EEXIST |
|
| 15 |
+ EFAULT |
|
| 16 |
+ EILSEQ |
|
| 17 |
+ EINTR |
|
| 18 |
+ EINVAL |
|
| 19 |
+ ENODEV |
|
| 20 |
+ ENOENT |
|
| 21 |
+ ENOSPC |
|
| 22 |
+ ENOTSUP |
|
| 23 |
+ ENOTSUPP |
|
| 24 |
+ EOPNOTSUPP |
|
| 25 |
+ EPERM |
|
| 26 |
+ ESRCH |
|
| 27 |
+ ESTALE |
|
| 28 |
+) |
| 0 | 29 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,59 @@ |
| 0 |
+// Code generated by "stringer -type=Errno -tags=windows -output=errno_string_windows.go"; DO NOT EDIT. |
|
| 1 |
+ |
|
| 2 |
+package unix |
|
| 3 |
+ |
|
| 4 |
+import "strconv" |
|
| 5 |
+ |
|
| 6 |
+func _() {
|
|
| 7 |
+ // An "invalid array index" compiler error signifies that the constant values have changed. |
|
| 8 |
+ // Re-run the stringer command to generate them again. |
|
| 9 |
+ var x [1]struct{}
|
|
| 10 |
+ _ = x[EPERM-1] |
|
| 11 |
+ _ = x[ENOENT-2] |
|
| 12 |
+ _ = x[ESRCH-3] |
|
| 13 |
+ _ = x[EINTR-4] |
|
| 14 |
+ _ = x[E2BIG-7] |
|
| 15 |
+ _ = x[EBADF-9] |
|
| 16 |
+ _ = x[EAGAIN-11] |
|
| 17 |
+ _ = x[EACCES-13] |
|
| 18 |
+ _ = x[EFAULT-14] |
|
| 19 |
+ _ = x[EEXIST-17] |
|
| 20 |
+ _ = x[ENODEV-19] |
|
| 21 |
+ _ = x[EINVAL-22] |
|
| 22 |
+ _ = x[ENOSPC-28] |
|
| 23 |
+ _ = x[EILSEQ-42] |
|
| 24 |
+ _ = x[ENOTSUP-129] |
|
| 25 |
+ _ = x[EOPNOTSUPP-130] |
|
| 26 |
+ _ = x[ENOTSUPP-536870912] |
|
| 27 |
+ _ = x[ESTALE-536870913] |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+const _Errno_name = "EPERMENOENTESRCHEINTRE2BIGEBADFEAGAINEACCESEFAULTEEXISTENODEVEINVALENOSPCEILSEQENOTSUPEOPNOTSUPPENOTSUPPESTALE" |
|
| 31 |
+ |
|
| 32 |
+var _Errno_map = map[Errno]string{
|
|
| 33 |
+ 1: _Errno_name[0:5], |
|
| 34 |
+ 2: _Errno_name[5:11], |
|
| 35 |
+ 3: _Errno_name[11:16], |
|
| 36 |
+ 4: _Errno_name[16:21], |
|
| 37 |
+ 7: _Errno_name[21:26], |
|
| 38 |
+ 9: _Errno_name[26:31], |
|
| 39 |
+ 11: _Errno_name[31:37], |
|
| 40 |
+ 13: _Errno_name[37:43], |
|
| 41 |
+ 14: _Errno_name[43:49], |
|
| 42 |
+ 17: _Errno_name[49:55], |
|
| 43 |
+ 19: _Errno_name[55:61], |
|
| 44 |
+ 22: _Errno_name[61:67], |
|
| 45 |
+ 28: _Errno_name[67:73], |
|
| 46 |
+ 42: _Errno_name[73:79], |
|
| 47 |
+ 129: _Errno_name[79:86], |
|
| 48 |
+ 130: _Errno_name[86:96], |
|
| 49 |
+ 536870912: _Errno_name[96:104], |
|
| 50 |
+ 536870913: _Errno_name[104:110], |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+func (i Errno) String() string {
|
|
| 54 |
+ if str, ok := _Errno_map[i]; ok {
|
|
| 55 |
+ return str |
|
| 56 |
+ } |
|
| 57 |
+ return "Errno(" + strconv.FormatInt(int64(i), 10) + ")"
|
|
| 58 |
+} |
| 0 | 59 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,78 @@ |
| 0 |
+package unix |
|
| 1 |
+ |
|
| 2 |
+// The code in this file is derived from syscall_unix.go in the Go source code, |
|
| 3 |
+// licensed under the MIT license. |
|
| 4 |
+ |
|
| 5 |
+import ( |
|
| 6 |
+ "errors" |
|
| 7 |
+ "os" |
|
| 8 |
+ "syscall" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=Errno -tags=windows -output=errno_string_windows.go |
|
| 12 |
+ |
|
| 13 |
+// Windows specific constants for Unix errnos. |
|
| 14 |
+// |
|
| 15 |
+// The values do not always match Linux, for example EILSEQ and EOPNOTSUPP. |
|
| 16 |
+// |
|
| 17 |
+// See https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170 |
|
| 18 |
+const ( |
|
| 19 |
+ EPERM Errno = 1 |
|
| 20 |
+ ENOENT Errno = 2 |
|
| 21 |
+ ESRCH Errno = 3 |
|
| 22 |
+ EINTR Errno = 4 |
|
| 23 |
+ E2BIG Errno = 7 |
|
| 24 |
+ EBADF Errno = 9 |
|
| 25 |
+ EAGAIN Errno = 11 |
|
| 26 |
+ EACCES Errno = 13 |
|
| 27 |
+ EFAULT Errno = 14 |
|
| 28 |
+ EEXIST Errno = 17 |
|
| 29 |
+ ENODEV Errno = 19 |
|
| 30 |
+ EINVAL Errno = 22 |
|
| 31 |
+ ENFILE Errno = 23 |
|
| 32 |
+ EMFILE Errno = 24 |
|
| 33 |
+ ENOSPC Errno = 28 |
|
| 34 |
+ ENOSYS Errno = 40 |
|
| 35 |
+ ENOTEMPTY Errno = 41 |
|
| 36 |
+ EILSEQ Errno = 42 |
|
| 37 |
+ ENOTSUP Errno = 129 |
|
| 38 |
+ EOPNOTSUPP Errno = 130 |
|
| 39 |
+ ETIMEDOUT Errno = 138 |
|
| 40 |
+ EWOULDBLOCK Errno = 140 |
|
| 41 |
+) |
|
| 42 |
+ |
|
| 43 |
+// These constants do not exist on Windows and therefore have a non-zero |
|
| 44 |
+// dummy value. |
|
| 45 |
+const ( |
|
| 46 |
+ ENOTSUPP Errno = Errno(syscall.APPLICATION_ERROR) + iota |
|
| 47 |
+ ESTALE |
|
| 48 |
+) |
|
| 49 |
+ |
|
| 50 |
+// Errno is a Windows compatibility shim for Unix errnos. |
|
| 51 |
+type Errno uintptr |
|
| 52 |
+ |
|
| 53 |
+func (e Errno) Error() string {
|
|
| 54 |
+ return e.String() |
|
| 55 |
+} |
|
| 56 |
+ |
|
| 57 |
+func (e Errno) Is(target error) bool {
|
|
| 58 |
+ switch target {
|
|
| 59 |
+ case os.ErrPermission: |
|
| 60 |
+ return e == EACCES || e == EPERM |
|
| 61 |
+ case os.ErrExist: |
|
| 62 |
+ return e == EEXIST || e == ENOTEMPTY |
|
| 63 |
+ case os.ErrNotExist: |
|
| 64 |
+ return e == ENOENT |
|
| 65 |
+ case errors.ErrUnsupported: |
|
| 66 |
+ return e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP |
|
| 67 |
+ } |
|
| 68 |
+ return false |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func (e Errno) Temporary() bool {
|
|
| 72 |
+ return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout() |
|
| 73 |
+} |
|
| 74 |
+ |
|
| 75 |
+func (e Errno) Timeout() bool {
|
|
| 76 |
+ return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT |
|
| 77 |
+} |
| 0 | 78 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,23 @@ |
| 0 |
+package unix |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "runtime" |
|
| 5 |
+ "strings" |
|
| 6 |
+ |
|
| 7 |
+ "github.com/cilium/ebpf/internal" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// errNonLinux returns an error which wraps [internal.ErrNotSupportedOnOS] and |
|
| 11 |
+// includes the name of the calling function. |
|
| 12 |
+func errNonLinux() error {
|
|
| 13 |
+ name := "unknown" |
|
| 14 |
+ pc, _, _, ok := runtime.Caller(1) |
|
| 15 |
+ if ok {
|
|
| 16 |
+ name = runtime.FuncForPC(pc).Name() |
|
| 17 |
+ if pos := strings.LastIndexByte(name, '.'); pos != -1 {
|
|
| 18 |
+ name = name[pos+1:] |
|
| 19 |
+ } |
|
| 20 |
+ } |
|
| 21 |
+ return fmt.Errorf("unix: %s: %w", name, internal.ErrNotSupportedOnOS)
|
|
| 22 |
+} |
| 0 | 11 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,19 @@ |
| 0 |
+package unix |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "syscall" |
|
| 4 |
+ |
|
| 5 |
+ "golang.org/x/sys/windows" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+func BytePtrFromString(s string) (*byte, error) {
|
|
| 9 |
+ p, err := windows.BytePtrFromString(s) |
|
| 10 |
+ if err == syscall.EINVAL {
|
|
| 11 |
+ err = EINVAL |
|
| 12 |
+ } |
|
| 13 |
+ return p, err |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+func ByteSliceToString(s []byte) string {
|
|
| 17 |
+ return windows.ByteSliceToString(s) |
|
| 18 |
+} |
| ... | ... |
@@ -9,26 +9,6 @@ import ( |
| 9 | 9 |
) |
| 10 | 10 |
|
| 11 | 11 |
const ( |
| 12 |
- ENOENT = linux.ENOENT |
|
| 13 |
- EEXIST = linux.EEXIST |
|
| 14 |
- EAGAIN = linux.EAGAIN |
|
| 15 |
- ENOSPC = linux.ENOSPC |
|
| 16 |
- EINVAL = linux.EINVAL |
|
| 17 |
- EPOLLIN = linux.EPOLLIN |
|
| 18 |
- EINTR = linux.EINTR |
|
| 19 |
- EPERM = linux.EPERM |
|
| 20 |
- ESRCH = linux.ESRCH |
|
| 21 |
- ENODEV = linux.ENODEV |
|
| 22 |
- EBADF = linux.EBADF |
|
| 23 |
- E2BIG = linux.E2BIG |
|
| 24 |
- EFAULT = linux.EFAULT |
|
| 25 |
- EACCES = linux.EACCES |
|
| 26 |
- EILSEQ = linux.EILSEQ |
|
| 27 |
- EOPNOTSUPP = linux.EOPNOTSUPP |
|
| 28 |
- ESTALE = linux.ESTALE |
|
| 29 |
-) |
|
| 30 |
- |
|
| 31 |
-const ( |
|
| 32 | 12 |
BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC |
| 33 | 13 |
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE |
| 34 | 14 |
BPF_F_RDONLY = linux.BPF_F_RDONLY |
| ... | ... |
@@ -81,15 +61,16 @@ const ( |
| 81 | 81 |
SO_DETACH_BPF = linux.SO_DETACH_BPF |
| 82 | 82 |
SOL_SOCKET = linux.SOL_SOCKET |
| 83 | 83 |
SIGPROF = linux.SIGPROF |
| 84 |
+ SIGUSR1 = linux.SIGUSR1 |
|
| 84 | 85 |
SIG_BLOCK = linux.SIG_BLOCK |
| 85 | 86 |
SIG_UNBLOCK = linux.SIG_UNBLOCK |
| 86 |
- EM_NONE = linux.EM_NONE |
|
| 87 |
- EM_BPF = linux.EM_BPF |
|
| 88 | 87 |
BPF_FS_MAGIC = linux.BPF_FS_MAGIC |
| 89 | 88 |
TRACEFS_MAGIC = linux.TRACEFS_MAGIC |
| 90 | 89 |
DEBUGFS_MAGIC = linux.DEBUGFS_MAGIC |
| 91 | 90 |
BPF_RB_NO_WAKEUP = linux.BPF_RB_NO_WAKEUP |
| 92 | 91 |
BPF_RB_FORCE_WAKEUP = linux.BPF_RB_FORCE_WAKEUP |
| 92 |
+ AF_UNSPEC = linux.AF_UNSPEC |
|
| 93 |
+ IFF_UP = linux.IFF_UP |
|
| 93 | 94 |
) |
| 94 | 95 |
|
| 95 | 96 |
type Statfs_t = linux.Statfs_t |
| ... | ... |
@@ -214,3 +195,7 @@ func SchedSetaffinity(pid int, set *CPUSet) error {
|
| 214 | 214 |
func SchedGetaffinity(pid int, set *CPUSet) error {
|
| 215 | 215 |
return linux.SchedGetaffinity(pid, set) |
| 216 | 216 |
} |
| 217 |
+ |
|
| 218 |
+func Auxv() ([][2]uintptr, error) {
|
|
| 219 |
+ return linux.Auxv() |
|
| 220 |
+} |
| ... | ... |
@@ -3,33 +3,9 @@ |
| 3 | 3 |
package unix |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
- "fmt" |
|
| 7 |
- "runtime" |
|
| 8 | 6 |
"syscall" |
| 9 | 7 |
) |
| 10 | 8 |
|
| 11 |
-var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
|
|
| 12 |
- |
|
| 13 |
-// Errnos are distinct and non-zero. |
|
| 14 |
-const ( |
|
| 15 |
- ENOENT syscall.Errno = iota + 1 |
|
| 16 |
- EEXIST |
|
| 17 |
- EAGAIN |
|
| 18 |
- ENOSPC |
|
| 19 |
- EINVAL |
|
| 20 |
- EINTR |
|
| 21 |
- EPERM |
|
| 22 |
- ESRCH |
|
| 23 |
- ENODEV |
|
| 24 |
- EBADF |
|
| 25 |
- E2BIG |
|
| 26 |
- EFAULT |
|
| 27 |
- EACCES |
|
| 28 |
- EILSEQ |
|
| 29 |
- EOPNOTSUPP |
|
| 30 |
- ESTALE |
|
| 31 |
-) |
|
| 32 |
- |
|
| 33 | 9 |
// Constants are distinct to avoid breaking switch statements. |
| 34 | 10 |
const ( |
| 35 | 11 |
BPF_F_NO_PREALLOC = iota |
| ... | ... |
@@ -84,16 +60,17 @@ const ( |
| 84 | 84 |
SO_DETACH_BPF |
| 85 | 85 |
SOL_SOCKET |
| 86 | 86 |
SIGPROF |
| 87 |
+ SIGUSR1 |
|
| 87 | 88 |
SIG_BLOCK |
| 88 | 89 |
SIG_UNBLOCK |
| 89 |
- EM_NONE |
|
| 90 |
- EM_BPF |
|
| 91 | 90 |
BPF_FS_MAGIC |
| 92 | 91 |
TRACEFS_MAGIC |
| 93 | 92 |
DEBUGFS_MAGIC |
| 94 | 93 |
BPF_RB_NO_WAKEUP |
| 95 | 94 |
BPF_RB_FORCE_WAKEUP |
| 96 | 95 |
BPF_F_LOCK |
| 96 |
+ AF_UNSPEC |
|
| 97 |
+ IFF_UP |
|
| 97 | 98 |
) |
| 98 | 99 |
|
| 99 | 100 |
type Statfs_t struct {
|
| ... | ... |
@@ -136,28 +113,28 @@ type Sigset_t struct {
|
| 136 | 136 |
Val [4]uint64 |
| 137 | 137 |
} |
| 138 | 138 |
|
| 139 |
-func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
|
| 140 |
- return 0, 0, syscall.ENOTSUP |
|
| 139 |
+func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) {
|
|
| 140 |
+ return 0, 0, ENOTSUP |
|
| 141 | 141 |
} |
| 142 | 142 |
|
| 143 | 143 |
func PthreadSigmask(how int, set, oldset *Sigset_t) error {
|
| 144 |
- return errNonLinux |
|
| 144 |
+ return errNonLinux() |
|
| 145 | 145 |
} |
| 146 | 146 |
|
| 147 | 147 |
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
| 148 |
- return -1, errNonLinux |
|
| 148 |
+ return -1, errNonLinux() |
|
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 | 151 |
func IoctlSetInt(fd int, req uint, value int) error {
|
| 152 |
- return errNonLinux |
|
| 152 |
+ return errNonLinux() |
|
| 153 | 153 |
} |
| 154 | 154 |
|
| 155 | 155 |
func Statfs(path string, buf *Statfs_t) error {
|
| 156 |
- return errNonLinux |
|
| 156 |
+ return errNonLinux() |
|
| 157 | 157 |
} |
| 158 | 158 |
|
| 159 | 159 |
func Close(fd int) (err error) {
|
| 160 |
- return errNonLinux |
|
| 160 |
+ return errNonLinux() |
|
| 161 | 161 |
} |
| 162 | 162 |
|
| 163 | 163 |
type EpollEvent struct {
|
| ... | ... |
@@ -167,23 +144,23 @@ type EpollEvent struct {
|
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 | 169 |
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
| 170 |
- return 0, errNonLinux |
|
| 170 |
+ return 0, errNonLinux() |
|
| 171 | 171 |
} |
| 172 | 172 |
|
| 173 | 173 |
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
| 174 |
- return errNonLinux |
|
| 174 |
+ return errNonLinux() |
|
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 | 177 |
func Eventfd(initval uint, flags int) (fd int, err error) {
|
| 178 |
- return 0, errNonLinux |
|
| 178 |
+ return 0, errNonLinux() |
|
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 | 181 |
func Write(fd int, p []byte) (n int, err error) {
|
| 182 |
- return 0, errNonLinux |
|
| 182 |
+ return 0, errNonLinux() |
|
| 183 | 183 |
} |
| 184 | 184 |
|
| 185 | 185 |
func EpollCreate1(flag int) (fd int, err error) {
|
| 186 |
- return 0, errNonLinux |
|
| 186 |
+ return 0, errNonLinux() |
|
| 187 | 187 |
} |
| 188 | 188 |
|
| 189 | 189 |
type PerfEventMmapPage struct {
|
| ... | ... |
@@ -213,15 +190,15 @@ type PerfEventMmapPage struct {
|
| 213 | 213 |
} |
| 214 | 214 |
|
| 215 | 215 |
func SetNonblock(fd int, nonblocking bool) (err error) {
|
| 216 |
- return errNonLinux |
|
| 216 |
+ return errNonLinux() |
|
| 217 | 217 |
} |
| 218 | 218 |
|
| 219 | 219 |
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
| 220 |
- return []byte{}, errNonLinux
|
|
| 220 |
+ return []byte{}, errNonLinux()
|
|
| 221 | 221 |
} |
| 222 | 222 |
|
| 223 | 223 |
func Munmap(b []byte) (err error) {
|
| 224 |
- return errNonLinux |
|
| 224 |
+ return errNonLinux() |
|
| 225 | 225 |
} |
| 226 | 226 |
|
| 227 | 227 |
type PerfEventAttr struct {
|
| ... | ... |
@@ -246,7 +223,7 @@ type PerfEventAttr struct {
|
| 246 | 246 |
} |
| 247 | 247 |
|
| 248 | 248 |
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
|
| 249 |
- return 0, errNonLinux |
|
| 249 |
+ return 0, errNonLinux() |
|
| 250 | 250 |
} |
| 251 | 251 |
|
| 252 | 252 |
type Utsname struct {
|
| ... | ... |
@@ -255,7 +232,7 @@ type Utsname struct {
|
| 255 | 255 |
} |
| 256 | 256 |
|
| 257 | 257 |
func Uname(buf *Utsname) (err error) {
|
| 258 |
- return errNonLinux |
|
| 258 |
+ return errNonLinux() |
|
| 259 | 259 |
} |
| 260 | 260 |
|
| 261 | 261 |
func Getpid() int {
|
| ... | ... |
@@ -267,35 +244,27 @@ func Gettid() int {
|
| 267 | 267 |
} |
| 268 | 268 |
|
| 269 | 269 |
func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) {
|
| 270 |
- return errNonLinux |
|
| 271 |
-} |
|
| 272 |
- |
|
| 273 |
-func BytePtrFromString(s string) (*byte, error) {
|
|
| 274 |
- return nil, errNonLinux |
|
| 275 |
-} |
|
| 276 |
- |
|
| 277 |
-func ByteSliceToString(s []byte) string {
|
|
| 278 |
- return "" |
|
| 270 |
+ return errNonLinux() |
|
| 279 | 271 |
} |
| 280 | 272 |
|
| 281 | 273 |
func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error {
|
| 282 |
- return errNonLinux |
|
| 274 |
+ return errNonLinux() |
|
| 283 | 275 |
} |
| 284 | 276 |
|
| 285 | 277 |
func Prlimit(pid, resource int, new, old *Rlimit) error {
|
| 286 |
- return errNonLinux |
|
| 278 |
+ return errNonLinux() |
|
| 287 | 279 |
} |
| 288 | 280 |
|
| 289 | 281 |
func Open(path string, mode int, perm uint32) (int, error) {
|
| 290 |
- return -1, errNonLinux |
|
| 282 |
+ return -1, errNonLinux() |
|
| 291 | 283 |
} |
| 292 | 284 |
|
| 293 | 285 |
func Fstat(fd int, stat *Stat_t) error {
|
| 294 |
- return errNonLinux |
|
| 286 |
+ return errNonLinux() |
|
| 295 | 287 |
} |
| 296 | 288 |
|
| 297 | 289 |
func SetsockoptInt(fd, level, opt, value int) error {
|
| 298 |
- return errNonLinux |
|
| 290 |
+ return errNonLinux() |
|
| 299 | 291 |
} |
| 300 | 292 |
|
| 301 | 293 |
type CPUSet struct{}
|
| ... | ... |
@@ -303,9 +272,13 @@ type CPUSet struct{}
|
| 303 | 303 |
func (*CPUSet) Set(int) {}
|
| 304 | 304 |
|
| 305 | 305 |
func SchedSetaffinity(pid int, set *CPUSet) error {
|
| 306 |
- return errNonLinux |
|
| 306 |
+ return errNonLinux() |
|
| 307 | 307 |
} |
| 308 | 308 |
|
| 309 | 309 |
func SchedGetaffinity(pid int, set *CPUSet) error {
|
| 310 |
- return errNonLinux |
|
| 310 |
+ return errNonLinux() |
|
| 311 |
+} |
|
| 312 |
+ |
|
| 313 |
+func Auxv() ([][2]uintptr, error) {
|
|
| 314 |
+ return nil, errNonLinux() |
|
| 311 | 315 |
} |
| 312 | 316 |
deleted file mode 100644 |
| ... | ... |
@@ -1,143 +0,0 @@ |
| 1 |
-package internal |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "debug/elf" |
|
| 5 |
- "encoding/binary" |
|
| 6 |
- "errors" |
|
| 7 |
- "fmt" |
|
| 8 |
- "io" |
|
| 9 |
- "math" |
|
| 10 |
- "os" |
|
| 11 |
- |
|
| 12 |
- "github.com/cilium/ebpf/internal/unix" |
|
| 13 |
-) |
|
| 14 |
- |
|
| 15 |
-var ( |
|
| 16 |
- errAuxvNoVDSO = errors.New("no vdso address found in auxv")
|
|
| 17 |
-) |
|
| 18 |
- |
|
| 19 |
-// vdsoVersion returns the LINUX_VERSION_CODE embedded in the vDSO library |
|
| 20 |
-// linked into the current process image. |
|
| 21 |
-func vdsoVersion() (uint32, error) {
|
|
| 22 |
- av, err := newAuxvRuntimeReader() |
|
| 23 |
- if err != nil {
|
|
| 24 |
- return 0, err |
|
| 25 |
- } |
|
| 26 |
- |
|
| 27 |
- defer av.Close() |
|
| 28 |
- |
|
| 29 |
- vdsoAddr, err := vdsoMemoryAddress(av) |
|
| 30 |
- if err != nil {
|
|
| 31 |
- return 0, fmt.Errorf("finding vDSO memory address: %w", err)
|
|
| 32 |
- } |
|
| 33 |
- |
|
| 34 |
- // Use /proc/self/mem rather than unsafe.Pointer tricks. |
|
| 35 |
- mem, err := os.Open("/proc/self/mem")
|
|
| 36 |
- if err != nil {
|
|
| 37 |
- return 0, fmt.Errorf("opening mem: %w", err)
|
|
| 38 |
- } |
|
| 39 |
- defer mem.Close() |
|
| 40 |
- |
|
| 41 |
- // Open ELF at provided memory address, as offset into /proc/self/mem. |
|
| 42 |
- c, err := vdsoLinuxVersionCode(io.NewSectionReader(mem, int64(vdsoAddr), math.MaxInt64)) |
|
| 43 |
- if err != nil {
|
|
| 44 |
- return 0, fmt.Errorf("reading linux version code: %w", err)
|
|
| 45 |
- } |
|
| 46 |
- |
|
| 47 |
- return c, nil |
|
| 48 |
-} |
|
| 49 |
- |
|
| 50 |
-// vdsoMemoryAddress returns the memory address of the vDSO library |
|
| 51 |
-// linked into the current process image. r is an io.Reader into an auxv blob. |
|
| 52 |
-func vdsoMemoryAddress(r auxvPairReader) (uintptr, error) {
|
|
| 53 |
- // Loop through all tag/value pairs in auxv until we find `AT_SYSINFO_EHDR`, |
|
| 54 |
- // the address of a page containing the virtual Dynamic Shared Object (vDSO). |
|
| 55 |
- for {
|
|
| 56 |
- tag, value, err := r.ReadAuxvPair() |
|
| 57 |
- if err != nil {
|
|
| 58 |
- return 0, err |
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- switch tag {
|
|
| 62 |
- case _AT_SYSINFO_EHDR: |
|
| 63 |
- if value != 0 {
|
|
| 64 |
- return uintptr(value), nil |
|
| 65 |
- } |
|
| 66 |
- return 0, fmt.Errorf("invalid vDSO address in auxv")
|
|
| 67 |
- // _AT_NULL is always the last tag/val pair in the aux vector |
|
| 68 |
- // and can be treated like EOF. |
|
| 69 |
- case _AT_NULL: |
|
| 70 |
- return 0, errAuxvNoVDSO |
|
| 71 |
- } |
|
| 72 |
- } |
|
| 73 |
-} |
|
| 74 |
- |
|
| 75 |
-// format described at https://www.man7.org/linux/man-pages/man5/elf.5.html in section 'Notes (Nhdr)' |
|
| 76 |
-type elfNoteHeader struct {
|
|
| 77 |
- NameSize int32 |
|
| 78 |
- DescSize int32 |
|
| 79 |
- Type int32 |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-// vdsoLinuxVersionCode returns the LINUX_VERSION_CODE embedded in |
|
| 83 |
-// the ELF notes section of the binary provided by the reader. |
|
| 84 |
-func vdsoLinuxVersionCode(r io.ReaderAt) (uint32, error) {
|
|
| 85 |
- hdr, err := NewSafeELFFile(r) |
|
| 86 |
- if err != nil {
|
|
| 87 |
- return 0, fmt.Errorf("reading vDSO ELF: %w", err)
|
|
| 88 |
- } |
|
| 89 |
- |
|
| 90 |
- sections := hdr.SectionsByType(elf.SHT_NOTE) |
|
| 91 |
- if len(sections) == 0 {
|
|
| 92 |
- return 0, fmt.Errorf("no note section found in vDSO ELF")
|
|
| 93 |
- } |
|
| 94 |
- |
|
| 95 |
- for _, sec := range sections {
|
|
| 96 |
- sr := sec.Open() |
|
| 97 |
- var n elfNoteHeader |
|
| 98 |
- |
|
| 99 |
- // Read notes until we find one named 'Linux'. |
|
| 100 |
- for {
|
|
| 101 |
- if err := binary.Read(sr, hdr.ByteOrder, &n); err != nil {
|
|
| 102 |
- if errors.Is(err, io.EOF) {
|
|
| 103 |
- // We looked at all the notes in this section |
|
| 104 |
- break |
|
| 105 |
- } |
|
| 106 |
- return 0, fmt.Errorf("reading note header: %w", err)
|
|
| 107 |
- } |
|
| 108 |
- |
|
| 109 |
- // If a note name is defined, it follows the note header. |
|
| 110 |
- var name string |
|
| 111 |
- if n.NameSize > 0 {
|
|
| 112 |
- // Read the note name, aligned to 4 bytes. |
|
| 113 |
- buf := make([]byte, Align(n.NameSize, 4)) |
|
| 114 |
- if err := binary.Read(sr, hdr.ByteOrder, &buf); err != nil {
|
|
| 115 |
- return 0, fmt.Errorf("reading note name: %w", err)
|
|
| 116 |
- } |
|
| 117 |
- |
|
| 118 |
- // Read nul-terminated string. |
|
| 119 |
- name = unix.ByteSliceToString(buf[:n.NameSize]) |
|
| 120 |
- } |
|
| 121 |
- |
|
| 122 |
- // If a note descriptor is defined, it follows the name. |
|
| 123 |
- // It is possible for a note to have a descriptor but not a name. |
|
| 124 |
- if n.DescSize > 0 {
|
|
| 125 |
- // LINUX_VERSION_CODE is a uint32 value. |
|
| 126 |
- if name == "Linux" && n.DescSize == 4 && n.Type == 0 {
|
|
| 127 |
- var version uint32 |
|
| 128 |
- if err := binary.Read(sr, hdr.ByteOrder, &version); err != nil {
|
|
| 129 |
- return 0, fmt.Errorf("reading note descriptor: %w", err)
|
|
| 130 |
- } |
|
| 131 |
- return version, nil |
|
| 132 |
- } |
|
| 133 |
- |
|
| 134 |
- // Discard the note descriptor if it exists but we're not interested in it. |
|
| 135 |
- if _, err := io.CopyN(io.Discard, sr, int64(Align(n.DescSize, 4))); err != nil {
|
|
| 136 |
- return 0, err |
|
| 137 |
- } |
|
| 138 |
- } |
|
| 139 |
- } |
|
| 140 |
- } |
|
| 141 |
- |
|
| 142 |
- return 0, fmt.Errorf("no Linux note in ELF")
|
|
| 143 |
-} |
| ... | ... |
@@ -2,9 +2,6 @@ package internal |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"fmt" |
| 5 |
- "sync" |
|
| 6 |
- |
|
| 7 |
- "github.com/cilium/ebpf/internal/unix" |
|
| 8 | 5 |
) |
| 9 | 6 |
|
| 10 | 7 |
const ( |
| ... | ... |
@@ -78,30 +75,3 @@ func (v Version) Kernel() uint32 {
|
| 78 | 78 |
// each other when overflowing 8 bits. |
| 79 | 79 |
return uint32(uint8(v[0]))<<16 | uint32(uint8(v[1]))<<8 | uint32(uint8(s)) |
| 80 | 80 |
} |
| 81 |
- |
|
| 82 |
-// KernelVersion returns the version of the currently running kernel. |
|
| 83 |
-var KernelVersion = sync.OnceValues(func() (Version, error) {
|
|
| 84 |
- return detectKernelVersion() |
|
| 85 |
-}) |
|
| 86 |
- |
|
| 87 |
-// detectKernelVersion returns the version of the running kernel. |
|
| 88 |
-func detectKernelVersion() (Version, error) {
|
|
| 89 |
- vc, err := vdsoVersion() |
|
| 90 |
- if err != nil {
|
|
| 91 |
- return Version{}, err
|
|
| 92 |
- } |
|
| 93 |
- return NewVersionFromCode(vc), nil |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-// KernelRelease returns the release string of the running kernel. |
|
| 97 |
-// Its format depends on the Linux distribution and corresponds to directory |
|
| 98 |
-// names in /lib/modules by convention. Some examples are 5.15.17-1-lts and |
|
| 99 |
-// 4.19.0-16-amd64. |
|
| 100 |
-func KernelRelease() (string, error) {
|
|
| 101 |
- var uname unix.Utsname |
|
| 102 |
- if err := unix.Uname(&uname); err != nil {
|
|
| 103 |
- return "", fmt.Errorf("uname failed: %w", err)
|
|
| 104 |
- } |
|
| 105 |
- |
|
| 106 |
- return unix.ByteSliceToString(uname.Release[:]), nil |
|
| 107 |
-} |
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
|
| 11 | 11 |
"github.com/cilium/ebpf" |
| 12 | 12 |
"github.com/cilium/ebpf/internal" |
| 13 |
+ "github.com/cilium/ebpf/internal/linux" |
|
| 13 | 14 |
"github.com/cilium/ebpf/internal/sys" |
| 14 | 15 |
"github.com/cilium/ebpf/internal/tracefs" |
| 15 | 16 |
"github.com/cilium/ebpf/internal/unix" |
| ... | ... |
@@ -60,6 +61,9 @@ func (ko *KprobeOptions) cookie() uint64 {
|
| 60 | 60 |
// platform's syscall prefix (e.g. __x64_) to support attaching to syscalls |
| 61 | 61 |
// in a portable fashion. |
| 62 | 62 |
// |
| 63 |
+// On kernels 6.11 and later, setting a kprobe on a nonexistent symbol using |
|
| 64 |
+// tracefs incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist]. |
|
| 65 |
+// |
|
| 63 | 66 |
// The returned Link may implement [PerfEvent]. |
| 64 | 67 |
func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {
|
| 65 | 68 |
k, err := kprobe(symbol, prog, opts, false) |
| ... | ... |
@@ -91,7 +95,7 @@ func Kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error |
| 91 | 91 |
// in a portable fashion. |
| 92 | 92 |
// |
| 93 | 93 |
// On kernels 5.10 and earlier, setting a kretprobe on a nonexistent symbol |
| 94 |
-// incorrectly returns unix.EINVAL instead of os.ErrNotExist. |
|
| 94 |
+// incorrectly returns [unix.EINVAL] instead of [os.ErrNotExist]. |
|
| 95 | 95 |
// |
| 96 | 96 |
// The returned Link may implement [PerfEvent]. |
| 97 | 97 |
func Kretprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions) (Link, error) {
|
| ... | ... |
@@ -169,7 +173,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (* |
| 169 | 169 |
// Use kprobe PMU if the kernel has it available. |
| 170 | 170 |
tp, err := pmuProbe(args) |
| 171 | 171 |
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
|
| 172 |
- if prefix := internal.PlatformPrefix(); prefix != "" {
|
|
| 172 |
+ if prefix := linux.PlatformPrefix(); prefix != "" {
|
|
| 173 | 173 |
args.Symbol = prefix + symbol |
| 174 | 174 |
tp, err = pmuProbe(args) |
| 175 | 175 |
} |
| ... | ... |
@@ -177,7 +181,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (* |
| 177 | 177 |
if err == nil {
|
| 178 | 178 |
return tp, nil |
| 179 | 179 |
} |
| 180 |
- if err != nil && !errors.Is(err, ErrNotSupported) {
|
|
| 180 |
+ if !errors.Is(err, ErrNotSupported) {
|
|
| 181 | 181 |
return nil, fmt.Errorf("creating perf_kprobe PMU (arch-specific fallback for %q): %w", symbol, err)
|
| 182 | 182 |
} |
| 183 | 183 |
|
| ... | ... |
@@ -185,7 +189,7 @@ func kprobe(symbol string, prog *ebpf.Program, opts *KprobeOptions, ret bool) (* |
| 185 | 185 |
args.Symbol = symbol |
| 186 | 186 |
tp, err = tracefsProbe(args) |
| 187 | 187 |
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
|
| 188 |
- if prefix := internal.PlatformPrefix(); prefix != "" {
|
|
| 188 |
+ if prefix := linux.PlatformPrefix(); prefix != "" {
|
|
| 189 | 189 |
args.Symbol = prefix + symbol |
| 190 | 190 |
tp, err = tracefsProbe(args) |
| 191 | 191 |
} |
| ... | ... |
@@ -37,6 +37,14 @@ type KprobeMultiOptions struct {
|
| 37 | 37 |
// Each Cookie is assigned to the Symbol or Address specified at the |
| 38 | 38 |
// corresponding slice index. |
| 39 | 39 |
Cookies []uint64 |
| 40 |
+ |
|
| 41 |
+ // Session must be true when attaching Programs with the |
|
| 42 |
+ // [ebpf.AttachTraceKprobeSession] attach type. |
|
| 43 |
+ // |
|
| 44 |
+ // This makes a Kprobe execute on both function entry and return. The entry |
|
| 45 |
+ // program can share a cookie value with the return program and can decide |
|
| 46 |
+ // whether the return program gets executed. |
|
| 47 |
+ Session bool |
|
| 40 | 48 |
} |
| 41 | 49 |
|
| 42 | 50 |
// KprobeMulti attaches the given eBPF program to the entry point of a given set |
| ... | ... |
@@ -60,7 +68,7 @@ func KprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
|
| 60 | 60 |
// |
| 61 | 61 |
// Requires at least Linux 5.18. |
| 62 | 62 |
func KretprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions) (Link, error) {
|
| 63 |
- return kprobeMulti(prog, opts, unix.BPF_F_KPROBE_MULTI_RETURN) |
|
| 63 |
+ return kprobeMulti(prog, opts, sys.BPF_F_KPROBE_MULTI_RETURN) |
|
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 | 66 |
func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Link, error) {
|
| ... | ... |
@@ -82,9 +90,14 @@ func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Lin |
| 82 | 82 |
return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput)
|
| 83 | 83 |
} |
| 84 | 84 |
|
| 85 |
+ attachType := sys.BPF_TRACE_KPROBE_MULTI |
|
| 86 |
+ if opts.Session {
|
|
| 87 |
+ attachType = sys.BPF_TRACE_KPROBE_SESSION |
|
| 88 |
+ } |
|
| 89 |
+ |
|
| 85 | 90 |
attr := &sys.LinkCreateKprobeMultiAttr{
|
| 86 | 91 |
ProgFd: uint32(prog.FD()), |
| 87 |
- AttachType: sys.BPF_TRACE_KPROBE_MULTI, |
|
| 92 |
+ AttachType: attachType, |
|
| 88 | 93 |
KprobeMultiFlags: flags, |
| 89 | 94 |
} |
| 90 | 95 |
|
| ... | ... |
@@ -103,21 +116,31 @@ func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Lin |
| 103 | 103 |
} |
| 104 | 104 |
|
| 105 | 105 |
fd, err := sys.LinkCreateKprobeMulti(attr) |
| 106 |
+ if err == nil {
|
|
| 107 |
+ return &kprobeMultiLink{RawLink{fd, ""}}, nil
|
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 106 | 110 |
if errors.Is(err, unix.ESRCH) {
|
| 107 | 111 |
return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist)
|
| 108 | 112 |
} |
| 109 |
- if errors.Is(err, unix.EINVAL) {
|
|
| 110 |
- return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err)
|
|
| 111 |
- } |
|
| 112 | 113 |
|
| 113 |
- if err != nil {
|
|
| 114 |
+ if opts.Session {
|
|
| 115 |
+ if haveFeatErr := haveBPFLinkKprobeSession(); haveFeatErr != nil {
|
|
| 116 |
+ return nil, haveFeatErr |
|
| 117 |
+ } |
|
| 118 |
+ } else {
|
|
| 114 | 119 |
if haveFeatErr := haveBPFLinkKprobeMulti(); haveFeatErr != nil {
|
| 115 | 120 |
return nil, haveFeatErr |
| 116 | 121 |
} |
| 117 |
- return nil, err |
|
| 118 | 122 |
} |
| 119 | 123 |
|
| 120 |
- return &kprobeMultiLink{RawLink{fd, ""}}, nil
|
|
| 124 |
+ // Check EINVAL after running feature probes, since it's also returned when |
|
| 125 |
+ // the kernel doesn't support the multi/session attach types. |
|
| 126 |
+ if errors.Is(err, unix.EINVAL) {
|
|
| 127 |
+ return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not %s?)", err, ebpf.AttachType(attachType))
|
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ return nil, err |
|
| 121 | 131 |
} |
| 122 | 132 |
|
| 123 | 133 |
type kprobeMultiLink struct {
|
| ... | ... |
@@ -126,7 +149,7 @@ type kprobeMultiLink struct {
|
| 126 | 126 |
|
| 127 | 127 |
var _ Link = (*kprobeMultiLink)(nil) |
| 128 | 128 |
|
| 129 |
-func (kml *kprobeMultiLink) Update(prog *ebpf.Program) error {
|
|
| 129 |
+func (kml *kprobeMultiLink) Update(_ *ebpf.Program) error {
|
|
| 130 | 130 |
return fmt.Errorf("update kprobe_multi: %w", ErrNotSupported)
|
| 131 | 131 |
} |
| 132 | 132 |
|
| ... | ... |
@@ -149,7 +172,7 @@ func (kml *kprobeMultiLink) Info() (*Info, error) {
|
| 149 | 149 |
}, nil |
| 150 | 150 |
} |
| 151 | 151 |
|
| 152 |
-var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5.18", func() error {
|
|
| 152 |
+var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", func() error {
|
|
| 153 | 153 |
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
| 154 | 154 |
Name: "probe_kpm_link", |
| 155 | 155 |
Type: ebpf.Kprobe, |
| ... | ... |
@@ -188,4 +211,45 @@ var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", "5
|
| 188 | 188 |
fd.Close() |
| 189 | 189 |
|
| 190 | 190 |
return nil |
| 191 |
-}) |
|
| 191 |
+}, "5.18") |
|
| 192 |
+ |
|
| 193 |
+var haveBPFLinkKprobeSession = internal.NewFeatureTest("bpf_link_kprobe_session", func() error {
|
|
| 194 |
+ prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
|
| 195 |
+ Name: "probe_kps_link", |
|
| 196 |
+ Type: ebpf.Kprobe, |
|
| 197 |
+ Instructions: asm.Instructions{
|
|
| 198 |
+ asm.Mov.Imm(asm.R0, 0), |
|
| 199 |
+ asm.Return(), |
|
| 200 |
+ }, |
|
| 201 |
+ AttachType: ebpf.AttachTraceKprobeSession, |
|
| 202 |
+ License: "MIT", |
|
| 203 |
+ }) |
|
| 204 |
+ if errors.Is(err, unix.E2BIG) {
|
|
| 205 |
+ // Kernel doesn't support AttachType field. |
|
| 206 |
+ return internal.ErrNotSupported |
|
| 207 |
+ } |
|
| 208 |
+ if err != nil {
|
|
| 209 |
+ return err |
|
| 210 |
+ } |
|
| 211 |
+ defer prog.Close() |
|
| 212 |
+ |
|
| 213 |
+ fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{
|
|
| 214 |
+ ProgFd: uint32(prog.FD()), |
|
| 215 |
+ AttachType: sys.BPF_TRACE_KPROBE_SESSION, |
|
| 216 |
+ Count: 1, |
|
| 217 |
+ Syms: sys.NewStringSlicePointer([]string{"vprintk"}),
|
|
| 218 |
+ }) |
|
| 219 |
+ switch {
|
|
| 220 |
+ case errors.Is(err, unix.EINVAL): |
|
| 221 |
+ return internal.ErrNotSupported |
|
| 222 |
+ // If CONFIG_FPROBE isn't set. |
|
| 223 |
+ case errors.Is(err, unix.EOPNOTSUPP): |
|
| 224 |
+ return internal.ErrNotSupported |
|
| 225 |
+ case err != nil: |
|
| 226 |
+ return err |
|
| 227 |
+ } |
|
| 228 |
+ |
|
| 229 |
+ fd.Close() |
|
| 230 |
+ |
|
| 231 |
+ return nil |
|
| 232 |
+}, "6.10") |
| ... | ... |
@@ -78,7 +78,9 @@ func NewFromID(id ID) (Link, error) {
|
| 78 | 78 |
return wrapRawLink(&RawLink{fd, ""})
|
| 79 | 79 |
} |
| 80 | 80 |
|
| 81 |
-// LoadPinnedLink loads a link that was persisted into a bpffs. |
|
| 81 |
+// LoadPinnedLink loads a Link from a pin (file) on the BPF virtual filesystem. |
|
| 82 |
+// |
|
| 83 |
+// Requires at least Linux 5.7. |
|
| 82 | 84 |
func LoadPinnedLink(fileName string, opts *ebpf.LoadPinOptions) (Link, error) {
|
| 83 | 85 |
raw, err := loadPinnedRawLink(fileName, opts) |
| 84 | 86 |
if err != nil {
|
| ... | ... |
@@ -350,7 +352,7 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
|
| 350 | 350 |
} |
| 351 | 351 |
|
| 352 | 352 |
func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, error) {
|
| 353 |
- fd, err := sys.ObjGet(&sys.ObjGetAttr{
|
|
| 353 |
+ fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{
|
|
| 354 | 354 |
Pathname: sys.NewStringPointer(fileName), |
| 355 | 355 |
FileFlags: opts.Marshal(), |
| 356 | 356 |
}) |
| ... | ... |
@@ -358,6 +360,11 @@ func loadPinnedRawLink(fileName string, opts *ebpf.LoadPinOptions) (*RawLink, er |
| 358 | 358 |
return nil, fmt.Errorf("load pinned link: %w", err)
|
| 359 | 359 |
} |
| 360 | 360 |
|
| 361 |
+ if typ != sys.BPF_TYPE_LINK {
|
|
| 362 |
+ _ = fd.Close() |
|
| 363 |
+ return nil, fmt.Errorf("%s is not a Link", fileName)
|
|
| 364 |
+ } |
|
| 365 |
+ |
|
| 361 | 366 |
return &RawLink{fd, fileName}, nil
|
| 362 | 367 |
} |
| 363 | 368 |
|
| ... | ... |
@@ -380,7 +387,7 @@ func (l *RawLink) Close() error {
|
| 380 | 380 |
// Calling Close on a pinned Link will not break the link |
| 381 | 381 |
// until the pin is removed. |
| 382 | 382 |
func (l *RawLink) Pin(fileName string) error {
|
| 383 |
- if err := internal.Pin(l.pinnedPath, fileName, l.fd); err != nil {
|
|
| 383 |
+ if err := sys.Pin(l.pinnedPath, fileName, l.fd); err != nil {
|
|
| 384 | 384 |
return err |
| 385 | 385 |
} |
| 386 | 386 |
l.pinnedPath = fileName |
| ... | ... |
@@ -389,7 +396,7 @@ func (l *RawLink) Pin(fileName string) error {
|
| 389 | 389 |
|
| 390 | 390 |
// Unpin implements the Link interface. |
| 391 | 391 |
func (l *RawLink) Unpin() error {
|
| 392 |
- if err := internal.Unpin(l.pinnedPath); err != nil {
|
|
| 392 |
+ if err := sys.Unpin(l.pinnedPath); err != nil {
|
|
| 393 | 393 |
return err |
| 394 | 394 |
} |
| 395 | 395 |
l.pinnedPath = "" |
| ... | ... |
@@ -63,7 +63,7 @@ func AttachNetfilter(opts NetfilterOptions) (Link, error) {
|
| 63 | 63 |
return &netfilterLink{RawLink{fd, ""}}, nil
|
| 64 | 64 |
} |
| 65 | 65 |
|
| 66 |
-func (*netfilterLink) Update(new *ebpf.Program) error {
|
|
| 66 |
+func (*netfilterLink) Update(_ *ebpf.Program) error {
|
|
| 67 | 67 |
return fmt.Errorf("netfilter update: %w", ErrNotSupported)
|
| 68 | 68 |
} |
| 69 | 69 |
|
| ... | ... |
@@ -115,7 +115,7 @@ func (pl *perfEventLink) Close() error {
|
| 115 | 115 |
return nil |
| 116 | 116 |
} |
| 117 | 117 |
|
| 118 |
-func (pl *perfEventLink) Update(prog *ebpf.Program) error {
|
|
| 118 |
+func (pl *perfEventLink) Update(_ *ebpf.Program) error {
|
|
| 119 | 119 |
return fmt.Errorf("perf event link update: %w", ErrNotSupported)
|
| 120 | 120 |
} |
| 121 | 121 |
|
| ... | ... |
@@ -185,7 +185,7 @@ func (pi *perfEventIoctl) isLink() {}
|
| 185 | 185 |
// |
| 186 | 186 |
// Detaching a program from a perf event is currently not possible, so a |
| 187 | 187 |
// program replacement mechanism cannot be implemented for perf events. |
| 188 |
-func (pi *perfEventIoctl) Update(prog *ebpf.Program) error {
|
|
| 188 |
+func (pi *perfEventIoctl) Update(_ *ebpf.Program) error {
|
|
| 189 | 189 |
return fmt.Errorf("perf event ioctl update: %w", ErrNotSupported)
|
| 190 | 190 |
} |
| 191 | 191 |
|
| ... | ... |
@@ -303,7 +303,7 @@ func openTracepointPerfEvent(tid uint64, pid int) (*sys.FD, error) {
|
| 303 | 303 |
// |
| 304 | 304 |
// https://elixir.bootlin.com/linux/v5.16.8/source/kernel/bpf/syscall.c#L4307 |
| 305 | 305 |
// https://github.com/torvalds/linux/commit/b89fbfbb854c9afc3047e8273cc3a694650b802e |
| 306 |
-var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", "5.15", func() error {
|
|
| 306 |
+var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", func() error {
|
|
| 307 | 307 |
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
| 308 | 308 |
Name: "probe_bpf_perf_link", |
| 309 | 309 |
Type: ebpf.Kprobe, |
| ... | ... |
@@ -329,4 +329,4 @@ var haveBPFLinkPerfEvent = internal.NewFeatureTest("bpf_link_perf_event", "5.15"
|
| 329 | 329 |
return nil |
| 330 | 330 |
} |
| 331 | 331 |
return err |
| 332 |
-}) |
|
| 332 |
+}, "5.15") |
| ... | ... |
@@ -30,7 +30,7 @@ const ( |
| 30 | 30 |
NetkitType = sys.BPF_LINK_TYPE_NETKIT |
| 31 | 31 |
) |
| 32 | 32 |
|
| 33 |
-var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", "4.10", func() error {
|
|
| 33 |
+var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", func() error {
|
|
| 34 | 34 |
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
| 35 | 35 |
Type: ebpf.CGroupSKB, |
| 36 | 36 |
License: "MIT", |
| ... | ... |
@@ -48,9 +48,9 @@ var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", "4.10", func() e
|
| 48 | 48 |
// have the syscall. |
| 49 | 49 |
prog.Close() |
| 50 | 50 |
return nil |
| 51 |
-}) |
|
| 51 |
+}, "4.10") |
|
| 52 | 52 |
|
| 53 |
-var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic replacement of MULTI progs", "5.5", func() error {
|
|
| 53 |
+var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic replacement of MULTI progs", func() error {
|
|
| 54 | 54 |
if err := haveProgAttach(); err != nil {
|
| 55 | 55 |
return err |
| 56 | 56 |
} |
| ... | ... |
@@ -90,9 +90,9 @@ var haveProgAttachReplace = internal.NewFeatureTest("BPF_PROG_ATTACH atomic repl
|
| 90 | 90 |
return nil |
| 91 | 91 |
} |
| 92 | 92 |
return err |
| 93 |
-}) |
|
| 93 |
+}, "5.5") |
|
| 94 | 94 |
|
| 95 |
-var haveBPFLink = internal.NewFeatureTest("bpf_link", "5.7", func() error {
|
|
| 95 |
+var haveBPFLink = internal.NewFeatureTest("bpf_link", func() error {
|
|
| 96 | 96 |
attr := sys.LinkCreateAttr{
|
| 97 | 97 |
// This is a hopefully invalid file descriptor, which triggers EBADF. |
| 98 | 98 |
TargetFd: ^uint32(0), |
| ... | ... |
@@ -107,9 +107,9 @@ var haveBPFLink = internal.NewFeatureTest("bpf_link", "5.7", func() error {
|
| 107 | 107 |
return nil |
| 108 | 108 |
} |
| 109 | 109 |
return err |
| 110 |
-}) |
|
| 110 |
+}, "5.7") |
|
| 111 | 111 |
|
| 112 |
-var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", "4.15", func() error {
|
|
| 112 |
+var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", func() error {
|
|
| 113 | 113 |
attr := sys.ProgQueryAttr{
|
| 114 | 114 |
// We rely on this being checked during the syscall. |
| 115 | 115 |
// With an otherwise correct payload we expect EBADF here |
| ... | ... |
@@ -127,9 +127,9 @@ var haveProgQuery = internal.NewFeatureTest("BPF_PROG_QUERY", "4.15", func() err
|
| 127 | 127 |
return ErrNotSupported |
| 128 | 128 |
} |
| 129 | 129 |
return errors.New("syscall succeeded unexpectedly")
|
| 130 |
-}) |
|
| 130 |
+}, "4.15") |
|
| 131 | 131 |
|
| 132 |
-var haveTCX = internal.NewFeatureTest("tcx", "6.6", func() error {
|
|
| 132 |
+var haveTCX = internal.NewFeatureTest("tcx", func() error {
|
|
| 133 | 133 |
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
| 134 | 134 |
Type: ebpf.SchedCLS, |
| 135 | 135 |
License: "MIT", |
| ... | ... |
@@ -162,9 +162,9 @@ var haveTCX = internal.NewFeatureTest("tcx", "6.6", func() error {
|
| 162 | 162 |
return ErrNotSupported |
| 163 | 163 |
} |
| 164 | 164 |
return errors.New("syscall succeeded unexpectedly")
|
| 165 |
-}) |
|
| 165 |
+}, "6.6") |
|
| 166 | 166 |
|
| 167 |
-var haveNetkit = internal.NewFeatureTest("netkit", "6.7", func() error {
|
|
| 167 |
+var haveNetkit = internal.NewFeatureTest("netkit", func() error {
|
|
| 168 | 168 |
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
| 169 | 169 |
Type: ebpf.SchedCLS, |
| 170 | 170 |
License: "MIT", |
| ... | ... |
@@ -197,4 +197,4 @@ var haveNetkit = internal.NewFeatureTest("netkit", "6.7", func() error {
|
| 197 | 197 |
return ErrNotSupported |
| 198 | 198 |
} |
| 199 | 199 |
return errors.New("syscall succeeded unexpectedly")
|
| 200 |
-}) |
|
| 200 |
+}, "6.7") |
| ... | ... |
@@ -16,7 +16,7 @@ var ( |
| 16 | 16 |
uprobeRefCtrOffsetPMUPath = "/sys/bus/event_source/devices/uprobe/format/ref_ctr_offset" |
| 17 | 17 |
// elixir.bootlin.com/linux/v5.15-rc7/source/kernel/events/core.c#L9799 |
| 18 | 18 |
uprobeRefCtrOffsetShift = 32 |
| 19 |
- haveRefCtrOffsetPMU = internal.NewFeatureTest("RefCtrOffsetPMU", "4.20", func() error {
|
|
| 19 |
+ haveRefCtrOffsetPMU = internal.NewFeatureTest("RefCtrOffsetPMU", func() error {
|
|
| 20 | 20 |
_, err := os.Stat(uprobeRefCtrOffsetPMUPath) |
| 21 | 21 |
if errors.Is(err, os.ErrNotExist) {
|
| 22 | 22 |
return internal.ErrNotSupported |
| ... | ... |
@@ -25,7 +25,7 @@ var ( |
| 25 | 25 |
return err |
| 26 | 26 |
} |
| 27 | 27 |
return nil |
| 28 |
- }) |
|
| 28 |
+ }, "4.20") |
|
| 29 | 29 |
|
| 30 | 30 |
// ErrNoSymbol indicates that the given symbol was not found |
| 31 | 31 |
// in the ELF symbols table. |
| ... | ... |
@@ -321,7 +321,7 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti |
| 321 | 321 |
if err == nil {
|
| 322 | 322 |
return tp, nil |
| 323 | 323 |
} |
| 324 |
- if err != nil && !errors.Is(err, ErrNotSupported) {
|
|
| 324 |
+ if !errors.Is(err, ErrNotSupported) {
|
|
| 325 | 325 |
return nil, fmt.Errorf("creating perf_uprobe PMU: %w", err)
|
| 326 | 326 |
} |
| 327 | 327 |
|
| ... | ... |
@@ -47,7 +47,7 @@ func (ex *Executable) UretprobeMulti(symbols []string, prog *ebpf.Program, opts |
| 47 | 47 |
// The return probe is not limited for symbols entry, so there's no special |
| 48 | 48 |
// setup for return uprobes (other than the extra flag). The symbols, opts.Offsets |
| 49 | 49 |
// and opts.Addresses arrays follow the same logic as for entry uprobes. |
| 50 |
- return ex.uprobeMulti(symbols, prog, opts, unix.BPF_F_UPROBE_MULTI_RETURN) |
|
| 50 |
+ return ex.uprobeMulti(symbols, prog, opts, sys.BPF_F_UPROBE_MULTI_RETURN) |
|
| 51 | 51 |
} |
| 52 | 52 |
|
| 53 | 53 |
func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *UprobeMultiOptions, flags uint32) (Link, error) {
|
| ... | ... |
@@ -99,8 +99,11 @@ func (ex *Executable) uprobeMulti(symbols []string, prog *ebpf.Program, opts *Up |
| 99 | 99 |
if errors.Is(err, unix.ESRCH) {
|
| 100 | 100 |
return nil, fmt.Errorf("%w (specified pid not found?)", os.ErrNotExist)
|
| 101 | 101 |
} |
| 102 |
+ // Since Linux commit 46ba0e49b642 ("bpf: fix multi-uprobe PID filtering
|
|
| 103 |
+ // logic"), if the provided pid overflows MaxInt32 (turning it negative), the |
|
| 104 |
+ // kernel will return EINVAL instead of ESRCH. |
|
| 102 | 105 |
if errors.Is(err, unix.EINVAL) {
|
| 103 |
- return nil, fmt.Errorf("%w (missing symbol or prog's AttachType not AttachTraceUprobeMulti?)", err)
|
|
| 106 |
+ return nil, fmt.Errorf("%w (invalid pid, missing symbol or prog's AttachType not AttachTraceUprobeMulti?)", err)
|
|
| 104 | 107 |
} |
| 105 | 108 |
|
| 106 | 109 |
if err != nil {
|
| ... | ... |
@@ -168,11 +171,11 @@ type uprobeMultiLink struct {
|
| 168 | 168 |
|
| 169 | 169 |
var _ Link = (*uprobeMultiLink)(nil) |
| 170 | 170 |
|
| 171 |
-func (kml *uprobeMultiLink) Update(prog *ebpf.Program) error {
|
|
| 171 |
+func (kml *uprobeMultiLink) Update(_ *ebpf.Program) error {
|
|
| 172 | 172 |
return fmt.Errorf("update uprobe_multi: %w", ErrNotSupported)
|
| 173 | 173 |
} |
| 174 | 174 |
|
| 175 |
-var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", "6.6", func() error {
|
|
| 175 |
+var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", func() error {
|
|
| 176 | 176 |
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
| 177 | 177 |
Name: "probe_upm_link", |
| 178 | 178 |
Type: ebpf.Kprobe, |
| ... | ... |
@@ -213,4 +216,4 @@ var haveBPFLinkUprobeMulti = internal.NewFeatureTest("bpf_link_uprobe_multi", "6
|
| 213 | 213 |
// should not happen |
| 214 | 214 |
fd.Close() |
| 215 | 215 |
return errors.New("successfully attached uprobe_multi to /, kernel bug?")
|
| 216 |
-}) |
|
| 216 |
+}, "6.6") |
| ... | ... |
@@ -9,10 +9,12 @@ import ( |
| 9 | 9 |
"io/fs" |
| 10 | 10 |
"math" |
| 11 | 11 |
"slices" |
| 12 |
+ "strings" |
|
| 12 | 13 |
|
| 13 | 14 |
"github.com/cilium/ebpf/asm" |
| 14 | 15 |
"github.com/cilium/ebpf/btf" |
| 15 | 16 |
"github.com/cilium/ebpf/internal" |
| 17 |
+ "github.com/cilium/ebpf/internal/kallsyms" |
|
| 16 | 18 |
) |
| 17 | 19 |
|
| 18 | 20 |
// handles stores handle objects to avoid gc cleanup |
| ... | ... |
@@ -205,13 +207,19 @@ func flattenPrograms(progs map[string]*ProgramSpec, names []string) {
|
| 205 | 205 |
// dependencies of each program. |
| 206 | 206 |
func flattenInstructions(name string, progs map[string]*ProgramSpec, refs map[*ProgramSpec][]string) asm.Instructions {
|
| 207 | 207 |
prog := progs[name] |
| 208 |
+ progRefs := refs[prog] |
|
| 209 |
+ |
|
| 210 |
+ if len(progRefs) == 0 {
|
|
| 211 |
+ // No references, nothing to do. |
|
| 212 |
+ return prog.Instructions |
|
| 213 |
+ } |
|
| 208 | 214 |
|
| 209 | 215 |
insns := make(asm.Instructions, len(prog.Instructions)) |
| 210 | 216 |
copy(insns, prog.Instructions) |
| 211 | 217 |
|
| 212 | 218 |
// Add all direct references of prog to the list of to be linked programs. |
| 213 |
- pending := make([]string, len(refs[prog])) |
|
| 214 |
- copy(pending, refs[prog]) |
|
| 219 |
+ pending := make([]string, len(progRefs)) |
|
| 220 |
+ copy(pending, progRefs) |
|
| 215 | 221 |
|
| 216 | 222 |
// All references for which we've appended instructions. |
| 217 | 223 |
linked := make(map[string]bool) |
| ... | ... |
@@ -457,3 +465,42 @@ func resolveKconfigReferences(insns asm.Instructions) (_ *Map, err error) {
|
| 457 | 457 |
|
| 458 | 458 |
return kconfig, nil |
| 459 | 459 |
} |
| 460 |
+ |
|
| 461 |
+func resolveKsymReferences(insns asm.Instructions) error {
|
|
| 462 |
+ var missing []string |
|
| 463 |
+ |
|
| 464 |
+ iter := insns.Iterate() |
|
| 465 |
+ for iter.Next() {
|
|
| 466 |
+ ins := iter.Ins |
|
| 467 |
+ meta, _ := ins.Metadata.Get(ksymMetaKey{}).(*ksymMeta)
|
|
| 468 |
+ if meta == nil {
|
|
| 469 |
+ continue |
|
| 470 |
+ } |
|
| 471 |
+ |
|
| 472 |
+ addr, err := kallsyms.Address(meta.Name) |
|
| 473 |
+ if err != nil {
|
|
| 474 |
+ return fmt.Errorf("resolve ksym %s: %w", meta.Name, err)
|
|
| 475 |
+ } |
|
| 476 |
+ if addr != 0 {
|
|
| 477 |
+ ins.Constant = int64(addr) |
|
| 478 |
+ continue |
|
| 479 |
+ } |
|
| 480 |
+ |
|
| 481 |
+ if meta.Binding == elf.STB_WEAK {
|
|
| 482 |
+ // A weak ksym variable in eBPF C means its resolution is optional. |
|
| 483 |
+ // Set a zero constant explicitly for clarity. |
|
| 484 |
+ ins.Constant = 0 |
|
| 485 |
+ continue |
|
| 486 |
+ } |
|
| 487 |
+ |
|
| 488 |
+ if !slices.Contains(missing, meta.Name) {
|
|
| 489 |
+ missing = append(missing, meta.Name) |
|
| 490 |
+ } |
|
| 491 |
+ } |
|
| 492 |
+ |
|
| 493 |
+ if len(missing) > 0 {
|
|
| 494 |
+ return fmt.Errorf("kernel is missing symbol: %s", strings.Join(missing, ","))
|
|
| 495 |
+ } |
|
| 496 |
+ |
|
| 497 |
+ return nil |
|
| 498 |
+} |
| ... | ... |
@@ -66,16 +66,13 @@ type MapSpec struct {
|
| 66 | 66 |
Pinning PinType |
| 67 | 67 |
|
| 68 | 68 |
// Specify numa node during map creation |
| 69 |
- // (effective only if unix.BPF_F_NUMA_NODE flag is set, |
|
| 69 |
+ // (effective only if sys.BPF_F_NUMA_NODE flag is set, |
|
| 70 | 70 |
// which can be imported from golang.org/x/sys/unix) |
| 71 | 71 |
NumaNode uint32 |
| 72 | 72 |
|
| 73 | 73 |
// The initial contents of the map. May be nil. |
| 74 | 74 |
Contents []MapKV |
| 75 | 75 |
|
| 76 |
- // Whether to freeze a map after setting its initial contents. |
|
| 77 |
- Freeze bool |
|
| 78 |
- |
|
| 79 | 76 |
// InnerMap is used as a template for ArrayOfMaps and HashOfMaps |
| 80 | 77 |
InnerMap *MapSpec |
| 81 | 78 |
|
| ... | ... |
@@ -161,6 +158,17 @@ func (spec *MapSpec) fixupMagicFields() (*MapSpec, error) {
|
| 161 | 161 |
// behaviour in the past. |
| 162 | 162 |
spec.MaxEntries = n |
| 163 | 163 |
} |
| 164 |
+ |
|
| 165 |
+ case CPUMap: |
|
| 166 |
+ n, err := PossibleCPU() |
|
| 167 |
+ if err != nil {
|
|
| 168 |
+ return nil, fmt.Errorf("fixup cpu map: %w", err)
|
|
| 169 |
+ } |
|
| 170 |
+ |
|
| 171 |
+ if n := uint32(n); spec.MaxEntries == 0 || spec.MaxEntries > n {
|
|
| 172 |
+ // Perform clamping similar to PerfEventArray. |
|
| 173 |
+ spec.MaxEntries = n |
|
| 174 |
+ } |
|
| 164 | 175 |
} |
| 165 | 176 |
|
| 166 | 177 |
return spec, nil |
| ... | ... |
@@ -190,6 +198,14 @@ func (ms *MapSpec) dataSection() ([]byte, *btf.Datasec, error) {
|
| 190 | 190 |
return value, ds, nil |
| 191 | 191 |
} |
| 192 | 192 |
|
| 193 |
+func (ms *MapSpec) readOnly() bool {
|
|
| 194 |
+ return (ms.Flags & sys.BPF_F_RDONLY_PROG) > 0 |
|
| 195 |
+} |
|
| 196 |
+ |
|
| 197 |
+func (ms *MapSpec) writeOnly() bool {
|
|
| 198 |
+ return (ms.Flags & sys.BPF_F_WRONLY_PROG) > 0 |
|
| 199 |
+} |
|
| 200 |
+ |
|
| 193 | 201 |
// MapKV is used to initialize the contents of a Map. |
| 194 | 202 |
type MapKV struct {
|
| 195 | 203 |
Key interface{}
|
| ... | ... |
@@ -222,7 +238,7 @@ func (ms *MapSpec) Compatible(m *Map) error {
|
| 222 | 222 |
|
| 223 | 223 |
// BPF_F_RDONLY_PROG is set unconditionally for devmaps. Explicitly allow this |
| 224 | 224 |
// mismatch. |
| 225 |
- if !((ms.Type == DevMap || ms.Type == DevMapHash) && m.flags^ms.Flags == unix.BPF_F_RDONLY_PROG) && |
|
| 225 |
+ if !((ms.Type == DevMap || ms.Type == DevMapHash) && m.flags^ms.Flags == sys.BPF_F_RDONLY_PROG) && |
|
| 226 | 226 |
m.flags != ms.Flags {
|
| 227 | 227 |
diffs = append(diffs, fmt.Sprintf("Flags: %d changed to %d", m.flags, ms.Flags))
|
| 228 | 228 |
} |
| ... | ... |
@@ -254,6 +270,8 @@ type Map struct {
|
| 254 | 254 |
pinnedPath string |
| 255 | 255 |
// Per CPU maps return values larger than the size in the spec |
| 256 | 256 |
fullValueSize int |
| 257 |
+ |
|
| 258 |
+ memory *Memory |
|
| 257 | 259 |
} |
| 258 | 260 |
|
| 259 | 261 |
// NewMapFromFD creates a map from a raw fd. |
| ... | ... |
@@ -359,7 +377,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
|
| 359 | 359 |
return nil, errors.New("inner maps cannot be pinned")
|
| 360 | 360 |
} |
| 361 | 361 |
|
| 362 |
- template, err := spec.InnerMap.createMap(nil, opts) |
|
| 362 |
+ template, err := spec.InnerMap.createMap(nil) |
|
| 363 | 363 |
if err != nil {
|
| 364 | 364 |
return nil, fmt.Errorf("inner map: %w", err)
|
| 365 | 365 |
} |
| ... | ... |
@@ -371,7 +389,7 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
|
| 371 | 371 |
innerFd = template.fd |
| 372 | 372 |
} |
| 373 | 373 |
|
| 374 |
- m, err := spec.createMap(innerFd, opts) |
|
| 374 |
+ m, err := spec.createMap(innerFd) |
|
| 375 | 375 |
if err != nil {
|
| 376 | 376 |
return nil, err |
| 377 | 377 |
} |
| ... | ... |
@@ -387,9 +405,54 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions) (_ *Map, err error) {
|
| 387 | 387 |
return m, nil |
| 388 | 388 |
} |
| 389 | 389 |
|
| 390 |
+// Memory returns a memory-mapped region for the Map. The Map must have been |
|
| 391 |
+// created with the BPF_F_MMAPABLE flag. Repeated calls to Memory return the |
|
| 392 |
+// same mapping. Callers are responsible for coordinating access to Memory. |
|
| 393 |
+func (m *Map) Memory() (*Memory, error) {
|
|
| 394 |
+ if m.memory != nil {
|
|
| 395 |
+ return m.memory, nil |
|
| 396 |
+ } |
|
| 397 |
+ |
|
| 398 |
+ if m.flags&sys.BPF_F_MMAPABLE == 0 {
|
|
| 399 |
+ return nil, fmt.Errorf("Map was not created with the BPF_F_MMAPABLE flag: %w", ErrNotSupported)
|
|
| 400 |
+ } |
|
| 401 |
+ |
|
| 402 |
+ size, err := m.memorySize() |
|
| 403 |
+ if err != nil {
|
|
| 404 |
+ return nil, err |
|
| 405 |
+ } |
|
| 406 |
+ |
|
| 407 |
+ mm, err := newMemory(m.FD(), size) |
|
| 408 |
+ if err != nil {
|
|
| 409 |
+ return nil, fmt.Errorf("creating new Memory: %w", err)
|
|
| 410 |
+ } |
|
| 411 |
+ |
|
| 412 |
+ m.memory = mm |
|
| 413 |
+ |
|
| 414 |
+ return mm, nil |
|
| 415 |
+} |
|
| 416 |
+ |
|
| 417 |
+func (m *Map) memorySize() (int, error) {
|
|
| 418 |
+ switch m.Type() {
|
|
| 419 |
+ case Array: |
|
| 420 |
+ // In Arrays, values are always laid out on 8-byte boundaries regardless of |
|
| 421 |
+ // architecture. Multiply by MaxEntries and align the result to the host's |
|
| 422 |
+ // page size. |
|
| 423 |
+ size := int(internal.Align(m.ValueSize(), 8) * m.MaxEntries()) |
|
| 424 |
+ size = internal.Align(size, os.Getpagesize()) |
|
| 425 |
+ return size, nil |
|
| 426 |
+ case Arena: |
|
| 427 |
+ // For Arenas, MaxEntries denotes the maximum number of pages available to |
|
| 428 |
+ // the arena. |
|
| 429 |
+ return int(m.MaxEntries()) * os.Getpagesize(), nil |
|
| 430 |
+ } |
|
| 431 |
+ |
|
| 432 |
+ return 0, fmt.Errorf("determine memory size of map type %s: %w", m.Type(), ErrNotSupported)
|
|
| 433 |
+} |
|
| 434 |
+ |
|
| 390 | 435 |
// createMap validates the spec's properties and creates the map in the kernel |
| 391 | 436 |
// using the given opts. It does not populate or freeze the map. |
| 392 |
-func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions) (_ *Map, err error) {
|
|
| 437 |
+func (spec *MapSpec) createMap(inner *sys.FD) (_ *Map, err error) {
|
|
| 393 | 438 |
closeOnError := func(closer io.Closer) {
|
| 394 | 439 |
if err != nil {
|
| 395 | 440 |
closer.Close() |
| ... | ... |
@@ -416,7 +479,7 @@ func (spec *MapSpec) createMap(inner *sys.FD, opts MapOptions) (_ *Map, err erro |
| 416 | 416 |
KeySize: spec.KeySize, |
| 417 | 417 |
ValueSize: spec.ValueSize, |
| 418 | 418 |
MaxEntries: spec.MaxEntries, |
| 419 |
- MapFlags: sys.MapFlags(spec.Flags), |
|
| 419 |
+ MapFlags: spec.Flags, |
|
| 420 | 420 |
NumaNode: spec.NumaNode, |
| 421 | 421 |
} |
| 422 | 422 |
|
| ... | ... |
@@ -474,32 +537,32 @@ func handleMapCreateError(attr sys.MapCreateAttr, spec *MapSpec, err error) erro |
| 474 | 474 |
if errors.Is(err, unix.EINVAL) && spec.Type == UnspecifiedMap {
|
| 475 | 475 |
return fmt.Errorf("map create: cannot use type %s", UnspecifiedMap)
|
| 476 | 476 |
} |
| 477 |
- if errors.Is(err, unix.EINVAL) && spec.Flags&unix.BPF_F_NO_PREALLOC > 0 {
|
|
| 477 |
+ if errors.Is(err, unix.EINVAL) && spec.Flags&sys.BPF_F_NO_PREALLOC > 0 {
|
|
| 478 | 478 |
return fmt.Errorf("map create: %w (noPrealloc flag may be incompatible with map type %s)", err, spec.Type)
|
| 479 | 479 |
} |
| 480 | 480 |
|
| 481 |
- switch spec.Type {
|
|
| 482 |
- case ArrayOfMaps, HashOfMaps: |
|
| 481 |
+ if spec.Type.canStoreMap() {
|
|
| 483 | 482 |
if haveFeatErr := haveNestedMaps(); haveFeatErr != nil {
|
| 484 | 483 |
return fmt.Errorf("map create: %w", haveFeatErr)
|
| 485 | 484 |
} |
| 486 | 485 |
} |
| 487 |
- if spec.Flags&(unix.BPF_F_RDONLY_PROG|unix.BPF_F_WRONLY_PROG) > 0 || spec.Freeze {
|
|
| 486 |
+ |
|
| 487 |
+ if spec.readOnly() || spec.writeOnly() {
|
|
| 488 | 488 |
if haveFeatErr := haveMapMutabilityModifiers(); haveFeatErr != nil {
|
| 489 | 489 |
return fmt.Errorf("map create: %w", haveFeatErr)
|
| 490 | 490 |
} |
| 491 | 491 |
} |
| 492 |
- if spec.Flags&unix.BPF_F_MMAPABLE > 0 {
|
|
| 492 |
+ if spec.Flags&sys.BPF_F_MMAPABLE > 0 {
|
|
| 493 | 493 |
if haveFeatErr := haveMmapableMaps(); haveFeatErr != nil {
|
| 494 | 494 |
return fmt.Errorf("map create: %w", haveFeatErr)
|
| 495 | 495 |
} |
| 496 | 496 |
} |
| 497 |
- if spec.Flags&unix.BPF_F_INNER_MAP > 0 {
|
|
| 497 |
+ if spec.Flags&sys.BPF_F_INNER_MAP > 0 {
|
|
| 498 | 498 |
if haveFeatErr := haveInnerMaps(); haveFeatErr != nil {
|
| 499 | 499 |
return fmt.Errorf("map create: %w", haveFeatErr)
|
| 500 | 500 |
} |
| 501 | 501 |
} |
| 502 |
- if spec.Flags&unix.BPF_F_NO_PREALLOC > 0 {
|
|
| 502 |
+ if spec.Flags&sys.BPF_F_NO_PREALLOC > 0 {
|
|
| 503 | 503 |
if haveFeatErr := haveNoPreallocMaps(); haveFeatErr != nil {
|
| 504 | 504 |
return fmt.Errorf("map create: %w", haveFeatErr)
|
| 505 | 505 |
} |
| ... | ... |
@@ -530,6 +593,7 @@ func newMap(fd *sys.FD, name string, typ MapType, keySize, valueSize, maxEntries |
| 530 | 530 |
flags, |
| 531 | 531 |
"", |
| 532 | 532 |
int(valueSize), |
| 533 |
+ nil, |
|
| 533 | 534 |
} |
| 534 | 535 |
|
| 535 | 536 |
if !typ.hasPerCPUValue() {
|
| ... | ... |
@@ -577,7 +641,12 @@ func (m *Map) Flags() uint32 {
|
| 577 | 577 |
return m.flags |
| 578 | 578 |
} |
| 579 | 579 |
|
| 580 |
-// Info returns metadata about the map. |
|
| 580 |
+// Info returns metadata about the map. This was first introduced in Linux 4.5, |
|
| 581 |
+// but newer kernels support more MapInfo fields with the introduction of more |
|
| 582 |
+// features. See [MapInfo] and its methods for more details. |
|
| 583 |
+// |
|
| 584 |
+// Returns an error wrapping ErrNotSupported if the kernel supports neither |
|
| 585 |
+// BPF_OBJ_GET_INFO_BY_FD nor reading map information from /proc/self/fdinfo. |
|
| 581 | 586 |
func (m *Map) Info() (*MapInfo, error) {
|
| 582 | 587 |
return newMapInfoFromFd(m.fd) |
| 583 | 588 |
} |
| ... | ... |
@@ -604,7 +673,7 @@ func (m *Map) Handle() (*btf.Handle, error) {
|
| 604 | 604 |
type MapLookupFlags uint64 |
| 605 | 605 |
|
| 606 | 606 |
// LookupLock look up the value of a spin-locked map. |
| 607 |
-const LookupLock MapLookupFlags = unix.BPF_F_LOCK |
|
| 607 |
+const LookupLock MapLookupFlags = sys.BPF_F_LOCK |
|
| 608 | 608 |
|
| 609 | 609 |
// Lookup retrieves a value from a Map. |
| 610 | 610 |
// |
| ... | ... |
@@ -1336,6 +1405,7 @@ func (m *Map) Clone() (*Map, error) {
|
| 1336 | 1336 |
m.flags, |
| 1337 | 1337 |
"", |
| 1338 | 1338 |
m.fullValueSize, |
| 1339 |
+ nil, |
|
| 1339 | 1340 |
}, nil |
| 1340 | 1341 |
} |
| 1341 | 1342 |
|
| ... | ... |
@@ -1349,7 +1419,7 @@ func (m *Map) Clone() (*Map, error) {
|
| 1349 | 1349 |
// This requires bpffs to be mounted above fileName. |
| 1350 | 1350 |
// See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd |
| 1351 | 1351 |
func (m *Map) Pin(fileName string) error {
|
| 1352 |
- if err := internal.Pin(m.pinnedPath, fileName, m.fd); err != nil {
|
|
| 1352 |
+ if err := sys.Pin(m.pinnedPath, fileName, m.fd); err != nil {
|
|
| 1353 | 1353 |
return err |
| 1354 | 1354 |
} |
| 1355 | 1355 |
m.pinnedPath = fileName |
| ... | ... |
@@ -1362,7 +1432,7 @@ func (m *Map) Pin(fileName string) error {
|
| 1362 | 1362 |
// |
| 1363 | 1363 |
// Unpinning an unpinned Map returns nil. |
| 1364 | 1364 |
func (m *Map) Unpin() error {
|
| 1365 |
- if err := internal.Unpin(m.pinnedPath); err != nil {
|
|
| 1365 |
+ if err := sys.Unpin(m.pinnedPath); err != nil {
|
|
| 1366 | 1366 |
return err |
| 1367 | 1367 |
} |
| 1368 | 1368 |
m.pinnedPath = "" |
| ... | ... |
@@ -1400,7 +1470,7 @@ func (m *Map) finalize(spec *MapSpec) error {
|
| 1400 | 1400 |
} |
| 1401 | 1401 |
} |
| 1402 | 1402 |
|
| 1403 |
- if spec.Freeze {
|
|
| 1403 |
+ if isConstantDataSection(spec.Name) || isKconfigSection(spec.Name) {
|
|
| 1404 | 1404 |
if err := m.Freeze(); err != nil {
|
| 1405 | 1405 |
return fmt.Errorf("freezing map: %w", err)
|
| 1406 | 1406 |
} |
| ... | ... |
@@ -1501,9 +1571,11 @@ func (m *Map) unmarshalValue(value any, buf sysenc.Buffer) error {
|
| 1501 | 1501 |
return buf.Unmarshal(value) |
| 1502 | 1502 |
} |
| 1503 | 1503 |
|
| 1504 |
-// LoadPinnedMap loads a Map from a BPF file. |
|
| 1504 |
+// LoadPinnedMap opens a Map from a pin (file) on the BPF virtual filesystem. |
|
| 1505 |
+// |
|
| 1506 |
+// Requires at least Linux 4.5. |
|
| 1505 | 1507 |
func LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) {
|
| 1506 |
- fd, err := sys.ObjGet(&sys.ObjGetAttr{
|
|
| 1508 |
+ fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{
|
|
| 1507 | 1509 |
Pathname: sys.NewStringPointer(fileName), |
| 1508 | 1510 |
FileFlags: opts.Marshal(), |
| 1509 | 1511 |
}) |
| ... | ... |
@@ -1511,6 +1583,11 @@ func LoadPinnedMap(fileName string, opts *LoadPinOptions) (*Map, error) {
|
| 1511 | 1511 |
return nil, err |
| 1512 | 1512 |
} |
| 1513 | 1513 |
|
| 1514 |
+ if typ != sys.BPF_TYPE_MAP {
|
|
| 1515 |
+ _ = fd.Close() |
|
| 1516 |
+ return nil, fmt.Errorf("%s is not a Map", fileName)
|
|
| 1517 |
+ } |
|
| 1518 |
+ |
|
| 1514 | 1519 |
m, err := newMapFromFD(fd) |
| 1515 | 1520 |
if err == nil {
|
| 1516 | 1521 |
m.pinnedPath = fileName |
| ... | ... |
@@ -1530,6 +1607,10 @@ func unmarshalMap(buf sysenc.Buffer) (*Map, error) {
|
| 1530 | 1530 |
|
| 1531 | 1531 |
// marshalMap marshals the fd of a map into a buffer in host endianness. |
| 1532 | 1532 |
func marshalMap(m *Map, length int) ([]byte, error) {
|
| 1533 |
+ if m == nil {
|
|
| 1534 |
+ return nil, errors.New("can't marshal a nil Map")
|
|
| 1535 |
+ } |
|
| 1536 |
+ |
|
| 1533 | 1537 |
if length != 4 {
|
| 1534 | 1538 |
return nil, fmt.Errorf("can't marshal map to %d bytes", length)
|
| 1535 | 1539 |
} |
| 1536 | 1540 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,145 @@ |
| 0 |
+package ebpf |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "errors" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "io" |
|
| 6 |
+ "runtime" |
|
| 7 |
+ |
|
| 8 |
+ "github.com/cilium/ebpf/internal/unix" |
|
| 9 |
+) |
|
| 10 |
+ |
|
| 11 |
+// Memory is the building block for accessing the memory of specific bpf map |
|
| 12 |
+// types (Array and Arena at the time of writing) without going through the bpf |
|
| 13 |
+// syscall interface. |
|
| 14 |
+// |
|
| 15 |
+// Given the fd of a bpf map created with the BPF_F_MMAPABLE flag, a shared |
|
| 16 |
+// 'file'-based memory-mapped region can be allocated in the process' address |
|
| 17 |
+// space, exposing the bpf map's memory by simply accessing a memory location. |
|
| 18 |
+ |
|
| 19 |
+var ErrReadOnly = errors.New("resource is read-only")
|
|
| 20 |
+ |
|
| 21 |
+// Memory implements accessing a Map's memory without making any syscalls. |
|
| 22 |
+// Pay attention to the difference between Go and C struct alignment rules. Use |
|
| 23 |
+// [structs.HostLayout] on supported Go versions to help with alignment. |
|
| 24 |
+// |
|
| 25 |
+// Note on memory coherence: avoid using packed structs in memory shared between |
|
| 26 |
+// user space and eBPF C programs. This drops a struct's memory alignment to 1, |
|
| 27 |
+// forcing the compiler to use single-byte loads and stores for field accesses. |
|
| 28 |
+// This may lead to partially-written data to be observed from user space. |
|
| 29 |
+// |
|
| 30 |
+// On most architectures, the memmove implementation used by Go's copy() will |
|
| 31 |
+// access data in word-sized chunks. If paired with a matching access pattern on |
|
| 32 |
+// the eBPF C side (and if using default memory alignment), accessing shared |
|
| 33 |
+// memory without atomics or other synchronization primitives should be sound |
|
| 34 |
+// for individual values. For accesses beyond a single value, the usual |
|
| 35 |
+// concurrent programming rules apply. |
|
| 36 |
+type Memory struct {
|
|
| 37 |
+ b []byte |
|
| 38 |
+ ro bool |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+func newMemory(fd, size int) (*Memory, error) {
|
|
| 42 |
+ // Typically, maps created with BPF_F_RDONLY_PROG remain writable from user |
|
| 43 |
+ // space until frozen. As a security precaution, the kernel doesn't allow |
|
| 44 |
+ // mapping bpf map memory as read-write into user space if the bpf map was |
|
| 45 |
+ // frozen, or if it was created using the RDONLY_PROG flag. |
|
| 46 |
+ // |
|
| 47 |
+ // The user would be able to write to the map after freezing (since the kernel |
|
| 48 |
+ // can't change the protection mode of an already-mapped page), while the |
|
| 49 |
+ // verifier assumes the contents to be immutable. |
|
| 50 |
+ b, err := unix.Mmap(fd, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) |
|
| 51 |
+ |
|
| 52 |
+ // If the map is frozen when an rw mapping is requested, expect EPERM. If the |
|
| 53 |
+ // map was created with BPF_F_RDONLY_PROG, expect EACCES. |
|
| 54 |
+ var ro bool |
|
| 55 |
+ if errors.Is(err, unix.EPERM) || errors.Is(err, unix.EACCES) {
|
|
| 56 |
+ ro = true |
|
| 57 |
+ b, err = unix.Mmap(fd, 0, size, unix.PROT_READ, unix.MAP_SHARED) |
|
| 58 |
+ } |
|
| 59 |
+ if err != nil {
|
|
| 60 |
+ return nil, fmt.Errorf("setting up memory-mapped region: %w", err)
|
|
| 61 |
+ } |
|
| 62 |
+ |
|
| 63 |
+ mm := &Memory{
|
|
| 64 |
+ b, |
|
| 65 |
+ ro, |
|
| 66 |
+ } |
|
| 67 |
+ runtime.SetFinalizer(mm, (*Memory).close) |
|
| 68 |
+ |
|
| 69 |
+ return mm, nil |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func (mm *Memory) close() {
|
|
| 73 |
+ if err := unix.Munmap(mm.b); err != nil {
|
|
| 74 |
+ panic(fmt.Errorf("unmapping memory: %w", err))
|
|
| 75 |
+ } |
|
| 76 |
+ mm.b = nil |
|
| 77 |
+} |
|
| 78 |
+ |
|
| 79 |
+// Size returns the size of the memory-mapped region in bytes. |
|
| 80 |
+func (mm *Memory) Size() int {
|
|
| 81 |
+ return len(mm.b) |
|
| 82 |
+} |
|
| 83 |
+ |
|
| 84 |
+// ReadOnly returns true if the memory-mapped region is read-only. |
|
| 85 |
+func (mm *Memory) ReadOnly() bool {
|
|
| 86 |
+ return mm.ro |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// bounds returns true if an access at off of the given size is within bounds. |
|
| 90 |
+func (mm *Memory) bounds(off uint64, size uint64) bool {
|
|
| 91 |
+ return off+size < uint64(len(mm.b)) |
|
| 92 |
+} |
|
| 93 |
+ |
|
| 94 |
+// ReadAt implements [io.ReaderAt]. Useful for creating a new [io.OffsetWriter]. |
|
| 95 |
+// |
|
| 96 |
+// See [Memory] for details around memory coherence. |
|
| 97 |
+func (mm *Memory) ReadAt(p []byte, off int64) (int, error) {
|
|
| 98 |
+ if mm.b == nil {
|
|
| 99 |
+ return 0, fmt.Errorf("memory-mapped region closed")
|
|
| 100 |
+ } |
|
| 101 |
+ |
|
| 102 |
+ if p == nil {
|
|
| 103 |
+ return 0, fmt.Errorf("input buffer p is nil")
|
|
| 104 |
+ } |
|
| 105 |
+ |
|
| 106 |
+ if off < 0 || off >= int64(len(mm.b)) {
|
|
| 107 |
+ return 0, fmt.Errorf("read offset out of range")
|
|
| 108 |
+ } |
|
| 109 |
+ |
|
| 110 |
+ n := copy(p, mm.b[off:]) |
|
| 111 |
+ if n < len(p) {
|
|
| 112 |
+ return n, io.EOF |
|
| 113 |
+ } |
|
| 114 |
+ |
|
| 115 |
+ return n, nil |
|
| 116 |
+} |
|
| 117 |
+ |
|
| 118 |
+// WriteAt implements [io.WriterAt]. Useful for creating a new |
|
| 119 |
+// [io.SectionReader]. |
|
| 120 |
+// |
|
| 121 |
+// See [Memory] for details around memory coherence. |
|
| 122 |
+func (mm *Memory) WriteAt(p []byte, off int64) (int, error) {
|
|
| 123 |
+ if mm.b == nil {
|
|
| 124 |
+ return 0, fmt.Errorf("memory-mapped region closed")
|
|
| 125 |
+ } |
|
| 126 |
+ if mm.ro {
|
|
| 127 |
+ return 0, fmt.Errorf("memory-mapped region not writable: %w", ErrReadOnly)
|
|
| 128 |
+ } |
|
| 129 |
+ |
|
| 130 |
+ if p == nil {
|
|
| 131 |
+ return 0, fmt.Errorf("output buffer p is nil")
|
|
| 132 |
+ } |
|
| 133 |
+ |
|
| 134 |
+ if off < 0 || off >= int64(len(mm.b)) {
|
|
| 135 |
+ return 0, fmt.Errorf("write offset out of range")
|
|
| 136 |
+ } |
|
| 137 |
+ |
|
| 138 |
+ n := copy(mm.b[off:], p) |
|
| 139 |
+ if n < len(p) {
|
|
| 140 |
+ return n, io.EOF |
|
| 141 |
+ } |
|
| 142 |
+ |
|
| 143 |
+ return n, nil |
|
| 144 |
+} |
| ... | ... |
@@ -16,6 +16,7 @@ import ( |
| 16 | 16 |
"github.com/cilium/ebpf/btf" |
| 17 | 17 |
"github.com/cilium/ebpf/internal" |
| 18 | 18 |
"github.com/cilium/ebpf/internal/kallsyms" |
| 19 |
+ "github.com/cilium/ebpf/internal/linux" |
|
| 19 | 20 |
"github.com/cilium/ebpf/internal/sys" |
| 20 | 21 |
"github.com/cilium/ebpf/internal/sysenc" |
| 21 | 22 |
"github.com/cilium/ebpf/internal/unix" |
| ... | ... |
@@ -46,14 +47,15 @@ const ( |
| 46 | 46 |
outputPad = 256 + 2 |
| 47 | 47 |
) |
| 48 | 48 |
|
| 49 |
-// Deprecated: the correct log size is now detected automatically and this |
|
| 50 |
-// constant is unused. |
|
| 51 |
-const DefaultVerifierLogSize = 64 * 1024 |
|
| 52 |
- |
|
| 53 | 49 |
// minVerifierLogSize is the default number of bytes allocated for the |
| 54 | 50 |
// verifier log. |
| 55 | 51 |
const minVerifierLogSize = 64 * 1024 |
| 56 | 52 |
|
| 53 |
+// maxVerifierLogSize is the maximum size of verifier log buffer the kernel |
|
| 54 |
+// will accept before returning EINVAL. May be increased to MaxUint32 in the |
|
| 55 |
+// future, but avoid the unnecessary EINVAL for now. |
|
| 56 |
+const maxVerifierLogSize = math.MaxUint32 >> 2 |
|
| 57 |
+ |
|
| 57 | 58 |
// ProgramOptions control loading a program into the kernel. |
| 58 | 59 |
type ProgramOptions struct {
|
| 59 | 60 |
// Bitmap controlling the detail emitted by the kernel's eBPF verifier log. |
| ... | ... |
@@ -73,9 +75,10 @@ type ProgramOptions struct {
|
| 73 | 73 |
// attempt at loading the program. |
| 74 | 74 |
LogLevel LogLevel |
| 75 | 75 |
|
| 76 |
- // Deprecated: the correct log buffer size is determined automatically |
|
| 77 |
- // and this field is ignored. |
|
| 78 |
- LogSize int |
|
| 76 |
+ // Starting size of the verifier log buffer. If the verifier log is larger |
|
| 77 |
+ // than this size, the buffer will be grown to fit the entire log. Leave at |
|
| 78 |
+ // its default value unless troubleshooting. |
|
| 79 |
+ LogSizeStart uint32 |
|
| 79 | 80 |
|
| 80 | 81 |
// Disables the verifier log completely, regardless of other options. |
| 81 | 82 |
LogDisabled bool |
| ... | ... |
@@ -162,26 +165,35 @@ func (ps *ProgramSpec) Tag() (string, error) {
|
| 162 | 162 |
return ps.Instructions.Tag(internal.NativeEndian) |
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 |
-// KernelModule returns the kernel module, if any, the AttachTo function is contained in. |
|
| 166 |
-func (ps *ProgramSpec) KernelModule() (string, error) {
|
|
| 165 |
+// kernelModule returns the kernel module providing the symbol in |
|
| 166 |
+// ProgramSpec.AttachTo, if any. Returns an empty string if the symbol is not |
|
| 167 |
+// present or not part of a kernel module. |
|
| 168 |
+func (ps *ProgramSpec) kernelModule() (string, error) {
|
|
| 169 |
+ if ps.targetsKernelModule() {
|
|
| 170 |
+ return kallsyms.Module(ps.AttachTo) |
|
| 171 |
+ } |
|
| 172 |
+ |
|
| 173 |
+ return "", nil |
|
| 174 |
+} |
|
| 175 |
+ |
|
| 176 |
+// targetsKernelModule returns true if the program supports being attached to a |
|
| 177 |
+// symbol provided by a kernel module. |
|
| 178 |
+func (ps *ProgramSpec) targetsKernelModule() bool {
|
|
| 167 | 179 |
if ps.AttachTo == "" {
|
| 168 |
- return "", nil |
|
| 180 |
+ return false |
|
| 169 | 181 |
} |
| 170 | 182 |
|
| 171 | 183 |
switch ps.Type {
|
| 172 |
- default: |
|
| 173 |
- return "", nil |
|
| 174 | 184 |
case Tracing: |
| 175 | 185 |
switch ps.AttachType {
|
| 176 |
- default: |
|
| 177 |
- return "", nil |
|
| 178 |
- case AttachTraceFEntry: |
|
| 179 |
- case AttachTraceFExit: |
|
| 186 |
+ case AttachTraceFEntry, AttachTraceFExit: |
|
| 187 |
+ return true |
|
| 180 | 188 |
} |
| 181 |
- fallthrough |
|
| 182 | 189 |
case Kprobe: |
| 183 |
- return kallsyms.KernelModule(ps.AttachTo) |
|
| 190 |
+ return true |
|
| 184 | 191 |
} |
| 192 |
+ |
|
| 193 |
+ return false |
|
| 185 | 194 |
} |
| 186 | 195 |
|
| 187 | 196 |
// VerifierError is returned by [NewProgram] and [NewProgramWithOptions] if a |
| ... | ... |
@@ -261,7 +273,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er |
| 261 | 261 |
// Overwrite Kprobe program version if set to zero or the magic version constant. |
| 262 | 262 |
kv := spec.KernelVersion |
| 263 | 263 |
if spec.Type == Kprobe && (kv == 0 || kv == internal.MagicKernelVersion) {
|
| 264 |
- v, err := internal.KernelVersion() |
|
| 264 |
+ v, err := linux.KernelVersion() |
|
| 265 | 265 |
if err != nil {
|
| 266 | 266 |
return nil, fmt.Errorf("detecting kernel version: %w", err)
|
| 267 | 267 |
} |
| ... | ... |
@@ -283,7 +295,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er |
| 283 | 283 |
insns := make(asm.Instructions, len(spec.Instructions)) |
| 284 | 284 |
copy(insns, spec.Instructions) |
| 285 | 285 |
|
| 286 |
- kmodName, err := spec.KernelModule() |
|
| 286 |
+ kmodName, err := spec.kernelModule() |
|
| 287 | 287 |
if err != nil {
|
| 288 | 288 |
return nil, fmt.Errorf("kernel module search: %w", err)
|
| 289 | 289 |
} |
| ... | ... |
@@ -344,6 +356,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er |
| 344 | 344 |
} |
| 345 | 345 |
defer kconfig.Close() |
| 346 | 346 |
|
| 347 |
+ if err := resolveKsymReferences(insns); err != nil {
|
|
| 348 |
+ return nil, fmt.Errorf("resolve .ksyms: %w", err)
|
|
| 349 |
+ } |
|
| 350 |
+ |
|
| 347 | 351 |
if err := fixupAndValidate(insns); err != nil {
|
| 348 | 352 |
return nil, err |
| 349 | 353 |
} |
| ... | ... |
@@ -395,9 +411,10 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er |
| 395 | 395 |
|
| 396 | 396 |
// The caller requested a specific verifier log level. Set up the log buffer |
| 397 | 397 |
// so that there is a chance of loading the program in a single shot. |
| 398 |
+ logSize := internal.Between(opts.LogSizeStart, minVerifierLogSize, maxVerifierLogSize) |
|
| 398 | 399 |
var logBuf []byte |
| 399 | 400 |
if !opts.LogDisabled && opts.LogLevel != 0 {
|
| 400 |
- logBuf = make([]byte, minVerifierLogSize) |
|
| 401 |
+ logBuf = make([]byte, logSize) |
|
| 401 | 402 |
attr.LogLevel = opts.LogLevel |
| 402 | 403 |
attr.LogSize = uint32(len(logBuf)) |
| 403 | 404 |
attr.LogBuf = sys.NewSlicePointer(logBuf) |
| ... | ... |
@@ -431,12 +448,11 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er |
| 431 | 431 |
attr.LogLevel = LogLevelBranch |
| 432 | 432 |
} |
| 433 | 433 |
|
| 434 |
- // Make an educated guess how large the buffer should be. Start |
|
| 435 |
- // at minVerifierLogSize and then double the size. |
|
| 436 |
- logSize := uint32(max(len(logBuf)*2, minVerifierLogSize)) |
|
| 437 |
- if int(logSize) < len(logBuf) {
|
|
| 438 |
- return nil, errors.New("overflow while probing log buffer size")
|
|
| 439 |
- } |
|
| 434 |
+ // Make an educated guess how large the buffer should be by multiplying. |
|
| 435 |
+ // Ensure the size doesn't overflow. |
|
| 436 |
+ const factor = 2 |
|
| 437 |
+ logSize = internal.Between(logSize, minVerifierLogSize, maxVerifierLogSize/factor) |
|
| 438 |
+ logSize *= factor |
|
| 440 | 439 |
|
| 441 | 440 |
if attr.LogTrueSize != 0 {
|
| 442 | 441 |
// The kernel has given us a hint how large the log buffer has to be. |
| ... | ... |
@@ -462,6 +478,12 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er |
| 462 | 462 |
return nil, fmt.Errorf("load program: %w (MEMLOCK may be too low, consider rlimit.RemoveMemlock)", err)
|
| 463 | 463 |
} |
| 464 | 464 |
|
| 465 |
+ case errors.Is(err, unix.EFAULT): |
|
| 466 |
+ // EFAULT is returned when the kernel hits a verifier bug, and always |
|
| 467 |
+ // overrides ENOSPC, defeating the buffer growth strategy. Warn the user |
|
| 468 |
+ // that they may need to increase the buffer size manually. |
|
| 469 |
+ return nil, fmt.Errorf("load program: %w (hit verifier bug, increase LogSizeStart to fit the log and check dmesg)", err)
|
|
| 470 |
+ |
|
| 465 | 471 |
case errors.Is(err, unix.EINVAL): |
| 466 | 472 |
if bytes.Contains(tail, coreBadCall) {
|
| 467 | 473 |
err = errBadRelocation |
| ... | ... |
@@ -598,7 +620,7 @@ func (p *Program) Clone() (*Program, error) {
|
| 598 | 598 |
// This requires bpffs to be mounted above fileName. |
| 599 | 599 |
// See https://docs.cilium.io/en/stable/network/kubernetes/configuration/#mounting-bpffs-with-systemd |
| 600 | 600 |
func (p *Program) Pin(fileName string) error {
|
| 601 |
- if err := internal.Pin(p.pinnedPath, fileName, p.fd); err != nil {
|
|
| 601 |
+ if err := sys.Pin(p.pinnedPath, fileName, p.fd); err != nil {
|
|
| 602 | 602 |
return err |
| 603 | 603 |
} |
| 604 | 604 |
p.pinnedPath = fileName |
| ... | ... |
@@ -611,7 +633,7 @@ func (p *Program) Pin(fileName string) error {
|
| 611 | 611 |
// |
| 612 | 612 |
// Unpinning an unpinned Program returns nil. |
| 613 | 613 |
func (p *Program) Unpin() error {
|
| 614 |
- if err := internal.Unpin(p.pinnedPath); err != nil {
|
|
| 614 |
+ if err := sys.Unpin(p.pinnedPath); err != nil {
|
|
| 615 | 615 |
return err |
| 616 | 616 |
} |
| 617 | 617 |
p.pinnedPath = "" |
| ... | ... |
@@ -699,6 +721,10 @@ func (p *Program) Test(in []byte) (uint32, []byte, error) {
|
| 699 | 699 |
// |
| 700 | 700 |
// Note: the same restrictions from Test apply. |
| 701 | 701 |
func (p *Program) Run(opts *RunOptions) (uint32, error) {
|
| 702 |
+ if opts == nil {
|
|
| 703 |
+ opts = &RunOptions{}
|
|
| 704 |
+ } |
|
| 705 |
+ |
|
| 702 | 706 |
ret, _, err := p.run(opts) |
| 703 | 707 |
if err != nil {
|
| 704 | 708 |
return ret, fmt.Errorf("run program: %w", err)
|
| ... | ... |
@@ -732,7 +758,7 @@ func (p *Program) Benchmark(in []byte, repeat int, reset func()) (uint32, time.D |
| 732 | 732 |
return ret, total, nil |
| 733 | 733 |
} |
| 734 | 734 |
|
| 735 |
-var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", "4.12", func() error {
|
|
| 735 |
+var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", func() error {
|
|
| 736 | 736 |
prog, err := NewProgram(&ProgramSpec{
|
| 737 | 737 |
// SocketFilter does not require privileges on newer kernels. |
| 738 | 738 |
Type: SocketFilter, |
| ... | ... |
@@ -774,7 +800,7 @@ var haveProgRun = internal.NewFeatureTest("BPF_PROG_RUN", "4.12", func() error {
|
| 774 | 774 |
} |
| 775 | 775 |
|
| 776 | 776 |
return err |
| 777 |
-}) |
|
| 777 |
+}, "4.12") |
|
| 778 | 778 |
|
| 779 | 779 |
func (p *Program) run(opts *RunOptions) (uint32, time.Duration, error) {
|
| 780 | 780 |
if uint(len(opts.Data)) > math.MaxUint32 {
|
| ... | ... |
@@ -883,6 +909,10 @@ func unmarshalProgram(buf sysenc.Buffer) (*Program, error) {
|
| 883 | 883 |
} |
| 884 | 884 |
|
| 885 | 885 |
func marshalProgram(p *Program, length int) ([]byte, error) {
|
| 886 |
+ if p == nil {
|
|
| 887 |
+ return nil, errors.New("can't marshal a nil Program")
|
|
| 888 |
+ } |
|
| 889 |
+ |
|
| 886 | 890 |
if length != 4 {
|
| 887 | 891 |
return nil, fmt.Errorf("can't marshal program to %d bytes", length)
|
| 888 | 892 |
} |
| ... | ... |
@@ -892,11 +922,12 @@ func marshalProgram(p *Program, length int) ([]byte, error) {
|
| 892 | 892 |
return buf, nil |
| 893 | 893 |
} |
| 894 | 894 |
|
| 895 |
-// LoadPinnedProgram loads a Program from a BPF file. |
|
| 895 |
+// LoadPinnedProgram loads a Program from a pin (file) on the BPF virtual |
|
| 896 |
+// filesystem. |
|
| 896 | 897 |
// |
| 897 | 898 |
// Requires at least Linux 4.11. |
| 898 | 899 |
func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) {
|
| 899 |
- fd, err := sys.ObjGet(&sys.ObjGetAttr{
|
|
| 900 |
+ fd, typ, err := sys.ObjGetTyped(&sys.ObjGetAttr{
|
|
| 900 | 901 |
Pathname: sys.NewStringPointer(fileName), |
| 901 | 902 |
FileFlags: opts.Marshal(), |
| 902 | 903 |
}) |
| ... | ... |
@@ -904,6 +935,11 @@ func LoadPinnedProgram(fileName string, opts *LoadPinOptions) (*Program, error) |
| 904 | 904 |
return nil, err |
| 905 | 905 |
} |
| 906 | 906 |
|
| 907 |
+ if typ != sys.BPF_TYPE_PROG {
|
|
| 908 |
+ _ = fd.Close() |
|
| 909 |
+ return nil, fmt.Errorf("%s is not a Program", fileName)
|
|
| 910 |
+ } |
|
| 911 |
+ |
|
| 907 | 912 |
info, err := newProgramInfoFromFd(fd) |
| 908 | 913 |
if err != nil {
|
| 909 | 914 |
_ = fd.Close() |
| ... | ... |
@@ -10,6 +10,7 @@ import ( |
| 10 | 10 |
|
| 11 | 11 |
"github.com/cilium/ebpf/asm" |
| 12 | 12 |
"github.com/cilium/ebpf/internal" |
| 13 |
+ "github.com/cilium/ebpf/internal/linux" |
|
| 13 | 14 |
"github.com/cilium/ebpf/internal/sys" |
| 14 | 15 |
"github.com/cilium/ebpf/internal/tracefs" |
| 15 | 16 |
"github.com/cilium/ebpf/internal/unix" |
| ... | ... |
@@ -60,7 +61,7 @@ func progLoad(insns asm.Instructions, typ ProgramType, license string) (*sys.FD, |
| 60 | 60 |
}) |
| 61 | 61 |
} |
| 62 | 62 |
|
| 63 |
-var haveNestedMaps = internal.NewFeatureTest("nested maps", "4.12", func() error {
|
|
| 63 |
+var haveNestedMaps = internal.NewFeatureTest("nested maps", func() error {
|
|
| 64 | 64 |
_, err := sys.MapCreate(&sys.MapCreateAttr{
|
| 65 | 65 |
MapType: sys.MapType(ArrayOfMaps), |
| 66 | 66 |
KeySize: 4, |
| ... | ... |
@@ -76,9 +77,9 @@ var haveNestedMaps = internal.NewFeatureTest("nested maps", "4.12", func() error
|
| 76 | 76 |
return nil |
| 77 | 77 |
} |
| 78 | 78 |
return err |
| 79 |
-}) |
|
| 79 |
+}, "4.12") |
|
| 80 | 80 |
|
| 81 |
-var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only maps", "5.2", func() error {
|
|
| 81 |
+var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only maps", func() error {
|
|
| 82 | 82 |
// This checks BPF_F_RDONLY_PROG and BPF_F_WRONLY_PROG. Since |
| 83 | 83 |
// BPF_MAP_FREEZE appeared in 5.2 as well we don't do a separate check. |
| 84 | 84 |
m, err := sys.MapCreate(&sys.MapCreateAttr{
|
| ... | ... |
@@ -86,39 +87,39 @@ var haveMapMutabilityModifiers = internal.NewFeatureTest("read- and write-only m
|
| 86 | 86 |
KeySize: 4, |
| 87 | 87 |
ValueSize: 4, |
| 88 | 88 |
MaxEntries: 1, |
| 89 |
- MapFlags: unix.BPF_F_RDONLY_PROG, |
|
| 89 |
+ MapFlags: sys.BPF_F_RDONLY_PROG, |
|
| 90 | 90 |
}) |
| 91 | 91 |
if err != nil {
|
| 92 | 92 |
return internal.ErrNotSupported |
| 93 | 93 |
} |
| 94 | 94 |
_ = m.Close() |
| 95 | 95 |
return nil |
| 96 |
-}) |
|
| 96 |
+}, "5.2") |
|
| 97 | 97 |
|
| 98 |
-var haveMmapableMaps = internal.NewFeatureTest("mmapable maps", "5.5", func() error {
|
|
| 98 |
+var haveMmapableMaps = internal.NewFeatureTest("mmapable maps", func() error {
|
|
| 99 | 99 |
// This checks BPF_F_MMAPABLE, which appeared in 5.5 for array maps. |
| 100 | 100 |
m, err := sys.MapCreate(&sys.MapCreateAttr{
|
| 101 | 101 |
MapType: sys.MapType(Array), |
| 102 | 102 |
KeySize: 4, |
| 103 | 103 |
ValueSize: 4, |
| 104 | 104 |
MaxEntries: 1, |
| 105 |
- MapFlags: unix.BPF_F_MMAPABLE, |
|
| 105 |
+ MapFlags: sys.BPF_F_MMAPABLE, |
|
| 106 | 106 |
}) |
| 107 | 107 |
if err != nil {
|
| 108 | 108 |
return internal.ErrNotSupported |
| 109 | 109 |
} |
| 110 | 110 |
_ = m.Close() |
| 111 | 111 |
return nil |
| 112 |
-}) |
|
| 112 |
+}, "5.5") |
|
| 113 | 113 |
|
| 114 |
-var haveInnerMaps = internal.NewFeatureTest("inner maps", "5.10", func() error {
|
|
| 114 |
+var haveInnerMaps = internal.NewFeatureTest("inner maps", func() error {
|
|
| 115 | 115 |
// This checks BPF_F_INNER_MAP, which appeared in 5.10. |
| 116 | 116 |
m, err := sys.MapCreate(&sys.MapCreateAttr{
|
| 117 | 117 |
MapType: sys.MapType(Array), |
| 118 | 118 |
KeySize: 4, |
| 119 | 119 |
ValueSize: 4, |
| 120 | 120 |
MaxEntries: 1, |
| 121 |
- MapFlags: unix.BPF_F_INNER_MAP, |
|
| 121 |
+ MapFlags: sys.BPF_F_INNER_MAP, |
|
| 122 | 122 |
}) |
| 123 | 123 |
|
| 124 | 124 |
if err != nil {
|
| ... | ... |
@@ -126,16 +127,16 @@ var haveInnerMaps = internal.NewFeatureTest("inner maps", "5.10", func() error {
|
| 126 | 126 |
} |
| 127 | 127 |
_ = m.Close() |
| 128 | 128 |
return nil |
| 129 |
-}) |
|
| 129 |
+}, "5.10") |
|
| 130 | 130 |
|
| 131 |
-var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", "4.6", func() error {
|
|
| 131 |
+var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", func() error {
|
|
| 132 | 132 |
// This checks BPF_F_NO_PREALLOC, which appeared in 4.6. |
| 133 | 133 |
m, err := sys.MapCreate(&sys.MapCreateAttr{
|
| 134 | 134 |
MapType: sys.MapType(Hash), |
| 135 | 135 |
KeySize: 4, |
| 136 | 136 |
ValueSize: 4, |
| 137 | 137 |
MaxEntries: 1, |
| 138 |
- MapFlags: unix.BPF_F_NO_PREALLOC, |
|
| 138 |
+ MapFlags: sys.BPF_F_NO_PREALLOC, |
|
| 139 | 139 |
}) |
| 140 | 140 |
|
| 141 | 141 |
if err != nil {
|
| ... | ... |
@@ -143,7 +144,7 @@ var haveNoPreallocMaps = internal.NewFeatureTest("prealloc maps", "4.6", func()
|
| 143 | 143 |
} |
| 144 | 144 |
_ = m.Close() |
| 145 | 145 |
return nil |
| 146 |
-}) |
|
| 146 |
+}, "4.6") |
|
| 147 | 147 |
|
| 148 | 148 |
func wrapMapError(err error) error {
|
| 149 | 149 |
if err == nil {
|
| ... | ... |
@@ -169,7 +170,7 @@ func wrapMapError(err error) error {
|
| 169 | 169 |
return err |
| 170 | 170 |
} |
| 171 | 171 |
|
| 172 |
-var haveObjName = internal.NewFeatureTest("object names", "4.15", func() error {
|
|
| 172 |
+var haveObjName = internal.NewFeatureTest("object names", func() error {
|
|
| 173 | 173 |
attr := sys.MapCreateAttr{
|
| 174 | 174 |
MapType: sys.MapType(Array), |
| 175 | 175 |
KeySize: 4, |
| ... | ... |
@@ -178,16 +179,24 @@ var haveObjName = internal.NewFeatureTest("object names", "4.15", func() error {
|
| 178 | 178 |
MapName: sys.NewObjName("feature_test"),
|
| 179 | 179 |
} |
| 180 | 180 |
|
| 181 |
+ // Tolerate EPERM as this runs during ELF loading which is potentially |
|
| 182 |
+ // unprivileged. Only EINVAL is conclusive, thrown from CHECK_ATTR. |
|
| 181 | 183 |
fd, err := sys.MapCreate(&attr) |
| 182 |
- if err != nil {
|
|
| 184 |
+ if errors.Is(err, unix.EPERM) {
|
|
| 185 |
+ return nil |
|
| 186 |
+ } |
|
| 187 |
+ if errors.Is(err, unix.EINVAL) {
|
|
| 183 | 188 |
return internal.ErrNotSupported |
| 184 | 189 |
} |
| 190 |
+ if err != nil {
|
|
| 191 |
+ return err |
|
| 192 |
+ } |
|
| 185 | 193 |
|
| 186 | 194 |
_ = fd.Close() |
| 187 | 195 |
return nil |
| 188 |
-}) |
|
| 196 |
+}, "4.15") |
|
| 189 | 197 |
|
| 190 |
-var objNameAllowsDot = internal.NewFeatureTest("dot in object names", "5.2", func() error {
|
|
| 198 |
+var objNameAllowsDot = internal.NewFeatureTest("dot in object names", func() error {
|
|
| 191 | 199 |
if err := haveObjName(); err != nil {
|
| 192 | 200 |
return err |
| 193 | 201 |
} |
| ... | ... |
@@ -200,16 +209,25 @@ var objNameAllowsDot = internal.NewFeatureTest("dot in object names", "5.2", fun
|
| 200 | 200 |
MapName: sys.NewObjName(".test"),
|
| 201 | 201 |
} |
| 202 | 202 |
|
| 203 |
+ // Tolerate EPERM, otherwise MapSpec.Name has its dots removed when run by |
|
| 204 |
+ // unprivileged tools. (bpf2go, other code gen). Only EINVAL is conclusive, |
|
| 205 |
+ // thrown from bpf_obj_name_cpy(). |
|
| 203 | 206 |
fd, err := sys.MapCreate(&attr) |
| 204 |
- if err != nil {
|
|
| 207 |
+ if errors.Is(err, unix.EPERM) {
|
|
| 208 |
+ return nil |
|
| 209 |
+ } |
|
| 210 |
+ if errors.Is(err, unix.EINVAL) {
|
|
| 205 | 211 |
return internal.ErrNotSupported |
| 206 | 212 |
} |
| 213 |
+ if err != nil {
|
|
| 214 |
+ return err |
|
| 215 |
+ } |
|
| 207 | 216 |
|
| 208 | 217 |
_ = fd.Close() |
| 209 | 218 |
return nil |
| 210 |
-}) |
|
| 219 |
+}, "5.2") |
|
| 211 | 220 |
|
| 212 |
-var haveBatchAPI = internal.NewFeatureTest("map batch api", "5.6", func() error {
|
|
| 221 |
+var haveBatchAPI = internal.NewFeatureTest("map batch api", func() error {
|
|
| 213 | 222 |
var maxEntries uint32 = 2 |
| 214 | 223 |
attr := sys.MapCreateAttr{
|
| 215 | 224 |
MapType: sys.MapType(Hash), |
| ... | ... |
@@ -239,9 +257,9 @@ var haveBatchAPI = internal.NewFeatureTest("map batch api", "5.6", func() error
|
| 239 | 239 |
return internal.ErrNotSupported |
| 240 | 240 |
} |
| 241 | 241 |
return nil |
| 242 |
-}) |
|
| 242 |
+}, "5.6") |
|
| 243 | 243 |
|
| 244 |
-var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", "5.5", func() error {
|
|
| 244 |
+var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", func() error {
|
|
| 245 | 245 |
insns := asm.Instructions{
|
| 246 | 246 |
asm.Mov.Reg(asm.R1, asm.R10), |
| 247 | 247 |
asm.Add.Imm(asm.R1, -8), |
| ... | ... |
@@ -257,9 +275,9 @@ var haveProbeReadKernel = internal.NewFeatureTest("bpf_probe_read_kernel", "5.5"
|
| 257 | 257 |
} |
| 258 | 258 |
_ = fd.Close() |
| 259 | 259 |
return nil |
| 260 |
-}) |
|
| 260 |
+}, "5.5") |
|
| 261 | 261 |
|
| 262 |
-var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", "4.16", func() error {
|
|
| 262 |
+var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", func() error {
|
|
| 263 | 263 |
insns := asm.Instructions{
|
| 264 | 264 |
asm.Call.Label("prog2").WithSymbol("prog1"),
|
| 265 | 265 |
asm.Return(), |
| ... | ... |
@@ -273,10 +291,10 @@ var haveBPFToBPFCalls = internal.NewFeatureTest("bpf2bpf calls", "4.16", func()
|
| 273 | 273 |
} |
| 274 | 274 |
_ = fd.Close() |
| 275 | 275 |
return nil |
| 276 |
-}) |
|
| 276 |
+}, "4.16") |
|
| 277 | 277 |
|
| 278 |
-var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", "4.17", func() error {
|
|
| 279 |
- prefix := internal.PlatformPrefix() |
|
| 278 |
+var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", func() error {
|
|
| 279 |
+ prefix := linux.PlatformPrefix() |
|
| 280 | 280 |
if prefix == "" {
|
| 281 | 281 |
return fmt.Errorf("unable to find the platform prefix for (%s)", runtime.GOARCH)
|
| 282 | 282 |
} |
| ... | ... |
@@ -302,9 +320,9 @@ var haveSyscallWrapper = internal.NewFeatureTest("syscall wrapper", "4.17", func
|
| 302 | 302 |
} |
| 303 | 303 |
|
| 304 | 304 |
return evt.Close() |
| 305 |
-}) |
|
| 305 |
+}, "4.17") |
|
| 306 | 306 |
|
| 307 |
-var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", "5.0", func() error {
|
|
| 307 |
+var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", func() error {
|
|
| 308 | 308 |
insns := asm.Instructions{
|
| 309 | 309 |
asm.Mov.Imm(asm.R0, 0), |
| 310 | 310 |
asm.Return(), |
| ... | ... |
@@ -334,4 +352,4 @@ var haveProgramExtInfos = internal.NewFeatureTest("program ext_infos", "5.0", fu
|
| 334 | 334 |
} |
| 335 | 335 |
|
| 336 | 336 |
return err |
| 337 |
-}) |
|
| 337 |
+}, "5.0") |
| ... | ... |
@@ -2,7 +2,6 @@ package ebpf |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"github.com/cilium/ebpf/internal/sys" |
| 5 |
- "github.com/cilium/ebpf/internal/unix" |
|
| 6 | 5 |
) |
| 7 | 6 |
|
| 8 | 7 |
//go:generate go run golang.org/x/tools/cmd/stringer@latest -output types_string.go -type=MapType,ProgramType,PinType |
| ... | ... |
@@ -95,6 +94,14 @@ const ( |
| 95 | 95 |
InodeStorage |
| 96 | 96 |
// TaskStorage - Specialized local storage map for task_struct. |
| 97 | 97 |
TaskStorage |
| 98 |
+ // BloomFilter - Space-efficient data structure to quickly test whether an element exists in a set. |
|
| 99 |
+ BloomFilter |
|
| 100 |
+ // UserRingbuf - The reverse of RingBuf, used to send messages from user space to BPF programs. |
|
| 101 |
+ UserRingbuf |
|
| 102 |
+ // CgroupStorage - Store data keyed on a cgroup. If the cgroup disappears, the key is automatically removed. |
|
| 103 |
+ CgroupStorage |
|
| 104 |
+ // Arena - Sparse shared memory region between a BPF program and user space. |
|
| 105 |
+ Arena |
|
| 98 | 106 |
) |
| 99 | 107 |
|
| 100 | 108 |
// hasPerCPUValue returns true if the Map stores a value per CPU. |
| ... | ... |
@@ -120,6 +127,21 @@ func (mt MapType) canStoreProgram() bool {
|
| 120 | 120 |
return mt == ProgramArray |
| 121 | 121 |
} |
| 122 | 122 |
|
| 123 |
+// canHaveValueSize returns true if the map type supports setting a value size. |
|
| 124 |
+func (mt MapType) canHaveValueSize() bool {
|
|
| 125 |
+ switch mt {
|
|
| 126 |
+ case RingBuf, Arena: |
|
| 127 |
+ return false |
|
| 128 |
+ |
|
| 129 |
+ // Special-case perf events since they require a value size of either 0 or 4 |
|
| 130 |
+ // for historical reasons. Let the library fix this up later. |
|
| 131 |
+ case PerfEventArray: |
|
| 132 |
+ return false |
|
| 133 |
+ } |
|
| 134 |
+ |
|
| 135 |
+ return true |
|
| 136 |
+} |
|
| 137 |
+ |
|
| 123 | 138 |
// ProgramType of the eBPF program |
| 124 | 139 |
type ProgramType uint32 |
| 125 | 140 |
|
| ... | ... |
@@ -214,6 +236,7 @@ const ( |
| 214 | 214 |
AttachSkReuseportSelectOrMigrate = AttachType(sys.BPF_SK_REUSEPORT_SELECT_OR_MIGRATE) |
| 215 | 215 |
AttachPerfEvent = AttachType(sys.BPF_PERF_EVENT) |
| 216 | 216 |
AttachTraceKprobeMulti = AttachType(sys.BPF_TRACE_KPROBE_MULTI) |
| 217 |
+ AttachTraceKprobeSession = AttachType(sys.BPF_TRACE_KPROBE_SESSION) |
|
| 217 | 218 |
AttachLSMCgroup = AttachType(sys.BPF_LSM_CGROUP) |
| 218 | 219 |
AttachStructOps = AttachType(sys.BPF_STRUCT_OPS) |
| 219 | 220 |
AttachNetfilter = AttachType(sys.BPF_NETFILTER) |
| ... | ... |
@@ -263,10 +286,10 @@ func (lpo *LoadPinOptions) Marshal() uint32 {
|
| 263 | 263 |
|
| 264 | 264 |
flags := lpo.Flags |
| 265 | 265 |
if lpo.ReadOnly {
|
| 266 |
- flags |= unix.BPF_F_RDONLY |
|
| 266 |
+ flags |= sys.BPF_F_RDONLY |
|
| 267 | 267 |
} |
| 268 | 268 |
if lpo.WriteOnly {
|
| 269 |
- flags |= unix.BPF_F_WRONLY |
|
| 269 |
+ flags |= sys.BPF_F_WRONLY |
|
| 270 | 270 |
} |
| 271 | 271 |
return flags |
| 272 | 272 |
} |
| ... | ... |
@@ -38,11 +38,15 @@ func _() {
|
| 38 | 38 |
_ = x[RingBuf-27] |
| 39 | 39 |
_ = x[InodeStorage-28] |
| 40 | 40 |
_ = x[TaskStorage-29] |
| 41 |
+ _ = x[BloomFilter-30] |
|
| 42 |
+ _ = x[UserRingbuf-31] |
|
| 43 |
+ _ = x[CgroupStorage-32] |
|
| 44 |
+ _ = x[Arena-33] |
|
| 41 | 45 |
} |
| 42 | 46 |
|
| 43 |
-const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStorage" |
|
| 47 |
+const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStorageBloomFilterUserRingbufCgroupStorageArena" |
|
| 44 | 48 |
|
| 45 |
-var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 260, 267, 279, 290}
|
|
| 49 |
+var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 260, 267, 279, 290, 301, 312, 325, 330}
|
|
| 46 | 50 |
|
| 47 | 51 |
func (i MapType) String() string {
|
| 48 | 52 |
if i >= MapType(len(_MapType_index)-1) {
|
| 49 | 53 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,230 @@ |
| 0 |
+package ebpf |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "io" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/cilium/ebpf/btf" |
|
| 7 |
+ "github.com/cilium/ebpf/internal/sysenc" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+// VariableSpec is a convenience wrapper for modifying global variables of a |
|
| 11 |
+// CollectionSpec before loading it into the kernel. |
|
| 12 |
+// |
|
| 13 |
+// All operations on a VariableSpec's underlying MapSpec are performed in the |
|
| 14 |
+// host's native endianness. |
|
| 15 |
+type VariableSpec struct {
|
|
| 16 |
+ name string |
|
| 17 |
+ offset uint64 |
|
| 18 |
+ size uint64 |
|
| 19 |
+ |
|
| 20 |
+ m *MapSpec |
|
| 21 |
+ t *btf.Var |
|
| 22 |
+} |
|
| 23 |
+ |
|
| 24 |
+// Set sets the value of the VariableSpec to the provided input using the host's |
|
| 25 |
+// native endianness. |
|
| 26 |
+func (s *VariableSpec) Set(in any) error {
|
|
| 27 |
+ buf, err := sysenc.Marshal(in, int(s.size)) |
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ return fmt.Errorf("marshaling value %s: %w", s.name, err)
|
|
| 30 |
+ } |
|
| 31 |
+ |
|
| 32 |
+ b, _, err := s.m.dataSection() |
|
| 33 |
+ if err != nil {
|
|
| 34 |
+ return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err)
|
|
| 35 |
+ } |
|
| 36 |
+ |
|
| 37 |
+ if int(s.offset+s.size) > len(b) {
|
|
| 38 |
+ return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name)
|
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ // MapSpec.Copy() performs a shallow copy. Fully copy the byte slice |
|
| 42 |
+ // to avoid any changes affecting other copies of the MapSpec. |
|
| 43 |
+ cpy := make([]byte, len(b)) |
|
| 44 |
+ copy(cpy, b) |
|
| 45 |
+ |
|
| 46 |
+ buf.CopyTo(cpy[s.offset : s.offset+s.size]) |
|
| 47 |
+ |
|
| 48 |
+ s.m.Contents[0] = MapKV{Key: uint32(0), Value: cpy}
|
|
| 49 |
+ |
|
| 50 |
+ return nil |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// Get writes the value of the VariableSpec to the provided output using the |
|
| 54 |
+// host's native endianness. |
|
| 55 |
+func (s *VariableSpec) Get(out any) error {
|
|
| 56 |
+ b, _, err := s.m.dataSection() |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ return fmt.Errorf("getting data section of map %s: %w", s.m.Name, err)
|
|
| 59 |
+ } |
|
| 60 |
+ |
|
| 61 |
+ if int(s.offset+s.size) > len(b) {
|
|
| 62 |
+ return fmt.Errorf("offset %d(+%d) for variable %s is out of bounds", s.offset, s.size, s.name)
|
|
| 63 |
+ } |
|
| 64 |
+ |
|
| 65 |
+ if err := sysenc.Unmarshal(out, b[s.offset:s.offset+s.size]); err != nil {
|
|
| 66 |
+ return fmt.Errorf("unmarshaling value: %w", err)
|
|
| 67 |
+ } |
|
| 68 |
+ |
|
| 69 |
+ return nil |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// Size returns the size of the variable in bytes. |
|
| 73 |
+func (s *VariableSpec) Size() uint64 {
|
|
| 74 |
+ return s.size |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+// MapName returns the name of the underlying MapSpec. |
|
| 78 |
+func (s *VariableSpec) MapName() string {
|
|
| 79 |
+ return s.m.Name |
|
| 80 |
+} |
|
| 81 |
+ |
|
| 82 |
+// Offset returns the offset of the variable in the underlying MapSpec. |
|
| 83 |
+func (s *VariableSpec) Offset() uint64 {
|
|
| 84 |
+ return s.offset |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+// Constant returns true if the VariableSpec represents a variable that is |
|
| 88 |
+// read-only from the perspective of the BPF program. |
|
| 89 |
+func (s *VariableSpec) Constant() bool {
|
|
| 90 |
+ return s.m.readOnly() |
|
| 91 |
+} |
|
| 92 |
+ |
|
| 93 |
+// Type returns the [btf.Var] representing the variable in its data section. |
|
| 94 |
+// This is useful for inspecting the variable's decl tags and the type |
|
| 95 |
+// information of the inner type. |
|
| 96 |
+// |
|
| 97 |
+// Returns nil if the original ELF object did not contain BTF information. |
|
| 98 |
+func (s *VariableSpec) Type() *btf.Var {
|
|
| 99 |
+ return s.t |
|
| 100 |
+} |
|
| 101 |
+ |
|
| 102 |
+func (s *VariableSpec) String() string {
|
|
| 103 |
+ return fmt.Sprintf("%s (type=%v, map=%s, offset=%d, size=%d)", s.name, s.t, s.m.Name, s.offset, s.size)
|
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// copy returns a new VariableSpec with the same values as the original, |
|
| 107 |
+// but with a different underlying MapSpec. This is useful when copying a |
|
| 108 |
+// CollectionSpec. Returns nil if a MapSpec with the same name is not found. |
|
| 109 |
+func (s *VariableSpec) copy(cpy *CollectionSpec) *VariableSpec {
|
|
| 110 |
+ out := &VariableSpec{
|
|
| 111 |
+ name: s.name, |
|
| 112 |
+ offset: s.offset, |
|
| 113 |
+ size: s.size, |
|
| 114 |
+ } |
|
| 115 |
+ if s.t != nil {
|
|
| 116 |
+ out.t = btf.Copy(s.t).(*btf.Var) |
|
| 117 |
+ } |
|
| 118 |
+ |
|
| 119 |
+ // Attempt to find a MapSpec with the same name in the copied CollectionSpec. |
|
| 120 |
+ for _, m := range cpy.Maps {
|
|
| 121 |
+ if m.Name == s.m.Name {
|
|
| 122 |
+ out.m = m |
|
| 123 |
+ return out |
|
| 124 |
+ } |
|
| 125 |
+ } |
|
| 126 |
+ |
|
| 127 |
+ return nil |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+// Variable is a convenience wrapper for modifying global variables of a |
|
| 131 |
+// Collection after loading it into the kernel. Operations on a Variable are |
|
| 132 |
+// performed using direct memory access, bypassing the BPF map syscall API. |
|
| 133 |
+// |
|
| 134 |
+// On kernels older than 5.5, most interactions with Variable return |
|
| 135 |
+// [ErrNotSupported]. |
|
| 136 |
+type Variable struct {
|
|
| 137 |
+ name string |
|
| 138 |
+ offset uint64 |
|
| 139 |
+ size uint64 |
|
| 140 |
+ t *btf.Var |
|
| 141 |
+ |
|
| 142 |
+ mm *Memory |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+func newVariable(name string, offset, size uint64, t *btf.Var, mm *Memory) (*Variable, error) {
|
|
| 146 |
+ if mm != nil {
|
|
| 147 |
+ if int(offset+size) > mm.Size() {
|
|
| 148 |
+ return nil, fmt.Errorf("offset %d(+%d) is out of bounds", offset, size)
|
|
| 149 |
+ } |
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ return &Variable{
|
|
| 153 |
+ name: name, |
|
| 154 |
+ offset: offset, |
|
| 155 |
+ size: size, |
|
| 156 |
+ t: t, |
|
| 157 |
+ mm: mm, |
|
| 158 |
+ }, nil |
|
| 159 |
+} |
|
| 160 |
+ |
|
| 161 |
+// Size returns the size of the variable. |
|
| 162 |
+func (v *Variable) Size() uint64 {
|
|
| 163 |
+ return v.size |
|
| 164 |
+} |
|
| 165 |
+ |
|
| 166 |
+// ReadOnly returns true if the Variable represents a variable that is read-only |
|
| 167 |
+// after loading the Collection into the kernel. |
|
| 168 |
+// |
|
| 169 |
+// On systems without BPF_F_MMAPABLE support, ReadOnly always returns true. |
|
| 170 |
+func (v *Variable) ReadOnly() bool {
|
|
| 171 |
+ if v.mm == nil {
|
|
| 172 |
+ return true |
|
| 173 |
+ } |
|
| 174 |
+ return v.mm.ReadOnly() |
|
| 175 |
+} |
|
| 176 |
+ |
|
| 177 |
+// Type returns the [btf.Var] representing the variable in its data section. |
|
| 178 |
+// This is useful for inspecting the variable's decl tags and the type |
|
| 179 |
+// information of the inner type. |
|
| 180 |
+// |
|
| 181 |
+// Returns nil if the original ELF object did not contain BTF information. |
|
| 182 |
+func (v *Variable) Type() *btf.Var {
|
|
| 183 |
+ return v.t |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 186 |
+func (v *Variable) String() string {
|
|
| 187 |
+ return fmt.Sprintf("%s (type=%v)", v.name, v.t)
|
|
| 188 |
+} |
|
| 189 |
+ |
|
| 190 |
+// Set the value of the Variable to the provided input. The input must marshal |
|
| 191 |
+// to the same length as the size of the Variable. |
|
| 192 |
+func (v *Variable) Set(in any) error {
|
|
| 193 |
+ if v.mm == nil {
|
|
| 194 |
+ return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported)
|
|
| 195 |
+ } |
|
| 196 |
+ |
|
| 197 |
+ if v.ReadOnly() {
|
|
| 198 |
+ return fmt.Errorf("variable %s: %w", v.name, ErrReadOnly)
|
|
| 199 |
+ } |
|
| 200 |
+ |
|
| 201 |
+ buf, err := sysenc.Marshal(in, int(v.size)) |
|
| 202 |
+ if err != nil {
|
|
| 203 |
+ return fmt.Errorf("marshaling value %s: %w", v.name, err)
|
|
| 204 |
+ } |
|
| 205 |
+ |
|
| 206 |
+ if _, err := v.mm.WriteAt(buf.Bytes(), int64(v.offset)); err != nil {
|
|
| 207 |
+ return fmt.Errorf("writing value to %s: %w", v.name, err)
|
|
| 208 |
+ } |
|
| 209 |
+ |
|
| 210 |
+ return nil |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 213 |
+// Get writes the value of the Variable to the provided output. The output must |
|
| 214 |
+// be a pointer to a value whose size matches the Variable. |
|
| 215 |
+func (v *Variable) Get(out any) error {
|
|
| 216 |
+ if v.mm == nil {
|
|
| 217 |
+ return fmt.Errorf("variable %s: direct access requires Linux 5.5 or later: %w", v.name, ErrNotSupported)
|
|
| 218 |
+ } |
|
| 219 |
+ |
|
| 220 |
+ if !v.mm.bounds(v.offset, v.size) {
|
|
| 221 |
+ return fmt.Errorf("variable %s: access out of bounds: %w", v.name, io.EOF)
|
|
| 222 |
+ } |
|
| 223 |
+ |
|
| 224 |
+ if err := sysenc.Unmarshal(out, v.mm.b[v.offset:v.offset+v.size]); err != nil {
|
|
| 225 |
+ return fmt.Errorf("unmarshaling value %s: %w", v.name, err)
|
|
| 226 |
+ } |
|
| 227 |
+ |
|
| 228 |
+ return nil |
|
| 229 |
+} |
| 0 | 230 |
deleted file mode 100644 |
| ... | ... |
@@ -1,27 +0,0 @@ |
| 1 |
-Copyright 2009 The Go Authors. |
|
| 2 |
- |
|
| 3 |
-Redistribution and use in source and binary forms, with or without |
|
| 4 |
-modification, are permitted provided that the following conditions are |
|
| 5 |
-met: |
|
| 6 |
- |
|
| 7 |
- * Redistributions of source code must retain the above copyright |
|
| 8 |
-notice, this list of conditions and the following disclaimer. |
|
| 9 |
- * Redistributions in binary form must reproduce the above |
|
| 10 |
-copyright notice, this list of conditions and the following disclaimer |
|
| 11 |
-in the documentation and/or other materials provided with the |
|
| 12 |
-distribution. |
|
| 13 |
- * Neither the name of Google LLC nor the names of its |
|
| 14 |
-contributors may be used to endorse or promote products derived from |
|
| 15 |
-this software without specific prior written permission. |
|
| 16 |
- |
|
| 17 |
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 18 |
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 19 |
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 20 |
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 21 |
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 22 |
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 23 |
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 24 |
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 25 |
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 26 |
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 27 |
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 28 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,22 +0,0 @@ |
| 1 |
-Additional IP Rights Grant (Patents) |
|
| 2 |
- |
|
| 3 |
-"This implementation" means the copyrightable works distributed by |
|
| 4 |
-Google as part of the Go project. |
|
| 5 |
- |
|
| 6 |
-Google hereby grants to You a perpetual, worldwide, non-exclusive, |
|
| 7 |
-no-charge, royalty-free, irrevocable (except as stated in this section) |
|
| 8 |
-patent license to make, have made, use, offer to sell, sell, import, |
|
| 9 |
-transfer and otherwise run, modify and propagate the contents of this |
|
| 10 |
-implementation of Go, where such license applies only to those patent |
|
| 11 |
-claims, both currently owned or controlled by Google and acquired in |
|
| 12 |
-the future, licensable by Google that are necessarily infringed by this |
|
| 13 |
-implementation of Go. This grant does not include claims that would be |
|
| 14 |
-infringed only as a consequence of further modification of this |
|
| 15 |
-implementation. If you or your agent or exclusive licensee institute or |
|
| 16 |
-order or agree to the institution of patent litigation against any |
|
| 17 |
-entity (including a cross-claim or counterclaim in a lawsuit) alleging |
|
| 18 |
-that this implementation of Go or any code incorporated within this |
|
| 19 |
-implementation of Go constitutes direct or contributory patent |
|
| 20 |
-infringement, or inducement of patent infringement, then any patent |
|
| 21 |
-rights granted to you under this License for this implementation of Go |
|
| 22 |
-shall terminate as of the date such litigation is filed. |
| 23 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,50 +0,0 @@ |
| 1 |
-// Copyright 2021 The Go Authors. All rights reserved. |
|
| 2 |
-// Use of this source code is governed by a BSD-style |
|
| 3 |
-// license that can be found in the LICENSE file. |
|
| 4 |
- |
|
| 5 |
-// Package constraints defines a set of useful constraints to be used |
|
| 6 |
-// with type parameters. |
|
| 7 |
-package constraints |
|
| 8 |
- |
|
| 9 |
-// Signed is a constraint that permits any signed integer type. |
|
| 10 |
-// If future releases of Go add new predeclared signed integer types, |
|
| 11 |
-// this constraint will be modified to include them. |
|
| 12 |
-type Signed interface {
|
|
| 13 |
- ~int | ~int8 | ~int16 | ~int32 | ~int64 |
|
| 14 |
-} |
|
| 15 |
- |
|
| 16 |
-// Unsigned is a constraint that permits any unsigned integer type. |
|
| 17 |
-// If future releases of Go add new predeclared unsigned integer types, |
|
| 18 |
-// this constraint will be modified to include them. |
|
| 19 |
-type Unsigned interface {
|
|
| 20 |
- ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
|
| 21 |
-} |
|
| 22 |
- |
|
| 23 |
-// Integer is a constraint that permits any integer type. |
|
| 24 |
-// If future releases of Go add new predeclared integer types, |
|
| 25 |
-// this constraint will be modified to include them. |
|
| 26 |
-type Integer interface {
|
|
| 27 |
- Signed | Unsigned |
|
| 28 |
-} |
|
| 29 |
- |
|
| 30 |
-// Float is a constraint that permits any floating-point type. |
|
| 31 |
-// If future releases of Go add new predeclared floating-point types, |
|
| 32 |
-// this constraint will be modified to include them. |
|
| 33 |
-type Float interface {
|
|
| 34 |
- ~float32 | ~float64 |
|
| 35 |
-} |
|
| 36 |
- |
|
| 37 |
-// Complex is a constraint that permits any complex numeric type. |
|
| 38 |
-// If future releases of Go add new predeclared complex numeric types, |
|
| 39 |
-// this constraint will be modified to include them. |
|
| 40 |
-type Complex interface {
|
|
| 41 |
- ~complex64 | ~complex128 |
|
| 42 |
-} |
|
| 43 |
- |
|
| 44 |
-// Ordered is a constraint that permits any ordered type: any type |
|
| 45 |
-// that supports the operators < <= >= >. |
|
| 46 |
-// If future releases of Go add new ordered types, |
|
| 47 |
-// this constraint will be modified to include them. |
|
| 48 |
-type Ordered interface {
|
|
| 49 |
- Integer | Float | ~string |
|
| 50 |
-} |
| ... | ... |
@@ -258,16 +258,18 @@ github.com/cenkalti/backoff/v4 |
| 258 | 258 |
# github.com/cespare/xxhash/v2 v2.3.0 |
| 259 | 259 |
## explicit; go 1.11 |
| 260 | 260 |
github.com/cespare/xxhash/v2 |
| 261 |
-# github.com/cilium/ebpf v0.16.0 |
|
| 262 |
-## explicit; go 1.21 |
|
| 261 |
+# github.com/cilium/ebpf v0.17.3 |
|
| 262 |
+## explicit; go 1.22 |
|
| 263 | 263 |
github.com/cilium/ebpf |
| 264 | 264 |
github.com/cilium/ebpf/asm |
| 265 | 265 |
github.com/cilium/ebpf/btf |
| 266 | 266 |
github.com/cilium/ebpf/internal |
| 267 | 267 |
github.com/cilium/ebpf/internal/kallsyms |
| 268 | 268 |
github.com/cilium/ebpf/internal/kconfig |
| 269 |
+github.com/cilium/ebpf/internal/linux |
|
| 269 | 270 |
github.com/cilium/ebpf/internal/sys |
| 270 | 271 |
github.com/cilium/ebpf/internal/sysenc |
| 272 |
+github.com/cilium/ebpf/internal/testutils/fdtrace |
|
| 271 | 273 |
github.com/cilium/ebpf/internal/tracefs |
| 272 | 274 |
github.com/cilium/ebpf/internal/unix |
| 273 | 275 |
github.com/cilium/ebpf/link |
| ... | ... |
@@ -1403,9 +1405,6 @@ golang.org/x/crypto/pkcs12/internal/rc2 |
| 1403 | 1403 |
golang.org/x/crypto/salsa20/salsa |
| 1404 | 1404 |
golang.org/x/crypto/ssh |
| 1405 | 1405 |
golang.org/x/crypto/ssh/internal/bcrypt_pbkdf |
| 1406 |
-# golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f |
|
| 1407 |
-## explicit; go 1.22.0 |
|
| 1408 |
-golang.org/x/exp/constraints |
|
| 1409 | 1406 |
# golang.org/x/mod v0.22.0 |
| 1410 | 1407 |
## explicit; go 1.22.0 |
| 1411 | 1408 |
golang.org/x/mod/internal/lazyregexp |