vtil_parser/
serialize.rs

1// BSD 3-Clause License
2//
3// Copyright © 2020-2021 Keegan Saunders
4// Copyright © 2020-2021 VTIL Project
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are met:
9//
10// 1. Redistributions of source code must retain the above copyright notice, this
11//    list of conditions and the following disclaimer.
12//
13// 2. Redistributions in binary form must reproduce the above copyright notice,
14//    this list of conditions and the following disclaimer in the documentation
15//    and/or other materials provided with the distribution.
16//
17// 3. Neither the name of the copyright holder nor the names of its
18//    contributors may be used to endorse or promote products derived from
19//    this software without specific prior written permission.
20//
21// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//
32
33use indexmap::map::IndexMap;
34use scroll::{
35    ctx::{self, SizeWith},
36    Endian, Pread, Pwrite,
37};
38use std::convert::TryInto;
39use std::mem::size_of;
40
41use super::{
42    ArchitectureIdentifier, BasicBlock, Error, Header, Immediate, ImmediateDesc, Instruction, Op,
43    Operand, RegisterDesc, RegisterFlags, Result, Routine, RoutineConvention, SubroutineConvention,
44    Vip, LOCAL_ID_MASK,
45};
46
47const VTIL_MAGIC_1: u32 = 0x4c495456;
48const VTIL_MAGIC_2: u16 = 0xdead;
49
50impl ctx::SizeWith<ArchitectureIdentifier> for ArchitectureIdentifier {
51    fn size_with(_arch_id: &ArchitectureIdentifier) -> usize {
52        size_of::<u8>()
53    }
54}
55
56impl ctx::TryFromCtx<'_, Endian> for ArchitectureIdentifier {
57    type Error = Error;
58
59    fn try_from_ctx(source: &[u8], _endian: Endian) -> Result<(Self, usize)> {
60        let arch_id = match source.pread::<u8>(0)? {
61            0 => ArchitectureIdentifier::Amd64,
62            1 => ArchitectureIdentifier::Arm64,
63            2 => ArchitectureIdentifier::Virtual,
64            arch_id => {
65                return Err(Error::Malformed(format!(
66                    "Invalid architecture identifier: {:#x}",
67                    arch_id
68                )))
69            }
70        };
71        assert_eq!(ArchitectureIdentifier::size_with(&arch_id), 1);
72        Ok((arch_id, 1))
73    }
74}
75
76impl ctx::TryIntoCtx<Endian> for ArchitectureIdentifier {
77    type Error = Error;
78
79    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
80        sink.pwrite::<u8>(self as u8, 0)?;
81        Ok(size_of::<u8>())
82    }
83}
84
85impl ctx::SizeWith<Header> for Header {
86    fn size_with(header: &Header) -> usize {
87        let mut size = size_of::<u32>();
88        size += ArchitectureIdentifier::size_with(&header.arch_id);
89        size += size_of::<u8>();
90        size += size_of::<u16>();
91        size
92    }
93}
94
95impl ctx::TryFromCtx<'_, Endian> for Header {
96    type Error = Error;
97
98    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
99        let offset = &mut 0;
100
101        let magic = source.gread_with::<u32>(offset, endian)?;
102        if magic != VTIL_MAGIC_1 {
103            return Err(Error::Malformed(format!(
104                "VTIL magic is invalid: {:#x}",
105                magic
106            )));
107        }
108
109        let arch_id = source.gread_with::<ArchitectureIdentifier>(offset, endian)?;
110        let _zero = source.gread::<u8>(offset)?;
111
112        let magic = source.gread_with::<u16>(offset, endian)?;
113        if magic != VTIL_MAGIC_2 {
114            return Err(Error::Malformed(format!(
115                "VTIL magic is invalid: {:#x}",
116                magic
117            )));
118        }
119
120        let header = Header { arch_id };
121        assert_eq!(Header::size_with(&header), *offset);
122        Ok((header, *offset))
123    }
124}
125
126impl ctx::TryIntoCtx<Endian> for Header {
127    type Error = Error;
128
129    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
130        let offset = &mut 0;
131        sink.gwrite::<u32>(VTIL_MAGIC_1, offset)?;
132        sink.gwrite::<ArchitectureIdentifier>(self.arch_id, offset)?;
133        sink.gwrite::<u8>(0, offset)?;
134        sink.gwrite::<u16>(VTIL_MAGIC_2, offset)?;
135        Ok(*offset)
136    }
137}
138
139impl ctx::SizeWith<Vip> for Vip {
140    fn size_with(_vip: &Vip) -> usize {
141        size_of::<u64>()
142    }
143}
144
145impl ctx::TryFromCtx<'_, Endian> for Vip {
146    type Error = Error;
147
148    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
149        let offset = &mut 0;
150        let vip = Vip(source.gread_with::<u64>(offset, endian)?);
151        assert_eq!(Vip::size_with(&vip), *offset);
152        Ok((vip, *offset))
153    }
154}
155
156impl ctx::TryIntoCtx<Endian> for Vip {
157    type Error = Error;
158
159    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
160        Ok(sink.pwrite::<u64>(self.0, 0)?)
161    }
162}
163
164impl ctx::SizeWith<RegisterDesc> for RegisterDesc {
165    fn size_with(_reg: &RegisterDesc) -> usize {
166        let mut size = 0;
167        size += size_of::<u64>();
168        size += size_of::<u64>();
169        size += size_of::<i32>();
170        size += size_of::<i32>();
171        size
172    }
173}
174
175impl ctx::TryFromCtx<'_, Endian> for RegisterDesc {
176    type Error = Error;
177
178    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
179        let offset = &mut 0;
180
181        let flags = unsafe {
182            RegisterFlags::from_bits_unchecked(source.gread_with::<u64>(offset, endian)?)
183        };
184
185        let combined_id = source.gread_with::<u64>(offset, endian)?;
186        if ((combined_id & !LOCAL_ID_MASK) >> 56) > 2 {
187            return Err(Error::Malformed(
188                "Register flags are invalid: >2".to_string(),
189            ));
190        }
191
192        let bit_count = source.gread_with::<i32>(offset, endian)?;
193        let bit_offset = source.gread_with::<i32>(offset, endian)?;
194
195        let reg = RegisterDesc {
196            flags,
197            combined_id,
198            bit_count,
199            bit_offset,
200        };
201        assert_eq!(RegisterDesc::size_with(&reg), *offset);
202        Ok((reg, *offset))
203    }
204}
205
206impl ctx::TryIntoCtx<Endian> for RegisterDesc {
207    type Error = Error;
208
209    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
210        let offset = &mut 0;
211        sink.gwrite::<u64>(self.flags.bits(), offset)?;
212        sink.gwrite::<u64>(self.combined_id, offset)?;
213        sink.gwrite::<i32>(self.bit_count, offset)?;
214        sink.gwrite::<i32>(self.bit_offset, offset)?;
215        Ok(*offset)
216    }
217}
218
219impl ctx::SizeWith<RoutineConvention> for RoutineConvention {
220    fn size_with(routine_convention: &RoutineConvention) -> usize {
221        let mut size = 0;
222
223        size += size_of::<u32>();
224        for reg in &routine_convention.volatile_registers {
225            size += RegisterDesc::size_with(reg);
226        }
227
228        size += size_of::<u32>();
229        for reg in &routine_convention.param_registers {
230            size += RegisterDesc::size_with(reg);
231        }
232
233        size += size_of::<u32>();
234        for reg in &routine_convention.retval_registers {
235            size += RegisterDesc::size_with(reg);
236        }
237
238        size += RegisterDesc::size_with(&routine_convention.frame_register);
239        size += size_of::<u64>();
240        size += size_of::<u8>();
241
242        size
243    }
244}
245
246impl ctx::TryFromCtx<'_, Endian> for RoutineConvention {
247    type Error = Error;
248
249    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
250        let offset = &mut 0;
251
252        let volatile_registers_count = source.gread_with::<u32>(offset, endian)?;
253        let mut volatile_registers =
254            Vec::<RegisterDesc>::with_capacity(volatile_registers_count as usize);
255        for _ in 0..volatile_registers_count {
256            volatile_registers.push(source.gread_with(offset, endian)?);
257        }
258
259        let param_registers_count = source.gread_with::<u32>(offset, endian)?;
260        let mut param_registers =
261            Vec::<RegisterDesc>::with_capacity(param_registers_count as usize);
262        for _ in 0..param_registers_count {
263            param_registers.push(source.gread_with(offset, endian)?);
264        }
265
266        let retval_registers_count = source.gread_with::<u32>(offset, endian)?;
267        let mut retval_registers =
268            Vec::<RegisterDesc>::with_capacity(retval_registers_count as usize);
269        for _ in 0..retval_registers_count {
270            retval_registers.push(source.gread_with(offset, endian)?);
271        }
272
273        let frame_register = source.gread_with::<RegisterDesc>(offset, endian)?;
274        let shadow_space = source.gread_with::<u64>(offset, endian)?;
275        let purge_stack = source.gread_with::<u8>(offset, endian)? != 0;
276
277        let routine_convention = RoutineConvention {
278            volatile_registers,
279            param_registers,
280            retval_registers,
281            frame_register,
282            shadow_space,
283            purge_stack,
284        };
285        assert_eq!(RoutineConvention::size_with(&routine_convention), *offset);
286        Ok((routine_convention, *offset))
287    }
288}
289
290impl ctx::TryIntoCtx<Endian> for RoutineConvention {
291    type Error = Error;
292
293    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
294        let offset = &mut 0;
295
296        sink.gwrite::<u32>(self.volatile_registers.len().try_into()?, offset)?;
297        for reg in self.volatile_registers {
298            sink.gwrite::<RegisterDesc>(reg, offset)?;
299        }
300
301        sink.gwrite::<u32>(self.param_registers.len().try_into()?, offset)?;
302        for reg in self.param_registers {
303            sink.gwrite::<RegisterDesc>(reg, offset)?;
304        }
305
306        sink.gwrite::<u32>(self.retval_registers.len().try_into()?, offset)?;
307        for reg in self.retval_registers {
308            sink.gwrite::<RegisterDesc>(reg, offset)?;
309        }
310
311        sink.gwrite::<RegisterDesc>(self.frame_register, offset)?;
312        sink.gwrite::<u64>(self.shadow_space, offset)?;
313        sink.gwrite::<u8>(self.purge_stack.into(), offset)?;
314        Ok(*offset)
315    }
316}
317
318impl ctx::SizeWith<ImmediateDesc> for ImmediateDesc {
319    fn size_with(_imm: &ImmediateDesc) -> usize {
320        let mut size = 0;
321        size += size_of::<u64>();
322        size += size_of::<u32>();
323        size
324    }
325}
326
327impl ctx::TryFromCtx<'_, Endian> for ImmediateDesc {
328    type Error = Error;
329
330    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
331        let offset = &mut 0;
332
333        let value = source.gread_with::<u64>(offset, endian)?;
334        let bit_count = source.gread_with::<u32>(offset, endian)?;
335
336        let imm = ImmediateDesc {
337            value: Immediate { u64: value },
338            bit_count,
339        };
340        assert_eq!(ImmediateDesc::size_with(&imm), *offset);
341        Ok((imm, *offset))
342    }
343}
344
345impl ctx::TryIntoCtx<Endian> for ImmediateDesc {
346    type Error = Error;
347
348    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
349        let offset = &mut 0;
350        sink.gwrite::<u64>(self.value.u64(), offset)?;
351        sink.gwrite::<u32>(self.bit_count, offset)?;
352        Ok(*offset)
353    }
354}
355
356impl ctx::SizeWith<Operand> for Operand {
357    fn size_with(operand: &Operand) -> usize {
358        let mut size = 0;
359        size += size_of::<u32>();
360        size += match operand {
361            Operand::ImmediateDesc(i) => ImmediateDesc::size_with(i),
362            Operand::RegisterDesc(r) => RegisterDesc::size_with(r),
363        };
364        size
365    }
366}
367
368impl ctx::TryFromCtx<'_, Endian> for Operand {
369    type Error = Error;
370
371    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
372        let offset = &mut 0;
373
374        let sp_index = source.gread_with::<u32>(offset, endian)?;
375        let operand = match sp_index {
376            0 => Operand::ImmediateDesc(source.gread_with::<ImmediateDesc>(offset, endian)?),
377            1 => Operand::RegisterDesc(source.gread_with::<RegisterDesc>(offset, endian)?),
378            i => return Err(Error::Malformed(format!("Invalid operand: {:#x}", i))),
379        };
380        assert_eq!(Operand::size_with(&operand), *offset);
381        Ok((operand, *offset))
382    }
383}
384
385impl ctx::TryIntoCtx<Endian> for Operand {
386    type Error = Error;
387
388    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
389        let offset = &mut 0;
390        match self {
391            Operand::ImmediateDesc(i) => {
392                sink.gwrite::<u32>(0, offset)?;
393                sink.gwrite::<ImmediateDesc>(i, offset)?;
394            }
395            Operand::RegisterDesc(r) => {
396                sink.gwrite::<u32>(1, offset)?;
397                sink.gwrite::<RegisterDesc>(r, offset)?;
398            }
399        }
400        Ok(*offset)
401    }
402}
403
404impl ctx::SizeWith<Op> for Op {
405    fn size_with(op: &Op) -> usize {
406        let mut size = 0;
407        size += size_of::<u32>();
408        size += op.name().as_bytes().len();
409        size += size_of::<u32>();
410        for operand in op.operands() {
411            size += Operand::size_with(operand);
412        }
413        size
414    }
415}
416
417impl<'a> ctx::TryFromCtx<'a, Endian> for Op {
418    type Error = Error;
419
420    fn try_from_ctx(source: &'a [u8], endian: Endian) -> Result<(Self, usize)> {
421        let offset = &mut 0;
422
423        let name_size = source.gread_with::<u32>(offset, endian)?;
424        let name = std::str::from_utf8(source.gread_with::<&'a [u8]>(offset, name_size as usize)?)?;
425
426        let operands_count = source.gread_with::<u32>(offset, endian)?;
427
428        let op = match name {
429            "mov" => {
430                if operands_count == 2 {
431                    let op1 = source.gread_with::<Operand>(offset, endian)?;
432                    let op2 = source.gread_with::<Operand>(offset, endian)?;
433                    Op::Mov(op1, op2)
434                } else {
435                    return Err(Error::OperandMismatch);
436                }
437            }
438            "movsx" => {
439                if operands_count == 2 {
440                    let op1 = source.gread_with::<Operand>(offset, endian)?;
441                    let op2 = source.gread_with::<Operand>(offset, endian)?;
442                    Op::Movsx(op1, op2)
443                } else {
444                    return Err(Error::OperandMismatch);
445                }
446            }
447            "str" => {
448                if operands_count == 3 {
449                    let op1 = source.gread_with::<Operand>(offset, endian)?;
450                    let op2 = source.gread_with::<Operand>(offset, endian)?;
451                    let op3 = source.gread_with::<Operand>(offset, endian)?;
452                    Op::Str(op1, op2, op3)
453                } else {
454                    return Err(Error::OperandMismatch);
455                }
456            }
457            "ldd" => {
458                if operands_count == 3 {
459                    let op1 = source.gread_with::<Operand>(offset, endian)?;
460                    let op2 = source.gread_with::<Operand>(offset, endian)?;
461                    let op3 = source.gread_with::<Operand>(offset, endian)?;
462                    Op::Ldd(op1, op2, op3)
463                } else {
464                    return Err(Error::OperandMismatch);
465                }
466            }
467            "neg" => {
468                if operands_count == 1 {
469                    let op1 = source.gread_with::<Operand>(offset, endian)?;
470                    Op::Neg(op1)
471                } else {
472                    return Err(Error::OperandMismatch);
473                }
474            }
475            "add" => {
476                if operands_count == 2 {
477                    let op1 = source.gread_with::<Operand>(offset, endian)?;
478                    let op2 = source.gread_with::<Operand>(offset, endian)?;
479                    Op::Add(op1, op2)
480                } else {
481                    return Err(Error::OperandMismatch);
482                }
483            }
484            "sub" => {
485                if operands_count == 2 {
486                    let op1 = source.gread_with::<Operand>(offset, endian)?;
487                    let op2 = source.gread_with::<Operand>(offset, endian)?;
488                    Op::Sub(op1, op2)
489                } else {
490                    return Err(Error::OperandMismatch);
491                }
492            }
493            "mul" => {
494                if operands_count == 2 {
495                    let op1 = source.gread_with::<Operand>(offset, endian)?;
496                    let op2 = source.gread_with::<Operand>(offset, endian)?;
497                    Op::Mul(op1, op2)
498                } else {
499                    return Err(Error::OperandMismatch);
500                }
501            }
502            "mulhi" => {
503                if operands_count == 2 {
504                    let op1 = source.gread_with::<Operand>(offset, endian)?;
505                    let op2 = source.gread_with::<Operand>(offset, endian)?;
506                    Op::Mulhi(op1, op2)
507                } else {
508                    return Err(Error::OperandMismatch);
509                }
510            }
511            "imul" => {
512                if operands_count == 2 {
513                    let op1 = source.gread_with::<Operand>(offset, endian)?;
514                    let op2 = source.gread_with::<Operand>(offset, endian)?;
515                    Op::Imul(op1, op2)
516                } else {
517                    return Err(Error::OperandMismatch);
518                }
519            }
520            "imulhi" => {
521                if operands_count == 2 {
522                    let op1 = source.gread_with::<Operand>(offset, endian)?;
523                    let op2 = source.gread_with::<Operand>(offset, endian)?;
524                    Op::Imulhi(op1, op2)
525                } else {
526                    return Err(Error::OperandMismatch);
527                }
528            }
529            "div" => {
530                if operands_count == 3 {
531                    let op1 = source.gread_with::<Operand>(offset, endian)?;
532                    let op2 = source.gread_with::<Operand>(offset, endian)?;
533                    let op3 = source.gread_with::<Operand>(offset, endian)?;
534                    Op::Div(op1, op2, op3)
535                } else {
536                    return Err(Error::OperandMismatch);
537                }
538            }
539            "rem" => {
540                if operands_count == 3 {
541                    let op1 = source.gread_with::<Operand>(offset, endian)?;
542                    let op2 = source.gread_with::<Operand>(offset, endian)?;
543                    let op3 = source.gread_with::<Operand>(offset, endian)?;
544                    Op::Rem(op1, op2, op3)
545                } else {
546                    return Err(Error::OperandMismatch);
547                }
548            }
549            "idiv" => {
550                if operands_count == 3 {
551                    let op1 = source.gread_with::<Operand>(offset, endian)?;
552                    let op2 = source.gread_with::<Operand>(offset, endian)?;
553                    let op3 = source.gread_with::<Operand>(offset, endian)?;
554                    Op::Idiv(op1, op2, op3)
555                } else {
556                    return Err(Error::OperandMismatch);
557                }
558            }
559            "irem" => {
560                if operands_count == 3 {
561                    let op1 = source.gread_with::<Operand>(offset, endian)?;
562                    let op2 = source.gread_with::<Operand>(offset, endian)?;
563                    let op3 = source.gread_with::<Operand>(offset, endian)?;
564                    Op::Irem(op1, op2, op3)
565                } else {
566                    return Err(Error::OperandMismatch);
567                }
568            }
569            "popcnt" => {
570                if operands_count == 1 {
571                    let op1 = source.gread_with::<Operand>(offset, endian)?;
572                    Op::Popcnt(op1)
573                } else {
574                    return Err(Error::OperandMismatch);
575                }
576            }
577            "bsf" => {
578                if operands_count == 1 {
579                    let op1 = source.gread_with::<Operand>(offset, endian)?;
580                    Op::Bsf(op1)
581                } else {
582                    return Err(Error::OperandMismatch);
583                }
584            }
585            "bsr" => {
586                if operands_count == 1 {
587                    let op1 = source.gread_with::<Operand>(offset, endian)?;
588                    Op::Bsr(op1)
589                } else {
590                    return Err(Error::OperandMismatch);
591                }
592            }
593            "not" => {
594                if operands_count == 1 {
595                    let op1 = source.gread_with::<Operand>(offset, endian)?;
596                    Op::Not(op1)
597                } else {
598                    return Err(Error::OperandMismatch);
599                }
600            }
601            "shr" => {
602                if operands_count == 2 {
603                    let op1 = source.gread_with::<Operand>(offset, endian)?;
604                    let op2 = source.gread_with::<Operand>(offset, endian)?;
605                    Op::Shr(op1, op2)
606                } else {
607                    return Err(Error::OperandMismatch);
608                }
609            }
610            "shl" => {
611                if operands_count == 2 {
612                    let op1 = source.gread_with::<Operand>(offset, endian)?;
613                    let op2 = source.gread_with::<Operand>(offset, endian)?;
614                    Op::Shl(op1, op2)
615                } else {
616                    return Err(Error::OperandMismatch);
617                }
618            }
619            "xor" => {
620                if operands_count == 2 {
621                    let op1 = source.gread_with::<Operand>(offset, endian)?;
622                    let op2 = source.gread_with::<Operand>(offset, endian)?;
623                    Op::Xor(op1, op2)
624                } else {
625                    return Err(Error::OperandMismatch);
626                }
627            }
628            "or" => {
629                if operands_count == 2 {
630                    let op1 = source.gread_with::<Operand>(offset, endian)?;
631                    let op2 = source.gread_with::<Operand>(offset, endian)?;
632                    Op::Or(op1, op2)
633                } else {
634                    return Err(Error::OperandMismatch);
635                }
636            }
637            "and" => {
638                if operands_count == 2 {
639                    let op1 = source.gread_with::<Operand>(offset, endian)?;
640                    let op2 = source.gread_with::<Operand>(offset, endian)?;
641                    Op::And(op1, op2)
642                } else {
643                    return Err(Error::OperandMismatch);
644                }
645            }
646            "ror" => {
647                if operands_count == 2 {
648                    let op1 = source.gread_with::<Operand>(offset, endian)?;
649                    let op2 = source.gread_with::<Operand>(offset, endian)?;
650                    Op::Ror(op1, op2)
651                } else {
652                    return Err(Error::OperandMismatch);
653                }
654            }
655            "rol" => {
656                if operands_count == 2 {
657                    let op1 = source.gread_with::<Operand>(offset, endian)?;
658                    let op2 = source.gread_with::<Operand>(offset, endian)?;
659                    Op::Rol(op1, op2)
660                } else {
661                    return Err(Error::OperandMismatch);
662                }
663            }
664            "tg" => {
665                if operands_count == 3 {
666                    let op1 = source.gread_with::<Operand>(offset, endian)?;
667                    let op2 = source.gread_with::<Operand>(offset, endian)?;
668                    let op3 = source.gread_with::<Operand>(offset, endian)?;
669                    Op::Tg(op1, op2, op3)
670                } else {
671                    return Err(Error::OperandMismatch);
672                }
673            }
674            "tge" => {
675                if operands_count == 3 {
676                    let op1 = source.gread_with::<Operand>(offset, endian)?;
677                    let op2 = source.gread_with::<Operand>(offset, endian)?;
678                    let op3 = source.gread_with::<Operand>(offset, endian)?;
679                    Op::Tge(op1, op2, op3)
680                } else {
681                    return Err(Error::OperandMismatch);
682                }
683            }
684            "te" => {
685                if operands_count == 3 {
686                    let op1 = source.gread_with::<Operand>(offset, endian)?;
687                    let op2 = source.gread_with::<Operand>(offset, endian)?;
688                    let op3 = source.gread_with::<Operand>(offset, endian)?;
689                    Op::Te(op1, op2, op3)
690                } else {
691                    return Err(Error::OperandMismatch);
692                }
693            }
694            "tne" => {
695                if operands_count == 3 {
696                    let op1 = source.gread_with::<Operand>(offset, endian)?;
697                    let op2 = source.gread_with::<Operand>(offset, endian)?;
698                    let op3 = source.gread_with::<Operand>(offset, endian)?;
699                    Op::Tne(op1, op2, op3)
700                } else {
701                    return Err(Error::OperandMismatch);
702                }
703            }
704            "tl" => {
705                if operands_count == 3 {
706                    let op1 = source.gread_with::<Operand>(offset, endian)?;
707                    let op2 = source.gread_with::<Operand>(offset, endian)?;
708                    let op3 = source.gread_with::<Operand>(offset, endian)?;
709                    Op::Tl(op1, op2, op3)
710                } else {
711                    return Err(Error::OperandMismatch);
712                }
713            }
714            "tle" => {
715                if operands_count == 3 {
716                    let op1 = source.gread_with::<Operand>(offset, endian)?;
717                    let op2 = source.gread_with::<Operand>(offset, endian)?;
718                    let op3 = source.gread_with::<Operand>(offset, endian)?;
719                    Op::Tle(op1, op2, op3)
720                } else {
721                    return Err(Error::OperandMismatch);
722                }
723            }
724            "tug" => {
725                if operands_count == 3 {
726                    let op1 = source.gread_with::<Operand>(offset, endian)?;
727                    let op2 = source.gread_with::<Operand>(offset, endian)?;
728                    let op3 = source.gread_with::<Operand>(offset, endian)?;
729                    Op::Tug(op1, op2, op3)
730                } else {
731                    return Err(Error::OperandMismatch);
732                }
733            }
734            "tuge" => {
735                if operands_count == 3 {
736                    let op1 = source.gread_with::<Operand>(offset, endian)?;
737                    let op2 = source.gread_with::<Operand>(offset, endian)?;
738                    let op3 = source.gread_with::<Operand>(offset, endian)?;
739                    Op::Tuge(op1, op2, op3)
740                } else {
741                    return Err(Error::OperandMismatch);
742                }
743            }
744            "tul" => {
745                if operands_count == 3 {
746                    let op1 = source.gread_with::<Operand>(offset, endian)?;
747                    let op2 = source.gread_with::<Operand>(offset, endian)?;
748                    let op3 = source.gread_with::<Operand>(offset, endian)?;
749                    Op::Tul(op1, op2, op3)
750                } else {
751                    return Err(Error::OperandMismatch);
752                }
753            }
754            "tule" => {
755                if operands_count == 3 {
756                    let op1 = source.gread_with::<Operand>(offset, endian)?;
757                    let op2 = source.gread_with::<Operand>(offset, endian)?;
758                    let op3 = source.gread_with::<Operand>(offset, endian)?;
759                    Op::Tule(op1, op2, op3)
760                } else {
761                    return Err(Error::OperandMismatch);
762                }
763            }
764            "ifs" => {
765                if operands_count == 3 {
766                    let op1 = source.gread_with::<Operand>(offset, endian)?;
767                    let op2 = source.gread_with::<Operand>(offset, endian)?;
768                    let op3 = source.gread_with::<Operand>(offset, endian)?;
769                    Op::Ifs(op1, op2, op3)
770                } else {
771                    return Err(Error::OperandMismatch);
772                }
773            }
774            "js" => {
775                if operands_count == 3 {
776                    let op1 = source.gread_with::<Operand>(offset, endian)?;
777                    let op2 = source.gread_with::<Operand>(offset, endian)?;
778                    let op3 = source.gread_with::<Operand>(offset, endian)?;
779                    Op::Js(op1, op2, op3)
780                } else {
781                    return Err(Error::OperandMismatch);
782                }
783            }
784            "jmp" => {
785                if operands_count == 1 {
786                    let op1 = source.gread_with::<Operand>(offset, endian)?;
787                    Op::Jmp(op1)
788                } else {
789                    return Err(Error::OperandMismatch);
790                }
791            }
792            "vexit" => {
793                if operands_count == 1 {
794                    let op1 = source.gread_with::<Operand>(offset, endian)?;
795                    Op::Vexit(op1)
796                } else {
797                    return Err(Error::OperandMismatch);
798                }
799            }
800            "vxcall" => {
801                if operands_count == 1 {
802                    let op1 = source.gread_with::<Operand>(offset, endian)?;
803                    Op::Vxcall(op1)
804                } else {
805                    return Err(Error::OperandMismatch);
806                }
807            }
808            "nop" => {
809                if operands_count == 0 {
810                    Op::Nop
811                } else {
812                    return Err(Error::OperandMismatch);
813                }
814            }
815            "sfence" => {
816                if operands_count == 0 {
817                    Op::Sfence
818                } else {
819                    return Err(Error::OperandMismatch);
820                }
821            }
822            "lfence" => {
823                if operands_count == 0 {
824                    Op::Lfence
825                } else {
826                    return Err(Error::OperandMismatch);
827                }
828            }
829            "vemit" => {
830                if operands_count == 1 {
831                    let op1 = source.gread_with::<Operand>(offset, endian)?;
832                    Op::Vemit(op1)
833                } else {
834                    return Err(Error::OperandMismatch);
835                }
836            }
837            "vpinr" => {
838                if operands_count == 1 {
839                    let op1 = source.gread_with::<Operand>(offset, endian)?;
840                    Op::Vpinr(op1)
841                } else {
842                    return Err(Error::OperandMismatch);
843                }
844            }
845            "vpinw" => {
846                if operands_count == 1 {
847                    let op1 = source.gread_with::<Operand>(offset, endian)?;
848                    Op::Vpinw(op1)
849                } else {
850                    return Err(Error::OperandMismatch);
851                }
852            }
853            "vpinrm" => {
854                if operands_count == 3 {
855                    let op1 = source.gread_with::<Operand>(offset, endian)?;
856                    let op2 = source.gread_with::<Operand>(offset, endian)?;
857                    let op3 = source.gread_with::<Operand>(offset, endian)?;
858                    Op::Vpinrm(op1, op2, op3)
859                } else {
860                    return Err(Error::OperandMismatch);
861                }
862            }
863            "vpinwm" => {
864                if operands_count == 3 {
865                    let op1 = source.gread_with::<Operand>(offset, endian)?;
866                    let op2 = source.gread_with::<Operand>(offset, endian)?;
867                    let op3 = source.gread_with::<Operand>(offset, endian)?;
868                    Op::Vpinwm(op1, op2, op3)
869                } else {
870                    return Err(Error::OperandMismatch);
871                }
872            }
873            _ => return Err(Error::Malformed(format!("Invalid operation: {}", name))),
874        };
875        assert_eq!(Op::size_with(&op), *offset);
876        Ok((op, *offset))
877    }
878}
879
880impl ctx::TryIntoCtx<Endian> for Op {
881    type Error = Error;
882
883    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
884        let offset = &mut 0;
885
886        let name = self.name();
887
888        sink.gwrite::<u32>(name.len().try_into()?, offset)?;
889        sink.gwrite::<&[u8]>(name.as_bytes(), offset)?;
890
891        sink.gwrite::<u32>(self.operands().len().try_into()?, offset)?;
892        for operand in self.operands() {
893            sink.gwrite::<Operand>(*operand, offset)?;
894        }
895
896        Ok(*offset)
897    }
898}
899
900impl ctx::SizeWith<Instruction> for Instruction {
901    fn size_with(instr: &Instruction) -> usize {
902        let mut size = 0;
903        size += Op::size_with(&instr.op);
904        size += Vip::size_with(&instr.vip);
905        size += size_of::<i64>();
906        size += size_of::<u32>();
907        size += size_of::<u8>();
908        size
909    }
910}
911
912impl ctx::TryFromCtx<'_, Endian> for Instruction {
913    type Error = Error;
914
915    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
916        let offset = &mut 0;
917
918        let op = source.gread_with::<Op>(offset, endian)?;
919        let vip = source.gread_with::<Vip>(offset, endian)?;
920        let sp_offset = source.gread_with::<i64>(offset, endian)?;
921        let sp_index = source.gread_with::<u32>(offset, endian)?;
922        let sp_reset = source.gread::<u8>(offset)? != 0;
923
924        let instr = Instruction {
925            op,
926            vip,
927            sp_offset,
928            sp_index,
929            sp_reset,
930        };
931        assert_eq!(Instruction::size_with(&instr), *offset);
932        Ok((instr, *offset))
933    }
934}
935
936impl ctx::TryIntoCtx<Endian> for Instruction {
937    type Error = Error;
938
939    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
940        let offset = &mut 0;
941
942        sink.gwrite::<Op>(self.op, offset)?;
943        sink.gwrite::<Vip>(self.vip, offset)?;
944        sink.gwrite::<i64>(self.sp_offset, offset)?;
945        sink.gwrite::<u32>(self.sp_index, offset)?;
946        sink.gwrite::<u8>(self.sp_reset.into(), offset)?;
947
948        Ok(*offset)
949    }
950}
951
952impl ctx::SizeWith<BasicBlock> for BasicBlock {
953    fn size_with(basic_block: &BasicBlock) -> usize {
954        let mut size = 0;
955
956        size += Vip::size_with(&basic_block.vip);
957        size += size_of::<i64>();
958        size += size_of::<u32>();
959        size += size_of::<u32>();
960
961        size += size_of::<u32>();
962        for instr in &basic_block.instructions {
963            size += Instruction::size_with(instr);
964        }
965
966        size += size_of::<u32>();
967        for prev_vip in &basic_block.prev_vip {
968            size += Vip::size_with(prev_vip);
969        }
970
971        size += size_of::<u32>();
972        for next_vip in &basic_block.next_vip {
973            size += Vip::size_with(next_vip);
974        }
975
976        size
977    }
978}
979
980impl ctx::TryFromCtx<'_, Endian> for BasicBlock {
981    type Error = Error;
982
983    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
984        let offset = &mut 0;
985
986        let vip = Vip(source.gread_with::<u64>(offset, endian)?);
987        let sp_offset = source.gread_with::<i64>(offset, endian)?;
988        let sp_index = source.gread_with::<u32>(offset, endian)?;
989        let last_temporary_index = source.gread_with::<u32>(offset, endian)?;
990
991        let instruction_count = source.gread_with::<u32>(offset, endian)?;
992        let mut instructions = Vec::<Instruction>::with_capacity(instruction_count as usize);
993        for _ in 0..instruction_count {
994            instructions.push(source.gread_with(offset, endian)?);
995        }
996
997        let prev_vip_count = source.gread_with::<u32>(offset, endian)?;
998        let mut prev_vip = Vec::<Vip>::with_capacity(prev_vip_count as usize);
999        for _ in 0..prev_vip_count {
1000            prev_vip.push(Vip(source.gread_with(offset, endian)?));
1001        }
1002
1003        let next_vip_count = source.gread_with::<u32>(offset, endian)?;
1004        let mut next_vip = Vec::<Vip>::with_capacity(next_vip_count as usize);
1005        for _ in 0..next_vip_count {
1006            next_vip.push(Vip(source.gread_with(offset, endian)?));
1007        }
1008
1009        let basic_block = BasicBlock {
1010            vip,
1011            sp_offset,
1012            sp_index,
1013            last_temporary_index,
1014            instructions,
1015            prev_vip,
1016            next_vip,
1017        };
1018        assert_eq!(BasicBlock::size_with(&basic_block), *offset);
1019        Ok((basic_block, *offset))
1020    }
1021}
1022
1023impl ctx::TryIntoCtx<Endian> for BasicBlock {
1024    type Error = Error;
1025
1026    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
1027        let offset = &mut 0;
1028
1029        sink.gwrite::<Vip>(self.vip, offset)?;
1030        sink.gwrite::<i64>(self.sp_offset, offset)?;
1031        sink.gwrite::<u32>(self.sp_index, offset)?;
1032        sink.gwrite::<u32>(self.last_temporary_index, offset)?;
1033
1034        sink.gwrite::<u32>(self.instructions.len().try_into()?, offset)?;
1035        for instr in self.instructions {
1036            sink.gwrite::<Instruction>(instr, offset)?;
1037        }
1038
1039        sink.gwrite::<u32>(self.prev_vip.len().try_into()?, offset)?;
1040        for vip in self.prev_vip {
1041            sink.gwrite::<Vip>(vip, offset)?;
1042        }
1043
1044        sink.gwrite::<u32>(self.next_vip.len().try_into()?, offset)?;
1045        for vip in self.next_vip {
1046            sink.gwrite::<Vip>(vip, offset)?;
1047        }
1048
1049        Ok(*offset)
1050    }
1051}
1052
1053impl ctx::SizeWith<Routine> for Routine {
1054    fn size_with(routine: &Routine) -> usize {
1055        let mut size = 0;
1056        size += Header::size_with(&routine.header);
1057        size += Vip::size_with(&routine.vip);
1058        size += RoutineConvention::size_with(&routine.routine_convention);
1059        size += SubroutineConvention::size_with(&routine.subroutine_convention);
1060
1061        size += size_of::<u32>();
1062        for convention in &routine.spec_subroutine_conventions {
1063            size += SubroutineConvention::size_with(convention);
1064        }
1065
1066        size += size_of::<u32>();
1067        for basic_block in routine.explored_blocks.values() {
1068            size += BasicBlock::size_with(basic_block);
1069        }
1070        size
1071    }
1072}
1073
1074impl ctx::TryFromCtx<'_, Endian> for Routine {
1075    type Error = Error;
1076
1077    fn try_from_ctx(source: &[u8], endian: Endian) -> Result<(Self, usize)> {
1078        let offset = &mut 0;
1079
1080        let header = source.gread_with::<Header>(offset, endian)?;
1081        let vip = source.gread_with::<Vip>(offset, endian)?;
1082        let routine_convention = source.gread_with::<RoutineConvention>(offset, endian)?;
1083        let subroutine_convention = source.gread_with::<SubroutineConvention>(offset, endian)?;
1084
1085        let spec_subroutine_conventions_count = source.gread_with::<u32>(offset, endian)?;
1086        let mut spec_subroutine_conventions =
1087            Vec::<SubroutineConvention>::with_capacity(spec_subroutine_conventions_count as usize);
1088        for _ in 0..spec_subroutine_conventions_count {
1089            spec_subroutine_conventions.push(source.gread_with(offset, endian)?);
1090        }
1091
1092        let explored_blocks_count = source.gread_with::<u32>(offset, endian)?;
1093        let mut explored_blocks = IndexMap::new();
1094        for _ in 0..explored_blocks_count {
1095            let basic_block = source.gread_with::<BasicBlock>(offset, endian)?;
1096            explored_blocks.insert(basic_block.vip, basic_block);
1097        }
1098
1099        let routine = Routine {
1100            header,
1101            vip,
1102            routine_convention,
1103            subroutine_convention,
1104            spec_subroutine_conventions,
1105            explored_blocks,
1106        };
1107        assert_eq!(Routine::size_with(&routine), *offset);
1108        Ok((routine, *offset))
1109    }
1110}
1111
1112impl ctx::TryIntoCtx<Endian> for Routine {
1113    type Error = Error;
1114
1115    fn try_into_ctx(self, sink: &mut [u8], _endian: Endian) -> Result<usize> {
1116        let offset = &mut 0;
1117
1118        sink.gwrite::<Header>(self.header, offset)?;
1119        sink.gwrite::<Vip>(self.vip, offset)?;
1120        sink.gwrite::<RoutineConvention>(self.routine_convention, offset)?;
1121        sink.gwrite::<SubroutineConvention>(self.subroutine_convention, offset)?;
1122
1123        sink.gwrite::<u32>(self.spec_subroutine_conventions.len().try_into()?, offset)?;
1124        for convention in self.spec_subroutine_conventions {
1125            sink.gwrite::<SubroutineConvention>(convention, offset)?;
1126        }
1127
1128        sink.gwrite::<u32>(self.explored_blocks.len().try_into()?, offset)?;
1129        for (_, basic_block) in self.explored_blocks.into_iter() {
1130            sink.gwrite::<BasicBlock>(basic_block, offset)?;
1131        }
1132
1133        Ok(*offset)
1134    }
1135}
1136
1137#[cfg(test)]
1138mod test {
1139    use super::Result;
1140
1141    #[test]
1142    fn round_trip() -> Result<()> {
1143        use crate::Routine;
1144        let data = std::fs::read("resources/big.vtil")?;
1145        let routine = Routine::from_vec(&data)?;
1146        let rounded_data = routine.into_bytes()?;
1147        assert_eq!(data, rounded_data);
1148        Ok(())
1149    }
1150}