Skip to content

Commit 0131640

Browse files
btf,linker: Apply ignore suffix rule to kfuncs
The ultimate goal of this change is to apply the ignore suffix rule to kfuncs. This allows a user to define multiple flavors of kfuncs as they may appear in different kernel versions, without name collisions. During kfunc relocation, we strip the ___... suffix before looking up kfunc candidates in the vmlinux BTF. This required changing the behavior of btf.Spec.AnyTypesByName, which would internally call newEssentialName on the provided name to lookup types but then still apply name filtering on the name with the suffix. By moving the suffix stripping out of the btf.Spec method responsibility the caller can decide if they want to find exact matches or if they want to apply the ignore suffix rule. This also surfaced a test with a wrong assumption. We were asserting that if the kernel contained a `ambiguous` and `ambiguous___flavor` type that we should match both during relocation. But this is not how the ignore suffix rule works, suffixes are only stripped off of user provided type names (those in ELF BTF), the logic should not be applied to kernel types. So removed tests with the bad assumptions and added a new test to assert the correct ambiguous relocation behavior. Signed-off-by: Dylan Reimerink <dylan.reimerink@isovalent.com>
1 parent b51a64f commit 0131640

9 files changed

Lines changed: 86 additions & 104 deletions

File tree

btf/btf.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ func (s *Spec) TypeID(typ Type) (TypeID, error) {
408408
//
409409
// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.
410410
func (s *Spec) AnyTypesByName(name string) ([]Type, error) {
411-
types, err := s.d.typesByName(newEssentialName(name))
411+
types, err := s.d.typesByName(name)
412412
if err != nil {
413413
return nil, err
414414
}

btf/btf_test.go

Lines changed: 2 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -90,78 +90,13 @@ func TestAnyTypesByName(t *testing.T) {
9090
testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) {
9191
spec := parseELFBTF(t, file)
9292

93-
types, err := spec.AnyTypesByName("ambiguous")
93+
types, err := spec.AnyTypesByName("u")
9494
if err != nil {
9595
t.Fatal(err)
9696
}
9797

9898
if len(types) != 1 {
99-
t.Fatalf("expected to receive exactly 1 types from querying ambiguous type, got: %v", types)
100-
}
101-
102-
types, err = spec.AnyTypesByName("ambiguous___flavour")
103-
if err != nil {
104-
t.Fatal(err)
105-
}
106-
107-
if len(types) != 1 {
108-
t.Fatalf("expected to receive exactly 1 type from querying ambiguous flavour, got: %v", types)
109-
}
110-
})
111-
}
112-
113-
func TestAnyTypesByNameExactMatch(t *testing.T) {
114-
spec := specFromTypes(t, []Type{
115-
&Int{Name: "foo___one", Size: 4},
116-
&Int{Name: "foo___two", Size: 4},
117-
&Int{Name: "foo", Size: 4},
118-
})
119-
120-
types, err := spec.AnyTypesByName("foo")
121-
qt.Assert(t, qt.IsNil(err))
122-
qt.Assert(t, qt.HasLen(types, 1))
123-
qt.Assert(t, qt.Equals(types[0].TypeName(), "foo"))
124-
}
125-
126-
func TestAnyTypesByNameNoExactMatch(t *testing.T) {
127-
spec := specFromTypes(t, []Type{
128-
&Int{Name: "foo___flavour", Size: 4},
129-
})
130-
131-
types, err := spec.AnyTypesByName("foo")
132-
qt.Assert(t, qt.ErrorIs(err, ErrNotFound))
133-
qt.Assert(t, qt.IsNil(types))
134-
}
135-
136-
func TestAnyTypeByNameNoExactMatch(t *testing.T) {
137-
spec := specFromTypes(t, []Type{
138-
&Int{Name: "foo___flavour", Size: 4},
139-
})
140-
141-
typ, err := spec.AnyTypeByName("foo")
142-
qt.Assert(t, qt.ErrorIs(err, ErrNotFound))
143-
qt.Assert(t, qt.IsNil(typ))
144-
}
145-
146-
func TestTypeByNameAmbiguous(t *testing.T) {
147-
testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) {
148-
spec := parseELFBTF(t, file)
149-
150-
var typ *Struct
151-
if err := spec.TypeByName("ambiguous", &typ); err != nil {
152-
t.Fatal(err)
153-
}
154-
155-
if name := typ.TypeName(); name != "ambiguous" {
156-
t.Fatal("expected type name 'ambiguous', got:", name)
157-
}
158-
159-
if err := spec.TypeByName("ambiguous___flavour", &typ); err != nil {
160-
t.Fatal(err)
161-
}
162-
163-
if name := typ.TypeName(); name != "ambiguous___flavour" {
164-
t.Fatal("expected type name 'ambiguous___flavour', got:", name)
99+
t.Fatalf("expected to receive exactly 1 types from querying type, got: %v", types)
165100
}
166101
})
167102
}

