github.com/cilium/ebpf@v0.15.1-0.20240517100537-8079b37aa138/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 64 if ins.OpCode.Class().IsALU() { 65 switch ins.OpCode.ALUOp() { 66 case Div: 67 if ins.Offset == 1 { 68 ins.OpCode = ins.OpCode.SetALUOp(SDiv) 69 ins.Offset = 0 70 } 71 case Mod: 72 if ins.Offset == 1 { 73 ins.OpCode = ins.OpCode.SetALUOp(SMod) 74 ins.Offset = 0 75 } 76 case Mov: 77 switch ins.Offset { 78 case 8: 79 ins.OpCode = ins.OpCode.SetALUOp(MovSX8) 80 ins.Offset = 0 81 case 16: 82 ins.OpCode = ins.OpCode.SetALUOp(MovSX16) 83 ins.Offset = 0 84 case 32: 85 ins.OpCode = ins.OpCode.SetALUOp(MovSX32) 86 ins.Offset = 0 87 } 88 } 89 } 90 91 // Convert to int32 before widening to int64 92 // to ensure the signed bit is carried over. 93 ins.Constant = int64(int32(bo.Uint32(data[4:8]))) 94 95 if !ins.OpCode.IsDWordLoad() { 96 return InstructionSize, nil 97 } 98 99 // Pull another instruction from the stream to retrieve the second 100 // half of the 64-bit immediate value. 101 if _, err := io.ReadFull(r, data); err != nil { 102 // No Wrap, to avoid io.EOF clash 103 return 0, errors.New("64bit immediate is missing second half") 104 } 105 106 // Require that all fields other than the value are zero. 107 if bo.Uint32(data[0:4]) != 0 { 108 return 0, errors.New("64bit immediate has non-zero fields") 109 } 110 111 cons1 := uint32(ins.Constant) 112 cons2 := int32(bo.Uint32(data[4:8])) 113 ins.Constant = int64(cons2)<<32 | int64(cons1) 114 115 return 2 * InstructionSize, nil 116 } 117 118 // Marshal encodes a BPF instruction. 119 func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) { 120 if ins.OpCode == InvalidOpCode { 121 return 0, errors.New("invalid opcode") 122 } 123 124 isDWordLoad := ins.OpCode.IsDWordLoad() 125 126 cons := int32(ins.Constant) 127 if isDWordLoad { 128 // Encode least significant 32bit first for 64bit operations. 129 cons = int32(uint32(ins.Constant)) 130 } 131 132 regs, err := newBPFRegisters(ins.Dst, ins.Src, bo) 133 if err != nil { 134 return 0, fmt.Errorf("can't marshal registers: %s", err) 135 } 136 137 if ins.OpCode.Class().IsALU() { 138 newOffset := int16(0) 139 switch ins.OpCode.ALUOp() { 140 case SDiv: 141 ins.OpCode = ins.OpCode.SetALUOp(Div) 142 newOffset = 1 143 case SMod: 144 ins.OpCode = ins.OpCode.SetALUOp(Mod) 145 newOffset = 1 146 case MovSX8: 147 ins.OpCode = ins.OpCode.SetALUOp(Mov) 148 newOffset = 8 149 case MovSX16: 150 ins.OpCode = ins.OpCode.SetALUOp(Mov) 151 newOffset = 16 152 case MovSX32: 153 ins.OpCode = ins.OpCode.SetALUOp(Mov) 154 newOffset = 32 155 } 156 if newOffset != 0 && ins.Offset != 0 { 157 return 0, fmt.Errorf("extended ALU opcodes should have an .Offset of 0: %s", ins) 158 } 159 ins.Offset = newOffset 160 } 161 162 op, err := ins.OpCode.bpfOpCode() 163 if err != nil { 164 return 0, err 165 } 166 167 data := make([]byte, InstructionSize) 168 data[0] = op 169 data[1] = byte(regs) 170 bo.PutUint16(data[2:4], uint16(ins.Offset)) 171 bo.PutUint32(data[4:8], uint32(cons)) 172 if _, err := w.Write(data); err != nil { 173 return 0, err 174 } 175 176 if !isDWordLoad { 177 return InstructionSize, nil 178 } 179 180 // The first half of the second part of a double-wide instruction 181 // must be zero. The second half carries the value. 182 bo.PutUint32(data[0:4], 0) 183 bo.PutUint32(data[4:8], uint32(ins.Constant>>32)) 184 if _, err := w.Write(data); err != nil { 185 return 0, err 186 } 187 188 return 2 * InstructionSize, nil 189 } 190 191 // AssociateMap associates a Map with this Instruction. 192 // 193 // Implicitly clears the Instruction's Reference field. 194 // 195 // Returns an error if the Instruction is not a map load. 196 func (ins *Instruction) AssociateMap(m FDer) error { 197 if !ins.IsLoadFromMap() { 198 return errors.New("not a load from a map") 199 } 200 201 ins.Metadata.Set(referenceMeta{}, nil) 202 ins.Metadata.Set(mapMeta{}, m) 203 204 return nil 205 } 206 207 // RewriteMapPtr changes an instruction to use a new map fd. 208 // 209 // Returns an error if the instruction doesn't load a map. 210 // 211 // Deprecated: use AssociateMap instead. If you cannot provide a Map, 212 // wrap an fd in a type implementing FDer. 213 func (ins *Instruction) RewriteMapPtr(fd int) error { 214 if !ins.IsLoadFromMap() { 215 return errors.New("not a load from a map") 216 } 217 218 ins.encodeMapFD(fd) 219 220 return nil 221 } 222 223 func (ins *Instruction) encodeMapFD(fd int) { 224 // Preserve the offset value for direct map loads. 225 offset := uint64(ins.Constant) & (math.MaxUint32 << 32) 226 rawFd := uint64(uint32(fd)) 227 ins.Constant = int64(offset | rawFd) 228 } 229 230 // MapPtr returns the map fd for this instruction. 231 // 232 // The result is undefined if the instruction is not a load from a map, 233 // see IsLoadFromMap. 234 // 235 // Deprecated: use Map() instead. 236 func (ins *Instruction) MapPtr() int { 237 // If there is a map associated with the instruction, return its FD. 238 if fd := ins.Metadata.Get(mapMeta{}); fd != nil { 239 return fd.(FDer).FD() 240 } 241 242 // Fall back to the fd stored in the Constant field 243 return ins.mapFd() 244 } 245 246 // mapFd returns the map file descriptor stored in the 32 least significant 247 // bits of ins' Constant field. 248 func (ins *Instruction) mapFd() int { 249 return int(int32(ins.Constant)) 250 } 251 252 // RewriteMapOffset changes the offset of a direct load from a map. 253 // 254 // Returns an error if the instruction is not a direct load. 255 func (ins *Instruction) RewriteMapOffset(offset uint32) error { 256 if !ins.OpCode.IsDWordLoad() { 257 return fmt.Errorf("%s is not a 64 bit load", ins.OpCode) 258 } 259 260 if ins.Src != PseudoMapValue { 261 return errors.New("not a direct load from a map") 262 } 263 264 fd := uint64(ins.Constant) & math.MaxUint32 265 ins.Constant = int64(uint64(offset)<<32 | fd) 266 return nil 267 } 268 269 func (ins *Instruction) mapOffset() uint32 { 270 return uint32(uint64(ins.Constant) >> 32) 271 } 272 273 // IsLoadFromMap returns true if the instruction loads from a map. 274 // 275 // This covers both loading the map pointer and direct map value loads. 276 func (ins *Instruction) IsLoadFromMap() bool { 277 return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue) 278 } 279 280 // IsFunctionCall returns true if the instruction calls another BPF function. 281 // 282 // This is not the same thing as a BPF helper call. 283 func (ins *Instruction) IsFunctionCall() bool { 284 return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall 285 } 286 287 // IsKfuncCall returns true if the instruction calls a kfunc. 288 // 289 // This is not the same thing as a BPF helper call. 290 func (ins *Instruction) IsKfuncCall() bool { 291 return ins.OpCode.JumpOp() == Call && ins.Src == PseudoKfuncCall 292 } 293 294 // IsLoadOfFunctionPointer returns true if the instruction loads a function pointer. 295 func (ins *Instruction) IsLoadOfFunctionPointer() bool { 296 return ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc 297 } 298 299 // IsFunctionReference returns true if the instruction references another BPF 300 // function, either by invoking a Call jump operation or by loading a function 301 // pointer. 302 func (ins *Instruction) IsFunctionReference() bool { 303 return ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer() 304 } 305 306 // IsBuiltinCall returns true if the instruction is a built-in call, i.e. BPF helper call. 307 func (ins *Instruction) IsBuiltinCall() bool { 308 return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0 309 } 310 311 // IsConstantLoad returns true if the instruction loads a constant of the 312 // given size. 313 func (ins *Instruction) IsConstantLoad(size Size) bool { 314 return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0 315 } 316 317 // Format implements fmt.Formatter. 318 func (ins Instruction) Format(f fmt.State, c rune) { 319 if c != 'v' { 320 fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c) 321 return 322 } 323 324 op := ins.OpCode 325 326 if op == InvalidOpCode { 327 fmt.Fprint(f, "INVALID") 328 return 329 } 330 331 // Omit trailing space for Exit 332 if op.JumpOp() == Exit { 333 fmt.Fprint(f, op) 334 return 335 } 336 337 if ins.IsLoadFromMap() { 338 fd := ins.mapFd() 339 m := ins.Map() 340 switch ins.Src { 341 case PseudoMapFD: 342 if m != nil { 343 fmt.Fprintf(f, "LoadMapPtr dst: %s map: %s", ins.Dst, m) 344 } else { 345 fmt.Fprintf(f, "LoadMapPtr dst: %s fd: %d", ins.Dst, fd) 346 } 347 348 case PseudoMapValue: 349 if m != nil { 350 fmt.Fprintf(f, "LoadMapValue dst: %s, map: %s off: %d", ins.Dst, m, ins.mapOffset()) 351 } else { 352 fmt.Fprintf(f, "LoadMapValue dst: %s, fd: %d off: %d", ins.Dst, fd, ins.mapOffset()) 353 } 354 } 355 356 goto ref 357 } 358 359 switch cls := op.Class(); { 360 case cls.isLoadOrStore(): 361 fmt.Fprintf(f, "%v ", op) 362 switch op.Mode() { 363 case ImmMode: 364 fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant) 365 case AbsMode: 366 fmt.Fprintf(f, "imm: %d", ins.Constant) 367 case IndMode: 368 fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant) 369 case MemMode, MemSXMode: 370 fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant) 371 case XAddMode: 372 fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src) 373 } 374 375 case cls.IsALU(): 376 fmt.Fprintf(f, "%v", op) 377 if op == Swap.Op(ImmSource) { 378 fmt.Fprintf(f, "%d", ins.Constant) 379 } 380 381 fmt.Fprintf(f, " dst: %s ", ins.Dst) 382 switch { 383 case op.ALUOp() == Swap: 384 break 385 case op.Source() == ImmSource: 386 fmt.Fprintf(f, "imm: %d", ins.Constant) 387 default: 388 fmt.Fprintf(f, "src: %s", ins.Src) 389 } 390 391 case cls.IsJump(): 392 fmt.Fprintf(f, "%v ", op) 393 switch jop := op.JumpOp(); jop { 394 case Call: 395 switch ins.Src { 396 case PseudoCall: 397 // bpf-to-bpf call 398 fmt.Fprint(f, ins.Constant) 399 case PseudoKfuncCall: 400 // kfunc call 401 fmt.Fprintf(f, "Kfunc(%d)", ins.Constant) 402 default: 403 fmt.Fprint(f, BuiltinFunc(ins.Constant)) 404 } 405 406 case Ja: 407 if ins.OpCode.Class() == Jump32Class { 408 fmt.Fprintf(f, "imm: %d", ins.Constant) 409 } else { 410 fmt.Fprintf(f, "off: %d", ins.Offset) 411 } 412 413 default: 414 fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset) 415 if op.Source() == ImmSource { 416 fmt.Fprintf(f, "imm: %d", ins.Constant) 417 } else { 418 fmt.Fprintf(f, "src: %s", ins.Src) 419 } 420 } 421 default: 422 fmt.Fprintf(f, "%v ", op) 423 } 424 425 ref: 426 if ins.Reference() != "" { 427 fmt.Fprintf(f, " <%s>", ins.Reference()) 428 } 429 } 430 431 func (ins Instruction) equal(other Instruction) bool { 432 return ins.OpCode == other.OpCode && 433 ins.Dst == other.Dst && 434 ins.Src == other.Src && 435 ins.Offset == other.Offset && 436 ins.Constant == other.Constant 437 } 438 439 // Size returns the amount of bytes ins would occupy in binary form. 440 func (ins Instruction) Size() uint64 { 441 return uint64(InstructionSize * ins.OpCode.rawInstructions()) 442 } 443 444 // WithMetadata sets the given Metadata on the Instruction. e.g. to copy 445 // Metadata from another Instruction when replacing it. 446 func (ins Instruction) WithMetadata(meta Metadata) Instruction { 447 ins.Metadata = meta 448 return ins 449 } 450 451 type symbolMeta struct{} 452 453 // WithSymbol marks the Instruction as a Symbol, which other Instructions 454 // can point to using corresponding calls to WithReference. 455 func (ins Instruction) WithSymbol(name string) Instruction { 456 ins.Metadata.Set(symbolMeta{}, name) 457 return ins 458 } 459 460 // Sym creates a symbol. 461 // 462 // Deprecated: use WithSymbol instead. 463 func (ins Instruction) Sym(name string) Instruction { 464 return ins.WithSymbol(name) 465 } 466 467 // Symbol returns the value ins has been marked with using WithSymbol, 468 // otherwise returns an empty string. A symbol is often an Instruction 469 // at the start of a function body. 470 func (ins Instruction) Symbol() string { 471 sym, _ := ins.Metadata.Get(symbolMeta{}).(string) 472 return sym 473 } 474 475 type referenceMeta struct{} 476 477 // WithReference makes ins reference another Symbol or map by name. 478 func (ins Instruction) WithReference(ref string) Instruction { 479 ins.Metadata.Set(referenceMeta{}, ref) 480 return ins 481 } 482 483 // Reference returns the Symbol or map name referenced by ins, if any. 484 func (ins Instruction) Reference() string { 485 ref, _ := ins.Metadata.Get(referenceMeta{}).(string) 486 return ref 487 } 488 489 type mapMeta struct{} 490 491 // Map returns the Map referenced by ins, if any. 492 // An Instruction will contain a Map if e.g. it references an existing, 493 // pinned map that was opened during ELF loading. 494 func (ins Instruction) Map() FDer { 495 fd, _ := ins.Metadata.Get(mapMeta{}).(FDer) 496 return fd 497 } 498 499 type sourceMeta struct{} 500 501 // WithSource adds source information about the Instruction. 502 func (ins Instruction) WithSource(src fmt.Stringer) Instruction { 503 ins.Metadata.Set(sourceMeta{}, src) 504 return ins 505 } 506 507 // Source returns source information about the Instruction. The field is 508 // present when the compiler emits BTF line info about the Instruction and 509 // usually contains the line of source code responsible for it. 510 func (ins Instruction) Source() fmt.Stringer { 511 str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer) 512 return str 513 } 514 515 // A Comment can be passed to Instruction.WithSource to add a comment 516 // to an instruction. 517 type Comment string 518 519 func (s Comment) String() string { 520 return string(s) 521 } 522 523 // FDer represents a resource tied to an underlying file descriptor. 524 // Used as a stand-in for e.g. ebpf.Map since that type cannot be 525 // imported here and FD() is the only method we rely on. 526 type FDer interface { 527 FD() int 528 } 529 530 // Instructions is an eBPF program. 531 type Instructions []Instruction 532 533 // Unmarshal unmarshals an Instructions from a binary instruction stream. 534 // All instructions in insns are replaced by instructions decoded from r. 535 func (insns *Instructions) Unmarshal(r io.Reader, bo binary.ByteOrder) error { 536 if len(*insns) > 0 { 537 *insns = nil 538 } 539 540 var offset uint64 541 for { 542 var ins Instruction 543 n, err := ins.Unmarshal(r, bo) 544 if errors.Is(err, io.EOF) { 545 break 546 } 547 if err != nil { 548 return fmt.Errorf("offset %d: %w", offset, err) 549 } 550 551 *insns = append(*insns, ins) 552 offset += n 553 } 554 555 return nil 556 } 557 558 // Name returns the name of the function insns belongs to, if any. 559 func (insns Instructions) Name() string { 560 if len(insns) == 0 { 561 return "" 562 } 563 return insns[0].Symbol() 564 } 565 566 func (insns Instructions) String() string { 567 return fmt.Sprint(insns) 568 } 569 570 // Size returns the amount of bytes insns would occupy in binary form. 571 func (insns Instructions) Size() uint64 { 572 var sum uint64 573 for _, ins := range insns { 574 sum += ins.Size() 575 } 576 return sum 577 } 578 579 // AssociateMap updates all Instructions that Reference the given symbol 580 // to point to an existing Map m instead. 581 // 582 // Returns ErrUnreferencedSymbol error if no references to symbol are found 583 // in insns. If symbol is anything else than the symbol name of map (e.g. 584 // a bpf2bpf subprogram), an error is returned. 585 func (insns Instructions) AssociateMap(symbol string, m FDer) error { 586 if symbol == "" { 587 return errors.New("empty symbol") 588 } 589 590 var found bool 591 for i := range insns { 592 ins := &insns[i] 593 if ins.Reference() != symbol { 594 continue 595 } 596 597 if err := ins.AssociateMap(m); err != nil { 598 return err 599 } 600 601 found = true 602 } 603 604 if !found { 605 return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol) 606 } 607 608 return nil 609 } 610 611 // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd. 612 // 613 // Returns ErrUnreferencedSymbol if the symbol isn't used. 614 // 615 // Deprecated: use AssociateMap instead. 616 func (insns Instructions) RewriteMapPtr(symbol string, fd int) error { 617 if symbol == "" { 618 return errors.New("empty symbol") 619 } 620 621 var found bool 622 for i := range insns { 623 ins := &insns[i] 624 if ins.Reference() != symbol { 625 continue 626 } 627 628 if !ins.IsLoadFromMap() { 629 return errors.New("not a load from a map") 630 } 631 632 ins.encodeMapFD(fd) 633 634 found = true 635 } 636 637 if !found { 638 return fmt.Errorf("symbol %s: %w", symbol, ErrUnreferencedSymbol) 639 } 640 641 return nil 642 } 643 644 // SymbolOffsets returns the set of symbols and their offset in 645 // the instructions. 646 func (insns Instructions) SymbolOffsets() (map[string]int, error) { 647 offsets := make(map[string]int) 648 649 for i, ins := range insns { 650 if ins.Symbol() == "" { 651 continue 652 } 653 654 if _, ok := offsets[ins.Symbol()]; ok { 655 return nil, fmt.Errorf("duplicate symbol %s", ins.Symbol()) 656 } 657 658 offsets[ins.Symbol()] = i 659 } 660 661 return offsets, nil 662 } 663 664 // FunctionReferences returns a set of symbol names these Instructions make 665 // bpf-to-bpf calls to. 666 func (insns Instructions) FunctionReferences() []string { 667 calls := make(map[string]struct{}) 668 for _, ins := range insns { 669 if ins.Constant != -1 { 670 // BPF-to-BPF calls have -1 constants. 671 continue 672 } 673 674 if ins.Reference() == "" { 675 continue 676 } 677 678 if !ins.IsFunctionReference() { 679 continue 680 } 681 682 calls[ins.Reference()] = struct{}{} 683 } 684 685 result := make([]string, 0, len(calls)) 686 for call := range calls { 687 result = append(result, call) 688 } 689 690 sort.Strings(result) 691 return result 692 } 693 694 // ReferenceOffsets returns the set of references and their offset in 695 // the instructions. 696 func (insns Instructions) ReferenceOffsets() map[string][]int { 697 offsets := make(map[string][]int) 698 699 for i, ins := range insns { 700 if ins.Reference() == "" { 701 continue 702 } 703 704 offsets[ins.Reference()] = append(offsets[ins.Reference()], i) 705 } 706 707 return offsets 708 } 709 710 // Format implements fmt.Formatter. 711 // 712 // You can control indentation of symbols by 713 // specifying a width. Setting a precision controls the indentation of 714 // instructions. 715 // The default character is a tab, which can be overridden by specifying 716 // the ' ' space flag. 717 func (insns Instructions) Format(f fmt.State, c rune) { 718 if c != 's' && c != 'v' { 719 fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c) 720 return 721 } 722 723 // Precision is better in this case, because it allows 724 // specifying 0 padding easily. 725 padding, ok := f.Precision() 726 if !ok { 727 padding = 1 728 } 729 730 indent := strings.Repeat("\t", padding) 731 if f.Flag(' ') { 732 indent = strings.Repeat(" ", padding) 733 } 734 735 symPadding, ok := f.Width() 736 if !ok { 737 symPadding = padding - 1 738 } 739 if symPadding < 0 { 740 symPadding = 0 741 } 742 743 symIndent := strings.Repeat("\t", symPadding) 744 if f.Flag(' ') { 745 symIndent = strings.Repeat(" ", symPadding) 746 } 747 748 // Guess how many digits we need at most, by assuming that all instructions 749 // are double wide. 750 highestOffset := len(insns) * 2 751 offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset)))) 752 753 iter := insns.Iterate() 754 for iter.Next() { 755 if iter.Ins.Symbol() != "" { 756 fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol()) 757 } 758 if src := iter.Ins.Source(); src != nil { 759 line := strings.TrimSpace(src.String()) 760 if line != "" { 761 fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line) 762 } 763 } 764 fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins) 765 } 766 } 767 768 // Marshal encodes a BPF program into the kernel format. 769 // 770 // insns may be modified if there are unresolved jumps or bpf2bpf calls. 771 // 772 // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction 773 // without a matching Symbol Instruction within insns. 774 func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error { 775 if err := insns.encodeFunctionReferences(); err != nil { 776 return err 777 } 778 779 if err := insns.encodeMapPointers(); err != nil { 780 return err 781 } 782 783 for i, ins := range insns { 784 if _, err := ins.Marshal(w, bo); err != nil { 785 return fmt.Errorf("instruction %d: %w", i, err) 786 } 787 } 788 return nil 789 } 790 791 // Tag calculates the kernel tag for a series of instructions. 792 // 793 // It mirrors bpf_prog_calc_tag in the kernel and so can be compared 794 // to ProgramInfo.Tag to figure out whether a loaded program matches 795 // certain instructions. 796 func (insns Instructions) Tag(bo binary.ByteOrder) (string, error) { 797 h := sha1.New() 798 for i, ins := range insns { 799 if ins.IsLoadFromMap() { 800 ins.Constant = 0 801 } 802 _, err := ins.Marshal(h, bo) 803 if err != nil { 804 return "", fmt.Errorf("instruction %d: %w", i, err) 805 } 806 } 807 return hex.EncodeToString(h.Sum(nil)[:unix.BPF_TAG_SIZE]), nil 808 } 809 810 // encodeFunctionReferences populates the Offset (or Constant, depending on 811 // the instruction type) field of instructions with a Reference field to point 812 // to the offset of the corresponding instruction with a matching Symbol field. 813 // 814 // Only Reference Instructions that are either jumps or BPF function references 815 // (calls or function pointer loads) are populated. 816 // 817 // Returns ErrUnsatisfiedProgramReference if there is a Reference Instruction 818 // without at least one corresponding Symbol Instruction within insns. 819 func (insns Instructions) encodeFunctionReferences() error { 820 // Index the offsets of instructions tagged as a symbol. 821 symbolOffsets := make(map[string]RawInstructionOffset) 822 iter := insns.Iterate() 823 for iter.Next() { 824 ins := iter.Ins 825 826 if ins.Symbol() == "" { 827 continue 828 } 829 830 if _, ok := symbolOffsets[ins.Symbol()]; ok { 831 return fmt.Errorf("duplicate symbol %s", ins.Symbol()) 832 } 833 834 symbolOffsets[ins.Symbol()] = iter.Offset 835 } 836 837 // Find all instructions tagged as references to other symbols. 838 // Depending on the instruction type, populate their constant or offset 839 // fields to point to the symbol they refer to within the insn stream. 840 iter = insns.Iterate() 841 for iter.Next() { 842 i := iter.Index 843 offset := iter.Offset 844 ins := iter.Ins 845 846 if ins.Reference() == "" { 847 continue 848 } 849 850 switch { 851 case ins.IsFunctionReference() && ins.Constant == -1, 852 ins.OpCode == Ja.opCode(Jump32Class, ImmSource) && ins.Constant == -1: 853 symOffset, ok := symbolOffsets[ins.Reference()] 854 if !ok { 855 return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) 856 } 857 858 ins.Constant = int64(symOffset - offset - 1) 859 860 case ins.OpCode.Class().IsJump() && ins.Offset == -1: 861 symOffset, ok := symbolOffsets[ins.Reference()] 862 if !ok { 863 return fmt.Errorf("%s at insn %d: symbol %q: %w", ins.OpCode, i, ins.Reference(), ErrUnsatisfiedProgramReference) 864 } 865 866 ins.Offset = int16(symOffset - offset - 1) 867 } 868 } 869 870 return nil 871 } 872 873 // encodeMapPointers finds all Map Instructions and encodes their FDs 874 // into their Constant fields. 875 func (insns Instructions) encodeMapPointers() error { 876 iter := insns.Iterate() 877 for iter.Next() { 878 ins := iter.Ins 879 880 if !ins.IsLoadFromMap() { 881 continue 882 } 883 884 m := ins.Map() 885 if m == nil { 886 continue 887 } 888 889 fd := m.FD() 890 if fd < 0 { 891 return fmt.Errorf("map %s: %w", m, sys.ErrClosedFd) 892 } 893 894 ins.encodeMapFD(m.FD()) 895 } 896 897 return nil 898 } 899 900 // Iterate allows iterating a BPF program while keeping track of 901 // various offsets. 902 // 903 // Modifying the instruction slice will lead to undefined behaviour. 904 func (insns Instructions) Iterate() *InstructionIterator { 905 return &InstructionIterator{insns: insns} 906 } 907 908 // InstructionIterator iterates over a BPF program. 909 type InstructionIterator struct { 910 insns Instructions 911 // The instruction in question. 912 Ins *Instruction 913 // The index of the instruction in the original instruction slice. 914 Index int 915 // The offset of the instruction in raw BPF instructions. This accounts 916 // for double-wide instructions. 917 Offset RawInstructionOffset 918 } 919 920 // Next returns true as long as there are any instructions remaining. 921 func (iter *InstructionIterator) Next() bool { 922 if len(iter.insns) == 0 { 923 return false 924 } 925 926 if iter.Ins != nil { 927 iter.Index++ 928 iter.Offset += RawInstructionOffset(iter.Ins.OpCode.rawInstructions()) 929 } 930 iter.Ins = &iter.insns[0] 931 iter.insns = iter.insns[1:] 932 return true 933 } 934 935 type bpfRegisters uint8 936 937 func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) { 938 switch bo { 939 case binary.LittleEndian: 940 return bpfRegisters((src << 4) | (dst & 0xF)), nil 941 case binary.BigEndian: 942 return bpfRegisters((dst << 4) | (src & 0xF)), nil 943 default: 944 return 0, fmt.Errorf("unrecognized ByteOrder %T", bo) 945 } 946 } 947 948 // IsUnreferencedSymbol returns true if err was caused by 949 // an unreferenced symbol. 950 // 951 // Deprecated: use errors.Is(err, asm.ErrUnreferencedSymbol). 952 func IsUnreferencedSymbol(err error) bool { 953 return errors.Is(err, ErrUnreferencedSymbol) 954 }