Skip to content

Commit 3aca741

Browse files
committed
btf: add a workaround for the "No type found" error
The kernel accepts line info for each instruction in a program, which is output as part of the verifier log and therefore provides very important context. The strings for each line info are interned into a BTF blob. Unfortunately, the kernel refuses to load BTF with a string table but without any types. This means it's not possible to add line info to a program which doesn't use BTF otherwise. Add a MarshalOption which fixes this behaviour by adding a dummy type if necessary. Signed-off-by: Lorenz Bauer <[email protected]>
1 parent ac52a2b commit 3aca741

3 files changed

Lines changed: 41 additions & 8 deletions

File tree

btf/btf_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,9 @@ func TestLoadSpecFromElf(t *testing.T) {
308308
}
309309

310310
func TestVerifierError(t *testing.T) {
311-
_, err := NewHandle(&Builder{})
311+
b, err := NewBuilder([]Type{&Int{Encoding: 255}})
312+
qt.Assert(t, qt.IsNil(err))
313+
_, err = NewHandle(b)
312314
testutils.SkipIfNotSupported(t, err)
313315
var ve *internal.VerifierError
314316
if !errors.As(err, &ve) {

btf/marshal.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,17 @@ type MarshalOptions struct {
2020
StripFuncLinkage bool
2121
// Replace Enum64 with a placeholder for compatibility with <6.0 kernels.
2222
ReplaceEnum64 bool
23+
// Prevent the "No type found" error when loading BTF without any types.
24+
PreventNoTypeFound bool
2325
}
2426

2527
// KernelMarshalOptions will generate BTF suitable for the current kernel.
2628
func KernelMarshalOptions() *MarshalOptions {
2729
return &MarshalOptions{
28-
Order: internal.NativeEndian,
29-
StripFuncLinkage: haveFuncLinkage() != nil,
30-
ReplaceEnum64: haveEnum64() != nil,
30+
Order: internal.NativeEndian,
31+
StripFuncLinkage: haveFuncLinkage() != nil,
32+
ReplaceEnum64: haveEnum64() != nil,
33+
PreventNoTypeFound: true, // All current kernels require this.
3134
}
3235
}
3336

@@ -94,9 +97,9 @@ func NewBuilder(types []Type) (*Builder, error) {
9497
return b, nil
9598
}
9699

97-
// Empty returns true if [Add] has not been invoked on the builder.
100+
// Empty returns true if neither types nor strings have been added.
98101
func (b *Builder) Empty() bool {
99-
return len(b.types) == 0
102+
return len(b.types) == 0 && (b.strings == nil || b.strings.Length() == 0)
100103
}
101104

102105
// Add a Type and allocate a stable ID for it.
@@ -169,10 +172,24 @@ func (b *Builder) Marshal(buf []byte, opts *MarshalOptions) ([]byte, error) {
169172
ids: maps.Clone(b.stableIDs),
170173
}
171174

175+
if e.ids == nil {
176+
e.ids = make(map[Type]TypeID)
177+
}
178+
179+
types := b.types
180+
if len(types) == 0 && stb.Length() > 0 && opts.PreventNoTypeFound {
181+
// We have strings that need to be written out,
182+
// but no types (besides the implicit Void).
183+
// Kernels as recent as v6.7 refuse to load such BTF
184+
// with a "No type found" error in the log.
185+
// Fix this by adding a dummy type.
186+
types = []Type{&Int{Size: 0}}
187+
}
188+
172189
// Ensure that types are marshaled in the exact order they were Add()ed.
173190
// Otherwise the ID returned from Add() won't match.
174-
e.pending.Grow(len(b.types))
175-
for _, typ := range b.types {
191+
e.pending.Grow(len(types))
192+
for _, typ := range types {
176193
e.pending.Push(typ)
177194
}
178195

btf/workarounds_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77

88
"github.com/cilium/ebpf/internal"
99
"github.com/cilium/ebpf/internal/testutils"
10+
11+
"github.com/go-quicktest/qt"
1012
)
1113

1214
func TestDatasecResolveWorkaround(t *testing.T) {
@@ -64,3 +66,15 @@ func TestDatasecResolveWorkaround(t *testing.T) {
6466
})
6567
}
6668
}
69+
70+
func TestEmptyBTFWithStringTableWorkaround(t *testing.T) {
71+
var b Builder
72+
73+
_, err := b.addString("foo")
74+
qt.Assert(t, qt.IsNil(err))
75+
76+
h, err := NewHandle(&b)
77+
testutils.SkipIfNotSupported(t, err)
78+
qt.Assert(t, qt.IsNil(err))
79+
qt.Assert(t, qt.IsNil(h.Close()))
80+
}

0 commit comments

Comments
 (0)