Skip to content

Commit

Permalink
btf: automatically use KernelSpec as base in Handle.Spec
Browse files Browse the repository at this point in the history
We currently punt on the user to provide us with a base spec to
parse kmod BTF via a Handle. This isn't very ergonomic, and also unnecessary.

If kmod / split BTF is present, we can also rely on /sys/... being available.
We can therefore automatically use the (cached) kernel types when retrieving
a Spec from a Handle.
  • Loading branch information
lmb committed Nov 23, 2022
1 parent 55ec55c commit 7b3aa3b
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 30 deletions.
28 changes: 17 additions & 11 deletions btf/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type Handle struct {

// Size of the raw BTF in bytes.
size uint32

needsKernelBase bool
}

// NewHandle loads BTF into the kernel.
Expand Down Expand Up @@ -57,7 +59,7 @@ func newHandleFromRawBTF(btf []byte) (*Handle, error) {

fd, err := sys.BtfLoad(attr)
if err == nil {
return &Handle{fd, attr.BtfSize}, nil
return &Handle{fd, attr.BtfSize, false}, nil
}

if err := haveBTF(); err != nil {
Expand Down Expand Up @@ -98,13 +100,11 @@ func NewHandleFromID(id ID) (*Handle, error) {
return nil, err
}

return &Handle{fd, info.size}, nil
return &Handle{fd, info.size, info.IsModule()}, nil
}

// Spec parses the kernel BTF into Go types.
//
// base is used to decode split BTF and may be nil.
func (h *Handle) Spec(base *Spec) (*Spec, error) {
func (h *Handle) Spec() (*Spec, error) {
var btfInfo sys.BtfInfo
btfBuffer := make([]byte, h.size)
btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer)
Expand All @@ -113,14 +113,20 @@ func (h *Handle) Spec(base *Spec) (*Spec, error) {
return nil, err
}

var baseTypes types
var baseStrings *stringTable
if base != nil {
baseTypes = base.types
baseStrings = base.strings
if !h.needsKernelBase {
return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil, nil)
}

base, fallback, err := kernelSpec()
if err != nil {
return nil, fmt.Errorf("load BTF base: %w", err)
}

if fallback {
return nil, fmt.Errorf("can't load split BTF without access to /sys")
}

return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, baseTypes, baseStrings)
return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, base.types, base.strings)
}

// Close destroys the handle.
Expand Down
12 changes: 1 addition & 11 deletions btf/handle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,10 @@ func TestParseModuleSplitSpec(t *testing.T) {
}
defer vmlinux.Close()

vmlinuxSpec, err := vmlinux.Spec(nil)
if err != nil {
t.Fatal("Parse vmlinux BTF:", err)
}

_, err = module.Spec(vmlinuxSpec)
_, err = module.Spec()
if err != nil {
t.Fatal("Parse module BTF:", err)
}

_, err = module.Spec(nil)
if err == nil {
t.Fatal("Parsing module BTF without vmlinux base didn't fail")
}
}

func ExampleHandleIterator() {
Expand Down
2 changes: 1 addition & 1 deletion link/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (
}
defer btfHandle.Close()

spec, err := btfHandle.Spec(nil)
spec, err := btfHandle.Spec()
if err != nil {
return nil, err
}
Expand Down
11 changes: 4 additions & 7 deletions prog.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,17 +846,14 @@ func findTargetInKernel(name string, progType ProgramType, attachType AttachType
return nil, 0, errUnrecognizedAttachType
}

// maybeLoadKernelBTF may return external BTF if /sys/... is not available.
// Ideally we shouldn't use external BTF here, since we might try to use
// it for parsing kmod split BTF later on. That seems unlikely to work.
spec, err := btf.LoadKernelSpec()
if err != nil {
return nil, 0, fmt.Errorf("load kernel spec: %w", err)
}

err = spec.TypeByName(typeName, &target)
if errors.Is(err, btf.ErrNotFound) {
module, id, err := findTargetInModule(spec, typeName, target)
module, id, err := findTargetInModule(typeName, target)
if errors.Is(err, btf.ErrNotFound) {
return nil, 0, &internal.UnsupportedFeatureError{Name: featureName}
}
Expand All @@ -878,7 +875,7 @@ func findTargetInKernel(name string, progType ProgramType, attachType AttachType
// vmlinux must contain the kernel's types and is used to parse kmod BTF.
//
// Returns btf.ErrNotFound if the target can't be found in any module.
func findTargetInModule(vmlinux *btf.Spec, typeName string, target btf.Type) (*btf.Handle, btf.TypeID, error) {
func findTargetInModule(typeName string, target btf.Type) (*btf.Handle, btf.TypeID, error) {
it := new(btf.HandleIterator)
defer it.Handle.Close()

Expand All @@ -892,7 +889,7 @@ func findTargetInModule(vmlinux *btf.Spec, typeName string, target btf.Type) (*b
continue
}

spec, err := it.Handle.Spec(vmlinux)
spec, err := it.Handle.Spec()
if err != nil {
return nil, 0, fmt.Errorf("parse types for module %s: %w", info.Name, err)
}
Expand Down Expand Up @@ -942,7 +939,7 @@ func findTargetInProgram(prog *Program, name string, progType ProgramType, attac
}
defer btfHandle.Close()

spec, err := btfHandle.Spec(nil)
spec, err := btfHandle.Spec()
if err != nil {
return 0, err
}
Expand Down

0 comments on commit 7b3aa3b

Please sign in to comment.