btf/core.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/cilium/ebpf/asm"
1313
"github.com/cilium/ebpf/internal"
1414
"github.com/cilium/ebpf/internal/platform"
15+
"github.com/davecgh/go-spew/spew"
1516
)
1617

1718
// Code in this file is derived from libbpf, which is available under a BSD
@@ -264,7 +265,7 @@ func CORERelocate(relos []*CORERelocation, targets []*Spec, bo binary.ByteOrder,
264265

265266
var targetTypes []Type
266267
for _, target := range targets {
267-
namedTypes, err := target.d.typesByName(essentialName)
268+
namedTypes, err := target.d.typesByName(string(essentialName))
268269
if errors.Is(err, ErrNotFound) {
269270
continue
270271
} else if err != nil {
@@ -458,6 +459,7 @@ func coreCalculateFixup(relo *CORERelocation, target Type, bo binary.ByteOrder,
458459
}
459460

460461
localField, targetField, err := coreFindField(local, relo.accessor, target)
462+
spew.Dump(localField, targetField, err)
461463
if errors.Is(err, errImpossibleRelocation) {
462464
return poison()
463465
}

btf/core_test.go

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -545,11 +545,6 @@ func TestCORERelocation(t *testing.T) {
545545
t.Skip("No ext_infos")
546546
}
547547

548-
errs := map[string]error{
549-
"err_ambiguous": errAmbiguousRelocation,
550-
"err_ambiguous_flavour": errAmbiguousRelocation,
551-
}
552-
553548
for section := range extInfos.Funcs {
554549
name := strings.TrimPrefix(section, "socket/")
555550
t.Run(name, func(t *testing.T) {
@@ -559,13 +554,6 @@ func TestCORERelocation(t *testing.T) {
559554
}
560555

561556
fixups, err := CORERelocate(relos, []*Spec{spec}, spec.byteOrder(), spec.TypeID)
562-
if want := errs[name]; want != nil {
563-
if !errors.Is(err, want) {
564-
t.Fatal("Expected", want, "got", err)
565-
}
566-
return
567-
}
568-
569557
if err != nil {
570558
t.Fatal("Can't relocate against itself:", err)
571559
}
@@ -582,6 +570,60 @@ func TestCORERelocation(t *testing.T) {
582570
})
583571
}
584572

573+
// TestCORERelocationAmbiguous test that when the kernel contains multiple types with the same name but of different
574+
// layout, and the relocation result would return different values for these types, we throw a errAmbiguousRelocation
575+
// error.
576+
func TestCORERelocationAmbiguous(t *testing.T) {
577+
b, err := NewBuilder(
578+
[]Type{
579+
&Struct{
580+
Name: "ambiguous",
581+
Members: []Member{
582+
{Name: "a", Type: &Int{Size: 4}},
583+
{Name: "b", Type: &Int{Size: 4}},
584+
{Name: "c", Type: &Int{Size: 4}},
585+
},
586+
},
587+
&Struct{
588+
Name: "ambiguous",
589+
Members: []Member{
590+
{Name: "a", Type: &Int{Size: 4}},
591+
{Name: "c", Type: &Int{Size: 4}},
592+
{Name: "b", Type: &Int{Size: 4}},
593+
},
594+
},
595+
},
596+
nil,
597+
)
598+
if err != nil {
599+
t.Fatal(err)
600+
}
601+
602+
fakeVmlinux, err := b.Spec()
603+
if err != nil {
604+
t.Fatal(err)
605+
}
606+
607+
relos := []*CORERelocation{
608+
{
609+
typ: &Struct{
610+
Name: "ambiguous",
611+
Members: []Member{
612+
{Name: "a", Type: &Int{Size: 4}},
613+
{Name: "b", Type: &Int{Size: 4}},
614+
},
615+
},
616+
kind: reloTypeIDTarget,
617+
accessor: coreAccessor{0},
618+
},
619+
}
620+
621+
_, err = CORERelocate(relos, []*Spec{fakeVmlinux}, fakeVmlinux.byteOrder(), fakeVmlinux.TypeID)
622+
if !errors.Is(err, errAmbiguousRelocation) {
623+
t.Fatalf("Expected errAmbiguousRelocation, got %s", err)
624+
}
625+
}
626+
585627
func TestCOREReloFieldSigned(t *testing.T) {
586628
for _, typ := range []Type{&Int{}, &Enum{}} {
587629
t.Run(fmt.Sprintf("%T with invalid target", typ), func(t *testing.T) {

btf/testdata/relocs-eb.elf

-888 Bytes
Binary file not shown.

btf/testdata/relocs-el.elf

-888 Bytes
Binary file not shown.

btf/testdata/relocs.c

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -271,21 +271,3 @@ __section("socket/fields") int fields() {
271271

272272
return 0;
273273
}
274-
275-
struct ambiguous {
276-
int _1;
277-
char _2;
278-
};
279-
280-
struct ambiguous___flavour {
281-
char _1;
282-
int _2;
283-
};
284-
285-
__section("socket/err_ambiguous") int err_ambiguous() {
286-
return bpf_core_type_id_kernel(struct ambiguous);
287-
}
288-
289-
__section("socket/err_ambiguous_flavour") int err_ambiguous_flavour() {
290-
return bpf_core_type_id_kernel(struct ambiguous___flavour);
291-
}

btf/unmarshal.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,18 +233,18 @@ func (d *decoder) typeID(typ Type) (TypeID, error) {
233233
return id, nil
234234
}
235235

236-
// typesByName returns all types which have the given essential name.
236+
// TypesByName returns all types which have the given name.
237237
//
238238
// Returns ErrNotFound if no matching Type exists.
239-
func (d *decoder) typesByName(name essentialName) ([]Type, error) {
239+
func (d *decoder) typesByName(name string) ([]Type, error) {
240240
var types []Type
241241
for id := range d.namedTypes.Find(string(name)) {
242242
typ, err := d.typeByID(id)
243243
if err != nil {
244244
return nil, err
245245
}
246246

247-
if newEssentialName(typ.TypeName()) == name {
247+
if typ.TypeName() == name {
248248
// Deal with hash collisions by checking against the name.
249249
types = append(types, typ)
250250
}

linker.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ fixups:
308308
// findTargetInKernel returns [btf.ErrNotFound] if the target can't be found
309309
// or if BTF is not enabled.
310310
target := btf.Type((*btf.Func)(nil))
311-
spec, module, err := findTargetInKernel(kfm.Func.Name, &target, cache)
311+
spec, module, err := findTargetInKernel(essentialName(kfm.Func.Name), &target, cache)
312312
if errors.Is(err, btf.ErrNotFound) {
313313
if kfm.Binding == elf.STB_WEAK {
314314
if ins.IsKfuncCall() {
@@ -344,6 +344,11 @@ fixups:
344344
}
345345

346346
if err := btf.CheckTypeCompatibility(kfm.Func.Type, target.(*btf.Func).Type); err != nil {
347+
if kfm.Binding == elf.STB_WEAK {
348+
iter.Next()
349+
continue
350+
}
351+
347352
return nil, &incompatibleKfuncError{kfm.Func.Name, err}
348353
}
349354

@@ -569,7 +574,7 @@ func applyTypedKsymFixups(fixups []ksymFixup, cache *btf.Cache) (modules handles
569574
sym, ok := symbols[varName]
570575
if !ok {
571576
var target *btf.Var
572-
spec, module, err := findTargetInKernel(varName, &target, cache)
577+
spec, module, err := findTargetInKernel(essentialName(varName), &target, cache)
573578
if errors.Is(err, btf.ErrNotFound) && fixup.Binding == elf.STB_WEAK {
574579
continue
575580
}
@@ -602,3 +607,19 @@ func applyTypedKsymFixups(fixups []ksymFixup, cache *btf.Cache) (modules handles
602607

603608
return modules, nil
604609
}
610+
611+
// essentialName returns name without a ___ suffix.
612+
//
613+
// When resolving types in the kernels BTF, the "ignore suffix" rule is applied where we strip the ___ suffix from
614+
// a name before we search for in in the kernel BTF. This allows multiple flavors of a type to be defined in a
615+
// program without name collisions, yet attempt to resolve multiple types.
616+
func essentialName(name string) string {
617+
if name == "" {
618+
return ""
619+
}
620+
lastIdx := strings.LastIndex(name, "___")
621+
if lastIdx > 0 {
622+
return name[:lastIdx]
623+
}
624+
return name
625+
}

0 commit comments

Comments
 (0)