p8n_types/
region.rs

1// Panopticon - A libre program analysis library for machine code
2// Copyright (C) 2014-2018  The Panopticon Developers
3//
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8//
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12// Lesser General Public License for more details.
13//
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17
18use std::ops::Range;
19use std::io::Cursor;
20use std::fs::File;
21use std::sync::Arc;
22use std::path::{Path, PathBuf};
23
24use byteorder::{
25    ReadBytesExt,
26    BigEndian,
27    LittleEndian
28};
29use memmap::Mmap;
30use ron_uuid::UUID;
31
32use {
33    Result,
34    Str,
35    Endianess,
36};
37
38#[derive(Clone, Debug)]
39/// A continuous address space. Regions are an array of cells numbered from 0. Each cell is either
40/// `undefined` of carries a single byte value.
41pub enum Region {
42    /// All undefined values
43    Undefined{
44        /// Addressable space. All cells in a region have addresses between 0 and `2^address_bits -
45        /// 1`.
46        address_bits: usize,
47        /// Human readable name. This name can vary over the livetime of a region and bears no
48        /// semantic meaning. It's for use in UIs.
49        name: Str,
50        /// Immutable UUID. The UUID of a region never changes. Can be used for housekeeping and
51        /// associating additional data with this region.
52        uuid: UUID,
53    },
54    /// In memory buffer
55    Buffer{
56        /// Addressable space. All cells in a region have addresses between 0 and `2^address_bits -
57        /// 1`.
58        address_bits: usize,
59        /// Human readable name. This name can vary over the livetime of a region and bears no
60        /// semantic meaning. It's for use in UIs.
61        name: Str,
62        /// Immutable UUID. The UUID of a region never changes. Can be used for housekeeping and
63        /// associating additional data with this region.
64        uuid: UUID,
65        /// Positon of the buffer in the address space.
66        offset: u64,
67        /// Region contents
68        buffer: Arc<[u8]>,
69    },
70    /// Memory mapped file.
71    File{
72        /// Addressable space. All cells in a region have addresses between 0 and `2^address_bits -
73        /// 1`.
74        address_bits: usize,
75        /// Human readable name. This name can vary over the livetime of a region and bears no
76        /// semantic meaning. It's for use in UIs.
77        name: Str,
78        /// Immutable UUID. The UUID of a region never changes. Can be used for housekeeping and
79        /// associating additional data with this region.
80        uuid: UUID,
81        /// Positon of the file in the address space.
82        offset: u64,
83        /// mmap()'d file.
84        file: Arc<Mmap>,
85        /// Path to the mapped file
86        path: Option<PathBuf>,
87        /// Start of mapping inside file
88        file_offset: u64,
89    },
90}
91
92impl Region {
93    /// Creates a new completly undefined region with `name` and `address_bits`.
94    pub fn undefined<S, U>(name: S, address_bits: usize, uuid: U) -> Region where S: Into<Str>, U: Into<Option<UUID>> {
95        Region::Undefined{
96            address_bits: address_bits,
97            name: name.into(),
98            uuid: uuid.into().unwrap_or_default(),
99        }
100    }
101
102    /// Creates a new region called `name` that is `address_bits` large. Maps `mmap` to `offset`.
103    pub fn from_mmap<S, O, P, U>(name: S, address_bits: usize, mmap: Mmap, path: P, file_offset: O, offset: O, uuid: U) -> Region where S: Into<Str>, O: Into<Option<u64>>, P: Into<Option<PathBuf>>, U: Into<Option<UUID>> {
104        Region::File{
105            offset: offset.into().unwrap_or(0),
106            name: name.into(),
107            file: Arc::new(mmap),
108            address_bits: address_bits,
109            uuid: uuid.into().unwrap_or_default(),
110            path: path.into(),
111            file_offset: file_offset.into().unwrap_or(0),
112        }
113    }
114
115    /// Creates a new region called `name` that is `address_bits` large. Maps `fd` to `offset`.
116    pub fn from_file<S, O, P, U>(name: S, address_bits: usize, fd: File, path: P, offset: O, uuid: U) -> Result<Region> where S: Into<Str>, O: Into<Option<u64>>, P: Into<Option<PathBuf>>, U: Into<Option<UUID>> {
117        Ok(Region::File{
118            offset: offset.into().unwrap_or(0),
119            name: name.into(),
120            file: Arc::new(unsafe { Mmap::map(&fd)? }),
121            address_bits: address_bits,
122            uuid: uuid.into().unwrap_or_default(),
123            path: path.into(),
124            file_offset: 0,
125        })
126    }
127
128    /// Creates a new region called `name` that is `address_bits` large. Maps `buf` to `offset`.
129    pub fn from_buf<S, O, B, U>(name: S, address_bits: usize, buf: B, offset: O, uuid: U) -> Region where S: Into<Str>, O: Into<Option<u64>>, B: Into<Arc<[u8]>>, U: Into<Option<UUID>> {
130        Region::Buffer{
131            offset: offset.into().unwrap_or(0),
132            name: name.into(),
133            buffer: buf.into(),
134            address_bits: address_bits,
135            uuid: uuid.into().unwrap_or_default(),
136        }
137    }
138
139    /// If this is a mmap()'d file, return the path to it and mmap() starting position.
140    pub fn file<'a>(&'a self) -> Option<(&'a Path, u64)> {
141        match self {
142            &Region::Undefined{ .. } => None,
143            &Region::File{ ref path, file_offset, .. } =>
144                path.as_ref().map(|x| (PathBuf::as_path(x), file_offset)),
145            &Region::Buffer{ .. } => None,
146        }
147    }
148
149    /// Returns the human readable name of this region.
150    pub fn name<'a>(&'a self) -> &'a Str {
151        match self {
152            &Region::Undefined{ ref name,.. } => name,
153            &Region::File{ ref name,.. } => name,
154            &Region::Buffer{ ref name,.. } => name,
155        }
156    }
157
158    /// Changes the human readable name to `new`.
159    pub fn rename(&mut self, new: Str) {
160        match self {
161            &mut Region::Undefined{ ref mut name,.. } => { *name = new }
162            &mut Region::File{ ref mut name,.. } => { *name = new }
163            &mut Region::Buffer{ ref mut  name,.. } => { *name = new }
164        }
165    }
166
167    /// Returns the immutable UUID of this region.
168    pub fn uuid<'a>(&'a self) -> &'a UUID {
169        match self {
170            &Region::Undefined{ ref uuid,.. } => uuid,
171            &Region::File{ ref uuid,.. } => uuid,
172            &Region::Buffer{ ref uuid,.. } => uuid,
173        }
174    }
175
176    /// Size of this region.
177    pub fn address_bits(&self) -> usize {
178        match self {
179            &Region::Undefined{ address_bits,.. } => address_bits,
180            &Region::File{ address_bits,.. } => address_bits,
181            &Region::Buffer{ address_bits,.. } => address_bits,
182        }
183    }
184
185    /// Defined range
186    pub fn defined(&self) -> Range<u64> {
187        match self {
188            &Region::Undefined{ .. } => 0..0,
189            &Region::File{ offset, ref file,.. } =>
190                offset..offset + file.len() as u64,
191            &Region::Buffer{ offset, ref buffer,.. } =>
192                offset..offset + buffer.len() as u64,
193        }
194    }
195
196    /// Fill `buf` the the values starting at `address`. Fails if `address..address + buf.len()`
197    /// is outside of the addressable range or contains undefined values.
198    pub fn read<'a>(&'a self, start: u64, len: usize) -> Result<&'a[u8]> {
199        if !self.in_range(start..start + len as u64) { return Err("Out of range".into()); }
200        if len == 0 || !self.is_defined(start..start + len as u64) {
201            return Err("Undefined".into());
202        }
203
204        match self {
205            &Region::Undefined{ .. } => unreachable!(),
206            &Region::File{ ref file, offset,.. } => {
207                let a = ((start - offset) as usize)..((start - offset) as usize + len);
208                Ok(&file[a])
209            }
210            &Region::Buffer{ ref buffer, offset,.. } => {
211                let a = ((start - offset) as usize)..((start - offset) as usize + len);
212                Ok(&buffer[a])
213            }
214        }
215    }
216
217    /// Trys to fill `buf` the the values starting at `address`. Returns early if `address..address + buf.len()`
218    /// contains undefined values and fails if it's outside of the addressable range. Returns the number of bytes read.
219    pub fn try_read(&self, address: u64, buf: &mut [u8]) -> Result<usize> {
220        use std::cmp;
221
222        if !self.in_range(address..address + 1) { return Err("Out of range".into()); }
223        if !self.is_defined(address..address + 1) { return Ok(0); }
224
225        match self {
226            &Region::Undefined{ .. } if buf.len() == 0 => Ok(0),
227            &Region::Undefined{ .. } => unreachable!(),
228            &Region::File{ ref file, offset,.. } => {
229                let o = offset as usize;
230                let l = cmp::min(buf.len(), file.len());
231                let a = (address as usize - o)..(address as usize + l - o);
232
233                buf[0..l].clone_from_slice(&file[a]);
234                Ok(l)
235            }
236
237            &Region::Buffer{ ref buffer, offset,.. } => {
238                let o = offset as usize;
239                let address = address as usize - o;
240                let l = cmp::min(buf.len(), buffer.len() - address);
241                let a = (address as usize)..(address as usize + l);
242
243                buf[0..l].clone_from_slice(&buffer[a]);
244                Ok(l)
245            }
246        }
247    }
248
249    /// Reads an `bytes` large integer from `address` and zero-extends it to an `u64`. Fails if `address..address + bytes`
250    /// is outside of the addressable range or contains undefined values.
251    pub fn read_integer(&self, address: u64, endianess: Endianess, bytes: usize) -> Result<u64> {
252        match bytes {
253            1 => {
254                Ok(self.read(address, 1)?[0] as u64)
255            }
256            2 => {
257                let buf = self.read(address, 2)?;
258                let mut cur = Cursor::new(buf);
259
260                match endianess {
261                    Endianess::Little => Ok(cur.read_u16::<LittleEndian>()? as u64),
262                    Endianess::Big => Ok(cur.read_u16::<BigEndian>()? as u64),
263                }
264            }
265            4 => {
266                let buf = self.read(address, 4)?;
267                let mut cur = Cursor::new(buf);
268
269                match endianess {
270                    Endianess::Little => Ok(cur.read_u32::<LittleEndian>()? as u64),
271                    Endianess::Big => Ok(cur.read_u32::<BigEndian>()? as u64),
272                }
273            }
274            8 => {
275                let buf = self.read(address, 8)?;
276                let mut cur = Cursor::new(buf);
277
278                match endianess {
279                    Endianess::Little => cur.read_u64::<LittleEndian>().map_err(|e| e.into()),
280                    Endianess::Big => cur.read_u64::<BigEndian>().map_err(|e| e.into()),
281                }
282            }
283            _ => Err(format!("reaidng a {} byte integer is unimplemented",bytes).into()),
284        }
285    }
286
287    /// Returns true if range is in the addressable space of this region, i.e. `0..address_bits^2 -
288    /// `.
289    pub fn in_range(&self, range: Range<u64>) -> bool {
290        match self {
291            &Region::Undefined{ address_bits,.. } => {
292                64 - range.start.leading_zeros() as usize <= address_bits &&
293                    64 - range.end.saturating_sub(1).leading_zeros() as usize <= address_bits
294            }
295            &Region::Buffer{ address_bits,.. } => {
296                64 - range.start.leading_zeros() as usize <= address_bits &&
297                    64 - range.end.saturating_sub(1).leading_zeros() as usize <= address_bits
298            }
299            &Region::File{ address_bits,.. } => {
300                64 - range.start.leading_zeros() as usize <= address_bits &&
301                    64 - range.end.saturating_sub(1).leading_zeros() as usize <= address_bits
302            }
303        }
304    }
305
306    /// Returns true if range is in the addressable space of this region and contains no undefined
307    /// values.
308    pub fn is_defined(&self, range: Range<u64>) -> bool {
309        match self {
310            &Region::Undefined{ .. } => false,
311            &Region::Buffer{ ref buffer, offset, address_bits,.. } => {
312                let end = if address_bits < 64 { 1 << address_bits } else { 0xFFFFFFFFFFFFFFFF };
313                let head = 0..offset;
314                let tail = offset + (buffer.len() as u64)..end;
315                let outside = head.end > range.start || tail.start < range.end;
316
317                !outside
318            }
319            &Region::File{ ref file, offset, address_bits,.. } => {
320                let end = if address_bits < 64 { 1 << address_bits } else { 0xFFFFFFFFFFFFFFFF };
321                let head = 0..offset;
322                let tail = offset + (file.len() as u64)..end;
323                let outside = head.end > range.start || tail.start < range.end;
324
325                !outside
326            }
327        }
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334
335    #[test]
336    fn construct() {
337        let b: Box<[u8]> = vec![1, 2, 3].into();
338        let l1 = Region::undefined("l1",6,None);
339        let l2 = Region::from_buf("l2",3,b,None,None);
340
341        assert_eq!(l1.defined(), 0..0);
342        assert_eq!(l2.defined(), 0..3);
343        assert_eq!(l1.address_bits(), 6);
344        assert_eq!(l1.name(), "l1");
345        assert_eq!(l2.address_bits(), 3);
346        assert_eq!(l2.name(), "l2");
347    }
348
349    #[test]
350    fn is_defined() {
351        let b: Box<[u8]> = vec![1, 2, 3].into();
352        let l1 = Region::from_buf("l2",16,b,5,None);
353
354        assert_eq!(l1.defined(), 5..8);
355        assert_eq!(l1.is_defined(0..1), false);
356        assert!(l1.try_read(0, &mut vec![0u8; 1]).is_ok());
357        assert_eq!(l1.is_defined(0..5), false);
358        assert!(l1.try_read(0, &mut vec![0u8; 4]).is_ok());
359        assert_eq!(l1.is_defined(3..7), false);
360        assert!(l1.try_read(3, &mut vec![0u8; 4]).is_ok());
361        assert_eq!(l1.is_defined(5..6), true);
362        assert!(l1.try_read(5, &mut vec![0u8; 1]).is_ok());
363        assert_eq!(l1.is_defined(5..8), true);
364        assert!(l1.try_read(5, &mut vec![0u8; 3]).is_ok());
365        assert_eq!(l1.is_defined(9..20), false);
366        assert!(l1.try_read(9, &mut vec![0u8; 10]).is_ok());
367    }
368
369    #[test]
370    fn in_range() {
371        let b: Box<[u8]> = vec![1, 2, 3].into();
372        let l1 = Region::from_buf("l2",16,b,0,None);
373
374        assert_eq!(l1.defined(), 0..3);
375        assert_eq!(l1.in_range(0..1), true);
376        assert!(l1.try_read(0, &mut vec![0u8; 1]).is_ok());
377        assert_eq!(l1.in_range(0x10000..0x10001), false);
378        assert_eq!(l1.in_range(3..0x1_00_00), true);
379        assert_eq!(l1.in_range(5..0x10001), false);
380    }
381
382    #[test]
383    fn read() {
384        let b: Box<[u8]> = vec![1, 2, 3].into();
385        let l1 = Region::from_buf("l2",3,b,None,None);
386
387        {
388            let buf = l1.read(0, 1).unwrap();
389            assert_eq!(buf, [1]);
390        }
391
392        {
393            let buf = l1.read(2, 1).unwrap();
394            assert_eq!(buf, [3]);
395        }
396
397        {
398            let buf = l1.read(1, 2).unwrap();
399            assert_eq!(buf, [2,3]);
400        }
401    }
402
403    #[test]
404    fn try_read() {
405        let b: Box<[u8]> = vec![1, 2, 3].into();
406        let l1 = Region::from_buf("l2",3,b,None,None);
407
408        {
409            let mut buf = [0u8; 3];
410            assert_eq!(l1.try_read(0, &mut buf).ok(), Some(3));
411            assert_eq!(buf, [1, 2, 3]);
412        }
413
414        {
415            let mut buf = [0u8; 3];
416            assert_eq!(l1.try_read(2, &mut buf).ok(), Some(1));
417            assert_eq!(buf, [3, 0, 0]);
418        }
419
420        {
421            let mut buf = [0u8; 3];
422            assert_eq!(l1.try_read(1, &mut buf).ok(), Some(2));
423            assert_eq!(buf, [2, 3, 0]);
424        }
425    }
426}