github.com/kubeshark/ebpf@v0.9.2/internal/cmd/gentypes/main.go (about)

     1  // Program gentypes reads a compressed vmlinux .BTF section and generates
     2  // syscall bindings from it.
     3  //
     4  // Output is written to "types.go".
     5  package main
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"sort"
    13  	"strings"
    14  
    15  	"github.com/kubeshark/ebpf/btf"
    16  	"github.com/kubeshark/ebpf/internal"
    17  	"github.com/kubeshark/ebpf/internal/sys"
    18  )
    19  
    20  type syscallRetval int
    21  
    22  const (
    23  	retError syscallRetval = iota
    24  	retFd
    25  )
    26  
    27  func main() {
    28  	if err := run(os.Args[1:]); err != nil {
    29  		fmt.Fprintln(os.Stderr, "Error:", err)
    30  		os.Exit(1)
    31  	}
    32  }
    33  
    34  func run(args []string) error {
    35  	if len(args) != 1 {
    36  		return fmt.Errorf("expect location of compressed vmlinux .BTF as argument")
    37  	}
    38  
    39  	raw, err := internal.ReadAllCompressed(args[0])
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	spec, err := btf.LoadSpecFromReader(bytes.NewReader(raw))
    45  	if err != nil {
    46  		return err
    47  	}
    48  
    49  	output, err := generateTypes(spec)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	w, err := os.Create("types.go")
    55  	if err != nil {
    56  		return err
    57  	}
    58  	defer w.Close()
    59  
    60  	return internal.WriteFormatted(output, w)
    61  }
    62  
    63  func generateTypes(spec *btf.Spec) ([]byte, error) {
    64  	objName := &btf.Array{Nelems: 16, Type: &btf.Int{Encoding: btf.Char, Size: 1}}
    65  	linkID := &btf.Int{Size: 4}
    66  	btfID := &btf.Int{Size: 4}
    67  	pointer := &btf.Int{Size: 8}
    68  
    69  	// Pre-declare handwritten types sys.ObjName and sys.Pointer so that
    70  	// generated types can refer to them.
    71  	var (
    72  		_ sys.Pointer
    73  		_ sys.ObjName
    74  		_ sys.LinkID
    75  	)
    76  
    77  	gf := &btf.GoFormatter{
    78  		Names: map[btf.Type]string{
    79  			objName: "ObjName",
    80  			linkID:  "LinkID",
    81  			btfID:   "BTFID",
    82  			pointer: "Pointer",
    83  		},
    84  		Identifier: internal.Identifier,
    85  		EnumIdentifier: func(name, element string) string {
    86  			return element
    87  		},
    88  	}
    89  
    90  	w := bytes.NewBuffer(nil)
    91  	w.WriteString(`// Code generated by internal/cmd/gentypes; DO NOT EDIT.
    92  
    93  package sys
    94  
    95  import (
    96  	"unsafe"
    97  )
    98  
    99  `)
   100  
   101  	enums := []struct {
   102  		goType string
   103  		cType  string
   104  	}{
   105  		{"Cmd", "bpf_cmd"},
   106  		{"MapType", "bpf_map_type"},
   107  		{"ProgType", "bpf_prog_type"},
   108  		{"AttachType", "bpf_attach_type"},
   109  		{"LinkType", "bpf_link_type"},
   110  		{"StatsType", "bpf_stats_type"},
   111  		{"SkAction", "sk_action"},
   112  		{"StackBuildIdStatus", "bpf_stack_build_id_status"},
   113  		{"FunctionId", "bpf_func_id"},
   114  		{"AdjRoomMode", "bpf_adj_room_mode"},
   115  		{"HdrStartOff", "bpf_hdr_start_off"},
   116  		{"RetCode", "bpf_ret_code"},
   117  		{"XdpAction", "xdp_action"},
   118  	}
   119  
   120  	sort.Slice(enums, func(i, j int) bool {
   121  		return enums[i].goType < enums[j].goType
   122  	})
   123  
   124  	enumTypes := make(map[string]btf.Type)
   125  	for _, o := range enums {
   126  		fmt.Println("enum", o.goType)
   127  
   128  		var t *btf.Enum
   129  		if err := spec.TypeByName(o.cType, &t); err != nil {
   130  			return nil, err
   131  		}
   132  
   133  		// Add the enum as a predeclared type so that generated structs
   134  		// refer to the Go types.
   135  		if name := gf.Names[t]; name != "" {
   136  			return nil, fmt.Errorf("type %q is already declared as %s", o.cType, name)
   137  		}
   138  		gf.Names[t] = o.goType
   139  		enumTypes[o.goType] = t
   140  
   141  		decl, err := gf.TypeDeclaration(o.goType, t)
   142  		if err != nil {
   143  			return nil, fmt.Errorf("generate %q: %w", o.goType, err)
   144  		}
   145  
   146  		w.WriteString(decl)
   147  		w.WriteRune('\n')
   148  	}
   149  
   150  	// Assorted structs
   151  
   152  	structs := []struct {
   153  		goType  string
   154  		cType   string
   155  		patches []patch
   156  	}{
   157  		{
   158  			"ProgInfo", "bpf_prog_info",
   159  			[]patch{
   160  				replace(objName, "name"),
   161  				replace(pointer, "xlated_prog_insns"),
   162  				replace(pointer, "map_ids"),
   163  			},
   164  		},
   165  		{
   166  			"MapInfo", "bpf_map_info",
   167  			[]patch{replace(objName, "name")},
   168  		},
   169  		{
   170  			"BtfInfo", "bpf_btf_info",
   171  			[]patch{
   172  				replace(pointer, "btf", "name"),
   173  				replace(btfID, "id"),
   174  			},
   175  		},
   176  		{
   177  			"LinkInfo", "bpf_link_info",
   178  			[]patch{
   179  				replace(enumTypes["LinkType"], "type"),
   180  				replace(linkID, "id"),
   181  				name(3, "extra"),
   182  				replaceWithBytes("extra"),
   183  			},
   184  		},
   185  		{"FuncInfo", "bpf_func_info", nil},
   186  		{"LineInfo", "bpf_line_info", nil},
   187  		{"XdpMd", "xdp_md", nil},
   188  		{
   189  			"SkLookup", "bpf_sk_lookup",
   190  			[]patch{
   191  				choose(0, "cookie"),
   192  				replaceWithBytes("remote_ip4", "remote_ip6", "local_ip4", "local_ip6"),
   193  			},
   194  		},
   195  	}
   196  
   197  	sort.Slice(structs, func(i, j int) bool {
   198  		return structs[i].goType < structs[j].goType
   199  	})
   200  
   201  	for _, s := range structs {
   202  		fmt.Println("struct", s.goType)
   203  
   204  		var t *btf.Struct
   205  		if err := spec.TypeByName(s.cType, &t); err != nil {
   206  			return nil, err
   207  		}
   208  
   209  		if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil {
   210  			return nil, fmt.Errorf("output %q: %w", s.goType, err)
   211  		}
   212  	}
   213  
   214  	// Attrs
   215  
   216  	attrs := []struct {
   217  		goType  string
   218  		ret     syscallRetval
   219  		cType   string
   220  		cmd     string
   221  		patches []patch
   222  	}{
   223  		{
   224  			"MapCreate", retFd, "map_create", "BPF_MAP_CREATE",
   225  			[]patch{
   226  				replace(objName, "map_name"),
   227  				replace(enumTypes["MapType"], "map_type"),
   228  			},
   229  		},
   230  		{
   231  			"MapLookupElem", retError, "map_elem", "BPF_MAP_LOOKUP_ELEM",
   232  			[]patch{choose(2, "value"), replace(pointer, "key", "value")},
   233  		},
   234  		{
   235  			"MapLookupAndDeleteElem", retError, "map_elem", "BPF_MAP_LOOKUP_AND_DELETE_ELEM",
   236  			[]patch{choose(2, "value"), replace(pointer, "key", "value")},
   237  		},
   238  		{
   239  			"MapUpdateElem", retError, "map_elem", "BPF_MAP_UPDATE_ELEM",
   240  			[]patch{choose(2, "value"), replace(pointer, "key", "value")},
   241  		},
   242  		{
   243  			"MapDeleteElem", retError, "map_elem", "BPF_MAP_DELETE_ELEM",
   244  			[]patch{choose(2, "value"), replace(pointer, "key", "value")},
   245  		},
   246  		{
   247  			"MapGetNextKey", retError, "map_elem", "BPF_MAP_GET_NEXT_KEY",
   248  			[]patch{
   249  				choose(2, "next_key"), replace(pointer, "key", "next_key"),
   250  				truncateAfter("next_key"),
   251  			},
   252  		},
   253  		{
   254  			"MapFreeze", retError, "map_elem", "BPF_MAP_FREEZE",
   255  			[]patch{truncateAfter("map_fd")},
   256  		},
   257  		{
   258  			"MapLookupBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_BATCH",
   259  			[]patch{replace(pointer, "in_batch", "out_batch", "keys", "values")},
   260  		},
   261  		{
   262  			"MapLookupAndDeleteBatch", retError, "map_elem_batch", "BPF_MAP_LOOKUP_AND_DELETE_BATCH",
   263  			[]patch{replace(pointer, "in_batch", "out_batch", "keys", "values")},
   264  		},
   265  		{
   266  			"MapUpdateBatch", retError, "map_elem_batch", "BPF_MAP_UPDATE_BATCH",
   267  			[]patch{replace(pointer, "in_batch", "out_batch", "keys", "values")},
   268  		},
   269  		{
   270  			"MapDeleteBatch", retError, "map_elem_batch", "BPF_MAP_DELETE_BATCH",
   271  			[]patch{replace(pointer, "in_batch", "out_batch", "keys", "values")},
   272  		},
   273  		{
   274  			"ProgLoad", retFd, "prog_load", "BPF_PROG_LOAD",
   275  			[]patch{
   276  				replace(objName, "prog_name"),
   277  				replace(enumTypes["ProgType"], "prog_type"),
   278  				replace(enumTypes["AttachType"], "expected_attach_type"),
   279  				replace(pointer,
   280  					"insns",
   281  					"license",
   282  					"log_buf",
   283  					"func_info",
   284  					"line_info",
   285  					"fd_array",
   286  					"core_relos",
   287  				),
   288  			},
   289  		},
   290  		{
   291  			"ProgBindMap", retError, "prog_bind_map", "BPF_PROG_BIND_MAP",
   292  			nil,
   293  		},
   294  		{
   295  			"ObjPin", retError, "obj_pin", "BPF_OBJ_PIN",
   296  			[]patch{replace(pointer, "pathname")},
   297  		},
   298  		{
   299  			"ObjGet", retFd, "obj_pin", "BPF_OBJ_GET",
   300  			[]patch{replace(pointer, "pathname")},
   301  		},
   302  		{
   303  			"ProgAttach", retError, "prog_attach", "BPF_PROG_ATTACH",
   304  			nil,
   305  		},
   306  		{
   307  			"ProgDetach", retError, "prog_attach", "BPF_PROG_DETACH",
   308  			[]patch{truncateAfter("attach_type")},
   309  		},
   310  		{
   311  			"ProgRun", retError, "prog_run", "BPF_PROG_TEST_RUN",
   312  			[]patch{replace(pointer, "data_in", "data_out", "ctx_in", "ctx_out")},
   313  		},
   314  		{
   315  			"ProgGetNextId", retError, "obj_next_id", "BPF_PROG_GET_NEXT_ID",
   316  			[]patch{
   317  				choose(0, "start_id"), rename("start_id", "id"),
   318  				truncateAfter("next_id"),
   319  			},
   320  		},
   321  		{
   322  			"MapGetNextId", retError, "obj_next_id", "BPF_MAP_GET_NEXT_ID",
   323  			[]patch{
   324  				choose(0, "start_id"), rename("start_id", "id"),
   325  				truncateAfter("next_id"),
   326  			},
   327  		},
   328  		{
   329  			"BtfGetNextId", retError, "obj_next_id", "BPF_BTF_GET_NEXT_ID",
   330  			[]patch{
   331  				choose(0, "start_id"), rename("start_id", "id"),
   332  				replace(btfID, "id", "next_id"),
   333  				truncateAfter("next_id"),
   334  			},
   335  		},
   336  		// These piggy back on the obj_next_id decl, but only support the
   337  		// first field...
   338  		{
   339  			"BtfGetFdById", retFd, "obj_next_id", "BPF_BTF_GET_FD_BY_ID",
   340  			[]patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")},
   341  		},
   342  		{
   343  			"MapGetFdById", retFd, "obj_next_id", "BPF_MAP_GET_FD_BY_ID",
   344  			[]patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")},
   345  		},
   346  		{
   347  			"ProgGetFdById", retFd, "obj_next_id", "BPF_PROG_GET_FD_BY_ID",
   348  			[]patch{choose(0, "start_id"), rename("start_id", "id"), truncateAfter("id")},
   349  		},
   350  		{
   351  			"ObjGetInfoByFd", retError, "info_by_fd", "BPF_OBJ_GET_INFO_BY_FD",
   352  			[]patch{replace(pointer, "info")},
   353  		},
   354  		{
   355  			"RawTracepointOpen", retFd, "raw_tracepoint_open", "BPF_RAW_TRACEPOINT_OPEN",
   356  			[]patch{replace(pointer, "name")},
   357  		},
   358  		{
   359  			"BtfLoad", retFd, "btf_load", "BPF_BTF_LOAD",
   360  			[]patch{replace(pointer, "btf", "btf_log_buf")},
   361  		},
   362  		{
   363  			"LinkCreate", retFd, "link_create", "BPF_LINK_CREATE",
   364  			[]patch{replace(enumTypes["AttachType"], "attach_type")},
   365  		},
   366  		{
   367  			"LinkCreateIter", retFd, "link_create", "BPF_LINK_CREATE",
   368  			[]patch{
   369  				chooseNth(4, 1),
   370  				replace(enumTypes["AttachType"], "attach_type"),
   371  				flattenAnon,
   372  				replace(pointer, "iter_info"),
   373  			},
   374  		},
   375  		{
   376  			"LinkCreatePerfEvent", retFd, "link_create", "BPF_LINK_CREATE",
   377  			[]patch{
   378  				chooseNth(4, 2),
   379  				replace(enumTypes["AttachType"], "attach_type"),
   380  				flattenAnon,
   381  			},
   382  		},
   383  		{
   384  			"LinkUpdate", retError, "link_update", "BPF_LINK_UPDATE",
   385  			nil,
   386  		},
   387  		{
   388  			"EnableStats", retFd, "enable_stats", "BPF_ENABLE_STATS",
   389  			nil,
   390  		},
   391  		{
   392  			"IterCreate", retFd, "iter_create", "BPF_ITER_CREATE",
   393  			nil,
   394  		},
   395  	}
   396  
   397  	sort.Slice(attrs, func(i, j int) bool {
   398  		return attrs[i].goType < attrs[j].goType
   399  	})
   400  
   401  	var bpfAttr *btf.Union
   402  	if err := spec.TypeByName("bpf_attr", &bpfAttr); err != nil {
   403  		return nil, err
   404  	}
   405  	attrTypes, err := splitUnion(bpfAttr, types{
   406  		{"map_create", "map_type"},
   407  		{"map_elem", "map_fd"},
   408  		{"map_elem_batch", "batch"},
   409  		{"prog_load", "prog_type"},
   410  		{"obj_pin", "pathname"},
   411  		{"prog_attach", "target_fd"},
   412  		{"prog_run", "test"},
   413  		{"obj_next_id", ""},
   414  		{"info_by_fd", "info"},
   415  		{"prog_query", "query"},
   416  		{"raw_tracepoint_open", "raw_tracepoint"},
   417  		{"btf_load", "btf"},
   418  		{"task_fd_query", "task_fd_query"},
   419  		{"link_create", "link_create"},
   420  		{"link_update", "link_update"},
   421  		{"link_detach", "link_detach"},
   422  		{"enable_stats", "enable_stats"},
   423  		{"iter_create", "iter_create"},
   424  		{"prog_bind_map", "prog_bind_map"},
   425  	})
   426  	if err != nil {
   427  		return nil, fmt.Errorf("splitting bpf_attr: %w", err)
   428  	}
   429  
   430  	for _, s := range attrs {
   431  		fmt.Println("attr", s.goType)
   432  
   433  		t := attrTypes[s.cType]
   434  		if t == nil {
   435  			return nil, fmt.Errorf("unknown attr %q", s.cType)
   436  		}
   437  
   438  		goAttrType := s.goType + "Attr"
   439  		if err := outputPatchedStruct(gf, w, goAttrType, t, s.patches); err != nil {
   440  			return nil, fmt.Errorf("output %q: %w", goAttrType, err)
   441  		}
   442  
   443  		switch s.ret {
   444  		case retError:
   445  			fmt.Fprintf(w, "func %s(attr *%s) error { _, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); return err }\n\n", s.goType, goAttrType, s.cmd)
   446  		case retFd:
   447  			fmt.Fprintf(w, "func %s(attr *%s) (*FD, error) { fd, err := BPF(%s, unsafe.Pointer(attr), unsafe.Sizeof(*attr)); if err != nil { return nil, err }; return NewFD(int(fd)) }\n\n", s.goType, goAttrType, s.cmd)
   448  		}
   449  	}
   450  
   451  	// Link info type specific
   452  
   453  	linkInfoExtraTypes := []struct {
   454  		goType  string
   455  		cType   string
   456  		patches []patch
   457  	}{
   458  		{"CgroupLinkInfo", "cgroup", []patch{replace(enumTypes["AttachType"], "attach_type")}},
   459  		{"IterLinkInfo", "iter", []patch{replace(pointer, "target_name"), truncateAfter("target_name_len")}},
   460  		{"NetNsLinkInfo", "netns", []patch{replace(enumTypes["AttachType"], "attach_type")}},
   461  		{"RawTracepointLinkInfo", "raw_tracepoint", []patch{replace(pointer, "tp_name")}},
   462  		{"TracingLinkInfo", "tracing", []patch{replace(enumTypes["AttachType"], "attach_type")}},
   463  		{"XDPLinkInfo", "xdp", nil},
   464  	}
   465  
   466  	sort.Slice(linkInfoExtraTypes, func(i, j int) bool {
   467  		return linkInfoExtraTypes[i].goType < linkInfoExtraTypes[j].goType
   468  	})
   469  
   470  	var bpfLinkInfo *btf.Struct
   471  	if err := spec.TypeByName("bpf_link_info", &bpfLinkInfo); err != nil {
   472  		return nil, err
   473  	}
   474  
   475  	member := bpfLinkInfo.Members[len(bpfLinkInfo.Members)-1]
   476  	bpfLinkInfoUnion, ok := member.Type.(*btf.Union)
   477  	if !ok {
   478  		return nil, fmt.Errorf("there is not type-specific union")
   479  	}
   480  
   481  	linkInfoTypes, err := splitUnion(bpfLinkInfoUnion, types{
   482  		{"raw_tracepoint", "raw_tracepoint"},
   483  		{"tracing", "tracing"},
   484  		{"cgroup", "cgroup"},
   485  		{"iter", "iter"},
   486  		{"netns", "netns"},
   487  		{"xdp", "xdp"},
   488  	})
   489  	if err != nil {
   490  		return nil, fmt.Errorf("splitting linkInfo: %w", err)
   491  	}
   492  
   493  	for _, s := range linkInfoExtraTypes {
   494  		t := linkInfoTypes[s.cType]
   495  		if err := outputPatchedStruct(gf, w, s.goType, t, s.patches); err != nil {
   496  			return nil, fmt.Errorf("output %q: %w", s.goType, err)
   497  		}
   498  	}
   499  
   500  	return w.Bytes(), nil
   501  }
   502  
   503  func outputPatchedStruct(gf *btf.GoFormatter, w *bytes.Buffer, id string, s *btf.Struct, patches []patch) error {
   504  	s = btf.Copy(s, nil).(*btf.Struct)
   505  
   506  	for i, p := range patches {
   507  		if err := p(s); err != nil {
   508  			return fmt.Errorf("patch %d: %w", i, err)
   509  		}
   510  	}
   511  
   512  	decl, err := gf.TypeDeclaration(id, s)
   513  	if err != nil {
   514  		return err
   515  	}
   516  
   517  	w.WriteString(decl)
   518  	w.WriteString("\n\n")
   519  	return nil
   520  }
   521  
   522  type types []struct {
   523  	name                string
   524  	cFieldOrFirstMember string
   525  }
   526  
   527  func splitUnion(union *btf.Union, types types) (map[string]*btf.Struct, error) {
   528  	structs := make(map[string]*btf.Struct)
   529  
   530  	for i, t := range types {
   531  		member := union.Members[i]
   532  		s, ok := member.Type.(*btf.Struct)
   533  		if !ok {
   534  			return nil, fmt.Errorf("%q: %s is not a struct", t.name, member.Type)
   535  		}
   536  
   537  		if member.Name == "" {
   538  			// This is an anonymous struct, check the name of the first member instead.
   539  			if name := s.Members[0].Name; name != t.cFieldOrFirstMember {
   540  				return nil, fmt.Errorf("first field of %q is %q, not %q", t.name, name, t.cFieldOrFirstMember)
   541  			}
   542  		} else if member.Name != t.cFieldOrFirstMember {
   543  			return nil, fmt.Errorf("name for %q is %q, not %q", t.name, member.Name, t.cFieldOrFirstMember)
   544  		}
   545  
   546  		structs[t.name] = s
   547  	}
   548  
   549  	return structs, nil
   550  }
   551  
   552  type patch func(*btf.Struct) error
   553  
   554  func modify(fn func(*btf.Member) error, members ...string) patch {
   555  	return func(s *btf.Struct) error {
   556  		want := make(map[string]bool)
   557  		for _, name := range members {
   558  			want[name] = true
   559  		}
   560  
   561  		for i, m := range s.Members {
   562  			if want[m.Name] {
   563  				if err := fn(&s.Members[i]); err != nil {
   564  					return err
   565  				}
   566  				delete(want, m.Name)
   567  			}
   568  		}
   569  
   570  		if len(want) == 0 {
   571  			return nil
   572  		}
   573  
   574  		var missing []string
   575  		for name := range want {
   576  			missing = append(missing, name)
   577  		}
   578  		sort.Strings(missing)
   579  
   580  		return fmt.Errorf("missing members: %v", strings.Join(missing, ", "))
   581  	}
   582  }
   583  
   584  func modifyNth(fn func(*btf.Member) error, indices ...int) patch {
   585  	return func(s *btf.Struct) error {
   586  		for _, i := range indices {
   587  			if i >= len(s.Members) {
   588  				return fmt.Errorf("index %d is out of bounds", i)
   589  			}
   590  
   591  			if err := fn(&s.Members[i]); err != nil {
   592  				return fmt.Errorf("member #%d: %w", i, err)
   593  			}
   594  		}
   595  		return nil
   596  	}
   597  }
   598  
   599  func replace(t btf.Type, members ...string) patch {
   600  	return modify(func(m *btf.Member) error {
   601  		m.Type = t
   602  		return nil
   603  	}, members...)
   604  }
   605  
   606  func choose(member int, name string) patch {
   607  	return modifyNth(func(m *btf.Member) error {
   608  		union, ok := m.Type.(*btf.Union)
   609  		if !ok {
   610  			return fmt.Errorf("member %d is %s, not a union", member, m.Type)
   611  		}
   612  
   613  		for _, um := range union.Members {
   614  			if um.Name == name {
   615  				m.Name = um.Name
   616  				m.Type = um.Type
   617  				return nil
   618  			}
   619  		}
   620  
   621  		return fmt.Errorf("%s has no member %q", union, name)
   622  	}, member)
   623  }
   624  
   625  func chooseNth(member int, n int) patch {
   626  	return modifyNth(func(m *btf.Member) error {
   627  		union, ok := m.Type.(*btf.Union)
   628  		if !ok {
   629  			return fmt.Errorf("member %d is %s, not a union", member, m.Type)
   630  		}
   631  
   632  		if n >= len(union.Members) {
   633  			return fmt.Errorf("member %d is out of bounds", n)
   634  		}
   635  
   636  		um := union.Members[n]
   637  		m.Name = um.Name
   638  		m.Type = um.Type
   639  		return nil
   640  	}, member)
   641  }
   642  
   643  func flattenAnon(s *btf.Struct) error {
   644  	for i := range s.Members {
   645  		m := &s.Members[i]
   646  
   647  		cs, ok := m.Type.(*btf.Struct)
   648  		if !ok || cs.TypeName() != "" {
   649  			continue
   650  		}
   651  
   652  		for j := range cs.Members {
   653  			cs.Members[j].Offset += m.Offset
   654  		}
   655  
   656  		newMembers := make([]btf.Member, 0, len(s.Members)+len(cs.Members)-1)
   657  		newMembers = append(newMembers, s.Members[:i]...)
   658  		newMembers = append(newMembers, cs.Members...)
   659  		newMembers = append(newMembers, s.Members[i+1:]...)
   660  
   661  		s.Members = newMembers
   662  	}
   663  
   664  	return nil
   665  }
   666  
   667  func truncateAfter(name string) patch {
   668  	return func(s *btf.Struct) error {
   669  		for i, m := range s.Members {
   670  			if m.Name != name {
   671  				continue
   672  			}
   673  
   674  			size, err := btf.Sizeof(m.Type)
   675  			if err != nil {
   676  				return err
   677  			}
   678  
   679  			s.Members = s.Members[:i+1]
   680  			s.Size = m.Offset.Bytes() + uint32(size)
   681  			return nil
   682  		}
   683  
   684  		return fmt.Errorf("no member %q", name)
   685  	}
   686  }
   687  
   688  func rename(from, to string) patch {
   689  	return func(s *btf.Struct) error {
   690  		for i, m := range s.Members {
   691  			if m.Name == from {
   692  				s.Members[i].Name = to
   693  				return nil
   694  			}
   695  		}
   696  		return fmt.Errorf("no member named %q", from)
   697  	}
   698  }
   699  
   700  func name(member int, name string) patch {
   701  	return modifyNth(func(m *btf.Member) error {
   702  		if m.Name != "" {
   703  			return fmt.Errorf("member already has name %q", m.Name)
   704  		}
   705  
   706  		m.Name = name
   707  		return nil
   708  	}, member)
   709  }
   710  
   711  func replaceWithBytes(members ...string) patch {
   712  	return modify(func(m *btf.Member) error {
   713  		if m.BitfieldSize != 0 {
   714  			return errors.New("replaceWithBytes: member is a bitfield")
   715  		}
   716  
   717  		size, err := btf.Sizeof(m.Type)
   718  		if err != nil {
   719  			return fmt.Errorf("replaceWithBytes: size of %s: %w", m.Type, err)
   720  		}
   721  
   722  		m.Type = &btf.Array{
   723  			Type:   &btf.Int{Size: 1},
   724  			Nelems: uint32(size),
   725  		}
   726  
   727  		return nil
   728  	}, members...)
   729  }