Skip to content

Commit

Permalink
btf: avoid repeated kernel BTF copies in Handle.Spec
Browse files Browse the repository at this point in the history
When parsing kernel module BTF we currently obtain a copy of the
vmlinux types. This is nice from an API standpoint: the user doesn't
have to care that they are dealing with a module. The problem is that
copying vmlinux BTF is very slow due to its size.

We need to copy vmlinux BTF since it's otherwise possible for callers
of Handle.Spec to modify the internal copy of vmlinux BTF by accident.

Add an explicit base so that we can avoid copying the kernel spec.

Signed-off-by: Lorenz Bauer <lmb@isovalent.com>
  • Loading branch information
lmb committed Apr 6, 2023
1 parent 73acad5 commit e27d8ba
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 23 deletions.
8 changes: 4 additions & 4 deletions btf/btf.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,10 @@ func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, base *Spec) (*Spec, error
return nil, fmt.Errorf("can't use split BTF as base")
}

if base.strings == nil {
return nil, fmt.Errorf("parse split BTF: base must be loaded from an ELF")
}

baseStrings = base.strings

firstTypeID, err = base.nextTypeID()
Expand Down Expand Up @@ -770,10 +774,6 @@ func (s *Spec) TypeByName(name string, typ interface{}) error {
// Types from base are used to resolve references in the split BTF.
// The returned Spec only contains types from the split BTF, not from the base.
func LoadSplitSpecFromReader(r io.ReaderAt, base *Spec) (*Spec, error) {
if base.strings == nil {
return nil, fmt.Errorf("parse split BTF: base must be loaded from an ELF")
}

return loadRawSpec(r, internal.NativeEndian, base)
}

Expand Down
19 changes: 6 additions & 13 deletions btf/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ func NewHandleFromID(id ID) (*Handle, error) {
}

// Spec parses the kernel BTF into Go types.
func (h *Handle) Spec() (*Spec, error) {
//
// base must contain type information for vmlinux if the handle is for
// a kernel module. It may be nil otherwise.
func (h *Handle) Spec(base *Spec) (*Spec, error) {
var btfInfo sys.BtfInfo
btfBuffer := make([]byte, h.size)
btfInfo.Btf, btfInfo.BtfSize = sys.NewSlicePointerLen(btfBuffer)
Expand All @@ -119,18 +122,8 @@ func (h *Handle) Spec() (*Spec, error) {
return nil, err
}

if !h.needsKernelBase {
return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, nil)
}

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

if fallback {
return nil, fmt.Errorf("can't load split BTF without access to /sys")
if h.needsKernelBase && base == nil {
return nil, fmt.Errorf("missing base types")
}

return loadRawSpec(bytes.NewReader(btfBuffer), internal.NativeEndian, base)
Expand Down
7 changes: 6 additions & 1 deletion btf/handle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,12 @@ func TestParseModuleSplitSpec(t *testing.T) {
}
defer vmlinux.Close()

_, err = module.Spec()
base, err := vmlinux.Spec(nil)
if err != nil {
t.Fatal(err)
}

_, err = module.Spec(base)
if err != nil {
t.Fatal("Parse module BTF:", err)
}
Expand Down
2 changes: 1 addition & 1 deletion link/tracing.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) (
}
defer btfHandle.Close()

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

err = spec.TypeByName(typeName, &target)
if errors.Is(err, btf.ErrNotFound) {
module, id, err := findTargetInModule(typeName, target)
module, id, err := findTargetInModule(spec, typeName, target)
if errors.Is(err, btf.ErrNotFound) {
return nil, 0, &internal.UnsupportedFeatureError{Name: featureName}
}
Expand Down Expand Up @@ -923,7 +923,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(typeName string, target btf.Type) (*btf.Handle, btf.TypeID, error) {
func findTargetInModule(base *btf.Spec, typeName string, target btf.Type) (*btf.Handle, btf.TypeID, error) {
it := new(btf.HandleIterator)
defer it.Handle.Close()

Expand All @@ -937,7 +937,7 @@ func findTargetInModule(typeName string, target btf.Type) (*btf.Handle, btf.Type
continue
}

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

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

0 comments on commit e27d8ba

Please sign in to comment.