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