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}