mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 11:02:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			726 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			726 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2016 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package bpf
 | |
| 
 | |
| import "fmt"
 | |
| 
 | |
| // An Instruction is one instruction executed by the BPF virtual
 | |
| // machine.
 | |
| type Instruction interface {
 | |
| 	// Assemble assembles the Instruction into a RawInstruction.
 | |
| 	Assemble() (RawInstruction, error)
 | |
| }
 | |
| 
 | |
| // A RawInstruction is a raw BPF virtual machine instruction.
 | |
| type RawInstruction struct {
 | |
| 	// Operation to execute.
 | |
| 	Op uint16
 | |
| 	// For conditional jump instructions, the number of instructions
 | |
| 	// to skip if the condition is true/false.
 | |
| 	Jt uint8
 | |
| 	Jf uint8
 | |
| 	// Constant parameter. The meaning depends on the Op.
 | |
| 	K uint32
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (ri RawInstruction) Assemble() (RawInstruction, error) { return ri, nil }
 | |
| 
 | |
| // Disassemble parses ri into an Instruction and returns it. If ri is
 | |
| // not recognized by this package, ri itself is returned.
 | |
| func (ri RawInstruction) Disassemble() Instruction {
 | |
| 	switch ri.Op & opMaskCls {
 | |
| 	case opClsLoadA, opClsLoadX:
 | |
| 		reg := Register(ri.Op & opMaskLoadDest)
 | |
| 		sz := 0
 | |
| 		switch ri.Op & opMaskLoadWidth {
 | |
| 		case opLoadWidth4:
 | |
| 			sz = 4
 | |
| 		case opLoadWidth2:
 | |
| 			sz = 2
 | |
| 		case opLoadWidth1:
 | |
| 			sz = 1
 | |
| 		default:
 | |
| 			return ri
 | |
| 		}
 | |
| 		switch ri.Op & opMaskLoadMode {
 | |
| 		case opAddrModeImmediate:
 | |
| 			if sz != 4 {
 | |
| 				return ri
 | |
| 			}
 | |
| 			return LoadConstant{Dst: reg, Val: ri.K}
 | |
| 		case opAddrModeScratch:
 | |
| 			if sz != 4 || ri.K > 15 {
 | |
| 				return ri
 | |
| 			}
 | |
| 			return LoadScratch{Dst: reg, N: int(ri.K)}
 | |
| 		case opAddrModeAbsolute:
 | |
| 			if ri.K > extOffset+0xffffffff {
 | |
| 				return LoadExtension{Num: Extension(-extOffset + ri.K)}
 | |
| 			}
 | |
| 			return LoadAbsolute{Size: sz, Off: ri.K}
 | |
| 		case opAddrModeIndirect:
 | |
| 			return LoadIndirect{Size: sz, Off: ri.K}
 | |
| 		case opAddrModePacketLen:
 | |
| 			if sz != 4 {
 | |
| 				return ri
 | |
| 			}
 | |
| 			return LoadExtension{Num: ExtLen}
 | |
| 		case opAddrModeMemShift:
 | |
| 			return LoadMemShift{Off: ri.K}
 | |
| 		default:
 | |
| 			return ri
 | |
| 		}
 | |
| 
 | |
| 	case opClsStoreA:
 | |
| 		if ri.Op != opClsStoreA || ri.K > 15 {
 | |
| 			return ri
 | |
| 		}
 | |
| 		return StoreScratch{Src: RegA, N: int(ri.K)}
 | |
| 
 | |
| 	case opClsStoreX:
 | |
| 		if ri.Op != opClsStoreX || ri.K > 15 {
 | |
| 			return ri
 | |
| 		}
 | |
| 		return StoreScratch{Src: RegX, N: int(ri.K)}
 | |
| 
 | |
| 	case opClsALU:
 | |
| 		switch op := ALUOp(ri.Op & opMaskOperator); op {
 | |
| 		case ALUOpAdd, ALUOpSub, ALUOpMul, ALUOpDiv, ALUOpOr, ALUOpAnd, ALUOpShiftLeft, ALUOpShiftRight, ALUOpMod, ALUOpXor:
 | |
| 			switch operand := opOperand(ri.Op & opMaskOperand); operand {
 | |
| 			case opOperandX:
 | |
| 				return ALUOpX{Op: op}
 | |
| 			case opOperandConstant:
 | |
| 				return ALUOpConstant{Op: op, Val: ri.K}
 | |
| 			default:
 | |
| 				return ri
 | |
| 			}
 | |
| 		case aluOpNeg:
 | |
| 			return NegateA{}
 | |
| 		default:
 | |
| 			return ri
 | |
| 		}
 | |
| 
 | |
| 	case opClsJump:
 | |
| 		switch op := jumpOp(ri.Op & opMaskOperator); op {
 | |
| 		case opJumpAlways:
 | |
| 			return Jump{Skip: ri.K}
 | |
| 		case opJumpEqual, opJumpGT, opJumpGE, opJumpSet:
 | |
| 			cond, skipTrue, skipFalse := jumpOpToTest(op, ri.Jt, ri.Jf)
 | |
| 			switch operand := opOperand(ri.Op & opMaskOperand); operand {
 | |
| 			case opOperandX:
 | |
| 				return JumpIfX{Cond: cond, SkipTrue: skipTrue, SkipFalse: skipFalse}
 | |
| 			case opOperandConstant:
 | |
| 				return JumpIf{Cond: cond, Val: ri.K, SkipTrue: skipTrue, SkipFalse: skipFalse}
 | |
| 			default:
 | |
| 				return ri
 | |
| 			}
 | |
| 		default:
 | |
| 			return ri
 | |
| 		}
 | |
| 
 | |
| 	case opClsReturn:
 | |
| 		switch ri.Op {
 | |
| 		case opClsReturn | opRetSrcA:
 | |
| 			return RetA{}
 | |
| 		case opClsReturn | opRetSrcConstant:
 | |
| 			return RetConstant{Val: ri.K}
 | |
| 		default:
 | |
| 			return ri
 | |
| 		}
 | |
| 
 | |
| 	case opClsMisc:
 | |
| 		switch ri.Op {
 | |
| 		case opClsMisc | opMiscTAX:
 | |
| 			return TAX{}
 | |
| 		case opClsMisc | opMiscTXA:
 | |
| 			return TXA{}
 | |
| 		default:
 | |
| 			return ri
 | |
| 		}
 | |
| 
 | |
| 	default:
 | |
| 		panic("unreachable") // switch is exhaustive on the bit pattern
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func jumpOpToTest(op jumpOp, skipTrue uint8, skipFalse uint8) (JumpTest, uint8, uint8) {
 | |
| 	var test JumpTest
 | |
| 
 | |
| 	// Decode "fake" jump conditions that don't appear in machine code
 | |
| 	// Ensures the Assemble -> Disassemble stage recreates the same instructions
 | |
| 	// See https://github.com/golang/go/issues/18470
 | |
| 	if skipTrue == 0 {
 | |
| 		switch op {
 | |
| 		case opJumpEqual:
 | |
| 			test = JumpNotEqual
 | |
| 		case opJumpGT:
 | |
| 			test = JumpLessOrEqual
 | |
| 		case opJumpGE:
 | |
| 			test = JumpLessThan
 | |
| 		case opJumpSet:
 | |
| 			test = JumpBitsNotSet
 | |
| 		}
 | |
| 
 | |
| 		return test, skipFalse, 0
 | |
| 	}
 | |
| 
 | |
| 	switch op {
 | |
| 	case opJumpEqual:
 | |
| 		test = JumpEqual
 | |
| 	case opJumpGT:
 | |
| 		test = JumpGreaterThan
 | |
| 	case opJumpGE:
 | |
| 		test = JumpGreaterOrEqual
 | |
| 	case opJumpSet:
 | |
| 		test = JumpBitsSet
 | |
| 	}
 | |
| 
 | |
| 	return test, skipTrue, skipFalse
 | |
| }
 | |
| 
 | |
| // LoadConstant loads Val into register Dst.
 | |
| type LoadConstant struct {
 | |
| 	Dst Register
 | |
| 	Val uint32
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a LoadConstant) Assemble() (RawInstruction, error) {
 | |
| 	return assembleLoad(a.Dst, 4, opAddrModeImmediate, a.Val)
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a LoadConstant) String() string {
 | |
| 	switch a.Dst {
 | |
| 	case RegA:
 | |
| 		return fmt.Sprintf("ld #%d", a.Val)
 | |
| 	case RegX:
 | |
| 		return fmt.Sprintf("ldx #%d", a.Val)
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // LoadScratch loads scratch[N] into register Dst.
 | |
| type LoadScratch struct {
 | |
| 	Dst Register
 | |
| 	N   int // 0-15
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a LoadScratch) Assemble() (RawInstruction, error) {
 | |
| 	if a.N < 0 || a.N > 15 {
 | |
| 		return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
 | |
| 	}
 | |
| 	return assembleLoad(a.Dst, 4, opAddrModeScratch, uint32(a.N))
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a LoadScratch) String() string {
 | |
| 	switch a.Dst {
 | |
| 	case RegA:
 | |
| 		return fmt.Sprintf("ld M[%d]", a.N)
 | |
| 	case RegX:
 | |
| 		return fmt.Sprintf("ldx M[%d]", a.N)
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // LoadAbsolute loads packet[Off:Off+Size] as an integer value into
 | |
| // register A.
 | |
| type LoadAbsolute struct {
 | |
| 	Off  uint32
 | |
| 	Size int // 1, 2 or 4
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a LoadAbsolute) Assemble() (RawInstruction, error) {
 | |
| 	return assembleLoad(RegA, a.Size, opAddrModeAbsolute, a.Off)
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a LoadAbsolute) String() string {
 | |
| 	switch a.Size {
 | |
| 	case 1: // byte
 | |
| 		return fmt.Sprintf("ldb [%d]", a.Off)
 | |
| 	case 2: // half word
 | |
| 		return fmt.Sprintf("ldh [%d]", a.Off)
 | |
| 	case 4: // word
 | |
| 		if a.Off > extOffset+0xffffffff {
 | |
| 			return LoadExtension{Num: Extension(a.Off + 0x1000)}.String()
 | |
| 		}
 | |
| 		return fmt.Sprintf("ld [%d]", a.Off)
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // LoadIndirect loads packet[X+Off:X+Off+Size] as an integer value
 | |
| // into register A.
 | |
| type LoadIndirect struct {
 | |
| 	Off  uint32
 | |
| 	Size int // 1, 2 or 4
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a LoadIndirect) Assemble() (RawInstruction, error) {
 | |
| 	return assembleLoad(RegA, a.Size, opAddrModeIndirect, a.Off)
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a LoadIndirect) String() string {
 | |
| 	switch a.Size {
 | |
| 	case 1: // byte
 | |
| 		return fmt.Sprintf("ldb [x + %d]", a.Off)
 | |
| 	case 2: // half word
 | |
| 		return fmt.Sprintf("ldh [x + %d]", a.Off)
 | |
| 	case 4: // word
 | |
| 		return fmt.Sprintf("ld [x + %d]", a.Off)
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // LoadMemShift multiplies the first 4 bits of the byte at packet[Off]
 | |
| // by 4 and stores the result in register X.
 | |
| //
 | |
| // This instruction is mainly useful to load into X the length of an
 | |
| // IPv4 packet header in a single instruction, rather than have to do
 | |
| // the arithmetic on the header's first byte by hand.
 | |
| type LoadMemShift struct {
 | |
| 	Off uint32
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a LoadMemShift) Assemble() (RawInstruction, error) {
 | |
| 	return assembleLoad(RegX, 1, opAddrModeMemShift, a.Off)
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a LoadMemShift) String() string {
 | |
| 	return fmt.Sprintf("ldx 4*([%d]&0xf)", a.Off)
 | |
| }
 | |
| 
 | |
| // LoadExtension invokes a linux-specific extension and stores the
 | |
| // result in register A.
 | |
| type LoadExtension struct {
 | |
| 	Num Extension
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a LoadExtension) Assemble() (RawInstruction, error) {
 | |
| 	if a.Num == ExtLen {
 | |
| 		return assembleLoad(RegA, 4, opAddrModePacketLen, 0)
 | |
| 	}
 | |
| 	return assembleLoad(RegA, 4, opAddrModeAbsolute, uint32(extOffset+a.Num))
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a LoadExtension) String() string {
 | |
| 	switch a.Num {
 | |
| 	case ExtLen:
 | |
| 		return "ld #len"
 | |
| 	case ExtProto:
 | |
| 		return "ld #proto"
 | |
| 	case ExtType:
 | |
| 		return "ld #type"
 | |
| 	case ExtPayloadOffset:
 | |
| 		return "ld #poff"
 | |
| 	case ExtInterfaceIndex:
 | |
| 		return "ld #ifidx"
 | |
| 	case ExtNetlinkAttr:
 | |
| 		return "ld #nla"
 | |
| 	case ExtNetlinkAttrNested:
 | |
| 		return "ld #nlan"
 | |
| 	case ExtMark:
 | |
| 		return "ld #mark"
 | |
| 	case ExtQueue:
 | |
| 		return "ld #queue"
 | |
| 	case ExtLinkLayerType:
 | |
| 		return "ld #hatype"
 | |
| 	case ExtRXHash:
 | |
| 		return "ld #rxhash"
 | |
| 	case ExtCPUID:
 | |
| 		return "ld #cpu"
 | |
| 	case ExtVLANTag:
 | |
| 		return "ld #vlan_tci"
 | |
| 	case ExtVLANTagPresent:
 | |
| 		return "ld #vlan_avail"
 | |
| 	case ExtVLANProto:
 | |
| 		return "ld #vlan_tpid"
 | |
| 	case ExtRand:
 | |
| 		return "ld #rand"
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // StoreScratch stores register Src into scratch[N].
 | |
| type StoreScratch struct {
 | |
| 	Src Register
 | |
| 	N   int // 0-15
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a StoreScratch) Assemble() (RawInstruction, error) {
 | |
| 	if a.N < 0 || a.N > 15 {
 | |
| 		return RawInstruction{}, fmt.Errorf("invalid scratch slot %d", a.N)
 | |
| 	}
 | |
| 	var op uint16
 | |
| 	switch a.Src {
 | |
| 	case RegA:
 | |
| 		op = opClsStoreA
 | |
| 	case RegX:
 | |
| 		op = opClsStoreX
 | |
| 	default:
 | |
| 		return RawInstruction{}, fmt.Errorf("invalid source register %v", a.Src)
 | |
| 	}
 | |
| 
 | |
| 	return RawInstruction{
 | |
| 		Op: op,
 | |
| 		K:  uint32(a.N),
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a StoreScratch) String() string {
 | |
| 	switch a.Src {
 | |
| 	case RegA:
 | |
| 		return fmt.Sprintf("st M[%d]", a.N)
 | |
| 	case RegX:
 | |
| 		return fmt.Sprintf("stx M[%d]", a.N)
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ALUOpConstant executes A = A <Op> Val.
 | |
| type ALUOpConstant struct {
 | |
| 	Op  ALUOp
 | |
| 	Val uint32
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a ALUOpConstant) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsALU | uint16(opOperandConstant) | uint16(a.Op),
 | |
| 		K:  a.Val,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a ALUOpConstant) String() string {
 | |
| 	switch a.Op {
 | |
| 	case ALUOpAdd:
 | |
| 		return fmt.Sprintf("add #%d", a.Val)
 | |
| 	case ALUOpSub:
 | |
| 		return fmt.Sprintf("sub #%d", a.Val)
 | |
| 	case ALUOpMul:
 | |
| 		return fmt.Sprintf("mul #%d", a.Val)
 | |
| 	case ALUOpDiv:
 | |
| 		return fmt.Sprintf("div #%d", a.Val)
 | |
| 	case ALUOpMod:
 | |
| 		return fmt.Sprintf("mod #%d", a.Val)
 | |
| 	case ALUOpAnd:
 | |
| 		return fmt.Sprintf("and #%d", a.Val)
 | |
| 	case ALUOpOr:
 | |
| 		return fmt.Sprintf("or #%d", a.Val)
 | |
| 	case ALUOpXor:
 | |
| 		return fmt.Sprintf("xor #%d", a.Val)
 | |
| 	case ALUOpShiftLeft:
 | |
| 		return fmt.Sprintf("lsh #%d", a.Val)
 | |
| 	case ALUOpShiftRight:
 | |
| 		return fmt.Sprintf("rsh #%d", a.Val)
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ALUOpX executes A = A <Op> X
 | |
| type ALUOpX struct {
 | |
| 	Op ALUOp
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a ALUOpX) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsALU | uint16(opOperandX) | uint16(a.Op),
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a ALUOpX) String() string {
 | |
| 	switch a.Op {
 | |
| 	case ALUOpAdd:
 | |
| 		return "add x"
 | |
| 	case ALUOpSub:
 | |
| 		return "sub x"
 | |
| 	case ALUOpMul:
 | |
| 		return "mul x"
 | |
| 	case ALUOpDiv:
 | |
| 		return "div x"
 | |
| 	case ALUOpMod:
 | |
| 		return "mod x"
 | |
| 	case ALUOpAnd:
 | |
| 		return "and x"
 | |
| 	case ALUOpOr:
 | |
| 		return "or x"
 | |
| 	case ALUOpXor:
 | |
| 		return "xor x"
 | |
| 	case ALUOpShiftLeft:
 | |
| 		return "lsh x"
 | |
| 	case ALUOpShiftRight:
 | |
| 		return "rsh x"
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown instruction: %#v", a)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NegateA executes A = -A.
 | |
| type NegateA struct{}
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a NegateA) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsALU | uint16(aluOpNeg),
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a NegateA) String() string {
 | |
| 	return fmt.Sprintf("neg")
 | |
| }
 | |
| 
 | |
| // Jump skips the following Skip instructions in the program.
 | |
| type Jump struct {
 | |
| 	Skip uint32
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a Jump) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsJump | uint16(opJumpAlways),
 | |
| 		K:  a.Skip,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a Jump) String() string {
 | |
| 	return fmt.Sprintf("ja %d", a.Skip)
 | |
| }
 | |
| 
 | |
| // JumpIf skips the following Skip instructions in the program if A
 | |
| // <Cond> Val is true.
 | |
| type JumpIf struct {
 | |
| 	Cond      JumpTest
 | |
| 	Val       uint32
 | |
| 	SkipTrue  uint8
 | |
| 	SkipFalse uint8
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a JumpIf) Assemble() (RawInstruction, error) {
 | |
| 	return jumpToRaw(a.Cond, opOperandConstant, a.Val, a.SkipTrue, a.SkipFalse)
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a JumpIf) String() string {
 | |
| 	return jumpToString(a.Cond, fmt.Sprintf("#%d", a.Val), a.SkipTrue, a.SkipFalse)
 | |
| }
 | |
| 
 | |
| // JumpIfX skips the following Skip instructions in the program if A
 | |
| // <Cond> X is true.
 | |
| type JumpIfX struct {
 | |
| 	Cond      JumpTest
 | |
| 	SkipTrue  uint8
 | |
| 	SkipFalse uint8
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a JumpIfX) Assemble() (RawInstruction, error) {
 | |
| 	return jumpToRaw(a.Cond, opOperandX, 0, a.SkipTrue, a.SkipFalse)
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a JumpIfX) String() string {
 | |
| 	return jumpToString(a.Cond, "x", a.SkipTrue, a.SkipFalse)
 | |
| }
 | |
| 
 | |
| // jumpToRaw assembles a jump instruction into a RawInstruction
 | |
| func jumpToRaw(test JumpTest, operand opOperand, k uint32, skipTrue, skipFalse uint8) (RawInstruction, error) {
 | |
| 	var (
 | |
| 		cond jumpOp
 | |
| 		flip bool
 | |
| 	)
 | |
| 	switch test {
 | |
| 	case JumpEqual:
 | |
| 		cond = opJumpEqual
 | |
| 	case JumpNotEqual:
 | |
| 		cond, flip = opJumpEqual, true
 | |
| 	case JumpGreaterThan:
 | |
| 		cond = opJumpGT
 | |
| 	case JumpLessThan:
 | |
| 		cond, flip = opJumpGE, true
 | |
| 	case JumpGreaterOrEqual:
 | |
| 		cond = opJumpGE
 | |
| 	case JumpLessOrEqual:
 | |
| 		cond, flip = opJumpGT, true
 | |
| 	case JumpBitsSet:
 | |
| 		cond = opJumpSet
 | |
| 	case JumpBitsNotSet:
 | |
| 		cond, flip = opJumpSet, true
 | |
| 	default:
 | |
| 		return RawInstruction{}, fmt.Errorf("unknown JumpTest %v", test)
 | |
| 	}
 | |
| 	jt, jf := skipTrue, skipFalse
 | |
| 	if flip {
 | |
| 		jt, jf = jf, jt
 | |
| 	}
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsJump | uint16(cond) | uint16(operand),
 | |
| 		Jt: jt,
 | |
| 		Jf: jf,
 | |
| 		K:  k,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // jumpToString converts a jump instruction to assembler notation
 | |
| func jumpToString(cond JumpTest, operand string, skipTrue, skipFalse uint8) string {
 | |
| 	switch cond {
 | |
| 	// K == A
 | |
| 	case JumpEqual:
 | |
| 		return conditionalJump(operand, skipTrue, skipFalse, "jeq", "jneq")
 | |
| 	// K != A
 | |
| 	case JumpNotEqual:
 | |
| 		return fmt.Sprintf("jneq %s,%d", operand, skipTrue)
 | |
| 	// K > A
 | |
| 	case JumpGreaterThan:
 | |
| 		return conditionalJump(operand, skipTrue, skipFalse, "jgt", "jle")
 | |
| 	// K < A
 | |
| 	case JumpLessThan:
 | |
| 		return fmt.Sprintf("jlt %s,%d", operand, skipTrue)
 | |
| 	// K >= A
 | |
| 	case JumpGreaterOrEqual:
 | |
| 		return conditionalJump(operand, skipTrue, skipFalse, "jge", "jlt")
 | |
| 	// K <= A
 | |
| 	case JumpLessOrEqual:
 | |
| 		return fmt.Sprintf("jle %s,%d", operand, skipTrue)
 | |
| 	// K & A != 0
 | |
| 	case JumpBitsSet:
 | |
| 		if skipFalse > 0 {
 | |
| 			return fmt.Sprintf("jset %s,%d,%d", operand, skipTrue, skipFalse)
 | |
| 		}
 | |
| 		return fmt.Sprintf("jset %s,%d", operand, skipTrue)
 | |
| 	// K & A == 0, there is no assembler instruction for JumpBitNotSet, use JumpBitSet and invert skips
 | |
| 	case JumpBitsNotSet:
 | |
| 		return jumpToString(JumpBitsSet, operand, skipFalse, skipTrue)
 | |
| 	default:
 | |
| 		return fmt.Sprintf("unknown JumpTest %#v", cond)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func conditionalJump(operand string, skipTrue, skipFalse uint8, positiveJump, negativeJump string) string {
 | |
| 	if skipTrue > 0 {
 | |
| 		if skipFalse > 0 {
 | |
| 			return fmt.Sprintf("%s %s,%d,%d", positiveJump, operand, skipTrue, skipFalse)
 | |
| 		}
 | |
| 		return fmt.Sprintf("%s %s,%d", positiveJump, operand, skipTrue)
 | |
| 	}
 | |
| 	return fmt.Sprintf("%s %s,%d", negativeJump, operand, skipFalse)
 | |
| }
 | |
| 
 | |
| // RetA exits the BPF program, returning the value of register A.
 | |
| type RetA struct{}
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a RetA) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsReturn | opRetSrcA,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a RetA) String() string {
 | |
| 	return fmt.Sprintf("ret a")
 | |
| }
 | |
| 
 | |
| // RetConstant exits the BPF program, returning a constant value.
 | |
| type RetConstant struct {
 | |
| 	Val uint32
 | |
| }
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a RetConstant) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsReturn | opRetSrcConstant,
 | |
| 		K:  a.Val,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a RetConstant) String() string {
 | |
| 	return fmt.Sprintf("ret #%d", a.Val)
 | |
| }
 | |
| 
 | |
| // TXA copies the value of register X to register A.
 | |
| type TXA struct{}
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a TXA) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsMisc | opMiscTXA,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a TXA) String() string {
 | |
| 	return fmt.Sprintf("txa")
 | |
| }
 | |
| 
 | |
| // TAX copies the value of register A to register X.
 | |
| type TAX struct{}
 | |
| 
 | |
| // Assemble implements the Instruction Assemble method.
 | |
| func (a TAX) Assemble() (RawInstruction, error) {
 | |
| 	return RawInstruction{
 | |
| 		Op: opClsMisc | opMiscTAX,
 | |
| 	}, nil
 | |
| }
 | |
| 
 | |
| // String returns the instruction in assembler notation.
 | |
| func (a TAX) String() string {
 | |
| 	return fmt.Sprintf("tax")
 | |
| }
 | |
| 
 | |
| func assembleLoad(dst Register, loadSize int, mode uint16, k uint32) (RawInstruction, error) {
 | |
| 	var (
 | |
| 		cls uint16
 | |
| 		sz  uint16
 | |
| 	)
 | |
| 	switch dst {
 | |
| 	case RegA:
 | |
| 		cls = opClsLoadA
 | |
| 	case RegX:
 | |
| 		cls = opClsLoadX
 | |
| 	default:
 | |
| 		return RawInstruction{}, fmt.Errorf("invalid target register %v", dst)
 | |
| 	}
 | |
| 	switch loadSize {
 | |
| 	case 1:
 | |
| 		sz = opLoadWidth1
 | |
| 	case 2:
 | |
| 		sz = opLoadWidth2
 | |
| 	case 4:
 | |
| 		sz = opLoadWidth4
 | |
| 	default:
 | |
| 		return RawInstruction{}, fmt.Errorf("invalid load byte length %d", sz)
 | |
| 	}
 | |
| 	return RawInstruction{
 | |
| 		Op: cls | sz | mode,
 | |
| 		K:  k,
 | |
| 	}, nil
 | |
| }
 |