summaryrefslogtreecommitdiff
path: root/vendor/github.com/cilium/ebpf/asm/instruction.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/cilium/ebpf/asm/instruction.go')
-rw-r--r--vendor/github.com/cilium/ebpf/asm/instruction.go581
1 files changed, 471 insertions, 110 deletions
diff --git a/vendor/github.com/cilium/ebpf/asm/instruction.go b/vendor/github.com/cilium/ebpf/asm/instruction.go
index 5d9d820e5..f17d88b51 100644
--- a/vendor/github.com/cilium/ebpf/asm/instruction.go
+++ b/vendor/github.com/cilium/ebpf/asm/instruction.go
@@ -8,8 +8,10 @@ import (
"fmt"
"io"
"math"
+ "sort"
"strings"
+ "github.com/cilium/ebpf/internal/sys"
"github.com/cilium/ebpf/internal/unix"
)
@@ -19,6 +21,10 @@ const InstructionSize = 8
// RawInstructionOffset is an offset in units of raw BPF instructions.
type RawInstructionOffset uint64
+var ErrUnreferencedSymbol = errors.New("unreferenced symbol")
+var ErrUnsatisfiedMapReference = errors.New("unsatisfied map reference")
+var ErrUnsatisfiedProgramReference = errors.New("unsatisfied program reference")
+
// Bytes returns the offset of an instruction in bytes.
func (rio RawInstructionOffset) Bytes() uint64 {
return uint64(rio) * InstructionSize
@@ -26,50 +32,57 @@ func (rio RawInstructionOffset) Bytes() uint64 {
// Instruction is a single eBPF instruction.
type Instruction struct {
- OpCode OpCode
- Dst Register
- Src Register
- Offset int16
- Constant int64
- Reference string
- Symbol string
-}
-
-// Sym creates a symbol.
-func (ins Instruction) Sym(name string) Instruction {
- ins.Symbol = name
- return ins
+ OpCode OpCode
+ Dst Register
+ Src Register
+ Offset int16
+ Constant int64
+
+ // Metadata contains optional metadata about this instruction.
+ Metadata Metadata
}
// Unmarshal decodes a BPF instruction.
func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) {
- var bi bpfInstruction
- err := binary.Read(r, bo, &bi)
- if err != nil {
+ data := make([]byte, InstructionSize)
+ if _, err := io.ReadFull(r, data); err != nil {
return 0, err
}
- ins.OpCode = bi.OpCode
- ins.Offset = bi.Offset
- ins.Constant = int64(bi.Constant)
- ins.Dst, ins.Src, err = bi.Registers.Unmarshal(bo)
- if err != nil {
- return 0, fmt.Errorf("can't unmarshal registers: %s", err)
+ ins.OpCode = OpCode(data[0])
+
+ regs := data[1]
+ switch bo {
+ case binary.LittleEndian:
+ ins.Dst, ins.Src = Register(regs&0xF), Register(regs>>4)
+ case binary.BigEndian:
+ ins.Dst, ins.Src = Register(regs>>4), Register(regs&0xf)
}
- if !bi.OpCode.isDWordLoad() {
+ ins.Offset = int16(bo.Uint16(data[2:4]))
+ // Convert to int32 before widening to int64
+ // to ensure the signed bit is carried over.
+ ins.Constant = int64(int32(bo.Uint32(data[4:8])))
+
+ if !ins.OpCode.IsDWordLoad() {
return InstructionSize, nil
}
- var bi2 bpfInstruction
- if err := binary.Read(r, bo, &bi2); err != nil {
+ // Pull another instruction from the stream to retrieve the second
+ // half of the 64-bit immediate value.
+ if _, err := io.ReadFull(r, data); err != nil {
// No Wrap, to avoid io.EOF clash
return 0, errors.New("64bit immediate is missing second half")
}
- if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 {
+
+ // Require that all fields other than the value are zero.
+ if bo.Uint32(data[0:4]) != 0 {
return 0, errors.New("64bit immediate has non-zero fields")
}
- ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant)))
+
+ cons1 := uint32(ins.Constant)
+ cons2 := int32(bo.Uint32(data[4:8]))
+ ins.Constant = int64(cons2)<<32 | int64(cons1)
return 2 * InstructionSize, nil
}
@@ -80,7 +93,7 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
return 0, errors.New("invalid opcode")
}
- isDWordLoad := ins.OpCode.isDWordLoad()
+ isDWordLoad := ins.OpCode.IsDWordLoad()
cons := int32(ins.Constant)
if isDWordLoad {
@@ -93,14 +106,12 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
return 0, fmt.Errorf("can't marshal registers: %s", err)
}
- bpfi := bpfInstruction{
- ins.OpCode,
- regs,
- ins.Offset,
- cons,
- }
-
- if err := binary.Write(w, bo, &bpfi); err != nil {
+ data := make([]byte, InstructionSize)
+ data[0] = byte(ins.OpCode)
+ data[1] = byte(regs)
+ bo.PutUint16(data[2:4], uint16(ins.Offset))
+ bo.PutUint32(data[4:8], uint32(cons))
+ if _, err := w.Write(data); err != nil {
return 0, err
}
@@ -108,45 +119,83 @@ func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error)
return InstructionSize, nil
}
- bpfi = bpfInstruction{
- Constant: int32(ins.Constant >> 32),
- }
-
- if err := binary.Write(w, bo, &bpfi); err != nil {
+ // The first half of the second part of a double-wide instruction
+ // must be zero. The second half carries the value.
+ bo.PutUint32(data[0:4], 0)
+ bo.PutUint32(data[4:8], uint32(ins.Constant>>32))
+ if _, err := w.Write(data); err != nil {
return 0, err
}
return 2 * InstructionSize, nil
}
+// AssociateMap associates a Map with this Instruction.
+//
+// Implicitly clears the Instruction's Reference field.
+//
+// Returns an error if the Instruction is not a map load.
+func (ins *Instruction) AssociateMap(m FDer) error {
+ if !ins.IsLoadFromMap() {
+ return errors.New("not a load from a map")
+ }
+
+ ins.Metadata.Set(referenceMeta{}, nil)
+ ins.Metadata.Set(mapMeta{}, m)
+
+ return nil
+}
+
// RewriteMapPtr changes an instruction to use a new map fd.
//
// Returns an error if the instruction doesn't load a map.
+//
+// Deprecated: use AssociateMap instead. If you cannot provide a Map,
+// wrap an fd in a type implementing FDer.
func (ins *Instruction) RewriteMapPtr(fd int) error {
- if !ins.OpCode.isDWordLoad() {
- return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
- }
-
- if ins.Src != PseudoMapFD && ins.Src != PseudoMapValue {
+ if !ins.IsLoadFromMap() {
return errors.New("not a load from a map")
}
+ ins.encodeMapFD(fd)
+
+ return nil
+}
+
+func (ins *Instruction) encodeMapFD(fd int) {
// Preserve the offset value for direct map loads.
offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
rawFd := uint64(uint32(fd))
ins.Constant = int64(offset | rawFd)
- return nil
}
-func (ins *Instruction) mapPtr() uint32 {
- return uint32(uint64(ins.Constant) & math.MaxUint32)
+// MapPtr returns the map fd for this instruction.
+//
+// The result is undefined if the instruction is not a load from a map,
+// see IsLoadFromMap.
+//
+// Deprecated: use Map() instead.
+func (ins *Instruction) MapPtr() int {
+ // If there is a map associated with the instruction, return its FD.
+ if fd := ins.Metadata.Get(mapMeta{}); fd != nil {
+ return fd.(FDer).FD()
+ }
+
+ // Fall back to the fd stored in the Constant field
+ return ins.mapFd()
+}
+
+// mapFd returns the map file descriptor stored in the 32 least significant
+// bits of ins' Constant field.
+func (ins *Instruction) mapFd() int {
+ return int(int32(ins.Constant))
}
// RewriteMapOffset changes the offset of a direct load from a map.
//
// Returns an error if the instruction is not a direct load.
func (ins *Instruction) RewriteMapOffset(offset uint32) error {
- if !ins.OpCode.isDWordLoad() {
+ if !ins.OpCode.IsDWordLoad() {
return fmt.Errorf("%s is not a 64 bit load", ins.OpCode)
}
@@ -163,10 +212,10 @@ func (ins *Instruction) mapOffset() uint32 {
return uint32(uint64(ins.Constant) >> 32)
}
-// isLoadFromMap returns true if the instruction loads from a map.
+// IsLoadFromMap returns true if the instruction loads from a map.
//
// This covers both loading the map pointer and direct map value loads.
-func (ins *Instruction) isLoadFromMap() bool {
+func (ins *Instruction) IsLoadFromMap() bool {
return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
}
@@ -177,6 +226,29 @@ func (ins *Instruction) IsFunctionCall() bool {
return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
}
+// IsLoadOfFunctionPointer returns true if the instruction loads a function pointer.
+func (ins *Instruction) IsLoadOfFunctionPointer() bool {
+ return ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc
+}
+
+// IsFunctionReference returns true if the instruction references another BPF
+// function, either by invoking a Call jump operation or by loading a function
+// pointer.
+func (ins *Instruction) IsFunctionReference() bool {
+ return ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer()
+}
+
+// IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call.
+func (ins *Instruction) IsBuiltinCall() bool {
+ return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0
+}
+
+// IsConstantLoad returns true if the instruction loads a constant of the
+// given size.
+func (ins *Instruction) IsConstantLoad(size Size) bool {
+ return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0
+}
+
// Format implements fmt.Formatter.
func (ins Instruction) Format(f fmt.State, c rune) {
if c != 'v' {
@@ -197,22 +269,31 @@ func (ins Instruction) Format(f fmt.State, c rune) {
return
}
- if ins.isLoadFromMap() {
- fd := int32(ins.mapPtr())
+ if ins.IsLoadFromMap() {
+ fd := ins.mapFd()
+ m := ins.Map()
switch ins.Src {
case PseudoMapFD:
- fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd)
+ if m != nil {
+ fmt.Fprintf(f, "LoadMapPtr dst: %s map: %s", ins.Dst, m)
+ } else {
+ fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd)
+ }
case PseudoMapValue:
- fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset())
+ if m != nil {
+ fmt.Fprintf(f, "LoadMapValue dst: %s, map: %s off: %d", ins.Dst, m, ins.mapOffset())
+ } else {
+ fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset())
+ }
}
goto ref
}
fmt.Fprintf(f, "%v ", op)
- switch cls := op.Class(); cls {
- case LdClass, LdXClass, StClass, StXClass:
+ switch cls := op.Class(); {
+ case cls.isLoadOrStore():
switch op.Mode() {
case ImmMode:
fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant)
@@ -226,7 +307,7 @@ func (ins Instruction) Format(f fmt.State, c rune) {
fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src)
}
- case ALU64Class, ALUClass:
+ case cls.IsALU():
fmt.Fprintf(f, "dst: %s ", ins.Dst)
if op.ALUOp() == Swap || op.Source() == ImmSource {
fmt.Fprintf(f, "imm: %d", ins.Constant)
@@ -234,7 +315,7 @@ func (ins Instruction) Format(f fmt.State, c rune) {
fmt.Fprintf(f, "src: %s", ins.Src)
}
- case JumpClass:
+ case cls.IsJump():
switch jop := op.JumpOp(); jop {
case Call:
if ins.Src == PseudoCall {
@@ -255,42 +336,212 @@ func (ins Instruction) Format(f fmt.State, c rune) {
}
ref:
- if ins.Reference != "" {
- fmt.Fprintf(f, " <%s>", ins.Reference)
+ if ins.Reference() != "" {
+ fmt.Fprintf(f, " <%s>", ins.Reference())
}
}
+func (ins Instruction) equal(other Instruction) bool {
+ return ins.OpCode == other.OpCode &&
+ ins.Dst == other.Dst &&
+ ins.Src == other.Src &&
+ ins.Offset == other.Offset &&
+ ins.Constant == other.Constant
+}
+
+// Size returns the amount of bytes ins would occupy in binary form.
+func (ins Instruction) Size() uint64 {
+ return uint64(InstructionSize * ins.OpCode.rawInstructions())
+}
+
+type symbolMeta struct{}
+
+// WithSymbol marks the Instruction as a Symbol, which other Instructions
+// can point to using corresponding calls to WithReference.
+func (ins Instruction) WithSymbol(name string) Instruction {
+ ins.Metadata.Set(symbolMeta{}, name)
+ return ins
+}
+
+// Sym creates a symbol.
+//
+// Deprecated: use WithSymbol instead.
+func (ins Instruction) Sym(name string) Instruction {
+ return ins.WithSymbol(name)
+}
+
+// Symbol returns the value ins has been marked with using WithSymbol,
+// otherwise returns an empty string. A symbol is often an Instruction
+// at the start of a function body.
+func (ins Instruction) Symbol() string {
+ sym, _ := ins.Metadata.Get(symbolMeta{}).(string)
+ return sym
+}
+
+type referenceMeta struct{}
+
+// WithReference makes ins reference another Symbol or map by name.
+func (ins Instruction) WithReference(ref string) Instruction {
+ ins.Metadata.Set(referenceMeta{}, ref)
+ return ins
+}
+
+// Reference returns the Symbol or map name referenced by ins, if any.
+func (ins Instruction) Reference() string {
+ ref, _ := ins.Metadata.Get(referenceMeta{}).(string)
+ return ref
+}
+
+type mapMeta struct{}
+
+// Map returns the Map referenced by ins, if any.
+// An Instruction will contain a Map if e.g. it references an existing,
+// pinned map that was opened during ELF loading.
+func (ins Instruction) Map() FDer {
+ fd, _ := ins.Metadata.Get(mapMeta{}).(FDer)
+ return fd
+}
+
+type sourceMeta struct{}
+
+// WithSource adds source information about the Instruction.
+func (ins Instruction) WithSource(src fmt.Stringer) Instruction {
+ ins.Metadata.Set(sourceMeta{}, src)
+ return ins
+}
+
+// Source returns source information about the Instruction. The field is
+// present when the compiler emits BTF line info about the Instruction and
+// usually contains the line of source code responsible for it.
+func (ins Instruction) Source() fmt.Stringer {
+ str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer)
+ return str
+}
+
+// A Comment can be passed to Instruction.WithSource to add a comment
+// to an instruction.
+type Comment string
+
+func (s Comment) String() string {
+ return string(s)
+}
+
+// FDer represents a resource tied to an underlying file descriptor.
+// Used as a stand-in for e.g. ebpf.Map since that type cannot be
+// imported here and FD() is the only method we rely on.
+type FDer interface {
+ FD() int
+}
+
// Instructions is an eBPF program.
type Instructions []Instruction
+// Unmarshal unmarshals an Instructions from a binary instruction stream.
+// All instructions in insns are replaced by instructions decoded from r.
+func (insns *Instructions) Unmarshal(r io.Reader, bo binary.ByteOrder) error {
+ if len(*insns) > 0 {
+ *insns = nil
+ }
+
+ var offset uint64
+ for {
+ var ins Instruction
+ n, err := ins.Unmarshal(r, bo)
+ if errors.Is(err, io.EOF) {
+ break
+ }
+ if err != nil {
+ return fmt.Errorf("offset %d: %w", offset, err)
+ }
+
+ *insns = append(*insns, ins)
+ offset += n
+ }
+
+ return nil
+}
+
+// Name returns the name of the function insns belongs to, if any.
+func (insns Instructions) Name() string {
+ if len(insns) == 0 {
+ return ""
+ }
+ return insns[0].Symbol()
+}
+
func (insns Instructions) String() string {
return fmt.Sprint(insns)
}
+// Size returns the amount of bytes insns would occupy in binary form.
+func (insns Instructions) Size() uint64 {
+ var sum uint64
+ for _, ins := range insns {
+ sum += ins.Size()
+ }
+ return sum
+}
+
+// AssociateMap updates all Instructions that Reference the given symbol
+// to point to an existing Map m instead.
+//
+// Returns ErrUnreferencedSymbol error if no references to symbol are found
+// in insns. If symbol is anything else than the symbol name of map (e.g.
+// a bpf2bpf subprogram), an error is returned.
+func (insns Instructions) AssociateMap(symbol string, m FDer) error {
+ if symbol == "" {
+ return errors.New("empty symbol")
+ }
+
+ var found bool
+ for i := range insns {
+ ins := &insns[i]
+ if ins.Reference() != symbol {
+ continue
+ }
+
+ if err := ins.AssociateMap(m); err != nil {
+ return err
+ }
+
+ found = true
+ }
+
+ if !found {
+ return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
+ }
+
+ return nil
+}
+
// RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
//
-// Returns an error if the symbol isn't used, see IsUnreferencedSymbol.
+// Returns ErrUnreferencedSymbol if the symbol isn't used.
+//
+// Deprecated: use AssociateMap instead.
func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
if symbol == "" {
return errors.New("empty symbol")
}
- found := false
+ var found bool
for i := range insns {
ins := &insns[i]
- if ins.Reference != symbol {
+ if ins.Reference() != symbol {
continue
}
- if err := ins.RewriteMapPtr(fd); err != nil {
- return err
+ if !ins.IsLoadFromMap() {
+ return errors.New("not a load from a map")
}
+ ins.encodeMapFD(fd)
+
found = true
}
if !found {
- return &unreferencedSymbolError{symbol}
+ return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol)
}
return nil
@@ -302,31 +553,61 @@ func (insns Instructions) SymbolOffsets() (map[string]int, error) {
offsets := make(map[string]int)
for i, ins := range insns {
- if ins.Symbol == "" {
+ if ins.Symbol() == "" {
continue
}
- if _, ok := offsets[ins.Symbol]; ok {
- return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol)
+ if _, ok := offsets[ins.Symbol()]; ok {
+ return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol())
}
- offsets[ins.Symbol] = i
+ offsets[ins.Symbol()] = i
}
return offsets, nil
}
+// FunctionReferences returns a set of symbol names these Instructions make
+// bpf-to-bpf calls to.
+func (insns Instructions) FunctionReferences() []string {
+ calls := make(map[string]struct{})
+ for _, ins := range insns {
+ if ins.Constant != -1 {
+ // BPF-to-BPF calls have -1 constants.
+ continue
+ }
+
+ if ins.Reference() == "" {
+ continue
+ }
+
+ if !ins.IsFunctionReference() {
+ continue
+ }
+
+ calls[ins.Reference()] = struct{}{}
+ }
+
+ result := make([]string, 0, len(calls))
+ for call := range calls {
+ result = append(result, call)
+ }
+
+ sort.Strings(result)
+ return result
+}
+
// ReferenceOffsets returns the set of references and their offset in
// the instructions.
func (insns Instructions) ReferenceOffsets() map[string][]int {
offsets := make(map[string][]int)
for i, ins := range insns {
- if ins.Reference == "" {
+ if ins.Reference() == "" {
continue
}
- offsets[ins.Reference] = append(offsets[ins.Reference], i)
+ offsets[ins.Reference()] = append(offsets[ins.Reference()], i)
}
return offsets
@@ -337,7 +618,7 @@ func (insns Instructions) ReferenceOffsets() map[string][]int {
// You can control indentation of symbols by
// specifying a width. Setting a precision controls the indentation of
// instructions.
-// The default character is a tab, which can be overriden by specifying
+// The default character is a tab, which can be overridden by specifying
// the ' ' space flag.
func (insns Instructions) Format(f fmt.State, c rune) {
if c != 's' && c != 'v' {
@@ -377,20 +658,36 @@ func (insns Instructions) Format(f fmt.State, c rune) {
iter := insns.Iterate()
for iter.Next() {
- if iter.Ins.Symbol != "" {
- fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol)
+ if iter.Ins.Symbol() != "" {
+ fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol())
+ }
+ if src := iter.Ins.Source(); src != nil {
+ line := strings.TrimSpace(src.String())
+ if line != "" {
+ fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line)
+ }
}
fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins)
}
-
- return
}
// Marshal encodes a BPF program into the kernel format.
+//
+// insns may be modified if there are unresolved jumps or bpf2bpf calls.
+//
+// Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
+// without a matching Symbol Instruction within insns.
func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
+ if err := insns.encodeFunctionReferences(); err != nil {
+ return err
+ }
+
+ if err := insns.encodeMapPointers(); err != nil {
+ return err
+ }
+
for i, ins := range insns {
- _, err := ins.Marshal(w, bo)
- if err != nil {
+ if _, err := ins.Marshal(w, bo); err != nil {
return fmt.Errorf("instruction %d: %w", i, err)
}
}
@@ -405,7 +702,7 @@ func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
h := sha1.New()
for i, ins := range insns {
- if ins.isLoadFromMap() {
+ if ins.IsLoadFromMap() {
ins.Constant = 0
}
_, err := ins.Marshal(h, bo)
@@ -416,6 +713,95 @@ func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) {
return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil
}
+// encodeFunctionReferences populates the Offset (or Constant, depending on
+// the instruction type) field of instructions with a Reference field to point
+// to the offset of the corresponding instruction with a matching Symbol field.
+//
+// Only Reference Instructions that are either jumps or BPF function references
+// (calls or function pointer loads) are populated.
+//
+// Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction
+// without at least one corresponding Symbol Instruction within insns.
+func (insns Instructions) encodeFunctionReferences() error {
+ // Index the offsets of instructions tagged as a symbol.
+ symbolOffsets := make(map[string]RawInstructionOffset)
+ iter := insns.Iterate()
+ for iter.Next() {
+ ins := iter.Ins
+
+ if ins.Symbol() == "" {
+ continue
+ }
+
+ if _, ok := symbolOffsets[ins.Symbol()]; ok {
+ return fmt.Errorf("duplicate symbol %s", ins.Symbol())
+ }
+
+ symbolOffsets[ins.Symbol()] = iter.Offset
+ }
+
+ // Find all instructions tagged as references to other symbols.
+ // Depending on the instruction type, populate their constant or offset
+ // fields to point to the symbol they refer to within the insn stream.
+ iter = insns.Iterate()
+ for iter.Next() {
+ i := iter.Index
+ offset := iter.Offset
+ ins := iter.Ins
+
+ if ins.Reference() == "" {
+ continue
+ }
+
+ switch {
+ case ins.IsFunctionReference() && ins.Constant == -1:
+ symOffset, ok := symbolOffsets[ins.Reference()]
+ if !ok {
+ return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
+ }
+
+ ins.Constant = int64(symOffset - offset - 1)
+
+ case ins.OpCode.Class().IsJump() && ins.Offset == -1:
+ symOffset, ok := symbolOffsets[ins.Reference()]
+ if !ok {
+ return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference)
+ }
+
+ ins.Offset = int16(symOffset - offset - 1)
+ }
+ }
+
+ return nil
+}
+
+// encodeMapPointers finds all Map Instructions and encodes their FDs
+// into their Constant fields.
+func (insns Instructions) encodeMapPointers() error {
+ iter := insns.Iterate()
+ for iter.Next() {
+ ins := iter.Ins
+
+ if !ins.IsLoadFromMap() {
+ continue
+ }
+
+ m := ins.Map()
+ if m == nil {
+ continue
+ }
+
+ fd := m.FD()
+ if fd < 0 {
+ return fmt.Errorf("map %s: %w", m, sys.ErrClosedFd)
+ }
+
+ ins.encodeMapFD(m.FD())
+ }
+
+ return nil
+}
+
// Iterate allows iterating a BPF program while keeping track of
// various offsets.
//
@@ -451,13 +837,6 @@ func (iter *InstructionIterator) Next() bool {
return true
}
-type bpfInstruction struct {
- OpCode OpCode
- Registers bpfRegisters
- Offset int16
- Constant int32
-}
-
type bpfRegisters uint8
func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
@@ -471,28 +850,10 @@ func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, erro
}
}
-func (r bpfRegisters) Unmarshal(bo binary.ByteOrder) (dst, src Register, err error) {
- switch bo {
- case binary.LittleEndian:
- return Register(r & 0xF), Register(r >> 4), nil
- case binary.BigEndian:
- return Register(r >> 4), Register(r & 0xf), nil
- default:
- return 0, 0, fmt.Errorf("unrecognized ByteOrder %T", bo)
- }
-}
-
-type unreferencedSymbolError struct {
- symbol string
-}
-
-func (use *unreferencedSymbolError) Error() string {
- return fmt.Sprintf("unreferenced symbol %s", use.symbol)
-}
-
// IsUnreferencedSymbol returns true if err was caused by
// an unreferenced symbol.
+//
+// Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol).
func IsUnreferencedSymbol(err error) bool {
- _, ok := err.(*unreferencedSymbolError)
- return ok
+ return errors.Is(err, ErrUnreferencedSymbol)
}