1 Star 1 Fork 0

amos49/cilium_ebpf

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
elf_reader_test.go 34.66 KB
一键复制 编辑 原始数据 按行查看 历史
Lorenz Bauer 提交于 2024-04-26 12:23 . ci: replace virtme with vimto
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315
package ebpf
import (
"bytes"
"encoding/binary"
"errors"
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"syscall"
"testing"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/internal"
"github.com/cilium/ebpf/internal/testutils"
"github.com/cilium/ebpf/internal/unix"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/go-quicktest/qt"
)
func TestLoadCollectionSpec(t *testing.T) {
coll := &CollectionSpec{
Maps: map[string]*MapSpec{
"hash_map": {
Name: "hash_map",
Type: Hash,
KeySize: 4,
ValueSize: 8,
MaxEntries: 1,
Flags: unix.BPF_F_NO_PREALLOC,
},
"hash_map2": {
Name: "hash_map2",
Type: Hash,
KeySize: 4,
ValueSize: 8,
MaxEntries: 2,
},
"array_of_hash_map": {
Name: "array_of_hash_map",
Type: ArrayOfMaps,
KeySize: 4,
MaxEntries: 2,
},
"perf_event_array": {
Name: "perf_event_array",
Type: PerfEventArray,
MaxEntries: 4096,
},
"btf_pin": {
Name: "btf_pin",
Type: Hash,
KeySize: 4,
ValueSize: 8,
MaxEntries: 1,
Pinning: PinByName,
},
"btf_outer_map": {
Name: "btf_outer_map",
Type: ArrayOfMaps,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
InnerMap: &MapSpec{
Name: "btf_outer_map_inner",
Type: Hash,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
},
},
"btf_outer_map_anon": {
Name: "btf_outer_map_anon",
Type: ArrayOfMaps,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
InnerMap: &MapSpec{
Name: "btf_outer_map_anon_inner",
Type: Hash,
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
},
},
"btf_typedef_map": {
Name: "btf_typedef_map",
Type: Array,
KeySize: 4,
ValueSize: 8,
MaxEntries: 1,
},
},
Programs: map[string]*ProgramSpec{
"xdp_prog": {
Name: "xdp_prog",
Type: XDP,
SectionName: "xdp",
License: "MIT",
},
"no_relocation": {
Name: "no_relocation",
Type: SocketFilter,
SectionName: "socket",
License: "MIT",
},
"asm_relocation": {
Name: "asm_relocation",
Type: SocketFilter,
SectionName: "socket/2",
License: "MIT",
},
"data_sections": {
Name: "data_sections",
Type: SocketFilter,
SectionName: "socket/3",
License: "MIT",
},
"global_fn3": {
Name: "global_fn3",
Type: UnspecifiedProgram,
SectionName: "other",
License: "MIT",
},
"static_fn": {
Name: "static_fn",
Type: UnspecifiedProgram,
SectionName: "static",
License: "MIT",
},
"anon_const": {
Name: "anon_const",
Type: SocketFilter,
SectionName: "socket/4",
License: "MIT",
},
},
}
cmpOpts := cmp.Options{
// Dummy Comparer that works with empty readers to support test cases.
cmp.Comparer(func(a, b bytes.Reader) bool {
if a.Len() == 0 && b.Len() == 0 {
return true
}
return false
}),
cmpopts.IgnoreTypes(new(btf.Spec)),
cmpopts.IgnoreFields(CollectionSpec{}, "ByteOrder", "Types"),
cmpopts.IgnoreFields(ProgramSpec{}, "Instructions", "ByteOrder"),
cmpopts.IgnoreFields(MapSpec{}, "Key", "Value"),
cmpopts.IgnoreUnexported(ProgramSpec{}),
cmpopts.IgnoreMapEntries(func(key string, _ *MapSpec) bool {
if key == ".bss" || key == ".data" || strings.HasPrefix(key, ".rodata") {
return true
}
return false
}),
}
testutils.Files(t, testutils.Glob(t, "testdata/loader-*.elf"), func(t *testing.T, file string) {
have, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal("Can't parse ELF:", err)
}
err = have.RewriteConstants(map[string]interface{}{
"arg": uint32(1),
"arg2": uint32(2),
})
if err != nil {
t.Fatal("Can't rewrite constant:", err)
}
err = have.RewriteConstants(map[string]interface{}{
"totallyBogus": uint32(1),
})
if err == nil {
t.Error("Rewriting a bogus constant doesn't fail")
}
if diff := cmp.Diff(coll, have, cmpOpts...); diff != "" {
t.Errorf("MapSpec mismatch (-want +got):\n%s", diff)
}
if have.ByteOrder != internal.NativeEndian {
return
}
have.Maps["array_of_hash_map"].InnerMap = have.Maps["hash_map"]
coll, err := NewCollectionWithOptions(have, CollectionOptions{
Maps: MapOptions{
PinPath: testutils.TempBPFFS(t),
},
Programs: ProgramOptions{
LogLevel: LogLevelBranch,
},
})
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer coll.Close()
ret, _, err := coll.Programs["xdp_prog"].Test(internal.EmptyBPFContext)
if err != nil {
t.Fatal("Can't run program:", err)
}
if ret != 7 {
t.Error("Unexpected return value:", ret)
}
})
}
func BenchmarkELFLoader(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
_, _ = LoadCollectionSpec("testdata/loader-el.elf")
}
}
func TestDataSections(t *testing.T) {
file := testutils.NativeFile(t, "testdata/loader-%s.elf")
coll, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
t.Log(coll.Programs["data_sections"].Instructions)
var obj struct {
Program *Program `ebpf:"data_sections"`
}
err = coll.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.Program.Close()
ret, _, err := obj.Program.Test(internal.EmptyBPFContext)
if err != nil {
t.Fatal(err)
}
if ret != 0 {
t.Error("BPF assertion failed on line", ret)
}
}
func TestInlineASMConstant(t *testing.T) {
file := testutils.NativeFile(t, "testdata/loader-%s.elf")
coll, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
spec := coll.Programs["asm_relocation"]
if spec.Instructions[0].Reference() != "MY_CONST" {
t.Fatal("First instruction is not a reference to MY_CONST")
}
// -1 is used by the loader to find unrewritten maps.
spec.Instructions[0].Constant = -1
t.Log(spec.Instructions)
var obj struct {
Program *Program `ebpf:"asm_relocation"`
}
err = coll.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
obj.Program.Close()
}
func TestFreezeRodata(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.9", "sk_lookup program type")
file := testutils.NativeFile(t, "testdata/constants-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Program *Program `ebpf:"freeze_rodata"`
}
if err := spec.RewriteConstants(map[string]interface{}{
"ret": uint32(1),
}); err != nil {
t.Fatal(err)
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.Program.Close()
}
func TestCollectionSpecDetach(t *testing.T) {
coll := Collection{
Maps: map[string]*Map{
"foo": new(Map),
},
Programs: map[string]*Program{
"bar": new(Program),
},
}
foo := coll.DetachMap("foo")
if foo == nil {
t.Error("Program not returned from DetachMap")
}
if _, ok := coll.Programs["foo"]; ok {
t.Error("DetachMap doesn't remove map from Maps")
}
bar := coll.DetachProgram("bar")
if bar == nil {
t.Fatal("Program not returned from DetachProgram")
}
if _, ok := coll.Programs["bar"]; ok {
t.Error("DetachProgram doesn't remove program from Programs")
}
}
func TestLoadInvalidMap(t *testing.T) {
file := testutils.NativeFile(t, "testdata/invalid_map-%s.elf")
cs, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal("Can't load CollectionSpec", err)
}
ms, ok := cs.Maps["invalid_map"]
if !ok {
t.Fatal("invalid_map not found in CollectionSpec")
}
m, err := NewMap(ms)
t.Log(err)
if err == nil {
m.Close()
t.Fatal("Creating a Map from a MapSpec with non-zero Extra is expected to fail.")
}
}
func TestLoadInvalidMapMissingSymbol(t *testing.T) {
file := testutils.NativeFile(t, "testdata/invalid_map_static-%s.elf")
_, err := LoadCollectionSpec(file)
t.Log(err)
if err == nil {
t.Fatal("Loading a map with static qualifier should fail")
}
}
func TestLoadInitializedBTFMap(t *testing.T) {
testutils.Files(t, testutils.Glob(t, "testdata/btf_map_init-*.elf"), func(t *testing.T, file string) {
coll, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
t.Run("NewCollection", func(t *testing.T) {
if coll.ByteOrder != internal.NativeEndian {
t.Skipf("Skipping %s collection", coll.ByteOrder)
}
tmp, err := NewCollection(coll)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal("NewCollection failed:", err)
}
tmp.Close()
})
t.Run("prog_array", func(t *testing.T) {
m, ok := coll.Maps["prog_array_init"]
if !ok {
t.Fatal("map prog_array_init not found in program")
}
if len(m.Contents) != 1 {
t.Error("expecting exactly 1 item in MapSpec contents")
}
p := m.Contents[0]
if cmp.Equal(p.Key, 1) {
t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
}
if _, ok := p.Value.(string); !ok {
t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
}
if p.Value != "tail_1" {
t.Errorf("expected MapSpec entry Value 'tail_1', got: %s", p.Value)
}
})
t.Run("array_of_maps", func(t *testing.T) {
m, ok := coll.Maps["outer_map_init"]
if !ok {
t.Fatal("map outer_map_init not found in program")
}
if len(m.Contents) != 1 {
t.Error("expecting exactly 1 item in MapSpec contents")
}
if m.Key == nil {
t.Error("Expected non-nil key")
}
if m.Value == nil {
t.Error("Expected non-nil value")
}
if m.InnerMap.Key == nil {
t.Error("Expected non-nil InnerMap key")
}
if m.InnerMap.Value == nil {
t.Error("Expected non-nil InnerMap value")
}
p := m.Contents[0]
if cmp.Equal(p.Key, 1) {
t.Errorf("expecting MapSpec entry Key to equal 1, got %v", p.Key)
}
if _, ok := p.Value.(string); !ok {
t.Errorf("expecting MapSpec entry Value to be a string, got %T", p.Value)
}
if p.Value != "inner_map" {
t.Errorf("expected MapSpec entry Value 'inner_map', got: %s", p.Value)
}
})
})
}
func TestLoadInvalidInitializedBTFMap(t *testing.T) {
file := testutils.NativeFile(t, "testdata/invalid_btf_map_init-%s.elf")
_, err := LoadCollectionSpec(file)
t.Log(err)
if !errors.Is(err, internal.ErrNotSupported) {
t.Fatal("Loading an initialized BTF map should be unsupported")
}
}
func TestStringSection(t *testing.T) {
file := testutils.NativeFile(t, "testdata/strings-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatalf("load collection spec: %s", err)
}
for name := range spec.Maps {
t.Log(name)
}
strMap := spec.Maps[".rodata.str1.1"]
if strMap == nil {
t.Fatal("Unable to find map '.rodata.str1.1' in loaded collection")
}
if !strMap.Freeze {
t.Fatal("Read only data maps should be frozen")
}
if strMap.Flags != unix.BPF_F_RDONLY_PROG {
t.Fatal("Read only data maps should have the prog-read-only flag set")
}
coll, err := NewCollection(spec)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatalf("new collection: %s", err)
}
defer coll.Close()
prog := coll.Programs["filter"]
if prog == nil {
t.Fatal("program not found")
}
testMap := coll.Maps["my_map"]
if testMap == nil {
t.Fatal("test map not found")
}
_, err = prog.Run(&RunOptions{
Data: internal.EmptyBPFContext, // Min size for XDP programs
})
if err != nil {
t.Fatalf("prog run: %s", err)
}
key := []byte("This string is allocated in the string section\n\x00")
var value uint32
if err = testMap.Lookup(&key, &value); err != nil {
t.Fatalf("test map lookup: %s", err)
}
if value != 1 {
t.Fatal("Test map value not 1!")
}
}
func TestLoadRawTracepoint(t *testing.T) {
testutils.SkipOnOldKernel(t, "4.17", "BPF_RAW_TRACEPOINT API")
file := testutils.NativeFile(t, "testdata/raw_tracepoint-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal("Can't parse ELF:", err)
}
coll, err := NewCollectionWithOptions(spec, CollectionOptions{
Programs: ProgramOptions{
LogLevel: LogLevelBranch,
},
})
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal("Can't create collection:", err)
}
coll.Close()
}
func TestTailCall(t *testing.T) {
file := testutils.NativeFile(t, "testdata/btf_map_init-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
TailMain *Program `ebpf:"tail_main"`
ProgArray *Map `ebpf:"prog_array_init"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.TailMain.Close()
defer obj.ProgArray.Close()
ret, _, err := obj.TailMain.Test(internal.EmptyBPFContext)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
// Expect the tail_1 tail call to be taken, returning value 42.
if ret != 42 {
t.Fatalf("Expected tail call to return value 42, got %d", ret)
}
}
func TestKconfigKernelVersion(t *testing.T) {
file := testutils.NativeFile(t, "testdata/kconfig-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Main *Program `ebpf:"kernel_version"`
}
testutils.SkipOnOldKernel(t, "5.2", "readonly maps")
err = spec.LoadAndAssign(&obj, nil)
if err != nil {
t.Fatal(err)
}
defer obj.Main.Close()
ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
v, err := internal.KernelVersion()
if err != nil {
t.Fatalf("getting kernel version: %s", err)
}
version := v.Kernel()
if ret != version {
t.Fatalf("Expected eBPF to return value %d, got %d", version, ret)
}
}
func TestKconfigSyscallWrapper(t *testing.T) {
file := testutils.NativeFile(t, "testdata/kconfig-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Main *Program `ebpf:"syscall_wrapper"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.Main.Close()
ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
var expected uint32
if testutils.IsKernelLessThan(t, "4.17") {
expected = 0
} else {
expected = 1
}
if ret != expected {
t.Fatalf("Expected eBPF to return value %d, got %d", expected, ret)
}
}
func TestKconfigConfig(t *testing.T) {
file := testutils.NativeFile(t, "testdata/kconfig_config-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Main *Program `ebpf:"kconfig"`
ArrayMap *Map `ebpf:"array_map"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.Main.Close()
defer obj.ArrayMap.Close()
_, _, err = obj.Main.Test(internal.EmptyBPFContext)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
var value uint64
err = obj.ArrayMap.Lookup(uint32(0), &value)
if err != nil {
t.Fatal(err)
}
// CONFIG_HZ must have a value.
qt.Assert(t, qt.Not(qt.Equals(value, 0)))
}
func TestKfunc(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.18", "kfunc support")
file := testutils.NativeFile(t, "testdata/kfunc-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Main *Program `ebpf:"call_kfunc"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatalf("%+v", err)
}
defer obj.Main.Close()
ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
if ret != 1 {
t.Fatalf("Expected kfunc to return value 1, got %d", ret)
}
}
func TestWeakKfunc(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.18", "kfunc support")
file := testutils.NativeFile(t, "testdata/kfunc-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Missing *Program `ebpf:"weak_kfunc_missing"`
Calling *Program `ebpf:"call_weak_kfunc"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatalf("%+v", err)
}
defer obj.Missing.Close()
defer obj.Calling.Close()
}
func TestInvalidKfunc(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.18", "kfunc support")
if !haveTestmod(t) {
t.Skip("bpf_testmod not loaded")
}
file := testutils.NativeFile(t, "testdata/invalid-kfunc-%s.elf")
coll, err := LoadCollection(file)
if err == nil {
coll.Close()
t.Fatal("Expected an error")
}
var ike *incompatibleKfuncError
if !errors.As(err, &ike) {
t.Fatalf("Expected an error wrapping incompatibleKfuncError, got %s", err)
}
}
func TestKfuncKmod(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.18", "Kernel module function calls")
if !haveTestmod(t) {
t.Skip("bpf_testmod not loaded")
}
file := testutils.NativeFile(t, "testdata/kfunc-kmod-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Main *Program `ebpf:"call_kfunc"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatalf("%v+", err)
}
defer obj.Main.Close()
ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
if ret != 1 {
t.Fatalf("Expected kfunc to return value 1, got %d", ret)
}
}
func TestSubprogRelocation(t *testing.T) {
testutils.SkipOnOldKernel(t, "5.13", "bpf_for_each_map_elem")
file := testutils.NativeFile(t, "testdata/subprog_reloc-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
var obj struct {
Main *Program `ebpf:"fp_relocation"`
HashMap *Map `ebpf:"hash_map"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
defer obj.Main.Close()
defer obj.HashMap.Close()
ret, _, err := obj.Main.Test(internal.EmptyBPFContext)
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal(err)
}
if ret != 42 {
t.Fatalf("Expected subprog reloc to return value 42, got %d", ret)
}
}
func TestUnassignedProgArray(t *testing.T) {
file := testutils.NativeFile(t, "testdata/btf_map_init-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal(err)
}
// tail_main references a ProgArray that is not being assigned
// to this struct. Normally, this would clear all its entries
// and make any tail calls into the ProgArray result in a miss.
// The library needs to explicitly refuse such operations.
var obj struct {
TailMain *Program `ebpf:"tail_main"`
// ProgArray *Map `ebpf:"prog_array_init"`
}
err = spec.LoadAndAssign(&obj, nil)
testutils.SkipIfNotSupported(t, err)
if err == nil {
obj.TailMain.Close()
t.Fatal("Expecting LoadAndAssign to return error")
}
}
func TestIPRoute2Compat(t *testing.T) {
file := testutils.NativeFile(t, "testdata/iproute2_map_compat-%s.elf")
spec, err := LoadCollectionSpec(file)
if err != nil {
t.Fatal("Can't parse ELF:", err)
}
ms, ok := spec.Maps["hash_map"]
if !ok {
t.Fatal("Map hash_map not found")
}
var id, pinning, innerID, innerIndex uint32
if ms.Extra == nil {
t.Fatal("missing extra bytes")
}
switch {
case binary.Read(ms.Extra, spec.ByteOrder, &id) != nil:
t.Fatal("missing id")
case binary.Read(ms.Extra, spec.ByteOrder, &pinning) != nil:
t.Fatal("missing pinning")
case binary.Read(ms.Extra, spec.ByteOrder, &innerID) != nil:
t.Fatal("missing inner_id")
case binary.Read(ms.Extra, spec.ByteOrder, &innerIndex) != nil:
t.Fatal("missing inner_idx")
}
if id != 0 || innerID != 0 || innerIndex != 0 {
t.Fatal("expecting id, inner_id and inner_idx to be zero")
}
if pinning != 2 {
t.Fatal("expecting pinning field to be 2 (PIN_GLOBAL_NS)")
}
// iproute2 (tc) pins maps in /sys/fs/bpf/tc/globals with PIN_GLOBAL_NS,
// which needs to be configured in this library using MapOptions.PinPath.
// For the sake of the test, we use a tempdir on bpffs below.
ms.Pinning = PinByName
coll, err := NewCollectionWithOptions(spec, CollectionOptions{
Maps: MapOptions{
PinPath: testutils.TempBPFFS(t),
},
})
testutils.SkipIfNotSupported(t, err)
if err != nil {
t.Fatal("Can't create collection:", err)
}
coll.Close()
}
var (
elfPath = flag.String("elfs", os.Getenv("CI_KERNEL_SELFTESTS"), "`Path` containing libbpf-compatible ELFs (defaults to $CI_KERNEL_SELFTESTS)")
elfPattern = flag.String("elf-pattern", "*.o", "Glob `pattern` for object files that should be tested")
)
func TestLibBPFCompat(t *testing.T) {
if *elfPath == "" {
// Specify the path to the directory containing the eBPF for
// the kernel's selftests if you want to run this test.
// As of 5.2 that is tools/testing/selftests/bpf/
t.Skip("No path specified")
}
load := func(t *testing.T, spec *CollectionSpec, opts CollectionOptions, valid bool) {
// Disable retrying a program load with the log enabled, it leads
// to OOM kills.
opts.Programs.LogDisabled = true
coll, err := NewCollectionWithOptions(spec, opts)
testutils.SkipIfNotSupported(t, err)
var errno syscall.Errno
if errors.As(err, &errno) {
// This error is most likely from a syscall and caused by us not
// replicating some fixups done in the selftests or the test
// intentionally failing. This is expected, so skip the test
// instead of failing.
t.Skip("Skipping since the kernel rejected the program:", err)
}
if err == nil {
coll.Close()
}
if !valid {
if err == nil {
t.Fatal("Expected an error during load")
}
} else if err != nil {
t.Fatal("Error during loading:", err)
}
}
files := testutils.Glob(t, filepath.Join(*elfPath, *elfPattern),
// These files are only used as a source of btf.
"btf__core_reloc_*",
)
testutils.Files(t, files, func(t *testing.T, path string) {
name := selftestName(path)
switch name {
case "test_map_in_map", "test_select_reuseport_kern":
t.Skip("Skipping due to missing InnerMap in map definition")
case "test_core_autosize":
t.Skip("Skipping since the test generates dynamic BTF")
case "test_static_linked":
t.Skip("Skipping since .text contains 'subprog' twice")
case "bloom_filter_map", "bloom_filter_bench":
t.Skip("Skipping due to missing MapExtra field in MapSpec")
case "netif_receive_skb",
"local_kptr_stash",
"local_kptr_stash_fail",
"type_cast",
"preempted_bpf_ma_op",
"percpu_alloc_fail":
// Error message like
// fixup for CORERelocation(local_type_id, Struct:"bin_data"[0],
// local_id=27): invalid immediate 31, expected 27 (fixup: local_type_id=27->1)
// See https://github.com/cilium/ebpf/issues/739
t.Skip("Skipping due to bug in libbpf type deduplication")
case "test_usdt", "test_urandom_usdt", "test_usdt_multispec":
t.Skip("Skipping due to missing support for usdt.bpf.h")
case "lsm_cgroup", "bpf_iter_ipv6_route", "test_core_extern",
"profiler1", "profiler2", "profiler3":
t.Skip("Skipping due to using weak CONFIG_* variables")
case "linked_maps", "linked_maps1", "linked_maps2", "linked_funcs1", "linked_funcs2",
"test_subskeleton", "test_subskeleton_lib":
t.Skip("Skipping due to relying on cross ELF linking")
case "test_log_fixup":
t.Skip("Skipping due to intentionally broken CO-RE relocations")
}
t.Parallel()
spec, err := LoadCollectionSpec(path)
testutils.SkipIfNotSupported(t, err)
if errors.Is(err, errUnsupportedBinding) {
t.Skip(err)
}
if err != nil {
t.Fatal(err)
}
switch name {
case "test_sk_assign":
// Test contains a legacy iproute2 bpf_elf_map definition.
for _, m := range spec.Maps {
if m.Extra == nil || m.Extra.Len() == 0 {
t.Fatalf("Expected extra bytes in map %s", m.Name)
}
m.Extra = nil
}
case "fexit_bpf2bpf",
"freplace_get_constant",
"freplace_global_func":
loadTargetProgram(t, spec, "test_pkt_access.bpf.o", "test_pkt_access")
case "freplace_cls_redirect":
loadTargetProgram(t, spec, "test_cls_redirect.bpf.o", "cls_redirect")
case "test_trace_ext":
loadTargetProgram(t, spec, "test_pkt_md_access.bpf.o", "test_pkt_md_access")
case "freplace_progmap":
loadTargetProgram(t, spec, "xdp_dummy.bpf.o", "xdp_dummy_prog")
if prog := spec.Programs["xdp_cpumap_prog"]; prog.AttachTo == "" {
prog.AttachTo = "xdp_dummy_prog"
}
case "freplace_attach_probe":
// Looks like the test should have a target, but 6.6 selftests don't
// seem to be using it.
}
var opts CollectionOptions
for _, mapSpec := range spec.Maps {
if mapSpec.Pinning != PinNone {
opts.Maps.PinPath = testutils.TempBPFFS(t)
break
}
}
coreFiles := sourceOfBTF(t, path)
if len(coreFiles) == 0 {
// NB: test_core_reloc_kernel.o doesn't have dedicated BTF and
// therefore goes via this code path.
load(t, spec, opts, true)
return
}
for _, coreFile := range coreFiles {
name := selftestName(coreFile)
t.Run(name, func(t *testing.T) {
// Some files like btf__core_reloc_arrays___err_too_small.o
// trigger an error on purpose. Use the name to infer whether
// the test should succeed.
var valid bool
switch name {
case "btf__core_reloc_existence___err_wrong_arr_kind",
"btf__core_reloc_existence___err_wrong_arr_value_type",
"btf__core_reloc_existence___err_wrong_int_kind",
"btf__core_reloc_existence___err_wrong_int_sz",
"btf__core_reloc_existence___err_wrong_int_type",
"btf__core_reloc_existence___err_wrong_struct_type":
// These tests are buggy upstream, see https://lore.kernel.org/bpf/20210420111639.155580-1-lmb@cloudflare.com/
valid = true
case "btf__core_reloc_ints___err_wrong_sz_16",
"btf__core_reloc_ints___err_wrong_sz_32",
"btf__core_reloc_ints___err_wrong_sz_64",
"btf__core_reloc_ints___err_wrong_sz_8",
"btf__core_reloc_arrays___err_wrong_val_type1",
"btf__core_reloc_arrays___err_wrong_val_type2":
// These tests are valid according to current libbpf behaviour,
// see commit 42765ede5c54ca915de5bfeab83be97207e46f68.
valid = true
case "btf__core_reloc_type_id___missing_targets",
"btf__core_reloc_flavors__err_wrong_name":
valid = false
case "btf__core_reloc_ints___err_bitfield":
// Bitfields are now valid.
valid = true
default:
valid = !strings.Contains(name, "___err_")
}
fh, err := os.Open(coreFile)
if err != nil {
t.Fatal(err)
}
defer fh.Close()
btfSpec, err := btf.LoadSpec(coreFile)
if err != nil {
t.Fatal(err)
}
opts := opts // copy
opts.Programs.KernelTypes = btfSpec
load(t, spec, opts, valid)
})
}
})
}
func loadTargetProgram(tb testing.TB, spec *CollectionSpec, file, program string) {
targetSpec, err := LoadCollectionSpec(filepath.Join(*elfPath, file))
if errors.Is(err, os.ErrNotExist) && strings.HasSuffix(file, ".bpf.o") {
// Prior to v6.1 BPF ELF used a plain .o suffix.
file = strings.TrimSuffix(file, ".bpf.o") + ".o"
targetSpec, err = LoadCollectionSpec(filepath.Join(*elfPath, file))
}
if err != nil {
tb.Fatalf("Can't read %s: %s", file, err)
}
qt.Assert(tb, qt.IsNotNil(targetSpec.Programs[program]))
coll, err := NewCollectionWithOptions(targetSpec, CollectionOptions{
Programs: ProgramOptions{LogDisabled: true},
})
if err != nil {
tb.Fatalf("Can't load target: %s", err)
}
tb.Cleanup(func() { coll.Close() })
target := coll.Programs[program]
for _, prog := range spec.Programs {
if prog.Type == Extension && prog.AttachType == AttachNone {
prog.AttachTarget = target
continue
}
if prog.Type == Tracing {
switch prog.AttachType {
case AttachTraceFEntry, AttachTraceFExit, AttachModifyReturn:
prog.AttachTarget = target
continue
}
}
}
}
func sourceOfBTF(tb testing.TB, path string) []string {
const testPrefix = "test_core_reloc_"
const btfPrefix = "btf__core_reloc_"
dir, base := filepath.Split(path)
if !strings.HasPrefix(base, testPrefix) {
return nil
}
base = strings.TrimSuffix(base[len(testPrefix):], ".o")
switch base {
case "bitfields_direct", "bitfields_probed":
base = "bitfields"
}
return testutils.Glob(tb, filepath.Join(dir, btfPrefix+base+"*.o"))
}
func TestELFSectionProgramTypes(t *testing.T) {
type testcase struct {
Section string
ProgramType ProgramType
AttachType AttachType
Flags uint32
Extra string
}
testcases := []testcase{
{"socket", SocketFilter, AttachNone, 0, ""},
{"socket/garbage", SocketFilter, AttachNone, 0, ""},
{"sk_reuseport/migrate", SkReuseport, AttachSkReuseportSelectOrMigrate, 0, ""},
{"sk_reuseport", SkReuseport, AttachSkReuseportSelect, 0, ""},
{"kprobe/", Kprobe, AttachNone, 0, ""},
{"kprobe/func", Kprobe, AttachNone, 0, "func"},
{"uprobe/", Kprobe, AttachNone, 0, ""},
{"kretprobe/", Kprobe, AttachNone, 0, ""},
{"uretprobe/", Kprobe, AttachNone, 0, ""},
{"tc", SchedCLS, AttachNone, 0, ""},
{"classifier", SchedCLS, AttachNone, 0, ""},
{"action", SchedACT, AttachNone, 0, ""},
{"tracepoint/", TracePoint, AttachNone, 0, ""},
{"tp/", TracePoint, AttachNone, 0, ""},
{"raw_tracepoint/", RawTracepoint, AttachNone, 0, ""},
{"raw_tp/", RawTracepoint, AttachNone, 0, ""},
{"raw_tracepoint.w/", RawTracepointWritable, AttachNone, 0, ""},
{"raw_tp.w/", RawTracepointWritable, AttachNone, 0, ""},
{"tp_btf/", Tracing, AttachTraceRawTp, 0, ""},
{"fentry/", Tracing, AttachTraceFEntry, 0, ""},
{"fmod_ret/", Tracing, AttachModifyReturn, 0, ""},
{"fexit/", Tracing, AttachTraceFExit, 0, ""},
{"fentry.s/", Tracing, AttachTraceFEntry, unix.BPF_F_SLEEPABLE, ""},
{"fmod_ret.s/", Tracing, AttachModifyReturn, unix.BPF_F_SLEEPABLE, ""},
{"fexit.s/", Tracing, AttachTraceFExit, unix.BPF_F_SLEEPABLE, ""},
{"freplace/", Extension, AttachNone, 0, ""},
{"lsm/foo", LSM, AttachLSMMac, 0, "foo"},
{"lsm.s/foo", LSM, AttachLSMMac, unix.BPF_F_SLEEPABLE, "foo"},
{"iter/bpf_map", Tracing, AttachTraceIter, 0, "bpf_map"},
{"iter.s/", Tracing, AttachTraceIter, unix.BPF_F_SLEEPABLE, ""},
// Was missing sleepable.
{"syscall", Syscall, AttachNone, unix.BPF_F_SLEEPABLE, ""},
{"xdp.frags_devmap/foo", XDP, AttachXDPDevMap, unix.BPF_F_XDP_HAS_FRAGS, "foo"},
{"xdp_devmap/foo", XDP, AttachXDPDevMap, 0, "foo"},
{"xdp.frags_cpumap/", XDP, AttachXDPCPUMap, unix.BPF_F_XDP_HAS_FRAGS, ""},
{"xdp_cpumap/", XDP, AttachXDPCPUMap, 0, ""},
// Used incorrect attach type.
{"xdp.frags/foo", XDP, AttachXDP, unix.BPF_F_XDP_HAS_FRAGS, ""},
{"xdp/foo", XDP, AttachNone, 0, ""},
{"perf_event", PerfEvent, AttachNone, 0, ""},
{"lwt_in", LWTIn, AttachNone, 0, ""},
{"lwt_out", LWTOut, AttachNone, 0, ""},
{"lwt_xmit", LWTXmit, AttachNone, 0, ""},
{"lwt_seg6local", LWTSeg6Local, AttachNone, 0, ""},
{"cgroup_skb/ingress", CGroupSKB, AttachCGroupInetIngress, 0, ""},
{"cgroup_skb/egress", CGroupSKB, AttachCGroupInetEgress, 0, ""},
{"cgroup/skb", CGroupSKB, AttachNone, 0, ""},
{"cgroup/sock_create", CGroupSock, AttachCGroupInetSockCreate, 0, ""},
{"cgroup/sock_release", CGroupSock, AttachCgroupInetSockRelease, 0, ""},
{"cgroup/sock", CGroupSock, AttachCGroupInetSockCreate, 0, ""},
{"cgroup/post_bind4", CGroupSock, AttachCGroupInet4PostBind, 0, ""},
{"cgroup/post_bind6", CGroupSock, AttachCGroupInet6PostBind, 0, ""},
{"cgroup/dev", CGroupDevice, AttachCGroupDevice, 0, ""},
{"sockops", SockOps, AttachCGroupSockOps, 0, ""},
{"sk_skb/stream_parser", SkSKB, AttachSkSKBStreamParser, 0, ""},
{"sk_skb/stream_verdict", SkSKB, AttachSkSKBStreamVerdict, 0, ""},
{"sk_skb/stream_verdict/foo", SkSKB, AttachSkSKBStreamVerdict, 0, ""},
{"sk_skb", SkSKB, AttachNone, 0, ""},
{"sk_skb/bar", SkSKB, AttachNone, 0, ""},
{"sk_msg", SkMsg, AttachSkMsgVerdict, 0, ""},
{"lirc_mode2", LircMode2, AttachLircMode2, 0, ""},
{"flow_dissector", FlowDissector, AttachFlowDissector, 0, ""},
{"cgroup/bind4", CGroupSockAddr, AttachCGroupInet4Bind, 0, ""},
{"cgroup/bind6", CGroupSockAddr, AttachCGroupInet6Bind, 0, ""},
{"cgroup/connect4", CGroupSockAddr, AttachCGroupInet4Connect, 0, ""},
{"cgroup/connect6", CGroupSockAddr, AttachCGroupInet6Connect, 0, ""},
{"cgroup/sendmsg4", CGroupSockAddr, AttachCGroupUDP4Sendmsg, 0, ""},
{"cgroup/sendmsg6", CGroupSockAddr, AttachCGroupUDP6Sendmsg, 0, ""},
{"cgroup/recvmsg4", CGroupSockAddr, AttachCGroupUDP4Recvmsg, 0, ""},
{"cgroup/recvmsg6", CGroupSockAddr, AttachCGroupUDP6Recvmsg, 0, ""},
{"cgroup/getpeername4", CGroupSockAddr, AttachCgroupInet4GetPeername, 0, ""},
{"cgroup/getpeername6", CGroupSockAddr, AttachCgroupInet6GetPeername, 0, ""},
{"cgroup/getsockname4", CGroupSockAddr, AttachCgroupInet4GetSockname, 0, ""},
{"cgroup/getsockname6", CGroupSockAddr, AttachCgroupInet6GetSockname, 0, ""},
{"cgroup/sysctl", CGroupSysctl, AttachCGroupSysctl, 0, ""},
{"cgroup/getsockopt", CGroupSockopt, AttachCGroupGetsockopt, 0, ""},
{"cgroup/setsockopt", CGroupSockopt, AttachCGroupSetsockopt, 0, ""},
// Bogus pattern means it never matched anything.
// {"struct_ops+", StructOps, AttachNone, 0, ""},
{"sk_lookup/", SkLookup, AttachSkLookup, 0, ""},
{"seccomp", SocketFilter, AttachNone, 0, ""},
{"kprobe.multi", Kprobe, AttachTraceKprobeMulti, 0, ""},
{"kretprobe.multi", Kprobe, AttachTraceKprobeMulti, 0, ""},
}
for _, tc := range testcases {
t.Run(tc.Section, func(t *testing.T) {
pt, at, fl, extra := getProgType(tc.Section)
have := testcase{tc.Section, pt, at, fl, extra}
qt.Assert(t, qt.DeepEquals(have, tc))
})
}
}
func TestMatchSectionName(t *testing.T) {
for _, testcase := range []struct {
pattern string
input string
matches bool
extra string
}{
{"prefix/", "prefix/", true, ""},
{"prefix/", "prefix/a", true, "a"},
{"prefix/", "prefix/b", true, "b"},
{"prefix/", "prefix", false, ""},
{"prefix/", "junk", false, ""},
{"prefix+", "prefix/", true, ""},
{"prefix+", "prefix/a", true, "a"},
{"prefix+", "prefix/b", true, "b"},
{"prefix+", "prefix", true, ""},
{"prefix+", "junk", false, ""},
{"exact", "exact", true, ""},
{"exact", "exact/", true, ""},
{"exact", "exact/a", true, ""},
{"exact", "exactement", true, ""},
{"exact", "junk", false, ""},
} {
name := fmt.Sprintf("%s:%s", testcase.pattern, testcase.input)
t.Run(name, func(t *testing.T) {
extra, matches := matchSectionName(testcase.input, testcase.pattern)
qt.Assert(t, qt.Equals(matches, testcase.matches))
if testcase.matches {
qt.Assert(t, qt.Equals(extra, testcase.extra))
}
})
}
}
// selftestName takes a path to a file and derives a canonical name from it.
//
// It strips various suffixes used by the selftest build system.
func selftestName(path string) string {
file := filepath.Base(path)
name := strings.TrimSuffix(file, ".o")
// Strip various suffixes.
// Various linking suffixes.
name = strings.TrimSuffix(name, ".linked3")
name = strings.TrimSuffix(name, ".llinked1")
name = strings.TrimSuffix(name, ".llinked2")
name = strings.TrimSuffix(name, ".llinked3")
// v6.1 started adding .bpf to all BPF ELF.
name = strings.TrimSuffix(name, ".bpf")
return name
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/amos49/cilium_ebpf.git
git@gitee.com:amos49/cilium_ebpf.git
amos49
cilium_ebpf
cilium_ebpf
main

搜索帮助

0d507c66 1850385 C8b1a773 1850385