github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/bpf/decoder.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package bpf 16 17 import ( 18 "bytes" 19 "fmt" 20 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 ) 23 24 // DecodeProgram translates a compiled BPF program into text format. 25 func DecodeProgram(p Program) (string, error) { 26 return DecodeInstructions(p.instructions) 27 } 28 29 // DecodeInstructions translates an array of BPF instructions into text format. 30 func DecodeInstructions(instns []linux.BPFInstruction) (string, error) { 31 var ret bytes.Buffer 32 for line, s := range instns { 33 ret.WriteString(fmt.Sprintf("%v: ", line)) 34 if err := decode(s, line, &ret); err != nil { 35 return "", err 36 } 37 ret.WriteString("\n") 38 } 39 return ret.String(), nil 40 } 41 42 // Decode translates a single BPF instruction into text format. 43 func Decode(inst linux.BPFInstruction) (string, error) { 44 var ret bytes.Buffer 45 err := decode(inst, -1, &ret) 46 return ret.String(), err 47 } 48 49 func decode(inst linux.BPFInstruction, line int, w *bytes.Buffer) error { 50 var err error 51 switch inst.OpCode & instructionClassMask { 52 case Ld: 53 err = decodeLd(inst, w) 54 case Ldx: 55 err = decodeLdx(inst, w) 56 case St: 57 w.WriteString(fmt.Sprintf("M[%v] <- A", inst.K)) 58 case Stx: 59 w.WriteString(fmt.Sprintf("M[%v] <- X", inst.K)) 60 case Alu: 61 err = decodeAlu(inst, w) 62 case Jmp: 63 err = decodeJmp(inst, line, w) 64 case Ret: 65 err = decodeRet(inst, w) 66 case Misc: 67 err = decodeMisc(inst, w) 68 default: 69 return fmt.Errorf("invalid BPF instruction: %v", inst) 70 } 71 return err 72 } 73 74 // A <- P[k:4] 75 func decodeLd(inst linux.BPFInstruction, w *bytes.Buffer) error { 76 w.WriteString("A <- ") 77 78 switch inst.OpCode & loadModeMask { 79 case Imm: 80 w.WriteString(fmt.Sprintf("%v", inst.K)) 81 case Abs: 82 w.WriteString(fmt.Sprintf("P[%v:", inst.K)) 83 if err := decodeLdSize(inst, w); err != nil { 84 return err 85 } 86 w.WriteString("]") 87 case Ind: 88 w.WriteString(fmt.Sprintf("P[X+%v:", inst.K)) 89 if err := decodeLdSize(inst, w); err != nil { 90 return err 91 } 92 w.WriteString("]") 93 case Mem: 94 w.WriteString(fmt.Sprintf("M[%v]", inst.K)) 95 case Len: 96 w.WriteString("len") 97 default: 98 return fmt.Errorf("invalid BPF LD instruction: %v", inst) 99 } 100 return nil 101 } 102 103 func decodeLdSize(inst linux.BPFInstruction, w *bytes.Buffer) error { 104 switch inst.OpCode & loadSizeMask { 105 case W: 106 w.WriteString("4") 107 case H: 108 w.WriteString("2") 109 case B: 110 w.WriteString("1") 111 default: 112 return fmt.Errorf("invalid BPF LD size: %v", inst) 113 } 114 return nil 115 } 116 117 // X <- P[k:4] 118 func decodeLdx(inst linux.BPFInstruction, w *bytes.Buffer) error { 119 w.WriteString("X <- ") 120 121 switch inst.OpCode & loadModeMask { 122 case Imm: 123 w.WriteString(fmt.Sprintf("%v", inst.K)) 124 case Mem: 125 w.WriteString(fmt.Sprintf("M[%v]", inst.K)) 126 case Len: 127 w.WriteString("len") 128 case Msh: 129 w.WriteString(fmt.Sprintf("4*(P[%v:1]&0xf)", inst.K)) 130 default: 131 return fmt.Errorf("invalid BPF LDX instruction: %v", inst) 132 } 133 return nil 134 } 135 136 // A <- A + k 137 func decodeAlu(inst linux.BPFInstruction, w *bytes.Buffer) error { 138 code := inst.OpCode & aluMask 139 if code == Neg { 140 w.WriteString("A <- -A") 141 return nil 142 } 143 144 w.WriteString("A <- A ") 145 switch code { 146 case Add: 147 w.WriteString("+ ") 148 case Sub: 149 w.WriteString("- ") 150 case Mul: 151 w.WriteString("* ") 152 case Div: 153 w.WriteString("/ ") 154 case Or: 155 w.WriteString("| ") 156 case And: 157 w.WriteString("& ") 158 case Lsh: 159 w.WriteString("<< ") 160 case Rsh: 161 w.WriteString(">> ") 162 case Mod: 163 w.WriteString("% ") 164 case Xor: 165 w.WriteString("^ ") 166 default: 167 return fmt.Errorf("invalid BPF ALU instruction: %v", inst) 168 } 169 return decodeSource(inst, w) 170 } 171 172 func decodeSource(inst linux.BPFInstruction, w *bytes.Buffer) error { 173 switch inst.OpCode & srcAluJmpMask { 174 case K: 175 w.WriteString(fmt.Sprintf("%v", inst.K)) 176 case X: 177 w.WriteString("X") 178 default: 179 return fmt.Errorf("invalid BPF ALU/JMP source instruction: %v", inst) 180 } 181 return nil 182 } 183 184 // pc += (A > k) ? jt : jf 185 func decodeJmp(inst linux.BPFInstruction, line int, w *bytes.Buffer) error { 186 code := inst.OpCode & jmpMask 187 188 w.WriteString("pc += ") 189 if code == Ja { 190 w.WriteString(printJmpTarget(inst.K, line)) 191 } else { 192 w.WriteString("(A ") 193 switch code { 194 case Jeq: 195 w.WriteString("== ") 196 case Jgt: 197 w.WriteString("> ") 198 case Jge: 199 w.WriteString(">= ") 200 case Jset: 201 w.WriteString("& ") 202 default: 203 return fmt.Errorf("invalid BPF ALU instruction: %v", inst) 204 } 205 if err := decodeSource(inst, w); err != nil { 206 return err 207 } 208 w.WriteString( 209 fmt.Sprintf(") ? %s : %s", 210 printJmpTarget(uint32(inst.JumpIfTrue), line), 211 printJmpTarget(uint32(inst.JumpIfFalse), line))) 212 } 213 return nil 214 } 215 216 func printJmpTarget(target uint32, line int) string { 217 if line == -1 { 218 return fmt.Sprintf("%v", target) 219 } 220 return fmt.Sprintf("%v [%v]", target, int(target)+line+1) 221 } 222 223 // ret k 224 func decodeRet(inst linux.BPFInstruction, w *bytes.Buffer) error { 225 w.WriteString("ret ") 226 227 code := inst.OpCode & srcRetMask 228 switch code { 229 case K: 230 w.WriteString(fmt.Sprintf("%v", inst.K)) 231 case A: 232 w.WriteString("A") 233 default: 234 return fmt.Errorf("invalid BPF RET source instruction: %v", inst) 235 } 236 return nil 237 } 238 239 func decodeMisc(inst linux.BPFInstruction, w *bytes.Buffer) error { 240 code := inst.OpCode & miscMask 241 switch code { 242 case Tax: 243 w.WriteString("X <- A") 244 case Txa: 245 w.WriteString("A <- X") 246 default: 247 return fmt.Errorf("invalid BPF ALU/JMP source instruction: %v", inst) 248 } 249 return nil 250 }