Skip to main content

sbpf_common/
decode.rs

1use {
2    crate::{
3        errors::SBPFError,
4        inst_param::{Number, Register},
5        instruction::Instruction,
6        opcode::Opcode,
7        syscalls::SYSCALLS,
8    },
9    either::Either,
10};
11
12// TODO: passing span for error reporting (not sure if it's necessary)
13
14#[inline]
15fn parse_bytes(bytes: &[u8]) -> Result<(Opcode, u8, u8, i16, i32), SBPFError> {
16    let opcode: Opcode = bytes[0].try_into()?;
17    let reg = bytes[1];
18    let dst = reg & 0x0f;
19    let src = reg >> 4;
20    let off = i16::from_le_bytes([bytes[2], bytes[3]]);
21    let imm = i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
22    Ok((opcode, dst, src, off, imm))
23}
24
25#[inline]
26fn parse_bytes_v3(bytes: &[u8]) -> Result<(Opcode, u8, u8, i16, i32), SBPFError> {
27    let opcode: Opcode = Opcode::try_from_sbpf_v3(bytes[0])?;
28    let reg = bytes[1];
29    let dst = reg & 0x0f;
30    let src = reg >> 4;
31    let off = i16::from_le_bytes([bytes[2], bytes[3]]);
32    let imm = i32::from_le_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
33    Ok((opcode, dst, src, off, imm))
34}
35
36pub fn decode_load_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
37    assert!(bytes.len() >= 16);
38    let (opcode, dst, src, off, imm_low) = parse_bytes(bytes)?;
39    if src != 0 || off != 0 {
40        return Err(SBPFError::BytecodeError {
41            error: format!(
42                "{} instruction has src: {}, off: {} supposed to be zero",
43                opcode, src, off
44            ),
45            span: 0..16,
46            custom_label: None,
47        });
48    }
49    let imm_high = i32::from_le_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]);
50    let imm = ((imm_high as i64) << 32) | (imm_low as u32 as i64);
51    Ok(Instruction {
52        opcode,
53        dst: Some(Register { n: dst }),
54        src: None,
55        off: None,
56        imm: Some(Either::Right(Number::Int(imm))),
57        span: 0..16,
58    })
59}
60
61pub fn decode_load_memory(bytes: &[u8]) -> Result<Instruction, SBPFError> {
62    assert!(bytes.len() >= 8);
63    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
64    if imm != 0 {
65        return Err(SBPFError::BytecodeError {
66            error: format!(
67                "{} instruction has imm: {} supposed to be zero",
68                opcode, imm
69            ),
70            span: 0..8,
71            custom_label: None,
72        });
73    }
74    Ok(Instruction {
75        opcode,
76        dst: Some(Register { n: dst }),
77        src: Some(Register { n: src }),
78        off: Some(Either::Right(off)),
79        imm: None,
80        span: 0..8,
81    })
82}
83
84pub fn decode_store_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
85    assert!(bytes.len() >= 8);
86    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
87    if src != 0 {
88        return Err(SBPFError::BytecodeError {
89            error: format!(
90                "{} instruction has src: {} supposed to be zero",
91                opcode, src
92            ),
93            span: 0..8,
94            custom_label: None,
95        });
96    }
97    Ok(Instruction {
98        opcode,
99        dst: Some(Register { n: dst }),
100        src: None,
101        off: Some(Either::Right(off)),
102        imm: Some(Either::Right(Number::Int(imm.into()))),
103        span: 0..8,
104    })
105}
106
107pub fn decode_store_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
108    assert!(bytes.len() >= 8);
109    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
110    if imm != 0 {
111        return Err(SBPFError::BytecodeError {
112            error: format!(
113                "{} instruction has imm: {} supposed to be zero",
114                opcode, imm
115            ),
116            span: 0..8,
117            custom_label: None,
118        });
119    }
120    Ok(Instruction {
121        opcode,
122        dst: Some(Register { n: dst }),
123        src: Some(Register { n: src }),
124        off: Some(Either::Right(off)),
125        imm: None,
126        span: 0..8,
127    })
128}
129
130pub fn decode_binary_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
131    assert!(bytes.len() >= 8);
132    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
133    if src != 0 || off != 0 {
134        return Err(SBPFError::BytecodeError {
135            error: format!(
136                "{} instruction has src: {}, off: {} supposed to be zeros",
137                opcode, src, off
138            ),
139            span: 0..8,
140            custom_label: None,
141        });
142    }
143    Ok(Instruction {
144        opcode,
145        dst: Some(Register { n: dst }),
146        src: None,
147        off: None,
148        imm: Some(Either::Right(Number::Int(imm.into()))),
149        span: 0..8,
150    })
151}
152
153pub fn decode_endian(bytes: &[u8]) -> Result<Instruction, SBPFError> {
154    assert!(bytes.len() >= 8);
155    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
156    if src != 0 || off != 0 {
157        return Err(SBPFError::BytecodeError {
158            error: format!(
159                "{} instruction has src: {}, off: {} supposed to be zeros",
160                opcode, src, off
161            ),
162            span: 0..8,
163            custom_label: None,
164        });
165    }
166    Ok(Instruction {
167        opcode,
168        dst: Some(Register { n: dst }),
169        src: None,
170        off: None,
171        imm: Some(Either::Right(Number::Int(imm.into()))),
172        span: 0..8,
173    })
174}
175
176pub fn decode_binary_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
177    assert!(bytes.len() >= 8);
178    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
179    if off != 0 || imm != 0 {
180        return Err(SBPFError::BytecodeError {
181            error: format!(
182                "{} instruction has off: {}, imm: {} supposed to be zeros",
183                opcode, off, imm
184            ),
185            span: 0..8,
186            custom_label: None,
187        });
188    }
189    Ok(Instruction {
190        opcode,
191        dst: Some(Register { n: dst }),
192        src: Some(Register { n: src }),
193        off: None,
194        imm: None,
195        span: 0..8,
196    })
197}
198
199pub fn decode_unary(bytes: &[u8]) -> Result<Instruction, SBPFError> {
200    assert!(bytes.len() >= 8);
201    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
202    if src != 0 || off != 0 || imm != 0 {
203        return Err(SBPFError::BytecodeError {
204            error: format!(
205                "{} instruction has src: {}, off: {}, imm: {} supposed to be zeros",
206                opcode, src, off, imm
207            ),
208            span: 0..8,
209            custom_label: None,
210        });
211    }
212    Ok(Instruction {
213        opcode,
214        dst: Some(Register { n: dst }),
215        src: None,
216        off: None,
217        imm: None,
218        span: 0..8,
219    })
220}
221
222pub fn decode_jump(bytes: &[u8]) -> Result<Instruction, SBPFError> {
223    assert!(bytes.len() >= 8);
224    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
225    if dst != 0 || src != 0 || imm != 0 {
226        return Err(SBPFError::BytecodeError {
227            error: format!(
228                "{} instruction has dst: {}, src: {}, imm: {} supposed to be zeros",
229                opcode, dst, src, imm
230            ),
231            span: 0..8,
232            custom_label: None,
233        });
234    }
235    Ok(Instruction {
236        opcode,
237        dst: None,
238        src: None,
239        off: Some(Either::Right(off)),
240        imm: None,
241        span: 0..8,
242    })
243}
244
245pub fn decode_jump_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
246    assert!(bytes.len() >= 8);
247    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
248    if src != 0 {
249        return Err(SBPFError::BytecodeError {
250            error: format!(
251                "{} instruction has src: {} supposed to be zero",
252                opcode, src
253            ),
254            span: 0..8,
255            custom_label: None,
256        });
257    }
258    Ok(Instruction {
259        opcode,
260        dst: Some(Register { n: dst }),
261        src: None,
262        off: Some(Either::Right(off)),
263        imm: Some(Either::Right(Number::Int(imm.into()))),
264        span: 0..8,
265    })
266}
267
268pub fn decode_jump_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
269    assert!(bytes.len() >= 8);
270    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
271    if imm != 0 {
272        return Err(SBPFError::BytecodeError {
273            error: format!(
274                "{} instruction has imm: {} supposed to be zero",
275                opcode, imm
276            ),
277            span: 0..8,
278            custom_label: None,
279        });
280    }
281    Ok(Instruction {
282        opcode,
283        dst: Some(Register { n: dst }),
284        src: Some(Register { n: src }),
285        off: Some(Either::Right(off)),
286        imm: None,
287        span: 0..8,
288    })
289}
290
291pub fn decode_call_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
292    assert!(bytes.len() >= 8);
293    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
294
295    if dst != 0 || off != 0 {
296        return Err(SBPFError::BytecodeError {
297            error: format!(
298                "{} instruction has dst: {} and off: {}, expected to be zeros",
299                opcode, dst, off
300            ),
301            span: 0..8,
302            custom_label: None,
303        });
304    }
305
306    let callimm = match src {
307        0 => {
308            // Static syscall - check if syscall is registered.
309            if let Some(syscall) = SYSCALLS.get(imm as u32) {
310                Either::Left(syscall.to_string())
311            } else {
312                Either::Right(Number::Int(imm.into()))
313            }
314        }
315        1 => Either::Right(Number::Int(imm.into())),
316        _ => {
317            return Err(SBPFError::BytecodeError {
318                error: format!("{} instruction has invalid src register: {}", opcode, src),
319                span: 0..8,
320                custom_label: None,
321            });
322        }
323    };
324
325    Ok(Instruction {
326        opcode,
327        dst: None,
328        src: Some(Register { n: src }),
329        off: None,
330        imm: Some(callimm),
331        span: 0..8,
332    })
333}
334
335pub fn decode_call_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
336    assert!(bytes.len() >= 8);
337    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
338    // Handle SBPF Callx normalization
339    let (dst, imm) = if dst == 0 && imm != 0 {
340        (imm as u8, 0)
341    } else {
342        (dst, 0)
343    };
344
345    // TODO: sbpf encodes dst_reg in immediate
346    if src != 0 || off != 0 || imm != 0 {
347        return Err(SBPFError::BytecodeError {
348            error: format!(
349                "{} instruction has src: {}, off: {}, imm: {} supposed to be zeros",
350                opcode, src, off, imm
351            ),
352            span: 0..8,
353            custom_label: None,
354        });
355    }
356    Ok(Instruction {
357        opcode,
358        dst: Some(Register { n: dst }),
359        src: None,
360        off: None,
361        imm: None,
362        span: 0..8,
363    })
364}
365
366pub fn decode_exit(bytes: &[u8]) -> Result<Instruction, SBPFError> {
367    assert!(bytes.len() >= 8);
368    let (opcode, dst, src, off, imm) = parse_bytes(bytes)?;
369    if dst != 0 || src != 0 || off != 0 || imm != 0 {
370        return Err(SBPFError::BytecodeError {
371            error: format!(
372                "{} instruction dst: {}, src: {}, off: {}, imm: {} supposed to be zero",
373                opcode, dst, src, off, imm
374            ),
375            span: 0..8,
376            custom_label: None,
377        });
378    }
379    Ok(Instruction {
380        opcode,
381        dst: None,
382        src: None,
383        off: None,
384        imm: None,
385        span: 0..8,
386    })
387}
388
389pub fn decode_jump32_immediate(bytes: &[u8]) -> Result<Instruction, SBPFError> {
390    assert!(bytes.len() >= 8);
391    let (opcode, dst, src, off, imm) = parse_bytes_v3(bytes)?;
392    if src != 0 {
393        return Err(SBPFError::BytecodeError {
394            error: format!(
395                "{} instruction has src: {} supposed to be zero",
396                opcode, src
397            ),
398            span: 0..8,
399            custom_label: None,
400        });
401    }
402    Ok(Instruction {
403        opcode,
404        dst: Some(Register { n: dst }),
405        src: None,
406        off: Some(Either::Right(off)),
407        imm: Some(Either::Right(Number::Int(imm.into()))),
408        span: 0..8,
409    })
410}
411
412pub fn decode_jump32_register(bytes: &[u8]) -> Result<Instruction, SBPFError> {
413    assert!(bytes.len() >= 8);
414    let (opcode, dst, src, off, imm) = parse_bytes_v3(bytes)?;
415    if imm != 0 {
416        return Err(SBPFError::BytecodeError {
417            error: format!(
418                "{} instruction has imm: {} supposed to be zero",
419                opcode, imm
420            ),
421            span: 0..8,
422            custom_label: None,
423        });
424    }
425    Ok(Instruction {
426        opcode,
427        dst: Some(Register { n: dst }),
428        src: Some(Register { n: src }),
429        off: Some(Either::Right(off)),
430        imm: None,
431        span: 0..8,
432    })
433}
434
435#[cfg(test)]
436mod tests {
437    use {super::*, crate::syscalls::REGISTERED_SYSCALLS, syscall_map::murmur3_32};
438
439    #[test]
440    fn test_decode_load_immediate_valid() {
441        // lddw r1, 0x123456789abcdef0
442        let mut bytes = vec![0x18, 0x01, 0x00, 0x00, 0xf0, 0xde, 0xbc, 0x9a];
443        bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x78, 0x56, 0x34, 0x12]);
444
445        let result = decode_load_immediate(&bytes).unwrap();
446        assert_eq!(result.opcode, Opcode::Lddw);
447        assert_eq!(result.dst.unwrap().n, 1);
448        assert!(result.src.is_none());
449        assert!(result.off.is_none());
450        assert_eq!(result.span, 0..16);
451    }
452
453    #[test]
454    fn test_decode_load_immediate_error_nonzero_src() {
455        let mut bytes = vec![0x18, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
456        bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
457
458        let result = decode_load_immediate(&bytes);
459        assert!(result.is_err());
460    }
461
462    #[test]
463    fn test_decode_load_immediate_error_nonzero_off() {
464        let mut bytes = vec![0x18, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
465        bytes.extend_from_slice(&[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
466
467        let result = decode_load_immediate(&bytes);
468        assert!(result.is_err());
469    }
470
471    #[test]
472    fn test_decode_load_memory_valid() {
473        // ldxw r2, [r3+10]
474        let bytes = vec![0x61, 0x32, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00];
475
476        let result = decode_load_memory(&bytes).unwrap();
477        assert_eq!(result.opcode, Opcode::Ldxw);
478        assert_eq!(result.dst.unwrap().n, 2);
479        assert_eq!(result.src.unwrap().n, 3);
480        assert_eq!(result.span, 0..8);
481    }
482
483    #[test]
484    fn test_decode_load_memory_error_nonzero_imm() {
485        let bytes = vec![0x61, 0x32, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00];
486
487        let result = decode_load_memory(&bytes);
488        assert!(result.is_err());
489    }
490
491    #[test]
492    fn test_decode_store_immediate_valid() {
493        // stw [r1+4], 100
494        let bytes = vec![0x62, 0x01, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00];
495
496        let result = decode_store_immediate(&bytes).unwrap();
497        assert_eq!(result.opcode, Opcode::Stw);
498        assert_eq!(result.dst.unwrap().n, 1);
499        assert!(result.src.is_none());
500        assert_eq!(result.span, 0..8);
501    }
502
503    #[test]
504    fn test_decode_store_immediate_error_nonzero_src() {
505        let bytes = vec![0x62, 0x11, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00];
506
507        let result = decode_store_immediate(&bytes);
508        assert!(result.is_err());
509    }
510
511    #[test]
512    fn test_decode_store_register_valid() {
513        // stxw [r1+4], r2
514        let bytes = vec![0x63, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00];
515
516        let result = decode_store_register(&bytes).unwrap();
517        assert_eq!(result.opcode, Opcode::Stxw);
518        assert_eq!(result.dst.unwrap().n, 1);
519        assert_eq!(result.src.unwrap().n, 2);
520        assert_eq!(result.span, 0..8);
521    }
522
523    #[test]
524    fn test_decode_store_register_error_nonzero_imm() {
525        let bytes = vec![0x63, 0x21, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00];
526
527        let result = decode_store_register(&bytes);
528        assert!(result.is_err());
529    }
530
531    #[test]
532    fn test_decode_binary_immediate_valid() {
533        // add32 r1, 100
534        let bytes = vec![0x04, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00];
535
536        let result = decode_binary_immediate(&bytes).unwrap();
537        assert_eq!(result.opcode, Opcode::Add32Imm);
538        assert_eq!(result.dst.unwrap().n, 1);
539        assert!(result.src.is_none());
540        assert!(result.off.is_none());
541        assert_eq!(result.span, 0..8);
542    }
543
544    #[test]
545    fn test_decode_binary_immediate_error_nonzero_src() {
546        let bytes = vec![0x04, 0x11, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00];
547
548        let result = decode_binary_immediate(&bytes);
549        assert!(result.is_err());
550    }
551
552    #[test]
553    fn test_decode_binary_immediate_error_nonzero_off() {
554        let bytes = vec![0x04, 0x01, 0x01, 0x00, 0x64, 0x00, 0x00, 0x00];
555
556        let result = decode_binary_immediate(&bytes);
557        assert!(result.is_err());
558    }
559
560    #[test]
561    fn test_decode_binary_register_valid() {
562        // add32 r1, r2
563        let bytes = vec![0x0c, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
564
565        let result = decode_binary_register(&bytes).unwrap();
566        assert_eq!(result.opcode, Opcode::Add32Reg);
567        assert_eq!(result.dst.unwrap().n, 1);
568        assert_eq!(result.src.unwrap().n, 2);
569        assert!(result.off.is_none());
570        assert!(result.imm.is_none());
571        assert_eq!(result.span, 0..8);
572    }
573
574    #[test]
575    fn test_decode_binary_register_error_nonzero_off() {
576        let bytes = vec![0x0c, 0x21, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
577
578        let result = decode_binary_register(&bytes);
579        assert!(result.is_err());
580    }
581
582    #[test]
583    fn test_decode_binary_register_error_nonzero_imm() {
584        let bytes = vec![0x0c, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
585
586        let result = decode_binary_register(&bytes);
587        assert!(result.is_err());
588    }
589
590    #[test]
591    fn test_decode_unary_valid() {
592        // neg64 r1
593        let bytes = vec![0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
594
595        let result = decode_unary(&bytes).unwrap();
596        assert_eq!(result.opcode, Opcode::Neg64);
597        assert_eq!(result.dst.unwrap().n, 1);
598        assert!(result.src.is_none());
599        assert!(result.off.is_none());
600        assert!(result.imm.is_none());
601        assert_eq!(result.span, 0..8);
602    }
603
604    #[test]
605    fn test_decode_unary_error_nonzero_src() {
606        let bytes = vec![0x87, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
607
608        let result = decode_unary(&bytes);
609        assert!(result.is_err());
610    }
611
612    #[test]
613    fn test_decode_unary_error_nonzero_off() {
614        let bytes = vec![0x87, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
615
616        let result = decode_unary(&bytes);
617        assert!(result.is_err());
618    }
619
620    #[test]
621    fn test_decode_unary_error_nonzero_imm() {
622        let bytes = vec![0x87, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
623
624        let result = decode_unary(&bytes);
625        assert!(result.is_err());
626    }
627
628    #[test]
629    fn test_decode_endian_valid() {
630        // le16 r1
631        let bytes = vec![0xd4, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00];
632
633        let result = decode_endian(&bytes).unwrap();
634        assert_eq!(result.opcode, Opcode::Le);
635        assert_eq!(result.dst.unwrap().n, 1);
636        assert!(result.src.is_none());
637        assert!(result.off.is_none());
638        assert_eq!(result.span, 0..8);
639    }
640
641    #[test]
642    fn test_decode_endian_error_nonzero_src() {
643        let bytes = vec![0xd4, 0x11, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00];
644
645        let result = decode_endian(&bytes);
646        assert!(result.is_err());
647    }
648
649    #[test]
650    fn test_decode_endian_error_nonzero_off() {
651        let bytes = vec![0xdc, 0x01, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00];
652
653        let result = decode_endian(&bytes);
654        assert!(result.is_err());
655    }
656
657    #[test]
658    fn test_decode_jump_valid() {
659        // ja +10
660        let bytes = vec![0x05, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00];
661
662        let result = decode_jump(&bytes).unwrap();
663        assert_eq!(result.opcode, Opcode::Ja);
664        assert!(result.dst.is_none());
665        assert!(result.src.is_none());
666        assert!(result.imm.is_none());
667        assert_eq!(result.span, 0..8);
668    }
669
670    #[test]
671    fn test_decode_jump_error_nonzero_dst() {
672        let bytes = vec![0x05, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00];
673
674        let result = decode_jump(&bytes);
675        assert!(result.is_err());
676    }
677
678    #[test]
679    fn test_decode_jump_error_nonzero_src() {
680        let bytes = vec![0x05, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00];
681
682        let result = decode_jump(&bytes);
683        assert!(result.is_err());
684    }
685
686    #[test]
687    fn test_decode_jump_error_nonzero_imm() {
688        let bytes = vec![0x05, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00];
689
690        let result = decode_jump(&bytes);
691        assert!(result.is_err());
692    }
693
694    #[test]
695    fn test_decode_jump_immediate_valid() {
696        // jeq r1, 100, +10
697        let bytes = vec![0x15, 0x01, 0x0a, 0x00, 0x64, 0x00, 0x00, 0x00];
698
699        let result = decode_jump_immediate(&bytes).unwrap();
700        assert_eq!(result.opcode, Opcode::JeqImm);
701        assert_eq!(result.dst.unwrap().n, 1);
702        assert!(result.src.is_none());
703        assert_eq!(result.span, 0..8);
704    }
705
706    #[test]
707    fn test_decode_jump_immediate_error_nonzero_src() {
708        let bytes = vec![0x15, 0x11, 0x0a, 0x00, 0x64, 0x00, 0x00, 0x00];
709
710        let result = decode_jump_immediate(&bytes);
711        assert!(result.is_err());
712    }
713
714    #[test]
715    fn test_decode_jump_register_valid() {
716        // jeq r1, r2, +10
717        let bytes = vec![0x1d, 0x21, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00];
718
719        let result = decode_jump_register(&bytes).unwrap();
720        assert_eq!(result.opcode, Opcode::JeqReg);
721        assert_eq!(result.dst.unwrap().n, 1);
722        assert_eq!(result.src.unwrap().n, 2);
723        assert_eq!(result.span, 0..8);
724    }
725
726    #[test]
727    fn test_decode_jump_register_error_nonzero_imm() {
728        let bytes = vec![0x1d, 0x21, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00];
729
730        let result = decode_jump_register(&bytes);
731        assert!(result.is_err());
732    }
733
734    #[test]
735    fn test_decode_call_immediate_valid_regular() {
736        // call 100 (non-syscall) - src must be 1
737        let bytes = vec![0x85, 0x10, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00];
738
739        let result = decode_call_immediate(&bytes).unwrap();
740        assert_eq!(result.opcode, Opcode::Call);
741        assert!(result.dst.is_none());
742        assert_eq!(result.src.unwrap().n, 1);
743        assert_eq!(result.span, 0..8);
744    }
745
746    #[test]
747    fn test_decode_call_immediate_error_invalid_regular() {
748        // Invalid: dst != 0 for non-syscall
749        let bytes = vec![0x85, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00];
750
751        let result = decode_call_immediate(&bytes);
752        assert!(result.is_err());
753    }
754
755    #[test]
756    fn test_decode_call_immediate_valid_syscall() {
757        let syscall_name = REGISTERED_SYSCALLS[0];
758        let syscall_hash = murmur3_32(syscall_name);
759        let hash_bytes = syscall_hash.to_le_bytes();
760
761        // Build call instruction with syscall hash and all zeros for dst, src, off
762        let bytes = vec![
763            0x85,
764            0x00,
765            0x00,
766            0x00,
767            hash_bytes[0],
768            hash_bytes[1],
769            hash_bytes[2],
770            hash_bytes[3],
771        ];
772
773        let result = decode_call_immediate(&bytes).unwrap();
774        assert_eq!(result.opcode, Opcode::Call);
775        assert!(result.dst.is_none());
776        assert_eq!(result.src.unwrap().n, 0);
777    }
778
779    #[test]
780    fn test_decode_call_immediate_syscall_error_nonzero_dst() {
781        let syscall_hash = murmur3_32(REGISTERED_SYSCALLS[0]);
782        let hash_bytes = syscall_hash.to_le_bytes();
783
784        let bytes = vec![
785            0x85,
786            0x01,
787            0x00,
788            0x00,
789            hash_bytes[0],
790            hash_bytes[1],
791            hash_bytes[2],
792            hash_bytes[3],
793        ];
794
795        let result = decode_call_immediate(&bytes);
796        assert!(result.is_err());
797    }
798
799    #[test]
800    fn test_decode_call_immediate_syscall_nonzero_src() {
801        let syscall_hash = murmur3_32(REGISTERED_SYSCALLS[0]);
802        let hash_bytes = syscall_hash.to_le_bytes();
803
804        let bytes = vec![
805            0x85,
806            0x10,
807            0x00,
808            0x00,
809            hash_bytes[0],
810            hash_bytes[1],
811            hash_bytes[2],
812            hash_bytes[3],
813        ];
814
815        let result = decode_call_immediate(&bytes).unwrap();
816        assert_eq!(result.src.unwrap().n, 1);
817        assert!(matches!(result.imm, Some(Either::Right(_))));
818    }
819
820    #[test]
821    fn test_decode_call_immediate_syscall_error_nonzero_off() {
822        let syscall_hash = murmur3_32(REGISTERED_SYSCALLS[0]);
823        let hash_bytes = syscall_hash.to_le_bytes();
824
825        let bytes = vec![
826            0x85,
827            0x00,
828            0x01,
829            0x00,
830            hash_bytes[0],
831            hash_bytes[1],
832            hash_bytes[2],
833            hash_bytes[3],
834        ];
835        let result = decode_call_immediate(&bytes);
836        assert!(result.is_err());
837    }
838
839    #[test]
840    fn test_decode_call_register_valid() {
841        // callx r1
842        let bytes = vec![0x8d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
843
844        let result = decode_call_register(&bytes).unwrap();
845        assert_eq!(result.opcode, Opcode::Callx);
846        assert_eq!(result.dst.unwrap().n, 1);
847        assert!(result.src.is_none());
848        assert!(result.off.is_none());
849        assert!(result.imm.is_none());
850        assert_eq!(result.span, 0..8);
851    }
852
853    #[test]
854    fn test_decode_call_register_normalized() {
855        // callx with dst in imm (sBPF normalization)
856        let bytes = vec![0x8d, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00];
857        let result = decode_call_register(&bytes).unwrap();
858        assert_eq!(result.dst.unwrap().n, 5);
859    }
860
861    #[test]
862    fn test_decode_call_register_error_nonzero_src() {
863        let bytes = vec![0x8d, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
864
865        let result = decode_call_register(&bytes);
866        assert!(result.is_err());
867    }
868
869    #[test]
870    fn test_decode_call_register_error_nonzero_off() {
871        let bytes = vec![0x8d, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
872
873        let result = decode_call_register(&bytes);
874        assert!(result.is_err());
875    }
876
877    #[test]
878    fn test_decode_exit_valid() {
879        // exit
880        let bytes = vec![0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
881
882        let result = decode_exit(&bytes).unwrap();
883        assert_eq!(result.opcode, Opcode::Exit);
884        assert!(result.dst.is_none());
885        assert!(result.src.is_none());
886        assert!(result.off.is_none());
887        assert!(result.imm.is_none());
888        assert_eq!(result.span, 0..8);
889    }
890
891    #[test]
892    fn test_decode_exit_error_nonzero_dst() {
893        let bytes = vec![0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
894
895        let result = decode_exit(&bytes);
896        assert!(result.is_err());
897    }
898
899    #[test]
900    fn test_decode_exit_error_nonzero_src() {
901        let bytes = vec![0x95, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
902
903        let result = decode_exit(&bytes);
904        assert!(result.is_err());
905    }
906
907    #[test]
908    fn test_decode_exit_error_nonzero_off() {
909        let bytes = vec![0x95, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00];
910
911        let result = decode_exit(&bytes);
912        assert!(result.is_err());
913    }
914
915    #[test]
916    fn test_decode_exit_error_nonzero_imm() {
917        let bytes = vec![0x95, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00];
918
919        let result = decode_exit(&bytes);
920        assert!(result.is_err());
921    }
922
923    #[test]
924    fn test_all_load_memory_opcodes() {
925        let opcodes = vec![
926            (0x71, Opcode::Ldxb),
927            (0x69, Opcode::Ldxh),
928            (0x61, Opcode::Ldxw),
929            (0x79, Opcode::Ldxdw),
930        ];
931
932        for (byte, expected_opcode) in opcodes {
933            let bytes = vec![byte, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00];
934            let result = decode_load_memory(&bytes).unwrap();
935            assert_eq!(result.opcode, expected_opcode);
936        }
937    }
938
939    #[test]
940    fn test_all_store_immediate_opcodes() {
941        let opcodes = vec![
942            (0x72, Opcode::Stb),
943            (0x6a, Opcode::Sth),
944            (0x62, Opcode::Stw),
945            (0x7a, Opcode::Stdw),
946        ];
947
948        for (byte, expected_opcode) in opcodes {
949            let bytes = vec![byte, 0x01, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00];
950            let result = decode_store_immediate(&bytes).unwrap();
951            assert_eq!(result.opcode, expected_opcode);
952        }
953    }
954
955    #[test]
956    fn test_all_store_register_opcodes() {
957        let opcodes = vec![
958            (0x73, Opcode::Stxb),
959            (0x6b, Opcode::Stxh),
960            (0x63, Opcode::Stxw),
961            (0x7b, Opcode::Stxdw),
962        ];
963
964        for (byte, expected_opcode) in opcodes {
965            let bytes = vec![byte, 0x21, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00];
966            let result = decode_store_register(&bytes).unwrap();
967            assert_eq!(result.opcode, expected_opcode);
968        }
969    }
970
971    #[test]
972    fn test_decode_alu64_operations() {
973        let ops = vec![
974            (0x07, Opcode::Add64Imm),
975            (0x0f, Opcode::Add64Reg),
976            (0x17, Opcode::Sub64Imm),
977            (0x1f, Opcode::Sub64Reg),
978        ];
979
980        for (byte, expected_opcode) in ops {
981            let mut bytes = vec![byte, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00];
982
983            if byte & 0x08 == 0 {
984                // Immediate
985                let result = decode_binary_immediate(&bytes).unwrap();
986                assert_eq!(result.opcode, expected_opcode);
987            } else {
988                // Register
989                bytes[4] = 0x00; // imm must be 0 for reg ops
990                bytes[1] = 0x21; // add src
991                let result = decode_binary_register(&bytes).unwrap();
992                assert_eq!(result.opcode, expected_opcode);
993            }
994        }
995    }
996
997    #[test]
998    fn test_decode_neg32() {
999        let bytes = vec![0x84, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1000
1001        let result = decode_unary(&bytes).unwrap();
1002        assert_eq!(result.opcode, Opcode::Neg32);
1003        assert_eq!(result.dst.unwrap().n, 1);
1004    }
1005
1006    #[test]
1007    fn test_decode_various_jump_immediates() {
1008        let jumps = vec![
1009            (0x15, Opcode::JeqImm),
1010            (0x25, Opcode::JgtImm),
1011            (0x35, Opcode::JgeImm),
1012        ];
1013
1014        for (byte, expected_opcode) in jumps {
1015            let bytes = vec![byte, 0x01, 0x0a, 0x00, 0x64, 0x00, 0x00, 0x00];
1016            let result = decode_jump_immediate(&bytes).unwrap();
1017            assert_eq!(result.opcode, expected_opcode);
1018        }
1019    }
1020
1021    #[test]
1022    fn test_decode_various_jump_registers() {
1023        let jumps = vec![
1024            (0x1d, Opcode::JeqReg),
1025            (0x2d, Opcode::JgtReg),
1026            (0x3d, Opcode::JgeReg),
1027        ];
1028
1029        for (byte, expected_opcode) in jumps {
1030            let bytes = vec![byte, 0x21, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00];
1031            let result = decode_jump_register(&bytes).unwrap();
1032            assert_eq!(result.opcode, expected_opcode);
1033        }
1034    }
1035
1036    #[test]
1037    fn test_decode_static_syscall_unknown_hash() {
1038        let unknown_hash: u32 = 0x12345678;
1039        let hash_bytes = unknown_hash.to_le_bytes();
1040
1041        let bytes = vec![
1042            0x85,
1043            0x00, // src=0 (static syscall)
1044            0x00,
1045            0x00,
1046            hash_bytes[0],
1047            hash_bytes[1],
1048            hash_bytes[2],
1049            hash_bytes[3],
1050        ];
1051
1052        let result = decode_call_immediate(&bytes).unwrap();
1053        assert_eq!(result.opcode, Opcode::Call);
1054        assert!(result.dst.is_none());
1055        assert_eq!(result.src.unwrap().n, 0);
1056
1057        // unknown hash should be kept as numeric immediate
1058        match result.imm {
1059            Some(Either::Right(Number::Int(imm))) => {
1060                assert_eq!(imm as u32, unknown_hash);
1061            }
1062            _ => panic!("Expected numeric immediate for unknown syscall hash"),
1063        }
1064    }
1065
1066    #[test]
1067    fn test_decode_call_invalid_src_register() {
1068        let bytes = vec![0x85, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; // src=2
1069        let result = decode_call_immediate(&bytes);
1070        assert!(result.is_err());
1071    }
1072}