1use {
2 crate::{
3 errors::SBPFError,
4 inst_handler::{OPCODE_TO_HANDLER, OPCODE_TO_TYPE},
5 inst_param::{Number, Register},
6 opcode::{Opcode, OperationType},
7 syscalls::REGISTERED_SYSCALLS,
8 },
9 core::ops::Range,
10 either::Either,
11 serde::{Deserialize, Serialize},
12};
13
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
15pub enum AsmFormat {
16 #[default]
17 Default,
18 Llvm,
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22pub struct Instruction {
23 pub opcode: Opcode,
24 pub dst: Option<Register>,
25 pub src: Option<Register>,
26 pub off: Option<Either<String, i16>>,
27 pub imm: Option<Either<String, Number>>,
28 pub span: Range<usize>,
29}
30
31impl Instruction {
32 pub fn get_size(&self) -> u64 {
33 match self.opcode {
34 Opcode::Lddw => 16,
35 _ => 8,
36 }
37 }
38
39 fn get_opcode_type(&self) -> OperationType {
40 *OPCODE_TO_TYPE.get(&self.opcode).unwrap()
41 }
42
43 pub fn is_jump(&self) -> bool {
44 matches!(
45 self.get_opcode_type(),
46 OperationType::Jump
47 | OperationType::JumpImmediate
48 | OperationType::JumpRegister
49 | OperationType::Jump32Immediate
50 | OperationType::Jump32Register
51 )
52 }
53
54 pub fn is_syscall(&self) -> bool {
57 if self.opcode == Opcode::Call
58 && let Some(Either::Left(identifier)) = &self.imm
59 {
60 return REGISTERED_SYSCALLS.contains(&identifier.as_str());
61 }
62 false
63 }
64
65 pub fn op_imm_bits(&self) -> Result<String, SBPFError> {
67 match &self.imm {
68 Some(Either::Right(Number::Int(imm))) => match *imm {
69 16 => Ok(format!("{}16", self.opcode)),
70 32 => Ok(format!("{}32", self.opcode)),
71 64 => Ok(format!("{}64", self.opcode)),
72 _ => Err(SBPFError::BytecodeError {
73 error: format!(
74 "Invalid immediate value: {:?} for opcode: {:?}",
75 self.imm, self.opcode
76 ),
77 span: self.span.clone(),
78 custom_label: None,
79 }),
80 },
81 _ => Err(SBPFError::BytecodeError {
82 error: format!("Expected immediate value for opcode: {:?}", self.opcode),
83 span: self.span.clone(),
84 custom_label: None,
85 }),
86 }
87 }
88
89 pub fn from_bytes(bytes: &[u8]) -> Result<Self, SBPFError> {
90 let opcode: Opcode = bytes[0].try_into()?;
91 if let Some(handler) = OPCODE_TO_HANDLER.get(&opcode) {
92 (handler.decode)(bytes)
93 } else {
94 Err(SBPFError::BytecodeError {
95 error: format!("no decode handler for opcode {}", opcode),
96 span: 0..1,
97 custom_label: Some("Invalid opcode".to_string()),
98 })
99 }
100 }
101
102 pub fn from_bytes_sbpf_v2(bytes: &[u8]) -> Result<Self, SBPFError> {
103 let mut processed_bytes = bytes.to_vec();
105
106 match processed_bytes[0] {
107 0x8C => processed_bytes[0] = 0x61, 0x8F => processed_bytes[0] = 0x63, 0x2C => processed_bytes[0] = 0x71, 0x3C => processed_bytes[0] = 0x69, 0x9C => processed_bytes[0] = 0x79, 0x27 => processed_bytes[0] = 0x72, 0x2F => processed_bytes[0] = 0x73, 0x37 => processed_bytes[0] = 0x6A, 0x3F => processed_bytes[0] = 0x6B, 0x87 => processed_bytes[0] = 0x62, 0x97 => processed_bytes[0] = 0x7A, 0x9F => processed_bytes[0] = 0x7B, 0x21 => {
123 if let Some(lddw_2) = processed_bytes.get(8)
124 && lddw_2 == &0xf7
125 {
126 processed_bytes[0] = 0x18;
127 processed_bytes[8..12].clone_from_slice(&[0u8; 4]);
128 }
129 }
130 0x8D => processed_bytes[1] >>= 4,
132 _ => (),
134 }
135
136 Self::from_bytes(&processed_bytes)
137 }
138
139 pub fn from_bytes_sbpf_v3(bytes: &[u8]) -> Result<Self, SBPFError> {
140 let opcode = Opcode::try_from_sbpf_v3(bytes[0])?;
141 OPCODE_TO_HANDLER
142 .get(&opcode)
143 .ok_or_else(|| SBPFError::BytecodeError {
144 error: format!("no decode handler for opcode {}", opcode),
145 span: 0..1,
146 custom_label: Some("Invalid opcode".to_string()),
147 })
148 .and_then(|handler| (handler.decode)(bytes))
149 }
150
151 pub fn to_bytes(&self) -> Result<Vec<u8>, SBPFError> {
152 let dst_val = self.dst.as_ref().map(|r| r.n).unwrap_or(0);
153 let src_val = self.src.as_ref().map(|r| r.n).unwrap_or(0);
154 let off_val = match &self.off {
155 Some(Either::Left(ident)) => {
156 unreachable!("Identifier '{}' should have been resolved earlier", ident)
157 }
158 Some(Either::Right(off)) => *off,
159 None => 0,
160 };
161 let imm_val = match &self.imm {
162 Some(Either::Left(ident)) => {
163 unreachable!("Identifier '{}' should have been resolved earlier", ident)
164 }
165 Some(Either::Right(Number::Int(imm))) | Some(Either::Right(Number::Addr(imm))) => *imm,
166 None => 0,
167 };
168 let (dst_val, imm_val) = match self.opcode {
170 Opcode::Callx => (0, dst_val as i64), _ => (dst_val, imm_val),
172 };
173
174 let mut b = vec![self.opcode.into(), src_val << 4 | dst_val];
175 b.extend_from_slice(&off_val.to_le_bytes());
176 b.extend_from_slice(&(imm_val as i32).to_le_bytes());
177 if self.opcode == Opcode::Lddw {
178 b.extend_from_slice(&[0; 4]);
179 b.extend_from_slice(&((imm_val >> 32) as i32).to_le_bytes());
180 }
181 Ok(b)
182 }
183
184 pub fn to_asm(&self, format: AsmFormat) -> Result<String, SBPFError> {
185 match format {
186 AsmFormat::Default => self.to_default_asm(),
187 AsmFormat::Llvm => self.to_llvm_asm(),
188 }
189 }
190
191 fn to_default_asm(&self) -> Result<String, SBPFError> {
192 if let Some(handler) = OPCODE_TO_HANDLER.get(&self.opcode) {
193 match (handler.validate)(self) {
194 Ok(()) => {
195 let mut asm = if self.opcode == Opcode::Le || self.opcode == Opcode::Be {
196 self.op_imm_bits()?
197 } else {
198 format!("{}", self.opcode)
199 };
200 let mut param = vec![];
201
202 fn fmt_mem_off(r: &Register, off: &Either<String, i16>) -> String {
203 format!("[r{}{}]", r.n, fmt_off(off))
204 }
205
206 if self.get_opcode_type() == OperationType::LoadMemory {
207 param.push(format!("r{}", self.dst.as_ref().unwrap().n));
208 param.push(fmt_mem_off(
209 self.src.as_ref().unwrap(),
210 self.off.as_ref().unwrap(),
211 ));
212 } else if self.get_opcode_type() == OperationType::StoreImmediate {
213 param.push(fmt_mem_off(
214 self.dst.as_ref().unwrap(),
215 self.off.as_ref().unwrap(),
216 ));
217 param.push(fmt_imm(self.imm.as_ref().unwrap()));
218 } else if self.get_opcode_type() == OperationType::StoreRegister {
219 param.push(fmt_mem_off(
220 self.dst.as_ref().unwrap(),
221 self.off.as_ref().unwrap(),
222 ));
223 param.push(format!("r{}", self.src.as_ref().unwrap().n));
224 } else {
225 if let Some(dst) = &self.dst {
226 param.push(format!("r{}", dst.n));
227 }
228 if let Some(src) = &self.src
229 && self.opcode != Opcode::Call
230 {
231 param.push(format!("r{}", src.n));
232 }
233 if let Some(imm) = &self.imm
234 && self.opcode != Opcode::Le
235 && self.opcode != Opcode::Be
236 {
237 param.push(fmt_imm(imm));
238 }
239 if let Some(off) = &self.off {
240 param.push(fmt_off(off));
241 }
242 }
243 if !param.is_empty() {
244 asm.push(' ');
245 asm.push_str(¶m.join(", "));
246 }
247 Ok(asm)
248 }
249 Err(e) => Err(e),
250 }
251 } else {
252 Err(SBPFError::BytecodeError {
253 error: format!("no validate handler for opcode {}", self.opcode),
254 span: self.span.clone(),
255 custom_label: None,
256 })
257 }
258 }
259
260 fn to_llvm_asm(&self) -> Result<String, SBPFError> {
261 let op_type = self.get_opcode_type();
262
263 fn fmt_mem_off(off: &Either<String, i16>) -> String {
264 match off {
265 Either::Left(label) => label.clone(),
266 Either::Right(v) if *v < 0 => format!("- 0x{:x}", -(*v as i32)),
267 Either::Right(v) => format!("+ 0x{:x}", v),
268 }
269 }
270
271 match op_type {
272 OperationType::BinaryImmediate | OperationType::BinaryRegister => {
273 let op = self
274 .opcode
275 .to_operator()
276 .ok_or_else(|| SBPFError::BytecodeError {
277 error: format!("unsupported opcode in LLVM format: {}", self.opcode),
278 span: self.span.clone(),
279 custom_label: None,
280 })?;
281 let prefix = if self.opcode.is_32bit() { "w" } else { "r" };
282 let dst = self.dst.as_ref().unwrap().n;
283 let rhs = if op_type == OperationType::BinaryRegister {
284 format!("{}{}", prefix, self.src.as_ref().unwrap().n)
285 } else {
286 fmt_imm(self.imm.as_ref().unwrap())
287 };
288 Ok(format!("{}{} {} {}", prefix, dst, op, rhs))
289 }
290 OperationType::Endian => {
291 let bits = self.op_imm_bits()?;
292 let dst = self.dst.as_ref().unwrap().n;
293 Ok(format!("r{} = {} r{}", dst, bits, dst))
294 }
295 OperationType::Unary => {
296 let prefix = if self.opcode == Opcode::Neg32 {
297 "w"
298 } else {
299 "r"
300 };
301 let dst = self.dst.as_ref().unwrap().n;
302 Ok(format!("{}{} = -{}{}", prefix, dst, prefix, dst))
303 }
304 OperationType::LoadImmediate => {
305 let dst = self.dst.as_ref().unwrap().n;
306 let imm = fmt_imm(self.imm.as_ref().unwrap());
307 Ok(format!("r{} = {} ll", dst, imm))
308 }
309 OperationType::LoadMemory => {
310 let size = self.opcode.to_size().unwrap();
311 let dst_prefix = if self.opcode == Opcode::Ldxdw {
312 "r"
313 } else {
314 "w"
315 };
316 let dst = self.dst.as_ref().unwrap().n;
317 let src = self.src.as_ref().unwrap().n;
318 let off = fmt_mem_off(self.off.as_ref().unwrap());
319 Ok(format!(
320 "{}{} = *({} *)(r{} {})",
321 dst_prefix, dst, size, src, off
322 ))
323 }
324 OperationType::StoreImmediate => {
325 let size = self.opcode.to_size().unwrap();
326 let dst = self.dst.as_ref().unwrap().n;
327 let off = fmt_mem_off(self.off.as_ref().unwrap());
328 let imm = fmt_imm(self.imm.as_ref().unwrap());
329 Ok(format!("*({} *)(r{} {}) = {}", size, dst, off, imm))
330 }
331 OperationType::StoreRegister => {
332 let size = self.opcode.to_size().unwrap();
333 let dst = self.dst.as_ref().unwrap().n;
334 let off = fmt_mem_off(self.off.as_ref().unwrap());
335 let src_prefix = if self.opcode == Opcode::Stxdw {
336 "r"
337 } else {
338 "w"
339 };
340 let src = self.src.as_ref().unwrap().n;
341 Ok(format!(
342 "*({} *)(r{} {}) = {}{}",
343 size, dst, off, src_prefix, src
344 ))
345 }
346 OperationType::Jump => {
347 let off = fmt_off(self.off.as_ref().unwrap());
348 Ok(format!("goto {}", off))
349 }
350 OperationType::JumpImmediate | OperationType::Jump32Immediate => {
351 let dst = self.dst.as_ref().unwrap().n;
352 let op = self.opcode.to_operator().unwrap();
353 let imm = fmt_imm(self.imm.as_ref().unwrap());
354 let off = fmt_off(self.off.as_ref().unwrap());
355 let prefix = if is_jump32_opcode(self.opcode) {
356 "w"
357 } else {
358 "r"
359 };
360 Ok(format!("if {}{} {} {} goto {}", prefix, dst, op, imm, off))
361 }
362 OperationType::JumpRegister | OperationType::Jump32Register => {
363 let dst = self.dst.as_ref().unwrap().n;
364 let op = self.opcode.to_operator().unwrap();
365 let src = self.src.as_ref().unwrap().n;
366 let off = fmt_off(self.off.as_ref().unwrap());
367 let prefix = if is_jump32_opcode(self.opcode) {
368 "w"
369 } else {
370 "r"
371 };
372 Ok(format!(
373 "if {}{} {} {}{} goto {}",
374 prefix, dst, op, prefix, src, off
375 ))
376 }
377 OperationType::CallImmediate | OperationType::CallRegister | OperationType::Exit => {
378 self.to_default_asm()
379 }
380 }
381 }
382}
383
384fn fmt_off(off: &Either<String, i16>) -> String {
385 match off {
386 Either::Left(label) => label.clone(),
387 Either::Right(v) if *v < 0 => format!("-0x{:x}", -(*v as i32)),
388 Either::Right(v) => format!("+0x{:x}", v),
389 }
390}
391
392fn fmt_imm(imm: &Either<String, Number>) -> String {
393 match imm {
394 Either::Left(label) => label.clone(),
395 Either::Right(Number::Int(v)) | Either::Right(Number::Addr(v)) => {
396 if *v < 0 {
397 format!("-0x{:x}", -v)
398 } else {
399 format!("0x{:x}", v)
400 }
401 }
402 }
403}
404
405fn is_jump32_opcode(opcode: Opcode) -> bool {
406 matches!(
407 opcode,
408 Opcode::Jeq32Imm
409 | Opcode::Jeq32Reg
410 | Opcode::Jgt32Imm
411 | Opcode::Jgt32Reg
412 | Opcode::Jge32Imm
413 | Opcode::Jge32Reg
414 | Opcode::Jlt32Imm
415 | Opcode::Jlt32Reg
416 | Opcode::Jle32Imm
417 | Opcode::Jle32Reg
418 | Opcode::Jset32Imm
419 | Opcode::Jset32Reg
420 | Opcode::Jne32Imm
421 | Opcode::Jne32Reg
422 | Opcode::Jsgt32Imm
423 | Opcode::Jsgt32Reg
424 | Opcode::Jsge32Imm
425 | Opcode::Jsge32Reg
426 | Opcode::Jslt32Imm
427 | Opcode::Jslt32Reg
428 | Opcode::Jsle32Imm
429 | Opcode::Jsle32Reg
430 )
431}
432
433#[cfg(test)]
434mod test {
435 use {
436 crate::{
437 inst_param::{Number, Register},
438 instruction::{AsmFormat, Instruction},
439 opcode::Opcode,
440 },
441 either::Either,
442 hex_literal::hex,
443 syscall_map::murmur3_32,
444 };
445
446 #[test]
447 fn serialize_e2e() {
448 let b = hex!("9700000000000000");
449 let i = Instruction::from_bytes(&b).unwrap();
450 assert_eq!(i.to_bytes().unwrap(), &b);
451 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "mod64 r0, 0x0");
452 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r0 %= 0x0");
453 }
454
455 #[test]
456 fn serialize_e2e_lddw() {
457 let b = hex!("18010000000000000000000000000000");
458 let i = Instruction::from_bytes(&b).unwrap();
459 assert_eq!(i.to_bytes().unwrap(), &b);
460 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "lddw r1, 0x0");
461 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = 0x0 ll");
462 }
463
464 #[test]
465 fn serialize_e2e_add64_imm() {
466 let b = hex!("0701000000000000");
467 let i = Instruction::from_bytes(&b).unwrap();
468 assert_eq!(i.to_bytes().unwrap(), &b);
469 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "add64 r1, 0x0");
470 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 += 0x0");
471 }
472
473 #[test]
474 fn serialize_e2e_add64_reg() {
475 let b = hex!("0f12000000000000");
476 let i = Instruction::from_bytes(&b).unwrap();
477 assert_eq!(i.to_bytes().unwrap(), &b);
478 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "add64 r2, r1");
479 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r2 += r1");
480 }
481
482 #[test]
483 fn serialize_e2e_ja() {
484 let b = hex!("05000a0000000000");
485 let i = Instruction::from_bytes(&b).unwrap();
486 assert_eq!(i.to_bytes().unwrap(), &b);
487 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "ja +0xa");
488 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "goto +0xa");
489 }
490
491 #[test]
492 fn serialize_e2e_jeq_imm() {
493 let b = hex!("15030a0001000000");
494 let i = Instruction::from_bytes(&b).unwrap();
495 assert_eq!(i.to_bytes().unwrap(), &b);
496 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jeq r3, 0x1, +0xa");
497 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if r3 == 0x1 goto +0xa");
498 }
499
500 #[test]
501 fn serialize_e2e_jeq_reg() {
502 let b = hex!("1d210a0000000000");
503 let i = Instruction::from_bytes(&b).unwrap();
504 assert_eq!(i.to_bytes().unwrap(), &b);
505 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jeq r1, r2, +0xa");
506 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if r1 == r2 goto +0xa");
507 }
508
509 #[test]
510 fn serialize_e2e_ldxw() {
511 let b = hex!("6112000000000000");
512 let i = Instruction::from_bytes(&b).unwrap();
513 assert_eq!(i.to_bytes().unwrap(), &b);
514 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "ldxw r2, [r1+0x0]");
515 assert_eq!(
516 i.to_asm(AsmFormat::Llvm).unwrap(),
517 "w2 = *(u32 *)(r1 + 0x0)"
518 );
519 }
520
521 #[test]
522 fn serialize_e2e_stxw() {
523 let b = hex!("6312000000000000");
524 let i = Instruction::from_bytes(&b).unwrap();
525 assert_eq!(i.to_bytes().unwrap(), &b);
526 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "stxw [r2+0x0], r1");
527 assert_eq!(
528 i.to_asm(AsmFormat::Llvm).unwrap(),
529 "*(u32 *)(r2 + 0x0) = w1"
530 );
531 }
532
533 #[test]
534 fn serialize_e2e_stb() {
535 let b = hex!("7200000000000000");
536 let i = Instruction::from_bytes(&b).unwrap();
537 assert_eq!(i.opcode, Opcode::Stb);
538 assert!(i.src.is_none());
539 assert_eq!(i.to_bytes().unwrap(), &b);
540 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "stb [r0+0x0], 0x0");
541 assert_eq!(
542 i.to_asm(AsmFormat::Llvm).unwrap(),
543 "*(u8 *)(r0 + 0x0) = 0x0"
544 );
545 }
546
547 #[test]
548 fn serialize_e2e_sth() {
549 let b = hex!("6a01040034120000");
550 let i = Instruction::from_bytes(&b).unwrap();
551 assert_eq!(i.opcode, Opcode::Sth);
552 assert!(i.src.is_none());
553 assert_eq!(i.to_bytes().unwrap(), &b);
554 assert_eq!(
555 i.to_asm(AsmFormat::Default).unwrap(),
556 "sth [r1+0x4], 0x1234"
557 );
558 assert_eq!(
559 i.to_asm(AsmFormat::Llvm).unwrap(),
560 "*(u16 *)(r1 + 0x4) = 0x1234"
561 );
562 }
563
564 #[test]
565 fn serialize_e2e_stw() {
566 let b = hex!("6201080064000000");
567 let i = Instruction::from_bytes(&b).unwrap();
568 assert_eq!(i.opcode, Opcode::Stw);
569 assert!(i.src.is_none());
570 assert_eq!(i.to_bytes().unwrap(), &b);
571 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "stw [r1+0x8], 0x64");
572 assert_eq!(
573 i.to_asm(AsmFormat::Llvm).unwrap(),
574 "*(u32 *)(r1 + 0x8) = 0x64"
575 );
576 }
577
578 #[test]
579 fn serialize_e2e_stdw() {
580 let b = hex!("7a021000efbeadde");
581 let i = Instruction::from_bytes(&b).unwrap();
582 assert_eq!(i.opcode, Opcode::Stdw);
583 assert!(i.src.is_none());
584 assert_eq!(i.to_bytes().unwrap(), &b);
585 assert_eq!(
586 i.to_asm(AsmFormat::Default).unwrap(),
587 "stdw [r2+0x10], -0x21524111"
588 );
589 assert_eq!(
590 i.to_asm(AsmFormat::Llvm).unwrap(),
591 "*(u64 *)(r2 + 0x10) = -0x21524111"
592 );
593 }
594
595 #[test]
596 fn serialize_e2e_le16() {
597 let b = hex!("d401000010000000");
598 let i = Instruction::from_bytes(&b).unwrap();
599 assert_eq!(i.opcode, Opcode::Le);
600 assert_eq!(i.to_bytes().unwrap(), &b);
601 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "le16 r1");
602 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = le16 r1");
603 }
604
605 #[test]
606 fn serialize_e2e_le32() {
607 let b = hex!("d401000020000000");
608 let i = Instruction::from_bytes(&b).unwrap();
609 assert_eq!(i.opcode, Opcode::Le);
610 assert_eq!(i.to_bytes().unwrap(), &b);
611 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "le32 r1");
612 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = le32 r1");
613 }
614
615 #[test]
616 fn serialize_e2e_le64() {
617 let b = hex!("d403000040000000");
618 let i = Instruction::from_bytes(&b).unwrap();
619 assert_eq!(i.opcode, Opcode::Le);
620 assert_eq!(i.to_bytes().unwrap(), &b);
621 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "le64 r3");
622 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r3 = le64 r3");
623 }
624
625 #[test]
626 fn serialize_e2e_be16() {
627 let b = hex!("dc01000010000000");
628 let i = Instruction::from_bytes(&b).unwrap();
629 assert_eq!(i.opcode, Opcode::Be);
630 assert_eq!(i.to_bytes().unwrap(), &b);
631 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "be16 r1");
632 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r1 = be16 r1");
633 }
634
635 #[test]
636 fn serialize_e2e_be32() {
637 let b = hex!("dc02000020000000");
638 let i = Instruction::from_bytes(&b).unwrap();
639 assert_eq!(i.opcode, Opcode::Be);
640 assert_eq!(i.to_bytes().unwrap(), &b);
641 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "be32 r2");
642 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r2 = be32 r2");
643 }
644
645 #[test]
646 fn serialize_e2e_be64() {
647 let b = hex!("dc03000040000000");
648 let i = Instruction::from_bytes(&b).unwrap();
649 assert_eq!(i.opcode, Opcode::Be);
650 assert_eq!(i.to_bytes().unwrap(), &b);
651 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "be64 r3");
652 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r3 = be64 r3");
653 }
654
655 #[test]
656 fn serialize_e2e_neg64() {
657 let b = hex!("8700000000000000");
658 let i = Instruction::from_bytes(&b).unwrap();
659 assert_eq!(i.to_bytes().unwrap(), &b);
660 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "neg64 r0");
661 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "r0 = -r0");
662 }
663
664 #[test]
665 fn serialize_e2e_exit() {
666 let b = hex!("9500000000000000");
667 let i = Instruction::from_bytes(&b).unwrap();
668 assert_eq!(i.to_bytes().unwrap(), &b);
669 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "exit");
670 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "exit");
671 }
672
673 #[test]
674 fn serialize_e2e_jset_imm() {
675 let b = hex!("45030a0010000000");
676 let i = Instruction::from_bytes(&b).unwrap();
677 assert_eq!(i.to_bytes().unwrap(), &b);
678 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jset r3, 0x10, +0xa");
679 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if r3 & 0x10 goto +0xa");
680 }
681
682 #[test]
683 fn serialize_e2e_jset32_imm() {
684 let b = hex!("46030a0010000000");
685 let i = Instruction::from_bytes_sbpf_v3(&b).unwrap();
686 assert_eq!(
687 i.to_asm(AsmFormat::Default).unwrap(),
688 "jset32 r3, 0x10, +0xa"
689 );
690 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if w3 & 0x10 goto +0xa");
691 }
692
693 #[test]
694 fn serialize_e2e_jset32_reg() {
695 let b = hex!("4e230a0000000000");
696 let i = Instruction::from_bytes_sbpf_v3(&b).unwrap();
697 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "jset32 r3, r2, +0xa");
698 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "if w3 & w2 goto +0xa");
699 }
700
701 #[test]
702 fn serialize_e2e_sub32_imm() {
703 let b = hex!("1401000042000000");
704 let i = Instruction::from_bytes(&b).unwrap();
705 assert_eq!(i.to_bytes().unwrap(), &b);
706 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "sub32 r1, 0x42");
707 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "w1 -= 0x42");
708 }
709
710 #[test]
711 fn serialize_e2e_mov32_imm() {
712 let b = hex!("b400000001000000");
713 let i = Instruction::from_bytes(&b).unwrap();
714 assert_eq!(i.to_bytes().unwrap(), &b);
715 assert_eq!(i.to_asm(AsmFormat::Default).unwrap(), "mov32 r0, 0x1");
716 assert_eq!(i.to_asm(AsmFormat::Llvm).unwrap(), "w0 = 0x1");
717 }
718
719 #[test]
720 fn test_instruction_size() {
721 let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
722 assert_eq!(exit.get_size(), 8);
723
724 let lddw = Instruction::from_bytes(&hex!("18010000000000000000000000000000")).unwrap();
725 assert_eq!(lddw.get_size(), 16);
726 }
727
728 #[test]
729 fn test_is_jump() {
730 let ja = Instruction::from_bytes(&hex!("0500000000000000")).unwrap();
731 assert!(ja.is_jump());
732
733 let jeq_imm = Instruction::from_bytes(&hex!("1502000000000000")).unwrap();
734 assert!(jeq_imm.is_jump());
735
736 let jeq_reg = Instruction::from_bytes(&hex!("1d12000000000000")).unwrap();
737 assert!(jeq_reg.is_jump());
738
739 let jset32_imm = Instruction::from_bytes_sbpf_v3(&hex!("46030a0010000000")).unwrap();
740 assert!(jset32_imm.is_jump());
741
742 let jset32_reg = Instruction::from_bytes_sbpf_v3(&hex!("4e230a0000000000")).unwrap();
743 assert!(jset32_reg.is_jump());
744
745 let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
746 assert!(!exit.is_jump());
747
748 let add64 = Instruction::from_bytes(&hex!("0701000000000000")).unwrap();
749 assert!(!add64.is_jump());
750 }
751
752 #[test]
753 fn test_invalid_opcode() {
754 let result = Instruction::from_bytes(&hex!("ff00000000000000"));
755 assert!(result.is_err());
756 }
757
758 #[test]
759 fn test_unsupported_opcode() {
760 let add32 = Instruction::from_bytes(&hex!("1300000000000000"));
761 assert!(add32.is_err());
762 }
763
764 #[test]
765 fn test_op_imm_bits_16() {
766 let inst = Instruction {
767 opcode: Opcode::Le,
768 dst: Some(Register { n: 1 }),
769 src: None,
770 off: None,
771 imm: Some(Either::Right(Number::Int(16))),
772 span: 0..8,
773 };
774 assert_eq!(inst.op_imm_bits().unwrap(), "le16");
775 }
776
777 #[test]
778 fn test_op_imm_bits_32() {
779 let inst = Instruction {
780 opcode: Opcode::Le,
781 dst: Some(Register { n: 1 }),
782 src: None,
783 off: None,
784 imm: Some(Either::Right(Number::Int(32))),
785 span: 0..8,
786 };
787 assert_eq!(inst.op_imm_bits().unwrap(), "le32");
788 }
789
790 #[test]
791 fn test_op_imm_bits_64() {
792 let inst = Instruction {
793 opcode: Opcode::Be,
794 dst: Some(Register { n: 1 }),
795 src: None,
796 off: None,
797 imm: Some(Either::Right(Number::Int(64))),
798 span: 0..8,
799 };
800 assert_eq!(inst.op_imm_bits().unwrap(), "be64");
801 }
802
803 #[test]
804 fn test_op_imm_bits_invalid() {
805 let inst = Instruction {
806 opcode: Opcode::Le,
807 dst: Some(Register { n: 1 }),
808 src: None,
809 off: None,
810 imm: Some(Either::Right(Number::Int(8))),
811 span: 0..8,
812 };
813 assert!(inst.op_imm_bits().is_err());
814 }
815
816 #[test]
817 fn test_op_imm_bits_no_imm() {
818 let inst = Instruction {
819 opcode: Opcode::Le,
820 dst: Some(Register { n: 1 }),
821 src: None,
822 off: None,
823 imm: None,
824 span: 0..8,
825 };
826 assert!(inst.op_imm_bits().is_err());
827 }
828
829 #[test]
830 fn test_to_bytes_callx() {
831 let inst = Instruction {
833 opcode: Opcode::Callx,
834 dst: Some(Register { n: 5 }),
835 src: None,
836 off: None,
837 imm: None,
838 span: 0..8,
839 };
840 let bytes = inst.to_bytes().unwrap();
841 assert_eq!(bytes[0], 0x8d);
842 assert_eq!(bytes[4], 5);
843 }
844
845 #[test]
846 #[should_panic(expected = "should have been resolved earlier")]
847 fn test_to_bytes_call_with_identifier() {
848 let inst = Instruction {
849 opcode: Opcode::Call,
850 dst: None,
851 src: None,
852 off: None,
853 imm: Some(Either::Left("function".to_string())),
854 span: 0..8,
855 };
856 let _ = inst.to_bytes().unwrap();
858 }
859
860 #[test]
861 fn test_to_asm_with_imm_addr() {
862 let inst = Instruction {
864 opcode: Opcode::Add64Imm,
865 dst: Some(Register { n: 1 }),
866 src: None,
867 off: None,
868 imm: Some(Either::Right(Number::Addr(100))),
869 span: 0..8,
870 };
871 let bytes = inst.to_bytes().unwrap();
872 assert_eq!(bytes[0], 0x07); assert_eq!(
874 i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]),
875 100
876 );
877 }
878
879 #[test]
880 fn test_from_bytes_sbpf_v2() {
881 let test_cases = vec![
883 (hex!("8c12000000000000"), Opcode::Ldxw, "v2: 0x8C -> ldxw"),
885 (hex!("8f12000000000000"), Opcode::Stxw, "v2: 0x8F -> stxw"),
886 (
888 hex!("2c12000000000000"),
889 Opcode::Ldxb,
890 "v2: 0x2C (mul32 reg) -> ldxb",
891 ),
892 (
893 hex!("3c12000000000000"),
894 Opcode::Ldxh,
895 "v2: 0x3C (div32 reg) -> ldxh",
896 ),
897 (
898 hex!("9c12000000000000"),
899 Opcode::Ldxdw,
900 "v2: 0x9C (mod32 reg) -> ldxdw",
901 ),
902 (
903 hex!("2701040064000000"),
904 Opcode::Stb,
905 "v2: 0x27 (mul64 imm) -> stb",
906 ),
907 (
908 hex!("2f12040000000000"),
909 Opcode::Stxb,
910 "v2: 0x2F (mul64 reg) -> stxb",
911 ),
912 (
913 hex!("3701040064000000"),
914 Opcode::Sth,
915 "v2: 0x37 (div64 imm) -> sth",
916 ),
917 (
918 hex!("3f12040000000000"),
919 Opcode::Stxh,
920 "v2: 0x3F (div64 reg) -> stxh",
921 ),
922 (
923 hex!("8701040064000000"),
924 Opcode::Stw,
925 "v2: 0x87 (neg64) -> stw",
926 ),
927 (
928 hex!("9701040064000000"),
929 Opcode::Stdw,
930 "v2: 0x97 (mod64 imm) -> stdw",
931 ),
932 (
933 hex!("9f12040000000000"),
934 Opcode::Stxdw,
935 "v2: 0x9F (mod64 reg) -> stxdw",
936 ),
937 ];
938
939 for (bytes, expected_opcode, description) in test_cases {
940 let inst = Instruction::from_bytes_sbpf_v2(&bytes).unwrap();
941 assert_eq!(inst.opcode, expected_opcode, "{}", description);
942 }
943
944 let callx_bytes = hex!("8d50000000000000");
946 let callx_inst = Instruction::from_bytes_sbpf_v2(&callx_bytes).unwrap();
947 assert_eq!(callx_inst.opcode, Opcode::Callx);
948 assert_eq!(callx_inst.dst.unwrap().n, 5);
949
950 let mut lddw_bytes = vec![0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
952 lddw_bytes.extend_from_slice(&[0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
953 let lddw_inst = Instruction::from_bytes_sbpf_v2(&lddw_bytes).unwrap();
954 assert_eq!(lddw_inst.opcode, Opcode::Lddw);
955 }
956
957 #[test]
958 fn test_is_syscall() {
959 let test_cases = vec![
960 ("sol_log_", true),
962 ("sol_invoke_signed_c", true),
963 ("abort", true),
964 ("sol_sha256", true),
965 ("sol_memcpy_", true),
966 ("my_fn", false),
968 ("helper_function", false),
969 ("entrypoint", false),
970 ("random", false),
971 ];
972
973 for (name, expected) in test_cases {
974 let inst = Instruction {
975 opcode: Opcode::Call,
976 dst: None,
977 src: Some(Register { n: 1 }),
978 off: None,
979 imm: Some(Either::Left(name.to_string())),
980 span: 0..8,
981 };
982 assert_eq!(inst.is_syscall(), expected);
983 }
984 }
985
986 #[test]
987 fn test_to_bytes_syscall_dynamic() {
988 let inst = Instruction {
989 opcode: Opcode::Call,
990 dst: None,
991 src: Some(Register { n: 1 }),
992 off: None,
993 imm: Some(Either::Right(Number::Int(-1))),
994 span: 0..8,
995 };
996 let bytes = inst.to_bytes().unwrap();
997 assert_eq!(bytes[0], 0x85);
998 assert_eq!(bytes[1], 0x10);
999
1000 assert_eq!(&bytes[4..8], &[0xFF, 0xFF, 0xFF, 0xFF]);
1002 }
1003
1004 #[test]
1005 fn test_to_bytes_syscall_static() {
1006 let syscall_hash = murmur3_32("sol_log_");
1007 let inst = Instruction {
1008 opcode: Opcode::Call,
1009 dst: None,
1010 src: Some(Register { n: 0 }),
1011 off: None,
1012 imm: Some(Either::Right(Number::Int(syscall_hash as i64))),
1013 span: 0..8,
1014 };
1015 let bytes = inst.to_bytes().unwrap();
1016 assert_eq!(bytes[0], 0x85);
1017 assert_eq!(bytes[1], 0x00);
1018
1019 let actual_imm = u32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
1021 assert_eq!(actual_imm, syscall_hash);
1022 }
1023}