Symbolic VM now uses references, implemented special unique ptr for dirs

This commit is contained in:
Can Bölük 2020-07-12 08:00:52 +02:00
parent 5d60e2b759
commit cb395a2b66
19 changed files with 221 additions and 121 deletions

View file

@ -55,7 +55,7 @@ namespace vtil::symbolic
// Declaration of symbolic memory type using sinkhole.
//
using memory = sinkhole<pointer, expression, pointer::make_weak>;
using memory = sinkhole<pointer, expression::reference, pointer::make_weak>;
// Creates a symbolic memory of the given type.
//
@ -64,11 +64,11 @@ namespace vtil::symbolic
switch ( type )
{
case memory_type::free:
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { return make_memory_ex( ptr, size ); } };
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { return make_memory_ex( ptr, size ); } };
case memory_type::relaxed:
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { return make_undefined_ex( size ); } };
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { return make_undefined_ex( size ); } };
default:
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression { unreachable(); return expression{}; } };
return { [ ] ( auto& ptr, bitcnt_t size ) -> expression::reference { unreachable(); return nullptr; } };
}
}
};

View file

@ -498,33 +498,33 @@ namespace vtil
// Create a lambda virtual machine and allocate a temporary result.
//
lambda_vm lvm;
symbolic::expression result = {};
symbolic::expression::reference result = {};
lvm.hooks.read_register = [ & ] ( const register_desc& desc )
{
return trace( { it, desc } );
};
lvm.hooks.read_memory = [ & ] ( const symbolic::expression& pointer, size_t byte_count )
lvm.hooks.read_memory = [ & ] ( const symbolic::expression::reference& pointer, size_t byte_count )
{
auto exp = trace( symbolic::variable{ it, { pointer, math::narrow_cast<bitcnt_t>( byte_count * 8 ) } } );
return exp.is_valid() ? exp.resize( result_bcnt ) : exp;
};
lvm.hooks.write_register = [ & ] ( const register_desc& desc, symbolic::expression value )
lvm.hooks.write_register = [ & ] ( const register_desc& desc, symbolic::expression::reference value )
{
if ( desc == lookup.reg() )
result = std::move( value );
};
lvm.hooks.write_memory = [ & ] ( const symbolic::expression& pointer, symbolic::expression value )
lvm.hooks.write_memory = [ & ] ( const symbolic::expression::reference& pointer, symbolic::expression::reference value )
{
if ( pointer.equals( lookup.mem().decay() ) )
if ( pointer->equals( lookup.mem().decay() ) )
result = std::move( value );
};
// Step one instruction, if result was successfuly captured, return.
//
if ( lvm.execute( *it ), result )
return result;
return *result;
// If we could not describe the behaviour, increment iterator and return.
//

View file

@ -36,7 +36,7 @@ namespace vtil
{
// Declare a helper to convert operands of current instruction into expressions.
//
auto cvt_operand = [ & ] ( int i ) -> symbolic::expression
auto cvt_operand = [ & ] ( int i ) -> symbolic::expression::reference
{
const operand& op = ins.operands[ i ];
@ -46,7 +46,7 @@ namespace vtil
{
// Trace the source register.
//
symbolic::expression result = read_register( op.reg() );
symbolic::expression::reference result = read_register( op.reg() );
// If stack pointer, add the current virtual offset.
//
@ -76,7 +76,7 @@ namespace vtil
//
write_register(
ins.operands[ 0 ].reg(),
cvt_operand( 1 ).resize( ins.operands[ 0 ].bit_count(), cast_signed )
cvt_operand( 1 )->resize( ins.operands[ 0 ].bit_count(), cast_signed )
);
return true;
}
@ -108,13 +108,14 @@ namespace vtil
// Read the source operand and byte-align.
//
auto src = cvt_operand( 2 );
src.resize( ( src.size() + 7 ) & ~7 );
bitcnt_t bsize = ( src->size() + 7 ) & ~7;
if ( src->size() != bsize ) ( +src )->resize( bsize );
// Query base pointer without using the wrapper to skip SP adjustment and
// add offset. Write the source to the pointer.
//
auto [base, offset] = ins.memory_location();
write_memory( read_register( base ) + offset, src );
write_memory( read_register( base ) + offset, *src );
return true;
}
// If any symbolic operator:
@ -161,7 +162,7 @@ namespace vtil
else if ( ( ins.operands[ 0 ].size() + ins.operands[ 1 ].size() ) <= 8 )
{
auto op1_low = cvt_operand( 0 );
auto op1 = op1_low | ( op1_high.resize( op1_high.size() + op1_low.size() ) << op1_low.size() );
auto op1 = op1_low | ( op1_high->resize( op1_high->size() + op1_low->size() ) << op1_low->size() );
result = { op1, op_id, cvt_operand( 2 ) };
}
// If operation is 65 bits or bigger:

View file

@ -41,19 +41,19 @@ namespace vtil
// Reads from the register.
//
virtual symbolic::expression read_register( const register_desc& desc ) { unreachable(); return {}; }
virtual symbolic::expression::reference read_register( const register_desc& desc ) { unreachable(); return {}; }
// Reads the given number of bytes from the memory.
//
virtual symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) { unreachable(); return {}; }
virtual symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) { unreachable(); return {}; }
// Writes to the register.
//
virtual void write_register( const register_desc& desc, symbolic::expression value ) { unreachable(); }
virtual void write_register( const register_desc& desc,symbolic::expression::reference value ) { unreachable(); }
// Writes the given expression to the memory.
//
virtual void write_memory( const symbolic::expression& pointer, symbolic::expression value ) { unreachable(); }
virtual void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) { unreachable(); }
// Runs the given instruction, returns whether it was successful.
//

