123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726 |
- // 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
- }
|