mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 14:42:26 -05:00 
			
		
		
		
	
		
			
	
	
		
			727 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			727 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 | ||
|  | } |