View file

@ -70,25 +70,25 @@ namespace vtil
? hooks.size_register( desc )
: vm_base::size_register( desc );
}
symbolic::expression read_register( const register_desc& desc ) override
symbolic::expression::reference read_register( const register_desc& desc ) override
{
return hooks.read_register
? hooks.read_register( desc )
: vm_base::read_register( desc );
}
symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) override
symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) override
{
return hooks.read_memory
? hooks.read_memory( pointer, byte_count )
: vm_base::read_memory( pointer, byte_count );
}
void write_register( const register_desc& desc, symbolic::expression value ) override
void write_register( const register_desc& desc, symbolic::expression::reference value ) override
{
return hooks.write_register
? hooks.write_register( desc, std::move( value ) )
: vm_base::write_register( desc, std::move( value ) );
}
void write_memory( const symbolic::expression& pointer, symbolic::expression value ) override
void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) override
{
return hooks.write_memory
? hooks.write_memory( pointer, std::move( value ) )

View file

@ -31,13 +31,13 @@ namespace vtil
{
// Reads from the register.
//
symbolic::expression symbolic_vm::read_register( const register_desc& desc )
symbolic::expression::reference symbolic_vm::read_register( const register_desc& desc )
{
bitcnt_t size = size_register( desc );
register_desc full = { desc.flags, desc.local_id, size, 0, desc.architecture };
auto it = register_state.find( full );
symbolic::expression exp = it == register_state.end()
auto exp = it == register_state.end()
? symbolic::variable{ full }.to_expression( false )
: it->second;
@ -49,7 +49,7 @@ namespace vtil
// Writes to the register.
//
void symbolic_vm::write_register( const register_desc& desc, symbolic::expression value )
void symbolic_vm::write_register( const register_desc& desc, symbolic::expression::reference value )
{
bitcnt_t size = size_register( desc );
register_desc full = { desc.flags, desc.local_id, size, 0, desc.architecture };
@ -57,22 +57,22 @@ namespace vtil
if ( desc.bit_count == size && desc.bit_offset == 0 )
{
register_state.erase( desc );
register_state.emplace( desc, std::move( value ) );
register_state.emplace( desc, value );
}
else
{
auto& exp = register_state[ full ];
if ( !exp ) exp = symbolic::make_register_ex( full );
exp = ( exp & ~desc.get_mask() ) | ( value.resize( desc.bit_count ).resize( size ) << desc.bit_offset );
exp = ( std::move( exp ) & ~desc.get_mask() ) | ( value.resize( desc.bit_count ).resize( size ) << desc.bit_offset );
}
}
// Reads the given number of bytes from the memory.
//
symbolic::expression symbolic_vm::read_memory( const symbolic::expression& pointer, size_t byte_count )
symbolic::expression::reference symbolic_vm::read_memory( const symbolic::expression::reference& pointer, size_t byte_count )
{
bitcnt_t bcnt = math::narrow_cast<bitcnt_t>( byte_count * 8 );
symbolic::expression exp = memory_state.read_v(
symbolic::expression::reference exp = memory_state.read_v(
pointer,
bcnt
);
@ -81,11 +81,11 @@ namespace vtil
// Writes the given expression to the memory.
//
void symbolic_vm::write_memory( const symbolic::expression& pointer, symbolic::expression value )
void symbolic_vm::write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value )
{
memory_state.write(
pointer,
value.resize( ( value.size() + 7 ) & ~7 )
value.resize( ( value->size() + 7 ) & ~7 )
);
}

View file

@ -42,7 +42,7 @@ namespace vtil
// State of the virtual machine.
//
symbolic::memory memory_state;
std::map<register_desc, symbolic::expression> register_state;
std::map<register_desc, symbolic::expression::reference> register_state;
// Construct from memory type, defaults to free.
//
@ -52,19 +52,19 @@ namespace vtil
// Reads from the register.
// - Value will be unpacked.
//
symbolic::expression read_register( const register_desc& desc ) override;
symbolic::expression::reference read_register( const register_desc& desc ) override;
// Writes to the register.
//
void write_register( const register_desc& desc, symbolic::expression value ) override;
void write_register( const register_desc& desc, symbolic::expression::reference value ) override;
// Reads the given number of bytes from the memory.
//
symbolic::expression read_memory( const symbolic::expression& pointer, size_t byte_count ) override;
symbolic::expression::reference read_memory( const symbolic::expression::reference& pointer, size_t byte_count ) override;
// Writes the given expression to the memory.
//
void write_memory( const symbolic::expression& pointer, symbolic::expression value ) override;
void write_memory( const symbolic::expression::reference& pointer, symbolic::expression::reference value ) override;
// Override execute to enforce lazyness.
//

View file

@ -131,7 +131,7 @@ namespace vtil::optimizer
//
auto k = pair.first; auto v = pair.second.simplify();
symbolic::expression v0 = symbolic::make_register_ex( k );
if ( v0.equals( v ) )
if ( v->equals( v0 ) )
continue;
// If register value is not used after this instruction, skip from emitted state.
@ -191,11 +191,11 @@ namespace vtil::optimizer
// Pack registers and the expression.
//
v = symbolic::variable::pack_all( v.simplify( true ) );
symbolic::expression final_value = symbolic::variable::pack_all( ( +v )->simplify( true ) );
// Buffer a mov instruction.
//
instruction_buffer.push_back( { &ins::mov, { k, translator << v } } );
instruction_buffer.push_back( { &ins::mov, { k, translator << final_value } } );
}
// For each memory state:
@ -208,7 +208,7 @@ namespace vtil::optimizer
// If value is unchanged, skip.
//
if ( v.equals( v0 ) )
if ( v->equals( v0 ) )
continue;
// Try minimizing expression size.
@ -230,7 +230,7 @@ namespace vtil::optimizer
// Pack registers and the expression.
//
v = symbolic::variable::pack_all( v.simplify( true ) );
v = symbolic::variable::pack_all( v->simplify( true ) );
// If pointer can be rewritten as $sp + C:
//
@ -242,7 +242,7 @@ namespace vtil::optimizer
instruction_buffer.push_back(
{
&ins::str,
{ REG_SP, make_imm<int64_t>( *displacement ), translator << v }
{ REG_SP, make_imm<int64_t>( *displacement ), translator << *v }
} );
}
else
@ -283,7 +283,7 @@ namespace vtil::optimizer
instruction_buffer.push_back(
{
&ins::str,
{ base, make_imm( offset ), translator << v }
{ base, make_imm( offset ), translator << *v }
} );
}
}

