...

Source file src/github.com/cilium/ebpf/asm/instruction.go

Documentation: github.com/cilium/ebpf/asm

     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  type symbolMeta struct{}
   358  
   359  // WithSymbol marks the Instruction as a Symbol, which other Instructions
   360  // can point to using corresponding calls to WithReference.
   361  func (ins Instruction) WithSymbol(name string) Instruction {
   362  	ins.Metadata.Set(symbolMeta{}, name)
   363  	return ins
   364  }
   365  
   366  // Sym creates a symbol.
   367  //
   368  // Deprecated: use WithSymbol instead.
   369  func (ins Instruction) Sym(name string) Instruction {
   370  	return ins.WithSymbol(name)
   371  }
   372  
   373  // Symbol returns the value ins has been marked with using WithSymbol,
   374  // otherwise returns an empty string. A symbol is often an Instruction
   375  // at the start of a function body.
   376  func (ins Instruction) Symbol() string {
   377  	sym, _ := ins.Metadata.Get(symbolMeta{}).(string)
   378  	return sym
   379  }
   380  
   381  type referenceMeta struct{}
   382  
   383  // WithReference makes ins reference another Symbol or map by name.
   384  func (ins Instruction) WithReference(ref string) Instruction {
   385  	ins.Metadata.Set(referenceMeta{}, ref)
   386  	return ins
   387  }
   388  
   389  // Reference returns the Symbol or map name referenced by ins, if any.
   390  func (ins Instruction) Reference() string {
   391  	ref, _ := ins.Metadata.Get(referenceMeta{}).(string)
   392  	return ref
   393  }
   394  
   395  type mapMeta struct{}
   396  
   397  // Map returns the Map referenced by ins, if any.
   398  // An Instruction will contain a Map if e.g. it references an existing,
   399  // pinned map that was opened during ELF loading.
   400  func (ins Instruction) Map() FDer {
   401  	fd, _ := ins.Metadata.Get(mapMeta{}).(FDer)
   402  	return fd
   403  }
   404  
   405  type sourceMeta struct{}
   406  
   407  // WithSource adds source information about the Instruction.
   408  func (ins Instruction) WithSource(src fmt.Stringer) Instruction {
   409  	ins.Metadata.Set(sourceMeta{}, src)
   410  	return ins
   411  }
   412  
   413  // Source returns source information about the Instruction. The field is
   414  // present when the compiler emits BTF line info about the Instruction and
   415  // usually contains the line of source code responsible for it.
   416  func (ins Instruction) Source() fmt.Stringer {
   417  	str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer)
   418  	return str
   419  }
   420  
   421  // A Comment can be passed to Instruction.WithSource to add a comment
   422  // to an instruction.
   423  type Comment string
   424  
   425  func (s Comment) String() string {
   426  	return string(s)
   427  }
   428  
   429  // FDer represents a resource tied to an underlying file descriptor.
   430  // Used as a stand-in for e.g. ebpf.Map since that type cannot be
   431  // imported here and FD() is the only method we rely on.
   432  type FDer interface {
   433  	FD() int
   434  }
   435  
   436  // Instructions is an eBPF program.
   437  type Instructions []Instruction
   438  
   439  // Unmarshal unmarshals an Instructions from a binary instruction stream.
   440  // All instructions in insns are replaced by instructions decoded from r.
   441  func (insns *Instructions) Unmarshal(r io.Reader, bo binary.ByteOrder) error {
   442  	if len(*insns) > 0 {
   443  		*insns = nil
   444  	}
   445  
   446  	var offset uint64
   447  	for {
   448  		var ins Instruction
   449  		n, err := ins.Unmarshal(r, bo)
   450  		if errors.Is(err, io.EOF) {
   451  			break
   452  		}
   453  		if err != nil {
   454  			return fmt.Errorf("offset %d: %w", offset, err)
   455  		}
   456  
   457  		*insns = append(*insns, ins)
   458  		offset += n
   459  	}
   460  
   461  	return nil
   462  }
   463  
   464  // Name returns the name of the function insns belongs to, if any.
   465  func (insns Instructions) Name() string {
   466  	if len(insns) == 0 {
   467  		return ""
   468  	}
   469  	return insns[0].Symbol()
   470  }
   471  
   472  func (insns Instructions) String() string {
   473  	return fmt.Sprint(insns)
   474  }
   475  
   476  // Size returns the amount of bytes insns would occupy in binary form.
   477  func (insns Instructions) Size() uint64 {
   478  	var sum uint64
   479  	for _, ins := range insns {
   480  		sum += ins.Size()
   481  	}
   482  	return sum
   483  }
   484  
   485  // AssociateMap updates all Instructions that Reference the given symbol
   486  // to point to an existing Map m instead.
   487  //
   488  // Returns ErrUnreferencedSymbol error if no references to symbol are found
   489  // in insns. If symbol is anything else than the symbol name of map (e.g.
   490  // a bpf2bpf subprogram), an error is returned.
   491  func (insns Instructions) AssociateMap(symbol string, m FDer) error {
   492  	if symbol == "" {
   493  		return errors.New("empty symbol")
   494  	}
   495  
   496  	var found bool
   497  	for i := range insns {
   498  		ins := &insns[i]
   499  		if ins.Reference() != symbol {
   500  			continue
   501  		}
   502  
   503  		if err := ins.AssociateMap(m); err != nil {
   504  			return err
   505  		}
   506  
   507  		found = true
   508  	}
   509  
   510  	if !found {
   511  		return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
   512  	}
   513  
   514  	return nil
   515  }
   516  
   517  // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
   518  //
   519  // Returns ErrUnreferencedSymbol if the symbol isn't used.
   520  //
   521  // Deprecated: use AssociateMap instead.
   522  func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
   523  	if symbol == "" {
   524  		return errors.New("empty symbol")
   525  	}
   526  
   527  	var found bool
   528  	for i := range insns {
   529  		ins := &insns[i]
   530  		if ins.Reference() != symbol {
   531  			continue
   532  		}
   533  
   534  		if !ins.IsLoadFromMap() {
   535  			return errors.New("not a load from a map")
   536  		}
   537  
   538  		ins.encodeMapFD(fd)
   539  
   540  		found = true
   541  	}
   542  
   543  	if !found {
   544  		return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
   545  	}
   546  
   547  	return nil
   548  }
   549  
   550  // SymbolOffsets returns the set of symbols and their offset in
   551  // the instructions.
   552  func (insns Instructions) SymbolOffsets() (map[string]int, error) {
   553  	offsets := make(map[string]int)
   554  
   555  	for i, ins := range insns {
   556  		if ins.Symbol() == "" {
   557  			continue
   558  		}
   559  
   560  		if _, ok := offsets[ins.Symbol()]; ok {
   561  			return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol())
   562  		}
   563  
   564  		offsets[ins.Symbol()] = i
   565  	}
   566  
   567  	return offsets, nil
   568  }
   569  
   570  // FunctionReferences returns a set of symbol names these Instructions make
   571  // bpf-to-bpf calls to.
   572  func (insns Instructions) FunctionReferences() []string {
   573  	calls := make(map[string]struct{})
   574  	for _, ins := range insns {
   575  		if ins.Constant != -1 {
   576  			// BPF-to-BPF calls have -1 constants.
   577  			continue
   578  		}
   579  
   580  		if ins.Reference() == "" {
   581  			continue
   582  		}
   583  
   584  		if !ins.IsFunctionReference() {
   585  			continue
   586  		}
   587  
   588  		calls[ins.Reference()] = struct{}{}
   589  	}
   590  
   591  	result := make([]string, 0, len(calls))
   592  	for call := range calls {
   593  		result = append(result, call)
   594  	}
   595  
   596  	sort.Strings(result)
   597  	return result
   598  }
   599  
   600  // ReferenceOffsets returns the set of references and their offset in
   601  // the instructions.
   602  func (insns Instructions) ReferenceOffsets() map[string][]int {
   603  	offsets := make(map[string][]int)
   604  
   605  	for i, ins := range insns {
   606  		if ins.Reference() == "" {
   607  			continue
   608  		}
   609  
   610  		offsets[ins.Reference()] = append(offsets[ins.Reference()], i)
   611  	}
   612  
   613  	return offsets
   614  }
   615  
   616  // Format implements fmt.Formatter.
   617  //
   618  // You can control indentation of symbols by
   619  // specifying a width. Setting a precision controls the indentation of
   620  // instructions.
   621  // The default character is a tab, which can be overridden by specifying
   622  // the ' ' space flag.
   623  func (insns Instructions) Format(f fmt.State, c rune) {
   624  	if c != 's' && c != 'v' {
   625  		fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c)
   626  		return
   627  	}
   628  
   629  	// Precision is better in this case, because it allows
   630  	// specifying 0 padding easily.
   631  	padding, ok := f.Precision()
   632  	if !ok {
   633  		padding = 1
   634  	}
   635  
   636  	indent := strings.Repeat("\t", padding)
   637  	if f.Flag(' ') {
   638  		indent = strings.Repeat(" ", padding)
   639  	}
   640  
   641  	symPadding, ok := f.Width()
   642  	if !ok {
   643  		symPadding = padding - 1
   644  	}
   645  	if symPadding < 0 {
   646  		symPadding = 0
   647  	}
   648  
   649  	symIndent := strings.Repeat("\t", symPadding)
   650  	if f.Flag(' ') {
   651  		symIndent = strings.Repeat(" ", symPadding)
   652  	}
   653  
   654  	// Guess how many digits we need at most, by assuming that all instructions
   655  	// are double wide.
   656  	highestOffset := len(insns) * 2
   657  	offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset))))
   658  
   659  	iter := insns.Iterate()
   660  	for iter.Next() {
   661  		if iter.Ins.Symbol() != "" {
   662  			fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol())
   663  		}
   664  		if src := iter.Ins.Source(); src != nil {
   665  			line := strings.TrimSpace(src.String())
   666  			if line != "" {
   667  				fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line)
   668  			}
   669  		}
   670  		fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins)
   671  	}
   672  }
   673  
   674  // Marshal encodes a BPF program into the kernel format.
   675  //
   676  // insns may be modified if there are unresolved jumps or bpf2bpf calls.
   677  //
   678  // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
   679  // without a matching Symbol Instruction within insns.
   680  func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
   681  	if err := insns.encodeFunctionReferences(); err != nil {
   682  		return err
   683  	}
   684  
   685  	if err := insns.encodeMapPointers(); err != nil {
   686  		return err
   687  	}
   688  
   689  	for i, ins := range insns {
   690  		if _, err := ins.Marshal(w, bo); err != nil {
   691  			return fmt.Errorf("instruction %d: %w", i, err)
   692  		}
   693  	}
   694  	return nil
   695  }
   696  
   697  // Tag calculates the kernel tag for a series of instructions.
   698  //
   699  // It mirrors bpf_prog_calc_tag in the kernel and so can be compared
   700  // to ProgramInfo.Tag to figure out whether a loaded program matches
   701  // certain instructions.
   702  func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
   703  	h := sha1.New()
   704  	for i, ins := range insns {
   705  		if ins.IsLoadFromMap() {
   706  			ins.Constant = 0
   707  		}
   708  		_, err := ins.Marshal(h, bo)
   709  		if err != nil {
   710  			return "", fmt.Errorf("instruction %d: %w", i, err)
   711  		}
   712  	}
   713  	return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil
   714  }
   715  
   716  // encodeFunctionReferences populates the Offset (or Constant, depending on
   717  // the instruction type) field of instructions with a Reference field to point
   718  // to the offset of the corresponding instruction with a matching Symbol field.
   719  //
   720  // Only Reference Instructions that are either jumps or BPF function references
   721  // (calls or function pointer loads) are populated.
   722  //
   723  // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
   724  // without at least one corresponding Symbol Instruction within insns.
   725  func (insns Instructions) encodeFunctionReferences() error {
   726  	// Index the offsets of instructions tagged as a symbol.
   727  	symbolOffsets := make(map[string]RawInstructionOffset)
   728  	iter := insns.Iterate()
   729  	for iter.Next() {
   730  		ins := iter.Ins
   731  
   732  		if ins.Symbol() == "" {
   733  			continue
   734  		}
   735  
   736  		if _, ok := symbolOffsets[ins.Symbol()]; ok {
   737  			return fmt.Errorf("duplicate symbol %s", ins.Symbol())
   738  		}
   739  
   740  		symbolOffsets[ins.Symbol()] = iter.Offset
   741  	}
   742  
   743  	// Find all instructions tagged as references to other symbols.
   744  	// Depending on the instruction type, populate their constant or offset
   745  	// fields to point to the symbol they refer to within the insn stream.
   746  	iter = insns.Iterate()
   747  	for iter.Next() {
   748  		i := iter.Index
   749  		offset := iter.Offset
   750  		ins := iter.Ins
   751  
   752  		if ins.Reference() == "" {
   753  			continue
   754  		}
   755  
   756  		switch {
   757  		case ins.IsFunctionReference() && ins.Constant == -1:
   758  			symOffset, ok := symbolOffsets[ins.Reference()]
   759  			if !ok {
   760  				return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
   761  			}
   762  
   763  			ins.Constant = int64(symOffset - offset - 1)
   764  
   765  		case ins.OpCode.Class().IsJump() && ins.Offset == -1:
   766  			symOffset, ok := symbolOffsets[ins.Reference()]
   767  			if !ok {
   768  				return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
   769  			}
   770  
   771  			ins.Offset = int16(symOffset - offset - 1)
   772  		}
   773  	}
   774  
   775  	return nil
   776  }
   777  
   778  // encodeMapPointers finds all Map Instructions and encodes their FDs
   779  // into their Constant fields.
   780  func (insns Instructions) encodeMapPointers() error {
   781  	iter := insns.Iterate()
   782  	for iter.Next() {
   783  		ins := iter.Ins
   784  
   785  		if !ins.IsLoadFromMap() {
   786  			continue
   787  		}
   788  
   789  		m := ins.Map()
   790  		if m == nil {
   791  			continue
   792  		}
   793  
   794  		fd := m.FD()
   795  		if fd < 0 {
   796  			return fmt.Errorf("map %s: %w", m, sys.ErrClosedFd)
   797  		}
   798  
   799  		ins.encodeMapFD(m.FD())
   800  	}
   801  
   802  	return nil
   803  }
   804  
   805  // Iterate allows iterating a BPF program while keeping track of
   806  // various offsets.
   807  //
   808  // Modifying the instruction slice will lead to undefined behaviour.
   809  func (insns Instructions) Iterate() *InstructionIterator {
   810  	return &InstructionIterator{insns: insns}
   811  }
   812  
   813  // InstructionIterator iterates over a BPF program.
   814  type InstructionIterator struct {
   815  	insns Instructions
   816  	// The instruction in question.
   817  	Ins *Instruction
   818  	// The index of the instruction in the original instruction slice.
   819  	Index int
   820  	// The offset of the instruction in raw BPF instructions. This accounts
   821  	// for double-wide instructions.
   822  	Offset RawInstructionOffset
   823  }
   824  
   825  // Next returns true as long as there are any instructions remaining.
   826  func (iter *InstructionIterator) Next() bool {
   827  	if len(iter.insns) == 0 {
   828  		return false
   829  	}
   830  
   831  	if iter.Ins != nil {
   832  		iter.Index++
   833  		iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions())
   834  	}
   835  	iter.Ins = &iter.insns[0]
   836  	iter.insns = iter.insns[1:]
   837  	return true
   838  }
   839  
   840  type bpfRegisters uint8
   841  
   842  func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
   843  	switch bo {
   844  	case binary.LittleEndian:
   845  		return bpfRegisters((src << 4) | (dst & 0xF)), nil
   846  	case binary.BigEndian:
   847  		return bpfRegisters((dst << 4) | (src & 0xF)), nil
   848  	default:
   849  		return 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
   850  	}
   851  }
   852  
   853  // IsUnreferencedSymbol returns true if err was caused by
   854  // an unreferenced symbol.
   855  //
   856  // Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol).
   857  func IsUnreferencedSymbol(err error) bool {
   858  	return errors.Is(err, ErrUnreferencedSymbol)
   859  }
   860  

View as plain text