github.com/cilium/ebpf@v0.10.0/asm/instruction.go (about)

     1  package asm
     2  
     3  import (
     4  	"crypto/sha1"
     5  	"encoding/binary"
     6  	"encoding/hex"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"math"
    11  	"sort"
    12  	"strings"
    13  
    14  	"github.com/cilium/ebpf/internal/sys"
    15  	"github.com/cilium/ebpf/internal/unix"
    16  )
    17  
    18  // InstructionSize is the size of a BPF instruction in bytes
    19  const InstructionSize = 8
    20  
    21  // RawInstructionOffset is an offset in units of raw BPF instructions.
    22  type RawInstructionOffset uint64
    23  
    24  var ErrUnreferencedSymbol = errors.New("unreferenced symbol")
    25  var ErrUnsatisfiedMapReference = errors.New("unsatisfied map reference")
    26  var ErrUnsatisfiedProgramReference = errors.New("unsatisfied program reference")
    27  
    28  // Bytes returns the offset of an instruction in bytes.
    29  func (rio RawInstructionOffset) Bytes() uint64 {
    30  	return uint64(rio) * InstructionSize
    31  }
    32  
    33  // Instruction is a single eBPF instruction.
    34  type Instruction struct {
    35  	OpCode   OpCode
    36  	Dst      Register
    37  	Src      Register
    38  	Offset   int16
    39  	Constant int64
    40  
    41  	// Metadata contains optional metadata about this instruction.
    42  	Metadata Metadata
    43  }
    44  
    45  // Unmarshal decodes a BPF instruction.
    46  func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) {
    47  	data := make([]byte, InstructionSize)
    48  	if _, err := io.ReadFull(r, data); err != nil {
    49  		return 0, err
    50  	}
    51  
    52  	ins.OpCode = OpCode(data[0])
    53  
    54  	regs := data[1]
    55  	switch bo {
    56  	case binary.LittleEndian:
    57  		ins.Dst, ins.Src = Register(regs&0xF), Register(regs>>4)
    58  	case binary.BigEndian:
    59  		ins.Dst, ins.Src = Register(regs>>4), Register(regs&0xf)
    60  	}
    61  
    62  	ins.Offset = int16(bo.Uint16(data[2:4]))
    63  	// Convert to int32 before widening to int64
    64  	// to ensure the signed bit is carried over.
    65  	ins.Constant = int64(int32(bo.Uint32(data[4:8])))
    66  
    67  	if !ins.OpCode.IsDWordLoad() {
    68  		return InstructionSize, nil
    69  	}
    70  
    71  	// Pull another instruction from the stream to retrieve the second
    72  	// half of the 64-bit immediate value.
    73  	if _, err := io.ReadFull(r, data); err != nil {
    74  		// No Wrap, to avoid io.EOF clash
    75  		return 0, errors.New("64bit immediate is missing second half")
    76  	}
    77  
    78  	// Require that all fields other than the value are zero.
    79  	if bo.Uint32(data[0:4]) != 0 {
    80  		return 0, errors.New("64bit immediate has non-zero fields")
    81  	}
    82  
    83  	cons1 := uint32(ins.Constant)
    84  	cons2 := int32(bo.Uint32(data[4:8]))
    85  	ins.Constant = int64(cons2)<<32 | int64(cons1)
    86  
    87  	return 2 * InstructionSize, nil
    88  }
    89  
    90  // Marshal encodes a BPF instruction.
    91  func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {
    92  	if ins.OpCode == InvalidOpCode {
    93  		return 0, errors.New("invalid opcode")
    94  	}
    95  
    96  	isDWordLoad := ins.OpCode.IsDWordLoad()
    97  
    98  	cons := int32(ins.Constant)
    99  	if isDWordLoad {
   100  		// Encode least significant 32bit first for 64bit operations.
   101  		cons = int32(uint32(ins.Constant))
   102  	}
   103  
   104  	regs, err := newBPFRegisters(ins.Dst, ins.Src, bo)
   105  	if err != nil {
   106  		return 0, fmt.Errorf("can't marshal registers: %s", err)
   107  	}
   108  
   109  	data := make([]byte, InstructionSize)
   110  	data[0] = byte(ins.OpCode)
   111  	data[1] = byte(regs)
   112  	bo.PutUint16(data[2:4], uint16(ins.Offset))
   113  	bo.PutUint32(data[4:8], uint32(cons))
   114  	if _, err := w.Write(data); err != nil {
   115  		return 0, err
   116  	}
   117  
   118  	if !isDWordLoad {
   119  		return InstructionSize, nil
   120  	}
   121  
   122  	// The first half of the second part of a double-wide instruction
   123  	// must be zero. The second half carries the value.
   124  	bo.PutUint32(data[0:4], 0)
   125  	bo.PutUint32(data[4:8], uint32(ins.Constant>>32))
   126  	if _, err := w.Write(data); err != nil {
   127  		return 0, err
   128  	}
   129  
   130  	return 2 * InstructionSize, nil
   131  }
   132  
   133  // AssociateMap associates a Map with this Instruction.
   134  //
   135  // Implicitly clears the Instruction's Reference field.
   136  //
   137  // Returns an error if the Instruction is not a map load.
   138  func (ins *Instruction) AssociateMap(m FDer) error {
   139  	if !ins.IsLoadFromMap() {
   140  		return errors.New("not a load from a map")
   141  	}
   142  
   143  	ins.Metadata.Set(referenceMeta{}, nil)
   144  	ins.Metadata.Set(mapMeta{}, m)
   145  
   146  	return nil
   147  }
   148  
   149  // RewriteMapPtr changes an instruction to use a new map fd.
   150  //
   151  // Returns an error if the instruction doesn't load a map.
   152  //
   153  // Deprecated: use AssociateMap instead. If you cannot provide a Map,
   154  // wrap an fd in a type implementing FDer.
   155  func (ins *Instruction) RewriteMapPtr(fd int) error {
   156  	if !ins.IsLoadFromMap() {
   157  		return errors.New("not a load from a map")
   158  	}
   159  
   160  	ins.encodeMapFD(fd)
   161  
   162  	return nil
   163  }
   164  
   165  func (ins *Instruction) encodeMapFD(fd int) {
   166  	// Preserve the offset value for direct map loads.
   167  	offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
   168  	rawFd := uint64(uint32(fd))
   169  	ins.Constant = int64(offset | rawFd)
   170  }
   171  
   172  // MapPtr returns the map fd for this instruction.
   173  //
   174  // The result is undefined if the instruction is not a load from a map,
   175  // see IsLoadFromMap.
   176  //
   177  // Deprecated: use Map() instead.
   178  func (ins *Instruction) MapPtr() int {
   179  	// If there is a map associated with the instruction, return its FD.
   180  	if fd := ins.Metadata.Get(mapMeta{}); fd != nil {
   181  		return fd.(FDer).FD()
   182  	}
   183  
   184  	// Fall back to the fd stored in the Constant field
   185  	return ins.mapFd()
   186  }
   187  
   188  // mapFd returns the map file descriptor stored in the 32 least significant
   189  // bits of ins' Constant field.
   190  func (ins *Instruction) mapFd() int {
   191  	return int(int32(ins.Constant))
   192  }
   193  
   194  // RewriteMapOffset changes the offset of a direct load from a map.
   195  //
   196  // Returns an error if the instruction is not a direct load.
   197  func (ins *Instruction) RewriteMapOffset(offset uint32) error {
   198  	if !ins.OpCode.IsDWordLoad() {
   199  		return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
   200  	}
   201  
   202  	if ins.Src != PseudoMapValue {
   203  		return errors.New("not a direct load from a map")
   204  	}
   205  
   206  	fd := uint64(ins.Constant) & math.MaxUint32
   207  	ins.Constant = int64(uint64(offset)<<32 | fd)
   208  	return nil
   209  }
   210  
   211  func (ins *Instruction) mapOffset() uint32 {
   212  	return uint32(uint64(ins.Constant) >> 32)
   213  }
   214  
   215  // IsLoadFromMap returns true if the instruction loads from a map.
   216  //
   217  // This covers both loading the map pointer and direct map value loads.
   218  func (ins *Instruction) IsLoadFromMap() bool {
   219  	return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
   220  }
   221  
   222  // IsFunctionCall returns true if the instruction calls another BPF function.
   223  //
   224  // This is not the same thing as a BPF helper call.
   225  func (ins *Instruction) IsFunctionCall() bool {
   226  	return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
   227  }
   228  
   229  // IsLoadOfFunctionPointer returns true if the instruction loads a function pointer.
   230  func (ins *Instruction) IsLoadOfFunctionPointer() bool {
   231  	return ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc
   232  }
   233  
   234  // IsFunctionReference returns true if the instruction references another BPF
   235  // function, either by invoking a Call jump operation or by loading a function
   236  // pointer.
   237  func (ins *Instruction) IsFunctionReference() bool {
   238  	return ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer()
   239  }
   240  
   241  // IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call.
   242  func (ins *Instruction) IsBuiltinCall() bool {
   243  	return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0
   244  }
   245  
   246  // IsConstantLoad returns true if the instruction loads a constant of the
   247  // given size.
   248  func (ins *Instruction) IsConstantLoad(size Size) bool {
   249  	return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0
   250  }
   251  
   252  // Format implements fmt.Formatter.
   253  func (ins Instruction) Format(f fmt.State, c rune) {
   254  	if c != 'v' {
   255  		fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c)
   256  		return
   257  	}
   258  
   259  	op := ins.OpCode
   260  
   261  	if op == InvalidOpCode {
   262  		fmt.Fprint(f, "INVALID")
   263  		return
   264  	}
   265  
   266  	// Omit trailing space for Exit
   267  	if op.JumpOp() == Exit {
   268  		fmt.Fprint(f, op)
   269  		return
   270  	}
   271  
   272  	if ins.IsLoadFromMap() {
   273  		fd := ins.mapFd()
   274  		m := ins.Map()
   275  		switch ins.Src {
   276  		case PseudoMapFD:
   277  			if m != nil {
   278  				fmt.Fprintf(f, "LoadMapPtr dst: %s map: %s", ins.Dst, m)
   279  			} else {
   280  				fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd)
   281  			}
   282  
   283  		case PseudoMapValue:
   284  			if m != nil {
   285  				fmt.Fprintf(f, "LoadMapValue dst: %s, map: %s off: %d", ins.Dst, m, ins.mapOffset())
   286  			} else {
   287  				fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset())
   288  			}
   289  		}
   290  
   291  		goto ref
   292  	}
   293  
   294  	fmt.Fprintf(f, "%v ", op)
   295  	switch cls := op.Class(); {
   296  	case cls.isLoadOrStore():
   297  		switch op.Mode() {
   298  		case ImmMode:
   299  			fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant)
   300  		case AbsMode:
   301  			fmt.Fprintf(f, "imm: %d", ins.Constant)
   302  		case IndMode:
   303  			fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant)
   304  		case MemMode:
   305  			fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant)
   306  		case XAddMode:
   307  			fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src)
   308  		}
   309  
   310  	case cls.IsALU():
   311  		fmt.Fprintf(f, "dst: %s ", ins.Dst)
   312  		if op.ALUOp() == Swap || op.Source() == ImmSource {
   313  			fmt.Fprintf(f, "imm: %d", ins.Constant)
   314  		} else {
   315  			fmt.Fprintf(f, "src: %s", ins.Src)
   316  		}
   317  
   318  	case cls.IsJump():
   319  		switch jop := op.JumpOp(); jop {
   320  		case Call:
   321  			if ins.Src == PseudoCall {
   322  				// bpf-to-bpf call
   323  				fmt.Fprint(f, ins.Constant)
   324  			} else {
   325  				fmt.Fprint(f, BuiltinFunc(ins.Constant))
   326  			}
   327  
   328  		default:
   329  			fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset)
   330  			if op.Source() == ImmSource {
   331  				fmt.Fprintf(f, "imm: %d", ins.Constant)
   332  			} else {
   333  				fmt.Fprintf(f, "src: %s", ins.Src)
   334  			}
   335  		}
   336  	}
   337  
   338  ref:
   339  	if ins.Reference() != "" {
   340  		fmt.Fprintf(f, " <%s>", ins.Reference())
   341  	}
   342  }
   343  
   344  func (ins Instruction) equal(other Instruction) bool {
   345  	return ins.OpCode == other.OpCode &&
   346  		ins.Dst == other.Dst &&
   347  		ins.Src == other.Src &&
   348  		ins.Offset == other.Offset &&
   349  		ins.Constant == other.Constant
   350  }
   351  
   352  // Size returns the amount of bytes ins would occupy in binary form.
   353  func (ins Instruction) Size() uint64 {
   354  	return uint64(InstructionSize * ins.OpCode.rawInstructions())
   355  }
   356  
   357  // WithMetadata sets the given Metadata on the Instruction. e.g. to copy
   358  // Metadata from another Instruction when replacing it.
   359  func (ins Instruction) WithMetadata(meta Metadata) Instruction {
   360  	ins.Metadata = meta
   361  	return ins
   362  }
   363  
   364  type symbolMeta struct{}
   365  
   366  // WithSymbol marks the Instruction as a Symbol, which other Instructions
   367  // can point to using corresponding calls to WithReference.
   368  func (ins Instruction) WithSymbol(name string) Instruction {
   369  	ins.Metadata.Set(symbolMeta{}, name)
   370  	return ins
   371  }
   372  
   373  // Sym creates a symbol.
   374  //
   375  // Deprecated: use WithSymbol instead.
   376  func (ins Instruction) Sym(name string) Instruction {
   377  	return ins.WithSymbol(name)
   378  }
   379  
   380  // Symbol returns the value ins has been marked with using WithSymbol,
   381  // otherwise returns an empty string. A symbol is often an Instruction
   382  // at the start of a function body.
   383  func (ins Instruction) Symbol() string {
   384  	sym, _ := ins.Metadata.Get(symbolMeta{}).(string)
   385  	return sym
   386  }
   387  
   388  type referenceMeta struct{}
   389  
   390  // WithReference makes ins reference another Symbol or map by name.
   391  func (ins Instruction) WithReference(ref string) Instruction {
   392  	ins.Metadata.Set(referenceMeta{}, ref)
   393  	return ins
   394  }
   395  
   396  // Reference returns the Symbol or map name referenced by ins, if any.
   397  func (ins Instruction) Reference() string {
   398  	ref, _ := ins.Metadata.Get(referenceMeta{}).(string)
   399  	return ref
   400  }
   401  
   402  type mapMeta struct{}
   403  
   404  // Map returns the Map referenced by ins, if any.
   405  // An Instruction will contain a Map if e.g. it references an existing,
   406  // pinned map that was opened during ELF loading.
   407  func (ins Instruction) Map() FDer {
   408  	fd, _ := ins.Metadata.Get(mapMeta{}).(FDer)
   409  	return fd
   410  }
   411  
   412  type sourceMeta struct{}
   413  
   414  // WithSource adds source information about the Instruction.
   415  func (ins Instruction) WithSource(src fmt.Stringer) Instruction {
   416  	ins.Metadata.Set(sourceMeta{}, src)
   417  	return ins
   418  }
   419  
   420  // Source returns source information about the Instruction. The field is
   421  // present when the compiler emits BTF line info about the Instruction and
   422  // usually contains the line of source code responsible for it.
   423  func (ins Instruction) Source() fmt.Stringer {
   424  	str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer)
   425  	return str
   426  }
   427  
   428  // A Comment can be passed to Instruction.WithSource to add a comment
   429  // to an instruction.
   430  type Comment string
   431  
   432  func (s Comment) String() string {
   433  	return string(s)
   434  }
   435  
   436  // FDer represents a resource tied to an underlying file descriptor.
   437  // Used as a stand-in for e.g. ebpf.Map since that type cannot be
   438  // imported here and FD() is the only method we rely on.
   439  type FDer interface {
   440  	FD() int
   441  }
   442  
   443  // Instructions is an eBPF program.
   444  type Instructions []Instruction
   445  
   446  // Unmarshal unmarshals an Instructions from a binary instruction stream.
   447  // All instructions in insns are replaced by instructions decoded from r.
   448  func (insns *Instructions) Unmarshal(r io.Reader, bo binary.ByteOrder) error {
   449  	if len(*insns) > 0 {
   450  		*insns = nil
   451  	}
   452  
   453  	var offset uint64
   454  	for {
   455  		var ins Instruction
   456  		n, err := ins.Unmarshal(r, bo)
   457  		if errors.Is(err, io.EOF) {
   458  			break
   459  		}
   460  		if err != nil {
   461  			return fmt.Errorf("offset %d: %w", offset, err)
   462  		}
   463  
   464  		*insns = append(*insns, ins)
   465  		offset += n
   466  	}
   467  
   468  	return nil
   469  }
   470  
   471  // Name returns the name of the function insns belongs to, if any.
   472  func (insns Instructions) Name() string {
   473  	if len(insns) == 0 {
   474  		return ""
   475  	}
   476  	return insns[0].Symbol()
   477  }
   478  
   479  func (insns Instructions) String() string {
   480  	return fmt.Sprint(insns)
   481  }
   482  
   483  // Size returns the amount of bytes insns would occupy in binary form.
   484  func (insns Instructions) Size() uint64 {
   485  	var sum uint64
   486  	for _, ins := range insns {
   487  		sum += ins.Size()
   488  	}
   489  	return sum
   490  }
   491  
   492  // AssociateMap updates all Instructions that Reference the given symbol
   493  // to point to an existing Map m instead.
   494  //
   495  // Returns ErrUnreferencedSymbol error if no references to symbol are found
   496  // in insns. If symbol is anything else than the symbol name of map (e.g.
   497  // a bpf2bpf subprogram), an error is returned.
   498  func (insns Instructions) AssociateMap(symbol string, m FDer) error {
   499  	if symbol == "" {
   500  		return errors.New("empty symbol")
   501  	}
   502  
   503  	var found bool
   504  	for i := range insns {
   505  		ins := &insns[i]
   506  		if ins.Reference() != symbol {
   507  			continue
   508  		}
   509  
   510  		if err := ins.AssociateMap(m); err != nil {
   511  			return err
   512  		}
   513  
   514  		found = true
   515  	}
   516  
   517  	if !found {
   518  		return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
   519  	}
   520  
   521  	return nil
   522  }
   523  
   524  // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
   525  //
   526  // Returns ErrUnreferencedSymbol if the symbol isn't used.
   527  //
   528  // Deprecated: use AssociateMap instead.
   529  func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
   530  	if symbol == "" {
   531  		return errors.New("empty symbol")
   532  	}
   533  
   534  	var found bool
   535  	for i := range insns {
   536  		ins := &insns[i]
   537  		if ins.Reference() != symbol {
   538  			continue
   539  		}
   540  
   541  		if !ins.IsLoadFromMap() {
   542  			return errors.New("not a load from a map")
   543  		}
   544  
   545  		ins.encodeMapFD(fd)
   546  
   547  		found = true
   548  	}
   549  
   550  	if !found {
   551  		return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
   552  	}
   553  
   554  	return nil
   555  }
   556  
   557  // SymbolOffsets returns the set of symbols and their offset in
   558  // the instructions.
   559  func (insns Instructions) SymbolOffsets() (map[string]int, error) {
   560  	offsets := make(map[string]int)
   561  
   562  	for i, ins := range insns {
   563  		if ins.Symbol() == "" {
   564  			continue
   565  		}
   566  
   567  		if _, ok := offsets[ins.Symbol()]; ok {
   568  			return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol())
   569  		}
   570  
   571  		offsets[ins.Symbol()] = i
   572  	}
   573  
   574  	return offsets, nil
   575  }
   576  
   577  // FunctionReferences returns a set of symbol names these Instructions make
   578  // bpf-to-bpf calls to.
   579  func (insns Instructions) FunctionReferences() []string {
   580  	calls := make(map[string]struct{})
   581  	for _, ins := range insns {
   582  		if ins.Constant != -1 {
   583  			// BPF-to-BPF calls have -1 constants.
   584  			continue
   585  		}
   586  
   587  		if ins.Reference() == "" {
   588  			continue
   589  		}
   590  
   591  		if !ins.IsFunctionReference() {
   592  			continue
   593  		}
   594  
   595  		calls[ins.Reference()] = struct{}{}
   596  	}
   597  
   598  	result := make([]string, 0, len(calls))
   599  	for call := range calls {
   600  		result = append(result, call)
   601  	}
   602  
   603  	sort.Strings(result)
   604  	return result
   605  }
   606  
   607  // ReferenceOffsets returns the set of references and their offset in
   608  // the instructions.
   609  func (insns Instructions) ReferenceOffsets() map[string][]int {
   610  	offsets := make(map[string][]int)
   611  
   612  	for i, ins := range insns {
   613  		if ins.Reference() == "" {
   614  			continue
   615  		}
   616  
   617  		offsets[ins.Reference()] = append(offsets[ins.Reference()], i)
   618  	}
   619  
   620  	return offsets
   621  }
   622  
   623  // Format implements fmt.Formatter.
   624  //
   625  // You can control indentation of symbols by
   626  // specifying a width. Setting a precision controls the indentation of
   627  // instructions.
   628  // The default character is a tab, which can be overridden by specifying
   629  // the ' ' space flag.
   630  func (insns Instructions) Format(f fmt.State, c rune) {
   631  	if c != 's' && c != 'v' {
   632  		fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c)
   633  		return
   634  	}
   635  
   636  	// Precision is better in this case, because it allows
   637  	// specifying 0 padding easily.
   638  	padding, ok := f.Precision()
   639  	if !ok {
   640  		padding = 1
   641  	}
   642  
   643  	indent := strings.Repeat("\t", padding)
   644  	if f.Flag(' ') {
   645  		indent = strings.Repeat(" ", padding)
   646  	}
   647  
   648  	symPadding, ok := f.Width()
   649  	if !ok {
   650  		symPadding = padding - 1
   651  	}
   652  	if symPadding < 0 {
   653  		symPadding = 0
   654  	}
   655  
   656  	symIndent := strings.Repeat("\t", symPadding)
   657  	if f.Flag(' ') {
   658  		symIndent = strings.Repeat(" ", symPadding)
   659  	}
   660  
   661  	// Guess how many digits we need at most, by assuming that all instructions
   662  	// are double wide.
   663  	highestOffset := len(insns) * 2
   664  	offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset))))
   665  
   666  	iter := insns.Iterate()
   667  	for iter.Next() {
   668  		if iter.Ins.Symbol() != "" {
   669  			fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol())
   670  		}
   671  		if src := iter.Ins.Source(); src != nil {
   672  			line := strings.TrimSpace(src.String())
   673  			if line != "" {
   674  				fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line)
   675  			}
   676  		}
   677  		fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins)
   678  	}
   679  }
   680  
   681  // Marshal encodes a BPF program into the kernel format.
   682  //
   683  // insns may be modified if there are unresolved jumps or bpf2bpf calls.
   684  //
   685  // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
   686  // without a matching Symbol Instruction within insns.
   687  func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
   688  	if err := insns.encodeFunctionReferences(); err != nil {
   689  		return err
   690  	}
   691  
   692  	if err := insns.encodeMapPointers(); err != nil {
   693  		return err
   694  	}
   695  
   696  	for i, ins := range insns {
   697  		if _, err := ins.Marshal(w, bo); err != nil {
   698  			return fmt.Errorf("instruction %d: %w", i, err)
   699  		}
   700  	}
   701  	return nil
   702  }
   703  
   704  // Tag calculates the kernel tag for a series of instructions.
   705  //
   706  // It mirrors bpf_prog_calc_tag in the kernel and so can be compared
   707  // to ProgramInfo.Tag to figure out whether a loaded program matches
   708  // certain instructions.
   709  func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
   710  	h := sha1.New()
   711  	for i, ins := range insns {
   712  		if ins.IsLoadFromMap() {
   713  			ins.Constant = 0
   714  		}
   715  		_, err := ins.Marshal(h, bo)
   716  		if err != nil {
   717  			return "", fmt.Errorf("instruction %d: %w", i, err)
   718  		}
   719  	}
   720  	return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil
   721  }
   722  
   723  // encodeFunctionReferences populates the Offset (or Constant, depending on
   724  // the instruction type) field of instructions with a Reference field to point
   725  // to the offset of the corresponding instruction with a matching Symbol field.
   726  //
   727  // Only Reference Instructions that are either jumps or BPF function references
   728  // (calls or function pointer loads) are populated.
   729  //
   730  // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
   731  // without at least one corresponding Symbol Instruction within insns.
   732  func (insns Instructions) encodeFunctionReferences() error {
   733  	// Index the offsets of instructions tagged as a symbol.
   734  	symbolOffsets := make(map[string]RawInstructionOffset)
   735  	iter := insns.Iterate()
   736  	for iter.Next() {
   737  		ins := iter.Ins
   738  
   739  		if ins.Symbol() == "" {
   740  			continue
   741  		}
   742  
   743  		if _, ok := symbolOffsets[ins.Symbol()]; ok {
   744  			return fmt.Errorf("duplicate symbol %s", ins.Symbol())
   745  		}
   746  
   747  		symbolOffsets[ins.Symbol()] = iter.Offset
   748  	}
   749  
   750  	// Find all instructions tagged as references to other symbols.
   751  	// Depending on the instruction type, populate their constant or offset
   752  	// fields to point to the symbol they refer to within the insn stream.
   753  	iter = insns.Iterate()
   754  	for iter.Next() {
   755  		i := iter.Index
   756  		offset := iter.Offset
   757  		ins := iter.Ins
   758  
   759  		if ins.Reference() == "" {
   760  			continue
   761  		}
   762  
   763  		switch {
   764  		case ins.IsFunctionReference() && ins.Constant == -1:
   765  			symOffset, ok := symbolOffsets[ins.Reference()]
   766  			if !ok {
   767  				return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
   768  			}
   769  
   770  			ins.Constant = int64(symOffset - offset - 1)
   771  
   772  		case ins.OpCode.Class().IsJump() && ins.Offset == -1:
   773  			symOffset, ok := symbolOffsets[ins.Reference()]
   774  			if !ok {
   775  				return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
   776  			}
   777  
   778  			ins.Offset = int16(symOffset - offset - 1)
   779  		}
   780  	}
   781  
   782  	return nil
   783  }
   784  
   785  // encodeMapPointers finds all Map Instructions and encodes their FDs
   786  // into their Constant fields.
   787  func (insns Instructions) encodeMapPointers() error {
   788  	iter := insns.Iterate()
   789  	for iter.Next() {
   790  		ins := iter.Ins
   791  
   792  		if !ins.IsLoadFromMap() {
   793  			continue
   794  		}
   795  
   796  		m := ins.Map()
   797  		if m == nil {
   798  			continue
   799  		}
   800  
   801  		fd := m.FD()
   802  		if fd < 0 {
   803  			return fmt.Errorf("map %s: %w", m, sys.ErrClosedFd)
   804  		}
   805  
   806  		ins.encodeMapFD(m.FD())
   807  	}
   808  
   809  	return nil
   810  }
   811  
   812  // Iterate allows iterating a BPF program while keeping track of
   813  // various offsets.
   814  //
   815  // Modifying the instruction slice will lead to undefined behaviour.
   816  func (insns Instructions) Iterate() *InstructionIterator {
   817  	return &InstructionIterator{insns: insns}
   818  }
   819  
   820  // InstructionIterator iterates over a BPF program.
   821  type InstructionIterator struct {
   822  	insns Instructions
   823  	// The instruction in question.
   824  	Ins *Instruction
   825  	// The index of the instruction in the original instruction slice.
   826  	Index int
   827  	// The offset of the instruction in raw BPF instructions. This accounts
   828  	// for double-wide instructions.
   829  	Offset RawInstructionOffset
   830  }
   831  
   832  // Next returns true as long as there are any instructions remaining.
   833  func (iter *InstructionIterator) Next() bool {
   834  	if len(iter.insns) == 0 {
   835  		return false
   836  	}
   837  
   838  	if iter.Ins != nil {
   839  		iter.Index++
   840  		iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions())
   841  	}
   842  	iter.Ins = &iter.insns[0]
   843  	iter.insns = iter.insns[1:]
   844  	return true
   845  }
   846  
   847  type bpfRegisters uint8
   848  
   849  func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
   850  	switch bo {
   851  	case binary.LittleEndian:
   852  		return bpfRegisters((src << 4) | (dst & 0xF)), nil
   853  	case binary.BigEndian:
   854  		return bpfRegisters((dst << 4) | (src & 0xF)), nil
   855  	default:
   856  		return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
   857  	}
   858  }
   859  
   860  // IsUnreferencedSymbol returns true if err was caused by
   861  // an unreferenced symbol.
   862  //
   863  // Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol).
   864  func IsUnreferencedSymbol(err error) bool {
   865  	return errors.Is(err, ErrUnreferencedSymbol)
   866  }