1 // SPDX-License-Identifier: (Apache-2.0 OR MIT) 2 // Copyright 2017 Rich Lane <lanerl@gmail.com> 3 4 //! This module translates eBPF assembly language to binary. 5 6 use alloc::{ 7 collections::BTreeMap, 8 format, 9 string::{String, ToString}, 10 vec, 11 vec::Vec, 12 }; 13 14 use self::InstructionType::{ 15 AluBinary, AluUnary, Call, Endian, JumpConditional, JumpUnconditional, LoadAbs, LoadImm, 16 LoadInd, LoadReg, NoOperand, StoreImm, StoreReg, 17 }; 18 use crate::{ 19 asm_parser::{ 20 parse, Instruction, Operand, 21 Operand::{Integer, Memory, Nil, Register}, 22 }, 23 ebpf::{self, Insn}, 24 }; 25 26 #[derive(Clone, Copy, Debug, PartialEq)] 27 enum InstructionType { 28 AluBinary, 29 AluUnary, 30 LoadImm, 31 LoadAbs, 32 LoadInd, 33 LoadReg, 34 StoreImm, 35 StoreReg, 36 JumpUnconditional, 37 JumpConditional, 38 Call, 39 Endian(i64), 40 NoOperand, 41 } 42 43 fn make_instruction_map() -> BTreeMap<String, (InstructionType, u8)> { 44 let mut result = BTreeMap::new(); 45 46 let alu_binary_ops = [ 47 ("add", ebpf::BPF_ADD), 48 ("sub", ebpf::BPF_SUB), 49 ("mul", ebpf::BPF_MUL), 50 ("div", ebpf::BPF_DIV), 51 ("or", ebpf::BPF_OR), 52 ("and", ebpf::BPF_AND), 53 ("lsh", ebpf::BPF_LSH), 54 ("rsh", ebpf::BPF_RSH), 55 ("mod", ebpf::BPF_MOD), 56 ("xor", ebpf::BPF_XOR), 57 ("mov", ebpf::BPF_MOV), 58 ("arsh", ebpf::BPF_ARSH), 59 ]; 60 61 let mem_sizes = [ 62 ("w", ebpf::BPF_W), 63 ("h", ebpf::BPF_H), 64 ("b", ebpf::BPF_B), 65 ("dw", ebpf::BPF_DW), 66 ]; 67 68 let jump_conditions = [ 69 ("jeq", ebpf::BPF_JEQ), 70 ("jgt", ebpf::BPF_JGT), 71 ("jge", ebpf::BPF_JGE), 72 ("jlt", ebpf::BPF_JLT), 73 ("jle", ebpf::BPF_JLE), 74 ("jset", ebpf::BPF_JSET), 75 ("jne", ebpf::BPF_JNE), 76 ("jsgt", ebpf::BPF_JSGT), 77 ("jsge", ebpf::BPF_JSGE), 78 ("jslt", ebpf::BPF_JSLT), 79 ("jsle", ebpf::BPF_JSLE), 80 ]; 81 82 { 83 let mut entry = |name: &str, inst_type: InstructionType, opc: u8| { 84 result.insert(name.to_string(), (inst_type, opc)) 85 }; 86 87 // Miscellaneous. 88 entry("exit", NoOperand, ebpf::EXIT); 89 entry("ja", JumpUnconditional, ebpf::JA); 90 entry("call", Call, ebpf::CALL); 91 entry("lddw", LoadImm, ebpf::LD_DW_IMM); 92 93 // AluUnary. 94 entry("neg", AluUnary, ebpf::NEG64); 95 entry("neg32", AluUnary, ebpf::NEG32); 96 entry("neg64", AluUnary, ebpf::NEG64); 97 98 // AluBinary. 99 for &(name, opc) in &alu_binary_ops { 100 entry(name, AluBinary, ebpf::BPF_ALU64 | opc); 101 entry(&format!("{name}32"), AluBinary, ebpf::BPF_ALU | opc); 102 entry(&format!("{name}64"), AluBinary, ebpf::BPF_ALU64 | opc); 103 } 104 105 // LoadAbs, LoadInd, LoadReg, StoreImm, and StoreReg. 106 for &(suffix, size) in &mem_sizes { 107 entry( 108 &format!("ldabs{suffix}"), 109 LoadAbs, 110 ebpf::BPF_ABS | ebpf::BPF_LD | size, 111 ); 112 entry( 113 &format!("ldind{suffix}"), 114 LoadInd, 115 ebpf::BPF_IND | ebpf::BPF_LD | size, 116 ); 117 entry( 118 &format!("ldx{suffix}"), 119 LoadReg, 120 ebpf::BPF_MEM | ebpf::BPF_LDX | size, 121 ); 122 entry( 123 &format!("st{suffix}"), 124 StoreImm, 125 ebpf::BPF_MEM | ebpf::BPF_ST | size, 126 ); 127 entry( 128 &format!("stx{suffix}"), 129 StoreReg, 130 ebpf::BPF_MEM | ebpf::BPF_STX | size, 131 ); 132 } 133 134 // JumpConditional. 135 for &(name, condition) in &jump_conditions { 136 entry(name, JumpConditional, ebpf::BPF_JMP | condition); 137 entry( 138 &format!("{name}32"), 139 JumpConditional, 140 ebpf::BPF_JMP32 | condition, 141 ); 142 } 143 144 // Endian. 145 for &size in &[16, 32, 64] { 146 entry(&format!("be{size}"), Endian(size), ebpf::BE); 147 entry(&format!("le{size}"), Endian(size), ebpf::LE); 148 } 149 } 150 151 result 152 } 153 154 fn insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result<Insn, String> { 155 if !(0..16).contains(&dst) { 156 return Err(format!("Invalid destination register {dst}")); 157 } 158 if dst < 0 || src >= 16 { 159 return Err(format!("Invalid source register {src}")); 160 } 161 if !(-32768..32768).contains(&off) { 162 return Err(format!("Invalid offset {off}")); 163 } 164 if !(-2147483648..2147483648).contains(&imm) { 165 return Err(format!("Invalid immediate {imm}")); 166 } 167 Ok(Insn { 168 opc, 169 dst: dst as u8, 170 src: src as u8, 171 off: off as i16, 172 imm: imm as i32, 173 }) 174 } 175 176 // TODO Use slice patterns when available and remove this function. 177 fn operands_tuple(operands: &[Operand]) -> Result<(Operand, Operand, Operand), String> { 178 match operands.len() { 179 0 => Ok((Nil, Nil, Nil)), 180 1 => Ok((operands[0], Nil, Nil)), 181 2 => Ok((operands[0], operands[1], Nil)), 182 3 => Ok((operands[0], operands[1], operands[2])), 183 _ => Err("Too many operands".to_string()), 184 } 185 } 186 187 fn encode(inst_type: InstructionType, opc: u8, operands: &[Operand]) -> Result<Insn, String> { 188 let (a, b, c) = (operands_tuple(operands))?; 189 match (inst_type, a, b, c) { 190 (AluBinary, Register(dst), Register(src), Nil) => insn(opc | ebpf::BPF_X, dst, src, 0, 0), 191 (AluBinary, Register(dst), Integer(imm), Nil) => insn(opc | ebpf::BPF_K, dst, 0, 0, imm), 192 (AluUnary, Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, 0), 193 (LoadAbs, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm), 194 (LoadInd, Register(src), Integer(imm), Nil) => insn(opc, 0, src, 0, imm), 195 (LoadReg, Register(dst), Memory(src, off), Nil) 196 | (StoreReg, Memory(dst, off), Register(src), Nil) => insn(opc, dst, src, off, 0), 197 (StoreImm, Memory(dst, off), Integer(imm), Nil) => insn(opc, dst, 0, off, imm), 198 (NoOperand, Nil, Nil, Nil) => insn(opc, 0, 0, 0, 0), 199 (JumpUnconditional, Integer(off), Nil, Nil) => insn(opc, 0, 0, off, 0), 200 (JumpConditional, Register(dst), Register(src), Integer(off)) => { 201 insn(opc | ebpf::BPF_X, dst, src, off, 0) 202 } 203 (JumpConditional, Register(dst), Integer(imm), Integer(off)) => { 204 insn(opc | ebpf::BPF_K, dst, 0, off, imm) 205 } 206 (Call, Integer(imm), Nil, Nil) => insn(opc, 0, 0, 0, imm), 207 (Endian(size), Register(dst), Nil, Nil) => insn(opc, dst, 0, 0, size), 208 (LoadImm, Register(dst), Integer(imm), Nil) => insn(opc, dst, 0, 0, (imm << 32) >> 32), 209 _ => Err(format!("Unexpected operands: {operands:?}")), 210 } 211 } 212 213 fn assemble_internal(parsed: &[Instruction]) -> Result<Vec<Insn>, String> { 214 let instruction_map = make_instruction_map(); 215 let mut result: Vec<Insn> = vec![]; 216 for instruction in parsed { 217 let name = instruction.name.as_str(); 218 match instruction_map.get(name) { 219 Some(&(inst_type, opc)) => { 220 match encode(inst_type, opc, &instruction.operands) { 221 Ok(insn) => result.push(insn), 222 Err(msg) => return Err(format!("Failed to encode {name}: {msg}")), 223 } 224 // Special case for lddw. 225 if let LoadImm = inst_type { 226 if let Integer(imm) = instruction.operands[1] { 227 result.push(insn(0, 0, 0, 0, imm >> 32).unwrap()); 228 } 229 } 230 } 231 None => return Err(format!("Invalid instruction {name:?}")), 232 } 233 } 234 Ok(result) 235 } 236 237 /// Parse assembly source and translate to binary. 238 /// 239 /// # Examples 240 /// 241 /// ``` 242 /// use rbpf::assembler::assemble; 243 /// let prog = assemble("add64 r1, 0x605 244 /// mov64 r2, 0x32 245 /// mov64 r1, r0 246 /// be16 r0 247 /// neg64 r2 248 /// exit"); 249 /// println!("{:?}", prog); 250 /// # assert_eq!(prog, 251 /// # Ok(vec![0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 252 /// # 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 253 /// # 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 254 /// # 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 255 /// # 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 256 /// # 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 257 /// ``` 258 /// 259 /// This will produce the following output: 260 /// 261 /// ```test 262 /// Ok([0x07, 0x01, 0x00, 0x00, 0x05, 0x06, 0x00, 0x00, 263 /// 0xb7, 0x02, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 264 /// 0xbf, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 265 /// 0xdc, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 266 /// 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 267 /// 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 268 /// ``` 269 pub fn assemble(src: &str) -> Result<Vec<u8>, String> { 270 let parsed = (parse(src))?; 271 let insns = (assemble_internal(&parsed))?; 272 let mut result: Vec<u8> = vec![]; 273 for insn in insns { 274 result.extend_from_slice(&insn.to_array()); 275 } 276 Ok(result) 277 } 278