mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
483 lines
16 KiB
C++
483 lines
16 KiB
C++
/* State will store states of variables for a function's single execution path.
|
|
It will be used for bit-level symbolic execution to determine values of bits
|
|
of function's return value and symbolic marked arguments.
|
|
Copyright (C) 2022-2026 Free Software Foundation, Inc.
|
|
Contributed by Matevos Mehrabyan <matevosmehrabyan@gmail.com>
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
|
|
#ifndef SYM_EXEC_STATE_H
|
|
#define SYM_EXEC_STATE_H
|
|
|
|
#define MAX_VALUE_SIZE 64
|
|
|
|
#include "sym-exec-expr-is-a-helper.h"
|
|
|
|
/* Struct used for representing values. */
|
|
|
|
struct value {
|
|
private:
|
|
/* bit-vector that represents the value. */
|
|
vec<value_bit *> number;
|
|
|
|
public:
|
|
/* Used for denoting whether the number is unsigned. */
|
|
const bool is_unsigned;
|
|
|
|
/* Constructor for value. The first argument is the size of the bit-vector
|
|
and the second argument is the sign of the number. */
|
|
value (unsigned size, bool is_unsigned);
|
|
|
|
/* Copy constructor for value. */
|
|
value (const value &other);
|
|
|
|
/* Pushes the given bit to the end of the bit-vector. */
|
|
value_bit **push (value_bit *elem);
|
|
|
|
/* Returns pushed bits count. */
|
|
unsigned length () const;
|
|
|
|
/* Returns a reference the last bit. */
|
|
value_bit *&last ();
|
|
|
|
/* Returns the size in bits. */
|
|
unsigned allocated () const;
|
|
|
|
/* Wrapper of vec<..>.exists for the bit-vector. */
|
|
bool exists () const;
|
|
|
|
/* Wrapper of vec<..>::operator[] for the bit-vector. */
|
|
value_bit *&operator[] (unsigned i);
|
|
|
|
/* Assignment operator. If the specified value's size is smaller,
|
|
then 0 constant bit will be assigned to the remaining upper bits. */
|
|
value &operator= (const value &other);
|
|
|
|
/* Wrapper of vec<..>::operator[] const for the bit-vector. */
|
|
value_bit *operator[] (unsigned i) const;
|
|
|
|
/* Destructor for value. */
|
|
~value ();
|
|
|
|
/* Removes given sequence of bits. */
|
|
void free_bits ();
|
|
};
|
|
|
|
|
|
/* Stores states of variables' values on bit-level. */
|
|
|
|
class state {
|
|
typedef void (state::*binary_func) (value *arg1, value *arg2, tree dest);
|
|
typedef value_bit *(*bit_func) (value_bit *bit1, value_bit *bit2);
|
|
typedef value_bit *(*bit_func3) (value_bit *var1, value_bit *var2,
|
|
value_bit **var3);
|
|
typedef void (state::*binary_cond_func) (value *arg1, value *arg2);
|
|
|
|
private:
|
|
|
|
/* Here is stored values by bits of each variable. */
|
|
hash_map<tree, value> var_states;
|
|
|
|
/* Here is stored conditions of symbolic bits. */
|
|
hash_set<bit_expression *> conditions;
|
|
|
|
/* The result of last added condition. */
|
|
condition_status last_cond_status = condition_status::CS_NO_COND;
|
|
|
|
/* Creates value for given constant tree. */
|
|
static value create_val_for_const (tree var, size_t size);
|
|
|
|
/* Checks if sizes of arguments and destination are compatible. */
|
|
bool check_args_compatibility (tree arg1, tree arg2, tree dest);
|
|
|
|
/* Adds equality condition for two values. */
|
|
void add_equal_cond (value *arg1, value *arg2);
|
|
|
|
/* Adds not equal condition for two values. */
|
|
void add_not_equal_cond (value *arg1, value *arg2);
|
|
|
|
/* Adds greater than condition for two values. */
|
|
void add_greater_than_cond (value *arg1, value *arg2);
|
|
|
|
/* Adds less than condition for two values. */
|
|
void add_less_than_cond (value *arg1, value *arg2);
|
|
|
|
/* Adds greater or equal condition for two values. */
|
|
void add_greater_or_equal_cond (value *arg1, value *arg2);
|
|
|
|
/* Adds less or equal condition for two values. */
|
|
void add_less_or_equal_cond (value *arg1, value *arg2);
|
|
|
|
/* Does preprocessing and postprocessing for condition adding.
|
|
Handles value creation for constants and their removement in the end. */
|
|
bool add_binary_cond (tree arg1, tree arg2, binary_cond_func cond_func);
|
|
|
|
/* Constructs expression trees of greater than condition for given values. */
|
|
bit_expression *construct_great_than_cond (value *arg1, value *arg2);
|
|
|
|
/* Constructs expression trees of less than condition for given values. */
|
|
bit_expression *construct_less_than_cond (value *arg1, value *arg2);
|
|
|
|
/* Constructs expression trees of equal condition for given values. */
|
|
bit_expression *construct_equal_cond (value *arg1, value *arg2);
|
|
|
|
/* A wrapper for operations on two bits.
|
|
Operation and operands are passed as arguments. */
|
|
static value_bit *operate_bits (bit_func bit_op, value_bit *bit1,
|
|
value_bit *bit2, value_bit **bit3);
|
|
|
|
/* A wrapper for operations on three bits.
|
|
Operation and operands are passed as arguments. */
|
|
static value_bit *operate_bits (bit_func3 bit_op, value_bit *bit1,
|
|
value_bit *bit2, value_bit **bit3);
|
|
|
|
/* Performs the given operation on passed arguments.
|
|
The result is stored in dest. */
|
|
template<class func>
|
|
void operate (value *arg1, value *arg2, value_bit **bit_arg, tree dest,
|
|
func bit_op);
|
|
|
|
/* Does preprocessing and postprocessing for expressions with tree operands.
|
|
Handles value creation for constant and their removement in the end. */
|
|
bool do_binary_operation (tree arg1, tree arg2, tree dest,
|
|
binary_func bin_func);
|
|
|
|
/* Performs AND operation on given values. The result is stored in dest. */
|
|
void do_and (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Performs OR operation on given values. The result is stored in dest. */
|
|
void do_or (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Performs XOR operation on given values. The result is stored in dest. */
|
|
void do_xor (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Performs shift right operation on given values.
|
|
The result is stored in dest. */
|
|
void do_shift_right (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Performs shift left operation on given values.
|
|
The result is stored in dest. */
|
|
void do_shift_left (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Adds given values. The result is stored in dest. */
|
|
void do_add (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Subtracks second value from the first. The result is stored in dest. */
|
|
void do_sub (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Performs AND operation on two bits. */
|
|
static value_bit *and_two_bits (value_bit *arg1, value_bit *arg2);
|
|
|
|
/* ANDs every bit of the value with var_bit, stroes the result in var1. */
|
|
void and_number_bit (value *var1, value_bit *var_bit);
|
|
|
|
/* Multiplies given values. The result is stored in dest. */
|
|
void do_mul (value *arg1, value *arg2, tree dest);
|
|
|
|
/* Performs AND operation for 2 symbolic_bit operands. */
|
|
static value_bit *and_sym_bits (const value_bit *var1,
|
|
const value_bit *var2);
|
|
|
|
/* Performs AND operation for a symbolic_bit and const_bit operands. */
|
|
static value_bit *and_var_const (const value_bit *var1,
|
|
const bit *const_bit);
|
|
|
|
/* Performs AND operation for 2 constant bit operands. */
|
|
static bit *and_const_bits (const bit *const_bit1, const bit *const_bit2);
|
|
|
|
/* Performs OR operation on two bits. */
|
|
static value_bit *or_two_bits (value_bit *arg1_bit, value_bit *arg2_bit);
|
|
|
|
/* Performs OR operation for 2 symbolic_bit operands. */
|
|
static value_bit *or_sym_bits (const value_bit *var1,
|
|
const value_bit *var2);
|
|
|
|
/* Performs OR operation for a symbolic_bit and a constant bit operands. */
|
|
static value_bit *or_var_const (const value_bit *var1,
|
|
const bit *const_bit);
|
|
|
|
/* Performs OR operation for 2 constant bit operands. */
|
|
static bit *or_const_bits (const bit *const_bit1, const bit *const_bit2);
|
|
|
|
/* Performs complement operation on a bit. */
|
|
static value_bit *complement_a_bit (value_bit *var);
|
|
|
|
/* Performs NOT operation for constant bit. */
|
|
static bit *complement_const_bit (const bit *const_bit);
|
|
|
|
/* Performs NOT operation for symbolic_bit. */
|
|
static value_bit *complement_sym_bit (const value_bit *var);
|
|
|
|
/* Performs XOR operation on two bits. */
|
|
static value_bit *xor_two_bits (value_bit *var1, value_bit *var2);
|
|
|
|
/* Performs XOR operation for 2 symbolic_bit operands. */
|
|
static value_bit *xor_sym_bits (const value_bit *var1,
|
|
const value_bit *var2);
|
|
|
|
/* Performs XOR operation for 2 constant bit operands. */
|
|
static bit *xor_const_bits (const bit *const_bit1, const bit *const_bit2);
|
|
|
|
/* Performs XOR operation for a symbolic_bit and const_bit operands. */
|
|
static value_bit *xor_var_const (const value_bit *var,
|
|
const bit *const_bit);
|
|
|
|
/* Shift_right operation. Case: var2 is a symbolic value. */
|
|
static value_bit *shift_right_sym_bits (value_bit *var1, value_bit *var2);
|
|
|
|
/* Shift_left operation. Case: var2 is a symbolic value. */
|
|
static value_bit *shift_left_sym_bits (value_bit *var1, value_bit *var2);
|
|
|
|
/* Shifts var right by size of shift_value. */
|
|
value *shift_right_by_const (value *var, size_t shift_value);
|
|
|
|
/* Return node which has a const bit child. Traversal is done based
|
|
on safe branching. */
|
|
static void get_parent_with_const_child (value_bit *root,
|
|
bit_expression *&parent,
|
|
bit_expression *&parent_of_parent);
|
|
|
|
/* Checks whether state for variable with specified name already
|
|
exists or not. */
|
|
bool is_declared (tree var);
|
|
|
|
/* Declares given variable if it has not been declared yet. */
|
|
void declare_if_needed (tree var, size_t size);
|
|
|
|
/* Shifts number left by size of shift_value. */
|
|
value *shift_left_by_const (const value *number, size_t shift_value);
|
|
|
|
/* Adds two bits and carry value.
|
|
Resturn result and stores new carry bit in "carry". */
|
|
static value_bit *full_adder (value_bit *var1, value_bit *var2,
|
|
value_bit **carry);
|
|
|
|
/* Returns the additive inverse of the given number. */
|
|
value *additive_inverse (const value *number);
|
|
|
|
/* Adds two values, stores the result in the first one. */
|
|
void add_numbers (value *var1, const value *var2);
|
|
|
|
/* Make a copy of given bits. */
|
|
static vec<value_bit *> *make_copy (vec<value_bit *> *bits);
|
|
|
|
/* Create LFSR value for the reversed CRC. */
|
|
static void create_reversed_lfsr (value &lfsr, const value &crc,
|
|
const value &polynomial);
|
|
|
|
/* Create LFSR value for the forward CRC. */
|
|
static void create_forward_lfsr (value &lfsr, const value &crc,
|
|
const value &polynomial);
|
|
|
|
public:
|
|
/* Default constructor for state. */
|
|
state () = default;
|
|
|
|
/* Destructor for state. */
|
|
~state ();
|
|
|
|
/* Adds an empty state for the given variable. */
|
|
bool decl_var (tree name, unsigned size);
|
|
|
|
/* Copy constructor for state. It copies all variables and conditions
|
|
of the given state. */
|
|
state (const state &s);
|
|
|
|
/* Adds the given variable to state. */
|
|
bool add_var_state (tree var, value *state);
|
|
|
|
/* Remove all states from the states' vector. */
|
|
static void remove_states (vec<state *> *states);
|
|
|
|
/* Remove all states from the states' vector and release the vector. */
|
|
static void clear_states (vec<state *> *states);
|
|
|
|
/* Removes all variables added to the state. */
|
|
void clear_var_states ();
|
|
|
|
/* Removes all conditions added to the state. */
|
|
void clear_conditions ();
|
|
|
|
/* Adds the given condition to the state. */
|
|
bool add_condition (bit_expression *cond);
|
|
|
|
/* Bulk add the given conditions to the state. */
|
|
bool bulk_add_conditions (const hash_set<bit_expression *> &conds);
|
|
|
|
/* Get value of the given variable. */
|
|
value *get_value (tree var);
|
|
|
|
/* Get the value of the tree, which is in the beginning of the var_states. */
|
|
value *get_first_value ();
|
|
|
|
/* Returns the list of conditions in the state. */
|
|
const hash_set<bit_expression *> &get_conditions ();
|
|
|
|
/* Adds a variable with unknown value to state. Such variables are
|
|
represented as sequence of symbolic bits. */
|
|
bool make_symbolic (tree var, unsigned size);
|
|
|
|
/* Returns size of the given variable. */
|
|
unsigned get_var_size (tree var);
|
|
|
|
/* Prints the given value. */
|
|
static void print_value (value *var);
|
|
|
|
/* Prints added conditions. */
|
|
void print_conditions ();
|
|
|
|
/* Returns the number represented by the value. */
|
|
static unsigned HOST_WIDE_INT
|
|
make_number (const value *var);
|
|
|
|
/* Checks if all bits of the given value have constant bit type. */
|
|
static bool is_bit_vector (const value *var);
|
|
|
|
/* Performs the specified operation on passed variables. */
|
|
bool do_operation (tree_code op_code, tree arg1, tree arg2, tree dest);
|
|
|
|
/* Does Assignment. */
|
|
bool do_assign (tree arg, tree dest);
|
|
|
|
/* Assigns pow 2 value. */
|
|
bool do_assign_pow2 (tree dest, unsigned pow);
|
|
|
|
/* Negates given variable. */
|
|
bool do_complement (tree arg, tree dest);
|
|
|
|
/* Adds EQUAL condition of given variables to state. */
|
|
bool add_equal_cond (tree arg1, tree arg2);
|
|
|
|
/* Adds NOT EQUAL condition of given variables to state. */
|
|
bool add_not_equal_cond (tree arg1, tree arg2);
|
|
|
|
/* Adds GREATER THAN condition of given variables to state. */
|
|
bool add_greater_than_cond (tree arg1, tree arg2);
|
|
|
|
/* Adds LESS THAN condition of given variables to state. */
|
|
bool add_less_than_cond (tree arg1, tree arg2);
|
|
|
|
/* Adds GREATER OR EQUAL condition of given variables to state. */
|
|
bool add_greater_or_equal_cond (tree arg1, tree arg2);
|
|
|
|
/* Adds LESS OR EQUAL condition of given variables to state. */
|
|
bool add_less_or_equal_cond (tree arg1, tree arg2);
|
|
|
|
/* Adds a bool condition to state. */
|
|
bool add_bool_cond (tree arg);
|
|
|
|
/* Checks whether the given two constant values are equal. */
|
|
static bool check_const_value_equality (value *arg1, value *arg2);
|
|
|
|
/* Checks whether the given two constant values are not equal. */
|
|
static bool check_const_value_are_not_equal (value *arg1, value *arg2);
|
|
|
|
/* Checks whether the first given constant value
|
|
is greater than the second one. */
|
|
static bool check_const_value_is_greater_than (value *arg1, value *arg2);
|
|
|
|
/* Checks whether the first given constant value
|
|
is less than the second one. */
|
|
static bool check_const_value_is_less_than (value *arg1, value *arg2);
|
|
|
|
/* For the given value_bit, iterates over its expression tree, complements
|
|
those bit which came from the given origin. */
|
|
static value_bit *complement_bits_with_origin (value_bit *root, tree origin);
|
|
|
|
/* Iterates over every bit of the given value and complements their
|
|
expression trees' those bits, that came from the given origin. */
|
|
static void complement_val_bits_with_origin (value *val, tree origin);
|
|
|
|
/* Complements all bits of all values that came from the given origin. */
|
|
void complement_all_vars_bits_with_origin (tree origin);
|
|
|
|
/* Complements all bits with the given origin of all added conditions. */
|
|
void complement_conditions_with_origin (tree origin);
|
|
|
|
/* Complements all bits with the given origin of all values
|
|
and added conditions. */
|
|
void complement_state_with_origin (tree origin);
|
|
|
|
/* Returns status of last added condition. */
|
|
condition_status get_last_cond_status ();
|
|
|
|
/* Create LFSR value. */
|
|
static value *create_lfsr (tree crc, value *polynomial, bool is_bit_forward);
|
|
};
|
|
|
|
|
|
/* Returns the minimum of A, B, C. */
|
|
|
|
size_t min (size_t a, size_t b, size_t c);
|
|
|
|
|
|
/* Performs the given operation on passed arguments.
|
|
The result is stored in dest. */
|
|
|
|
template<class func>
|
|
void
|
|
state::operate (value *arg1, value *arg2, value_bit **bit_arg, tree dest,
|
|
func bit_op)
|
|
{
|
|
value *dest_var = var_states.get (dest);
|
|
size_t min_iter = min (arg1->length (), arg2->length (), dest_var->length ());
|
|
|
|
size_t i = 0;
|
|
for (; i < min_iter; i++)
|
|
{
|
|
value_bit *temp = (*var_states.get (dest))[i];
|
|
(*var_states.get (dest))[i] = operate_bits (bit_op, (*arg1)[i],
|
|
(*arg2)[i], bit_arg);
|
|
delete temp;
|
|
}
|
|
|
|
if (i >= dest_var->length ())
|
|
return;
|
|
|
|
value *biggest = arg1;
|
|
value_bit *sign_bit = (*arg2)[i - 1];
|
|
if (arg2->length () > arg1->length ())
|
|
{
|
|
biggest = arg2;
|
|
sign_bit = (*arg1)[i - 1];
|
|
}
|
|
|
|
min_iter = min (biggest->length (), dest_var->length (), dest_var->length ());
|
|
for (; i < min_iter; i++)
|
|
{
|
|
value_bit *temp = (*var_states.get (dest))[i];
|
|
(*var_states.get (dest))[i] = operate_bits (bit_op, (*biggest)[i],
|
|
sign_bit, bit_arg);
|
|
delete temp;
|
|
}
|
|
|
|
if (i >= dest_var->length ())
|
|
return;
|
|
|
|
sign_bit = (*biggest)[i - 1];
|
|
for (; i < dest_var->length (); i++)
|
|
{
|
|
value_bit *temp = (*var_states.get (dest))[i];
|
|
(*var_states.get (dest))[i] = operate_bits (bit_op, sign_bit, sign_bit,
|
|
bit_arg);
|
|
delete temp;
|
|
}
|
|
}
|
|
|
|
#endif /* SYM_EXEC_STATE_H. */
|