mirror of
https://github.com/vtil-project/VTIL-Core
synced 2026-08-17 08:23:03 -04:00
Majorly optimized refs for overwrite, fixed some of the messy parts.
This commit is contained in:
parent
2bac0c4e9b
commit
a1e8a013fb
21 changed files with 251 additions and 231 deletions
|
|
@ -66,7 +66,7 @@ namespace vtil::symbolic
|
|||
// Construct from symbolic expression.
|
||||
//
|
||||
pointer( const expression::reference& base );
|
||||
pointer( const expression& base ) : pointer( ( ( expression::reference&& ) make_local_reference( &base ) ) ) {}
|
||||
pointer( const expression& base ) : pointer( make_local_reference( &base ) ) {}
|
||||
|
||||
// Default copy/move.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -47,9 +47,9 @@ namespace vtil
|
|||
return tbl;
|
||||
}();
|
||||
|
||||
fassert( lookup_table.size() > ( size_t ) op );
|
||||
dassert( lookup_table.size() > ( size_t ) op );
|
||||
const instruction_desc* desc = lookup_table[ ( size_t ) op ];
|
||||
fassert( desc );
|
||||
dassert( desc );
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -274,7 +274,7 @@ namespace vtil
|
|||
ref.simplify();
|
||||
return false;
|
||||
}
|
||||
ref = {};
|
||||
ref.reset();
|
||||
return *result;
|
||||
}
|
||||
|
||||
|
|
@ -414,7 +414,7 @@ namespace vtil
|
|||
{
|
||||
symbolic::variable&& var = std::move( ( +exp )->uid.get<symbolic::variable>() );
|
||||
var.is_branch_dependant = true;
|
||||
*+exp = { var, exp->size() };
|
||||
exp = symbolic::expression{ var, exp->size() };
|
||||
}
|
||||
}, true, false );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace vtil
|
|||
//
|
||||
else
|
||||
{
|
||||
fassert( op.is_immediate() );
|
||||
dassert( op.is_immediate() );
|
||||
return { op.imm().i64, op.imm().bit_count };
|
||||
}
|
||||
};
|
||||
|
|
@ -186,7 +186,7 @@ namespace vtil
|
|||
|
||||
// Operand 0 should always be the result for this class.
|
||||
//
|
||||
fassert( ins.base->operand_types[ 0 ] >= operand_type::write );
|
||||
dassert( ins.base->operand_types[ 0 ] >= operand_type::write );
|
||||
return vm_exit_reason::none;
|
||||
}
|
||||
// If NOP:
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ namespace vtil::format
|
|||
template<typename T>
|
||||
static auto as_string( const T& x );
|
||||
template<typename T>
|
||||
concept StringConvertible = requires( T v ) { !is_specialization_v<type_tag, decltype( as_string( v ) )>; };
|
||||
concept StringConvertible = requires( std::string res, T v ) { res = as_string( v ); };
|
||||
|
||||
template<typename T>
|
||||
__forceinline static auto as_string( const T& x )
|
||||
|
|
@ -190,7 +190,7 @@ namespace vtil::format
|
|||
}
|
||||
else if constexpr ( std::is_same_v<base_type, bool> )
|
||||
{
|
||||
return std::string{ x ? "true" : "false" };
|
||||
return x ? "true"s : "false"s;
|
||||
}
|
||||
else if constexpr ( StdStringConvertible<T> )
|
||||
{
|
||||
|
|
@ -248,7 +248,7 @@ namespace vtil::format
|
|||
}();
|
||||
|
||||
if constexpr ( std::tuple_size_v<base_type> == 0 )
|
||||
return "{}";
|
||||
return "{}"s;
|
||||
else if constexpr ( is_tuple_str_cvtable )
|
||||
{
|
||||
std::string res = std::apply( [ ] ( auto&&... args ) {
|
||||
|
|
@ -265,7 +265,7 @@ namespace vtil::format
|
|||
if ( x.has_value() )
|
||||
return as_string( x.value() );
|
||||
else
|
||||
return std::string{ "nullopt" };
|
||||
return "nullopt"s;
|
||||
}
|
||||
else return type_tag<T>{};
|
||||
}
|
||||
|
|
@ -277,11 +277,11 @@ namespace vtil::format
|
|||
}
|
||||
else if constexpr ( Iterable<T> )
|
||||
{
|
||||
if constexpr ( StringConvertible<decltype( *std::begin( x ) )> )
|
||||
if constexpr ( StringConvertible<iterator_value_type_t<T>> )
|
||||
{
|
||||
std::string items = {};
|
||||
for ( auto&& entry : x )
|
||||
items += as_string( entry ) + ", ";
|
||||
items += as_string( entry ) + ", "s;
|
||||
if ( !items.empty() ) items.resize( items.size() - 2 );
|
||||
return "{" + items + "}";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include <cstdarg>
|
||||
#include "formatting.hpp"
|
||||
#include "../util/intrinsics.hpp"
|
||||
#include "../util/literals.hpp"
|
||||
|
|
@ -176,68 +177,84 @@ namespace vtil::logger
|
|||
|
||||
// Main function used when logging.
|
||||
//
|
||||
namespace impl
|
||||
{
|
||||
template<bool has_args>
|
||||
static int log_w( FILE* dst, console_color color, const char* fmt, ... )
|
||||
{
|
||||
// Hold the lock for the critical section guarding ::log.
|
||||
//
|
||||
std::lock_guard g( logger_state );
|
||||
|
||||
// Do not execute if logs are disabled.
|
||||
//
|
||||
if ( logger_state.mute ) return 0;
|
||||
|
||||
// If we should pad this output:
|
||||
//
|
||||
int out_cnt = 0;
|
||||
if ( logger_state.padding > 0 )
|
||||
{
|
||||
// If it was not carried from previous:
|
||||
//
|
||||
if ( int pad_by = logger_state.padding - logger_state.padding_carry )
|
||||
{
|
||||
for ( int i = 0; i < pad_by; i++ )
|
||||
{
|
||||
if ( ( i + 1 ) == pad_by )
|
||||
{
|
||||
out_cnt += fprintf( dst, "%*c", log_padding_step - 1, ' ' );
|
||||
if ( fmt[ 0 ] == ' ' ) putchar( log_padding_c );
|
||||
}
|
||||
else
|
||||
{
|
||||
out_cnt += fprintf( dst, "%*c%c", log_padding_step - 1, ' ', log_padding_c );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set or clear the carry for next.
|
||||
//
|
||||
if ( fmt[ strlen( fmt ) - 1 ] == '\n' )
|
||||
logger_state.padding_carry = 0;
|
||||
else
|
||||
logger_state.padding_carry = logger_state.padding;
|
||||
}
|
||||
|
||||
// Set to requested color and redirect to printf.
|
||||
//
|
||||
set_color( color );
|
||||
|
||||
// If string literal with no parameters, use puts instead.
|
||||
//
|
||||
if ( has_args )
|
||||
{
|
||||
va_list args;
|
||||
va_start( args, fmt );
|
||||
out_cnt += vfprintf( dst, fmt, args );
|
||||
va_end( args );
|
||||
}
|
||||
else
|
||||
{
|
||||
out_cnt += fputs( fmt, dst );
|
||||
}
|
||||
|
||||
// Reset to defualt color.
|
||||
//
|
||||
set_color( CON_DEF );
|
||||
return out_cnt;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... params>
|
||||
static int log( console_color color, const char* fmt, params&&... ps )
|
||||
{
|
||||
// Hold the lock for the critical section guarding ::log.
|
||||
//
|
||||
std::lock_guard g( logger_state );
|
||||
|
||||
// Do not execute if logs are disabled.
|
||||
//
|
||||
if ( logger_state.mute ) return 0;
|
||||
|
||||
// If we should pad this output:
|
||||
//
|
||||
int out_cnt = 0;
|
||||
if ( logger_state.padding > 0 )
|
||||
{
|
||||
// If it was not carried from previous:
|
||||
//
|
||||
if ( int pad_by = logger_state.padding - logger_state.padding_carry )
|
||||
{
|
||||
for ( int i = 0; i < pad_by; i++ )
|
||||
{
|
||||
if ( ( i + 1 ) == pad_by )
|
||||
{
|
||||
out_cnt += fprintf( VTIL_LOGGER_DST, "%*c", log_padding_step - 1, ' ' );
|
||||
if ( fmt[ 0 ] == ' ' ) putchar( log_padding_c );
|
||||
}
|
||||
else
|
||||
{
|
||||
out_cnt += fprintf( VTIL_LOGGER_DST, "%*c%c", log_padding_step - 1, ' ', log_padding_c );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set or clear the carry for next.
|
||||
//
|
||||
if ( fmt[ strlen( fmt ) - 1 ] == '\n' )
|
||||
logger_state.padding_carry = 0;
|
||||
else
|
||||
logger_state.padding_carry = logger_state.padding;
|
||||
}
|
||||
|
||||
// Set to requested color and redirect to printf.
|
||||
//
|
||||
set_color( color );
|
||||
|
||||
// If string literal with no parameters, use puts instead.
|
||||
//
|
||||
if ( sizeof...( ps ) == 0 )
|
||||
out_cnt += fputs( fmt, VTIL_LOGGER_DST );
|
||||
else
|
||||
out_cnt += fprintf( VTIL_LOGGER_DST, fmt, format::fix_parameter<params>( std::forward<params>( ps ) )... );
|
||||
|
||||
// Reset to defualt color.
|
||||
//
|
||||
set_color( CON_DEF );
|
||||
return out_cnt;
|
||||
return impl::log_w<sizeof...( params ) != 0>( VTIL_LOGGER_DST, color, fmt, format::fix_parameter<params>( std::forward<params>( ps ) )... );
|
||||
}
|
||||
template<console_color color = CON_DEF, typename... params>
|
||||
static int log( const char* fmt, params&&... ps )
|
||||
{
|
||||
return log( color, fmt, std::forward<params>( ps )... );
|
||||
return impl::log_w<sizeof...( params ) != 0>( VTIL_LOGGER_DST, color, fmt, format::fix_parameter<params>( std::forward<params>( ps ) )... );
|
||||
}
|
||||
|
||||
// Prints a warning message.
|
||||
|
|
|
|||
|
|
@ -42,33 +42,6 @@ namespace vtil
|
|||
{
|
||||
namespace impl
|
||||
{
|
||||
template<typename... params> struct first_of { using type = std::tuple_element_t<0, std::tuple<params...>>; };
|
||||
template<> struct first_of<> { using type = void; };
|
||||
|
||||
template<typename... params>
|
||||
using first_of_t = typename first_of<params...>::type;
|
||||
|
||||
template<typename T, typename... params>
|
||||
static constexpr bool should_invoke_constructor()
|
||||
{
|
||||
// Constructor should be always invoked if we have more than one parameter and
|
||||
// never if we have zero parameters.
|
||||
//
|
||||
if constexpr ( sizeof...( params ) != 1 )
|
||||
{
|
||||
return sizeof...( params ) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invoke if not equal to the reference type.
|
||||
//
|
||||
return !std::is_base_of_v<T, std::remove_cvref_t<first_of_t<params...>>>;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename... params>
|
||||
using enable_if_constructor = typename std::enable_if_t<should_invoke_constructor<T, params...>(), int>;
|
||||
|
||||
template<typename T>
|
||||
inline static T* reloc_const( const T* ptr, const void* src, void* dst )
|
||||
{
|
||||
|
|
@ -87,7 +60,7 @@ namespace vtil
|
|||
// Used to implement shared and extremely fast Copy-on-Write memory.
|
||||
//
|
||||
template<typename T>
|
||||
struct shared_reference
|
||||
struct base_shared_reference
|
||||
{
|
||||
// Declare the object entry and its pool.
|
||||
//
|
||||
|
|
@ -138,25 +111,24 @@ namespace vtil
|
|||
|
||||
// Null reference construction.
|
||||
//
|
||||
constexpr shared_reference() : combined_value( 0 ) {}
|
||||
constexpr shared_reference( std::nullptr_t ) : shared_reference() {}
|
||||
constexpr shared_reference( std::nullopt_t ) : shared_reference() {}
|
||||
constexpr base_shared_reference() : combined_value( 0 ) {}
|
||||
constexpr base_shared_reference( std::nullptr_t ) : base_shared_reference() {}
|
||||
|
||||
// Owning reference constructor.
|
||||
//
|
||||
template<typename... params, impl::enable_if_constructor<shared_reference<T>, params...> = 0>
|
||||
shared_reference( params&&... p )
|
||||
template<typename... Tx> requires ( Constructable<T, Tx...> && sizeof...( Tx ) > 0 )
|
||||
base_shared_reference( Tx&&... p )
|
||||
{
|
||||
combined_value = ( uint64_t ) object_pool::construct
|
||||
(
|
||||
/*Object itself*/ T( std::forward<params>( p )... ),
|
||||
/*Object itself*/ T( std::forward<Tx>( p )... ),
|
||||
/*Reference counter*/ 1
|
||||
);
|
||||
}
|
||||
|
||||
// Shared reference constructor.
|
||||
//
|
||||
shared_reference( const shared_reference& ref )
|
||||
base_shared_reference( const base_shared_reference& ref )
|
||||
: combined_value( ref.combined_value )
|
||||
{
|
||||
// If object is null, return.
|
||||
|
|
@ -173,7 +145,7 @@ namespace vtil
|
|||
else
|
||||
inc_ref( get_entry() );
|
||||
}
|
||||
shared_reference& operator=( const shared_reference& o )
|
||||
base_shared_reference& operator=( const base_shared_reference& o )
|
||||
{
|
||||
// If object is null, reset and return.
|
||||
//
|
||||
|
|
@ -225,15 +197,44 @@ namespace vtil
|
|||
|
||||
// Construction and assignment operator for rvalue references.
|
||||
//
|
||||
shared_reference( shared_reference&& ref )
|
||||
base_shared_reference( base_shared_reference&& ref )
|
||||
: combined_value( std::exchange( ref.combined_value, 0 ) ) {}
|
||||
shared_reference& operator=( shared_reference&& o )
|
||||
base_shared_reference& operator=( base_shared_reference&& o )
|
||||
{
|
||||
uint64_t value = std::exchange( o.combined_value, 0 );
|
||||
reset().combined_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Assignment of value.
|
||||
//
|
||||
template<typename Tv> requires Constructable<T, Tv>
|
||||
base_shared_reference& operator=( Tv&& value )
|
||||
{
|
||||
// If we have valid memory:
|
||||
//
|
||||
if ( combined_value )
|
||||
{
|
||||
// If it's unique memory, move over it and return, otherwise dereference.
|
||||
//
|
||||
if ( is_temporary() || get_ref( get_entry() ) == 1 )
|
||||
{
|
||||
*( T* ) pointer = std::forward<Tv>( value );
|
||||
return *this;
|
||||
}
|
||||
dec_ref( get_entry() );
|
||||
}
|
||||
|
||||
// Construct a new object and return.
|
||||
//
|
||||
combined_value = ( uint64_t ) object_pool::construct
|
||||
(
|
||||
/*Object itself*/ std::forward<Tv>( value ),
|
||||
/*Reference counter*/ 1
|
||||
);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Gets object entry.
|
||||
//
|
||||
constexpr object_entry* get_entry() const { dassert( !is_temporary() ); return _entry; }
|
||||
|
|
@ -292,8 +293,8 @@ namespace vtil
|
|||
|
||||
// Basic comparison operators are redirected to the pointer type.
|
||||
//
|
||||
constexpr bool operator==( const shared_reference& o ) const { return combined_value == o.combined_value; }
|
||||
constexpr bool operator<( const shared_reference& o ) const { return combined_value < o.combined_value; }
|
||||
constexpr bool operator==( const base_shared_reference& o ) const { return combined_value == o.combined_value; }
|
||||
constexpr bool operator<( const base_shared_reference& o ) const { return combined_value < o.combined_value; }
|
||||
|
||||
// Redirect pointer and dereferencing operator to the reference and cast to const-qualified equivalent.
|
||||
//
|
||||
|
|
@ -312,7 +313,7 @@ namespace vtil
|
|||
|
||||
// Resets the reference to nullptr.
|
||||
//
|
||||
__forceinline shared_reference& reset()
|
||||
__forceinline base_shared_reference& reset()
|
||||
{
|
||||
// If non-temporary and non-null, decrement reference count, if
|
||||
// it reaches 0, destroy the object and deallocate.
|
||||
|
|
@ -328,9 +329,16 @@ namespace vtil
|
|||
|
||||
// Constructor invokes reset.
|
||||
//
|
||||
~shared_reference() { reset(); }
|
||||
~base_shared_reference() { reset(); }
|
||||
};
|
||||
|
||||
// Can be overloaded to implement customization.
|
||||
//
|
||||
template<typename T, typename = void>
|
||||
struct specialized_shared_reference { using type = base_shared_reference<T>; };
|
||||
template<typename T>
|
||||
using shared_reference = typename specialized_shared_reference<T>::type;
|
||||
|
||||
// Weak references are used to store shared references without implying
|
||||
// ownership. This class should not be used together with temporaries.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -123,12 +123,17 @@ namespace vtil
|
|||
//
|
||||
else if constexpr ( Iterable<const T&> )
|
||||
{
|
||||
hash_t hash = {};
|
||||
size_t i = 0;
|
||||
for ( const auto& entry : value )
|
||||
hash = combine_hash( hash, hasher<std::decay_t<decltype( entry )>>{}( entry ) ), i++;
|
||||
hash.add_bytes( sizeof( T ) + i );
|
||||
return hash;
|
||||
using value_type = std::decay_t<iterator_value_type_t<const T&>>;
|
||||
|
||||
if constexpr ( !std::is_void_v<decltype( hasher<value_type>{}( std::declval<value_type&>() ) ) > )
|
||||
{
|
||||
hash_t hash = {};
|
||||
size_t i = 0;
|
||||
for ( const auto& entry : value )
|
||||
hash = combine_hash( hash, hasher<value_type>{}( entry ) ), i++;
|
||||
hash.add_bytes( sizeof( T ) + i );
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
// If hash, combine with default seed.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -73,34 +73,6 @@
|
|||
#pragma warning(disable: 4305)
|
||||
namespace vtil
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
// Applies type modifier over each element in pair/tuple.
|
||||
//
|
||||
template<template<typename> typename F, typename T>
|
||||
struct apply_each { using type = F<T>; };
|
||||
template<template<typename> typename F, typename... T>
|
||||
struct apply_each<F, std::pair<T...>> { using type = std::pair<F<T>...>; };
|
||||
template<template<typename> typename F, typename... T>
|
||||
struct apply_each<F, std::tuple<T...>> { using type = std::tuple<F<T>...>; };
|
||||
template<template<typename> typename F, typename T>
|
||||
using apply_each_t = typename apply_each<F, T>::type;
|
||||
};
|
||||
|
||||
// Mask of requested reducable auto declarations.
|
||||
//
|
||||
enum reducable_auto_decl_id : uint8_t
|
||||
{
|
||||
reducable_none = 0x00,
|
||||
reducable_equ = 1 << 0,
|
||||
reducable_nequ = 1 << 1,
|
||||
reducable_leq = 1 << 2,
|
||||
reducable_greq = 1 << 3,
|
||||
reducable_less = 1 << 4,
|
||||
reducable_greater = 1 << 5,
|
||||
reducable_all = 0xFF,
|
||||
};
|
||||
|
||||
// Reducable tag let's us check if a type is reducable without having
|
||||
// to template for proxied type or the auto-decl flags.
|
||||
//
|
||||
|
|
@ -113,7 +85,7 @@ namespace vtil
|
|||
|
||||
// The main definition of the helper:
|
||||
//
|
||||
template<typename T, uint8_t flags = reducable_all>
|
||||
template<typename T>
|
||||
struct reducable : reducable_tag_t
|
||||
{
|
||||
protected:
|
||||
|
|
@ -132,18 +104,9 @@ namespace vtil
|
|||
public:
|
||||
// Define basic comparison operators using std::tuple.
|
||||
//
|
||||
template<std::enable_if_t<( flags & reducable_equ ) != 0, int> = 0>
|
||||
__forceinline constexpr auto operator==( const T& other ) const { return &other == this || reduce_proxy( ( T& ) *this ) == reduce_proxy( other ); }
|
||||
template<std::enable_if_t<( flags & reducable_nequ ) != 0, int> = 0>
|
||||
__forceinline constexpr auto operator!=( const T& other ) const { return &other != this && reduce_proxy( ( T& ) *this ) != reduce_proxy( other ); }
|
||||
template<std::enable_if_t<( flags & reducable_leq ) != 0, int> = 0>
|
||||
__forceinline constexpr auto operator<=( const T& other ) const { return &other == this || reduce_proxy( ( T& ) *this ) <= reduce_proxy( other ); }
|
||||
template<std::enable_if_t<( flags & reducable_greq ) != 0, int> = 0>
|
||||
__forceinline constexpr auto operator>=( const T& other ) const { return &other == this || reduce_proxy( ( T& ) *this ) >= reduce_proxy( other ); }
|
||||
template<std::enable_if_t<( flags & reducable_less ) != 0, int> = 0>
|
||||
__forceinline constexpr auto operator< ( const T& other ) const { return &other != this && reduce_proxy( ( T& ) *this ) < reduce_proxy( other ); }
|
||||
template<std::enable_if_t<( flags & reducable_greater ) != 0, int> = 0>
|
||||
__forceinline constexpr auto operator> ( const T& other ) const { return &other != this && reduce_proxy( ( T& ) *this ) > reduce_proxy( other ); }
|
||||
__forceinline constexpr auto operator==( const T& other ) const { return &other == this || reduce_proxy( ( const T& ) *this ) == reduce_proxy( other ); }
|
||||
__forceinline constexpr auto operator!=( const T& other ) const { return &other != this && reduce_proxy( ( const T& ) *this ) != reduce_proxy( other ); }
|
||||
__forceinline constexpr auto operator< ( const T& other ) const { return &other != this && reduce_proxy( ( const T& ) *this ) > reduce_proxy( other ); }
|
||||
|
||||
// Define VTIL hash using a simple VTIL tuple hasher.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -149,10 +149,20 @@ namespace vtil
|
|||
template <template<typename...> typename Tmp, typename T>
|
||||
concept Specialization = is_specialization_v<Tmp, T>;
|
||||
template<typename T, typename... Args>
|
||||
concept Constructable = requires { T( std::declval<Args>()... ); };
|
||||
concept Constructable = requires( Args&&... a ) { T( a... ); };
|
||||
template<typename T, typename X>
|
||||
concept Assignable = requires( T r, X v ) { r = v; };
|
||||
|
||||
// Comparison traits.
|
||||
//
|
||||
template<typename T, typename O> concept ThreeWayComparable = requires( T&& a, O&& b ) { a <=> b; };
|
||||
template<typename T, typename O> concept LessEqualComparable = requires( T&& a, O&& b ) { a <= b; };
|
||||
template<typename T, typename O> concept LessComparable = requires( T&& a, O&& b ) { a < b; };
|
||||
template<typename T, typename O> concept EqualComparable = requires( T&& a, O&& b ) { a == b; };
|
||||
template<typename T, typename O> concept NotEqualComparable = requires( T&& a, O&& b ) { a != b; };
|
||||
template<typename T, typename O> concept GreatComparable = requires( T&& a, O&& b ) { a > b; };
|
||||
template<typename T, typename O> concept GreatEqualComparable = requires( T&& a, O&& b ) { a >= b; };
|
||||
|
||||
// Functor traits.
|
||||
//
|
||||
template<typename T, typename Ret, typename... Args>
|
||||
|
|
@ -207,7 +217,7 @@ namespace vtil
|
|||
// Atomicity-related traits.
|
||||
//
|
||||
template<typename T>
|
||||
concept Lockable = requires( T & x ) { x.lock(); x.unlock(); };
|
||||
concept Lockable = requires( T& x ) { x.lock(); x.unlock(); };
|
||||
template<typename T>
|
||||
concept Atomic = is_specialization_v<std::atomic, T>;
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ namespace vtil::analysis
|
|||
branch_targets.emplace_back( cvt_operand( 0 ) );
|
||||
is_branch_real = true;
|
||||
is_branch_exiting = ins.base == &ins::vexit;
|
||||
branch_cc = nullptr;
|
||||
branch_cc.reset();
|
||||
return vm_exit_reason::stream_end;
|
||||
}
|
||||
// If unconditional jump:
|
||||
|
|
@ -153,7 +153,7 @@ namespace vtil::analysis
|
|||
{
|
||||
branch_targets.emplace_back( cvt_operand( 0 ) );
|
||||
is_branch_real = false;
|
||||
branch_cc = nullptr;
|
||||
branch_cc.reset();
|
||||
return vm_exit_reason::stream_end;
|
||||
}
|
||||
// If conditional jump:
|
||||
|
|
@ -351,12 +351,12 @@ namespace vtil::analysis
|
|||
if ( exp_approx == approx )
|
||||
{
|
||||
if ( pexp->equals( ccexp ) )
|
||||
*+pexp = { expected_value, 1 };
|
||||
pexp = symbolic::expression{ expected_value, 1 };
|
||||
}
|
||||
else if ( inv_approx == approx )
|
||||
{
|
||||
if ( pexp->equals( inv_cc ) )
|
||||
*+pexp = { !expected_value, 1 };
|
||||
pexp = symbolic::expression{ !expected_value, 1 };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ namespace vtil::optimizer::aux
|
|||
{
|
||||
auto& var = ex->uid.get<symbolic::variable>();
|
||||
if ( var.is_register() && var.reg() == REG_IMGBASE )
|
||||
*+ex = { 0, ex->size() };
|
||||
ex = symbolic::expression{ 0, ex->size() };
|
||||
}
|
||||
}, true, false ).simplify( true );
|
||||
|
||||
|
|
@ -393,12 +393,12 @@ namespace vtil::optimizer::aux
|
|||
{
|
||||
if ( exp->is_identical( *cnd_out ) )
|
||||
{
|
||||
*+exp = symbolic::expression{ state, exp->size() };
|
||||
exp = symbolic::expression{ state, exp->size() };
|
||||
confirmed |= !state;
|
||||
}
|
||||
else if ( exp->is_identical( ~cnd_out ) )
|
||||
{
|
||||
*+exp = symbolic::expression{ state ^ 1, exp->size() };
|
||||
exp = symbolic::expression{ state ^ 1, exp->size() };
|
||||
confirmed |= !state;
|
||||
}
|
||||
}
|
||||
|
|
@ -420,7 +420,7 @@ namespace vtil::optimizer::aux
|
|||
|
||||
dst->enumerate( explore_cc_space );
|
||||
if ( cnd_out ) dst.transform( transform_cc );
|
||||
if ( !confirmed ) cnd_out = {};
|
||||
if ( !confirmed ) cnd_out.reset();
|
||||
};
|
||||
|
||||
symbolic::expression::reference cc = {};
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ namespace vtil::optimizer
|
|||
if ( !( math::fill( vctx.linear_store[ n ].size(), n ) & read_mask ) )
|
||||
{
|
||||
math::bit_reset( vctx.bitmap, n );
|
||||
vctx.linear_store[ n ] = nullptr;
|
||||
vctx.linear_store[ n ].reset();
|
||||
cnt++;
|
||||
}
|
||||
// Otherwise, or with value mask.
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ namespace vtil::symbolic::directive
|
|||
// and type of expressions it can match.
|
||||
//
|
||||
const char* id = nullptr;
|
||||
int lookup_index = 0;
|
||||
uint32_t lookup_index = 0xFFFFFFFF;
|
||||
matching_type mtype = match_any;
|
||||
|
||||
// Priority hint for transformer.
|
||||
|
|
@ -215,7 +215,7 @@ namespace vtil::symbolic::directive
|
|||
for ( auto [out, idx] : zip( signatures, iindices ) )
|
||||
out = { make_copy( value ).resize( math::narrow_cast<bitcnt_t>( idx + 1 ) ) };
|
||||
}
|
||||
instance( const char* id, int lookup_index, matching_type mtype = match_any ) :
|
||||
instance( const char* id, uint32_t lookup_index, matching_type mtype = match_any ) :
|
||||
id( id ), lookup_index( lookup_index ), mtype( mtype ), num_nodes( 1 ) {}
|
||||
|
||||
// Constructor for directive representing the result of an unary operator.
|
||||
|
|
@ -292,9 +292,9 @@ namespace vtil::symbolic::directive
|
|||
|
||||
// Special variables, one per type:
|
||||
//
|
||||
static const instance V = { "Π", 7, match_variable };
|
||||
static const instance U = { "Σ", 8, match_constant };
|
||||
static const instance Q = { "Ω", 9, match_expression };
|
||||
static const instance V = { "Π", 7, match_variable };
|
||||
static const instance U = { "Σ", 8, match_constant };
|
||||
static const instance Q = { "Ω", 9, match_expression };
|
||||
static const instance W = { "Ψ", 10, match_non_constant };
|
||||
static const instance X = { "Θ", 11, match_non_expression };
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,11 @@ namespace vtil::symbolic::directive
|
|||
// Adds the mapping of a variable to an expression.
|
||||
//
|
||||
bool add( const instance* dir, expression::weak_reference exp )
|
||||
{
|
||||
{
|
||||
// Assert the looked up type is variable.
|
||||
//
|
||||
dassert( dir->op == math::operator_id::invalid && !dir->is_constant() );
|
||||
|
||||
// If it's the first time this variable is being used:
|
||||
//
|
||||
if ( !lookup_table[ dir->lookup_index ] )
|
||||
|
|
@ -78,7 +82,7 @@ namespace vtil::symbolic::directive
|
|||
{
|
||||
// Assert the looked up type is variable.
|
||||
//
|
||||
fassert( dir->op == math::operator_id::invalid && !dir->is_constant() );
|
||||
dassert( dir->op == math::operator_id::invalid && !dir->is_constant() );
|
||||
|
||||
// Translate using the lookup table.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ namespace vtil::symbolic
|
|||
// Return the unknown mask.
|
||||
//
|
||||
|
||||
return ( *+exp = expression{ exp->unknown_mask(), exp->size() }, exp );
|
||||
return exp = expression{ exp->unknown_mask(), exp->size() };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -207,7 +207,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// Return the unknown mask.
|
||||
//
|
||||
return ( *+exp = expression{ exp->known_one(), exp->size() }, exp );
|
||||
return exp = expression{ exp->known_one(), exp->size() };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -219,7 +219,7 @@ namespace vtil::symbolic
|
|||
{
|
||||
// Return the unknown mask.
|
||||
//
|
||||
return ( *+exp = expression{ exp->known_zero(), exp->size() }, exp );
|
||||
return exp = expression{ exp->known_zero(), exp->size() };
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ namespace vtil::symbolic
|
|||
//
|
||||
if ( exp_new->is_constant() )
|
||||
{
|
||||
exp_new = { *exp_new->value.get(), exp->size() };
|
||||
exp_new = expression{ *exp_new->value.get(), exp->size() };
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -351,7 +351,7 @@ namespace vtil::symbolic
|
|||
//
|
||||
else
|
||||
{
|
||||
*+rhs = new_size;
|
||||
rhs = expression{ new_size, 8 };
|
||||
return update( false );
|
||||
}
|
||||
break;
|
||||
|
|
@ -361,7 +361,7 @@ namespace vtil::symbolic
|
|||
case math::operator_id::cast:
|
||||
// Signed cast should not be used to shrink.
|
||||
//
|
||||
fassert( lhs->size() <= rhs->get().value() );
|
||||
dassert( lhs->size() <= rhs->get().value() );
|
||||
|
||||
// If sizes match, escape cast operator.
|
||||
//
|
||||
|
|
@ -373,7 +373,7 @@ namespace vtil::symbolic
|
|||
//
|
||||
else if ( signed_cast )
|
||||
{
|
||||
*+rhs = new_size;
|
||||
rhs = expression{ new_size, 8 };
|
||||
return update( false );
|
||||
}
|
||||
// Else, convert to unsigned cast since top bits will be zero.
|
||||
|
|
@ -490,7 +490,7 @@ namespace vtil::symbolic
|
|||
//
|
||||
if ( ( is_lazy || auto_simplify ) && value.is_known() )
|
||||
{
|
||||
lhs = {}; rhs = {};
|
||||
lhs.reset(); rhs.reset();
|
||||
op = math::operator_id::invalid;
|
||||
is_lazy = false;
|
||||
return update( false );
|
||||
|
|
@ -531,7 +531,7 @@ namespace vtil::symbolic
|
|||
//
|
||||
if ( ( is_lazy || auto_simplify ) && value.is_known() )
|
||||
{
|
||||
lhs = {}; rhs = {};
|
||||
lhs.reset(); rhs.reset();
|
||||
op = math::operator_id::invalid;
|
||||
is_lazy = false;
|
||||
return update( false );
|
||||
|
|
@ -718,7 +718,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.
|
||||
//
|
||||
reference ref = ( reference&& ) make_local_reference( this );
|
||||
reference ref = make_local_reference( this );
|
||||
simplify_expression( ref, prettify );
|
||||
|
||||
// Set the simplifier hint to indicate skipping further calls to simplify_expression.
|
||||
|
|
@ -816,8 +816,8 @@ namespace vtil::symbolic
|
|||
|
||||
// Simplify both expressions.
|
||||
//
|
||||
expression::reference a = make_local_reference( this );
|
||||
expression::reference b = make_local_reference( &other );
|
||||
reference a = make_local_reference( this );
|
||||
reference b = make_local_reference( &other );
|
||||
a.simplify();
|
||||
b.simplify();
|
||||
|
||||
|
|
|
|||
|
|
@ -44,41 +44,25 @@
|
|||
|
||||
// Allow expression::reference to be used with expression type directly as operable.
|
||||
//
|
||||
namespace vtil::symbolic { struct expression; struct expression_reference; };
|
||||
namespace vtil::symbolic { struct expression; struct expression_reference; struct expression_delegate; };
|
||||
namespace vtil::math { template<> struct resolve_alias<symbolic::expression_reference> { using type = symbolic::expression; }; };
|
||||
|
||||
// Specialize reference.
|
||||
//
|
||||
namespace vtil
|
||||
{
|
||||
template<>
|
||||
struct specialized_shared_reference<symbolic::expression, void>
|
||||
{
|
||||
using type = symbolic::expression_reference;
|
||||
};
|
||||
};
|
||||
|
||||
namespace vtil::symbolic
|
||||
{
|
||||
struct expression;
|
||||
|
||||
// Expression delegates used to implement copyless write detection
|
||||
// in case the reference is already owning.
|
||||
//
|
||||
struct expression_delegate
|
||||
{
|
||||
shared_reference<expression>& ref;
|
||||
bool dirty;
|
||||
|
||||
expression_delegate( shared_reference<expression>& ref ) : ref( ref ), dirty( false ) {}
|
||||
expression_delegate( const expression_delegate& ) = delete;
|
||||
expression_delegate& operator=( const expression_delegate& ) = delete;
|
||||
|
||||
template<typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, expression_delegate>, int> = 0>
|
||||
expression_delegate& operator=( T&& value )
|
||||
{
|
||||
ref = std::forward<T>( value );
|
||||
dirty = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const expression* operator->() const { return ref.get(); }
|
||||
const expression& operator*() const { return *ref.get(); }
|
||||
expression* operator+() { dirty = 1; return ref.own(); }
|
||||
};
|
||||
|
||||
// Expression references.
|
||||
//
|
||||
struct expression_reference : shared_reference<expression>
|
||||
struct expression_reference : base_shared_reference<expression>
|
||||
{
|
||||
// Declare hasher and equivalence checker.
|
||||
//
|
||||
|
|
@ -99,21 +83,25 @@ namespace vtil::symbolic
|
|||
|
||||
// Forward operators and constructor.
|
||||
//
|
||||
template<typename... Tx>
|
||||
expression_reference( Tx&&... args )
|
||||
: shared_reference( std::forward<Tx>( args )...) {}
|
||||
template<typename... Tx> requires ( Constructable<expression, Tx...> && sizeof...(Tx) > 0 )
|
||||
expression_reference( Tx&&... args ) : base_shared_reference( std::forward<Tx>( args )... ) {}
|
||||
constexpr expression_reference() {}
|
||||
constexpr expression_reference( std::nullptr_t ) {}
|
||||
|
||||
using shared_reference::operator bool;
|
||||
using shared_reference::operator*;
|
||||
using shared_reference::operator+;
|
||||
using shared_reference::operator->;
|
||||
expression_reference( expression_reference&& o ) : base_shared_reference( std::move( o ) ) {}
|
||||
expression_reference( const expression_reference& o ) : base_shared_reference( o ) {}
|
||||
expression_reference& operator=( expression_reference&& o ) { base_shared_reference::operator=( std::move( o ) ); return *this; }
|
||||
expression_reference& operator=( const expression_reference& o ) { base_shared_reference::operator=( o ); return *this; }
|
||||
|
||||
// Basic comparison operators are redirected to the pointer type.
|
||||
//
|
||||
bool operator<( const shared_reference& o ) const { return combined_value < o.combined_value; }
|
||||
bool operator==( const shared_reference& o ) const { return combined_value == o.combined_value; }
|
||||
bool operator<( const expression_reference& o ) const { return combined_value < o.combined_value; }
|
||||
bool operator==( const expression_reference& o ) const { return combined_value == o.combined_value; }
|
||||
template<typename Tv> requires Constructable<expression, Tv>
|
||||
expression_reference& operator=( Tv&& o ) { base_shared_reference::operator=( std::forward<Tv>( o ) ); return *this; }
|
||||
|
||||
using base_shared_reference::operator bool;
|
||||
using base_shared_reference::operator*;
|
||||
using base_shared_reference::operator+;
|
||||
using base_shared_reference::operator->;
|
||||
using base_shared_reference::operator<;
|
||||
using base_shared_reference::operator==;
|
||||
|
||||
// Implement some helpers to conditionally copy.
|
||||
//
|
||||
|
|
@ -178,6 +166,31 @@ namespace vtil::symbolic
|
|||
}
|
||||
};
|
||||
|
||||
// Expression delegates used to implement copyless write detection
|
||||
// in case the reference is already owning.
|
||||
//
|
||||
struct expression_delegate
|
||||
{
|
||||
expression_reference& ref;
|
||||
bool dirty;
|
||||
|
||||
expression_delegate( expression_reference& ref ) : ref( ref ), dirty( false ) {}
|
||||
expression_delegate( const expression_delegate& ) = delete;
|
||||
expression_delegate& operator=( const expression_delegate& ) = delete;
|
||||
|
||||
template<typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, expression_delegate>, int> = 0>
|
||||
expression_delegate& operator=( T&& value )
|
||||
{
|
||||
ref = std::forward<T>( value );
|
||||
dirty = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const expression* operator->() const { return ref.get(); }
|
||||
const expression& operator*() const { return *ref.get(); }
|
||||
expression* operator+() { dirty = 1; return ref.own(); }
|
||||
};
|
||||
|
||||
// Expression descriptor.
|
||||
//
|
||||
struct expression : math::operable<expression>
|
||||
|
|
|
|||
|
|
@ -515,7 +515,7 @@ namespace vtil::symbolic
|
|||
//
|
||||
if ( exp->value.is_known() )
|
||||
{
|
||||
*+exp = expression{ exp->value.known_one(), exp->value.size() };
|
||||
exp = expression{ exp->value.known_one(), exp->value.size() };
|
||||
#if VTIL_SYMEX_SIMPLIFY_VERBOSE
|
||||
log<CON_CYN>( "= %s [By evaluation]\n", *exp );
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@
|
|||
#define VTIL_SYMEX_LRU_CACHE_SIZE 0x18000
|
||||
#endif
|
||||
#ifndef VTIL_SYMEX_LRU_PRUNE_COEFF
|
||||
#define VTIL_SYMEX_LRU_PRUNE_COEFF 0.2
|
||||
#define VTIL_SYMEX_LRU_PRUNE_COEFF 0.2f
|
||||
#endif
|
||||
#ifndef VTIL_SYMEX_HASH_COLLISION_MAX
|
||||
#define VTIL_SYMEX_HASH_COLLISION_MAX 8
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue