sbpf_assembler/
instruction.rs

1use crate::dynsym::RelocationType;
2use crate::lexer::{ImmediateValue, Token};
3use crate::syscall::SYSCALLS;
4use crate::errors::CompileError;
5use sbpf_common::opcode::Opcode;
6
7use std::ops::Range;
8
9#[derive(Debug, Clone)]
10pub struct Instruction {
11    pub opcode: Opcode,
12    pub operands: Vec<Token>,
13    pub span: Range<usize>,
14}
15
16impl Instruction {
17    //
18    pub fn get_size(&self) -> u64 {
19        match self.opcode {
20            Opcode::Lddw => 16,
21            _ => 8,
22        }
23    }
24    //
25    pub fn needs_relocation(&self) -> bool {
26        match self.opcode {
27            Opcode::Call | Opcode::Lddw => {
28                matches!(&self.operands.last(), Some(Token::Identifier(_, _)))
29            }
30            _ => false,
31        }
32    }
33
34    //
35    pub fn is_jump(&self) -> bool {
36        matches!(
37            self.opcode,
38            Opcode::Ja
39                | Opcode::JeqImm
40                | Opcode::JgtImm
41                | Opcode::JgeImm
42                | Opcode::JltImm
43                | Opcode::JleImm
44                | Opcode::JsetImm
45                | Opcode::JneImm
46                | Opcode::JsgtImm
47                | Opcode::JsgeImm
48                | Opcode::JsltImm
49                | Opcode::JsleImm
50                | Opcode::JeqReg
51                | Opcode::JgtReg
52                | Opcode::JgeReg
53                | Opcode::JltReg
54                | Opcode::JleReg
55                | Opcode::JsetReg
56                | Opcode::JneReg
57                | Opcode::JsgtReg
58                | Opcode::JsgeReg
59                | Opcode::JsltReg
60                | Opcode::JsleReg
61        )
62    }
63    //
64    pub fn get_relocation_info(&self) -> (RelocationType, String) {
65        match self.opcode {
66            Opcode::Lddw => match &self.operands[1] {
67                Token::Identifier(name, _) => (RelocationType::RSbf64Relative, name.clone()),
68                _ => panic!("Expected label operand"),
69            },
70            _ => {
71                if let Token::Identifier(name, _) = &self.operands[0] {
72                    (RelocationType::RSbfSyscall, name.clone())
73                } else {
74                    panic!("Expected label operand")
75                }
76            }
77        }
78    }
79    //
80    pub fn from_bytes(bytes: &[u8]) -> Result<Self, CompileError> {
81        let mut operands = Vec::new();
82        let span = 0..bytes.len();
83
84        let opcode = Opcode::from_u8(bytes[0]).unwrap();
85        let reg = bytes[1];
86        let src = reg >> 4;
87        let dst = reg & 0x0f;
88        let off = i16::from_le_bytes([bytes[2], bytes[3]]);
89        let imm = match opcode {
90            Opcode::Lddw => {
91                let imm_low = i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
92                let imm_high = i32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
93
94                ((imm_high as i64) << 32) | (imm_low as u32 as i64)
95            }
96            _ => i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]) as i64,
97        };
98
99        match opcode {
100            Opcode::Lddw => {
101                if src != 0 || off != 0 {
102                    return Err(CompileError::BytecodeError {
103                        error: format!("Lddw instruction expects src and off to be 0, but got src: {}, off: {}", src, off),
104                        span: span.clone(),
105                        custom_label: None,
106                    });
107                }
108                operands.push(Token::Register(dst, 1..2));
109                operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..12));
110            }
111
112            Opcode::Call => {
113                if let Some(name) = SYSCALLS.get(&(imm as u32)) {
114                    if reg != 0 || off != 0 {
115                        return Err(CompileError::BytecodeError {
116                            error: format!("Call instruction with syscall expects reg and off to be 0, but got reg: {}, off: {}", reg, off),
117                            span: span.clone(),
118                            custom_label: None,
119                        });
120                    }
121                    operands.push(Token::Identifier(name.to_string(), 4..8));
122                } else {
123                    if reg != 16 || off != 0 {
124                        return Err(CompileError::BytecodeError {
125                            error: format!("Call instruction with immediate expects reg to be 16 and off to be 0, but got reg: {}, off: {}", reg, off),
126                            span: span.clone(),
127                            custom_label: None,
128                        });
129                    }
130                    operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
131                }
132            }
133
134            Opcode::Callx => {
135                if src != 0 || off != 0 || imm != 0 {
136                    return Err(CompileError::BytecodeError {
137                        error: format!("Callx instruction expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
138                        span: span.clone(),
139                        custom_label: None,
140                    });
141                }
142                // callx destination register is encoded in the dst field
143                operands.push(Token::Register(dst, 1..2));
144            }
145
146            Opcode::Ja => {
147                if reg != 0 || imm != 0 {
148                    return Err(CompileError::BytecodeError {
149                        error: format!("Ja instruction expects reg and imm to be 0, but got reg: {}, imm: {}", reg, imm),
150                        span: span.clone(),
151                        custom_label: None,
152                    });
153                }
154                operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
155            }
156
157            Opcode::JeqImm
158            | Opcode::JgtImm
159            | Opcode::JgeImm
160            | Opcode::JltImm
161            | Opcode::JleImm
162            | Opcode::JsetImm
163            | Opcode::JneImm
164            | Opcode::JsgtImm
165            | Opcode::JsgeImm
166            | Opcode::JsltImm
167            | Opcode::JsleImm => {
168                if src != 0 {
169                    return Err(CompileError::BytecodeError {
170                        error: format!("Jump instruction with immediate expects src to be 0, but got src: {}", src),
171                        span: span.clone(),
172                        custom_label: None,
173                    });
174                }
175                operands.push(Token::Register(dst, 1..2));
176                operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
177                operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
178            }
179
180            Opcode::JeqReg
181            | Opcode::JgtReg
182            | Opcode::JgeReg
183            | Opcode::JltReg
184            | Opcode::JleReg
185            | Opcode::JsetReg
186            | Opcode::JneReg
187            | Opcode::JsgtReg
188            | Opcode::JsgeReg
189            | Opcode::JsltReg
190            | Opcode::JsleReg => {
191                if imm != 0 {
192                    return Err(CompileError::BytecodeError {
193                        error: format!("Jump instruction with register expects imm to be 0, but got imm: {}", imm),
194                        span: span.clone(),
195                        custom_label: None,
196                    });
197                }
198                operands.push(Token::Register(dst, 1..2));
199                operands.push(Token::Register(src, 1..2));
200                operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
201            }
202
203            // Arithmetic instructions with immediate values
204            Opcode::Add32Imm
205            | Opcode::Sub32Imm
206            | Opcode::Mul32Imm
207            | Opcode::Div32Imm
208            | Opcode::Or32Imm
209            | Opcode::And32Imm
210            | Opcode::Lsh32Imm
211            | Opcode::Rsh32Imm
212            | Opcode::Mod32Imm
213            | Opcode::Xor32Imm
214            | Opcode::Mov32Imm
215            | Opcode::Arsh32Imm
216            | Opcode::Lmul32Imm
217            | Opcode::Udiv32Imm
218            | Opcode::Urem32Imm
219            | Opcode::Sdiv32Imm
220            | Opcode::Srem32Imm
221            | Opcode::Add64Imm
222            | Opcode::Sub64Imm
223            | Opcode::Mul64Imm
224            | Opcode::Div64Imm
225            | Opcode::Or64Imm
226            | Opcode::And64Imm
227            | Opcode::Lsh64Imm
228            | Opcode::Rsh64Imm
229            | Opcode::Mod64Imm
230            | Opcode::Xor64Imm
231            | Opcode::Mov64Imm
232            | Opcode::Arsh64Imm
233            | Opcode::Hor64Imm
234            | Opcode::Lmul64Imm
235            | Opcode::Uhmul64Imm
236            | Opcode::Udiv64Imm
237            | Opcode::Urem64Imm
238            | Opcode::Shmul64Imm
239            | Opcode::Sdiv64Imm
240            | Opcode::Srem64Imm
241            | Opcode::Be
242            | Opcode::Le => {
243                if src != 0 || off != 0 {
244                    return Err(CompileError::BytecodeError {
245                        error: format!("Arithmetic instruction with immediate expects src and off to be 0, but got src: {}, off: {}", src, off),
246                        span: span.clone(),
247                        custom_label: None,
248                    });
249                }
250                operands.push(Token::Register(dst, 1..2));
251                operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
252            }
253
254            // Arithmetic instructions with register operands
255            Opcode::Add32Reg
256            | Opcode::Sub32Reg
257            | Opcode::Mul32Reg
258            | Opcode::Div32Reg
259            | Opcode::Or32Reg
260            | Opcode::And32Reg
261            | Opcode::Lsh32Reg
262            | Opcode::Rsh32Reg
263            | Opcode::Mod32Reg
264            | Opcode::Xor32Reg
265            | Opcode::Mov32Reg
266            | Opcode::Arsh32Reg
267            | Opcode::Lmul32Reg
268            | Opcode::Udiv32Reg
269            | Opcode::Urem32Reg
270            | Opcode::Sdiv32Reg
271            | Opcode::Srem32Reg
272            | Opcode::Add64Reg
273            | Opcode::Sub64Reg
274            | Opcode::Mul64Reg
275            | Opcode::Div64Reg
276            | Opcode::Or64Reg
277            | Opcode::And64Reg
278            | Opcode::Lsh64Reg
279            | Opcode::Rsh64Reg
280            | Opcode::Mod64Reg
281            | Opcode::Xor64Reg
282            | Opcode::Mov64Reg
283            | Opcode::Arsh64Reg
284            | Opcode::Lmul64Reg
285            | Opcode::Uhmul64Reg
286            | Opcode::Udiv64Reg
287            | Opcode::Urem64Reg
288            | Opcode::Shmul64Reg
289            | Opcode::Sdiv64Reg
290            | Opcode::Srem64Reg => {
291                if off != 0 || imm != 0 {
292                    return Err(CompileError::BytecodeError {
293                        error: format!("Arithmetic instruction with register expects off and imm to be 0, but got off: {}, imm: {}", off, imm),
294                        span: span.clone(),
295                        custom_label: None,
296                    });
297                }
298                operands.push(Token::Register(dst, 1..2));
299                operands.push(Token::Register(src, 1..2));
300            }
301
302            Opcode::Ldxw | Opcode::Ldxh | Opcode::Ldxb | Opcode::Ldxdw => {
303                if imm != 0 {
304                    return Err(CompileError::BytecodeError {
305                        error: format!("Load instruction expects imm to be 0, but got imm: {}", imm),
306                        span: span.clone(),
307                        custom_label: None,
308                    });
309                }
310                operands.push(Token::Register(dst, 1..2));
311                operands.push(Token::Register(src, 1..2));
312                operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
313            }
314
315            Opcode::Stw | Opcode::Sth | Opcode::Stb | Opcode::Stdw => {
316                if src != 0 {
317                    return Err(CompileError::BytecodeError {
318                        error: format!("Store instruction expects src to be 0, but got src: {}", src),
319                        span: span.clone(),
320                        custom_label: None,
321                    });
322                }
323                operands.push(Token::Register(dst, 1..2));
324                operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
325                operands.push(Token::ImmediateValue(ImmediateValue::Int(imm), 4..8));
326            }
327
328            Opcode::Stxb | Opcode::Stxh | Opcode::Stxw | Opcode::Stxdw => {
329                if imm != 0 {
330                    return Err(CompileError::BytecodeError {
331                        error: format!("Store instruction with register expects imm to be 0, but got imm: {}", imm),
332                        span: span.clone(),
333                        custom_label: None,
334                    });
335                }
336                operands.push(Token::Register(dst, 1..2));
337                operands.push(Token::Register(src, 1..2));
338                operands.push(Token::ImmediateValue(ImmediateValue::Int(off as i64), 2..4));
339            }
340
341            // Unary operations
342            Opcode::Neg32 | Opcode::Neg64 | Opcode::Exit => {
343                if src != 0 || off != 0 || imm != 0 {
344                    return Err(CompileError::BytecodeError {
345                        error: format!("Unary operation expects src, off, and imm to be 0, but got src: {}, off: {}, imm: {}", src, off, imm),
346                        span: span.clone(),
347                        custom_label: None,
348                    });
349                }
350                operands.push(Token::Register(dst, 1..2));
351            }
352
353            _ => {
354                return Err(CompileError::BytecodeError {
355                    error: format!("Unsupported opcode: {:?}", opcode),
356                    span: span.clone(),
357                    custom_label: None,
358                });
359            }
360        }
361
362        Ok(Instruction {
363            opcode,
364            operands,
365            span,
366        })
367    }
368}