|
ESBMC
Efficient SMT-based Context-Bounded Model Checker
|
#include <cassert>#include <goto-symex/scheduler/execution_state.h>#include <goto-symex/engine/goto_symex.h>#include <goto-symex/scheduler/reachability_tree.h>#include <string>#include <vector>#include <util/arith/arith_tools.h>#include <util/lang/c_types.h>#include <util/expr/expr_util.h>#include <irep2/irep2.h>#include <irep2/irep2_utils.h>#include <util/message/message.h>#include <util/message/format.h>#include <util/irep/migrate.h>#include <util/base/prefix.h>#include <util/irep/std_types.h>#include <algorithm>Functions | |
| static const array_type2t * | multibyte_array (const expr2tc &object) |
| static expr2tc | object_byte (const expr2tc &object, uint64_t offset, bool big_endian) |
Byte offset of object. | |
| static expr2tc | update_object_byte (const expr2tc &object, uint64_t offset, const expr2tc &byte, bool big_endian) |
object with byte offset replaced by byte. | |
| static bool | target_is_big_endian () |
| static size_t | byte_shift (const type2tc &type, size_t offset, size_t n) |
| static expr2tc | gen_byte_expression_byte_update (const type2tc &type, const expr2tc &src, const expr2tc &value, const size_t num_of_bytes, const size_t offset) |
| static expr2tc | gen_byte_expression (const type2tc &type, const expr2tc &src, const expr2tc &value, const size_t num_of_bytes, const size_t offset) |
| static bool | lacks_byte_model (const type2tc &type) |
| static expr2tc | gen_value_by_byte (const type2tc &type, const expr2tc &src, const expr2tc &value, const size_t num_of_bytes, const size_t offset) |
| static bool | member_byte_range (const struct_type2t &st, const type2tc &root_type, size_t i, uint64_t &offset, uint64_t &size) |
| static bool | spans_bytes (const type2tc &type, uint64_t n) |
| static bool | fits_bytes (const type2tc &type, uint64_t offset, uint64_t n) |
| static void | collect_subobjects (const expr2tc &root, uint64_t offset, uint64_t n, std::vector< expr2tc > &out) |
| static expr2tc | replace_subobject (const expr2tc &root, uint64_t offset, uint64_t n, const expr2tc &value) |
| static expr2tc | replace_in_struct (const expr2tc &root, uint64_t offset, uint64_t n, const expr2tc &value) |
| static expr2tc | object_bytes (const expr2tc &object, uint64_t offset, uint64_t n) |
The n bytes of object from byte offset, as one bit-vector in the target's byte order. | |
| static expr2tc | copy_into_float (const expr2tc &dst, uint64_t dst_offset, const expr2tc &src, uint64_t src_offset, uint64_t n) |
| static expr2tc | copy_primitive (const expr2tc &dst, uint64_t dst_offset, const expr2tc &src, uint64_t src_offset, uint64_t n) |
| static expr2tc | do_memcpy_expression (const expr2tc &dst, const size_t &dst_offset, const expr2tc &src, const size_t &src_offset, const size_t num_of_bytes) |
| static void | offset_simplifier (expr2tc &e) |
|
static |
|
static |
|
static |
|
inlinestatic |
|
inlinestatic |
The idea of this expression is to compute the object value in the case where every byte value was set up until num_of_bytes
Note: this function assumes that all memory checks have been done!
In summary, there are two main computations here:
A. Generate the byte representation, this is mostly through the result expression. The expression is initialized with zero and then, until the num_of_bytes is reached it will do a full byte left-shift followed by an bitor operation with the byte value:
For example, for a integer(4 bytes) with memset using 3 bytes and value 0xF1
step 1: 0x00000000 – left-shift 8 – 0x00000000 – bitor – 0x000000F1 step 2: 0x000000F1 – left-shift 8 – 0x0000F100 – bitor – 0x0000F1F1 step 3: 0x0000F1F1 – left-shift 8 – 0x00F1F100 – bitor – 0x00F1F1F1
Since we only want 3 bytes, the initialized object value would be 0x00F1F1F1
B. Generate a mask of the bits that were not set, this is done because skipped bits need to be returned back. The computation of this is simple, we initialize every bit that was changed by the byte-representation computation with a 1, which is then negated to be applied with an bitand in the original value:
Back to the example in A, we had the byte-representation of 0x00F1F1F1. If the original value was 0xA2A2A2A2, then we would have the following mask:
step 1: 0x00000000 – set-bits – 0x000000FF step 2: 0x000000FF – set-bits – 0x0000FFFF step 3: 0x0000FFFF – set-bits – 0x00FFFFFF
So, 0x00FFFFFF is the mask for all bits changed. We can negate it to: 0xFF000000
Then, we can apply it to the original source value with bitand
0xA2A2A2A2 AND 0xFF000000 --> 0xA2000000
Finally, we get the result from A and B and unify them through a bitor
0xA2000000 OR 0x00F1F1F1 --> 0xA2F1F1F1
Note about offsets: To handle them, we apply left shifts to the remaining offset after the computation of the object-value and initial mask representation
|
inlinestatic |
|
inlinestatic |
Construct a new object, initializing it with the memset equivalent
There are a few corner cases here:
1 - Primitives: these are simple: just generate the byte_expression directly 2 - Arrays: these are ok: just keep generating byte_expression for each member until a limit has arrived. Dynamic memory is dealt here. 3 - Structs/Union: these are the hardest as we have to take the alignment into account when dealing with it. Hopefully the clang-frontend already give it to us.
Similar to array, however get the size of each component
Unions are not nice, let's go through every member and get the biggest one! And then use it directly
|
static |
|
static |
Byte offset of object.
The n bytes of object from byte offset, as one bit-vector in the target's byte order.
|
static |
|
static |
|
static |
|
static |