|
|
@@ -132,7 +132,11 @@ impl Instruction {
|
|
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, SBPFError> {
|
|
|
let span = 0..bytes.len();
|
|
|
|
|
|
- let opcode = Opcode::from_u8(bytes[0]).unwrap();
|
|
|
+ let opcode = Opcode::from_u8(bytes[0]).ok_or(SBPFError::BytecodeError {
|
|
|
+ error: format!("Invalid opcode: {:?}", bytes[0]),
|
|
|
+ span: span.clone(),
|
|
|
+ custom_label: None,
|
|
|
+ })?;
|
|
|
let reg = bytes[1];
|
|
|
let src = reg >> 4;
|
|
|
let dst = reg & 0x0f;
|
|
|
@@ -747,13 +751,15 @@ impl Instruction {
|
|
|
mod test {
|
|
|
use hex_literal::hex;
|
|
|
|
|
|
- use crate::instruction::Instruction;
|
|
|
+ use crate::instruction::{Instruction, Register};
|
|
|
+ use crate::opcode::Opcode;
|
|
|
|
|
|
#[test]
|
|
|
fn serialize_e2e() {
|
|
|
let b = hex!("9700000000000000");
|
|
|
let i = Instruction::from_bytes(&b).unwrap();
|
|
|
assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "mod64 r0, 0");
|
|
|
}
|
|
|
|
|
|
#[test]
|
|
|
@@ -761,5 +767,158 @@ mod test {
|
|
|
let b = hex!("18010000000000000000000000000000");
|
|
|
let i = Instruction::from_bytes(&b).unwrap();
|
|
|
assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "lddw r1, 0");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_add64_imm() {
|
|
|
+ let b = hex!("0701000000000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "add64 r1, 0");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_add64_reg() {
|
|
|
+ let b = hex!("0f12000000000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "add64 r2, r1");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_ja() {
|
|
|
+ let b = hex!("05000a0000000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "jeq +10");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_jeq_imm() {
|
|
|
+ let b = hex!("15030a0001000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "jeq r3, 1, +10");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_jeq_reg() {
|
|
|
+ let b = hex!("1d210a0000000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "jeq r1, r2, +10");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_ldxw() {
|
|
|
+ let b = hex!("6112000000000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "ldxw r2, [r1+0]");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_stxw() {
|
|
|
+ let b = hex!("6312000000000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "stxw [r2+0], r1");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn serialize_e2e_neg64() {
|
|
|
+ let b = hex!("8700000000000000");
|
|
|
+ let i = Instruction::from_bytes(&b).unwrap();
|
|
|
+ assert_eq!(i.to_bytes(), &b);
|
|
|
+ assert_eq!(i.to_asm().unwrap(), "neg64 r0");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_instruction_size() {
|
|
|
+ let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
|
|
|
+ assert_eq!(exit.get_size(), 8);
|
|
|
+
|
|
|
+ let lddw = Instruction::from_bytes(&hex!("18010000000000000000000000000000")).unwrap();
|
|
|
+ assert_eq!(lddw.get_size(), 16);
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_is_jump() {
|
|
|
+ let ja = Instruction::from_bytes(&hex!("0500000000000000")).unwrap();
|
|
|
+ assert!(ja.is_jump());
|
|
|
+
|
|
|
+ let jeq_imm = Instruction::from_bytes(&hex!("1502000000000000")).unwrap();
|
|
|
+ assert!(jeq_imm.is_jump());
|
|
|
+
|
|
|
+ let jeq_reg = Instruction::from_bytes(&hex!("1d12000000000000")).unwrap();
|
|
|
+ assert!(jeq_reg.is_jump());
|
|
|
+
|
|
|
+ let exit = Instruction::from_bytes(&hex!("9500000000000000")).unwrap();
|
|
|
+ assert!(!exit.is_jump());
|
|
|
+
|
|
|
+ let add64 = Instruction::from_bytes(&hex!("0701000000000000")).unwrap();
|
|
|
+ assert!(!add64.is_jump());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_off_str() {
|
|
|
+ let pos_off = Instruction {
|
|
|
+ opcode: Opcode::Ja,
|
|
|
+ dst: None,
|
|
|
+ src: None,
|
|
|
+ off: Some(10),
|
|
|
+ imm: None,
|
|
|
+ span: 0..8,
|
|
|
+ };
|
|
|
+ assert_eq!(pos_off.off_str(), "+10");
|
|
|
+
|
|
|
+ let neg_off = Instruction {
|
|
|
+ opcode: Opcode::Ja,
|
|
|
+ dst: None,
|
|
|
+ src: None,
|
|
|
+ off: Some(-10),
|
|
|
+ imm: None,
|
|
|
+ span: 0..8,
|
|
|
+ };
|
|
|
+ assert_eq!(neg_off.off_str(), "-10");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_dst_off() {
|
|
|
+ let inst = Instruction {
|
|
|
+ opcode: Opcode::Ldxw,
|
|
|
+ dst: Some(Register { n: 1 }),
|
|
|
+ src: Some(Register { n: 2 }),
|
|
|
+ off: Some(10),
|
|
|
+ imm: None,
|
|
|
+ span: 0..8,
|
|
|
+ };
|
|
|
+ assert_eq!(inst.dst_off(), "[r1+10]");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_src_off() {
|
|
|
+ let inst = Instruction {
|
|
|
+ opcode: Opcode::Ldxw,
|
|
|
+ dst: Some(Register { n: 1 }),
|
|
|
+ src: Some(Register { n: 2 }),
|
|
|
+ off: Some(-5),
|
|
|
+ imm: None,
|
|
|
+ span: 0..8,
|
|
|
+ };
|
|
|
+ assert_eq!(inst.src_off(), "[r2-5]");
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_invalid_opcode() {
|
|
|
+ let result = Instruction::from_bytes(&hex!("ff00000000000000"));
|
|
|
+ assert!(result.is_err());
|
|
|
+ }
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_unsupported_opcode() {
|
|
|
+ let add32 = Instruction::from_bytes(&hex!("1300000000000000"));
|
|
|
+ assert!(add32.is_err());
|
|
|
}
|
|
|
}
|