Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

btf: replace decl and type tags with placeholders on unsupported kernels #1615

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions btf/btf_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
kindFloat // Float
// Added 5.16
kindDeclTag // DeclTag
// Added 5.17
kindTypeTag // TypeTag
// Added 6.0
kindEnum64 // Enum64
Expand Down
35 changes: 35 additions & 0 deletions btf/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,41 @@ var haveFuncLinkage = internal.NewFeatureTest("BTF func linkage", func() error {
return err
}, "5.6")

var haveDeclTags = internal.NewFeatureTest("BTF decl tags", func() error {
if err := haveBTF(); err != nil {
return err
}

t := &Typedef{
Name: "a",
Type: &Int{},
Tags: []string{"a"},
}

err := probeBTF(t)
if errors.Is(err, unix.EINVAL) {
return internal.ErrNotSupported
}
return err
}, "5.16")

var haveTypeTags = internal.NewFeatureTest("BTF type tags", func() error {
if err := haveBTF(); err != nil {
return err
}

t := &TypeTag{
Type: &Int{},
Value: "a",
}

err := probeBTF(t)
if errors.Is(err, unix.EINVAL) {
return internal.ErrNotSupported
}
return err
}, "5.17")

var haveEnum64 = internal.NewFeatureTest("ENUM64", func() error {
if err := haveBTF(); err != nil {
return err
Expand Down
8 changes: 8 additions & 0 deletions btf/feature_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ func TestHaveFuncLinkage(t *testing.T) {
testutils.CheckFeatureTest(t, haveFuncLinkage)
}

func TestHaveDeclTags(t *testing.T) {
testutils.CheckFeatureTest(t, haveDeclTags)
}

func TestHaveTypeTags(t *testing.T) {
testutils.CheckFeatureTest(t, haveTypeTags)
}

func TestHaveEnum64(t *testing.T) {
testutils.CheckFeatureTest(t, haveEnum64)
}
79 changes: 61 additions & 18 deletions btf/marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ type MarshalOptions struct {
Order binary.ByteOrder
// Remove function linkage information for compatibility with <5.6 kernels.
StripFuncLinkage bool
// Replace decl tags with a placeholder for compatibility with <5.16 kernels.
ReplaceDeclTags bool
// Replace TypeTags with a placeholder for compatibility with <5.17 kernels.
ReplaceTypeTags bool
// Replace Enum64 with a placeholder for compatibility with <6.0 kernels.
ReplaceEnum64 bool
// Prevent the "No type found" error when loading BTF without any types.
Expand All @@ -29,6 +33,8 @@ func KernelMarshalOptions() *MarshalOptions {
return &MarshalOptions{
Order: internal.NativeEndian,
StripFuncLinkage: haveFuncLinkage() != nil,
ReplaceDeclTags: haveDeclTags() != nil,
ReplaceTypeTags: haveTypeTags() != nil,
ReplaceEnum64: haveEnum64() != nil,
PreventNoTypeFound: true, // All current kernels require this.
}
Expand Down Expand Up @@ -318,15 +324,7 @@ func (e *encoder) deflateType(typ Type) (err error) {
return errors.New("Void is implicit in BTF wire format")

case *Int:
raw.SetKind(kindInt)
raw.SetSize(v.Size)

var bi btfInt
bi.SetEncoding(v.Encoding)
// We need to set bits in addition to size, since btf_type_int_is_regular
// otherwise flags this as a bitfield.
bi.SetBits(byte(v.Size) * 8)
raw.data = bi
e.deflateInt(&raw, v)

case *Pointer:
raw.SetKind(kindPointer)
Expand Down Expand Up @@ -368,8 +366,7 @@ func (e *encoder) deflateType(typ Type) (err error) {
raw.SetType(e.id(v.Type))

case *Const:
raw.SetKind(kindConst)
raw.SetType(e.id(v.Type))
e.deflateConst(&raw, v)

case *Restrict:
raw.SetKind(kindRestrict)
Expand Down Expand Up @@ -404,15 +401,10 @@ func (e *encoder) deflateType(typ Type) (err error) {
raw.SetSize(v.Size)

case *declTag:
raw.SetKind(kindDeclTag)
raw.SetType(e.id(v.Type))
raw.data = &btfDeclTag{uint32(v.Index)}
raw.NameOff, err = e.strings.Add(v.Value)
err = e.deflateDeclTag(&raw, v)

case *TypeTag:
raw.SetKind(kindTypeTag)
raw.SetType(e.id(v.Type))
raw.NameOff, err = e.strings.Add(v.Value)
err = e.deflateTypeTag(&raw, v)

default:
return fmt.Errorf("don't know how to deflate %T", v)
Expand All @@ -425,6 +417,57 @@ func (e *encoder) deflateType(typ Type) (err error) {
return raw.Marshal(e.buf, e.Order)
}

func (e *encoder) deflateInt(raw *rawType, i *Int) {
raw.SetKind(kindInt)
raw.SetSize(i.Size)

var bi btfInt
bi.SetEncoding(i.Encoding)
// We need to set bits in addition to size, since btf_type_int_is_regular
// otherwise flags this as a bitfield.
bi.SetBits(byte(i.Size) * 8)
raw.data = bi
}

func (e *encoder) deflateDeclTag(raw *rawType, tag *declTag) (err error) {
// Replace a decl tag with an integer for compatibility with <5.16 kernels,
// following libbpf behaviour.
if e.ReplaceDeclTags {
typ := &Int{"decl_tag_placeholder", 1, Unsigned}
e.deflateInt(raw, typ)

// Add the placeholder type name to the string table. The encoder added the
// original type name before this call.
raw.NameOff, err = e.strings.Add(typ.TypeName())
return
}

raw.SetKind(kindDeclTag)
raw.SetType(e.id(tag.Type))
raw.data = &btfDeclTag{uint32(tag.Index)}
raw.NameOff, err = e.strings.Add(tag.Value)
return
}

func (e *encoder) deflateConst(raw *rawType, c *Const) {
raw.SetKind(kindConst)
raw.SetType(e.id(c.Type))
}

func (e *encoder) deflateTypeTag(raw *rawType, tag *TypeTag) (err error) {
// Replace a type tag with a const qualifier for compatibility with <5.17
// kernels, following libbpf behaviour.
if e.ReplaceTypeTags {
e.deflateConst(raw, &Const{tag.Type})
return
}

raw.SetKind(kindTypeTag)
raw.SetType(e.id(tag.Type))
raw.NameOff, err = e.strings.Add(tag.Value)
return
}

func (e *encoder) deflateUnion(raw *rawType, union *Union) (err error) {
raw.SetKind(kindUnion)
raw.SetSize(union.Size)
Expand Down
60 changes: 60 additions & 0 deletions btf/marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,66 @@ func TestMarshalEnum64(t *testing.T) {
}))
}

func TestMarshalDeclTags(t *testing.T) {
types := []Type{
// Instead of an adjacent declTag, this will receive a placeholder Int.
&Typedef{
Name: "decl tag typedef",
Tags: []string{"decl tag"},
Type: &Int{Name: "decl tag target"},
},
}

b, err := NewBuilder(types)
qt.Assert(t, qt.IsNil(err))
buf, err := b.Marshal(nil, &MarshalOptions{
Order: internal.NativeEndian,
ReplaceDeclTags: true,
})
qt.Assert(t, qt.IsNil(err))

spec, err := loadRawSpec(bytes.NewReader(buf), internal.NativeEndian, nil)
qt.Assert(t, qt.IsNil(err))

var td *Typedef
qt.Assert(t, qt.IsNil(spec.TypeByName("decl tag typedef", &td)))
var ti *Int
qt.Assert(t, qt.IsNil(spec.TypeByName("decl_tag_placeholder", &ti)))
}

func TestMarshalTypeTags(t *testing.T) {
types := []Type{
// Instead of pointing to a TypeTag, this will point to an intermediary Const.
&Typedef{
Name: "type tag typedef",
Type: &TypeTag{
Value: "type tag",
Type: &Pointer{
Target: &Int{Name: "type tag target"},
},
},
},
}

b, err := NewBuilder(types)
qt.Assert(t, qt.IsNil(err))
buf, err := b.Marshal(nil, &MarshalOptions{
Order: internal.NativeEndian,
ReplaceTypeTags: true,
})
qt.Assert(t, qt.IsNil(err))

spec, err := loadRawSpec(bytes.NewReader(buf), internal.NativeEndian, nil)
qt.Assert(t, qt.IsNil(err))

var td *Typedef
qt.Assert(t, qt.IsNil(spec.TypeByName("type tag typedef", &td)))
qt.Assert(t, qt.Satisfies(td.Type, func(typ Type) bool {
_, ok := typ.(*Const)
return ok
}))
}

func BenchmarkMarshaler(b *testing.B) {
types := typesFromSpec(vmlinuxTestdataSpec(b))[:100]

Expand Down