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
make_instruction_map() -> BTreeMap<String, (InstructionType, u8)>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
insn(opc: u8, dst: i64, src: i64, off: i64, imm: i64) -> Result<Insn, String>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.
operands_tuple(operands: &[Operand]) -> Result<(Operand, Operand, Operand), String>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
encode(inst_type: InstructionType, opc: u8, operands: &[Operand]) -> Result<Insn, String>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
assemble_internal(parsed: &[Instruction]) -> Result<Vec<Insn>, String>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 /// ```
assemble(src: &str) -> Result<Vec<u8>, String>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