vm_instructions.go raw

   1  // Copyright 2016 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package bpf
   6  
   7  import (
   8  	"encoding/binary"
   9  	"fmt"
  10  )
  11  
  12  func aluOpConstant(ins ALUOpConstant, regA uint32) uint32 {
  13  	return aluOpCommon(ins.Op, regA, ins.Val)
  14  }
  15  
  16  func aluOpX(ins ALUOpX, regA uint32, regX uint32) (uint32, bool) {
  17  	// Guard against division or modulus by zero by terminating
  18  	// the program, as the OS BPF VM does
  19  	if regX == 0 {
  20  		switch ins.Op {
  21  		case ALUOpDiv, ALUOpMod:
  22  			return 0, false
  23  		}
  24  	}
  25  
  26  	return aluOpCommon(ins.Op, regA, regX), true
  27  }
  28  
  29  func aluOpCommon(op ALUOp, regA uint32, value uint32) uint32 {
  30  	switch op {
  31  	case ALUOpAdd:
  32  		return regA + value
  33  	case ALUOpSub:
  34  		return regA - value
  35  	case ALUOpMul:
  36  		return regA * value
  37  	case ALUOpDiv:
  38  		// Division by zero not permitted by NewVM and aluOpX checks
  39  		return regA / value
  40  	case ALUOpOr:
  41  		return regA | value
  42  	case ALUOpAnd:
  43  		return regA & value
  44  	case ALUOpShiftLeft:
  45  		return regA << value
  46  	case ALUOpShiftRight:
  47  		return regA >> value
  48  	case ALUOpMod:
  49  		// Modulus by zero not permitted by NewVM and aluOpX checks
  50  		return regA % value
  51  	case ALUOpXor:
  52  		return regA ^ value
  53  	default:
  54  		return regA
  55  	}
  56  }
  57  
  58  func jumpIf(ins JumpIf, regA uint32) int {
  59  	return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, ins.Val)
  60  }
  61  
  62  func jumpIfX(ins JumpIfX, regA uint32, regX uint32) int {
  63  	return jumpIfCommon(ins.Cond, ins.SkipTrue, ins.SkipFalse, regA, regX)
  64  }
  65  
  66  func jumpIfCommon(cond JumpTest, skipTrue, skipFalse uint8, regA uint32, value uint32) int {
  67  	var ok bool
  68  
  69  	switch cond {
  70  	case JumpEqual:
  71  		ok = regA == value
  72  	case JumpNotEqual:
  73  		ok = regA != value
  74  	case JumpGreaterThan:
  75  		ok = regA > value
  76  	case JumpLessThan:
  77  		ok = regA < value
  78  	case JumpGreaterOrEqual:
  79  		ok = regA >= value
  80  	case JumpLessOrEqual:
  81  		ok = regA <= value
  82  	case JumpBitsSet:
  83  		ok = (regA & value) != 0
  84  	case JumpBitsNotSet:
  85  		ok = (regA & value) == 0
  86  	}
  87  
  88  	if ok {
  89  		return int(skipTrue)
  90  	}
  91  
  92  	return int(skipFalse)
  93  }
  94  
  95  func loadAbsolute(ins LoadAbsolute, in []byte) (uint32, bool) {
  96  	offset := int(ins.Off)
  97  	size := ins.Size
  98  
  99  	return loadCommon(in, offset, size)
 100  }
 101  
 102  func loadConstant(ins LoadConstant, regA uint32, regX uint32) (uint32, uint32) {
 103  	switch ins.Dst {
 104  	case RegA:
 105  		regA = ins.Val
 106  	case RegX:
 107  		regX = ins.Val
 108  	}
 109  
 110  	return regA, regX
 111  }
 112  
 113  func loadExtension(ins LoadExtension, in []byte) uint32 {
 114  	switch ins.Num {
 115  	case ExtLen:
 116  		return uint32(len(in))
 117  	default:
 118  		panic(fmt.Sprintf("unimplemented extension: %d", ins.Num))
 119  	}
 120  }
 121  
 122  func loadIndirect(ins LoadIndirect, in []byte, regX uint32) (uint32, bool) {
 123  	offset := int(ins.Off) + int(regX)
 124  	size := ins.Size
 125  
 126  	return loadCommon(in, offset, size)
 127  }
 128  
 129  func loadMemShift(ins LoadMemShift, in []byte) (uint32, bool) {
 130  	offset := int(ins.Off)
 131  
 132  	// Size of LoadMemShift is always 1 byte
 133  	if !inBounds(len(in), offset, 1) {
 134  		return 0, false
 135  	}
 136  
 137  	// Mask off high 4 bits and multiply low 4 bits by 4
 138  	return uint32(in[offset]&0x0f) * 4, true
 139  }
 140  
 141  func inBounds(inLen int, offset int, size int) bool {
 142  	return offset+size <= inLen
 143  }
 144  
 145  func loadCommon(in []byte, offset int, size int) (uint32, bool) {
 146  	if !inBounds(len(in), offset, size) {
 147  		return 0, false
 148  	}
 149  
 150  	switch size {
 151  	case 1:
 152  		return uint32(in[offset]), true
 153  	case 2:
 154  		return uint32(binary.BigEndian.Uint16(in[offset : offset+size])), true
 155  	case 4:
 156  		return uint32(binary.BigEndian.Uint32(in[offset : offset+size])), true
 157  	default:
 158  		panic(fmt.Sprintf("invalid load size: %d", size))
 159  	}
 160  }
 161  
 162  func loadScratch(ins LoadScratch, regScratch [16]uint32, regA uint32, regX uint32) (uint32, uint32) {
 163  	switch ins.Dst {
 164  	case RegA:
 165  		regA = regScratch[ins.N]
 166  	case RegX:
 167  		regX = regScratch[ins.N]
 168  	}
 169  
 170  	return regA, regX
 171  }
 172  
 173  func storeScratch(ins StoreScratch, regScratch [16]uint32, regA uint32, regX uint32) [16]uint32 {
 174  	switch ins.Src {
 175  	case RegA:
 176  		regScratch[ins.N] = regA
 177  	case RegX:
 178  		regScratch[ins.N] = regX
 179  	}
 180  
 181  	return regScratch
 182  }
 183