gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

instruction.go (22359B)


      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 }