View file

@ -123,12 +123,12 @@ namespace vtil::optimizer::validation
// Validate target.
//
symbolic::expression target_call = ins.operands[ 0 ].is_immediate()
? ins.operands[ 0 ].imm().u64
auto target_call = ins.operands[ 0 ].is_immediate()
? symbolic::expression::reference{ ins.operands[ 0 ].imm().u64 }
: vm.read_register( ins.operands[ 0 ].reg() );
if ( target_call.value.get() != call.address )
if ( target_call->value.get() != call.address )
{
logger::warning( "Unexpected callee, expected 0x%llx, got [%s].", call.address, target_call );
logger::warning( "Unexpected callee, expected 0x%llx, got [%s].", call.address, *target_call );
success = false;
return false;
}
@ -139,7 +139,7 @@ namespace vtil::optimizer::validation
auto it = call_conv.param_registers.begin();
for ( auto [value, id] : zip( call.parameters, iindices() ) )
{
symbolic::expression exp;
symbolic::expression::reference exp;
// If we did not reach the end of registers yet:
//
@ -163,7 +163,7 @@ namespace vtil::optimizer::validation
// Fail if value does not match.
//
if ( exp.value.get() != value )
if ( exp->value.get() != value )
{
logger::warning( "Parameter %d does not match, expected 0x%llx, got [%s].", id, value, exp );
success = false;
@ -196,12 +196,12 @@ namespace vtil::optimizer::validation
// Validate return address.
//
symbolic::expression sreturn_address = ins.operands[ 0 ].is_immediate()
? ins.operands[ 0 ].imm().u64
auto sreturn_address = ins.operands[ 0 ].is_immediate()
? symbolic::expression::reference{ ins.operands[ 0 ].imm().u64 }
: vm.read_register( ins.operands[ 0 ].reg() );
if ( sreturn_address.value.get() != return_address )
if ( sreturn_address->value.get() != return_address )
{
logger::warning( "Unexpected return address, expected 0x%llx, got [%s].", return_address, sreturn_address );
logger::warning( "Unexpected return address, expected 0x%llx, got [%s].", return_address, *sreturn_address );
success = false;
return false;
}
@ -210,8 +210,8 @@ namespace vtil::optimizer::validation
//
for ( auto& [reg, value] : exit.register_state )
{
symbolic::expression exp = vm.read_register( reg );
if ( exp.value.get() != value )
auto exp = vm.read_register( reg );
if ( exp->value.get() != value )
{
logger::warning( "Return state %s does not match, expected 0x%llx, got [%s].", reg, value, exp );
success = false;
@ -227,42 +227,40 @@ namespace vtil::optimizer::validation
return vm.symbolic_vm::execute( ins );
};
vm.hooks.read_memory = [ & ] ( const symbolic::expression& pointer, size_t sz )
vm.hooks.read_memory = [ & ] ( const symbolic::expression::reference& pointer, size_t sz )
{
symbolic::expression ptr = pointer.simplify();
// If action log has a matching read memory on top of the stack:
//
if ( action_it != action_end && std::get_if<memory_read>( &*action_it ) )
{
auto& mem = std::get<memory_read>( *action_it );
if ( ptr.value.get() == mem.address )
if ( pointer->value.get() == mem.address )
{
// Write fake value to the state and pop the stack.
//
symbolic::expression value = { mem.fake_value, mem.size };
vm.symbolic_vm::write_memory( ptr, value );
vm.symbolic_vm::write_memory( pointer, value );
++action_it;
}
}
return vm.symbolic_vm::read_memory( ptr, sz );
return vm.symbolic_vm::read_memory( pointer, sz );
};
vm.hooks.write_memory = [ & ] ( const symbolic::expression& pointer, symbolic::expression exp )
vm.hooks.write_memory = [ & ] ( const symbolic::expression::reference& pointer, symbolic::expression::reference exp )
{
// If action log has a matching write memory on top of the stack:
//
if ( action_it != action_end && std::get_if<memory_write>( &*action_it ) )
{
auto& mem = std::get<memory_write>( *action_it );
if ( pointer.value.get() == mem.address )
if ( pointer->value.get() == mem.address )
{
// Pop the stack and validate the value.
//
if ( exp.value.get() != mem.value )
if ( exp->value.get() != mem.value )
{
logger::warning( "Unexpected memory write into 0x%llx, expected 0x%llx, got [%s].", mem.address, mem.value, exp );
logger::warning( "Unexpected memory write into 0x%llx, expected 0x%llx, got [%s].", mem.address, mem.value, *exp );
success = false;
}
++action_it;
@ -315,7 +313,7 @@ namespace vtil::optimizer::validation
//
operand dst = {};
if ( lim->base == &ins::js )
dst = *vm.read_register( lim->operands[ 0 ].reg() ).get<bool>() ? lim->operands[ 1 ] : lim->operands[ 2 ];
dst = *vm.read_register( lim->operands[ 0 ].reg() )->get<bool>() ? lim->operands[ 1 ] : lim->operands[ 2 ];
else if ( lim->base == &ins::jmp )
dst = lim->operands[ 0 ];
@ -326,7 +324,7 @@ namespace vtil::optimizer::validation
eit = rtn->explored_blocks.find( dst.imm().u64 );
// Otherwise read VM context.
//
else if ( auto jmp_dst = vm.read_register( dst.reg() ).get() )
else if ( auto jmp_dst = vm.read_register( dst.reg() )->get() )
eit = rtn->explored_blocks.find( *jmp_dst );
// If no valid destination, fail.

View file

@ -31,11 +31,13 @@ namespace vtil::symbolic::directive
{
// Constructor for directive representing the result of an unary operator.
//
instance::instance( math::operator_id op, const instance& e1 ) : rhs( e1 ), op( op ) {}
instance::instance( math::operator_id op, const instance& e1 )
: rhs( e1 ), op( op ) {}
// Constructor for directive representing the result of a binary operator.
//
instance::instance( const instance& e1, math::operator_id op, const instance& e2 ) : lhs( e1 ), rhs( e2 ), op( op ) {}
instance::instance( const instance& e1, math::operator_id op, const instance& e2 )
: lhs( e1 ), rhs( e2 ), op( op ) {}
// Enumerates each unique variable.
//
@ -94,4 +96,25 @@ namespace vtil::symbolic::directive
else if ( !o.lhs || !lhs->equals( *o.lhs ) ) return false;
return true;
}
// Simple copyable unique pointer implementation.
//
instance::reference::reference( const instance& o ) : ptr( new instance( o ) ) {}
instance::reference::reference( instance&& o ) : ptr( new instance( std::move( o ) ) ) {}
instance::reference::reference( const reference& o ) : ptr( o ? new instance( *o ) : nullptr ) {}
instance::reference::reference( reference&& o ) : ptr( std::exchange( o.ptr, nullptr ) ) {}
instance::reference::~reference()
{
if ( ptr ) delete ptr;
}
instance::reference& instance::reference::operator=( instance::reference&& o )
{
ptr = std::exchange( o.ptr, nullptr );
return *this;
}
instance::reference& instance::reference::operator=( const instance::reference& o )
{
ptr = o ? new instance( *o ) : nullptr;
return *this;
}
};

View file

@ -147,7 +147,42 @@ namespace vtil::symbolic::directive
//
struct instance : math::operable<instance>
{
using reference = shared_reference<instance>;
// Simple copyable unique pointer implementation.
//
struct reference
{
instance* ptr = nullptr;
// Construct by implicit null or instance value.
//
reference() {}
reference( const instance& i );
reference( instance&& i );
// Copy / Move from another reference.
//
reference( const reference& o );
reference( reference&& o );
reference& operator=( reference&& o );
reference& operator=( const reference& o );
// Destructor deletes the value.
//
~reference();
// Null check.
//
explicit operator bool() const { return ptr; }
// Pointer interface.
//
operator instance*() { return ptr; }
operator const instance*() const { return ptr; }
instance& operator*() { return *ptr; }
const instance& operator*() const { return *ptr; }
instance* operator->() { return ptr; }
const instance* operator->() const { return ptr; }
};
// If symbolic variable, the identifier of the variable
// and type of expressions it can match.

View file

@ -40,7 +40,7 @@ namespace vtil::symbolic::directive
// Adds the mapping of a variable to an expression.
//
bool add( const instance::reference& dir, const expression::reference& exp )
bool add( const instance* dir, const expression::reference& exp )
{
// If it's the first time this variable is being used:
//
@ -74,7 +74,7 @@ namespace vtil::symbolic::directive
// Translates a variable to the matching expression.
//
expression::reference translate( const instance::reference& dir ) const
expression::reference translate( const instance* dir ) const
{
// Assert the looked up type is variable.
//
@ -83,7 +83,8 @@ namespace vtil::symbolic::directive
// Translate using the lookup table.
//
return lookup_table[ dir->lookup_index ];
}
}
expression::reference translate( const instance& dir ) const { return translate( &dir ); }
};
// Tries to match the the given expression with the directive and fills the
@ -91,7 +92,7 @@ namespace vtil::symbolic::directive
//
template<typename T, std::enable_if_t<std::is_same_v<typename T::value_type, symbol_table_t>, int> = 0>
static size_t fast_match( T* results,
const instance::reference& dir,
const instance* dir,
const expression::reference& exp,
size_t index = 0 )
{
@ -196,4 +197,6 @@ namespace vtil::symbolic::directive
//
return ( results->size() + 1 ) - size_0;
}
template<typename T, std::enable_if_t<std::is_same_v<typename T::value_type, symbol_table_t>, int> = 0>
static size_t fast_match( T* results, const instance& dir, const expression::reference& exp, size_t index = 0 ) { return fast_match( results, &dir, exp, index ); }
};

View file

@ -38,7 +38,7 @@ namespace vtil::symbolic
// or a null reference if it would fail.
//
expression::reference translate( const symbol_table_t& sym,
const instance::reference& dir,
const instance* dir,
bitcnt_t bit_cnt,
bool speculative_condition,
int64_t max_depth )
@ -260,7 +260,7 @@ namespace vtil::symbolic
// and returns the first instance that matches query.
//
expression::reference transform( const expression::reference& exp,
const instance::reference& from, const instance::reference& to,
const instance* from, const instance* to,
const expression_filter_t& filter,
int64_t max_depth )
{
@ -283,7 +283,7 @@ namespace vtil::symbolic
// Log the translation.
//
log<CON_BLU>( "Translating [%s] => [%s]:\n", *from, *to );
from->enum_variables( [ & ] ( const instance& ins )
from->enum_variables( [ & ] ( const instance* ins )
{
log<CON_BLU>( " %s: %s\n", ins.id, *match.translate( ins ) );
} );
@ -343,7 +343,7 @@ namespace vtil::symbolic
// Log the translation.
//
log<CON_BLU>( "Translating [%s] => [%s]:\n", *from, *to );
from->enum_variables( [ & ] ( const instance& ins )
from->enum_variables( [ & ] ( const instance* ins )
{
log<CON_BLU>( " %s: %s\n", ins.id, *match.translate( ins ) );
} );

View file

@ -40,7 +40,7 @@ namespace vtil::symbolic
// or a null reference if it would fail.
//
expression::reference translate( const directive::symbol_table_t& sym,
const directive::instance::reference& dir,
const directive::instance* dir,
bitcnt_t bit_cnt,
bool speculative_condition,
int64_t max_depth );
@ -49,7 +49,7 @@ namespace vtil::symbolic
// and returns the first instance that matches query.
//
expression::reference transform( const expression::reference& exp,
const directive::instance::reference& from, const directive::instance::reference& to,
const directive::instance* from, const directive::instance* to,
const expression_filter_t& filter,
int64_t max_depth );
};

View file

@ -691,7 +691,7 @@ namespace vtil::symbolic
// this way and additionally we avoid copying where an operand is being simplified
// as that can be replaced by a simple swap of shared references.
//
auto ref = make_local_reference( this );
reference ref = make_local_reference( this );
simplify_expression( ref, prettify );
// Only thing that we should be careful about is the case expression->simplify(),
@ -869,4 +869,44 @@ namespace vtil::symbolic
if ( is_variable() ) return uid.to_string();
return "null";
}
// Implement some helpers to conditionally copy.
//
expression_reference& expression_reference::resize( bitcnt_t new_size, bool signed_cast, bool no_explicit )
{
if ( new_size != get()->size() )
own()->resize( new_size, signed_cast, no_explicit );
return *this;
}
expression_reference expression_reference::resize( bitcnt_t new_size, bool signed_cast, bool no_explicit ) const
{
return expression_reference{ *this }.resize( new_size, signed_cast, no_explicit );
}
expression_reference& expression_reference::simplify( bool prettify )
{
if ( prettify || !get()->simplify_hint )
own()->simplify( prettify );
return *this;
}
expression_reference expression_reference::simplify( bool prettify ) const
{
return expression_reference{ *this }.simplify( prettify );
}
expression_reference& expression_reference::make_lazy()
{
if ( !get()->is_lazy )
own()->is_lazy = true;
return *this;
}
expression_reference expression_reference::make_lazy() const
{
return expression_reference{ *this }.make_lazy();
}
// Implemented for sinkhole-use.
//
bitcnt_t expression_reference::size() const
{
return get()->size();
}
};

View file

@ -29,7 +29,7 @@
namespace vtil::symbolic::directive
{
std::vector<std::pair<instance::reference, instance::reference>> boolean_simplifiers
std::vector<std::pair<instance, instance>> boolean_simplifiers
{
// Manually added:
//
@ -57,12 +57,12 @@ namespace vtil::symbolic::directive
{ A!=-A, 1 },
};
const std::vector<std::pair<instance::reference, instance::reference>>& build_boolean_simplifiers()
const std::vector<std::pair<instance, instance>>& build_boolean_simplifiers()
{
if ( !boolean_simplifiers.empty() )
return boolean_simplifiers;
#ifndef __INTELLISENSE__
#define ADD_DIRECTIVE( ... ) ([ ] () { boolean_simplifiers.push_back( { __VA_ARGS__ } ); })()
#define ADD_DIRECTIVE( ... ) ([ ] () { boolean_simplifiers.emplace_back( __VA_ARGS__ ); })()
// Boolean Simplifiers
//
ADD_DIRECTIVE( ((A>B)&(A>C)), __iff((B>C), (A>B)) );
@ -2617,7 +2617,7 @@ namespace vtil::symbolic::directive
constexpr auto overflow = [ ] ( auto a, auto b ) { return ((a<0)==(b<0))&((a<0)!=((a+b)<0)); };
constexpr auto underflow = [ ] ( auto a, auto b ) { return ((a<0)!=(b<0))&((a<0)!=((a-b)<0)); };
const std::vector<std::pair<instance::reference, instance::reference>> boolean_joiners =
const std::vector<std::pair<instance, instance>> boolean_joiners =
{
// Manually added:
//

View file

@ -32,10 +32,10 @@
namespace vtil::symbolic::directive
{
extern std::vector<std::pair<instance::reference, instance::reference>> boolean_simplifiers;
extern const std::vector<std::pair<instance::reference, instance::reference>> boolean_joiners;
extern std::vector<std::pair<instance, instance>> boolean_simplifiers;
extern const std::vector<std::pair<instance, instance>> boolean_joiners;
const std::vector<std::pair<instance::reference, instance::reference>>& build_boolean_simplifiers();
const std::vector<std::pair<instance, instance>>& build_boolean_simplifiers();
/*
Auto generated using:

View file

@ -34,7 +34,7 @@ namespace vtil::symbolic::directive
// List of universal simplifiers, they have to reduce complexity or keep it equal
// at the very least to not cause an infinity loop.
//
static const std::pair<instance::reference, instance::reference> universal_simplifiers[] =
static const std::pair<instance, instance> universal_simplifiers[] =
{
// TODO: Arithmetic operators, */% etc.
//
@ -235,7 +235,7 @@ namespace vtil::symbolic::directive
// Describes the way operands of two operators join each other.
// - Has no obligation to produce simple output, should be checked.
//
static const std::pair<instance::reference, instance::reference> join_descriptors[] =
static const std::pair<instance, instance> join_descriptors[] =
{
// TODO: Arithmetic operators, */% etc.
// TODO: Should we add ADD and SUB to bitwise despite the partial evaluator?
@ -391,7 +391,7 @@ namespace vtil::symbolic::directive
// Grouping of simple representations into more complex directives.
//
static const std::pair<instance::reference, instance::reference> pack_descriptors[] =
static const std::pair<instance, instance> pack_descriptors[] =
{
{ __ucast(A>>B, 0x1), __bt(A, B) },
{ (A>>B)&1, __ucast(__bt(A,B),__bcnt(A)) },
@ -413,7 +413,7 @@ namespace vtil::symbolic::directive
// Conversion from more complex directives into simple representations.
//
static const std::pair<instance::reference, instance::reference> unpack_descriptors[] =
static const std::pair<instance, instance> unpack_descriptors[] =
{
{ __bt(A,B), __ucast((A&(1<<B))>>B,1) },
{ __min(A,B), __if(A<=B,A)|__if(A>B,B) },

View file

@ -44,8 +44,8 @@ namespace vtil::symbolic
// Implement lookup-table based dynamic tables.
//
using static_directive_table_entry = std::pair<directive::instance::reference, directive::instance::reference>;
using dynamic_directive_table_entry = std::pair<const directive::instance*, const directive::instance*>;
using static_directive_table_entry = std::pair<directive::instance, directive::instance>;
using dynamic_directive_table_entry = std::pair<const directive::instance*, const directive::instance*>;
using dynamic_directive_table = std::vector<dynamic_directive_table_entry>;
using organized_directive_table = std::array<dynamic_directive_table, ( size_t ) math::operator_id::max>;
@ -56,8 +56,8 @@ namespace vtil::symbolic
organized_directive_table table;
for ( auto [table, op] : zip( table, iindices() ) )
for( auto& directive : container )
if ( directive.first->op == ( math::operator_id ) op )
table.emplace_back( directive.first.get(), directive.second.get() );
if ( directive.first.op == ( math::operator_id ) op )
table.emplace_back( &directive.first, &directive.second );
return table;
};
@ -122,7 +122,7 @@ namespace vtil::symbolic
{
// If we can transform the expression by the directive set:
//
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, -1 ) )
if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, -1 ) )
{
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
log<CON_PRP>( "[Pack] %s => %s\n", *dir_src, *dir_dst );
@ -141,7 +141,7 @@ namespace vtil::symbolic
// Checks if the expression can be interpreted as a vector-boolean expression.
//
static std::pair<bool, const expression*> match_boolean_expression( const expression::reference& exp )
static std::pair<bool, expression::reference> match_boolean_expression( const expression::reference& exp )
{
switch ( exp->op )
{
@ -149,7 +149,7 @@ namespace vtil::symbolic
//
case math::operator_id::invalid:
{
if ( exp->is_variable() ) return { true, &*exp };
if ( exp->is_variable() ) return { true, exp };
else return { true, nullptr };
}
@ -170,11 +170,11 @@ namespace vtil::symbolic
auto [m2, p2] = match_boolean_expression( exp->rhs );
if ( !m2 ) return { false, nullptr };
if ( !p2 ) return { true, p1 };
if ( !p1 ) return { true, p2 };
if ( !p2 ) return { true, std::move( p1 ) };
if ( !p1 ) return { true, std::move( p2 ) };
if ( p1->uid == p2->uid )
return { true, p1 };
return { true, std::move( p1 ) };
else
return { false, nullptr };
}
@ -208,18 +208,18 @@ namespace vtil::symbolic
// Apply each mask if not no-op.
//
expression exp_new = uid_base->clone();
expression::reference& exp_new = uid_base;
if ( and_mask != ~0ull ) exp_new = exp_new & expression{ and_mask, exp->size() };
if ( xor_mask ) exp_new = exp_new ^ expression{ xor_mask, exp->size() };
if ( or_mask ) exp_new = exp_new | expression{ or_mask, exp->size() };
// If complexity was higher or equal, fail.
//
if ( exp_new.complexity >= exp->complexity ) return false;
if ( exp_new->complexity >= exp->complexity ) return false;
// Apply and return.
//
*+exp = exp_new;
exp = std::move( exp_new );
return true;
}
@ -304,7 +304,7 @@ namespace vtil::symbolic
// Invoke resize with failure on explicit cast:
//
( +exp_new )->resize( new_size, exp->op == math::operator_id::cast, true );
exp_new.resize( new_size, exp->op == math::operator_id::cast, true );
// If implicit resize failed:
//
@ -330,7 +330,7 @@ namespace vtil::symbolic
}
}
( +exp )->simplify_hint = true;
exp->simplify_hint = true;
cache_entry = *exp;
return success_flag;
}
@ -342,7 +342,7 @@ namespace vtil::symbolic
// Recurse, and indicate success.
//
simplify_expression( exp, pretty, max_depth - 1 );
( +exp )->simplify_hint = true;
exp->simplify_hint = true;
cache_entry = *exp;
success_flag = true;
return true;
@ -374,7 +374,7 @@ namespace vtil::symbolic
// Recurse, and indicate success.
//
simplify_expression( exp, pretty, max_depth - 1 );
( +exp )->simplify_hint = true;
exp->simplify_hint = true;
cache_entry = *exp;
success_flag = true;
return true;
@ -462,7 +462,7 @@ namespace vtil::symbolic
{
// If we can transform the expression by the directive set:
//
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, max_depth ) )
if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, max_depth ) )
{
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
log<CON_GRN>( "[Simplify] %s => %s\n", *dir_src, *dir_dst );
@ -471,7 +471,7 @@ namespace vtil::symbolic
// Recurse, set the hint and return the simplified instance.
//
simplify_expression( exp_new, pretty, max_depth );
( +exp_new )->simplify_hint = true;
exp_new->simplify_hint = true;
cache_entry = exp_new;
success_flag = !exp->is_identical( *exp_new );
exp = exp_new;
@ -489,7 +489,7 @@ namespace vtil::symbolic
{
// If we can transform the expression by the directive set:
//
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, {}, max_depth ) )
if ( auto exp_new = transform( exp, dir_src, dir_dst, {}, max_depth ) )
{
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
log<CON_GRN>( "[Simplify] %s => %s\n", *dir_src, *dir_dst );
@ -498,7 +498,7 @@ namespace vtil::symbolic
// Recurse, set the hint and return the simplified instance.
//
simplify_expression( exp_new, pretty, max_depth );
( +exp_new )->simplify_hint = true;
exp_new->simplify_hint = true;
cache_entry = exp_new;
success_flag = !exp->is_identical( *exp_new );
exp = exp_new;
@ -513,7 +513,7 @@ namespace vtil::symbolic
{
// If we can transform the expression by the directive set:
//
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, filter, max_depth ) )
if ( auto exp_new = transform( exp, dir_src, dir_dst, filter, max_depth ) )
{
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
log<CON_GRN>( "[Join] %s => %s\n", *dir_src, *dir_dst );
@ -523,7 +523,7 @@ namespace vtil::symbolic
// Recurse, set the hint and return the simplified instance.
//
simplify_expression( exp_new, pretty, max_depth - 1 );
( +exp_new )->simplify_hint = true;
exp_new->simplify_hint = true;
cache_entry = exp_new;
success_flag = !exp->is_identical( *exp_new );
exp = exp_new;
@ -541,7 +541,7 @@ namespace vtil::symbolic
{
// If we can transform the expression by the directive set:
//
if ( auto exp_new = transform( exp, *dir_src, *dir_dst, filter, max_depth ) )
if ( auto exp_new = transform( exp, dir_src, dir_dst, filter, max_depth ) )
{
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
log<CON_GRN>( "[Join] %s => %s\n", *dir_src, *dir_dst );
@ -551,7 +551,7 @@ namespace vtil::symbolic
// Recurse, set the hint and return the simplified instance.
//
simplify_expression( exp_new, pretty, max_depth - 1 );
( +exp_new )->simplify_hint = true;
exp_new->simplify_hint = true;
cache_entry = exp_new;
success_flag = !exp->is_identical( *exp_new );
exp = exp_new;
@ -570,7 +570,7 @@ namespace vtil::symbolic
{
// If we can transform the expression by the directive set:
//
if ( auto exp_new = transform( exp, *dir_src, *dir_dst,
if ( auto exp_new = transform( exp, dir_src, dir_dst,
[ & ] ( auto& exp_new ) { simplify_expression( exp_new, true, max_depth - 1 ); return exp_new->complexity < exp->complexity; }, max_depth ) )
{
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
@ -580,7 +580,7 @@ namespace vtil::symbolic
// Set the hint and return the simplified instance.
//
( +exp_new )->simplify_hint = true;
exp_new->simplify_hint = true;
cache_entry = exp_new;
success_flag = !exp->is_identical( *exp_new );
exp = exp_new;