mirror of
https://github.com/vtil-project/VTIL-Core
synced 2026-08-17 08:23:03 -04:00
188 lines
6.3 KiB
C++
188 lines
6.3 KiB
C++
// Copyright (c) 2020 Can Boluk and contributors of the VTIL Project
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// 2. Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// 3. Neither the name of VTIL Project nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
#include <vtil/math>
|
|
#include "context.hpp"
|
|
|
|
namespace vtil::symbolic
|
|
{
|
|
// Checks if the symbolic context contains any writes to the given region described by the register desc.
|
|
//
|
|
bool context::contains( const register_desc& desc ) const
|
|
{
|
|
// If identifier is not in the store, return false.
|
|
//
|
|
auto it = value_map.find( desc );
|
|
if ( it == value_map.end() )
|
|
return false;
|
|
|
|
// Enumerate each bit set within (size+offset, 0]:
|
|
//
|
|
bool found = false;
|
|
math::bit_enum( it->second.bitmap & math::fill( desc.bit_count + desc.bit_offset ), [ & ] ( bitcnt_t i )
|
|
{
|
|
// If value extends into the region, declare found.
|
|
//
|
|
const expression::reference& value = it->second.linear_store[ i ];
|
|
if ( ( value.size() + i ) > desc.bit_offset )
|
|
found = true;
|
|
} );
|
|
return found;
|
|
}
|
|
|
|
// Reads the value of the given region described by the register desc.
|
|
//
|
|
expression::reference context::read( const register_desc& desc, const il_const_iterator& reference_iterator ) const
|
|
{
|
|
// If identifier is not in the store, return default.
|
|
//
|
|
auto it = value_map.find( desc );
|
|
if ( it == value_map.end() )
|
|
return CTX( reference_iterator )[ desc ];
|
|
|
|
// Allocate storage for result and create masks.
|
|
//
|
|
uint64_t known_mask = 0;
|
|
uint64_t read_mask = desc.get_mask();
|
|
expression::reference result = nullptr;
|
|
|
|
// Enumerate each bit set within (size+offset, 0]:
|
|
//
|
|
math::bit_enum( it->second.bitmap & math::fill( desc.bit_count + desc.bit_offset ), [ & ] ( bitcnt_t i )
|
|
{
|
|
// If value extends into the region:
|
|
//
|
|
const expression::reference& value = it->second.linear_store[ i ];
|
|
if ( ( value.size() + i ) > desc.bit_offset )
|
|
{
|
|
// Set known mask.
|
|
//
|
|
known_mask |= math::fill( value.size(), i );
|
|
|
|
// Adjust the value.
|
|
//
|
|
expression::reference adjusted = value;
|
|
if ( i > desc.bit_offset ) adjusted.resize( desc.bit_count ) <<= ( i - desc.bit_offset );
|
|
else if ( i < desc.bit_offset ) adjusted >>= ( desc.bit_offset - i ), adjusted.resize( desc.bit_count );
|
|
else adjusted.resize( desc.bit_count );
|
|
|
|
// Append to the result.
|
|
//
|
|
if ( result ) result |= std::move( adjusted );
|
|
else result = std::move( adjusted );
|
|
}
|
|
} );
|
|
|
|
// If no bits set in known mask, return default.
|
|
//
|
|
if ( !known_mask )
|
|
return CTX( reference_iterator )[ desc ];
|
|
|
|
// If all bits set in known mask, return as is.
|
|
//
|
|
if ( ( known_mask & read_mask ) == read_mask )
|
|
return result;
|
|
|
|
// Or with the bits that we do not know and return.
|
|
//
|
|
return result | ( variable{ reference_iterator, desc.select( 64, 0 ) }.to_expression() & ( read_mask & ~known_mask ) ) >> desc.bit_offset;
|
|
}
|
|
|
|
// Writes the given value to the region described by the register desc.
|
|
//
|
|
void context::write( const register_desc& desc, expression::reference value )
|
|
{
|
|
// Find the register in the map and determine limit of the descriptor.
|
|
//
|
|
auto& context = value_map[ desc ];
|
|
bitcnt_t reg_end = desc.bit_count + desc.bit_offset;
|
|
|
|
// Push left (size+offset, offset].
|
|
//
|
|
math::bit_enum( context.bitmap & desc.get_mask(), [ & ] ( bitcnt_t i )
|
|
{
|
|
// Reset the bit, and move the value.
|
|
//
|
|
expression::reference stored_value = std::exchange( context.linear_store[ i ], nullptr );
|
|
bitcnt_t value_end = stored_value.size() + i;
|
|
math::bit_reset( context.bitmap, i );
|
|
|
|
// If value extends beyond the region we're overwriting:
|
|
//
|
|
if ( value_end > reg_end )
|
|
{
|
|
// Shift the value and place it at the border.
|
|
//
|
|
auto& ref = context.linear_store[ reg_end ];
|
|
dassert( !ref );
|
|
|
|
ref = std::move( stored_value ) >> ( reg_end - i );
|
|
ref.resize( value_end - reg_end );
|
|
math::bit_set( context.bitmap, reg_end );
|
|
}
|
|
} );
|
|
|
|
// Push right (offset, 0].
|
|
//
|
|
math::bit_enum( context.bitmap & math::fill( desc.bit_offset, 0 ), [ & ] ( bitcnt_t i )
|
|
{
|
|
// If value extends into the region we're overwriting:
|
|
//
|
|
expression::reference& stored_value = context.linear_store[ i ];
|
|
bitcnt_t value_end = stored_value.size() + i;
|
|
|
|
if ( value_end > desc.bit_offset )
|
|
{
|
|
// If value extends beyond the region we're overwriting:
|
|
//
|
|
if ( value_end > reg_end )
|
|
{
|
|
auto& ext = context.linear_store[ reg_end ];
|
|
dassert( !ext );
|
|
|
|
ext = stored_value >> ( reg_end - i );
|
|
ext.resize( value_end - reg_end );
|
|
math::bit_set( context.bitmap, reg_end );
|
|
}
|
|
|
|
// Resize the value.
|
|
//
|
|
stored_value.resize( desc.bit_offset - i );
|
|
}
|
|
} );
|
|
|
|
// Write the value.
|
|
//
|
|
value.resize( desc.bit_count );
|
|
|
|
auto& res = context.linear_store[ desc.bit_offset ];
|
|
dassert( !res );
|
|
|
|
res = std::move( value );
|
|
math::bit_set( context.bitmap, desc.bit_offset );
|
|
}
|
|
};
|