isla_lib/
bitvector.rs

1// BSD 2-Clause License
2//
3// Copyright (c) 2019, 2020 Alasdair Armstrong
4//
5// All rights reserved.
6//
7// Redistribution and use in source and binary forms, with or without
8// modification, are permitted provided that the following conditions are
9// met:
10//
11// 1. Redistributions of source code must retain the above copyright
12// notice, this list of conditions and the following disclaimer.
13//
14// 2. Redistributions in binary form must reproduce the above copyright
15// notice, this list of conditions and the following disclaimer in the
16// documentation and/or other materials provided with the distribution.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30//! This module defines the bitvector trait BV, and includes modules
31//! for concrete bitvectors of up to 64-bits, or up to 129-bits. The
32//! 129-bit bitvectors are intended for CHERI architectures as it
33//! allows capabilities to be represented without involving the SMT
34//! solver. Most functions in isla-lib, and dependent libraries will
35//! be parametric over the BV trait.
36//!
37//! The reason for having an upper-bound on the size of concrete
38//! bitvectors is so they can be fixed size, which allows them to be
39//! Copy types in rust. This does not affect expressivity, just that
40//! longer bitvectors will also be represented in the SMT solver, and
41//! appear in isla as `Val::Symbolic` (as defined the ir module).
42
43use serde::de::DeserializeOwned;
44use serde::Serialize;
45use std::arch::x86_64::_bzhi_u64;
46use std::convert::TryInto;
47use std::fmt;
48use std::hash::Hash;
49use std::io::Write;
50use std::ops::{Add, BitAnd, BitOr, BitXor, Neg, Not, Shl, Shr, Sub};
51
52use crate::error::ExecError;
53
54#[macro_export]
55macro_rules! write_bits {
56    ($f: expr, $bits: expr, $len: expr) => {{
57        if $len == 4 {
58            write!($f, "{:x}", $bits & 0xF)?
59        } else if $len % 4 == 0 {
60            for i in (0..($len / 4)).rev() {
61                write!($f, "{:x}", ($bits >> (i * 4)) & 0xF)?;
62            }
63        } else {
64            for i in (0..$len).rev() {
65                write!($f, "{:b}", ($bits >> i) & 0b1)?;
66            }
67        }
68        Ok(())
69    }};
70}
71
72pub mod b129;
73pub mod b64;
74
75/// This trait allows us to be generic over the representation of
76/// concrete bitvectors. Specific users of isla-lib may then choose
77/// different representations depending on use case - B64 will likely
78/// be the most efficient for ordinary use, but B129 can represent
79/// [CHERI](https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/)
80/// compressed capabilities concretely.
81pub trait BV
82where
83    Self: fmt::Debug + fmt::LowerHex + fmt::UpperHex + fmt::Display,
84    Self: Copy + Clone + PartialEq + Eq + Hash + Send + Sync,
85    Self: Serialize + DeserializeOwned,
86    Self: Add<Output = Self>,
87    Self: Sub<Output = Self>,
88    Self: BitAnd<Output = Self>,
89    Self: BitOr<Output = Self>,
90    Self: BitXor<Output = Self>,
91    Self: Not<Output = Self>,
92    Self: Neg<Output = Self>,
93    Self: Shl<Output = Self>,
94    Self: Shr<Output = Self>,
95    Self: TryInto<u64, Error = ExecError>,
96{
97    const BIT_ONE: Self;
98    const BIT_ZERO: Self;
99
100    /// In Isla concrete bitvectors are only represented up to a
101    /// specific maximum width/length. Beyond this they will be
102    /// promoted to symbolic variables which are equal to a concrete
103    /// value represented in the SMT solver. This makes computation
104    /// over concrete bitvectors below this max width very efficient,
105    /// as they can be represented as simple Copy types like `u64`.
106    const MAX_WIDTH: u32;
107
108    fn new(value: u64, len: u32) -> Self;
109
110    fn len(self) -> u32;
111
112    fn lower_u64(self) -> u64;
113
114    fn lower_u8(self) -> u8;
115
116    fn is_zero(self) -> bool;
117
118    /// Make a small bitvector of all zeros.
119    ///
120    /// # Panics
121    ///
122    /// `len` must be less than or equal to `MAX_WIDTH`
123    fn zeros(len: u32) -> Self;
124
125    /// Make a small bitvector of all ones.
126    ///
127    /// # Panics
128    ///
129    /// `len` must be less than or equal to `MAX_WIDTH`
130    fn ones(len: u32) -> Self;
131
132    fn leading_zeros(self) -> u32;
133
134    fn from_u8(value: u8) -> Self;
135
136    fn from_u16(value: u16) -> Self;
137
138    fn from_u32(value: u32) -> Self;
139
140    fn from_u64(value: u64) -> Self;
141
142    /// Byte order is: from_bytes(&[0xAB, 0xCD, 0xEF] == 0xABCDEF
143    ///
144    /// # Panics
145    ///
146    /// bytes.len() * 8 must be less than or equal to `MAX_WIDTH`
147    fn from_bytes(bytes: &[u8]) -> Self;
148
149    fn to_le_bytes(self) -> Vec<u8>;
150    fn to_be_bytes(self) -> Vec<u8>;
151
152    /// Parses a bitvector from a string slice. String must be
153    /// prefixed by either #x/0x, or #b/0b (allowing both SMT style
154    /// and Sail/C style prefixes) for hexadecimal or binary. Returns
155    /// `None` if the string is not parseable for any reason
156    fn from_str(bv: &str) -> Option<Self>;
157
158    fn len_i128(self) -> i128 {
159        i128::from(self.len())
160    }
161
162    fn is_empty(self) -> bool {
163        self.len() == 0
164    }
165
166    fn add_i128(self, op: i128) -> Self;
167
168    fn sub_i128(self, op: i128) -> Self {
169        self.add_i128(-op)
170    }
171
172    /// zero_extend a bitvector to a specific new length.
173    ///
174    /// # Panics
175    ///
176    /// `new_len` must be greater than the current length, but less
177    /// than `MAX_WIDTH`.
178    fn zero_extend(self, new_len: u32) -> Self;
179
180    /// sign_extend a bitvector to a specific new length. Sign
181    /// extending a zero-length bitvector creates a `new_len` sized
182    /// bitvector containing only zeros.
183    ///
184    /// # Panics
185    ///
186    /// `new_len` must be greater than the current length, but less
187    /// than `MAX_WIDTH`.
188    fn sign_extend(self, new_len: u32) -> Self;
189
190    fn unsigned(self) -> i128;
191
192    fn signed(self) -> i128;
193
194    fn append(self, suffix: Self) -> Option<Self> {
195        let new_len = self.len() + suffix.len();
196        if new_len <= Self::MAX_WIDTH {
197            let shift = Self::new(u64::from(suffix.len()), new_len);
198            Some(self.zero_extend(new_len) << shift | suffix.zero_extend(new_len))
199        } else {
200            None
201        }
202    }
203
204    fn slice(self, from: u32, len: u32) -> Option<Self>;
205
206    fn set_slice(self, n: u32, update: Self) -> Self;
207
208    fn extract(self, high: u32, low: u32) -> Option<Self> {
209        let len = (high - low) + 1;
210        if low <= high && high <= self.len() {
211            self.slice(low, len)
212        } else {
213            None
214        }
215    }
216
217    fn shiftr(self, shift: i128) -> Self {
218        if shift < 0 {
219            self.shiftl(shift.abs())
220        } else if shift >= self.len() as i128 {
221            Self::zeros(self.len())
222        } else {
223            self >> Self::new(shift as u64, self.len())
224        }
225    }
226
227    fn arith_shiftr(self, shift: i128) -> Self {
228        if shift < 0 {
229            self.shiftl(shift.abs())
230        } else if shift >= self.len() as i128 {
231            if self.leading_zeros() > 0 {
232                Self::zeros(self.len())
233            } else {
234                Self::ones(self.len())
235            }
236        } else if self.leading_zeros() > 0 {
237            self.shiftr(shift)
238        } else {
239            self.shiftr(shift).slice(0, self.len() - shift as u32).unwrap().sign_extend(self.len())
240        }
241    }
242
243    fn shiftl(self, shift: i128) -> Self {
244        if shift < 0 {
245            self.shiftr(shift.abs())
246        } else if shift >= self.len() as i128 {
247            Self::zeros(self.len())
248        } else {
249            self << Self::new(shift as u64, self.len())
250        }
251    }
252
253    fn truncate_lsb(self, len: i128) -> Option<Self> {
254        if 0 < len && len <= Self::MAX_WIDTH as i128 {
255            let len = len as u64;
256            (self >> Self::new(64 - len, self.len())).slice(0, len as u32)
257        } else if len == 0 {
258            Some(Self::new(0, 0))
259        } else {
260            None
261        }
262    }
263
264    fn replicate(self, times: i128) -> Option<Self> {
265        if times == 0 {
266            Some(Self::new(0, 0))
267        } else if 0 <= times && self.len() as i128 * times <= Self::MAX_WIDTH as i128 {
268            let mut bv = self;
269            for _ in 1..times {
270                bv = bv.append(self).unwrap()
271            }
272            Some(bv)
273        } else {
274            None
275        }
276    }
277
278    fn set_slice_int(int: i128, n: u32, update: Self) -> i128;
279
280    fn get_slice_int(len: u32, int: i128, n: u32) -> Self;
281}
282
283pub fn write_bits64(buf: &mut dyn Write, bits: u64, len: u32) -> std::io::Result<()> {
284    if len % 4 == 0 {
285        write!(buf, "#x")?
286    } else {
287        write!(buf, "#b")?
288    }
289    write_bits!(buf, bits, len)
290}
291
292#[inline(always)]
293pub fn bzhi_u64(bits: u64, len: u32) -> u64 {
294    unsafe { _bzhi_u64(bits, len) }
295}
296
297pub fn bzhi_u128(bits: u128, len: u32) -> u128 {
298    bits & (std::u128::MAX >> (128 - len))
299}