diff --git a/btf/marshal.go b/btf/marshal.go index 5ed79c1f5..9a3789729 100644 --- a/btf/marshal.go +++ b/btf/marshal.go @@ -521,7 +521,7 @@ func (e *encoder) deflateEnum64(raw *rawType, enum *Enum) (err error) { }) } - return e.deflateUnion(raw, &Union{enum.Name, enum.Size, members}) + return e.deflateUnion(raw, &Union{enum.Name, enum.Size, members, nil, nil}) } raw.SetKind(kindEnum64) diff --git a/btf/marshal_test.go b/btf/marshal_test.go index 0d3dbe68c..3f5bb9528 100644 --- a/btf/marshal_test.go +++ b/btf/marshal_test.go @@ -24,7 +24,7 @@ func TestBuilderMarshal(t *testing.T) { (*Void)(nil), typ, &Pointer{typ}, - &Typedef{"baz", typ}, + &Typedef{"baz", typ, nil}, } b, err := NewBuilder(want) @@ -65,7 +65,7 @@ func TestBuilderAdd(t *testing.T) { qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(id, TypeID(2)), qt.Commentf("Adding a type twice returns different ids")) - id, err = b.Add(&Typedef{"baz", i}) + id, err = b.Add(&Typedef{"baz", i, nil}) qt.Assert(t, qt.IsNil(err)) qt.Assert(t, qt.Equals(id, TypeID(3))) } @@ -144,6 +144,7 @@ func TestMarshalEnum64(t *testing.T) { {Name: "A", Type: placeholder}, {Name: "B", Type: placeholder}, }, + MemberTags: make([][]string, 2), })) } diff --git a/btf/traversal.go b/btf/traversal.go index 384ae774f..0c3f7d4e1 100644 --- a/btf/traversal.go +++ b/btf/traversal.go @@ -41,7 +41,7 @@ func children(typ Type, yield func(child *Type) bool) bool { // do its work. This avoids allocating intermediate slices from walk() on // the heap. switch v := typ.(type) { - case *Void, *Int, *Enum, *Fwd, *Float: + case *Void, *Int, *Enum, *Fwd, *Float, *declTag: // No children to traverse. case *Pointer: if !yield(&v.Target) { @@ -59,12 +59,41 @@ func children(typ Type, yield func(child *Type) bool) bool { if !yield(&v.Members[i].Type) { return false } + if len(v.MemberTags) > i { + for _, t := range v.MemberTags[i] { + var tag Type = &declTag{t, v, i} + if !yield(&tag) { + return false + } + } + } + } + for _, t := range v.Tags { + var tag Type = &declTag{t, v, -1} + if !yield(&tag) { + return false + } } case *Union: for i := range v.Members { if !yield(&v.Members[i].Type) { return false } + + if len(v.MemberTags) > i { + for _, t := range v.MemberTags[i] { + var tag Type = &declTag{t, v, i} + if !yield(&tag) { + return false + } + } + } + } + for _, t := range v.Tags { + var tag Type = &declTag{t, v, -1} + if !yield(&tag) { + return false + } } case *Typedef: if !yield(&v.Type) { @@ -86,6 +115,22 @@ func children(typ Type, yield func(child *Type) bool) bool { if !yield(&v.Type) { return false } + for _, t := range v.Tags { + var tag Type = &declTag{t, v, -1} + if !yield(&tag) { + return false + } + } + if fp, ok := v.Type.(*FuncProto); ok { + for i, param := range fp.Params { + for _, t := range param.Tags { + var tag Type = &declTag{t, v, i} + if !yield(&tag) { + return false + } + } + } + } case *FuncProto: if !yield(&v.Return) { return false @@ -99,16 +144,18 @@ func children(typ Type, yield func(child *Type) bool) bool { if !yield(&v.Type) { return false } + for _, t := range v.Tags { + var tag Type = &declTag{t, v, -1} + if !yield(&tag) { + return false + } + } case *Datasec: for i := range v.Vars { if !yield(&v.Vars[i].Type) { return false } } - case *declTag: - if !yield(&v.Type) { - return false - } case *TypeTag: if !yield(&v.Type) { return false diff --git a/btf/types.go b/btf/types.go index db7fdc6b3..79a1dcbc3 100644 --- a/btf/types.go +++ b/btf/types.go @@ -66,7 +66,6 @@ var ( _ Type = (*Var)(nil) _ Type = (*Datasec)(nil) _ Type = (*Float)(nil) - _ Type = (*declTag)(nil) _ Type = (*TypeTag)(nil) _ Type = (*cycle)(nil) ) @@ -167,8 +166,10 @@ func (arr *Array) copy() Type { type Struct struct { Name string // The size of the struct including padding, in bytes - Size uint32 - Members []Member + Size uint32 + Members []Member + Tags []string + MemberTags [][]string } func (s *Struct) Format(fs fmt.State, verb rune) { @@ -193,8 +194,10 @@ func (s *Struct) members() []Member { type Union struct { Name string // The size of the union including padding, in bytes. - Size uint32 - Members []Member + Size uint32 + Members []Member + Tags []string + MemberTags [][]string } func (u *Union) Format(fs fmt.State, verb rune) { @@ -334,6 +337,7 @@ func (f *Fwd) matches(typ Type) bool { type Typedef struct { Name string Type Type + Tags []string } func (td *Typedef) Format(fs fmt.State, verb rune) { @@ -403,6 +407,7 @@ type Func struct { Name string Type Type Linkage FuncLinkage + Tags []string } func FuncMetadata(ins *asm.Instruction) *Func { @@ -449,6 +454,7 @@ func (fp *FuncProto) copy() Type { type FuncParam struct { Name string Type Type + Tags []string } // Var is a global variable. @@ -456,6 +462,7 @@ type Var struct { Name string Type Type Linkage VarLinkage + Tags []string } func (v *Var) Format(fs fmt.State, verb rune) { @@ -522,8 +529,8 @@ func (f *Float) copy() Type { // declTag associates metadata with a declaration. type declTag struct { - Type Type Value string + Type Type // The index this tag refers to in the target type. For composite types, // a value of -1 indicates that the tag refers to the whole type. Otherwise // it indicates which member or argument the tag applies to. @@ -918,7 +925,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt if err != nil { return nil, fmt.Errorf("struct %s (id %d): %w", name, id, err) } - typ = &Struct{name, header.Size(), members} + typ = &Struct{name, header.Size(), members, nil, make([][]string, len(members))} case kindUnion: vlen := header.Vlen() @@ -935,7 +942,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt if err != nil { return nil, fmt.Errorf("union %s (id %d): %w", name, id, err) } - typ = &Union{name, header.Size(), members} + typ = &Union{name, header.Size(), members, nil, make([][]string, len(members))} case kindEnum: vlen := header.Vlen() @@ -968,7 +975,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt typ = &Fwd{name, header.FwdKind()} case kindTypedef: - typedef := &Typedef{name, nil} + typedef := &Typedef{name, nil, nil} fixup(header.Type(), &typedef.Type) typ = typedef @@ -988,7 +995,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt typ = restrict case kindFunc: - fn := &Func{name, nil, header.Linkage()} + fn := &Func{name, nil, header.Linkage(), nil} fixup(header.Type(), &fn.Type) typ = fn @@ -1030,7 +1037,7 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt return nil, fmt.Errorf("can't read btfVariable, id: %d: %w", id, err) } - v := &Var{name, nil, VarLinkage(bVariable.Linkage)} + v := &Var{name, nil, VarLinkage(bVariable.Linkage), nil} fixup(header.Type(), &v.Type) typ = v @@ -1074,9 +1081,9 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt return nil, fmt.Errorf("type id %d: index exceeds int", id) } - dt := &declTag{nil, name, int(int32(btfIndex))} + dt := &declTag{name, nil, int(int32(btfIndex))} fixup(header.Type(), &dt.Type) - typ = dt + typ = &Void{} declTags = append(declTags, dt) @@ -1142,26 +1149,77 @@ func readAndInflateTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32, rawSt for _, dt := range declTags { switch t := dt.Type.(type) { - case *Var, *Typedef: + case *Var: + if dt.Index != -1 { + return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index) + } + t.Tags = append(t.Tags, dt.Value) + + case *Typedef: if dt.Index != -1 { - return nil, fmt.Errorf("type %s: index %d is not -1", dt, dt.Index) + return nil, fmt.Errorf("type %s: component idx %d is not -1", dt, dt.Index) } + t.Tags = append(t.Tags, dt.Value) case composite: - if dt.Index >= len(t.members()) { - return nil, fmt.Errorf("type %s: index %d exceeds members of %s", dt, dt.Index, t) + switch t2 := t.(type) { + case *Struct: + if dt.Index >= 0 { + members := t.members() + if dt.Index >= len(members) { + return nil, fmt.Errorf("type %s: component idx %d exceeds members of %s", dt, dt.Index, t) + } + + t2.MemberTags[dt.Index] = append(t2.MemberTags[dt.Index], dt.Value) + continue + } + + if dt.Index == -1 { + t2.Tags = append(t2.Tags, dt.Value) + continue + } + + case *Union: + if dt.Index >= 0 { + members := t.members() + if dt.Index >= len(members) { + return nil, fmt.Errorf("type %s: component idx %d exceeds members of %s", dt, dt.Index, t) + } + + t2.MemberTags[dt.Index] = append(t2.MemberTags[dt.Index], dt.Value) + continue + } + + if dt.Index == -1 { + t2.Tags = append(t2.Tags, dt.Value) + continue + } } + return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t) + case *Func: fp, ok := t.Type.(*FuncProto) if !ok { return nil, fmt.Errorf("type %s: %s is not a FuncProto", dt, t.Type) } - if dt.Index >= len(fp.Params) { - return nil, fmt.Errorf("type %s: index %d exceeds params of %s", dt, dt.Index, t) + if dt.Index >= 0 { + if dt.Index >= len(fp.Params) { + return nil, fmt.Errorf("type %s: component idx %d exceeds params of %s", dt, dt.Index, t) + } + + fp.Params[dt.Index].Tags = append(fp.Params[dt.Index].Tags, dt.Value) + continue } + if dt.Index == -1 { + t.Tags = append(t.Tags, dt.Value) + continue + } + + return nil, fmt.Errorf("type %s: decl tag for type %s has invalid component idx", dt, t) + default: return nil, fmt.Errorf("type %s: decl tag for type %s is not supported", dt, t) } diff --git a/btf/types_test.go b/btf/types_test.go index 0704a9a3b..6d320151b 100644 --- a/btf/types_test.go +++ b/btf/types_test.go @@ -174,7 +174,6 @@ func TestType(t *testing.T) { } }, func() Type { return &Float{} }, - func() Type { return &declTag{Type: &Void{}} }, func() Type { return &TypeTag{Type: &Void{}} }, func() Type { return &cycle{&Void{}} }, } @@ -213,7 +212,8 @@ func TestType(t *testing.T) { func TestTagMarshaling(t *testing.T) { for _, typ := range []Type{ - &declTag{&Struct{Members: []Member{}}, "foo", -1}, + &Struct{Members: []Member{}, Tags: []string{"foo"}, MemberTags: make([][]string, 0)}, + &Struct{Members: []Member{{Name: "field1", Type: &Int{}}}, Tags: nil, MemberTags: [][]string{{"foo"}}}, &TypeTag{&Int{}, "foo"}, } { t.Run(fmt.Sprint(typ), func(t *testing.T) { diff --git a/btf/workarounds_test.go b/btf/workarounds_test.go index ce260ccf0..70fc85446 100644 --- a/btf/workarounds_test.go +++ b/btf/workarounds_test.go @@ -17,7 +17,7 @@ func TestDatasecResolveWorkaround(t *testing.T) { i := &Int{Size: 1} for _, typ := range []Type{ - &Typedef{"foo", i}, + &Typedef{"foo", i, nil}, &Volatile{i}, &Const{i}, &Restrict{i},