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 }