diff --git a/fugue-core/src/lifter.rs b/fugue-core/src/lifter.rs index 23fc586..1ff020d 100644 --- a/fugue-core/src/lifter.rs +++ b/fugue-core/src/lifter.rs @@ -1,59 +1,30 @@ use std::cell::{Cell, Ref, RefCell}; -use std::mem; use fugue_ir::disassembly::lift::ArenaVec; -use fugue_ir::disassembly::{ContextDatabase, IRBuilderArena, PCodeData, ParserContext}; +use fugue_ir::disassembly::PCodeRaw; +use fugue_ir::disassembly::{ContextDatabase, IRBuilderArena, PCodeData}; use fugue_ir::error::Error; +use fugue_ir::il::instruction::Instruction; +use fugue_ir::translator::TranslationContext; use fugue_ir::{Address, Translator}; -use ouroboros::self_referencing; - use crate::ir::{Insn, PCode}; -#[self_referencing] -struct LifterInner<'a> { - translator: &'a Translator, - irb: IRBuilderArena, - ctx: ContextDatabase, - #[borrows(irb)] - #[covariant] - pctx: ParserContext<'a, 'this>, -} - +#[derive(Clone)] #[repr(transparent)] -pub struct Lifter<'a>(LifterInner<'a>); - -impl<'a> Clone for Lifter<'a> { - fn clone(&self) -> Self { - // we recreate based on the current context database - let translator = *self.0.borrow_translator(); - let ctx = self.0.borrow_ctx().clone(); - - Self::new_with(translator, ctx) - } - - fn clone_from(&mut self, source: &Self) { - // we only need to copy the context database - let sctx = source.0.borrow_ctx().clone(); - self.0.with_ctx_mut(|ctx| *ctx = sctx); - } -} +pub struct Lifter<'a>(TranslationContext<'a>); impl<'a> Lifter<'a> { pub fn new(translator: &'a Translator) -> Self { - Self::new_with(translator, translator.context_database()) + Self(TranslationContext::new(translator)) } pub fn new_with(translator: &'a Translator, ctx: ContextDatabase) -> Self { - let irb = IRBuilderArena::with_capacity(4096); - - Self(LifterInner::new(translator, irb, ctx, |irb| { - ParserContext::empty(irb, translator.manager()) - })) + Self(TranslationContext::new_with(translator, ctx)) } pub fn irb(&self, size: usize) -> IRBuilderArena { - IRBuilderArena::with_capacity(size) + self.0.irb(size) } pub fn disassemble<'z>( @@ -62,39 +33,23 @@ impl<'a> Lifter<'a> { address: impl Into
, bytes: impl AsRef<[u8]>, ) -> Result, Error> { + let address = address.into(); let bytes = bytes.as_ref(); - self.0.with_mut(|slf| { - let address = address.into(); - let address_val = slf.translator.address(address.into()); - - let (mnemonic, operand_str, delay_slots, length) = slf.translator.disassemble_aux( - slf.ctx, - slf.pctx, - slf.irb, - address_val, - bytes, - |fmt, delay_slots, length| -> Result<_, Error> { - let mnemonic = fmt.mnemonic_str(irb); - let operand_str = fmt.operands_str(irb); - - Ok((mnemonic, operand_str, delay_slots, length)) - }, - )?; - - if length as usize > bytes.len() { - return Err(Error::Disassembly( - fugue_ir::disassembly::Error::InstructionResolution, - )); - } - - Ok(Insn { - address, - mnemonic, - operands: operand_str, - delay_slots: delay_slots as u8, - length: length as u8, - }) + let Instruction { + mnemonic, + operands, + delay_slots, + length, + .. + } = self.0.disassemble(irb, address, bytes)?; + + Ok(Insn { + address, + mnemonic, + operands, + delay_slots: delay_slots as u8, + length: length as u8, }) } @@ -104,49 +59,34 @@ impl<'a> Lifter<'a> { address: impl Into
, bytes: impl AsRef<[u8]>, ) -> Result, Error> { + let address = address.into(); let bytes = bytes.as_ref(); - self.0.with_mut(|slf| { - let address = address.into(); - let address_val = slf.translator.address(address.into()); - let mut irbb = irb.builder(&slf.translator); - - let pcode_raw = slf.translator.lift_with( - slf.ctx, - slf.pctx, - slf.irb, - &mut irbb, - address_val, - bytes, - )?; - - if pcode_raw.length as usize > bytes.len() { - return Err(Error::Disassembly( - fugue_ir::disassembly::Error::InstructionResolution, - )); - } - - Ok(PCode { - address, - operations: pcode_raw.operations, - delay_slots: pcode_raw.delay_slots as u8, - length: pcode_raw.length as u8, - }) + let PCodeRaw { + operations, + delay_slots, + length, + .. + } = self.0.lift(irb, address, bytes)?; + + Ok(PCode { + address, + operations, + delay_slots: delay_slots as u8, + length: length as u8, }) } pub fn translator(&self) -> &Translator { - self.0.borrow_translator() + self.0.translator() } - pub fn reset(&mut self) { - let translator = *self.0.borrow_translator(); - let mut ctx = translator.context_database(); - - // we preserve the old context database - self.0.with_ctx_mut(|old_ctx| mem::swap(old_ctx, &mut ctx)); + pub fn context(&self) -> &ContextDatabase { + self.0.context() + } - *self = Self::new_with(translator, ctx); + pub fn reset(&mut self) { + self.0.reset(); } } diff --git a/fugue-ir/Cargo.toml b/fugue-ir/Cargo.toml index c3810d6..c91535f 100644 --- a/fugue-ir/Cargo.toml +++ b/fugue-ir/Cargo.toml @@ -16,9 +16,11 @@ fugue-bytes = { path = "../fugue-bytes", version = "0.3" } iset = { version = "0.2", features = ["serde"] } itertools = "0.12" log = "0.4" +ouroboros = "0.18" roxmltree = "0.19" serde = { version = "1", features = ["derive", "rc"] } smallvec = { version = "1", features = ["serde"] } +stack-map = "1.0" thiserror = "1" ustr = { version = "1.0", features = ["serde"] } walkdir = "2" diff --git a/fugue-ir/src/disassembly/lift.rs b/fugue-ir/src/disassembly/lift.rs index f59281e..3021d86 100644 --- a/fugue-ir/src/disassembly/lift.rs +++ b/fugue-ir/src/disassembly/lift.rs @@ -4,6 +4,7 @@ use std::ops::Deref; use std::sync::Arc; use ahash::AHashMap as Map; +use stack_map::StackMap; use ustr::Ustr; use crate::address::AddressValue; @@ -16,7 +17,7 @@ use crate::disassembly::{Error, ParserContext, ParserWalker}; use crate::float_format::FloatFormat; use crate::space::AddressSpace; use crate::space_manager::SpaceManager; -use crate::Translator; +use crate::translator::{Translator, MAX_DELAY_SLOTS}; pub use bumpalo::collections::String as ArenaString; pub use bumpalo::collections::Vec as ArenaVec; @@ -349,14 +350,6 @@ impl IRBuilderArena { &self.0 } - pub fn builder<'b, 'z>(&'z self, translator: &'b Translator) -> IRBuilderBase<'b, 'z> { - IRBuilderBase::empty( - &self, - translator.manager(), - translator.unique_mask(), - ) - } - pub fn boxed<'z, T>(&'z self, val: T) -> ArenaRef<'z, T> { ArenaRef::new_in(self, val) } @@ -382,34 +375,31 @@ impl Deref for IRBuilderArena { } } -pub struct IRBuilderBase<'b, 'z> { +pub struct IRBuilderBase<'b, 'cz> { const_space: &'b AddressSpace, unique_mask: u64, - alloc: &'z IRBuilderArena, - label_base: usize, label_count: usize, - label_refs: ArenaVec<'z, RelativeRecord>, - labels: ArenaVec<'z, u64>, + label_refs: ArenaVec<'cz, RelativeRecord>, + labels: ArenaVec<'cz, u64>, manager: &'b SpaceManager, } -impl<'b, 'z> IRBuilderBase<'b, 'z> { +impl<'b, 'cz> IRBuilderBase<'b, 'cz> { pub fn empty( - alloc: &'z IRBuilderArena, + alloc_inner: &'cz IRBuilderArena, manager: &'b SpaceManager, unique_mask: u64, ) -> Self { Self { const_space: manager.constant_space_ref(), unique_mask, - alloc, label_base: 0, label_count: 0, - labels: ArenaVec::with_capacity_in(16, alloc.inner()), - label_refs: ArenaVec::with_capacity_in(16, alloc.inner()), + labels: ArenaVec::with_capacity_in(16, alloc_inner.inner()), + label_refs: ArenaVec::with_capacity_in(16, alloc_inner.inner()), manager, } } @@ -421,6 +411,7 @@ impl<'b, 'z> IRBuilderBase<'b, 'z> { self.label_refs.clear(); } + /* pub(crate) fn arena(&self) -> &'z Arena { self.alloc } @@ -432,13 +423,15 @@ impl<'b, 'z> IRBuilderBase<'b, 'z> { pub fn alloc_vec(&self) -> ArenaVec<'z, T> { ArenaVec::new_in(self.alloc) } + */ } pub struct IRBuilder<'b, 'c, 'cz, 'z> { - base: &'c mut IRBuilderBase<'b, 'z>, + base: &'c mut IRBuilderBase<'b, 'cz>, + arena: &'z IRBuilderArena, unique_offset: u64, issued: ArenaVec<'z, PCodeData<'z>>, - delay_contexts: Map>, + delay_contexts: StackMap, MAX_DELAY_SLOTS>, walker: ParserWalker<'b, 'c, 'cz>, } @@ -466,7 +459,7 @@ pub struct IRBuilder<'b, 'c> { */ impl<'b, 'c, 'cz, 'z> Deref for IRBuilder<'b, 'c, 'cz, 'z> { - type Target = &'c mut IRBuilderBase<'b, 'z>; + type Target = &'c mut IRBuilderBase<'b, 'cz>; fn deref(&self) -> &Self::Target { &self.base @@ -475,20 +468,19 @@ impl<'b, 'c, 'cz, 'z> Deref for IRBuilder<'b, 'c, 'cz, 'z> { impl<'b, 'c, 'cz, 'z> IRBuilder<'b, 'c, 'cz, 'z> { pub fn new( - base: &'c mut IRBuilderBase<'b, 'z>, + base: &'c mut IRBuilderBase<'b, 'cz>, + arena: &'z IRBuilderArena, walker: ParserWalker<'b, 'c, 'cz>, - delay_contexts: &'c mut Map>, + delay_contexts: StackMap, MAX_DELAY_SLOTS>, ) -> Self { base.reinitialise(); Self { unique_offset: (walker.address().offset() & base.unique_mask) >> 4, - issued: ArenaVec::with_capacity_in(16, &base.alloc), + issued: ArenaVec::with_capacity_in(16, arena), base, + arena, walker, - delay_contexts: delay_contexts - .iter_mut() - .map(|(k, v)| (k.clone(), v)) - .collect(), + delay_contexts, } } @@ -686,7 +678,7 @@ impl<'b, 'c, 'cz, 'z> IRBuilder<'b, 'c, 'cz, 'z> { pub fn dump(&mut self, op: &'b OpTpl) -> Result<(), Error> { let input_count = op.input_count(); - let mut pcode = PCodeData::new_in(&self.base.arena(), op.opcode(), input_count); + let mut pcode = PCodeData::new_in(self.arena, op.opcode(), input_count); for i in 0..input_count { let input = op.input(i); @@ -696,7 +688,7 @@ impl<'b, 'c, 'cz, 'z> IRBuilder<'b, 'c, 'cz, 'z> { let index = VarnodeData::new(self.const_space, spc.index() as u64, 0); self.issued.push(PCodeData { opcode: Opcode::Load, - inputs: arena_vec![in self.base.alloc; index, ptr], + inputs: arena_vec![in self.arena; index, ptr], output: Some(varnode.clone()), }); pcode.inputs.push(varnode); @@ -720,7 +712,7 @@ impl<'b, 'c, 'cz, 'z> IRBuilder<'b, 'c, 'cz, 'z> { let index = VarnodeData::new(self.const_space, spc.index() as u64, 0); self.issued.push(PCodeData { opcode: Opcode::Store, - inputs: arena_vec![in self.base.alloc; index, ptr, outp], + inputs: arena_vec![in self.arena; index, ptr, outp], output: None, }) } @@ -782,8 +774,8 @@ impl<'b, 'c, 'cz, 'z> IRBuilder<'b, 'c, 'cz, 'z> { ))); } Some(label) => { - let res = - label.wrapping_sub(rel.instruction as u64) & bits::calculate_mask(varnode.size()); + let res = label.wrapping_sub(rel.instruction as u64) + & bits::calculate_mask(varnode.size()); varnode.offset = res; } } @@ -796,7 +788,7 @@ impl<'b, 'c, 'cz, 'z> IRBuilder<'b, 'c, 'cz, 'z> { let mut slf = self; slf.walker.base_state(); - let mut operations = ArenaVec::new_in(&slf.base.alloc); + let mut operations = ArenaVec::new_in(slf.arena); swap(&mut slf.issued, &mut operations); PCodeRaw { diff --git a/fugue-ir/src/disassembly/walker.rs b/fugue-ir/src/disassembly/walker.rs index 79310fa..281f003 100644 --- a/fugue-ir/src/disassembly/walker.rs +++ b/fugue-ir/src/disassembly/walker.rs @@ -225,7 +225,6 @@ impl<'b, 'z> ParserContext<'b, 'z> { pub fn reinitialise( &mut self, - _arena: &'z IRBuilderArena, context_db: &ContextDatabase, address: AddressValue, buffer: &[u8], diff --git a/fugue-ir/src/translator.rs b/fugue-ir/src/translator.rs index 096fb79..f99a1de 100644 --- a/fugue-ir/src/translator.rs +++ b/fugue-ir/src/translator.rs @@ -1,6 +1,8 @@ +use std::array; use std::borrow::Borrow; use std::fs::File; use std::io::Read; +use std::mem; use std::path::Path; use std::sync::Arc; @@ -8,7 +10,8 @@ use ahash::AHashMap as Map; use fugue_arch::ArchitectureDef; use itertools::Itertools; - +use ouroboros::self_referencing; +use stack_map::StackMap; use ustr::Ustr; use crate::address::AddressValue; @@ -31,6 +34,9 @@ use crate::float_format::FloatFormat; use crate::il::instruction::{Instruction, InstructionFull}; use crate::register::RegisterNames; use crate::space_manager::SpaceManager; +use crate::Address; + +pub(crate) const MAX_DELAY_SLOTS: usize = 8; // Translator is used for parsing the processor spec XML and // lifting instructions @@ -57,7 +63,159 @@ pub struct Translator { source_files: Map, } +struct TranslationContextState<'a, 'az> { + translation_parser_insn_ctx: ParserContext<'a, 'az>, // for main instruction + translation_parser_delay_ctxs: [ParserContext<'a, 'az>; MAX_DELAY_SLOTS], + translation_builder_base: IRBuilderBase<'a, 'az>, +} + +#[self_referencing] +struct TranslationContextInner<'a> { + translator: &'a Translator, + translation_context_db: ContextDatabase, + + translation_allocator: IRBuilderArena, // for translation temporaries + + #[borrows(translation_allocator)] + #[covariant] + translation_context_state: TranslationContextState<'a, 'this>, +} + +#[repr(transparent)] +pub struct TranslationContext<'a>(TranslationContextInner<'a>); + +impl<'a> TranslationContext<'a> { + pub fn new(translator: &'a Translator) -> Self { + Self::new_with(translator, translator.context_database()) + } + + pub fn new_with(translator: &'a Translator, context_db: ContextDatabase) -> Self { + Self(TranslationContextInner::new( + translator, + context_db, + IRBuilderArena::with_capacity(1024), + |arena| TranslationContextState { + translation_parser_insn_ctx: ParserContext::empty(arena, translator.manager()), + translation_parser_delay_ctxs: array::from_fn(|_| { + ParserContext::empty(arena, translator.manager()) + }), + translation_builder_base: IRBuilderBase::empty( + arena, + translator.manager(), + translator.unique_mask(), + ), + }, + )) + } + + pub fn irb(&self, size: usize) -> IRBuilderArena { + IRBuilderArena::with_capacity(size) + } + + pub fn disassemble<'z>( + &mut self, + irb: &'z IRBuilderArena, + addr: impl Into
, + bytes: impl AsRef<[u8]>, + ) -> Result, Error> { + self.0.with_mut(|slf| { + let addr = addr.into(); + let addr_val = slf.translator.address(addr.into()); + + let bytes = bytes.as_ref(); + + let insn = slf.translator.disassemble_with( + slf.translation_context_db, + &mut slf.translation_context_state.translation_parser_insn_ctx, + irb, + addr_val, + bytes, + )?; + + if insn.length() > bytes.len() { + return Err(Error::Disassembly(DisassemblyError::InstructionResolution)); + } + + Ok(insn) + }) + } + + pub fn lift<'z>( + &mut self, + irb: &'z IRBuilderArena, + addr: impl Into
, + bytes: impl AsRef<[u8]>, + ) -> Result, Error> { + self.0.with_mut(|slf| { + let addr = addr.into(); + let addr_val = slf.translator.address(addr.into()); + + let bytes = bytes.as_ref(); + + let insn = slf.translator.lift_with( + slf.translation_context_db, + &mut slf.translation_context_state.translation_parser_insn_ctx, + &mut slf.translation_context_state.translation_parser_delay_ctxs, + &mut slf.translation_context_state.translation_builder_base, + irb, + addr_val, + bytes, + )?; + + if insn.length() > bytes.len() { + return Err(Error::Disassembly(DisassemblyError::InstructionResolution)); + } + + Ok(insn) + }) + } + + pub fn translator(&self) -> &Translator { + self.0.borrow_translator() + } + + pub fn context(&self) -> &ContextDatabase { + self.0.borrow_translation_context_db() + } + + pub fn reset(&mut self) { + let translator = *self.0.borrow_translator(); + let mut context_db = translator.context_database(); + + self.0.with_translation_context_db_mut(|old_context_db| { + mem::swap(old_context_db, &mut context_db); + }); + + *self = Self::new_with(translator, context_db); + } +} + +impl<'a> Clone for TranslationContext<'a> { + fn clone(&self) -> Self { + let translator = *self.0.borrow_translator(); + let context_db = self.0.borrow_translation_context_db().clone(); + + Self::new_with(translator, context_db) + } + + fn clone_from(&mut self, source: &Self) { + // we only need to copy the context database + let sctx = source.0.borrow_translation_context_db(); + self.0.with_translation_context_db_mut(|ctx| { + ctx.clone_from(sctx); + }); + } +} + impl Translator { + pub fn context<'a>(&'a self) -> TranslationContext<'a> { + TranslationContext::new(self) + } + + pub fn context_with<'a>(&'a self, db: ContextDatabase) -> TranslationContext<'a> { + TranslationContext::new_with(self, db) + } + pub fn is_big_endian(&self) -> bool { self.big_endian } @@ -413,14 +571,13 @@ impl Translator { ) -> Result, Error> { let arena = IRBuilderArena::with_capacity(4096); let mut ctxt = ParserContext::empty(&arena, self.manager()); - self.disassemble_with(db, &mut ctxt, &arena, builder, address, bytes) + self.disassemble_with(db, &mut ctxt, builder, address, bytes) } pub fn disassemble_aux<'a, 'az, 'c, T, E, F>( &'a self, db: &mut ContextDatabase, context: &'c mut ParserContext<'a, 'az>, - arena: &'az IRBuilderArena, address: AddressValue, bytes: &[u8], mut f: F, @@ -439,7 +596,7 @@ impl Translator { } } - context.reinitialise(arena, db, address.clone(), bytes); + context.reinitialise(db, address.clone(), bytes); let mut walker = ParserWalker::new(context, self); Translator::resolve(&mut walker, self.root.id(), &self.symbol_table)?; @@ -466,12 +623,11 @@ impl Translator { &'a self, db: &mut ContextDatabase, context: &mut ParserContext<'a, 'az>, - arena: &'az IRBuilderArena, builder: &'z IRBuilderArena, address: AddressValue, bytes: &[u8], ) -> Result, Error> { - self.disassemble_aux(db, context, arena, address, bytes, |fmt, delay_slots, length| { + self.disassemble_aux(db, context, address, bytes, |fmt, delay_slots, length| { let mnemonic = fmt.mnemonic_str(builder); let operands = fmt.operands_str(builder); @@ -489,12 +645,11 @@ impl Translator { &'a self, db: &mut ContextDatabase, context: &mut ParserContext<'a, 'az>, - arena: &'az IRBuilderArena, builder: &'z IRBuilderArena, address: AddressValue, bytes: &[u8], ) -> Result, Error> { - self.disassemble_aux(db, context, arena, address, bytes, |fmt, delay_slots, length| { + self.disassemble_aux(db, context, address, bytes, |fmt, delay_slots, length| { let mnemonic = fmt.mnemonic_str(builder); let operands = fmt.operands_str(builder); let operand_data = fmt.operand_data(builder); @@ -519,16 +674,26 @@ impl Translator { ) -> Result, Error> { let arena = IRBuilderArena::with_capacity(1024); let mut context = ParserContext::empty(&arena, self.manager()); - let mut base = builder.builder(self); - self.lift_with(db, &mut context, &arena, &mut base, address, bytes) + let mut dcontexts = array::from_fn(|_| ParserContext::empty(&arena, self.manager())); + let mut base = IRBuilderBase::empty(&arena, self.manager(), self.unique_mask()); + self.lift_with( + db, + &mut context, + &mut dcontexts, + &mut base, + builder, + address, + bytes, + ) } pub fn lift_with<'a, 'az, 'z>( &'a self, db: &mut ContextDatabase, context: &mut ParserContext<'a, 'az>, - arena: &'az IRBuilderArena, - builder: &mut IRBuilderBase<'a, 'z>, + delay_contexts: &mut [ParserContext<'a, 'az>; MAX_DELAY_SLOTS], + builder: &mut IRBuilderBase<'a, 'az>, + builder_arena: &'z IRBuilderArena, address: AddressValue, bytes: &[u8], ) -> Result, Error> { @@ -542,7 +707,7 @@ impl Translator { } // Main instruction - context.reinitialise(arena, db, address.clone(), bytes); + context.reinitialise(db, address, bytes); let mut walker = ParserWalker::new(context, self); Translator::resolve(&mut walker, self.root.id(), &self.symbol_table)?; @@ -554,18 +719,25 @@ impl Translator { let mut fall_offset = walker.length(); let delay_slots = walker.delay_slot(); - let mut delay_contexts = Map::default(); + + let mut delay_context_mapping = StackMap::<_, _, MAX_DELAY_SLOTS>::new(); if delay_slots > 0 { let mut byte_count = 0; - loop { + + // NOTE: this loop assumes we have enough mappings + for dcontext in delay_contexts.iter_mut() { + /* let mut dcontext = ParserContext::new( arena, db, address.clone() + fall_offset, &bytes[fall_offset..], ); - let mut dwalker = ParserWalker::new(&mut dcontext, self); + */ + + dcontext.reinitialise(db, address + fall_offset, &bytes[fall_offset..]); + let mut dwalker = ParserWalker::new(dcontext, self); Translator::resolve(&mut dwalker, self.root.id(), &self.symbol_table)?; Translator::resolve_handles(&mut dwalker, &self.manager, &self.symbol_table)?; @@ -575,7 +747,7 @@ impl Translator { let length = dwalker.length(); - delay_contexts.insert(address.clone() + fall_offset, dcontext); + delay_context_mapping.insert(address.clone() + fall_offset, dcontext); fall_offset += length; byte_count += length; @@ -584,18 +756,23 @@ impl Translator { break; } } + walker.set_next_address(address.clone() + fall_offset); } if let Some(ctor) = walker.constructor()? { let tmpl = ctor.unchecked_template(); - let mut builder = - IRBuilder::new(builder, ParserWalker::new(context, self), &mut delay_contexts); + let mut builder = IRBuilder::new( + builder, + builder_arena, + ParserWalker::new(context, self), + delay_context_mapping, + ); builder.build(tmpl, None, &self.symbol_table)?; builder.resolve_relatives()?; Ok(builder.emit(fall_offset)) } else { - Ok(PCodeRaw::nop_in(builder.arena(), address, walker.length())) + Ok(PCodeRaw::nop_in(builder_arena, address, walker.length())) } } @@ -747,6 +924,30 @@ mod test { Ok(()) } + #[test] + fn test_tctx() -> Result<(), Error> { + let mut translator = Translator::from_file( + "pc", + &ArchitectureDef::new("AARCH64", Endian::Little, 64, "v8A"), + &Map::default(), + "./data/processors/AARCH64/AARCH64.sla", + )?; + + translator.set_variable_default("ShowPAC", 0); + translator.set_variable_default("PAC_clobber", 0); + translator.set_variable_default("ShowBTI", 0); + translator.set_variable_default("ShowMemTag", 0); + + let bytes = [0x20, 0xf8, 0x48, 0x4f]; + let mut trans = translator.context(); + + let irb = trans.irb(4096); + + trans.lift(&irb, 0x1000u32, &bytes)?; + + Ok(()) + } + #[test] #[ignore = "test arm32 bug #6"] fn test_arm32_bug_6() -> Result<(), Box> {