deps: update runc to 1.1.0

This updates vendored runc/libcontainer to 1.1.0,
and google/cadvisor to a version updated to runc 1.1.0
(google/cadvisor#3048).

Changes in vendor are generated by (roughly):

        ./hack/pin-dependency.sh github.com/google/cadvisor v0.44.0
        ./hack/pin-dependency.sh github.com/opencontainers/runc v1.1.0
        ./hack/update-vendor.sh
        ./hack/lint-dependencies.sh # And follow all its recommendations.
        ./hack/update-vendor.sh
        ./hack/update-internal-modules.sh
        ./hack/lint-dependencies.sh # Re-check everything again.

Co-Authored-By: Kir Kolyshkin <kolyshkin@gmail.com>
This commit is contained in:
Elana Hashman
2022-03-28 11:32:04 -07:00
parent 41830a1f79
commit 07af1bab70
245 changed files with 6520 additions and 5250 deletions

View File

@@ -5,6 +5,7 @@
*.so
*.dylib
*.o
!*_bpf*.o
# Test binary, build with `go test -c`
*.test

View File

@@ -24,6 +24,5 @@ linters:
# Could be enabled later:
# - gocyclo
# - prealloc
# - maligned
# - gosec

View File

@@ -57,7 +57,7 @@ Objects
loading a spec will fail because the kernel is too old, or a feature is not
enabled. There are multiple ways the library deals with that:
* Fallback: older kernels don't allowing naming programs and maps. The library
* Fallback: older kernels don't allow naming programs and maps. The library
automatically detects support for names, and omits them during load if
necessary. This works since name is primarily a debug aid.
@@ -68,7 +68,7 @@ enabled. There are multiple ways the library deals with that:
Once program and map objects are loaded they expose the kernel's low-level API,
e.g. `NextKey`. Often this API is awkward to use in Go, so there are safer
wrappers on top of the low-level API, like `MapIterator`. The low-level API is
useful as an out when our higher-level API doesn't support a particular use case.
useful when our higher-level API doesn't support a particular use case.
Links
---

View File

@@ -6,8 +6,8 @@ are welcome. Please take a look at [the architecture](ARCHITECTURE.md) to get
a better understanding for the high-level goals.
New features must be accompanied by tests. Before starting work on any large
feature, please [join](https://cilium.herokuapp.com/) the
[#libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack to
feature, please [join](https://ebpf.io/slack) the
[#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack to
discuss the design first.
When submitting pull requests, consider writing details about what problem you

View File

@@ -18,11 +18,14 @@ TARGETS := \
testdata/loader-clang-7 \
testdata/loader-clang-9 \
testdata/loader-$(CLANG) \
testdata/btf_map_init \
testdata/invalid_map \
testdata/raw_tracepoint \
testdata/invalid_map_static \
testdata/initialized_btf_map \
testdata/invalid_btf_map_init \
testdata/strings \
testdata/freplace \
testdata/iproute2_map_compat \
internal/btf/testdata/relocs
.PHONY: all clean docker-all docker-shell

View File

@@ -2,28 +2,16 @@
[![PkgGoDev](https://pkg.go.dev/badge/github.com/cilium/ebpf)](https://pkg.go.dev/github.com/cilium/ebpf)
![HoneyGopher](.github/images/cilium-ebpf.png)
eBPF is a pure Go library that provides utilities for loading, compiling, and
debugging eBPF programs. It has minimal external dependencies and is intended to
be used in long running processes.
* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic
assembler
* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF
to various hooks
* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a
`PERF_EVENT_ARRAY`
* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows
compiling and embedding eBPF programs in Go code
The library is maintained by [Cloudflare](https://www.cloudflare.com) and
[Cilium](https://www.cilium.io). Feel free to
[join](https://cilium.herokuapp.com/) the
[#libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack.
[Cilium](https://www.cilium.io).
## Current status
The package is production ready, but **the API is explicitly unstable right
now**. Expect to update your code if you want to follow along.
See [ebpf.io](https://ebpf.io) for other projects from the eBPF ecosystem.
## Getting Started
@@ -33,21 +21,37 @@ your own tools can be found under [examples/](examples/).
Contributions are highly encouraged, as they highlight certain use cases of
eBPF and the library, and help shape the future of the project.
## Getting Help
Please
[join](https://ebpf.io/slack) the
[#ebpf-go](https://cilium.slack.com/messages/ebpf-go) channel on Slack if you
have questions regarding the library.
## Packages
This library includes the following packages:
* [asm](https://pkg.go.dev/github.com/cilium/ebpf/asm) contains a basic
assembler, allowing you to write eBPF assembly instructions directly
within your Go code. (You don't need to use this if you prefer to write your eBPF program in C.)
* [cmd/bpf2go](https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go) allows
compiling and embedding eBPF programs written in C within Go code. As well as
compiling the C code, it auto-generates Go code for loading and manipulating
the eBPF program and map objects.
* [link](https://pkg.go.dev/github.com/cilium/ebpf/link) allows attaching eBPF
to various hooks
* [perf](https://pkg.go.dev/github.com/cilium/ebpf/perf) allows reading from a
`PERF_EVENT_ARRAY`
* [ringbuf](https://pkg.go.dev/github.com/cilium/ebpf/ringbuf) allows reading from a
`BPF_MAP_TYPE_RINGBUF` map
## Requirements
* A version of Go that is [supported by
upstream](https://golang.org/doc/devel/release.html#policy)
* Linux 4.9, 4.19 or 5.4 (versions in-between should work, but are not tested)
## Useful resources
* [eBPF.io](https://ebpf.io) (recommended)
* [Cilium eBPF documentation](https://docs.cilium.io/en/latest/bpf/#bpf-guide)
(recommended)
* [Linux documentation on
BPF](https://www.kernel.org/doc/html/latest/networking/filter.html)
* [eBPF features by Linux
version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md)
* Linux >= 4.9. CI is run against LTS releases.
## Regenerating Testdata
@@ -60,3 +64,7 @@ The toolchain image build files are kept in [testdata/docker/](testdata/docker/)
## License
MIT
### eBPF Gopher
The eBPF honeygopher is based on the Go gopher designed by Renee French.

View File

@@ -184,6 +184,12 @@ const (
FnKtimeGetCoarseNs
FnImaInodeHash
FnSockFromFile
FnCheckMtu
FnForEachMapElem
FnSnprintf
FnSysBpf
FnBtfFindByNameKind
FnSysClose
)
// Call emits a function call.

View File

@@ -171,11 +171,17 @@ func _() {
_ = x[FnKtimeGetCoarseNs-160]
_ = x[FnImaInodeHash-161]
_ = x[FnSockFromFile-162]
_ = x[FnCheckMtu-163]
_ = x[FnForEachMapElem-164]
_ = x[FnSnprintf-165]
_ = x[FnSysBpf-166]
_ = x[FnBtfFindByNameKind-167]
_ = x[FnSysClose-168]
}
const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFile"
const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysClose"
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}
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}
func (i BuiltinFunc) String() string {
if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) {

View File

@@ -181,6 +181,11 @@ func (ins *Instruction) IsFunctionCall() bool {
return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
}
// IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call.
func (ins *Instruction) IsBuiltinCall() bool {
return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0
}
// IsConstantLoad returns true if the instruction loads a constant of the
// given size.
func (ins *Instruction) IsConstantLoad(size Size) bool {

65
vendor/github.com/cilium/ebpf/attachtype_string.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
// Code generated by "stringer -type AttachType -trimprefix Attach"; DO NOT EDIT.
package ebpf
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[AttachNone-0]
_ = x[AttachCGroupInetIngress-0]
_ = x[AttachCGroupInetEgress-1]
_ = x[AttachCGroupInetSockCreate-2]
_ = x[AttachCGroupSockOps-3]
_ = x[AttachSkSKBStreamParser-4]
_ = x[AttachSkSKBStreamVerdict-5]
_ = x[AttachCGroupDevice-6]
_ = x[AttachSkMsgVerdict-7]
_ = x[AttachCGroupInet4Bind-8]
_ = x[AttachCGroupInet6Bind-9]
_ = x[AttachCGroupInet4Connect-10]
_ = x[AttachCGroupInet6Connect-11]
_ = x[AttachCGroupInet4PostBind-12]
_ = x[AttachCGroupInet6PostBind-13]
_ = x[AttachCGroupUDP4Sendmsg-14]
_ = x[AttachCGroupUDP6Sendmsg-15]
_ = x[AttachLircMode2-16]
_ = x[AttachFlowDissector-17]
_ = x[AttachCGroupSysctl-18]
_ = x[AttachCGroupUDP4Recvmsg-19]
_ = x[AttachCGroupUDP6Recvmsg-20]
_ = x[AttachCGroupGetsockopt-21]
_ = x[AttachCGroupSetsockopt-22]
_ = x[AttachTraceRawTp-23]
_ = x[AttachTraceFEntry-24]
_ = x[AttachTraceFExit-25]
_ = x[AttachModifyReturn-26]
_ = x[AttachLSMMac-27]
_ = x[AttachTraceIter-28]
_ = x[AttachCgroupInet4GetPeername-29]
_ = x[AttachCgroupInet6GetPeername-30]
_ = x[AttachCgroupInet4GetSockname-31]
_ = x[AttachCgroupInet6GetSockname-32]
_ = x[AttachXDPDevMap-33]
_ = x[AttachCgroupInetSockRelease-34]
_ = x[AttachXDPCPUMap-35]
_ = x[AttachSkLookup-36]
_ = x[AttachXDP-37]
_ = x[AttachSkSKBVerdict-38]
_ = x[AttachSkReuseportSelect-39]
_ = x[AttachSkReuseportSelectOrMigrate-40]
_ = x[AttachPerfEvent-41]
}
const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEvent"
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}
func (i AttachType) String() string {
if i >= AttachType(len(_AttachType_index)-1) {
return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]]
}

View File

@@ -1,6 +1,7 @@
package ebpf
import (
"encoding/binary"
"errors"
"fmt"
"io"
@@ -25,6 +26,10 @@ type CollectionOptions struct {
type CollectionSpec struct {
Maps map[string]*MapSpec
Programs map[string]*ProgramSpec
// ByteOrder specifies whether the ELF was compiled for
// big-endian or little-endian architectures.
ByteOrder binary.ByteOrder
}
// Copy returns a recursive copy of the spec.
@@ -34,8 +39,9 @@ func (cs *CollectionSpec) Copy() *CollectionSpec {
}
cpy := CollectionSpec{
Maps: make(map[string]*MapSpec, len(cs.Maps)),
Programs: make(map[string]*ProgramSpec, len(cs.Programs)),
Maps: make(map[string]*MapSpec, len(cs.Maps)),
Programs: make(map[string]*ProgramSpec, len(cs.Programs)),
ByteOrder: cs.ByteOrder,
}
for name, spec := range cs.Maps {
@@ -123,7 +129,7 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
buf := make([]byte, len(value))
copy(buf, value)
err := patchValue(buf, btf.MapValue(rodata.BTF), consts)
err := patchValue(buf, rodata.BTF.Value, consts)
if err != nil {
return err
}
@@ -134,15 +140,15 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
// Assign the contents of a CollectionSpec to a struct.
//
// This function is a short-cut to manually checking the presence
// of maps and programs in a collection spec. Consider using bpf2go if this
// sounds useful.
// This function is a shortcut to manually checking the presence
// of maps and programs in a CollectionSpec. Consider using bpf2go
// if this sounds useful.
//
// The argument to must be a pointer to a struct. A field of the
// 'to' must be a pointer to a struct. A field of the
// struct is updated with values from Programs or Maps if it
// has an `ebpf` tag and its type is *ProgramSpec or *MapSpec.
// The tag gives the name of the program or map as found in
// the CollectionSpec.
// The tag's value specifies the name of the program or map as
// found in the CollectionSpec.
//
// struct {
// Foo *ebpf.ProgramSpec `ebpf:"xdp_foo"`
@@ -150,42 +156,47 @@ func (cs *CollectionSpec) RewriteConstants(consts map[string]interface{}) error
// Ignored int
// }
//
// Returns an error if any of the fields can't be found, or
// if the same map or program is assigned multiple times.
// Returns an error if any of the eBPF objects can't be found, or
// if the same MapSpec or ProgramSpec is assigned multiple times.
func (cs *CollectionSpec) Assign(to interface{}) error {
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
// Assign() only supports assigning ProgramSpecs and MapSpecs,
// so doesn't load any resources into the kernel.
getValue := func(typ reflect.Type, name string) (interface{}, error) {
switch typ {
case reflect.TypeOf((*ProgramSpec)(nil)):
p := cs.Programs[name]
if p == nil {
return reflect.Value{}, fmt.Errorf("missing program %q", name)
if p := cs.Programs[name]; p != nil {
return p, nil
}
return reflect.ValueOf(p), nil
return nil, fmt.Errorf("missing program %q", name)
case reflect.TypeOf((*MapSpec)(nil)):
m := cs.Maps[name]
if m == nil {
return reflect.Value{}, fmt.Errorf("missing map %q", name)
if m := cs.Maps[name]; m != nil {
return m, nil
}
return reflect.ValueOf(m), nil
return nil, fmt.Errorf("missing map %q", name)
default:
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
return nil, fmt.Errorf("unsupported type %s", typ)
}
}
return assignValues(to, valueOf)
return assignValues(to, getValue)
}
// LoadAndAssign maps and programs into the kernel and assign them to a struct.
// LoadAndAssign loads Maps and Programs into the kernel and assigns them
// to a struct.
//
// This function is a short-cut to manually checking the presence
// of maps and programs in a collection spec. Consider using bpf2go if this
// sounds useful.
// This function is a shortcut to manually checking the presence
// of maps and programs in a CollectionSpec. Consider using bpf2go
// if this sounds useful.
//
// The argument to must be a pointer to a struct. A field of the
// struct is updated with values from Programs or Maps if it
// has an `ebpf` tag and its type is *Program or *Map.
// The tag gives the name of the program or map as found in
// the CollectionSpec.
// 'to' must be a pointer to a struct. A field of the struct is updated with
// a Program or Map if it has an `ebpf` tag and its type is *Program or *Map.
// The tag's value specifies the name of the program or map as found in the
// CollectionSpec. Before updating the struct, the requested objects and their
// dependent resources are loaded into the kernel and populated with values if
// specified.
//
// struct {
// Foo *ebpf.Program `ebpf:"xdp_foo"`
@@ -196,39 +207,53 @@ func (cs *CollectionSpec) Assign(to interface{}) error {
// opts may be nil.
//
// Returns an error if any of the fields can't be found, or
// if the same map or program is assigned multiple times.
// if the same Map or Program is assigned multiple times.
func (cs *CollectionSpec) LoadAndAssign(to interface{}, opts *CollectionOptions) error {
if opts == nil {
opts = &CollectionOptions{}
}
loader := newCollectionLoader(cs, opts)
defer loader.cleanup()
loadMap, loadProgram, done, cleanup := lazyLoadCollection(cs, opts)
defer cleanup()
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
// Support assigning Programs and Maps, lazy-loading the required objects.
assignedMaps := make(map[string]bool)
getValue := func(typ reflect.Type, name string) (interface{}, error) {
switch typ {
case reflect.TypeOf((*Program)(nil)):
p, err := loadProgram(name)
if err != nil {
return reflect.Value{}, err
}
return reflect.ValueOf(p), nil
return loader.loadProgram(name)
case reflect.TypeOf((*Map)(nil)):
m, err := loadMap(name)
if err != nil {
return reflect.Value{}, err
}
return reflect.ValueOf(m), nil
assignedMaps[name] = true
return loader.loadMap(name)
default:
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
return nil, fmt.Errorf("unsupported type %s", typ)
}
}
if err := assignValues(to, valueOf); err != nil {
// Load the Maps and Programs requested by the annotated struct.
if err := assignValues(to, getValue); err != nil {
return err
}
done()
// Populate the requested maps. Has a chance of lazy-loading other dependent maps.
if err := loader.populateMaps(); err != nil {
return err
}
// Evaluate the loader's objects after all (lazy)loading has taken place.
for n, m := range loader.maps {
switch m.typ {
case ProgramArray:
// Require all lazy-loaded ProgramArrays to be assigned to the given object.
// Without any references, they will be closed on the first GC and all tail
// calls into them will miss.
if !assignedMaps[n] {
return fmt.Errorf("ProgramArray %s must be assigned to prevent missed tail calls", n)
}
}
}
loader.finalize()
return nil
}
@@ -246,24 +271,32 @@ func NewCollection(spec *CollectionSpec) (*Collection, error) {
// NewCollectionWithOptions creates a Collection from a specification.
func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) {
loadMap, loadProgram, done, cleanup := lazyLoadCollection(spec, &opts)
defer cleanup()
loader := newCollectionLoader(spec, &opts)
defer loader.cleanup()
// Create maps first, as their fds need to be linked into programs.
for mapName := range spec.Maps {
_, err := loadMap(mapName)
if err != nil {
if _, err := loader.loadMap(mapName); err != nil {
return nil, err
}
}
for progName := range spec.Programs {
_, err := loadProgram(progName)
if err != nil {
if _, err := loader.loadProgram(progName); err != nil {
return nil, err
}
}
maps, progs := done()
// Maps can contain Program and Map stubs, so populate them after
// all Maps and Programs have been successfully loaded.
if err := loader.populateMaps(); err != nil {
return nil, err
}
maps, progs := loader.maps, loader.programs
loader.finalize()
return &Collection{
progs,
maps,
@@ -314,113 +347,154 @@ func (hc handleCache) close() {
for _, handle := range hc.btfHandles {
handle.Close()
}
hc.btfHandles = nil
hc.btfSpecs = nil
}
func lazyLoadCollection(coll *CollectionSpec, opts *CollectionOptions) (
loadMap func(string) (*Map, error),
loadProgram func(string) (*Program, error),
done func() (map[string]*Map, map[string]*Program),
cleanup func(),
) {
var (
maps = make(map[string]*Map)
progs = make(map[string]*Program)
handles = newHandleCache()
skipMapsAndProgs = false
)
type collectionLoader struct {
coll *CollectionSpec
opts *CollectionOptions
maps map[string]*Map
programs map[string]*Program
handles *handleCache
}
cleanup = func() {
handles.close()
if skipMapsAndProgs {
return
}
for _, m := range maps {
m.Close()
}
for _, p := range progs {
p.Close()
}
func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) *collectionLoader {
if opts == nil {
opts = &CollectionOptions{}
}
done = func() (map[string]*Map, map[string]*Program) {
skipMapsAndProgs = true
return maps, progs
return &collectionLoader{
coll,
opts,
make(map[string]*Map),
make(map[string]*Program),
newHandleCache(),
}
}
loadMap = func(mapName string) (*Map, error) {
if m := maps[mapName]; m != nil {
return m, nil
}
// finalize should be called when all the collectionLoader's resources
// have been successfully loaded into the kernel and populated with values.
func (cl *collectionLoader) finalize() {
cl.maps, cl.programs = nil, nil
}
mapSpec := coll.Maps[mapName]
if mapSpec == nil {
return nil, fmt.Errorf("missing map %s", mapName)
}
// cleanup cleans up all resources left over in the collectionLoader.
// Call finalize() when Map and Program creation/population is successful
// to prevent them from getting closed.
func (cl *collectionLoader) cleanup() {
cl.handles.close()
for _, m := range cl.maps {
m.Close()
}
for _, p := range cl.programs {
p.Close()
}
}
m, err := newMapWithOptions(mapSpec, opts.Maps, handles)
if err != nil {
return nil, fmt.Errorf("map %s: %w", mapName, err)
}
maps[mapName] = m
func (cl *collectionLoader) loadMap(mapName string) (*Map, error) {
if m := cl.maps[mapName]; m != nil {
return m, nil
}
loadProgram = func(progName string) (*Program, error) {
if prog := progs[progName]; prog != nil {
return prog, nil
mapSpec := cl.coll.Maps[mapName]
if mapSpec == nil {
return nil, fmt.Errorf("missing map %s", mapName)
}
m, err := newMapWithOptions(mapSpec, cl.opts.Maps, cl.handles)
if err != nil {
return nil, fmt.Errorf("map %s: %w", mapName, err)
}
cl.maps[mapName] = m
return m, nil
}
func (cl *collectionLoader) loadProgram(progName string) (*Program, error) {
if prog := cl.programs[progName]; prog != nil {
return prog, nil
}
progSpec := cl.coll.Programs[progName]
if progSpec == nil {
return nil, fmt.Errorf("unknown program %s", progName)
}
progSpec = progSpec.Copy()
// Rewrite any reference to a valid map.
for i := range progSpec.Instructions {
ins := &progSpec.Instructions[i]
if !ins.IsLoadFromMap() || ins.Reference == "" {
continue
}
progSpec := coll.Programs[progName]
if progSpec == nil {
return nil, fmt.Errorf("unknown program %s", progName)
if uint32(ins.Constant) != math.MaxUint32 {
// Don't overwrite maps already rewritten, users can
// rewrite programs in the spec themselves
continue
}
progSpec = progSpec.Copy()
// Rewrite any reference to a valid map.
for i := range progSpec.Instructions {
ins := &progSpec.Instructions[i]
if !ins.IsLoadFromMap() || ins.Reference == "" {
continue
}
if uint32(ins.Constant) != math.MaxUint32 {
// Don't overwrite maps already rewritten, users can
// rewrite programs in the spec themselves
continue
}
m, err := loadMap(ins.Reference)
if err != nil {
return nil, fmt.Errorf("program %s: %w", progName, err)
}
fd := m.FD()
if fd < 0 {
return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
}
if err := ins.RewriteMapPtr(m.FD()); err != nil {
return nil, fmt.Errorf("progam %s: map %s: %w", progName, ins.Reference, err)
}
}
prog, err := newProgramWithOptions(progSpec, opts.Programs, handles)
m, err := cl.loadMap(ins.Reference)
if err != nil {
return nil, fmt.Errorf("program %s: %w", progName, err)
}
progs[progName] = prog
return prog, nil
fd := m.FD()
if fd < 0 {
return nil, fmt.Errorf("map %s: %w", ins.Reference, internal.ErrClosedFd)
}
if err := ins.RewriteMapPtr(m.FD()); err != nil {
return nil, fmt.Errorf("program %s: map %s: %w", progName, ins.Reference, err)
}
}
return
prog, err := newProgramWithOptions(progSpec, cl.opts.Programs, cl.handles)
if err != nil {
return nil, fmt.Errorf("program %s: %w", progName, err)
}
cl.programs[progName] = prog
return prog, nil
}
func (cl *collectionLoader) populateMaps() error {
for mapName, m := range cl.maps {
mapSpec, ok := cl.coll.Maps[mapName]
if !ok {
return fmt.Errorf("missing map spec %s", mapName)
}
mapSpec = mapSpec.Copy()
// Replace any object stubs with loaded objects.
for i, kv := range mapSpec.Contents {
switch v := kv.Value.(type) {
case programStub:
// loadProgram is idempotent and could return an existing Program.
prog, err := cl.loadProgram(string(v))
if err != nil {
return fmt.Errorf("loading program %s, for map %s: %w", v, mapName, err)
}
mapSpec.Contents[i] = MapKV{kv.Key, prog}
case mapStub:
// loadMap is idempotent and could return an existing Map.
innerMap, err := cl.loadMap(string(v))
if err != nil {
return fmt.Errorf("loading inner map %s, for map %s: %w", v, mapName, err)
}
mapSpec.Contents[i] = MapKV{kv.Key, innerMap}
}
}
// Populate and freeze the map if specified.
if err := m.finalize(mapSpec); err != nil {
return fmt.Errorf("populating map %s: %w", mapName, err)
}
}
return nil
}
// LoadCollection parses an object file and converts it to a collection.
@@ -466,108 +540,81 @@ func (coll *Collection) DetachProgram(name string) *Program {
return p
}
// Assign the contents of a collection to a struct.
//
// Deprecated: use CollectionSpec.Assign instead. It provides the same
// functionality but creates only the maps and programs requested.
func (coll *Collection) Assign(to interface{}) error {
assignedMaps := make(map[string]struct{})
assignedPrograms := make(map[string]struct{})
valueOf := func(typ reflect.Type, name string) (reflect.Value, error) {
switch typ {
case reflect.TypeOf((*Program)(nil)):
p := coll.Programs[name]
if p == nil {
return reflect.Value{}, fmt.Errorf("missing program %q", name)
}
assignedPrograms[name] = struct{}{}
return reflect.ValueOf(p), nil
case reflect.TypeOf((*Map)(nil)):
m := coll.Maps[name]
if m == nil {
return reflect.Value{}, fmt.Errorf("missing map %q", name)
}
assignedMaps[name] = struct{}{}
return reflect.ValueOf(m), nil
default:
return reflect.Value{}, fmt.Errorf("unsupported type %s", typ)
}
}
if err := assignValues(to, valueOf); err != nil {
return err
}
for name := range assignedPrograms {
coll.DetachProgram(name)
}
for name := range assignedMaps {
coll.DetachMap(name)
}
return nil
// structField represents a struct field containing the ebpf struct tag.
type structField struct {
reflect.StructField
value reflect.Value
}
func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Value, error)) error {
type structField struct {
reflect.StructField
value reflect.Value
// ebpfFields extracts field names tagged with 'ebpf' from a struct type.
// Keep track of visited types to avoid infinite recursion.
func ebpfFields(structVal reflect.Value, visited map[reflect.Type]bool) ([]structField, error) {
if visited == nil {
visited = make(map[reflect.Type]bool)
}
var (
fields []structField
visitedTypes = make(map[reflect.Type]bool)
flattenStruct func(reflect.Value) error
)
structType := structVal.Type()
if structType.Kind() != reflect.Struct {
return nil, fmt.Errorf("%s is not a struct", structType)
}
flattenStruct = func(structVal reflect.Value) error {
structType := structVal.Type()
if structType.Kind() != reflect.Struct {
return fmt.Errorf("%s is not a struct", structType)
if visited[structType] {
return nil, fmt.Errorf("recursion on type %s", structType)
}
fields := make([]structField, 0, structType.NumField())
for i := 0; i < structType.NumField(); i++ {
field := structField{structType.Field(i), structVal.Field(i)}
// If the field is tagged, gather it and move on.
name := field.Tag.Get("ebpf")
if name != "" {
fields = append(fields, field)
continue
}
if visitedTypes[structType] {
return fmt.Errorf("recursion on type %s", structType)
}
for i := 0; i < structType.NumField(); i++ {
field := structField{structType.Field(i), structVal.Field(i)}
name := field.Tag.Get("ebpf")
if name != "" {
fields = append(fields, field)
// If the field does not have an ebpf tag, but is a struct or a pointer
// to a struct, attempt to gather its fields as well.
var v reflect.Value
switch field.Type.Kind() {
case reflect.Ptr:
if field.Type.Elem().Kind() != reflect.Struct {
continue
}
var err error
switch field.Type.Kind() {
case reflect.Ptr:
if field.Type.Elem().Kind() != reflect.Struct {
continue
}
if field.value.IsNil() {
return fmt.Errorf("nil pointer to %s", structType)
}
err = flattenStruct(field.value.Elem())
case reflect.Struct:
err = flattenStruct(field.value)
default:
continue
if field.value.IsNil() {
return nil, fmt.Errorf("nil pointer to %s", structType)
}
if err != nil {
return fmt.Errorf("field %s: %w", field.Name, err)
}
// Obtain the destination type of the pointer.
v = field.value.Elem()
case reflect.Struct:
// Reference the value's type directly.
v = field.value
default:
continue
}
return nil
inner, err := ebpfFields(v, visited)
if err != nil {
return nil, fmt.Errorf("field %s: %w", field.Name, err)
}
fields = append(fields, inner...)
}
return fields, nil
}
// assignValues attempts to populate all fields of 'to' tagged with 'ebpf'.
//
// getValue is called for every tagged field of 'to' and must return the value
// to be assigned to the field with the given typ and name.
func assignValues(to interface{},
getValue func(typ reflect.Type, name string) (interface{}, error)) error {
toValue := reflect.ValueOf(to)
if toValue.Type().Kind() != reflect.Ptr {
return fmt.Errorf("%T is not a pointer to struct", to)
@@ -577,7 +624,8 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va
return fmt.Errorf("nil pointer to %T", to)
}
if err := flattenStruct(toValue.Elem()); err != nil {
fields, err := ebpfFields(toValue.Elem(), nil)
if err != nil {
return err
}
@@ -587,19 +635,23 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va
name string
}
assignedTo := make(map[elem]string)
assigned := make(map[elem]string)
for _, field := range fields {
name := field.Tag.Get("ebpf")
if strings.Contains(name, ",") {
// Get string value the field is tagged with.
tag := field.Tag.Get("ebpf")
if strings.Contains(tag, ",") {
return fmt.Errorf("field %s: ebpf tag contains a comma", field.Name)
}
e := elem{field.Type, name}
if assignedField := assignedTo[e]; assignedField != "" {
return fmt.Errorf("field %s: %q was already assigned to %s", field.Name, name, assignedField)
// Check if the eBPF object with the requested
// type and tag was already assigned elsewhere.
e := elem{field.Type, tag}
if af := assigned[e]; af != "" {
return fmt.Errorf("field %s: object %q was already assigned to %s", field.Name, tag, af)
}
value, err := valueOf(field.Type, name)
// Get the eBPF object referred to by the tag.
value, err := getValue(field.Type, tag)
if err != nil {
return fmt.Errorf("field %s: %w", field.Name, err)
}
@@ -607,9 +659,9 @@ func assignValues(to interface{}, valueOf func(reflect.Type, string) (reflect.Va
if !field.value.CanSet() {
return fmt.Errorf("field %s: can't set value", field.Name)
}
field.value.Set(reflect.ValueOf(value))
field.value.Set(value)
assignedTo[e] = field.Name
assigned[e] = field.Name
}
return nil

View File

@@ -19,7 +19,7 @@ import (
)
// elfCode is a convenience to reduce the amount of arguments that have to
// be passed around explicitly. You should treat it's contents as immutable.
// be passed around explicitly. You should treat its contents as immutable.
type elfCode struct {
*internal.SafeELFFile
sections map[elf.SectionIndex]*elfSection
@@ -188,7 +188,7 @@ func LoadCollectionSpecFromReader(rd io.ReaderAt) (*CollectionSpec, error) {
return nil, fmt.Errorf("load programs: %w", err)
}
return &CollectionSpec{maps, progs}, nil
return &CollectionSpec{maps, progs, ec.ByteOrder}, nil
}
func loadLicense(sec *elf.Section) (string, error) {
@@ -520,8 +520,12 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error {
return fmt.Errorf("map %s: missing flags", mapName)
}
if _, err := io.Copy(internal.DiscardZeroes{}, lr); err != nil {
return fmt.Errorf("map %s: unknown and non-zero fields in definition", mapName)
extra, err := io.ReadAll(lr)
if err != nil {
return fmt.Errorf("map %s: reading map tail: %w", mapName, err)
}
if len(extra) > 0 {
spec.Extra = *bytes.NewReader(extra)
}
if err := spec.clampPerfEventArraySize(); err != nil {
@@ -535,6 +539,9 @@ func (ec *elfCode) loadMaps(maps map[string]*MapSpec) error {
return nil
}
// loadBTFMaps iterates over all ELF sections marked as BTF map sections
// (like .maps) and parses them into MapSpecs. Dump the .maps section and
// any relocations with `readelf -x .maps -r <elf_file>`.
func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error {
for _, sec := range ec.sections {
if sec.kind != btfMapSection {
@@ -545,33 +552,46 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error {
return fmt.Errorf("missing BTF")
}
_, err := io.Copy(internal.DiscardZeroes{}, bufio.NewReader(sec.Open()))
if err != nil {
return fmt.Errorf("section %v: initializing BTF map definitions: %w", sec.Name, internal.ErrNotSupported)
}
var ds btf.Datasec
// Each section must appear as a DataSec in the ELF's BTF blob.
var ds *btf.Datasec
if err := ec.btf.FindType(sec.Name, &ds); err != nil {
return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err)
}
// Open a Reader to the ELF's raw section bytes so we can assert that all
// of them are zero on a per-map (per-Var) basis. For now, the section's
// sole purpose is to receive relocations, so all must be zero.
rs := sec.Open()
for _, vs := range ds.Vars {
// BPF maps are declared as and assigned to global variables,
// so iterate over each Var in the DataSec and validate their types.
v, ok := vs.Type.(*btf.Var)
if !ok {
return fmt.Errorf("section %v: unexpected type %s", sec.Name, vs.Type)
}
name := string(v.Name)
// The BTF metadata for each Var contains the full length of the map
// declaration, so read the corresponding amount of bytes from the ELF.
// This way, we can pinpoint which map declaration contains unexpected
// (and therefore unsupported) data.
_, err := io.Copy(internal.DiscardZeroes{}, io.LimitReader(rs, int64(vs.Size)))
if err != nil {
return fmt.Errorf("section %v: map %s: initializing BTF map definitions: %w", sec.Name, name, internal.ErrNotSupported)
}
if maps[name] != nil {
return fmt.Errorf("section %v: map %s already exists", sec.Name, name)
}
// Each Var representing a BTF map definition contains a Struct.
mapStruct, ok := v.Type.(*btf.Struct)
if !ok {
return fmt.Errorf("expected struct, got %s", v.Type)
}
mapSpec, err := mapSpecFromBTF(name, mapStruct, false, ec.btf)
mapSpec, err := mapSpecFromBTF(sec, &vs, mapStruct, ec.btf, name, false)
if err != nil {
return fmt.Errorf("map %v: %w", name, err)
}
@@ -582,32 +602,52 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error {
maps[name] = mapSpec
}
// Drain the ELF section reader to make sure all bytes are accounted for
// with BTF metadata.
i, err := io.Copy(io.Discard, rs)
if err != nil {
return fmt.Errorf("section %v: unexpected error reading remainder of ELF section: %w", sec.Name, err)
}
if i > 0 {
return fmt.Errorf("section %v: %d unexpected remaining bytes in ELF section, invalid BTF?", sec.Name, i)
}
}
return nil
}
// A programStub is a placeholder for a Program to be inserted at a certain map key.
// It needs to be resolved into a Program later on in the loader process.
type programStub string
// A mapStub is a placeholder for a Map to be inserted at a certain map key.
// It needs to be resolved into a Map later on in the loader process.
type mapStub string
// mapSpecFromBTF produces a MapSpec based on a btf.Struct def representing
// a BTF map definition. The name and spec arguments will be copied to the
// resulting MapSpec, and inner must be true on any resursive invocations.
func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*MapSpec, error) {
func mapSpecFromBTF(es *elfSection, vs *btf.VarSecinfo, def *btf.Struct, spec *btf.Spec, name string, inner bool) (*MapSpec, error) {
var (
key, value btf.Type
keySize, valueSize uint32
mapType, flags, maxEntries uint32
pinType PinType
innerMapSpec *MapSpec
err error
key, value btf.Type
keySize, valueSize uint32
mapType MapType
flags, maxEntries uint32
pinType PinType
innerMapSpec *MapSpec
contents []MapKV
err error
)
for i, member := range def.Members {
switch member.Name {
case "type":
mapType, err = uintFromBTF(member.Type)
mt, err := uintFromBTF(member.Type)
if err != nil {
return nil, fmt.Errorf("can't get type: %w", err)
}
mapType = MapType(mt)
case "map_flags":
flags, err = uintFromBTF(member.Type)
@@ -717,7 +757,7 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*
case *btf.Struct:
// The values member pointing to an array of structs means we're expecting
// a map-in-map declaration.
if MapType(mapType) != ArrayOfMaps && MapType(mapType) != HashOfMaps {
if mapType != ArrayOfMaps && mapType != HashOfMaps {
return nil, errors.New("outer map needs to be an array or a hash of maps")
}
if inner {
@@ -731,21 +771,38 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*
// on kernels 5.2 and up)
// Pass the BTF spec from the parent object, since both parent and
// child must be created from the same BTF blob (on kernels that support BTF).
innerMapSpec, err = mapSpecFromBTF(name+"_inner", t, true, spec)
innerMapSpec, err = mapSpecFromBTF(es, vs, t, spec, name+"_inner", true)
if err != nil {
return nil, fmt.Errorf("can't parse BTF map definition of inner map: %w", err)
}
case *btf.FuncProto:
// The values member contains an array of function pointers, meaning an
// autopopulated PROG_ARRAY.
if mapType != ProgramArray {
return nil, errors.New("map needs to be a program array")
}
default:
return nil, fmt.Errorf("unsupported value type %q in 'values' field", t)
}
contents, err = resolveBTFValuesContents(es, vs, member)
if err != nil {
return nil, fmt.Errorf("resolving values contents: %w", err)
}
default:
return nil, fmt.Errorf("unrecognized field %s in BTF map definition", member.Name)
}
}
bm := btf.NewMap(spec, key, value)
if key == nil {
key = &btf.Void{}
}
if value == nil {
value = &btf.Void{}
}
return &MapSpec{
Name: SanitizeName(name, -1),
@@ -754,9 +811,10 @@ func mapSpecFromBTF(name string, def *btf.Struct, inner bool, spec *btf.Spec) (*
ValueSize: valueSize,
MaxEntries: maxEntries,
Flags: flags,
BTF: &bm,
BTF: &btf.Map{Spec: spec, Key: key, Value: value},
Pinning: pinType,
InnerMap: innerMapSpec,
Contents: contents,
}, nil
}
@@ -793,6 +851,64 @@ func resolveBTFArrayMacro(typ btf.Type) (btf.Type, error) {
return ptr.Target, nil
}
// resolveBTFValuesContents resolves relocations into ELF sections belonging
// to btf.VarSecinfo's. This can be used on the 'values' member in BTF map
// definitions to extract static declarations of map contents.
func resolveBTFValuesContents(es *elfSection, vs *btf.VarSecinfo, member btf.Member) ([]MapKV, error) {
// The elements of a .values pointer array are not encoded in BTF.
// Instead, relocations are generated into each array index.
// However, it's possible to leave certain array indices empty, so all
// indices' offsets need to be checked for emitted relocations.
// The offset of the 'values' member within the _struct_ (in bits)
// is the starting point of the array. Convert to bytes. Add VarSecinfo
// offset to get the absolute position in the ELF blob.
start := (member.OffsetBits / 8) + vs.Offset
// 'values' is encoded in BTF as a zero (variable) length struct
// member, and its contents run until the end of the VarSecinfo.
// Add VarSecinfo offset to get the absolute position in the ELF blob.
end := vs.Size + vs.Offset
// The size of an address in this section. This determines the width of
// an index in the array.
align := uint32(es.SectionHeader.Addralign)
// Check if variable-length section is aligned.
if (end-start)%align != 0 {
return nil, errors.New("unaligned static values section")
}
elems := (end - start) / align
if elems == 0 {
return nil, nil
}
contents := make([]MapKV, 0, elems)
// k is the array index, off is its corresponding ELF section offset.
for k, off := uint32(0), start; k < elems; k, off = k+1, off+align {
r, ok := es.relocations[uint64(off)]
if !ok {
continue
}
// Relocation exists for the current offset in the ELF section.
// Emit a value stub based on the type of relocation to be replaced by
// a real fd later in the pipeline before populating the map.
// Map keys are encoded in MapKV entries, so empty array indices are
// skipped here.
switch t := elf.ST_TYPE(r.Info); t {
case elf.STT_FUNC:
contents = append(contents, MapKV{uint32(k), programStub(r.Name)})
case elf.STT_OBJECT:
contents = append(contents, MapKV{uint32(k), mapStub(r.Name)})
default:
return nil, fmt.Errorf("unknown relocation type %v", t)
}
}
return contents, nil
}
func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
for _, sec := range ec.sections {
if sec.kind != dataSection {
@@ -809,9 +925,9 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
return errors.New("data sections require BTF, make sure all consts are marked as static")
}
btfMap, err := ec.btf.Datasec(sec.Name)
if err != nil {
return err
var datasec *btf.Datasec
if err := ec.btf.FindType(sec.Name, &datasec); err != nil {
return fmt.Errorf("data section %s: can't get BTF: %w", sec.Name, err)
}
data, err := sec.Data()
@@ -830,7 +946,7 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error {
ValueSize: uint32(len(data)),
MaxEntries: 1,
Contents: []MapKV{{uint32(0), data}},
BTF: btfMap,
BTF: &btf.Map{Spec: ec.btf, Key: &btf.Void{}, Value: datasec},
}
switch sec.Name {
@@ -855,6 +971,8 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
}{
// From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c
"socket": {SocketFilter, AttachNone, 0},
"sk_reuseport/migrate": {SkReuseport, AttachSkReuseportSelectOrMigrate, 0},
"sk_reuseport": {SkReuseport, AttachSkReuseportSelect, 0},
"seccomp": {SocketFilter, AttachNone, 0},
"kprobe/": {Kprobe, AttachNone, 0},
"uprobe/": {Kprobe, AttachNone, 0},
@@ -884,6 +1002,7 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
"fmod_ret.s/": {Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE},
"fexit.s/": {Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE},
"sk_lookup/": {SkLookup, AttachSkLookup, 0},
"freplace/": {Extension, AttachNone, 0},
"lsm/": {LSM, AttachLSMMac, 0},
"lsm.s/": {LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE},
@@ -907,6 +1026,11 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
"cgroup/setsockopt": {CGroupSockopt, AttachCGroupSetsockopt, 0},
"classifier": {SchedCLS, AttachNone, 0},
"action": {SchedACT, AttachNone, 0},
"cgroup/getsockname4": {CGroupSockAddr, AttachCgroupInet4GetSockname, 0},
"cgroup/getsockname6": {CGroupSockAddr, AttachCgroupInet6GetSockname, 0},
"cgroup/getpeername4": {CGroupSockAddr, AttachCgroupInet4GetPeername, 0},
"cgroup/getpeername6": {CGroupSockAddr, AttachCgroupInet6GetPeername, 0},
}
for prefix, t := range types {

View File

@@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz
// Use with https://github.com/dvyukov/go-fuzz

View File

@@ -1,9 +1,9 @@
module github.com/cilium/ebpf
go 1.15
go 1.16
require (
github.com/frankban/quicktest v1.11.3
github.com/google/go-cmp v0.5.4
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34
)

View File

@@ -7,7 +7,7 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34 h1:GkvMjFtXUmahfDtashnc1mnrCtuBVcwse5QV2lUk/tI=
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -12,6 +12,7 @@ import (
"time"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
)
// MapInfo describes a map.
@@ -87,12 +88,16 @@ type ProgramInfo struct {
Tag string
// Name as supplied by user space at load time.
Name string
// BTF for the program.
btf btf.ID
// IDS map ids related to program.
ids []MapID
stats *programStats
}
func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
info, err := bpfGetProgInfoByFD(fd)
info, err := bpfGetProgInfoByFD(fd, nil)
if errors.Is(err, syscall.EINVAL) {
return newProgramInfoFromProc(fd)
}
@@ -100,6 +105,15 @@ func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
return nil, err
}
var mapIDs []MapID
if info.nr_map_ids > 0 {
mapIDs = make([]MapID, info.nr_map_ids)
info, err = bpfGetProgInfoByFD(fd, mapIDs)
if err != nil {
return nil, err
}
}
return &ProgramInfo{
Type: ProgramType(info.prog_type),
id: ProgramID(info.id),
@@ -107,6 +121,8 @@ func newProgramInfoFromFd(fd *internal.FD) (*ProgramInfo, error) {
Tag: hex.EncodeToString(info.tag[:]),
// name is available from 4.15.
Name: internal.CString(info.name[:]),
btf: btf.ID(info.btf_id),
ids: mapIDs,
stats: &programStats{
runtime: time.Duration(info.run_time_ns),
runCount: info.run_cnt,
@@ -142,6 +158,17 @@ func (pi *ProgramInfo) ID() (ProgramID, bool) {
return pi.id, pi.id > 0
}
// BTFID returns the BTF ID associated with the program.
//
// Available from 5.0.
//
// The bool return value indicates whether this optional field is available and
// populated. (The field may be available but not populated if the kernel
// supports the field but the program was loaded without BTF information.)
func (pi *ProgramInfo) BTFID() (btf.ID, bool) {
return pi.btf, pi.btf > 0
}
// RunCount returns the total number of times the program was called.
//
// Can return 0 if the collection of statistics is not enabled. See EnableStats().
@@ -164,6 +191,13 @@ func (pi *ProgramInfo) Runtime() (time.Duration, bool) {
return time.Duration(0), false
}
// MapIDs returns the maps related to the program.
//
// The bool return value indicates whether this optional field is available.
func (pi *ProgramInfo) MapIDs() ([]MapID, bool) {
return pi.ids, pi.ids != nil
}
func scanFdInfo(fd *internal.FD, fields map[string]interface{}) error {
raw, err := fd.Value()
if err != nil {

6
vendor/github.com/cilium/ebpf/internal/align.go generated vendored Normal file
View File

@@ -0,0 +1,6 @@
package internal
// Align returns 'n' updated to 'alignment' boundary.
func Align(n, alignment int) int {
return (int(n) + alignment - 1) / alignment * alignment
}

View File

@@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"reflect"
@@ -27,12 +26,15 @@ var (
ErrNoExtendedInfo = errors.New("no extended info")
)
// ID represents the unique ID of a BTF object.
type ID uint32
// Spec represents decoded BTF.
type Spec struct {
rawTypes []rawType
strings stringTable
types []Type
namedTypes map[string][]namedType
namedTypes map[string][]NamedType
funcInfos map[string]extInfo
lineInfos map[string]extInfo
coreRelos map[string]coreRelos
@@ -61,15 +63,6 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
}
defer file.Close()
btfSection, btfExtSection, sectionSizes, err := findBtfSections(file)
if err != nil {
return nil, err
}
if btfSection == nil {
return nil, fmt.Errorf("btf: %w", ErrNotFound)
}
symbols, err := file.Symbols()
if err != nil {
return nil, fmt.Errorf("can't read symbols: %v", err)
@@ -87,10 +80,6 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
}
secName := file.Sections[symbol.Section].Name
if _, ok := sectionSizes[secName]; !ok {
continue
}
if symbol.Value > math.MaxUint32 {
return nil, fmt.Errorf("section %s: symbol %s: size exceeds maximum", secName, symbol.Name)
}
@@ -98,24 +87,10 @@ func LoadSpecFromReader(rd io.ReaderAt) (*Spec, error) {
variableOffsets[variable{secName, symbol.Name}] = uint32(symbol.Value)
}
spec, err := loadNakedSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets)
if err != nil {
return nil, err
}
if btfExtSection == nil {
return spec, nil
}
spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
if err != nil {
return nil, fmt.Errorf("can't read ext info: %w", err)
}
return spec, nil
return loadSpecFromELF(file, variableOffsets)
}
func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, map[string]uint32, error) {
func loadSpecFromELF(file *internal.SafeELFFile, variableOffsets map[variable]uint32) (*Spec, error) {
var (
btfSection *elf.Section
btfExtSection *elf.Section
@@ -134,33 +109,45 @@ func findBtfSections(file *internal.SafeELFFile) (*elf.Section, *elf.Section, ma
}
if sec.Size > math.MaxUint32 {
return nil, nil, nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
return nil, fmt.Errorf("section %s exceeds maximum size", sec.Name)
}
sectionSizes[sec.Name] = uint32(sec.Size)
}
}
return btfSection, btfExtSection, sectionSizes, nil
}
func loadSpecFromVmlinux(rd io.ReaderAt) (*Spec, error) {
file, err := internal.NewSafeELFFile(rd)
if btfSection == nil {
return nil, fmt.Errorf("btf: %w", ErrNotFound)
}
spec, err := loadRawSpec(btfSection.Open(), file.ByteOrder, sectionSizes, variableOffsets)
if err != nil {
return nil, err
}
defer file.Close()
btfSection, _, _, err := findBtfSections(file)
if btfExtSection == nil {
return spec, nil
}
spec.funcInfos, spec.lineInfos, spec.coreRelos, err = parseExtInfos(btfExtSection.Open(), file.ByteOrder, spec.strings)
if err != nil {
return nil, fmt.Errorf(".BTF ELF section: %s", err)
return nil, fmt.Errorf("can't read ext info: %w", err)
}
if btfSection == nil {
return nil, fmt.Errorf("unable to find .BTF ELF section")
}
return loadNakedSpec(btfSection.Open(), file.ByteOrder, nil, nil)
return spec, nil
}
func loadNakedSpec(btf io.ReadSeeker, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
// LoadRawSpec reads a blob of BTF data that isn't wrapped in an ELF file.
//
// Prefer using LoadSpecFromReader, since this function only supports a subset
// of BTF.
func LoadRawSpec(btf io.Reader, bo binary.ByteOrder) (*Spec, error) {
// This will return an error if we encounter a Datasec, since we can't fix
// it up.
return loadRawSpec(btf, bo, nil, nil)
}
func loadRawSpec(btf io.Reader, bo binary.ByteOrder, sectionSizes map[string]uint32, variableOffsets map[variable]uint32) (*Spec, error) {
rawTypes, rawStrings, err := parseBTF(btf, bo)
if err != nil {
return nil, err
@@ -217,7 +204,7 @@ func loadKernelSpec() (*Spec, error) {
if err == nil {
defer fh.Close()
return loadNakedSpec(fh, internal.NativeEndian, nil, nil)
return LoadRawSpec(fh, internal.NativeEndian)
}
// use same list of locations as libbpf
@@ -241,14 +228,20 @@ func loadKernelSpec() (*Spec, error) {
}
defer fh.Close()
return loadSpecFromVmlinux(fh)
file, err := internal.NewSafeELFFile(fh)
if err != nil {
return nil, err
}
defer file.Close()
return loadSpecFromELF(file, nil)
}
return nil, fmt.Errorf("no BTF for kernel version %s: %w", release, internal.ErrNotSupported)
}
func parseBTF(btf io.ReadSeeker, bo binary.ByteOrder) ([]rawType, stringTable, error) {
rawBTF, err := ioutil.ReadAll(btf)
func parseBTF(btf io.Reader, bo binary.ByteOrder) ([]rawType, stringTable, error) {
rawBTF, err := io.ReadAll(btf)
if err != nil {
return nil, nil, fmt.Errorf("can't read BTF: %v", err)
}
@@ -357,6 +350,30 @@ func fixupDatasec(rawTypes []rawType, rawStrings stringTable, sectionSizes map[s
return nil
}
// Copy creates a copy of Spec.
func (s *Spec) Copy() *Spec {
types, _ := copyTypes(s.types, nil)
namedTypes := make(map[string][]NamedType)
for _, typ := range types {
if named, ok := typ.(NamedType); ok {
name := essentialName(named.TypeName())
namedTypes[name] = append(namedTypes[name], named)
}
}
// NB: Other parts of spec are not copied since they are immutable.
return &Spec{
s.rawTypes,
s.strings,
types,
namedTypes,
s.funcInfos,
s.lineInfos,
s.coreRelos,
s.byteOrder,
}
}
type marshalOpts struct {
ByteOrder binary.ByteOrder
StripFuncLinkage bool
@@ -447,36 +464,37 @@ func (s *Spec) Program(name string, length uint64) (*Program, error) {
return &Program{s, length, funcInfos, lineInfos, relos}, nil
}
// Datasec returns the BTF required to create maps which represent data sections.
func (s *Spec) Datasec(name string) (*Map, error) {
var datasec Datasec
if err := s.FindType(name, &datasec); err != nil {
return nil, fmt.Errorf("data section %s: can't get BTF: %w", name, err)
}
m := NewMap(s, &Void{}, &datasec)
return &m, nil
}
// FindType searches for a type with a specific name.
//
// hint determines the type of the returned Type.
// Called T a type that satisfies Type, typ must be a non-nil **T.
// On success, the address of the found type will be copied in typ.
//
// Returns an error wrapping ErrNotFound if no matching
// type exists in spec.
func (s *Spec) FindType(name string, typ Type) error {
var (
wanted = reflect.TypeOf(typ)
candidate Type
)
func (s *Spec) FindType(name string, typ interface{}) error {
typValue := reflect.ValueOf(typ)
if typValue.Kind() != reflect.Ptr {
return fmt.Errorf("%T is not a pointer", typ)
}
typPtr := typValue.Elem()
if !typPtr.CanSet() {
return fmt.Errorf("%T cannot be set", typ)
}
wanted := typPtr.Type()
if !wanted.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) {
return fmt.Errorf("%T does not satisfy Type interface", typ)
}
var candidate Type
for _, typ := range s.namedTypes[essentialName(name)] {
if reflect.TypeOf(typ) != wanted {
continue
}
// Match against the full name, not just the essential one.
if typ.name() != name {
if typ.TypeName() != name {
continue
}
@@ -491,15 +509,15 @@ func (s *Spec) FindType(name string, typ Type) error {
return fmt.Errorf("type %s: %w", name, ErrNotFound)
}
cpy, _ := copyType(candidate, nil)
value := reflect.Indirect(reflect.ValueOf(cpy))
reflect.Indirect(reflect.ValueOf(typ)).Set(value)
typPtr.Set(reflect.ValueOf(candidate))
return nil
}
// Handle is a reference to BTF loaded into the kernel.
type Handle struct {
fd *internal.FD
spec *Spec
fd *internal.FD
}
// NewHandle loads BTF into the kernel.
@@ -541,7 +559,32 @@ func NewHandle(spec *Spec) (*Handle, error) {
return nil, internal.ErrorWithLog(err, logBuf, logErr)
}
return &Handle{fd}, nil
return &Handle{spec.Copy(), fd}, nil
}
// NewHandleFromID returns the BTF handle for a given id.
//
// Returns ErrNotExist, if there is no BTF with the given id.
//
// Requires CAP_SYS_ADMIN.
func NewHandleFromID(id ID) (*Handle, error) {
fd, err := internal.BPFObjGetFDByID(internal.BPF_BTF_GET_FD_BY_ID, uint32(id))
if err != nil {
return nil, fmt.Errorf("get BTF by id: %w", err)
}
info, err := newInfoFromFd(fd)
if err != nil {
_ = fd.Close()
return nil, fmt.Errorf("get BTF spec for handle: %w", err)
}
return &Handle{info.BTF, fd}, nil
}
// Spec returns the Spec that defined the BTF loaded into the kernel.
func (h *Handle) Spec() *Spec {
return h.spec
}
// Close destroys the handle.
@@ -563,43 +606,8 @@ func (h *Handle) FD() int {
// Map is the BTF for a map.
type Map struct {
spec *Spec
key, value Type
}
// NewMap returns a new Map containing the given values.
// The key and value arguments are initialized to Void if nil values are given.
func NewMap(spec *Spec, key Type, value Type) Map {
if key == nil {
key = &Void{}
}
if value == nil {
value = &Void{}
}
return Map{
spec: spec,
key: key,
value: value,
}
}
// MapSpec should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapSpec(m *Map) *Spec {
return m.spec
}
// MapKey should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapKey(m *Map) Type {
return m.key
}
// MapValue should be a method on Map, but is a free function
// to hide it from users of the ebpf package.
func MapValue(m *Map) Type {
return m.value
Spec *Spec
Key, Value Type
}
// Program is the BTF information for a stream of instructions.
@@ -610,68 +618,59 @@ type Program struct {
coreRelos coreRelos
}
// ProgramSpec returns the Spec needed for loading function and line infos into the kernel.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramSpec(s *Program) *Spec {
return s.spec
// Spec returns the BTF spec of this program.
func (p *Program) Spec() *Spec {
return p.spec
}
// ProgramAppend the information from other to the Program.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramAppend(s, other *Program) error {
funcInfos, err := s.funcInfos.append(other.funcInfos, s.length)
// Append the information from other to the Program.
func (p *Program) Append(other *Program) error {
if other.spec != p.spec {
return fmt.Errorf("can't append program with different BTF specs")
}
funcInfos, err := p.funcInfos.append(other.funcInfos, p.length)
if err != nil {
return fmt.Errorf("func infos: %w", err)
}
lineInfos, err := s.lineInfos.append(other.lineInfos, s.length)
lineInfos, err := p.lineInfos.append(other.lineInfos, p.length)
if err != nil {
return fmt.Errorf("line infos: %w", err)
}
s.funcInfos = funcInfos
s.lineInfos = lineInfos
s.coreRelos = s.coreRelos.append(other.coreRelos, s.length)
s.length += other.length
p.funcInfos = funcInfos
p.lineInfos = lineInfos
p.coreRelos = p.coreRelos.append(other.coreRelos, p.length)
p.length += other.length
return nil
}
// ProgramFuncInfos returns the binary form of BTF function infos.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramFuncInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
bytes, err = s.funcInfos.MarshalBinary()
// FuncInfos returns the binary form of BTF function infos.
func (p *Program) FuncInfos() (recordSize uint32, bytes []byte, err error) {
bytes, err = p.funcInfos.MarshalBinary()
if err != nil {
return 0, nil, err
return 0, nil, fmt.Errorf("func infos: %w", err)
}
return s.funcInfos.recordSize, bytes, nil
return p.funcInfos.recordSize, bytes, nil
}
// ProgramLineInfos returns the binary form of BTF line infos.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramLineInfos(s *Program) (recordSize uint32, bytes []byte, err error) {
bytes, err = s.lineInfos.MarshalBinary()
// LineInfos returns the binary form of BTF line infos.
func (p *Program) LineInfos() (recordSize uint32, bytes []byte, err error) {
bytes, err = p.lineInfos.MarshalBinary()
if err != nil {
return 0, nil, err
return 0, nil, fmt.Errorf("line infos: %w", err)
}
return s.lineInfos.recordSize, bytes, nil
return p.lineInfos.recordSize, bytes, nil
}
// ProgramFixups returns the changes required to adjust the program to the target.
// Fixups returns the changes required to adjust the program to the target.
//
// This is a free function instead of a method to hide it from users
// of package ebpf.
func ProgramFixups(s *Program, target *Spec) (COREFixups, error) {
if len(s.coreRelos) == 0 {
// Passing a nil target will relocate against the running kernel.
func (p *Program) Fixups(target *Spec) (COREFixups, error) {
if len(p.coreRelos) == 0 {
return nil, nil
}
@@ -683,7 +682,7 @@ func ProgramFixups(s *Program, target *Spec) (COREFixups, error) {
}
}
return coreRelocate(s.spec, target, s.coreRelos)
return coreRelocate(p.spec, target, p.coreRelos)
}
type bpfLoadBTFAttr struct {

View File

@@ -31,6 +31,8 @@ const (
// Added ~5.1
kindVar
kindDatasec
// Added ~5.13
kindFloat
)
// FuncLinkage describes BTF function linkage metadata.
@@ -54,7 +56,7 @@ const (
const (
btfTypeKindShift = 24
btfTypeKindLen = 4
btfTypeKindLen = 5
btfTypeVlenShift = 0
btfTypeVlenMask = 16
btfTypeKindFlagShift = 31
@@ -67,8 +69,8 @@ type btfType struct {
/* "info" bits arrangement
* bits 0-15: vlen (e.g. # of struct's members), linkage
* bits 16-23: unused
* bits 24-27: kind (e.g. int, ptr, array...etc)
* bits 28-30: unused
* bits 24-28: kind (e.g. int, ptr, array...etc)
* bits 29-30: unused
* bit 31: kind_flag, currently used by
* struct, union and fwd
*/
@@ -117,6 +119,8 @@ func (k btfKind) String() string {
return "Variable"
case kindDatasec:
return "Section"
case kindFloat:
return "Float"
default:
return fmt.Sprintf("Unknown (%d)", k)
}
@@ -260,6 +264,7 @@ func readTypes(r io.Reader, bo binary.ByteOrder) ([]rawType, error) {
data = new(btfVariable)
case kindDatasec:
data = make([]btfVarSecinfo, header.Vlen())
case kindFloat:
default:
return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
}

View File

@@ -234,13 +234,13 @@ func coreRelocate(local, target *Spec, relos coreRelos) (COREFixups, error) {
}
localType := local.types[id]
named, ok := localType.(namedType)
if !ok || named.name() == "" {
named, ok := localType.(NamedType)
if !ok || named.TypeName() == "" {
return nil, fmt.Errorf("relocate unnamed or anonymous type %s: %w", localType, ErrNotSupported)
}
relos := relosByID[id]
targets := target.namedTypes[named.essentialName()]
targets := target.namedTypes[essentialName(named.TypeName())]
fixups, err := coreCalculateFixups(localType, targets, relos)
if err != nil {
return nil, fmt.Errorf("relocate %s: %w", localType, err)
@@ -262,7 +262,7 @@ var errImpossibleRelocation = errors.New("impossible relocation")
//
// The best target is determined by scoring: the less poisoning we have to do
// the better the target is.
func coreCalculateFixups(local Type, targets []namedType, relos coreRelos) ([]COREFixup, error) {
func coreCalculateFixups(local Type, targets []NamedType, relos coreRelos) ([]COREFixup, error) {
localID := local.ID()
local, err := copyType(local, skipQualifierAndTypedef)
if err != nil {
@@ -467,8 +467,8 @@ func parseCoreAccessor(accessor string) (coreAccessor, error) {
return nil, fmt.Errorf("empty accessor")
}
var result coreAccessor
parts := strings.Split(accessor, ":")
result := make(coreAccessor, 0, len(parts))
for _, part := range parts {
// 31 bits to avoid overflowing int on 32 bit platforms.
index, err := strconv.ParseUint(part, 10, 31)
@@ -564,7 +564,7 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie
// This is an anonymous struct or union, ignore it.
local = localMember.Type
localOffset += localMember.Offset
localOffset += localMember.OffsetBits
localMaybeFlex = false
continue
}
@@ -585,10 +585,10 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie
local = localMember.Type
localMaybeFlex = acc == len(localMembers)-1
localOffset += localMember.Offset
localOffset += localMember.OffsetBits
target = targetMember.Type
targetMaybeFlex = last
targetOffset += targetMember.Offset
targetOffset += targetMember.OffsetBits
case *Array:
// For arrays, acc is the index in the target.
@@ -639,7 +639,7 @@ func coreFindField(local Type, localAcc coreAccessor, target Type) (_, _ coreFie
// coreFindMember finds a member in a composite type while handling anonymous
// structs and unions.
func coreFindMember(typ composite, name Name) (Member, bool, error) {
func coreFindMember(typ composite, name string) (Member, bool, error) {
if name == "" {
return Member{}, false, errors.New("can't search for anonymous member")
}
@@ -670,7 +670,7 @@ func coreFindMember(typ composite, name Name) (Member, bool, error) {
for j, member := range members {
if member.Name == name {
// NB: This is safe because member is a copy.
member.Offset += target.offset
member.OffsetBits += target.offset
return member, j == len(members)-1, nil
}
@@ -685,7 +685,7 @@ func coreFindMember(typ composite, name Name) (Member, bool, error) {
return Member{}, false, fmt.Errorf("anonymous non-composite type %T not allowed", member.Type)
}
targets = append(targets, offsetTarget{comp, target.offset + member.Offset})
targets = append(targets, offsetTarget{comp, target.offset + member.OffsetBits})
}
}
@@ -704,9 +704,9 @@ func coreFindEnumValue(local Type, localAcc coreAccessor, target Type) (localVal
return nil, nil, errImpossibleRelocation
}
localName := localValue.Name.essentialName()
localName := essentialName(localValue.Name)
for i, targetValue := range targetEnum.Values {
if targetValue.Name.essentialName() != localName {
if essentialName(targetValue.Name) != localName {
continue
}
@@ -813,6 +813,7 @@ func coreAreTypesCompatible(localType Type, targetType Type) error {
* least one of enums should be anonymous;
* - for ENUMs, check sizes, names are ignored;
* - for INT, size and signedness are ignored;
* - any two FLOATs are always compatible;
* - for ARRAY, dimensionality is ignored, element types are checked for
* compatibility recursively;
* [ NB: coreAreMembersCompatible doesn't recurse, this check is done
@@ -848,16 +849,16 @@ func coreAreMembersCompatible(localType Type, targetType Type) error {
}
switch lv := localType.(type) {
case *Array, *Pointer:
case *Array, *Pointer, *Float:
return nil
case *Enum:
tv := targetType.(*Enum)
return doNamesMatch(lv.name(), tv.name())
return doNamesMatch(lv.Name, tv.Name)
case *Fwd:
tv := targetType.(*Fwd)
return doNamesMatch(lv.name(), tv.name())
return doNamesMatch(lv.Name, tv.Name)
case *Int:
tv := targetType.(*Int)

View File

@@ -7,7 +7,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
@@ -64,7 +63,7 @@ func parseExtInfos(r io.ReadSeeker, bo binary.ByteOrder, strings stringTable) (f
// Of course, the .BTF.ext header has different semantics than the
// .BTF ext header. We need to ignore non-null values.
_, err = io.CopyN(ioutil.Discard, r, remainder)
_, err = io.CopyN(io.Discard, r, remainder)
if err != nil {
return nil, nil, nil, fmt.Errorf("header padding: %v", err)
}
@@ -114,11 +113,16 @@ type extInfoRecord struct {
}
type extInfo struct {
byteOrder binary.ByteOrder
recordSize uint32
records []extInfoRecord
}
func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
if other.byteOrder != ei.byteOrder {
return extInfo{}, fmt.Errorf("ext_info byte order mismatch, want %v (got %v)", ei.byteOrder, other.byteOrder)
}
if other.recordSize != ei.recordSize {
return extInfo{}, fmt.Errorf("ext_info record size mismatch, want %d (got %d)", ei.recordSize, other.recordSize)
}
@@ -131,10 +135,14 @@ func (ei extInfo) append(other extInfo, offset uint64) (extInfo, error) {
Opaque: info.Opaque,
})
}
return extInfo{ei.recordSize, records}, nil
return extInfo{ei.byteOrder, ei.recordSize, records}, nil
}
func (ei extInfo) MarshalBinary() ([]byte, error) {
if ei.byteOrder != internal.NativeEndian {
return nil, fmt.Errorf("%s is not the native byte order", ei.byteOrder)
}
if len(ei.records) == 0 {
return nil, nil
}
@@ -197,6 +205,7 @@ func parseExtInfo(r io.Reader, bo binary.ByteOrder, strings stringTable) (map[st
}
result[secName] = extInfo{
bo,
recordSize,
records,
}

View File

@@ -1,3 +1,4 @@
//go:build gofuzz
// +build gofuzz
// Use with https://github.com/dvyukov/go-fuzz

48
vendor/github.com/cilium/ebpf/internal/btf/info.go generated vendored Normal file
View File

@@ -0,0 +1,48 @@
package btf
import (
"bytes"
"github.com/cilium/ebpf/internal"
)
// info describes a BTF object.
type info struct {
BTF *Spec
ID ID
// Name is an identifying name for the BTF, currently only used by the
// kernel.
Name string
// KernelBTF is true if the BTf originated with the kernel and not
// userspace.
KernelBTF bool
}
func newInfoFromFd(fd *internal.FD) (*info, error) {
// We invoke the syscall once with a empty BTF and name buffers to get size
// information to allocate buffers. Then we invoke it a second time with
// buffers to receive the data.
bpfInfo, err := bpfGetBTFInfoByFD(fd, nil, nil)
if err != nil {
return nil, err
}
btfBuffer := make([]byte, bpfInfo.btfSize)
nameBuffer := make([]byte, bpfInfo.nameLen)
bpfInfo, err = bpfGetBTFInfoByFD(fd, btfBuffer, nameBuffer)
if err != nil {
return nil, err
}
spec, err := loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil, nil)
if err != nil {
return nil, err
}
return &info{
BTF: spec,
ID: ID(bpfInfo.id),
Name: internal.CString(nameBuffer),
KernelBTF: bpfInfo.kernelBTF != 0,
}, nil
}

View File

@@ -5,13 +5,12 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
)
type stringTable []byte
func readStringTable(r io.Reader) (stringTable, error) {
contents, err := ioutil.ReadAll(r)
contents, err := io.ReadAll(r)
if err != nil {
return nil, fmt.Errorf("can't read string table: %v", err)
}
@@ -53,8 +52,3 @@ func (st stringTable) Lookup(offset uint32) (string, error) {
return string(str[:end]), nil
}
func (st stringTable) LookupName(offset uint32) (Name, error) {
str, err := st.Lookup(offset)
return Name(str), err
}

31
vendor/github.com/cilium/ebpf/internal/btf/syscalls.go generated vendored Normal file
View File

@@ -0,0 +1,31 @@
package btf
import (
"fmt"
"unsafe"
"github.com/cilium/ebpf/internal"
)
type bpfBTFInfo struct {
btf internal.Pointer
btfSize uint32
id uint32
name internal.Pointer
nameLen uint32
kernelBTF uint32
}
func bpfGetBTFInfoByFD(fd *internal.FD, btf, name []byte) (*bpfBTFInfo, error) {
info := bpfBTFInfo{
btf: internal.NewSlicePointer(btf),
btfSize: uint32(len(btf)),
name: internal.NewSlicePointer(name),
nameLen: uint32(len(name)),
}
if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
return nil, fmt.Errorf("can't get program info: %w", err)
}
return &info, nil
}

View File

@@ -30,27 +30,26 @@ type Type interface {
walk(*typeDeque)
}
// namedType is a type with a name.
//
// Most named types simply embed Name.
type namedType interface {
// NamedType is a type with a name.
type NamedType interface {
Type
name() string
essentialName() string
// Name of the type, empty for anonymous types.
TypeName() string
}
// Name identifies a type.
//
// Anonymous types have an empty name.
type Name string
func (n Name) name() string {
return string(n)
}
func (n Name) essentialName() string {
return essentialName(string(n))
}
var (
_ NamedType = (*Int)(nil)
_ NamedType = (*Struct)(nil)
_ NamedType = (*Union)(nil)
_ NamedType = (*Enum)(nil)
_ NamedType = (*Fwd)(nil)
_ NamedType = (*Func)(nil)
_ NamedType = (*Typedef)(nil)
_ NamedType = (*Var)(nil)
_ NamedType = (*Datasec)(nil)
_ NamedType = (*Float)(nil)
)
// Void is the unit type of BTF.
type Void struct{}
@@ -72,19 +71,17 @@ const (
// Int is an integer of a given length.
type Int struct {
TypeID
Name
Name string
// The size of the integer in bytes.
Size uint32
Encoding IntEncoding
// Offset is the starting bit offset. Currently always 0.
// OffsetBits is the starting bit offset. Currently always 0.
// See https://www.kernel.org/doc/html/latest/bpf/btf.html#btf-kind-int
Offset uint32
Bits byte
OffsetBits uint32
Bits byte
}
var _ namedType = (*Int)(nil)
func (i *Int) String() string {
var s strings.Builder
@@ -110,15 +107,16 @@ func (i *Int) String() string {
return s.String()
}
func (i *Int) size() uint32 { return i.Size }
func (i *Int) walk(*typeDeque) {}
func (i *Int) TypeName() string { return i.Name }
func (i *Int) size() uint32 { return i.Size }
func (i *Int) walk(*typeDeque) {}
func (i *Int) copy() Type {
cpy := *i
return &cpy
}
func (i *Int) isBitfield() bool {
return i.Offset > 0
return i.OffsetBits > 0
}
// Pointer is a pointer to another type.
@@ -158,7 +156,7 @@ func (arr *Array) copy() Type {
// Struct is a compound type of consecutive members.
type Struct struct {
TypeID
Name
Name string
// The size of the struct including padding, in bytes
Size uint32
Members []Member
@@ -168,6 +166,8 @@ func (s *Struct) String() string {
return fmt.Sprintf("struct#%d[%q]", s.TypeID, s.Name)
}
func (s *Struct) TypeName() string { return s.Name }
func (s *Struct) size() uint32 { return s.Size }
func (s *Struct) walk(tdq *typeDeque) {
@@ -189,7 +189,7 @@ func (s *Struct) members() []Member {
// Union is a compound type where members occupy the same memory.
type Union struct {
TypeID
Name
Name string
// The size of the union including padding, in bytes.
Size uint32
Members []Member
@@ -199,6 +199,8 @@ func (u *Union) String() string {
return fmt.Sprintf("union#%d[%q]", u.TypeID, u.Name)
}
func (u *Union) TypeName() string { return u.Name }
func (u *Union) size() uint32 { return u.Size }
func (u *Union) walk(tdq *typeDeque) {
@@ -236,17 +238,17 @@ var (
//
// It is not a valid Type.
type Member struct {
Name
Name string
Type Type
// Offset is the bit offset of this member
Offset uint32
// OffsetBits is the bit offset of this member.
OffsetBits uint32
BitfieldSize uint32
}
// Enum lists possible values.
type Enum struct {
TypeID
Name
Name string
Values []EnumValue
}
@@ -254,11 +256,13 @@ func (e *Enum) String() string {
return fmt.Sprintf("enum#%d[%q]", e.TypeID, e.Name)
}
func (e *Enum) TypeName() string { return e.Name }
// EnumValue is part of an Enum
//
// Is is not a valid Type
type EnumValue struct {
Name
Name string
Value int32
}
@@ -294,7 +298,7 @@ func (fk FwdKind) String() string {
// Fwd is a forward declaration of a Type.
type Fwd struct {
TypeID
Name
Name string
Kind FwdKind
}
@@ -302,6 +306,8 @@ func (f *Fwd) String() string {
return fmt.Sprintf("fwd#%d[%s %q]", f.TypeID, f.Kind, f.Name)
}
func (f *Fwd) TypeName() string { return f.Name }
func (f *Fwd) walk(*typeDeque) {}
func (f *Fwd) copy() Type {
cpy := *f
@@ -311,7 +317,7 @@ func (f *Fwd) copy() Type {
// Typedef is an alias of a Type.
type Typedef struct {
TypeID
Name
Name string
Type Type
}
@@ -319,6 +325,8 @@ func (td *Typedef) String() string {
return fmt.Sprintf("typedef#%d[%q #%d]", td.TypeID, td.Name, td.Type.ID())
}
func (td *Typedef) TypeName() string { return td.Name }
func (td *Typedef) walk(tdq *typeDeque) { tdq.push(&td.Type) }
func (td *Typedef) copy() Type {
cpy := *td
@@ -379,7 +387,7 @@ func (r *Restrict) copy() Type {
// Func is a function definition.
type Func struct {
TypeID
Name
Name string
Type Type
Linkage FuncLinkage
}
@@ -388,6 +396,8 @@ func (f *Func) String() string {
return fmt.Sprintf("func#%d[%s %q proto=#%d]", f.TypeID, f.Linkage, f.Name, f.Type.ID())
}
func (f *Func) TypeName() string { return f.Name }
func (f *Func) walk(tdq *typeDeque) { tdq.push(&f.Type) }
func (f *Func) copy() Type {
cpy := *f
@@ -426,14 +436,14 @@ func (fp *FuncProto) copy() Type {
}
type FuncParam struct {
Name
Name string
Type Type
}
// Var is a global variable.
type Var struct {
TypeID
Name
Name string
Type Type
Linkage VarLinkage
}
@@ -442,6 +452,8 @@ func (v *Var) String() string {
return fmt.Sprintf("var#%d[%s %q]", v.TypeID, v.Linkage, v.Name)
}
func (v *Var) TypeName() string { return v.Name }
func (v *Var) walk(tdq *typeDeque) { tdq.push(&v.Type) }
func (v *Var) copy() Type {
cpy := *v
@@ -451,7 +463,7 @@ func (v *Var) copy() Type {
// Datasec is a global program section containing data.
type Datasec struct {
TypeID
Name
Name string
Size uint32
Vars []VarSecinfo
}
@@ -460,6 +472,8 @@ func (ds *Datasec) String() string {
return fmt.Sprintf("section#%d[%q]", ds.TypeID, ds.Name)
}
func (ds *Datasec) TypeName() string { return ds.Name }
func (ds *Datasec) size() uint32 { return ds.Size }
func (ds *Datasec) walk(tdq *typeDeque) {
@@ -475,7 +489,7 @@ func (ds *Datasec) copy() Type {
return &cpy
}
// VarSecinfo describes variable in a Datasec
// VarSecinfo describes variable in a Datasec.
//
// It is not a valid Type.
type VarSecinfo struct {
@@ -484,6 +498,27 @@ type VarSecinfo struct {
Size uint32
}
// Float is a float of a given length.
type Float struct {
TypeID
Name string
// The size of the float in bytes.
Size uint32
}
func (f *Float) String() string {
return fmt.Sprintf("float%d#%d[%q]", f.Size*8, f.TypeID, f.Name)
}
func (f *Float) TypeName() string { return f.Name }
func (f *Float) size() uint32 { return f.Size }
func (f *Float) walk(*typeDeque) {}
func (f *Float) copy() Type {
cpy := *f
return &cpy
}
type sizer interface {
size() uint32
}
@@ -565,14 +600,36 @@ func Sizeof(typ Type) (int, error) {
//
// Returns any errors from transform verbatim.
func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) {
var (
copies = make(map[Type]Type)
work typeDeque
)
copies := make(copier)
return typ, copies.copy(&typ, transform)
}
for t := &typ; t != nil; t = work.pop() {
// copy a slice of Types recursively.
//
// Types may form a cycle.
//
// Returns any errors from transform verbatim.
func copyTypes(types []Type, transform func(Type) (Type, error)) ([]Type, error) {
result := make([]Type, len(types))
copy(result, types)
copies := make(copier)
for i := range result {
if err := copies.copy(&result[i], transform); err != nil {
return nil, err
}
}
return result, nil
}
type copier map[Type]Type
func (c copier) copy(typ *Type, transform func(Type) (Type, error)) error {
var work typeDeque
for t := typ; t != nil; t = work.pop() {
// *t is the identity of the type.
if cpy := copies[*t]; cpy != nil {
if cpy := c[*t]; cpy != nil {
*t = cpy
continue
}
@@ -581,21 +638,21 @@ func copyType(typ Type, transform func(Type) (Type, error)) (Type, error) {
if transform != nil {
tf, err := transform(*t)
if err != nil {
return nil, fmt.Errorf("copy %s: %w", typ, err)
return fmt.Errorf("copy %s: %w", *t, err)
}
cpy = tf.copy()
} else {
cpy = (*t).copy()
}
copies[*t] = cpy
c[*t] = cpy
*t = cpy
// Mark any nested types for copying.
cpy.walk(&work)
}
return typ, nil
return nil
}
// typeDeque keeps track of pointers to types which still
@@ -606,6 +663,10 @@ type typeDeque struct {
mask uint64
}
func (dq *typeDeque) empty() bool {
return dq.read == dq.write
}
// push adds a type to the stack.
func (dq *typeDeque) push(t *Type) {
if dq.write-dq.read < uint64(len(dq.types)) {
@@ -632,7 +693,7 @@ func (dq *typeDeque) push(t *Type) {
// shift returns the first element or null.
func (dq *typeDeque) shift() *Type {
if dq.read == dq.write {
if dq.empty() {
return nil
}
@@ -645,7 +706,7 @@ func (dq *typeDeque) shift() *Type {
// pop returns the last element or null.
func (dq *typeDeque) pop() *Type {
if dq.read == dq.write {
if dq.empty() {
return nil
}
@@ -674,7 +735,7 @@ func (dq *typeDeque) all() []*Type {
// Returns a map of named types (so, where NameOff is non-zero) and a slice of types
// indexed by TypeID. Since BTF ignores compilation units, multiple types may share
// the same name. A Type may form a cyclic graph by pointing at itself.
func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, namedTypes map[string][]namedType, err error) {
func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type, namedTypes map[string][]NamedType, err error) {
type fixupDef struct {
id TypeID
expectedKind btfKind
@@ -691,17 +752,17 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
// work, since otherwise append might re-allocate members.
members := make([]Member, 0, len(raw))
for i, btfMember := range raw {
name, err := rawStrings.LookupName(btfMember.NameOff)
name, err := rawStrings.Lookup(btfMember.NameOff)
if err != nil {
return nil, fmt.Errorf("can't get name for member %d: %w", i, err)
}
m := Member{
Name: name,
Offset: btfMember.Offset,
Name: name,
OffsetBits: btfMember.Offset,
}
if kindFlag {
m.BitfieldSize = btfMember.Offset >> 24
m.Offset &= 0xffffff
m.OffsetBits &= 0xffffff
}
members = append(members, m)
}
@@ -713,7 +774,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
types = make([]Type, 0, len(rawTypes))
types = append(types, (*Void)(nil))
namedTypes = make(map[string][]namedType)
namedTypes = make(map[string][]NamedType)
for i, raw := range rawTypes {
var (
@@ -723,7 +784,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
typ Type
)
name, err := rawStrings.LookupName(raw.NameOff)
name, err := rawStrings.Lookup(raw.NameOff)
if err != nil {
return nil, nil, fmt.Errorf("get name for type id %d: %w", id, err)
}
@@ -765,7 +826,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
rawvals := raw.data.([]btfEnum)
vals := make([]EnumValue, 0, len(rawvals))
for i, btfVal := range rawvals {
name, err := rawStrings.LookupName(btfVal.NameOff)
name, err := rawStrings.Lookup(btfVal.NameOff)
if err != nil {
return nil, nil, fmt.Errorf("get name for enum value %d: %s", i, err)
}
@@ -812,7 +873,7 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
rawparams := raw.data.([]btfParam)
params := make([]FuncParam, 0, len(rawparams))
for i, param := range rawparams {
name, err := rawStrings.LookupName(param.NameOff)
name, err := rawStrings.Lookup(param.NameOff)
if err != nil {
return nil, nil, fmt.Errorf("get name for func proto parameter %d: %s", i, err)
}
@@ -848,14 +909,17 @@ func inflateRawTypes(rawTypes []rawType, rawStrings stringTable) (types []Type,
}
typ = &Datasec{id, name, raw.SizeType, vars}
case kindFloat:
typ = &Float{id, name, raw.Size()}
default:
return nil, nil, fmt.Errorf("type id %d: unknown kind: %v", id, raw.Kind())
}
types = append(types, typ)
if named, ok := typ.(namedType); ok {
if name := essentialName(named.name()); name != "" {
if named, ok := typ.(NamedType); ok {
if name := essentialName(named.TypeName()); name != "" {
namedTypes[name] = append(namedTypes[name], named)
}
}

View File

@@ -2,7 +2,7 @@ package internal
import (
"fmt"
"io/ioutil"
"os"
"strings"
"sync"
)
@@ -24,7 +24,7 @@ func PossibleCPUs() (int, error) {
}
func parseCPUsFromFile(path string) (int, error) {
spec, err := ioutil.ReadFile(path)
spec, err := os.ReadFile(path)
if err != nil {
return 0, err
}

View File

@@ -1,3 +1,4 @@
//go:build armbe || mips || mips64p32
// +build armbe mips mips64p32
package internal

View File

@@ -1,3 +1,4 @@
//go:build 386 || amd64p32 || arm || mipsle || mips64p32le
// +build 386 amd64p32 arm mipsle mips64p32le
package internal

View File

@@ -1,5 +1,5 @@
// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le
// +build !armbe,!mips,!mips64p32
//go:build !386 && !amd64p32 && !arm && !mipsle && !mips64p32le && !armbe && !mips && !mips64p32
// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le,!armbe,!mips,!mips64p32
package internal

View File

@@ -1,6 +1,7 @@
package internal
import (
"errors"
"fmt"
"path/filepath"
"runtime"
@@ -68,6 +69,48 @@ func BPF(cmd BPFCmd, attr unsafe.Pointer, size uintptr) (uintptr, error) {
return r1, err
}
type BPFProgLoadAttr struct {
ProgType uint32
InsCount uint32
Instructions Pointer
License Pointer
LogLevel uint32
LogSize uint32
LogBuf Pointer
KernelVersion uint32 // since 4.1 2541517c32be
ProgFlags uint32 // since 4.11 e07b98d9bffe
ProgName BPFObjName // since 4.15 067cae47771c
ProgIfIndex uint32 // since 4.15 1f6f4cb7ba21
ExpectedAttachType uint32 // since 4.17 5e43f899b03a
ProgBTFFd uint32
FuncInfoRecSize uint32
FuncInfo Pointer
FuncInfoCnt uint32
LineInfoRecSize uint32
LineInfo Pointer
LineInfoCnt uint32
AttachBTFID uint32
AttachProgFd uint32
}
// BPFProgLoad wraps BPF_PROG_LOAD.
func BPFProgLoad(attr *BPFProgLoadAttr) (*FD, error) {
for {
fd, err := BPF(BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
// As of ~4.20 the verifier can be interrupted by a signal,
// and returns EAGAIN in that case.
if errors.Is(err, unix.EAGAIN) {
continue
}
if err != nil {
return nil, err
}
return NewFD(uint32(fd)), nil
}
}
type BPFProgAttachAttr struct {
TargetFd uint32
AttachBpfFd uint32
@@ -180,6 +223,22 @@ func BPFObjGetInfoByFD(fd *FD, info unsafe.Pointer, size uintptr) error {
return nil
}
type bpfGetFDByIDAttr struct {
id uint32
next uint32
}
// BPFObjGetInfoByFD wraps BPF_*_GET_FD_BY_ID.
//
// Available from 4.13.
func BPFObjGetFDByID(cmd BPFCmd, id uint32) (*FD, error) {
attr := bpfGetFDByIDAttr{
id: id,
}
ptr, err := BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return NewFD(uint32(ptr)), err
}
// BPFObjName is a null-terminated string made up of
// 'A-Za-z0-9_' characters.
type BPFObjName [unix.BPF_OBJ_NAME_LEN]byte

View File

@@ -1,3 +1,4 @@
//go:build linux
// +build linux
package unix
@@ -20,10 +21,11 @@ const (
EPERM = linux.EPERM
ESRCH = linux.ESRCH
ENODEV = linux.ENODEV
EBADF = linux.EBADF
E2BIG = linux.E2BIG
// ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP
ENOTSUPP = syscall.Errno(0x20c)
EBADF = linux.EBADF
BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC
BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE
BPF_F_RDONLY = linux.BPF_F_RDONLY
@@ -35,6 +37,9 @@ const (
BPF_F_INNER_MAP = linux.BPF_F_INNER_MAP
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
BPF_TAG_SIZE = linux.BPF_TAG_SIZE
BPF_RINGBUF_BUSY_BIT = linux.BPF_RINGBUF_BUSY_BIT
BPF_RINGBUF_DISCARD_BIT = linux.BPF_RINGBUF_DISCARD_BIT
BPF_RINGBUF_HDR_SZ = linux.BPF_RINGBUF_HDR_SZ
SYS_BPF = linux.SYS_BPF
F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
@@ -69,11 +74,6 @@ type Statfs_t = linux.Statfs_t
// Rlimit is a wrapper
type Rlimit = linux.Rlimit
// Setrlimit is a wrapper
func Setrlimit(resource int, rlim *Rlimit) (err error) {
return linux.Setrlimit(resource, rlim)
}
// Syscall is a wrapper
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return linux.Syscall(trap, a1, a2, a3)
@@ -202,3 +202,7 @@ func KernelRelease() (string, error) {
release := string(uname.Release[:end])
return release, nil
}
func Prlimit(pid, resource int, new, old *Rlimit) error {
return linux.Prlimit(pid, resource, new, old)
}

View File

@@ -1,3 +1,4 @@
//go:build !linux
// +build !linux
package unix
@@ -21,6 +22,7 @@ const (
ESRCH = syscall.ESRCH
ENODEV = syscall.ENODEV
EBADF = syscall.Errno(0)
E2BIG = syscall.Errno(0)
// ENOTSUPP is not the same as ENOTSUP or EOPNOTSUP
ENOTSUPP = syscall.Errno(0x20c)
@@ -35,6 +37,9 @@ const (
BPF_F_INNER_MAP = 0
BPF_OBJ_NAME_LEN = 0x10
BPF_TAG_SIZE = 0x8
BPF_RINGBUF_BUSY_BIT = 0
BPF_RINGBUF_DISCARD_BIT = 0
BPF_RINGBUF_HDR_SZ = 0
SYS_BPF = 321
F_DUPFD_CLOEXEC = 0x406
EPOLLIN = 0x1
@@ -86,11 +91,6 @@ type Rlimit struct {
Max uint64
}
// Setrlimit is a wrapper
func Setrlimit(resource int, rlim *Rlimit) (err error) {
return errNonLinux
}
// Syscall is a wrapper
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
return 0, 0, syscall.Errno(1)
@@ -261,3 +261,7 @@ func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags
func KernelRelease() (string, error) {
return "", errNonLinux
}
func Prlimit(pid, resource int, new, old *Rlimit) error {
return errNonLinux
}

View File

@@ -2,7 +2,7 @@ package internal
import (
"fmt"
"io/ioutil"
"os"
"regexp"
"sync"
@@ -109,7 +109,7 @@ func detectKernelVersion() (Version, error) {
// Example format: Ubuntu 4.15.0-91.92-generic 4.15.18
// This method exists in the kernel itself, see d18acd15c
// ("perf tools: Fix kernel version error in ubuntu").
if pvs, err := ioutil.ReadFile("/proc/version_signature"); err == nil {
if pvs, err := os.ReadFile("/proc/version_signature"); err == nil {
// If /proc/version_signature exists, failing to parse it is an error.
// It only exists on Ubuntu, where the real patch level is not obtainable
// through any other method.

88
vendor/github.com/cilium/ebpf/link/freplace.go generated vendored Normal file
View File

@@ -0,0 +1,88 @@
package link
import (
"fmt"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal/btf"
)
type FreplaceLink struct {
RawLink
}
// AttachFreplace attaches the given eBPF program to the function it replaces.
//
// The program and name can either be provided at link time, or can be provided
// at program load time. If they were provided at load time, they should be nil
// and empty respectively here, as they will be ignored by the kernel.
// Examples:
//
// AttachFreplace(dispatcher, "function", replacement)
// AttachFreplace(nil, "", replacement)
func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (*FreplaceLink, error) {
if (name == "") != (targetProg == nil) {
return nil, fmt.Errorf("must provide both or neither of name and targetProg: %w", errInvalidInput)
}
if prog == nil {
return nil, fmt.Errorf("prog cannot be nil: %w", errInvalidInput)
}
if prog.Type() != ebpf.Extension {
return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput)
}
var (
target int
typeID btf.TypeID
)
if targetProg != nil {
info, err := targetProg.Info()
if err != nil {
return nil, err
}
btfID, ok := info.BTFID()
if !ok {
return nil, fmt.Errorf("could not get BTF ID for program %s: %w", info.Name, errInvalidInput)
}
btfHandle, err := btf.NewHandleFromID(btfID)
if err != nil {
return nil, err
}
defer btfHandle.Close()
var function *btf.Func
if err := btfHandle.Spec().FindType(name, &function); err != nil {
return nil, err
}
target = targetProg.FD()
typeID = function.ID()
}
link, err := AttachRawLink(RawLinkOptions{
Target: target,
Program: prog,
Attach: ebpf.AttachNone,
BTF: typeID,
})
if err != nil {
return nil, err
}
return &FreplaceLink{*link}, nil
}
// Update implements the Link interface.
func (f *FreplaceLink) Update(new *ebpf.Program) error {
return fmt.Errorf("freplace update: %w", ErrNotSupported)
}
// LoadPinnedFreplace loads a pinned iterator from a bpffs.
func LoadPinnedFreplace(fileName string, opts *ebpf.LoadPinOptions) (*FreplaceLink, error) {
link, err := LoadPinnedRawLink(fileName, TracingType, opts)
if err != nil {
return nil, err
}
return &FreplaceLink{*link}, err
}

View File

@@ -5,7 +5,6 @@ import (
"crypto/rand"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"runtime"
@@ -72,10 +71,11 @@ func (pt probeType) RetprobeBit() (uint64, error) {
// given kernel symbol starts executing. See /proc/kallsyms for available
// symbols. For example, printk():
//
// Kprobe("printk", prog)
// kp, err := Kprobe("printk", prog)
//
// The resulting Link must be Closed during program shutdown to avoid leaking
// system resources.
// Losing the reference to the resulting Link (kp) will close the Kprobe
// and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
func Kprobe(symbol string, prog *ebpf.Program) (Link, error) {
k, err := kprobe(symbol, prog, false)
if err != nil {
@@ -95,10 +95,11 @@ func Kprobe(symbol string, prog *ebpf.Program) (Link, error) {
// before the given kernel symbol exits, with the function stack left intact.
// See /proc/kallsyms for available symbols. For example, printk():
//
// Kretprobe("printk", prog)
// kp, err := Kretprobe("printk", prog)
//
// The resulting Link must be Closed during program shutdown to avoid leaking
// system resources.
// Losing the reference to the resulting Link (kp) will close the Kretprobe
// and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
func Kretprobe(symbol string, prog *ebpf.Program) (Link, error) {
k, err := kprobe(symbol, prog, true)
if err != nil {
@@ -157,7 +158,7 @@ func kprobe(symbol string, prog *ebpf.Program, ret bool) (*perfEvent, error) {
// pmuKprobe opens a perf event based on the kprobe PMU.
// Returns os.ErrNotExist if the given symbol does not exist in the kernel.
func pmuKprobe(symbol string, ret bool) (*perfEvent, error) {
return pmuProbe(kprobeType, symbol, "", 0, ret)
return pmuProbe(kprobeType, symbol, "", 0, perfAllThreads, ret)
}
// pmuProbe opens a perf event based on a Performance Monitoring Unit.
@@ -167,7 +168,7 @@ func pmuKprobe(symbol string, ret bool) (*perfEvent, error) {
// 33ea4b24277b "perf/core: Implement the 'perf_uprobe' PMU"
//
// Returns ErrNotSupported if the kernel doesn't support perf_[k,u]probe PMU
func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
func pmuProbe(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
// Getting the PMU type will fail if the kernel doesn't support
// the perf_[k,u]probe PMU.
et, err := getPMUEventType(typ)
@@ -191,7 +192,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
switch typ {
case kprobeType:
// Create a pointer to a NUL-terminated string for the kernel.
sp, err := unsafeStringPtr(symbol)
sp, err = unsafeStringPtr(symbol)
if err != nil {
return nil, err
}
@@ -202,7 +203,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
Config: config, // Retprobe flag
}
case uprobeType:
sp, err := unsafeStringPtr(path)
sp, err = unsafeStringPtr(path)
if err != nil {
return nil, err
}
@@ -220,7 +221,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
}
}
fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
// Since commit 97c753e62e6c, ENOENT is correctly returned instead of EINVAL
// when trying to create a kretprobe for a missing symbol. Make sure ENOENT
@@ -228,6 +229,11 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
if errors.Is(err, os.ErrNotExist) || errors.Is(err, unix.EINVAL) {
return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, os.ErrNotExist)
}
// Since at least commit cb9a19fe4aa51, ENOTSUPP is returned
// when attempting to set a uprobe on a trap instruction.
if errors.Is(err, unix.ENOTSUPP) {
return nil, fmt.Errorf("failed setting uprobe on offset %#x (possible trap insn): %w", offset, err)
}
if err != nil {
return nil, fmt.Errorf("opening perf event: %w", err)
}
@@ -246,7 +252,7 @@ func pmuProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*per
// tracefsKprobe creates a Kprobe tracefs entry.
func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) {
return tracefsProbe(kprobeType, symbol, "", 0, ret)
return tracefsProbe(kprobeType, symbol, "", 0, perfAllThreads, ret)
}
// tracefsProbe creates a trace event by writing an entry to <tracefs>/[k,u]probe_events.
@@ -255,7 +261,7 @@ func tracefsKprobe(symbol string, ret bool) (*perfEvent, error) {
// Path and offset are only set in the case of uprobe(s) and are used to set
// the executable/library path on the filesystem and the offset where the probe is inserted.
// A perf event is then opened on the newly-created trace event and returned to the caller.
func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
func tracefsProbe(typ probeType, symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
// Generate a random string for each trace event we attempt to create.
// This value is used as the 'group' token in tracefs to allow creating
// multiple kprobe trace events with the same name.
@@ -288,7 +294,7 @@ func tracefsProbe(typ probeType, symbol, path string, offset uint64, ret bool) (
}
// Kprobes are ephemeral tracepoints and share the same perf event type.
fd, err := openTracepointPerfEvent(tid)
fd, err := openTracepointPerfEvent(tid, pid)
if err != nil {
return nil, err
}
@@ -413,7 +419,7 @@ func probePrefix(ret bool) string {
func determineRetprobeBit(typ probeType) (uint64, error) {
p := filepath.Join("/sys/bus/event_source/devices/", typ.String(), "/format/retprobe")
data, err := ioutil.ReadFile(p)
data, err := os.ReadFile(p)
if err != nil {
return 0, err
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
)
var ErrNotSupported = internal.ErrNotSupported
@@ -29,8 +30,8 @@ type Link interface {
// Close frees resources.
//
// The link will be broken unless it has been pinned. A link
// may continue past the lifetime of the process if Close is
// The link will be broken unless it has been successfully pinned.
// A link may continue past the lifetime of the process if Close is
// not called.
Close() error
@@ -49,6 +50,8 @@ type RawLinkOptions struct {
Program *ebpf.Program
// Attach must match the attach type of Program.
Attach ebpf.AttachType
// BTF is the BTF of the attachment target.
BTF btf.TypeID
}
// RawLinkInfo contains metadata on a link.
@@ -83,9 +86,10 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
}
attr := bpfLinkCreateAttr{
targetFd: uint32(opts.Target),
progFd: uint32(progFd),
attachType: opts.Attach,
targetFd: uint32(opts.Target),
progFd: uint32(progFd),
attachType: opts.Attach,
targetBTFID: uint32(opts.BTF),
}
fd, err := bpfLinkCreate(&attr)
if err != nil {

View File

@@ -4,7 +4,6 @@ import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
@@ -236,7 +235,7 @@ func getPMUEventType(typ probeType) (uint64, error) {
// openTracepointPerfEvent opens a tracepoint-type perf event. System-wide
// [k,u]probes created by writing to <tracefs>/[k,u]probe_events are tracepoints
// behind the scenes, and can be attached to using these perf events.
func openTracepointPerfEvent(tid uint64) (*internal.FD, error) {
func openTracepointPerfEvent(tid uint64, pid int) (*internal.FD, error) {
attr := unix.PerfEventAttr{
Type: unix.PERF_TYPE_TRACEPOINT,
Config: tid,
@@ -245,7 +244,7 @@ func openTracepointPerfEvent(tid uint64) (*internal.FD, error) {
Wakeup: 1,
}
fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
fd, err := unix.PerfEventOpen(&attr, pid, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
if err != nil {
return nil, fmt.Errorf("opening tracepoint perf event: %w", err)
}
@@ -263,7 +262,7 @@ func uint64FromFile(base string, path ...string) (uint64, error) {
return 0, fmt.Errorf("path '%s' attempts to escape base path '%s': %w", l, base, errInvalidInput)
}
data, err := ioutil.ReadFile(p)
data, err := os.ReadFile(p)
if err != nil {
return 0, fmt.Errorf("reading file %s: %w", p, err)
}

View File

@@ -88,10 +88,11 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace
})
type bpfLinkCreateAttr struct {
progFd uint32
targetFd uint32
attachType ebpf.AttachType
flags uint32
progFd uint32
targetFd uint32
attachType ebpf.AttachType
flags uint32
targetBTFID uint32
}
func bpfLinkCreate(attr *bpfLinkCreateAttr) (*internal.FD, error) {

View File

@@ -11,7 +11,11 @@ import (
// tracepoints. The top-level directory is the group, the event's subdirectory
// is the name. Example:
//
// Tracepoint("syscalls", "sys_enter_fork", prog)
// tp, err := Tracepoint("syscalls", "sys_enter_fork", prog)
//
// Losing the reference to the resulting Link (tp) will close the Tracepoint
// and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
//
// Note that attaching eBPF programs to syscalls (sys_enter_*/sys_exit_*) is
// only possible as of kernel 4.14 (commit cf5f5ce).
@@ -34,7 +38,7 @@ func Tracepoint(group, name string, prog *ebpf.Program) (Link, error) {
return nil, err
}
fd, err := openTracepointPerfEvent(tid)
fd, err := openTracepointPerfEvent(tid, perfAllThreads)
if err != nil {
return nil, err
}

View File

@@ -25,14 +25,18 @@ var (
value uint64
err error
}{}
// ErrNoSymbol indicates that the given symbol was not found
// in the ELF symbols table.
ErrNoSymbol = errors.New("not found")
)
// Executable defines an executable program on the filesystem.
type Executable struct {
// Path of the executable on the filesystem.
path string
// Parsed ELF symbols and dynamic symbols.
symbols map[string]elf.Symbol
// Parsed ELF symbols and dynamic symbols offsets.
offsets map[string]uint64
}
// UprobeOptions defines additional parameters that will be used
@@ -41,6 +45,9 @@ type UprobeOptions struct {
// Symbol offset. Must be provided in case of external symbols (shared libs).
// If set, overrides the offset eventually parsed from the executable.
Offset uint64
// Only set the uprobe on the given process ID. Useful when tracing
// shared library calls or programs that have many running instances.
PID int
}
// To open a new Executable, use:
@@ -64,42 +71,84 @@ func OpenExecutable(path string) (*Executable, error) {
return nil, fmt.Errorf("parse ELF file: %w", err)
}
var ex = Executable{
path: path,
symbols: make(map[string]elf.Symbol),
}
if err := ex.addSymbols(se.Symbols); err != nil {
return nil, err
if se.Type != elf.ET_EXEC && se.Type != elf.ET_DYN {
// ELF is not an executable or a shared object.
return nil, errors.New("the given file is not an executable or a shared object")
}
if err := ex.addSymbols(se.DynamicSymbols); err != nil {
ex := Executable{
path: path,
offsets: make(map[string]uint64),
}
if err := ex.load(se); err != nil {
return nil, err
}
return &ex, nil
}
func (ex *Executable) addSymbols(f func() ([]elf.Symbol, error)) error {
// elf.Symbols and elf.DynamicSymbols return ErrNoSymbols if the section is not found.
syms, err := f()
func (ex *Executable) load(f *internal.SafeELFFile) error {
syms, err := f.Symbols()
if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
return err
}
dynsyms, err := f.DynamicSymbols()
if err != nil && !errors.Is(err, elf.ErrNoSymbols) {
return err
}
syms = append(syms, dynsyms...)
for _, s := range syms {
if elf.ST_TYPE(s.Info) != elf.STT_FUNC {
// Symbol not associated with a function or other executable code.
continue
}
ex.symbols[s.Name] = s
off := s.Value
// Loop over ELF segments.
for _, prog := range f.Progs {
// Skip uninteresting segments.
if prog.Type != elf.PT_LOAD || (prog.Flags&elf.PF_X) == 0 {
continue
}
if prog.Vaddr <= s.Value && s.Value < (prog.Vaddr+prog.Memsz) {
// If the symbol value is contained in the segment, calculate
// the symbol offset.
//
// fn symbol offset = fn symbol VA - .text VA + .text offset
//
// stackoverflow.com/a/40249502
off = s.Value - prog.Vaddr + prog.Off
break
}
}
ex.offsets[s.Name] = off
}
return nil
}
func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) {
if s, ok := ex.symbols[symbol]; ok {
return &s, nil
func (ex *Executable) offset(symbol string) (uint64, error) {
if off, ok := ex.offsets[symbol]; ok {
// Symbols with location 0 from section undef are shared library calls and
// are relocated before the binary is executed. Dynamic linking is not
// implemented by the library, so mark this as unsupported for now.
//
// Since only offset values are stored and not elf.Symbol, if the value is 0,
// assume it's an external symbol.
if off == 0 {
return 0, fmt.Errorf("cannot resolve %s library call '%s', "+
"consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported)
}
return off, nil
}
return nil, fmt.Errorf("symbol %s not found", symbol)
return 0, fmt.Errorf("symbol %s: %w", symbol, ErrNoSymbol)
}
// Uprobe attaches the given eBPF program to a perf event that fires when the
@@ -112,11 +161,14 @@ func (ex *Executable) symbol(symbol string) (*elf.Symbol, error) {
// When using symbols which belongs to shared libraries,
// an offset must be provided via options:
//
// ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
// up, err := ex.Uprobe("main", prog, &UprobeOptions{Offset: 0x123})
//
// The resulting Link must be Closed during program shutdown to avoid leaking
// system resources. Functions provided by shared libraries can currently not
// be traced and will result in an ErrNotSupported.
// Losing the reference to the resulting Link (up) will close the Uprobe
// and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
//
// Functions provided by shared libraries can currently not be traced and
// will result in an ErrNotSupported.
func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
u, err := ex.uprobe(symbol, prog, opts, false)
if err != nil {
@@ -141,11 +193,14 @@ func (ex *Executable) Uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
// When using symbols which belongs to shared libraries,
// an offset must be provided via options:
//
// ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
// up, err := ex.Uretprobe("main", prog, &UprobeOptions{Offset: 0x123})
//
// The resulting Link must be Closed during program shutdown to avoid leaking
// system resources. Functions provided by shared libraries can currently not
// be traced and will result in an ErrNotSupported.
// Losing the reference to the resulting Link (up) will close the Uprobe
// and prevent further execution of prog. The Link must be Closed during
// program shutdown to avoid leaking system resources.
//
// Functions provided by shared libraries can currently not be traced and
// will result in an ErrNotSupported.
func (ex *Executable) Uretprobe(symbol string, prog *ebpf.Program, opts *UprobeOptions) (Link, error) {
u, err := ex.uprobe(symbol, prog, opts, true)
if err != nil {
@@ -175,24 +230,20 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
if opts != nil && opts.Offset != 0 {
offset = opts.Offset
} else {
sym, err := ex.symbol(symbol)
off, err := ex.offset(symbol)
if err != nil {
return nil, fmt.Errorf("symbol '%s' not found: %w", symbol, err)
return nil, err
}
offset = off
}
// Symbols with location 0 from section undef are shared library calls and
// are relocated before the binary is executed. Dynamic linking is not
// implemented by the library, so mark this as unsupported for now.
if sym.Section == elf.SHN_UNDEF && sym.Value == 0 {
return nil, fmt.Errorf("cannot resolve %s library call '%s', "+
"consider providing the offset via options: %w", ex.path, symbol, ErrNotSupported)
}
offset = sym.Value
pid := perfAllThreads
if opts != nil && opts.PID != 0 {
pid = opts.PID
}
// Use uprobe PMU if the kernel has it available.
tp, err := pmuUprobe(symbol, ex.path, offset, ret)
tp, err := pmuUprobe(symbol, ex.path, offset, pid, ret)
if err == nil {
return tp, nil
}
@@ -201,7 +252,7 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
}
// Use tracefs if uprobe PMU is missing.
tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, ret)
tp, err = tracefsUprobe(uprobeSanitizedSymbol(symbol), ex.path, offset, pid, ret)
if err != nil {
return nil, fmt.Errorf("creating trace event '%s:%s' in tracefs: %w", ex.path, symbol, err)
}
@@ -210,13 +261,13 @@ func (ex *Executable) uprobe(symbol string, prog *ebpf.Program, opts *UprobeOpti
}
// pmuUprobe opens a perf event based on the uprobe PMU.
func pmuUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
return pmuProbe(uprobeType, symbol, path, offset, ret)
func pmuUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
return pmuProbe(uprobeType, symbol, path, offset, pid, ret)
}
// tracefsUprobe creates a Uprobe tracefs entry.
func tracefsUprobe(symbol, path string, offset uint64, ret bool) (*perfEvent, error) {
return tracefsProbe(uprobeType, symbol, path, offset, ret)
func tracefsUprobe(symbol, path string, offset uint64, pid int, ret bool) (*perfEvent, error) {
return tracefsProbe(uprobeType, symbol, path, offset, pid, ret)
}
// uprobeSanitizedSymbol replaces every invalid characted for the tracefs api with an underscore.

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal/btf"
)
// link resolves bpf-to-bpf calls.
@@ -40,7 +39,7 @@ func link(prog *ProgramSpec, libs []*ProgramSpec) error {
pending = append(pending, lib.Instructions)
if prog.BTF != nil && lib.BTF != nil {
if err := btf.ProgramAppend(prog.BTF, lib.BTF); err != nil {
if err := prog.BTF.Append(lib.BTF); err != nil {
return fmt.Errorf("linking BTF of %s: %w", lib.Name, err)
}
}
@@ -136,5 +135,25 @@ func fixupJumpsAndCalls(insns asm.Instructions) error {
}
}
// fixupBPFCalls replaces bpf_probe_read_{kernel,user}[_str] with bpf_probe_read[_str] on older kernels
// https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L6009
iter = insns.Iterate()
for iter.Next() {
ins := iter.Ins
if !ins.IsBuiltinCall() {
continue
}
switch asm.BuiltinFunc(ins.Constant) {
case asm.FnProbeReadKernel, asm.FnProbeReadUser:
if err := haveProbeReadKernel(); err != nil {
ins.Constant = int64(asm.FnProbeRead)
}
case asm.FnProbeReadKernelStr, asm.FnProbeReadUserStr:
if err := haveProbeReadKernel(); err != nil {
ins.Constant = int64(asm.FnProbeReadStr)
}
}
}
return nil
}

95
vendor/github.com/cilium/ebpf/map.go generated vendored
View File

@@ -1,6 +1,7 @@
package ebpf
import (
"bytes"
"errors"
"fmt"
"io"
@@ -65,6 +66,11 @@ type MapSpec struct {
// InnerMap is used as a template for ArrayOfMaps and HashOfMaps
InnerMap *MapSpec
// Extra trailing bytes found in the ELF map definition when using structs
// larger than libbpf's bpf_map_def. Must be empty before instantiating
// the MapSpec into a Map.
Extra bytes.Reader
// The BTF associated with this map.
BTF *btf.Map
}
@@ -82,9 +88,12 @@ func (ms *MapSpec) Copy() *MapSpec {
}
cpy := *ms
cpy.Contents = make([]MapKV, len(ms.Contents))
copy(cpy.Contents, ms.Contents)
cpy.InnerMap = ms.InnerMap.Copy()
return &cpy
}
@@ -188,14 +197,24 @@ func NewMap(spec *MapSpec) (*Map, error) {
//
// The caller is responsible for ensuring the process' rlimit is set
// sufficiently high for locking memory during map creation. This can be done
// by calling unix.Setrlimit with unix.RLIMIT_MEMLOCK prior to calling NewMapWithOptions.
// by calling rlimit.RemoveMemlock() prior to calling NewMapWithOptions.
//
// May return an error wrapping ErrMapIncompatible.
func NewMapWithOptions(spec *MapSpec, opts MapOptions) (*Map, error) {
handles := newHandleCache()
defer handles.close()
return newMapWithOptions(spec, opts, handles)
m, err := newMapWithOptions(spec, opts, handles)
if err != nil {
return nil, fmt.Errorf("creating map: %w", err)
}
err = m.finalize(spec)
if err != nil {
return nil, fmt.Errorf("populating map: %w", err)
}
return m, nil
}
func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_ *Map, err error) {
@@ -207,8 +226,12 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
switch spec.Pinning {
case PinByName:
if spec.Name == "" || opts.PinPath == "" {
return nil, fmt.Errorf("pin by name: missing Name or PinPath")
if spec.Name == "" {
return nil, fmt.Errorf("pin by name: missing Name")
}
if opts.PinPath == "" {
return nil, fmt.Errorf("pin by name: missing MapOptions.PinPath")
}
path := filepath.Join(opts.PinPath, spec.Name)
@@ -244,16 +267,19 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
return nil, errors.New("inner maps cannot be pinned")
}
template, err := createMap(spec.InnerMap, nil, opts, handles)
template, err := spec.InnerMap.createMap(nil, opts, handles)
if err != nil {
return nil, err
return nil, fmt.Errorf("inner map: %w", err)
}
defer template.Close()
// Intentionally skip populating and freezing (finalizing)
// the inner map template since it will be removed shortly.
innerFd = template.fd
}
m, err := createMap(spec, innerFd, opts, handles)
m, err := spec.createMap(innerFd, opts, handles)
if err != nil {
return nil, err
}
@@ -269,7 +295,9 @@ func newMapWithOptions(spec *MapSpec, opts MapOptions, handles *handleCache) (_
return m, nil
}
func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) {
// createMap validates the spec's properties and creates the map in the kernel
// using the given opts. It does not populate or freeze the map.
func (spec *MapSpec) createMap(inner *internal.FD, opts MapOptions, handles *handleCache) (_ *Map, err error) {
closeOnError := func(closer io.Closer) {
if err != nil {
closer.Close()
@@ -278,10 +306,16 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
spec = spec.Copy()
// Kernels 4.13 through 5.4 used a struct bpf_map_def that contained
// additional 'inner_map_idx' and later 'numa_node' fields.
// In order to support loading these definitions, tolerate the presence of
// extra bytes, but require them to be zeroes.
if _, err := io.Copy(internal.DiscardZeroes{}, &spec.Extra); err != nil {
return nil, errors.New("extra contains unhandled non-zero bytes, drain before creating map")
}
switch spec.Type {
case ArrayOfMaps:
fallthrough
case HashOfMaps:
case ArrayOfMaps, HashOfMaps:
if err := haveNestedMaps(); err != nil {
return nil, err
}
@@ -350,7 +384,7 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
var btfDisabled bool
if spec.BTF != nil {
handle, err := handles.btfHandle(btf.MapSpec(spec.BTF))
handle, err := handles.btfHandle(spec.BTF.Spec)
btfDisabled = errors.Is(err, btf.ErrNotSupported)
if err != nil && !btfDisabled {
return nil, fmt.Errorf("load BTF: %w", err)
@@ -358,15 +392,15 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
if handle != nil {
attr.BTFFd = uint32(handle.FD())
attr.BTFKeyTypeID = uint32(btf.MapKey(spec.BTF).ID())
attr.BTFValueTypeID = uint32(btf.MapValue(spec.BTF).ID())
attr.BTFKeyTypeID = uint32(spec.BTF.Key.ID())
attr.BTFValueTypeID = uint32(spec.BTF.Value.ID())
}
}
fd, err := internal.BPFMapCreate(&attr)
if err != nil {
if errors.Is(err, unix.EPERM) {
return nil, fmt.Errorf("map create: RLIMIT_MEMLOCK may be too low: %w", err)
return nil, fmt.Errorf("map create: %w (MEMLOCK bay be too low, consider rlimit.RemoveMemlock)", err)
}
if btfDisabled {
return nil, fmt.Errorf("map create without BTF: %w", err)
@@ -380,19 +414,11 @@ func createMap(spec *MapSpec, inner *internal.FD, opts MapOptions, handles *hand
return nil, fmt.Errorf("map create: %w", err)
}
if err := m.populate(spec.Contents); err != nil {
return nil, fmt.Errorf("map create: can't set initial contents: %w", err)
}
if spec.Freeze {
if err := m.Freeze(); err != nil {
return nil, fmt.Errorf("can't freeze map: %w", err)
}
}
return m, nil
}
// newMap allocates and returns a new Map structure.
// Sets the fullValueSize on per-CPU maps.
func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEntries, flags uint32) (*Map, error) {
m := &Map{
name,
@@ -415,7 +441,7 @@ func newMap(fd *internal.FD, name string, typ MapType, keySize, valueSize, maxEn
return nil, err
}
m.fullValueSize = align(int(valueSize), 8) * possibleCPUs
m.fullValueSize = internal.Align(int(valueSize), 8) * possibleCPUs
return m, nil
}
@@ -892,12 +918,21 @@ func (m *Map) Freeze() error {
return nil
}
func (m *Map) populate(contents []MapKV) error {
for _, kv := range contents {
// finalize populates the Map according to the Contents specified
// in spec and freezes the Map if requested by spec.
func (m *Map) finalize(spec *MapSpec) error {
for _, kv := range spec.Contents {
if err := m.Put(kv.Key, kv.Value); err != nil {
return fmt.Errorf("key %v: %w", kv.Key, err)
return fmt.Errorf("putting value: key %v: %w", kv.Key, err)
}
}
if spec.Freeze {
if err := m.Freeze(); err != nil {
return fmt.Errorf("freezing map: %w", err)
}
}
return nil
}
@@ -1212,7 +1247,7 @@ func MapGetNextID(startID MapID) (MapID, error) {
//
// Returns ErrNotExist, if there is no eBPF map with the given id.
func NewMapFromID(id MapID) (*Map, error) {
fd, err := bpfObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id))
fd, err := internal.BPFObjGetFDByID(internal.BPF_MAP_GET_FD_BY_ID, uint32(id))
if err != nil {
return nil, err
}

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"reflect"
"runtime"
"sync"
"unsafe"
"github.com/cilium/ebpf/internal"
@@ -39,6 +40,10 @@ func marshalPtr(data interface{}, length int) (internal.Pointer, error) {
// Returns an error if the given value isn't representable in exactly
// length bytes.
func marshalBytes(data interface{}, length int) (buf []byte, err error) {
if data == nil {
return nil, errors.New("can't marshal a nil value")
}
switch value := data.(type) {
case encoding.BinaryMarshaler:
buf, err = value.MarshalBinary()
@@ -77,22 +82,30 @@ func makeBuffer(dst interface{}, length int) (internal.Pointer, []byte) {
return internal.NewSlicePointer(buf), buf
}
var bytesReaderPool = sync.Pool{
New: func() interface{} {
return new(bytes.Reader)
},
}
// unmarshalBytes converts a byte buffer into an arbitrary value.
//
// Prefer using Map.unmarshalKey and Map.unmarshalValue if possible, since
// those have special cases that allow more types to be encoded.
//
// The common int32 and int64 types are directly handled to avoid
// unnecessary heap allocations as happening in the default case.
func unmarshalBytes(data interface{}, buf []byte) error {
switch value := data.(type) {
case unsafe.Pointer:
// This could be solved in Go 1.17 by unsafe.Slice instead. (https://github.com/golang/go/issues/19367)
// We could opt for removing unsafe.Pointer support in the lib as well.
sh := &reflect.SliceHeader{ //nolint:govet
Data: uintptr(value),
Len: len(buf),
Cap: len(buf),
}
var dst []byte
// Use unsafe.Slice when we drop support for pre1.17 (https://github.com/golang/go/issues/19367)
// We could opt for removing unsafe.Pointer support in the lib as well
sh := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
sh.Data = uintptr(value)
sh.Len = len(buf)
sh.Cap = len(buf)
dst := *(*[]byte)(unsafe.Pointer(sh))
copy(dst, buf)
runtime.KeepAlive(value)
return nil
@@ -106,12 +119,38 @@ func unmarshalBytes(data interface{}, buf []byte) error {
case *[]byte:
*value = buf
return nil
case *int32:
if len(buf) < 4 {
return errors.New("int32 requires 4 bytes")
}
*value = int32(internal.NativeEndian.Uint32(buf))
return nil
case *uint32:
if len(buf) < 4 {
return errors.New("uint32 requires 4 bytes")
}
*value = internal.NativeEndian.Uint32(buf)
return nil
case *int64:
if len(buf) < 8 {
return errors.New("int64 requires 8 bytes")
}
*value = int64(internal.NativeEndian.Uint64(buf))
return nil
case *uint64:
if len(buf) < 8 {
return errors.New("uint64 requires 8 bytes")
}
*value = internal.NativeEndian.Uint64(buf)
return nil
case string:
return errors.New("require pointer to string")
case []byte:
return errors.New("require pointer to []byte")
default:
rd := bytes.NewReader(buf)
rd := bytesReaderPool.Get().(*bytes.Reader)
rd.Reset(buf)
defer bytesReaderPool.Put(rd)
if err := binary.Read(rd, internal.NativeEndian, value); err != nil {
return fmt.Errorf("decoding %T: %v", value, err)
}
@@ -142,7 +181,7 @@ func marshalPerCPUValue(slice interface{}, elemLength int) (internal.Pointer, er
return internal.Pointer{}, fmt.Errorf("per-CPU value exceeds number of CPUs")
}
alignedElemLength := align(elemLength, 8)
alignedElemLength := internal.Align(elemLength, 8)
buf := make([]byte, alignedElemLength*possibleCPUs)
for i := 0; i < sliceLen; i++ {
@@ -212,7 +251,3 @@ func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) erro
reflect.ValueOf(slicePtr).Elem().Set(slice)
return nil
}
func align(n, alignment int) int {
return (int(n) + alignment - 1) / alignment * alignment
}

123
vendor/github.com/cilium/ebpf/prog.go generated vendored
View File

@@ -57,16 +57,21 @@ type ProgramSpec struct {
// Name is passed to the kernel as a debug aid. Must only contain
// alpha numeric and '_' characters.
Name string
// Type determines at which hook in the kernel a program will run.
Type ProgramType
AttachType AttachType
// Name of a kernel data structure to attach to. It's interpretation
// depends on Type and AttachType.
AttachTo string
// Name of a kernel data structure or function to attach to. Its
// interpretation depends on Type and AttachType.
AttachTo string
// The program to attach to. Must be provided manually.
AttachTarget *Program
Instructions asm.Instructions
// Flags is passed to the kernel and specifies additional program
// load attributes.
Flags uint32
// License of the program. Some helpers are only available if
// the license is deemed compatible with the GPL.
//
@@ -146,7 +151,7 @@ func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, er
func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *handleCache) (*Program, error) {
if len(spec.Instructions) == 0 {
return nil, errors.New("Instructions cannot be empty")
return nil, errors.New("instructions cannot be empty")
}
if spec.ByteOrder != nil && spec.ByteOrder != internal.NativeEndian {
@@ -166,16 +171,16 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
kv = v.Kernel()
}
attr := &bpfProgLoadAttr{
progType: spec.Type,
progFlags: spec.Flags,
expectedAttachType: spec.AttachType,
license: internal.NewStringPointer(spec.License),
kernelVersion: kv,
attr := &internal.BPFProgLoadAttr{
ProgType: uint32(spec.Type),
ProgFlags: spec.Flags,
ExpectedAttachType: uint32(spec.AttachType),
License: internal.NewStringPointer(spec.License),
KernelVersion: kv,
}
if haveObjName() == nil {
attr.progName = internal.NewBPFObjName(spec.Name)
attr.ProgName = internal.NewBPFObjName(spec.Name)
}
var err error
@@ -190,35 +195,35 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
var btfDisabled bool
var core btf.COREFixups
if spec.BTF != nil {
core, err = btf.ProgramFixups(spec.BTF, targetBTF)
core, err = spec.BTF.Fixups(targetBTF)
if err != nil {
return nil, fmt.Errorf("CO-RE relocations: %w", err)
}
handle, err := handles.btfHandle(btf.ProgramSpec(spec.BTF))
handle, err := handles.btfHandle(spec.BTF.Spec())
btfDisabled = errors.Is(err, btf.ErrNotSupported)
if err != nil && !btfDisabled {
return nil, fmt.Errorf("load BTF: %w", err)
}
if handle != nil {
attr.progBTFFd = uint32(handle.FD())
attr.ProgBTFFd = uint32(handle.FD())
recSize, bytes, err := btf.ProgramLineInfos(spec.BTF)
recSize, bytes, err := spec.BTF.LineInfos()
if err != nil {
return nil, fmt.Errorf("get BTF line infos: %w", err)
}
attr.lineInfoRecSize = recSize
attr.lineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.lineInfo = internal.NewSlicePointer(bytes)
attr.LineInfoRecSize = recSize
attr.LineInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.LineInfo = internal.NewSlicePointer(bytes)
recSize, bytes, err = btf.ProgramFuncInfos(spec.BTF)
recSize, bytes, err = spec.BTF.FuncInfos()
if err != nil {
return nil, fmt.Errorf("get BTF function infos: %w", err)
}
attr.funcInfoRecSize = recSize
attr.funcInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.funcInfo = internal.NewSlicePointer(bytes)
attr.FuncInfoRecSize = recSize
attr.FuncInfoCnt = uint32(uint64(len(bytes)) / uint64(recSize))
attr.FuncInfo = internal.NewSlicePointer(bytes)
}
}
@@ -238,16 +243,41 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
}
bytecode := buf.Bytes()
attr.instructions = internal.NewSlicePointer(bytecode)
attr.insCount = uint32(len(bytecode) / asm.InstructionSize)
attr.Instructions = internal.NewSlicePointer(bytecode)
attr.InsCount = uint32(len(bytecode) / asm.InstructionSize)
if spec.AttachTo != "" {
if spec.AttachTarget != nil {
info, err := spec.AttachTarget.Info()
if err != nil {
return nil, fmt.Errorf("load target BTF: %w", err)
}
btfID, ok := info.BTFID()
if !ok {
return nil, fmt.Errorf("load target BTF: no BTF info available")
}
btfHandle, err := btf.NewHandleFromID(btfID)
if err != nil {
return nil, fmt.Errorf("load target BTF: %w", err)
}
defer btfHandle.Close()
targetBTF = btfHandle.Spec()
if err != nil {
return nil, fmt.Errorf("load target BTF: %w", err)
}
}
target, err := resolveBTFType(targetBTF, spec.AttachTo, spec.Type, spec.AttachType)
if err != nil {
return nil, err
}
if target != nil {
attr.attachBTFID = target.ID()
attr.AttachBTFID = uint32(target.ID())
}
if spec.AttachTarget != nil {
attr.AttachProgFd = uint32(spec.AttachTarget.FD())
}
}
@@ -259,12 +289,12 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
var logBuf []byte
if opts.LogLevel > 0 {
logBuf = make([]byte, logSize)
attr.logLevel = opts.LogLevel
attr.logSize = uint32(len(logBuf))
attr.logBuf = internal.NewSlicePointer(logBuf)
attr.LogLevel = opts.LogLevel
attr.LogSize = uint32(len(logBuf))
attr.LogBuf = internal.NewSlicePointer(logBuf)
}
fd, err := bpfProgLoad(attr)
fd, err := internal.BPFProgLoad(attr)
if err == nil {
return &Program{internal.CString(logBuf), fd, spec.Name, "", spec.Type}, nil
}
@@ -273,17 +303,20 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
if opts.LogLevel == 0 && opts.LogSize >= 0 {
// Re-run with the verifier enabled to get better error messages.
logBuf = make([]byte, logSize)
attr.logLevel = 1
attr.logSize = uint32(len(logBuf))
attr.logBuf = internal.NewSlicePointer(logBuf)
attr.LogLevel = 1
attr.LogSize = uint32(len(logBuf))
attr.LogBuf = internal.NewSlicePointer(logBuf)
_, logErr = bpfProgLoad(attr)
fd, logErr = internal.BPFProgLoad(attr)
if logErr == nil {
fd.Close()
}
}
if errors.Is(logErr, unix.EPERM) && logBuf[0] == 0 {
// EPERM due to RLIMIT_MEMLOCK happens before the verifier, so we can
// check that the log is empty to reduce false positives.
return nil, fmt.Errorf("load program: RLIMIT_MEMLOCK may be too low: %w", logErr)
return nil, fmt.Errorf("load program: %w (MEMLOCK bay be too low, consider rlimit.RemoveMemlock)", logErr)
}
err = internal.ErrorWithLog(err, logBuf, logErr)
@@ -310,7 +343,7 @@ func NewProgramFromFD(fd int) (*Program, error) {
//
// Returns ErrNotExist, if there is no eBPF program with the given id.
func NewProgramFromID(id ProgramID) (*Program, error) {
fd, err := bpfObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id))
fd, err := internal.BPFObjGetFDByID(internal.BPF_PROG_GET_FD_BY_ID, uint32(id))
if err != nil {
return nil, fmt.Errorf("get program by id: %w", err)
}
@@ -677,45 +710,44 @@ func ProgramGetNextID(startID ProgramID) (ProgramID, error) {
//
// Deprecated: use ProgramInfo.ID() instead.
func (p *Program) ID() (ProgramID, error) {
info, err := bpfGetProgInfoByFD(p.fd)
info, err := bpfGetProgInfoByFD(p.fd, nil)
if err != nil {
return ProgramID(0), err
}
return ProgramID(info.id), nil
}
func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
func resolveBTFType(spec *btf.Spec, name string, progType ProgramType, attachType AttachType) (btf.Type, error) {
type match struct {
p ProgramType
a AttachType
}
var target btf.Type
var typeName, featureName string
switch (match{progType, attachType}) {
case match{LSM, AttachLSMMac}:
target = new(btf.Func)
typeName = "bpf_lsm_" + name
featureName = name + " LSM hook"
case match{Tracing, AttachTraceIter}:
target = new(btf.Func)
typeName = "bpf_iter_" + name
featureName = name + " iterator"
case match{Extension, AttachNone}:
typeName = name
featureName = fmt.Sprintf("freplace %s", name)
default:
return nil, nil
}
if kernel == nil {
if spec == nil {
var err error
kernel, err = btf.LoadKernelSpec()
spec, err = btf.LoadKernelSpec()
if err != nil {
return nil, fmt.Errorf("load kernel spec: %w", err)
}
}
err := kernel.FindType(typeName, target)
var target *btf.Func
err := spec.FindType(typeName, &target)
if errors.Is(err, btf.ErrNotFound) {
return nil, &internal.UnsupportedFeatureError{
Name: featureName,
@@ -724,5 +756,6 @@ func resolveBTFType(kernel *btf.Spec, name string, progType ProgramType, attachT
if err != nil {
return nil, fmt.Errorf("resolve BTF for %s: %w", featureName, err)
}
return target, nil
}

View File

@@ -5,7 +5,7 @@
# Run all tests on a 5.4 kernel
# $ ./run-tests.sh 5.4
# Run a subset of tests:
# $ ./run-tests.sh 5.4 go test ./link
# $ ./run-tests.sh 5.4 ./link
set -euo pipefail
@@ -48,15 +48,17 @@ if [[ "${1:-}" = "--exec-vm" ]]; then
rm "${output}/fake-stdin"
fi
$sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \
--rwdir="${testdir}=${testdir}" \
--rodir=/run/input="${input}" \
--rwdir=/run/output="${output}" \
--script-sh "PATH=\"$PATH\" \"$script\" --exec-test $cmd" \
--qemu-opts -smp 2 # need at least two CPUs for some tests
if ! $sudo virtme-run --kimg "${input}/bzImage" --memory 768M --pwd \
--rwdir="${testdir}=${testdir}" \
--rodir=/run/input="${input}" \
--rwdir=/run/output="${output}" \
--script-sh "PATH=\"$PATH\" \"$script\" --exec-test $cmd" \
--kopt possible_cpus=2; then # need at least two CPUs for some tests
exit 23
fi
if [[ ! -e "${output}/success" ]]; then
exit 1
exit 42
fi
$sudo rm -r "$output"
@@ -74,7 +76,7 @@ elif [[ "${1:-}" = "--exec-test" ]]; then
dmesg -C
if ! "$@"; then
dmesg
exit 1
exit 1 # this return code is "swallowed" by qemu
fi
touch "/run/output/success"
exit 0
@@ -108,7 +110,7 @@ else
echo "No selftests found, disabling"
fi
args=(-v -short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...)
args=(-short -coverpkg=./... -coverprofile=coverage.out -count 1 ./...)
if (( $# > 0 )); then
args=("$@")
fi

View File

@@ -1,13 +1,14 @@
package ebpf
import (
"bytes"
"errors"
"fmt"
"os"
"unsafe"
"github.com/cilium/ebpf/asm"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/btf"
"github.com/cilium/ebpf/internal/unix"
)
@@ -73,30 +74,6 @@ type bpfMapInfo struct {
btf_value_type_id uint32
}
type bpfProgLoadAttr struct {
progType ProgramType
insCount uint32
instructions internal.Pointer
license internal.Pointer
logLevel uint32
logSize uint32
logBuf internal.Pointer
kernelVersion uint32 // since 4.1 2541517c32be
progFlags uint32 // since 4.11 e07b98d9bffe
progName internal.BPFObjName // since 4.15 067cae47771c
progIfIndex uint32 // since 4.15 1f6f4cb7ba21
expectedAttachType AttachType // since 4.17 5e43f899b03a
progBTFFd uint32
funcInfoRecSize uint32
funcInfo internal.Pointer
funcInfoCnt uint32
lineInfoRecSize uint32
lineInfo internal.Pointer
lineInfoCnt uint32
attachBTFID btf.TypeID
attachProgFd uint32
}
type bpfProgInfo struct {
prog_type uint32
id uint32
@@ -107,7 +84,7 @@ type bpfProgInfo struct {
xlated_prog_insns internal.Pointer
load_time uint64 // since 4.15 cb4d2b3f03d8
created_by_uid uint32
nr_map_ids uint32
nr_map_ids uint32 // since 4.15 cb4d2b3f03d8
map_ids internal.Pointer
name internal.BPFObjName // since 4.15 067cae47771c
ifindex uint32
@@ -145,11 +122,6 @@ type bpfProgTestRunAttr struct {
duration uint32
}
type bpfGetFDByIDAttr struct {
id uint32
next uint32
}
type bpfMapFreezeAttr struct {
mapFd uint32
}
@@ -160,23 +132,6 @@ type bpfObjGetNextIDAttr struct {
openFlags uint32
}
func bpfProgLoad(attr *bpfProgLoadAttr) (*internal.FD, error) {
for {
fd, err := internal.BPF(internal.BPF_PROG_LOAD, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
// As of ~4.20 the verifier can be interrupted by a signal,
// and returns EAGAIN in that case.
if errors.Is(err, unix.EAGAIN) {
continue
}
if err != nil {
return nil, err
}
return internal.NewFD(uint32(fd)), nil
}
}
func bpfProgTestRun(attr *bpfProgTestRunAttr) error {
_, err := internal.BPF(internal.BPF_PROG_TEST_RUN, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
return err
@@ -372,6 +327,10 @@ func wrapMapError(err error) error {
return internal.SyscallError(ErrNotSupported, unix.ENOTSUPP)
}
if errors.Is(err, unix.E2BIG) {
return fmt.Errorf("key too big for map: %w", err)
}
return err
}
@@ -388,8 +347,13 @@ func bpfMapFreeze(m *internal.FD) error {
return err
}
func bpfGetProgInfoByFD(fd *internal.FD) (*bpfProgInfo, error) {
func bpfGetProgInfoByFD(fd *internal.FD, ids []MapID) (*bpfProgInfo, error) {
var info bpfProgInfo
if len(ids) > 0 {
info.nr_map_ids = uint32(len(ids))
info.map_ids = internal.NewPointer(unsafe.Pointer(&ids[0]))
}
if err := internal.BPFObjGetInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info)); err != nil {
return nil, fmt.Errorf("can't get program info: %w", err)
}
@@ -471,10 +435,30 @@ var haveBatchAPI = internal.FeatureTest("map batch api", "5.6", func() error {
return nil
})
func bpfObjGetFDByID(cmd internal.BPFCmd, id uint32) (*internal.FD, error) {
attr := bpfGetFDByIDAttr{
id: id,
var haveProbeReadKernel = internal.FeatureTest("bpf_probe_read_kernel", "5.5", func() error {
insns := asm.Instructions{
asm.Mov.Reg(asm.R1, asm.R10),
asm.Add.Imm(asm.R1, -8),
asm.Mov.Imm(asm.R2, 8),
asm.Mov.Imm(asm.R3, 0),
asm.FnProbeReadKernel.Call(),
asm.Return(),
}
ptr, err := internal.BPF(cmd, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
return internal.NewFD(uint32(ptr)), err
}
buf := bytes.NewBuffer(make([]byte, 0, len(insns)*asm.InstructionSize))
if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
return err
}
bytecode := buf.Bytes()
fd, err := internal.BPFProgLoad(&internal.BPFProgLoadAttr{
ProgType: uint32(Kprobe),
License: internal.NewStringPointer("GPL"),
Instructions: internal.NewSlicePointer(bytecode),
InsCount: uint32(len(bytecode) / asm.InstructionSize),
})
if err != nil {
return internal.ErrNotSupported
}
_ = fd.Close()
return nil
})

View File

@@ -4,12 +4,17 @@ import (
"github.com/cilium/ebpf/internal/unix"
)
//go:generate stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType
//go:generate stringer -output types_string.go -type=MapType,ProgramType,PinType
// MapType indicates the type map structure
// that will be initialized in the kernel.
type MapType uint32
// Max returns the latest supported MapType.
func (_ MapType) Max() MapType {
return maxMapType - 1
}
// All the various map types that can be created
const (
UnspecifiedMap MapType = iota
@@ -85,15 +90,28 @@ const (
SkStorage
// DevMapHash - Hash-based indexing scheme for references to network devices.
DevMapHash
StructOpts
// StructOpsMap - This map holds a kernel struct with its function pointer implemented in a BPF
// program.
StructOpsMap
// RingBuf - Similar to PerfEventArray, but shared across all CPUs.
RingBuf
// InodeStorage - Specialized local storage map for inodes.
InodeStorage
// TaskStorage - Specialized local storage map for task_struct.
TaskStorage
// maxMapType - Bound enum of MapTypes, has to be last in enum.
maxMapType
)
// Deprecated: StructOpts was a typo, use StructOpsMap instead.
//
// Declared as a variable to prevent stringer from picking it up
// as an enum value.
var StructOpts MapType = StructOpsMap
// hasPerCPUValue returns true if the Map stores a value per CPU.
func (mt MapType) hasPerCPUValue() bool {
return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash
return mt == PerCPUHash || mt == PerCPUArray || mt == LRUCPUHash || mt == PerCPUCGroupStorage
}
// canStoreMap returns true if the map type accepts a map fd
@@ -111,6 +129,11 @@ func (mt MapType) canStoreProgram() bool {
// ProgramType of the eBPF program
type ProgramType uint32
// Max return the latest supported ProgramType.
func (_ ProgramType) Max() ProgramType {
return maxProgramType - 1
}
// eBPF program types
const (
UnspecifiedProgram ProgramType = iota
@@ -144,6 +167,7 @@ const (
Extension
LSM
SkLookup
maxProgramType
)
// AttachType of the eBPF program, needed to differentiate allowed context accesses in
@@ -151,6 +175,8 @@ const (
// Will cause invalid argument (EINVAL) at program load time if set incorrectly.
type AttachType uint32
//go:generate stringer -type AttachType -trimprefix Attach
// AttachNone is an alias for AttachCGroupInetIngress for readability reasons.
const AttachNone AttachType = 0
@@ -193,6 +219,10 @@ const (
AttachXDPCPUMap
AttachSkLookup
AttachXDP
AttachSkSKBVerdict
AttachSkReuseportSelect
AttachSkReuseportSelectOrMigrate
AttachPerfEvent
)
// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command

View File

@@ -1,4 +1,4 @@
// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,AttachType,PinType"; DO NOT EDIT.
// Code generated by "stringer -output types_string.go -type=MapType,ProgramType,PinType"; DO NOT EDIT.
package ebpf
@@ -34,15 +34,16 @@ func _() {
_ = x[Stack-23]
_ = x[SkStorage-24]
_ = x[DevMapHash-25]
_ = x[StructOpts-26]
_ = x[StructOpsMap-26]
_ = x[RingBuf-27]
_ = x[InodeStorage-28]
_ = x[TaskStorage-29]
_ = x[maxMapType-30]
}
const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOptsRingBufInodeStorageTaskStorage"
const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMapsDevMapSockMapCPUMapXSKMapSockHashCGroupStorageReusePortSockArrayPerCPUCGroupStorageQueueStackSkStorageDevMapHashStructOpsMapRingBufInodeStorageTaskStoragemaxMapType"
var _MapType_index = [...]uint16{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136, 142, 149, 155, 161, 169, 182, 200, 219, 224, 229, 238, 248, 258, 265, 277, 288}
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, 300}
func (i MapType) String() string {
if i >= MapType(len(_MapType_index)-1) {
@@ -85,11 +86,12 @@ func _() {
_ = x[Extension-28]
_ = x[LSM-29]
_ = x[SkLookup-30]
_ = x[maxProgramType-31]
}
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookup"
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockoptTracingStructOpsExtensionLSMSkLookupmaxProgramType"
var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294}
var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258, 265, 274, 283, 286, 294, 308}
func (i ProgramType) String() string {
if i >= ProgramType(len(_ProgramType_index)-1) {
@@ -97,61 +99,6 @@ func (i ProgramType) String() string {
}
return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[AttachNone-0]
_ = x[AttachCGroupInetIngress-0]
_ = x[AttachCGroupInetEgress-1]
_ = x[AttachCGroupInetSockCreate-2]
_ = x[AttachCGroupSockOps-3]
_ = x[AttachSkSKBStreamParser-4]
_ = x[AttachSkSKBStreamVerdict-5]
_ = x[AttachCGroupDevice-6]
_ = x[AttachSkMsgVerdict-7]
_ = x[AttachCGroupInet4Bind-8]
_ = x[AttachCGroupInet6Bind-9]
_ = x[AttachCGroupInet4Connect-10]
_ = x[AttachCGroupInet6Connect-11]
_ = x[AttachCGroupInet4PostBind-12]
_ = x[AttachCGroupInet6PostBind-13]
_ = x[AttachCGroupUDP4Sendmsg-14]
_ = x[AttachCGroupUDP6Sendmsg-15]
_ = x[AttachLircMode2-16]
_ = x[AttachFlowDissector-17]
_ = x[AttachCGroupSysctl-18]
_ = x[AttachCGroupUDP4Recvmsg-19]
_ = x[AttachCGroupUDP6Recvmsg-20]
_ = x[AttachCGroupGetsockopt-21]
_ = x[AttachCGroupSetsockopt-22]
_ = x[AttachTraceRawTp-23]
_ = x[AttachTraceFEntry-24]
_ = x[AttachTraceFExit-25]
_ = x[AttachModifyReturn-26]
_ = x[AttachLSMMac-27]
_ = x[AttachTraceIter-28]
_ = x[AttachCgroupInet4GetPeername-29]
_ = x[AttachCgroupInet6GetPeername-30]
_ = x[AttachCgroupInet4GetSockname-31]
_ = x[AttachCgroupInet6GetSockname-32]
_ = x[AttachXDPDevMap-33]
_ = x[AttachCgroupInetSockRelease-34]
_ = x[AttachXDPCPUMap-35]
_ = x[AttachSkLookup-36]
_ = x[AttachXDP-37]
}
const _AttachType_name = "AttachNoneAttachCGroupInetEgressAttachCGroupInetSockCreateAttachCGroupSockOpsAttachSkSKBStreamParserAttachSkSKBStreamVerdictAttachCGroupDeviceAttachSkMsgVerdictAttachCGroupInet4BindAttachCGroupInet6BindAttachCGroupInet4ConnectAttachCGroupInet6ConnectAttachCGroupInet4PostBindAttachCGroupInet6PostBindAttachCGroupUDP4SendmsgAttachCGroupUDP6SendmsgAttachLircMode2AttachFlowDissectorAttachCGroupSysctlAttachCGroupUDP4RecvmsgAttachCGroupUDP6RecvmsgAttachCGroupGetsockoptAttachCGroupSetsockoptAttachTraceRawTpAttachTraceFEntryAttachTraceFExitAttachModifyReturnAttachLSMMacAttachTraceIterAttachCgroupInet4GetPeernameAttachCgroupInet6GetPeernameAttachCgroupInet4GetSocknameAttachCgroupInet6GetSocknameAttachXDPDevMapAttachCgroupInetSockReleaseAttachXDPCPUMapAttachSkLookupAttachXDP"
var _AttachType_index = [...]uint16{0, 10, 32, 58, 77, 100, 124, 142, 160, 181, 202, 226, 250, 275, 300, 323, 346, 361, 380, 398, 421, 444, 466, 488, 504, 521, 537, 555, 567, 582, 610, 638, 666, 694, 709, 736, 751, 765, 774}
func (i AttachType) String() string {
if i >= AttachType(len(_AttachType_index)-1) {
return "AttachType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _AttachType_name[_AttachType_index[i]:_AttachType_index[i+1]]
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.