diff --git a/elf_reader.go b/elf_reader.go index 53a77ac7d..17f60e5ca 100644 --- a/elf_reader.go +++ b/elf_reader.go @@ -554,7 +554,7 @@ func (ec *elfCode) loadBTFMaps(maps map[string]*MapSpec) error { } // Each section must appear as a DataSec in the ELF's BTF blob. - var ds btf.Datasec + var ds *btf.Datasec if err := ec.btf.FindType(sec.Name, &ds); err != nil { return fmt.Errorf("cannot find section '%s' in BTF: %w", sec.Name, err) } @@ -926,7 +926,7 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error { return errors.New("data sections require BTF, make sure all consts are marked as static") } - var datasec btf.Datasec + var datasec *btf.Datasec if err := ec.btf.FindType(sec.Name, &datasec); err != nil { return fmt.Errorf("data section %s: can't get BTF: %w", sec.Name, err) } @@ -947,7 +947,7 @@ func (ec *elfCode) loadDataSections(maps map[string]*MapSpec) error { ValueSize: uint32(len(data)), MaxEntries: 1, Contents: []MapKV{{uint32(0), data}}, - BTF: &btf.Map{Spec: ec.btf, Key: &btf.Void{}, Value: &datasec}, + BTF: &btf.Map{Spec: ec.btf, Key: &btf.Void{}, Value: datasec}, } switch sec.Name { diff --git a/internal/btf/btf.go b/internal/btf/btf.go index 91779f72e..0c2524be5 100644 --- a/internal/btf/btf.go +++ b/internal/btf/btf.go @@ -467,16 +467,28 @@ func (s *Spec) Program(name string, length uint64) (*Program, error) { // FindType searches for a type with a specific name. // -// hint determines the type of the returned Type. +// Called T a type that satisfies Type, typ must be a non-nil **T. +// On success, the address of the found type will be copied in typ. // // Returns an error wrapping ErrNotFound if no matching // type exists in spec. -func (s *Spec) FindType(name string, typ Type) error { - var ( - wanted = reflect.TypeOf(typ) - candidate Type - ) +func (s *Spec) FindType(name string, typ interface{}) error { + typValue := reflect.ValueOf(typ) + if typValue.Kind() != reflect.Ptr { + return fmt.Errorf("%T is not a pointer", typ) + } + + typPtr := typValue.Elem() + if !typPtr.CanSet() { + return fmt.Errorf("%T cannot be set", typ) + } + + wanted := typPtr.Type() + if !wanted.AssignableTo(reflect.TypeOf((*Type)(nil)).Elem()) { + return fmt.Errorf("%T does not satisfy Type interface", typ) + } + var candidate Type for _, typ := range s.namedTypes[essentialName(name)] { if reflect.TypeOf(typ) != wanted { continue @@ -498,8 +510,8 @@ func (s *Spec) FindType(name string, typ Type) error { return fmt.Errorf("type %s: %w", name, ErrNotFound) } - value := reflect.Indirect(reflect.ValueOf(candidate)) - reflect.Indirect(reflect.ValueOf(typ)).Set(value) + typPtr.Set(reflect.ValueOf(candidate)) + return nil } diff --git a/internal/btf/btf_test.go b/internal/btf/btf_test.go index ca391a98b..34514d93b 100644 --- a/internal/btf/btf_test.go +++ b/internal/btf/btf_test.go @@ -12,6 +12,57 @@ import ( "github.com/cilium/ebpf/internal/testutils" ) +func TestFindType(t *testing.T) { + fh, err := os.Open("testdata/vmlinux-btf.gz") + if err != nil { + t.Fatal(err) + } + defer fh.Close() + + rd, err := gzip.NewReader(fh) + if err != nil { + t.Fatal(err) + } + defer rd.Close() + + spec, err := loadRawSpec(rd, binary.LittleEndian, nil, nil) + if err != nil { + t.Fatal("Can't load BTF:", err) + } + + // spec.FindType MUST fail if typ is not a non-nil **T, where T satisfies btf.Type. + i := 0 + p := &i + for _, typ := range []interface{}{ + nil, + Struct{}, + &Struct{}, + []Struct{}, + &[]Struct{}, + map[int]Struct{}, + &map[int]Struct{}, + p, + &p, + } { + if err := spec.FindType("iphdr", typ); err == nil { + t.Fatalf("FindType does not fail with type %T", typ) + } + } + + // spec.FindType MUST return the same address for multiple calls with the same type name. + var iphdr1, iphdr2 *Struct + if err := spec.FindType("iphdr", &iphdr1); err != nil { + t.Fatal(err) + } + if err := spec.FindType("iphdr", &iphdr2); err != nil { + t.Fatal(err) + } + + if iphdr1 != iphdr2 { + t.Fatal("multiple FindType calls for `iphdr` name do not return the same addresses") + } +} + func TestParseVmlinux(t *testing.T) { fh, err := os.Open("testdata/vmlinux-btf.gz") if err != nil { @@ -29,7 +80,7 @@ func TestParseVmlinux(t *testing.T) { t.Fatal("Can't load BTF:", err) } - var iphdr Struct + var iphdr *Struct err = spec.FindType("iphdr", &iphdr) if err != nil { t.Fatalf("unable to find `iphdr` struct: %s", err) @@ -100,17 +151,17 @@ func TestLoadSpecFromElf(t *testing.T) { t.Error("Missing BTF for the socket section") } - var bpfMapDef Struct + var bpfMapDef *Struct if err := spec.FindType("bpf_map_def", &bpfMapDef); err != nil { t.Error("Can't find bpf_map_def:", err) } - var tmp Void + var tmp *Void if err := spec.FindType("totally_bogus_type", &tmp); !errors.Is(err, ErrNotFound) { t.Error("FindType doesn't return ErrNotFound:", err) } - var fn Func + var fn *Func if err := spec.FindType("global_fn", &fn); err != nil { t.Error("Can't find global_fn():", err) } else { @@ -119,7 +170,7 @@ func TestLoadSpecFromElf(t *testing.T) { } } - var v Var + var v *Var if err := spec.FindType("key3", &v); err != nil { t.Error("Cant find key3:", err) } else { @@ -198,7 +249,7 @@ func ExampleSpec_FindType() { spec := new(Spec) // Declare a variable of the desired type - var foo Struct + var foo *Struct if err := spec.FindType("foo", &foo); err != nil { // There is no struct with name foo, or there diff --git a/link/freplace.go b/link/freplace.go index 0eaaa017d..a698e1a9d 100644 --- a/link/freplace.go +++ b/link/freplace.go @@ -31,8 +31,10 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) ( return nil, fmt.Errorf("eBPF program type %s is not an Extension: %w", prog.Type(), errInvalidInput) } - var target int - var function btf.Func + var ( + target int + typeID btf.TypeID + ) if targetProg != nil { info, err := targetProg.Info() if err != nil { @@ -48,18 +50,20 @@ func AttachFreplace(targetProg *ebpf.Program, name string, prog *ebpf.Program) ( } defer btfHandle.Close() + var function *btf.Func if err := btfHandle.Spec().FindType(name, &function); err != nil { return nil, err } target = targetProg.FD() + typeID = function.ID() } link, err := AttachRawLink(RawLinkOptions{ Target: target, Program: prog, Attach: ebpf.AttachNone, - BTF: function.ID(), + BTF: typeID, }) if err != nil { return nil, err diff --git a/prog.go b/prog.go index 1f3b49356..90016a890 100644 --- a/prog.go +++ b/prog.go @@ -720,24 +720,17 @@ func resolveBTFType(spec *btf.Spec, name string, progType ProgramType, attachTyp a AttachType } - var target btf.Type var typeName, featureName string switch (match{progType, attachType}) { case match{LSM, AttachLSMMac}: - target = new(btf.Func) typeName = "bpf_lsm_" + name featureName = name + " LSM hook" - case match{Tracing, AttachTraceIter}: - target = new(btf.Func) typeName = "bpf_iter_" + name featureName = name + " iterator" - case match{Extension, AttachNone}: - target = new(btf.Func) typeName = name featureName = fmt.Sprintf("freplace %s", name) - default: return nil, nil } @@ -750,7 +743,8 @@ func resolveBTFType(spec *btf.Spec, name string, progType ProgramType, attachTyp } } - err := spec.FindType(typeName, target) + var target *btf.Func + err := spec.FindType(typeName, &target) if errors.Is(err, btf.ErrNotFound) { return nil, &internal.UnsupportedFeatureError{ Name: featureName,