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