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
19 const InstructionSize = 8
20
21
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
29 func (rio RawInstructionOffset) Bytes() uint64 {
30 return uint64(rio) * InstructionSize
31 }
32
33
34 type Instruction struct {
35 OpCode OpCode
36 Dst Register
37 Src Register
38 Offset int16
39 Constant int64
40
41
42 Metadata Metadata
43 }
44
45
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
65 ins.Constant = int64(int32(bo.Uint32(data[4:8])))
66
67 if !ins.OpCode.IsDWordLoad() {
68 return InstructionSize, nil
69 }
70
71
72
73 if _, err := io.ReadFull(r, data); err != nil {
74
75 return 0, errors.New("64bit immediate is missing second half")
76 }
77
78
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
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
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
123
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
134
135
136
137
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
150
151
152
153
154
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
167 offset := uint64(ins.Constant) & (math.MaxUint32 << 32)
168 rawFd := uint64(uint32(fd))
169 ins.Constant = int64(offset | rawFd)
170 }
171
172
173
174
175
176
177
178 func (ins *Instruction) MapPtr() int {
179
180 if fd := ins.Metadata.Get(mapMeta{}); fd != nil {
181 return fd.(FDer).FD()
182 }
183
184
185 return ins.mapFd()
186 }
187
188
189
190 func (ins *Instruction) mapFd() int {
191 return int(int32(ins.Constant))
192 }
193
194
195
196
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
216
217
218 func (ins *Instruction) IsLoadFromMap() bool {
219 return ins.OpCode == LoadImmOp(DWord) && (ins.Src == PseudoMapFD || ins.Src == PseudoMapValue)
220 }
221
222
223
224
225 func (ins *Instruction) IsFunctionCall() bool {
226 return ins.OpCode.JumpOp() == Call && ins.Src == PseudoCall
227 }
228
229
230 func (ins *Instruction) IsLoadOfFunctionPointer() bool {
231 return ins.OpCode.IsDWordLoad() && ins.Src == PseudoFunc
232 }
233
234
235
236
237 func (ins *Instruction) IsFunctionReference() bool {
238 return ins.IsFunctionCall() || ins.IsLoadOfFunctionPointer()
239 }
240
241
242 func (ins *Instruction) IsBuiltinCall() bool {
243 return ins.OpCode.JumpOp() == Call && ins.Src == R0 && ins.Dst == R0
244 }
245
246
247
248 func (ins *Instruction) IsConstantLoad(size Size) bool {
249 return ins.OpCode == LoadImmOp(size) && ins.Src == R0 && ins.Offset == 0
250 }
251
252
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
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
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
353 func (ins Instruction) Size() uint64 {
354 return uint64(InstructionSize * ins.OpCode.rawInstructions())
355 }
356
357 type symbolMeta struct{}
358
359
360
361 func (ins Instruction) WithSymbol(name string) Instruction {
362 ins.Metadata.Set(symbolMeta{}, name)
363 return ins
364 }
365
366
367
368
369 func (ins Instruction) Sym(name string) Instruction {
370 return ins.WithSymbol(name)
371 }
372
373
374
375
376 func (ins Instruction) Symbol() string {
377 sym, _ := ins.Metadata.Get(symbolMeta{}).(string)
378 return sym
379 }
380
381 type referenceMeta struct{}
382
383
384 func (ins Instruction) WithReference(ref string) Instruction {
385 ins.Metadata.Set(referenceMeta{}, ref)
386 return ins
387 }
388
389
390 func (ins Instruction) Reference() string {
391 ref, _ := ins.Metadata.Get(referenceMeta{}).(string)
392 return ref
393 }
394
395 type mapMeta struct{}
396
397
398
399
400 func (ins Instruction) Map() FDer {
401 fd, _ := ins.Metadata.Get(mapMeta{}).(FDer)
402 return fd
403 }
404
405 type sourceMeta struct{}
406
407
408 func (ins Instruction) WithSource(src fmt.Stringer) Instruction {
409 ins.Metadata.Set(sourceMeta{}, src)
410 return ins
411 }
412
413
414
415
416 func (ins Instruction) Source() fmt.Stringer {
417 str, _ := ins.Metadata.Get(sourceMeta{}).(fmt.Stringer)
418 return str
419 }
420
421
422
423 type Comment string
424
425 func (s Comment) String() string {
426 return string(s)
427 }
428
429
430
431
432 type FDer interface {
433 FD() int
434 }
435
436
437 type Instructions []Instruction
438
439
440
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
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
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
486
487
488
489
490
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
518
519
520
521
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
551
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
571
572 func (insns Instructions) FunctionReferences() []string {
573 calls := make(map[string]struct{})
574 for _, ins := range insns {
575 if ins.Constant != -1 {
576
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
601
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
617
618
619
620
621
622
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
630
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
655
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
675
676
677
678
679
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
698
699
700
701
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
717
718
719
720
721
722
723
724
725 func (insns Instructions) encodeFunctionReferences() error {
726
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
744
745
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
779
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
806
807
808
809 func (insns Instructions) Iterate() *InstructionIterator {
810 return &InstructionIterator{insns: insns}
811 }
812
813
814 type InstructionIterator struct {
815 insns Instructions
816
817 Ins *Instruction
818
819 Index int
820
821
822 Offset RawInstructionOffset
823 }
824
825
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
854
855
856
857 func IsUnreferencedSymbol(err error) bool {
858 return errors.Is(err, ErrUnreferencedSymbol)
859 }
860
View as plain text