<commit_before>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::os::unix::io::{AsRawFd, RawFd};

use crate::virtio::test_utils::VirtQueue as GuestQ;
use crate::virtio::vsock::device::{RXQ_INDEX, TXQ_INDEX};
use crate::virtio::vsock::packet::{VsockPacket, VSOCK_PKT_HDR_SIZE};
use crate::virtio::{
    QueueError, VirtioDevice, Vsock, VsockBackend, VsockChannel, VsockEpollListener, VsockError,
    VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE,
};
use crate::Error as DeviceError;
use core::result;
use utils::epoll::{EpollEvent, EventSet};
use utils::eventfd::EventFd;
use vm_memory::{GuestAddress, GuestMemoryMmap};

type Result<T> = std::result::Result<T, VsockError>;

pub struct TestBackend {
    pub evfd: EventFd,
    pub rx_err: Option<VsockError>,
    pub tx_err: Option<VsockError>,
    pub pending_rx: bool,
    pub rx_ok_cnt: usize,
    pub tx_ok_cnt: usize,
    pub evset: Option<EventSet>,
}

impl TestBackend {
    pub fn new() -> Self {
        Self {
            evfd: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
            rx_err: None,
            tx_err: None,
            pending_rx: false,
            rx_ok_cnt: 0,
            tx_ok_cnt: 0,
            evset: None,
        }
    }

    pub fn set_rx_err(&mut self, err: Option<VsockError>) {
        self.rx_err = err;
    }
    pub fn set_tx_err(&mut self, err: Option<VsockError>) {
        self.tx_err = err;
    }
    pub fn set_pending_rx(&mut self, prx: bool) {
        self.pending_rx = prx;
    }
}

impl Default for TestBackend {
    fn default() -> Self {
        Self::new()
    }
}

impl VsockChannel for TestBackend {
    fn recv_pkt(&mut self, _pkt: &mut VsockPacket) -> Result<()> {
        let cool_buf = [0xDu8, 0xE, 0xA, 0xD, 0xB, 0xE, 0xE, 0xF];
        match self.rx_err.take() {
            None => {
                if let Some(buf) = _pkt.buf_mut() {
                    for i in 0..buf.len() {
                        buf[i] = cool_buf[i % cool_buf.len()];
                    }
                }
                self.rx_ok_cnt += 1;
                Ok(())
            }
            Some(e) => Err(e),
        }
    }

    fn send_pkt(&mut self, _pkt: &VsockPacket) -> Result<()> {
        match self.tx_err.take() {
            None => {
                self.tx_ok_cnt += 1;
                Ok(())
            }
            Some(e) => Err(e),
        }
    }

    fn has_pending_rx(&self) -> bool {
        self.pending_rx
    }
}

impl AsRawFd for TestBackend {
    fn as_raw_fd(&self) -> RawFd {
        self.evfd.as_raw_fd()
    }
}

impl VsockEpollListener for TestBackend {
    fn get_polled_evset(&self) -> EventSet {
        EventSet::IN
    }
    fn notify(&mut self, evset: EventSet) {
        self.evset = Some(evset);
    }
}
impl VsockBackend for TestBackend {}

pub struct TestContext {
    pub cid: u64,
    pub mem: GuestMemoryMmap,
    pub mem_size: usize,
    pub device: Vsock<TestBackend>,
}

impl TestContext {
    pub fn new() -> Self {
        const CID: u64 = 52;
        const MEM_SIZE: usize = 1024 * 1024 * 128;
        let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
        Self {
            cid: CID,
            mem,
            mem_size: MEM_SIZE,
            device: Vsock::new(CID, TestBackend::new()).unwrap(),
        }
    }

    pub fn create_event_handler_context(&self) -> EventHandlerContext {
        const QSIZE: u16 = 2;

        let guest_rxvq = GuestQ::new(GuestAddress(0x0010_0000), &self.mem, QSIZE as u16);
        let guest_txvq = GuestQ::new(GuestAddress(0x0020_0000), &self.mem, QSIZE as u16);
        let guest_evvq = GuestQ::new(GuestAddress(0x0030_0000), &self.mem, QSIZE as u16);
        let rxvq = guest_rxvq.create_queue();
        let txvq = guest_txvq.create_queue();
        let evvq = guest_evvq.create_queue();

        // Set up one available descriptor in the RX queue.
        guest_rxvq.dtable[0].set(
            0x0040_0000,
            VSOCK_PKT_HDR_SIZE as u32,
            VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT,
            1,
        );
        guest_rxvq.dtable[1].set(0x0040_1000, 4096, VIRTQ_DESC_F_WRITE, 0);

        guest_rxvq.avail.ring[0].set(0);
        guest_rxvq.avail.idx.set(1);

        // Set up one available descriptor in the TX queue.
        guest_txvq.dtable[0].set(0x0050_0000, VSOCK_PKT_HDR_SIZE as u32, VIRTQ_DESC_F_NEXT, 1);
        guest_txvq.dtable[1].set(0x0050_1000, 4096, 0, 0);
        guest_txvq.avail.ring[0].set(0);
        guest_txvq.avail.idx.set(1);

        let queues = vec![rxvq, txvq, evvq];
        EventHandlerContext {
            guest_rxvq,
            guest_txvq,
            guest_evvq,
            device: Vsock::with_queues(self.cid, TestBackend::new(), queues).unwrap(),
        }
    }
}

impl Default for TestContext {
    fn default() -> Self {
        Self::new()
    }
}

pub struct EventHandlerContext<'a> {
    pub device: Vsock<TestBackend>,
    pub guest_rxvq: GuestQ<'a>,
    pub guest_txvq: GuestQ<'a>,
    pub guest_evvq: GuestQ<'a>,
}

impl<'a> EventHandlerContext<'a> {
    pub fn mock_activate(&mut self, mem: GuestMemoryMmap) {
        // Artificially activate the device.
        self.device.activate(mem).unwrap();
    }

    pub fn signal_txq_event(&mut self) {
        self.device.queue_events[TXQ_INDEX].write(1).unwrap();
        self.device
            .handle_txq_event(&EpollEvent::new(EventSet::IN, 0));
    }
    pub fn signal_rxq_event(&mut self) {
        self.device.queue_events[RXQ_INDEX].write(1).unwrap();
        self.device
            .handle_rxq_event(&EpollEvent::new(EventSet::IN, 0));
    }
}

impl<B> Vsock<B>
where
    B: VsockBackend,
{
    pub fn write_element_in_queue(
        vsock: &Vsock<B>,
        idx: usize,
        val: u64,
    ) -> result::Result<(), DeviceError> {
        if idx > vsock.queue_events.len() {
            return Err(DeviceError::QueueError(QueueError::DescIndexOutOfBounds(
                idx as u16,
            )));
        }
        vsock.queue_events[idx].write(val).unwrap();
        Ok(())
    }

    pub fn get_element_from_queue(
        vsock: &Vsock<B>,
        idx: usize,
    ) -> result::Result<u64, DeviceError> {
        if idx > vsock.queue_events.len() {
            return Err(DeviceError::QueueError(QueueError::DescIndexOutOfBounds(
                idx as u16,
            )));
        }
        Ok(vsock.queue_events[idx].as_raw_fd() as u64)
    }
}
<commit_msg>Modified queue size in TestContext.<commit_after>// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use std::os::unix::io::{AsRawFd, RawFd};

use crate::virtio::test_utils::VirtQueue as GuestQ;
use crate::virtio::vsock::device::{RXQ_INDEX, TXQ_INDEX};
use crate::virtio::vsock::packet::{VsockPacket, VSOCK_PKT_HDR_SIZE};
use crate::virtio::{
    QueueError, VirtioDevice, Vsock, VsockBackend, VsockChannel, VsockEpollListener, VsockError,
    VIRTQ_DESC_F_NEXT, VIRTQ_DESC_F_WRITE,
};
use crate::Error as DeviceError;
use core::result;
use utils::epoll::{EpollEvent, EventSet};
use utils::eventfd::EventFd;
use vm_memory::{GuestAddress, GuestMemoryMmap};

type Result<T> = std::result::Result<T, VsockError>;

pub struct TestBackend {
    pub evfd: EventFd,
    pub rx_err: Option<VsockError>,
    pub tx_err: Option<VsockError>,
    pub pending_rx: bool,
    pub rx_ok_cnt: usize,
    pub tx_ok_cnt: usize,
    pub evset: Option<EventSet>,
}

impl TestBackend {
    pub fn new() -> Self {
        Self {
            evfd: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
            rx_err: None,
            tx_err: None,
            pending_rx: false,
            rx_ok_cnt: 0,
            tx_ok_cnt: 0,
            evset: None,
        }
    }

    pub fn set_rx_err(&mut self, err: Option<VsockError>) {
        self.rx_err = err;
    }
    pub fn set_tx_err(&mut self, err: Option<VsockError>) {
        self.tx_err = err;
    }
    pub fn set_pending_rx(&mut self, prx: bool) {
        self.pending_rx = prx;
    }
}

impl Default for TestBackend {
    fn default() -> Self {
        Self::new()
    }
}

impl VsockChannel for TestBackend {
    fn recv_pkt(&mut self, _pkt: &mut VsockPacket) -> Result<()> {
        let cool_buf = [0xDu8, 0xE, 0xA, 0xD, 0xB, 0xE, 0xE, 0xF];
        match self.rx_err.take() {
            None => {
                if let Some(buf) = _pkt.buf_mut() {
                    for i in 0..buf.len() {
                        buf[i] = cool_buf[i % cool_buf.len()];
                    }
                }
                self.rx_ok_cnt += 1;
                Ok(())
            }
            Some(e) => Err(e),
        }
    }

    fn send_pkt(&mut self, _pkt: &VsockPacket) -> Result<()> {
        match self.tx_err.take() {
            None => {
                self.tx_ok_cnt += 1;
                Ok(())
            }
            Some(e) => Err(e),
        }
    }

    fn has_pending_rx(&self) -> bool {
        self.pending_rx
    }
}

impl AsRawFd for TestBackend {
    fn as_raw_fd(&self) -> RawFd {
        self.evfd.as_raw_fd()
    }
}

impl VsockEpollListener for TestBackend {
    fn get_polled_evset(&self) -> EventSet {
        EventSet::IN
    }
    fn notify(&mut self, evset: EventSet) {
        self.evset = Some(evset);
    }
}
impl VsockBackend for TestBackend {}

pub struct TestContext {
    pub cid: u64,
    pub mem: GuestMemoryMmap,
    pub mem_size: usize,
    pub device: Vsock<TestBackend>,
}

impl TestContext {
    pub fn new() -> Self {
        const CID: u64 = 52;
        const MEM_SIZE: usize = 1024 * 1024 * 128;
        let mem = GuestMemoryMmap::from_ranges(&[(GuestAddress(0), MEM_SIZE)]).unwrap();
        Self {
            cid: CID,
            mem,
            mem_size: MEM_SIZE,
            device: Vsock::new(CID, TestBackend::new()).unwrap(),
        }
    }

    pub fn create_event_handler_context(&self) -> EventHandlerContext {
        const QSIZE: u16 = 256;

        let guest_rxvq = GuestQ::new(GuestAddress(0x0010_0000), &self.mem, QSIZE as u16);
        let guest_txvq = GuestQ::new(GuestAddress(0x0020_0000), &self.mem, QSIZE as u16);
        let guest_evvq = GuestQ::new(GuestAddress(0x0030_0000), &self.mem, QSIZE as u16);
        let rxvq = guest_rxvq.create_queue();
        let txvq = guest_txvq.create_queue();
        let evvq = guest_evvq.create_queue();

        // Set up one available descriptor in the RX queue.
        guest_rxvq.dtable[0].set(
            0x0040_0000,
            VSOCK_PKT_HDR_SIZE as u32,
            VIRTQ_DESC_F_WRITE | VIRTQ_DESC_F_NEXT,
            1,
        );
        guest_rxvq.dtable[1].set(0x0040_1000, 4096, VIRTQ_DESC_F_WRITE, 0);

        guest_rxvq.avail.ring[0].set(0);
        guest_rxvq.avail.idx.set(1);

        // Set up one available descriptor in the TX queue.
        guest_txvq.dtable[0].set(0x0050_0000, VSOCK_PKT_HDR_SIZE as u32, VIRTQ_DESC_F_NEXT, 1);
        guest_txvq.dtable[1].set(0x0050_1000, 4096, 0, 0);
        guest_txvq.avail.ring[0].set(0);
        guest_txvq.avail.idx.set(1);

        let queues = vec![rxvq, txvq, evvq];
        EventHandlerContext {
            guest_rxvq,
            guest_txvq,
            guest_evvq,
            device: Vsock::with_queues(self.cid, TestBackend::new(), queues).unwrap(),
        }
    }
}

impl Default for TestContext {
    fn default() -> Self {
        Self::new()
    }
}

pub struct EventHandlerContext<'a> {
    pub device: Vsock<TestBackend>,
    pub guest_rxvq: GuestQ<'a>,
    pub guest_txvq: GuestQ<'a>,
    pub guest_evvq: GuestQ<'a>,
}

impl<'a> EventHandlerContext<'a> {
    pub fn mock_activate(&mut self, mem: GuestMemoryMmap) {
        // Artificially activate the device.
        self.device.activate(mem).unwrap();
    }

    pub fn signal_txq_event(&mut self) {
        self.device.queue_events[TXQ_INDEX].write(1).unwrap();
        self.device
            .handle_txq_event(&EpollEvent::new(EventSet::IN, 0));
    }
    pub fn signal_rxq_event(&mut self) {
        self.device.queue_events[RXQ_INDEX].write(1).unwrap();
        self.device
            .handle_rxq_event(&EpollEvent::new(EventSet::IN, 0));
    }
}

impl<B> Vsock<B>
where
    B: VsockBackend,
{
    pub fn write_element_in_queue(
        vsock: &Vsock<B>,
        idx: usize,
        val: u64,
    ) -> result::Result<(), DeviceError> {
        if idx > vsock.queue_events.len() {
            return Err(DeviceError::QueueError(QueueError::DescIndexOutOfBounds(
                idx as u16,
            )));
        }
        vsock.queue_events[idx].write(val).unwrap();
        Ok(())
    }

    pub fn get_element_from_queue(
        vsock: &Vsock<B>,
        idx: usize,
    ) -> result::Result<u64, DeviceError> {
        if idx > vsock.queue_events.len() {
            return Err(DeviceError::QueueError(QueueError::DescIndexOutOfBounds(
                idx as u16,
            )));
        }
        Ok(vsock.queue_events[idx].as_raw_fd() as u64)
    }
}
<|endoftext|>


<commit_before>use std::convert;
use std::ops::{Add, Sub};
use std::default::Default;
use internals::Node;

pub trait Semigroup {
    /// The sum operation of the semigroup
    fn sum(self, other: Self) -> Self;
}

/// The class of monoids
///
/// The class of monoids (types with an accumulative binary operation that has
/// an default).
///
/// An instance _M_ should satisfy the following laws:
///
///  + _x_`.sum(`_M_`::default())` = _x_
///  + _M_`::default().sum(`_x_`)` = _x_
///  + _x_`.sum(`_y_`.sum(`_z_`))` = _z_`.sum(`_x_`.sum(`_y_`))`
///  + _M_`::accumulate(`_a_`)` = _a_`.fold(`_M_`::default,`_M_`::sum)`
///
pub trait Monoid: Semigroup + Default + Sized {

    #[inline]
    fn accumulate<F>(xs: F) -> Self
    where F: Iterator<Item=Self>
        , Self: Sized {
        xs.fold(Self::default(), Self::sum)
    }
}

/// A monoid that can be applied to a `Node` as a measurement
pub trait Metric: Monoid + Eq + Add<usize, Output=Self>
                              + Sub<usize, Output=Self>
                              + Sized {
    /// Apply this `Metric` to the given `Node`
    fn measure(node: &Node) -> Self;

    /// Convert the `Metric` into a byte index into the given `Node`
    ///
    /// # Returns
    /// - `Some` with the byte index of the beginning of the `n`th  element
    ///    in `node` measured by this `Metric`, if there is an `n`th element
    /// - `None` if there is no `n`th element in `node`
    fn to_byte_index(&self, node: &Node) -> Option<usize>;

    /// Returns the byte index of the next element of this metric in `Node`
    #[inline] fn next(self, node: &Node) -> Option<usize> {
        (self + 1).to_byte_index(node)
    }

    /// Returns the byte index of the previous element of this metric in `Node`
    #[inline] fn back(self, node: &Node) -> Option<usize> {
        (self - 1).to_byte_index(node)
    }

    /// Returns true if index `i` in `node` is a boundary along this `Metric`
    // TODO: should this be a method on `Node`s instead?
    fn is_boundary(node: &Node, i: usize) -> bool;
}

#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Grapheme(usize);
impl Default for Grapheme {
    #[inline] fn default() -> Self { Grapheme(0) }
}
impl Semigroup for Grapheme {
    #[inline] fn sum(self, other: Self) -> Self { Grapheme(self.0 + other.0) }
}
impl convert::From<usize> for Grapheme {
    #[inline] fn from(u: usize) -> Self { Grapheme(u) }
}
impl Add<usize> for Grapheme {
    type Output = Self;
    #[inline] fn add(self, rhs: usize) -> Self { Grapheme(self.0 + rhs) }
}
impl Sub<usize> for Grapheme {
    type Output = Self;
    #[inline] fn sub(self, rhs: usize) -> Self { Grapheme(self.0 - rhs) }
}

#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Line(usize);
impl Default for Line {
    #[inline] fn default() -> Self { Line(0) }
}
impl Semigroup for Line {
    #[inline] fn sum(self, other: Self) -> Self { Line(self.0 + other.0) }
}

impl convert::From<usize> for Line {
    #[inline] fn from(u: usize) -> Self { Line(u) }
}
impl Add<usize> for Line {
    type Output = Self;
    #[inline] fn add(self, rhs: usize) -> Self { Line(self.0 + rhs) }
}
impl Sub<usize> for Line {
    type Output = Self;
    #[inline] fn sub(self, rhs: usize) -> Self { Line(self.0 - rhs) }
}
<commit_msg>docs: add some "documentation" to Metrics<commit_after>use std::convert;
use std::ops::{Add, Sub};
use std::default::Default;
use internals::Node;

pub trait Semigroup {
    /// The sum operation of the semigroup
    fn sum(self, other: Self) -> Self;
}

/// The class of monoids
///
/// The class of monoids (types with an accumulative binary operation that has
/// an default).
///
/// An instance _M_ should satisfy the following laws:
///
///  + _x_`.sum(`_M_`::default())` = _x_
///  + _M_`::default().sum(`_x_`)` = _x_
///  + _x_`.sum(`_y_`.sum(`_z_`))` = _z_`.sum(`_x_`.sum(`_y_`))`
///  + _M_`::accumulate(`_a_`)` = _a_`.fold(`_M_`::default,`_M_`::sum)`
///
pub trait Monoid: Semigroup + Default + Sized {

    #[inline]
    fn accumulate<F>(xs: F) -> Self
    where F: Iterator<Item=Self>
        , Self: Sized {
        xs.fold(Self::default(), Self::sum)
    }
}

/// A monoid that can be applied to a `Node` as a measurement
pub trait Metric: Monoid + Eq + Add<usize, Output=Self>
                              + Sub<usize, Output=Self>
                              + Sized {
    /// Apply this `Metric` to the given `Node`
    ///
    /// Although we aren't currently enforcing this, `measure`ing a `Node` with
    /// two children should produce the same result as `measure`ing both
    /// children and `Monoid::sum`ming the result. That is to say, `measure`
    /// should be a [_monoid homomorphism_]
    /// (https://en.wikipedia.org/wiki/Monoid#Monoid_homomorphisms).
    fn measure(node: &Node) -> Self;

    /// Convert the `Metric` into a byte index into the given `Node`
    ///
    /// # Returns
    /// - `Some` with the byte index of the beginning of the `n`th  element
    ///    in `node` measured by this `Metric`, if there is an `n`th element
    /// - `None` if there is no `n`th element in `node`
    fn to_byte_index(&self, node: &Node) -> Option<usize>;

    /// Returns the byte index of the next element of this metric in `Node`
    #[inline] fn next(self, node: &Node) -> Option<usize> {
        (self + 1).to_byte_index(node)
    }

    /// Returns the byte index of the previous element of this metric in `Node`
    #[inline] fn back(self, node: &Node) -> Option<usize> {
        (self - 1).to_byte_index(node)
    }

    /// Returns true if index `i` in `node` is a boundary along this `Metric`
    // TODO: should this be a method on `Node`s instead?
    fn is_boundary(node: &Node, i: usize) -> bool;
}

#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Grapheme(usize);
impl Default for Grapheme {
    #[inline] fn default() -> Self { Grapheme(0) }
}
impl Semigroup for Grapheme {
    #[inline] fn sum(self, other: Self) -> Self { Grapheme(self.0 + other.0) }
}
impl convert::From<usize> for Grapheme {
    #[inline] fn from(u: usize) -> Self { Grapheme(u) }
}
impl Add<usize> for Grapheme {
    type Output = Self;
    #[inline] fn add(self, rhs: usize) -> Self { Grapheme(self.0 + rhs) }
}
impl Sub<usize> for Grapheme {
    type Output = Self;
    #[inline] fn sub(self, rhs: usize) -> Self { Grapheme(self.0 - rhs) }
}

#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
pub struct Line(usize);
impl Default for Line {
    #[inline] fn default() -> Self { Line(0) }
}
impl Semigroup for Line {
    #[inline] fn sum(self, other: Self) -> Self { Line(self.0 + other.0) }
}

impl convert::From<usize> for Line {
    #[inline] fn from(u: usize) -> Self { Line(u) }
}
impl Add<usize> for Line {
    type Output = Self;
    #[inline] fn add(self, rhs: usize) -> Self { Line(self.0 + rhs) }
}
impl Sub<usize> for Line {
    type Output = Self;
    #[inline] fn sub(self, rhs: usize) -> Self { Line(self.0 - rhs) }
}
<|endoftext|>


<commit_before>use std::fmt;
use data::Data;
use query::SortDirection;

#[derive(Clone, Debug, PartialEq)]
pub struct Answer {
    pub columns: Vec<String>,
    pub rows: Vec<Vec<Data>>,
}

impl Answer {
    pub fn sort(&mut self, column_indices: &[(usize, SortDirection)]) {
        if column_indices.is_empty() {
            return;
        }

        for &(index, ref direction) in column_indices.iter().rev() {
            match direction {
                &SortDirection::Asc => self.rows.sort_by(|a, b| a[index].cmp(&b[index])),
                &SortDirection::Desc => self.rows.sort_by(|a, b| b[index].cmp(&a[index])),
            }
        }
    }
}

impl fmt::Display for Answer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{}", self.columns.join("\t"))?;

        for row in self.rows.iter() {
            let formatted_cells = row.iter()
                .map(|cell| format!("{}", cell))
                .collect::<Vec<String>>();

            writeln!(f, "{}", formatted_cells.join("\t"))?;
        }

        Ok(())
    }
}
<commit_msg>Remove redundant check<commit_after>use std::fmt;
use data::Data;
use query::SortDirection;

#[derive(Clone, Debug, PartialEq)]
pub struct Answer {
    pub columns: Vec<String>,
    pub rows: Vec<Vec<Data>>,
}

impl Answer {
    pub fn sort(&mut self, column_indices: &[(usize, SortDirection)]) {
        for &(index, ref direction) in column_indices.iter().rev() {
            match direction {
                &SortDirection::Asc => self.rows.sort_by(|a, b| a[index].cmp(&b[index])),
                &SortDirection::Desc => self.rows.sort_by(|a, b| b[index].cmp(&a[index])),
            }
        }
    }
}

impl fmt::Display for Answer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "{}", self.columns.join("\t"))?;

        for row in self.rows.iter() {
            let formatted_cells = row.iter()
                .map(|cell| format!("{}", cell))
                .collect::<Vec<String>>();

            writeln!(f, "{}", formatted_cells.join("\t"))?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::PathBuf;
use std::fs::metadata;
use std::process::Command;

fn get_path() -> Option<String> {
    for (key, val) in env::vars() {
        if key == "PATH" {
            return Some(val)
        }
    }
    None
}

fn get_matlab_path(ml_output: &str) -> Option<&str> {
    for line in ml_output.split("\n") {
        let mut it = line.split("=");
        match (it.next(), it.next()) {
            (Some("MATLAB"), Some(p)) => {
                return Some(p)
            },
            _ => {
            },
        }
    }
    None
}

fn main() {
    let mut matched = false;

    for (key, val) in env::vars() {
        match (&key[..], &val[..], matched) {
            ("MATLAB_LIB_PATH", _, true) |
                ("OCTAVE_LIB_PATH", _, true) => {
                    panic!("Please specify only MATLAB_LIB_PATH or OCTAVE_LIB_PATH");
            },
            ("MATLAB_LIB_PATH", path, false) => {
                println!("cargo:rustc-link-search={}", path);
                println!("cargo:rustc-cfg=matlab");
                matched = true;
            },
            ("OCTAVE_LIB_PATH", path, false) => {
                println!("cargo:rustc-link-search={}", path);
                println!("cargo:rustc-cfg=octave");
                matched = true;
            },
            _ => { }
        };
    }

    if let (Some(path), false) = (get_path(), matched) {
        for path_item in path.split(":") {
            // look for octave-config
            let mut oct_path = PathBuf::from(path_item);
            oct_path.push("octave-config");
            if let Ok(_) = metadata(&oct_path) {
                if let Ok(output) = Command::new(&oct_path).arg("--print").arg("OCTLIBDIR").output() {
                    if let Ok(txt) = String::from_utf8(output.stdout) {
                        println!("cargo:rustc-link-search={}", txt);
                        println!("cargo:rustc-cfg=octave");
                        matched = true;
                    }
                }
            }

            if matched  {
                break;
            }

            // look for matlab
            let mut matlab_path = PathBuf::from(path_item);
            matlab_path.push("matlab");
            if let Ok(_) = metadata(&matlab_path) {
                if let Ok(output) = Command::new(&matlab_path).arg("-e").output() {
                    if let Ok(txt) = String::from_utf8(output.stdout) {
                        if let Some(p) = get_matlab_path(&txt) {
                            println!("cargo:rustc-link-search={}", p);
                            println!("cargo:rustc-cfg=matlab");
                            matched = true;
                        }
                    }
                }
            }
        }
    }

    if !matched {
        panic!("Please set either MATLAB_LIB_PATH or OCTAVE_LIB_PATH");
    }
}

<commit_msg>improves matlab build support<commit_after>use std::env;
use std::path::PathBuf;
use std::fs::metadata;
use std::process::Command;

fn get_path() -> Option<String> {
    for (key, val) in env::vars() {
        if key == "PATH" {
            return Some(val)
        }
    }
    None
}

fn get_matlab_path(ml_output: &str) -> Option<&str> {
    for line in ml_output.split("\n") {
        let mut it = line.split("=");
        match (it.next(), it.next()) {
            (Some("LD_LIBRARY_PATH"), Some(p)) => {
                return Some(p)
            },
            _ => {
            },
        }
    }
    None
}

fn main() {
    let mut matched = false;

    for (key, val) in env::vars() {
        match (&key[..], &val[..], matched) {
            ("MATLAB_LIB_PATH", _, true) |
                ("OCTAVE_LIB_PATH", _, true) => {
                    panic!("Please specify only MATLAB_LIB_PATH or OCTAVE_LIB_PATH");
            },
            ("MATLAB_LIB_PATH", path, false) => {
                println!("cargo:rustc-link-search={}", path);
                println!("cargo:rustc-cfg=matlab");
                matched = true;
            },
            ("OCTAVE_LIB_PATH", path, false) => {
                println!("cargo:rustc-link-search={}", path);
                println!("cargo:rustc-cfg=octave");
                matched = true;
            },
            _ => { }
        };
    }

    if let (Some(path), false) = (get_path(), matched) {
        for path_item in path.split(":") {
            // look for octave-config
            let mut oct_path = PathBuf::from(path_item);
            oct_path.push("octave-config");
            if let Ok(_) = metadata(&oct_path) {
                if let Ok(output) = Command::new(&oct_path).arg("--print").arg("OCTLIBDIR").output() {
                    if let Ok(txt) = String::from_utf8(output.stdout) {
                        println!("cargo:rustc-link-search={}", txt);
                        println!("cargo:rustc-cfg=octave");
                        matched = true;
                    }
                }
            }

            if matched  {
                break;
            }

            // look for matlab
            let mut matlab_path = PathBuf::from(path_item);
            matlab_path.push("matlab");
            if let Ok(_) = metadata(&matlab_path) {
                if let Ok(output) = Command::new(&matlab_path).arg("-e").output() {
                    if let Ok(txt) = String::from_utf8(output.stdout) {
                        if let Some(p) = get_matlab_path(&txt) {
                            println!("cargo:rustc-link-search={}", p);
                            println!("cargo:rustc-cfg=matlab");
                            matched = true;
                        }
                    }
                }
            }
        }
    }

    if !matched {
        panic!("Please set either MATLAB_LIB_PATH or OCTAVE_LIB_PATH");
    }
}

<|endoftext|>


<commit_before>extern crate git2;
extern crate rustc_version;
extern crate time;
#[macro_use]
extern crate clap;

use git2::{DescribeFormatOptions, DescribeOptions, Repository};
use rustc_version::Version;
use clap::Shell;

use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
use std::process::exit;

include!("src/cli.rs");

fn completions() {
  let outdir = match env::var_os("OUT_DIR") {
    None => return,
    Some(outdir) => outdir,
  };
  let mut app = create_app(false);
  app.gen_completions("bins", Shell::Bash, &outdir);
  app.gen_completions("bins", Shell::Zsh, &outdir);
  app.gen_completions("bins", Shell::Fish, &outdir);
}

fn get_version<'a>() -> (String, Option<String>, String) {
  let profile = env::var("PROFILE").unwrap();
  let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
  let git = if let Ok(repo) = Repository::open(&manifest_dir) {
    Some(repo.describe(DescribeOptions::new().describe_tags().show_commit_oid_as_fallback(true))
      .unwrap()
      .format(Some(DescribeFormatOptions::new().dirty_suffix("-dirty")))
      .unwrap())
  } else {
    None
  };
  let date = format!("{}", time::now().strftime("%b %d, %Y %H:%M:%S %z").unwrap());
  (profile, git, date)
}

fn main() {
  if rustc_version::version().unwrap() < Version::parse("1.17.0-nightly").unwrap() {
    writeln!(&mut io::stderr(), "bins requires at least Rust 1.17.0").unwrap();
    exit(1);
  }
  let (profile, git, date) = get_version();
  let out_dir = env::var("OUT_DIR").unwrap();
  let dest_path = Path::new(&out_dir).join("version_info.rs");
  let mut f = File::create(&dest_path).unwrap();
  f.write_all(format!("
      struct VersionInfo {{
        profile: &'static str,
        git: Option<&'static str>,
        date: &'static str
      }}

      impl VersionInfo {{
        fn get() -> VersionInfo {{
            VersionInfo {{
              profile: \"{}\",
              git: {},
              date: \"{}\"
            }}
        }}
      }}
  ",
      profile,
      if let Some(ref g) = git { format!("Some(\"{}\")", g) } else { "None".to_owned() },
      date)
      .as_bytes())
    .unwrap();

    completions();
}
<commit_msg>fix: work around describe failures<commit_after>extern crate git2;
extern crate rustc_version;
extern crate time;
#[macro_use]
extern crate clap;

use git2::{DescribeFormatOptions, DescribeOptions, Repository};
use rustc_version::Version;
use clap::Shell;

use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;
use std::process::exit;

include!("src/cli.rs");

fn completions() {
  let outdir = match env::var_os("OUT_DIR") {
    None => return,
    Some(outdir) => outdir,
  };
  let mut app = create_app(false);
  app.gen_completions("bins", Shell::Bash, &outdir);
  app.gen_completions("bins", Shell::Zsh, &outdir);
  app.gen_completions("bins", Shell::Fish, &outdir);
}

fn git_describe() -> Option<String> {
  let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
  let repo = match Repository::open(&manifest_dir) {
    Err(_) => return None,
    Ok(r) => r
  };
  let description = match repo.describe(DescribeOptions::new().describe_tags().show_commit_oid_as_fallback(true)) {
    Err(_) => return None,
    Ok(d) => d
  };
  match description.format(Some(DescribeFormatOptions::new().dirty_suffix("-dirty"))) {
    Err(_) => None,
    Ok(fd) => Some(fd)
  }
}

fn get_version<'a>() -> (String, Option<String>, String) {
  let profile = env::var("PROFILE").unwrap();
  let git = git_describe();
  let date = format!("{}", time::now().strftime("%b %d, %Y %H:%M:%S %z").unwrap());
  (profile, git, date)
}

fn main() {
  if rustc_version::version().unwrap() < Version::parse("1.17.0-nightly").unwrap() {
    writeln!(&mut io::stderr(), "bins requires at least Rust 1.17.0").unwrap();
    exit(1);
  }
  let (profile, git, date) = get_version();
  let out_dir = env::var("OUT_DIR").unwrap();
  let dest_path = Path::new(&out_dir).join("version_info.rs");
  let mut f = File::create(&dest_path).unwrap();
  f.write_all(format!("
      struct VersionInfo {{
        profile: &'static str,
        git: Option<&'static str>,
        date: &'static str
      }}

      impl VersionInfo {{
        fn get() -> VersionInfo {{
          VersionInfo {{
            profile: \"{}\",
            git: {},
            date: \"{}\"
          }}
        }}
      }}
  ",
      profile,
      if let Some(ref g) = git { format!("Some(\"{}\")", g) } else { "None".to_owned() },
      date)
      .as_bytes())
    .unwrap();

    completions();
}
<|endoftext|>


<commit_before>use std::env;
use std::fs;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::process;

static LAYOUT_FILE_NAME: &str = "layout.ld";
static LAYOUT_GENERIC_FILENAME: &str = "layout_generic.ld";

fn main() {
    static PLATFORM_ENV_VAR: &str = "PLATFORM";
    static PLATFORM_FILE_NAME: &str = "platform";
    static APP_HEAP_SIZE: &str = "APP_HEAP_SIZE";
    static KERNEL_HEAP_SIZE: &str = "KERNEL_HEAP_SIZE";

    println!("cargo:rerun-if-env-changed={}", PLATFORM_ENV_VAR);
    println!("cargo:rerun-if-env-changed={}", APP_HEAP_SIZE);
    println!("cargo:rerun-if-env-changed={}", KERNEL_HEAP_SIZE);
    println!("cargo:rerun-if-changed={}", PLATFORM_FILE_NAME);
    println!("cargo:rerun-if-changed={}", LAYOUT_FILE_NAME);
    println!("cargo:rerun-if-changed={}", LAYOUT_GENERIC_FILENAME);

    let platform_name =
        read_env_var(PLATFORM_ENV_VAR).or_else(|| read_board_name_from_file(PLATFORM_FILE_NAME));
    if let Some(platform_name) = platform_name {
        println!("cargo:rustc-env={}={}", PLATFORM_ENV_VAR, platform_name);
        copy_linker_file(platform_name.trim());
    } else {
        println!(
            "cargo:warning=No platform specified. \
             Remember to manually specify a linker file.",
        );
    }

    set_default_env(APP_HEAP_SIZE, "1024");
    set_default_env(KERNEL_HEAP_SIZE, "1024");
}

fn set_default_env(env_var: &str, default: &str) {
    if let Some(s) = read_env_var(env_var) {
        println!("cargo:rustc-env={}={}", env_var, s);
    } else {
        // Just use a default of 1024 if nothing is passed in
        println!("cargo:rustc-env={}={}", env_var, default);
    }
}

fn read_env_var(env_var: &str) -> Option<String> {
    env::var_os(env_var).map(|os_string| os_string.into_string().unwrap())
}

fn read_board_name_from_file(file_name: &str) -> Option<String> {
    let path = Path::new(file_name);
    if !path.exists() {
        return None;
    }

    let board_file = File::open(path).unwrap();
    let mut board_name = String::new();
    BufReader::new(board_file)
        .read_line(&mut board_name)
        .unwrap();
    Some(board_name)
}

fn copy_linker_file(platform_name: &str) {
    let linker_file_name = format!("boards/layout_{}.ld", platform_name);
    let path = Path::new(&linker_file_name);
    if !path.exists() {
        println!("Cannot find layout file {:?}", path);
        process::exit(1);
    }
    // Note: cargo fails if run in a path that is not valid Unicode, so this
    // script doesn't need to handle non-Unicode paths. Also, OUT_DIR cannot be
    // in a location with a newline in it, or we have no way to pass
    // rustc-link-search to cargo.
    let out_dir = &std::env::var("OUT_DIR").expect("Unable to read OUT_DIR");
    assert!(
        !out_dir.contains('\n'),
        "Build path contains a newline, which is unsupported"
    );
    let out_layout_path: PathBuf = [out_dir, "layout.ld"].iter().collect();
    fs::copy(linker_file_name, out_layout_path).unwrap();

    // Copy the generic layout file into OUT_DIR.
    let out_layout_generic: PathBuf = [out_dir, LAYOUT_GENERIC_FILENAME].iter().collect();
    fs::copy(LAYOUT_GENERIC_FILENAME, out_layout_generic)
        .expect("Unable to copy layout_generic.ld into OUT_DIR");
    println!("cargo:rustc-link-search={}", out_dir);
}
<commit_msg>Remove support for `platform` file.<commit_after>use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process;

static LAYOUT_FILE_NAME: &str = "layout.ld";
static LAYOUT_GENERIC_FILENAME: &str = "layout_generic.ld";

fn main() {
    static PLATFORM_ENV_VAR: &str = "PLATFORM";
    static APP_HEAP_SIZE: &str = "APP_HEAP_SIZE";
    static KERNEL_HEAP_SIZE: &str = "KERNEL_HEAP_SIZE";

    println!("cargo:rerun-if-env-changed={}", PLATFORM_ENV_VAR);
    println!("cargo:rerun-if-env-changed={}", APP_HEAP_SIZE);
    println!("cargo:rerun-if-env-changed={}", KERNEL_HEAP_SIZE);
    println!("cargo:rerun-if-changed={}", LAYOUT_FILE_NAME);
    println!("cargo:rerun-if-changed={}", LAYOUT_GENERIC_FILENAME);

    let platform_name = read_env_var(PLATFORM_ENV_VAR);
    if let Some(platform_name) = platform_name {
        println!("cargo:rustc-env={}={}", PLATFORM_ENV_VAR, platform_name);
        copy_linker_file(platform_name.trim());
    } else {
        println!(
            "cargo:warning=No platform specified. \
             Remember to manually specify a linker file.",
        );
    }

    set_default_env(APP_HEAP_SIZE, "1024");
    set_default_env(KERNEL_HEAP_SIZE, "1024");
}

fn set_default_env(env_var: &str, default: &str) {
    if let Some(s) = read_env_var(env_var) {
        println!("cargo:rustc-env={}={}", env_var, s);
    } else {
        // Just use a default of 1024 if nothing is passed in
        println!("cargo:rustc-env={}={}", env_var, default);
    }
}

fn read_env_var(env_var: &str) -> Option<String> {
    env::var_os(env_var).map(|os_string| os_string.into_string().unwrap())
}

fn copy_linker_file(platform_name: &str) {
    let linker_file_name = format!("boards/layout_{}.ld", platform_name);
    let path = Path::new(&linker_file_name);
    if !path.exists() {
        println!("Cannot find layout file {:?}", path);
        process::exit(1);
    }
    // Note: cargo fails if run in a path that is not valid Unicode, so this
    // script doesn't need to handle non-Unicode paths. Also, OUT_DIR cannot be
    // in a location with a newline in it, or we have no way to pass
    // rustc-link-search to cargo.
    let out_dir = &std::env::var("OUT_DIR").expect("Unable to read OUT_DIR");
    assert!(
        !out_dir.contains('\n'),
        "Build path contains a newline, which is unsupported"
    );
    let out_layout_path: PathBuf = [out_dir, "layout.ld"].iter().collect();
    fs::copy(linker_file_name, out_layout_path).unwrap();

    // Copy the generic layout file into OUT_DIR.
    let out_layout_generic: PathBuf = [out_dir, LAYOUT_GENERIC_FILENAME].iter().collect();
    fs::copy(LAYOUT_GENERIC_FILENAME, out_layout_generic)
        .expect("Unable to copy layout_generic.ld into OUT_DIR");
    println!("cargo:rustc-link-search={}", out_dir);
}
<|endoftext|>


<commit_before>#[cfg(unix)]
mod unix {
    use std::env;
    use std::fs;
    use std::path::PathBuf;

    const LAUNCH_SCRIPT: &str = "anitrack.sh";

    pub fn run() {
        move_to_output(LAUNCH_SCRIPT);
    }

    fn move_to_output(name: &str) {
        let profile = env::var("PROFILE").unwrap();

        match profile.as_ref() {
            "debug" | "release" => {
                let mut out_path = PathBuf::from("target");
                out_path.push(profile);
                out_path.push(name);

                fs::copy(name, out_path).unwrap();
            }
            _ => (),
        }
    }
}

fn main() {
    #[cfg(unix)]
    unix::run();
}
<commit_msg>Only copy the launch script to the ouput folder if building in release mode<commit_after>#[cfg(unix)]
mod unix {
    use std::env;
    use std::fs;
    use std::path::PathBuf;

    const LAUNCH_SCRIPT: &str = "anitrack.sh";

    pub fn run() {
        let profile = env::var("PROFILE").unwrap();

        if profile != "release" {
            return;
        }

        let mut out_path = PathBuf::from("target");
        out_path.push(profile);
        out_path.push(LAUNCH_SCRIPT);

        fs::copy(LAUNCH_SCRIPT, out_path).unwrap();
    }
}

fn main() {
    #[cfg(unix)]
    unix::run();
}
<|endoftext|>


<commit_before>use super::super::ast::nodes::{
    Assignment, Block, Identifier, Infix, Integer, Node, PostfixProperty, Program,
};

use super::lexer::{Token, TokenStream};

fn parse_program(input: &mut TokenStream) -> Node {
    let mut nodes: Vec<Node> = Vec::new();
    while input.peek() != Token::EOF {
        nodes.append(&mut parse_statements(input, Token::EOF))
    }
    Node::Program(Program { nodes })
}

fn parse_statements(input: &mut TokenStream, terminator: Token) -> Vec<Node> {
    let mut nodes = vec![];
    loop {
        consume_terminals(input);
        if input.peek() == terminator {
            break;
        }

        nodes.push(parse_statement(input, terminator.clone()));

        consume_terminals(input);
        if input.peek() == terminator {
            break;
        }
    }
    nodes
}

// `terminator` is a pseudo-terminal (eg. a closing brace) that can act like a
// terminal. However, when it is encountered it will be peeked, not read, from
// the input.
fn parse_statement(input: &mut TokenStream, terminator: Token) -> Node {
    let next = input.peek();
    let node = match next {
        // Token::Func => parse_function(input),
        // Token::Let => parse_let_and_var(input),
        // Token::Return => parse_return(input, terminator),
        // Token::Var => parse_let_and_var(input),
        _ => parse_expression(input),
    };
    // Treat the statement as terminated if we encounter the terminator (but
    // don't try to consume it).
    if input.peek() == terminator {
        return node;
    }
    let next = input.peek();
    if let Token::Terminal(_) = next {
        return node;
    }
    panic_unexpected(
        next,
        Some(vec![Token::Terminal('\n'), Token::Terminal(';')]),
    );
    unreachable!()
}

fn parse_expression(input: &mut TokenStream) -> Node {
    parse_infix(input)
}

fn parse_infix(input: &mut TokenStream) -> Node {
    enum Subnode {
        Node(Node),
        Op(Token),
    }

    impl From<Subnode> for Node {
        fn from(subnode: Subnode) -> Node {
            match subnode {
                Subnode::Node(node) => node,
                _ => unreachable!(),
            }
        }
    }

    fn reduce_subnodes(subnodes: &mut Vec<Subnode>, reduce_token: Token) {
        let mut index = 1;
        while index < subnodes.len() {
            let subnode = &subnodes[index];
            if let Subnode::Op(token) = subnode {
                if *token == reduce_token {
                    // Take out the operation and the nodes on either side.
                    let mut removed_nodes: Vec<Subnode> =
                        subnodes.drain((index - 1)..=(index + 1)).collect();
                    assert_eq!(removed_nodes.len(), 3);

                    // Then turn that 3-vector into stuff we can work with.
                    let rhs = removed_nodes.pop().unwrap();
                    let op = match removed_nodes.pop().unwrap() {
                        Subnode::Op(token) => token,
                        _ => unreachable!(),
                    };
                    let lhs = removed_nodes.pop().unwrap();

                    let node = Node::Infix(Infix::new(lhs.into(), op, rhs.into()));
                    subnodes.insert(index - 1, Subnode::Node(node));
                    // Since we replaced things in-place we should repeat
                    // from where we are rather than advancing.
                    continue;
                }
            }
            index += 1;
        }
    }

    let mut subnodes = vec![Subnode::Node(parse_block(input))];
    while infix(input.peek()) {
        subnodes.push(Subnode::Op(input.read()));
        subnodes.push(Subnode::Node(parse_block(input)));
    }
    if subnodes.len() > 1 {
        // Implement associativity by reducing around operators. The earlier
        // reductions have higher associativity than later ones.
        reduce_subnodes(&mut subnodes, Token::Star);
        reduce_subnodes(&mut subnodes, Token::Plus);
    }
    // It better have fully reduced!
    assert_eq!(subnodes.len(), 1);
    subnodes.remove(0).into()
}

fn infix(token: Token) -> bool {
    match token {
        Token::Minus | Token::Plus | Token::Star => true,
        _ => false,
    }
}

fn parse_block(input: &mut TokenStream) -> Node {
    if let Token::BraceLeft = input.peek() {
        input.read(); // Opening brace
        let nodes = parse_statements(input, Token::BraceRight);
        expect_to_read(input, Token::BraceRight); // Closing brace
        Node::Block(Block { nodes })
    } else {
        parse_parentheses(input)
    }
}

fn parse_parentheses(input: &mut TokenStream) -> Node {
    if let Token::ParenthesesLeft = input.peek() {
        input.read(); // Opening parentheses
        let node = parse_atom(input);
        expect_to_read(input, Token::ParenthesesRight); // Closing parentheses
        node
    } else {
        parse_assignment(input)
    }
}

fn parse_assignment(input: &mut TokenStream) -> Node {
    let lhs = parse_postfix(input);
    if input.peek() == Token::Equals {
        input.read(); // Equals sign
        let rhs = parse_expression(input);
        Node::Assignment(Assignment::new(lhs, rhs))
    } else {
        lhs
    }
}

fn parse_postfix(input: &mut TokenStream) -> Node {
    let mut target = parse_atom(input);
    loop {
        if let Some(new_target) = try_parse_postfix_property(input, &target) {
            target = new_target;
            continue;
        }
        break;
    }
    target
}

fn try_parse_postfix_property(input: &mut TokenStream, target: &Node) -> Option<Node> {
    let mut needs_backtrack = false;
    let savepoint = input.clone();

    // Lookahead to for subsequent-line properties, eg:
    //   myCoolVariable
    //     .myCoolProperty
    if input.peek().newline() {
        input.read(); // Newline
        needs_backtrack = true;
    }

    if input.peek() == Token::Dot {
        input.read(); // Dot
        needs_backtrack = true;

        if let Token::Identifier(value) = input.peek() {
            input.read(); // Identifier
            return Some(Node::PostfixProperty(PostfixProperty::new(
                target.to_owned(),
                value,
            )));
        }
    }

    if needs_backtrack {
        input.backtrack(&savepoint);
    }
    None
}

fn parse_atom(input: &mut TokenStream) -> Node {
    let next = input.read();
    match next {
        Token::Identifier(value) => Node::Identifier(Identifier { value }),
        Token::Integer(value) => Node::Integer(Integer { value }),
        _ => {
            panic_unexpected(next, None);
            unreachable!()
        }
    }
}

fn consume_terminals(input: &mut TokenStream) {
    while let Token::Terminal(_) = input.peek() {
        input.read();
    }
}

fn expect_to_read(input: &mut TokenStream, token: Token) {
    let next = input.read();
    if next != token {
        panic_unexpected(next, Some(vec![token]));
    }
}

fn panic_unexpected(token: Token, expected_tokens: Option<Vec<Token>>) {
    let expected = match expected_tokens {
        Some(tokens) => format!(" (expected: {:?})", tokens),
        None => "".to_string(),
    };
    panic!("Unexpected token: {:?}{}", token, expected)
}

#[cfg(test)]
mod tests {
    use super::super::super::ast::nodes::{
        Block, Identifier, Infix, Integer, Node, PostfixProperty, Program,
    };

    use super::super::lexer::{Token, TokenStream};

    use super::{parse_block, parse_infix, parse_postfix, parse_program};

    fn input(input: &str) -> TokenStream {
        TokenStream::from_string(input.to_string())
    }

    #[test]
    fn it_parses_program() {
        // Test handling of multiple newlines (terminals).
        assert_eq!(
            parse_program(&mut input(
                "
                1

                2

            "
            )),
            Node::Program(Program {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
    }

    #[test]
    fn it_parses_block() {
        assert_eq!(
            parse_block(&mut input("{}")),
            Node::Block(Block { nodes: vec![] }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1 }")),
            Node::Block(Block {
                nodes: vec![Node::Integer(Integer { value: 1 })],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1; }")),
            Node::Block(Block {
                nodes: vec![Node::Integer(Integer { value: 1 })],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1; 2 }")),
            Node::Block(Block {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1; 2; }")),
            Node::Block(Block {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{\n  1\n  2;\n}")),
            Node::Block(Block {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
    }

    #[test]
    fn it_parses_infix() {
        assert_eq!(
            parse_infix(&mut input("1 + 2")),
            Node::Infix(Infix::new(
                Node::Integer(Integer { value: 1 }),
                Token::Plus,
                Node::Integer(Integer { value: 2 }),
            )),
        );
        // Now with associativity!
        assert_eq!(
            parse_infix(&mut input("1 * 2 + 3 * 4")),
            Node::Infix(Infix::new(
                Node::Infix(Infix::new(
                    Node::Integer(Integer { value: 1 }),
                    Token::Star,
                    Node::Integer(Integer { value: 2 }),
                )),
                Token::Plus,
                Node::Infix(Infix::new(
                    Node::Integer(Integer { value: 3 }),
                    Token::Star,
                    Node::Integer(Integer { value: 4 }),
                )),
            )),
        );
    }

    #[test]
    fn it_parses_postfix() {
        assert_eq!(
            parse_postfix(&mut input("foo.bar")),
            Node::PostfixProperty(PostfixProperty::new(
                Node::Identifier(Identifier {
                    value: "foo".to_string()
                }),
                "bar".to_string(),
            ))
        )
    }
}
<commit_msg>Parentheses are for a subexpression so use `parse_expression`<commit_after>use super::super::ast::nodes::{
    Assignment, Block, Identifier, Infix, Integer, Node, PostfixProperty, Program,
};

use super::lexer::{Token, TokenStream};

fn parse_program(input: &mut TokenStream) -> Node {
    let mut nodes: Vec<Node> = Vec::new();
    while input.peek() != Token::EOF {
        nodes.append(&mut parse_statements(input, Token::EOF))
    }
    Node::Program(Program { nodes })
}

fn parse_statements(input: &mut TokenStream, terminator: Token) -> Vec<Node> {
    let mut nodes = vec![];
    loop {
        consume_terminals(input);
        if input.peek() == terminator {
            break;
        }

        nodes.push(parse_statement(input, terminator.clone()));

        consume_terminals(input);
        if input.peek() == terminator {
            break;
        }
    }
    nodes
}

// `terminator` is a pseudo-terminal (eg. a closing brace) that can act like a
// terminal. However, when it is encountered it will be peeked, not read, from
// the input.
fn parse_statement(input: &mut TokenStream, terminator: Token) -> Node {
    let next = input.peek();
    let node = match next {
        // Token::Func => parse_function(input),
        // Token::Let => parse_let_and_var(input),
        // Token::Return => parse_return(input, terminator),
        // Token::Var => parse_let_and_var(input),
        _ => parse_expression(input),
    };
    // Treat the statement as terminated if we encounter the terminator (but
    // don't try to consume it).
    if input.peek() == terminator {
        return node;
    }
    let next = input.peek();
    if let Token::Terminal(_) = next {
        return node;
    }
    panic_unexpected(
        next,
        Some(vec![Token::Terminal('\n'), Token::Terminal(';')]),
    );
    unreachable!()
}

fn parse_expression(input: &mut TokenStream) -> Node {
    parse_infix(input)
}

fn parse_infix(input: &mut TokenStream) -> Node {
    enum Subnode {
        Node(Node),
        Op(Token),
    }

    impl From<Subnode> for Node {
        fn from(subnode: Subnode) -> Node {
            match subnode {
                Subnode::Node(node) => node,
                _ => unreachable!(),
            }
        }
    }

    fn reduce_subnodes(subnodes: &mut Vec<Subnode>, reduce_token: Token) {
        let mut index = 1;
        while index < subnodes.len() {
            let subnode = &subnodes[index];
            if let Subnode::Op(token) = subnode {
                if *token == reduce_token {
                    // Take out the operation and the nodes on either side.
                    let mut removed_nodes: Vec<Subnode> =
                        subnodes.drain((index - 1)..=(index + 1)).collect();
                    assert_eq!(removed_nodes.len(), 3);

                    // Then turn that 3-vector into stuff we can work with.
                    let rhs = removed_nodes.pop().unwrap();
                    let op = match removed_nodes.pop().unwrap() {
                        Subnode::Op(token) => token,
                        _ => unreachable!(),
                    };
                    let lhs = removed_nodes.pop().unwrap();

                    let node = Node::Infix(Infix::new(lhs.into(), op, rhs.into()));
                    subnodes.insert(index - 1, Subnode::Node(node));
                    // Since we replaced things in-place we should repeat
                    // from where we are rather than advancing.
                    continue;
                }
            }
            index += 1;
        }
    }

    let mut subnodes = vec![Subnode::Node(parse_block(input))];
    while infix(input.peek()) {
        subnodes.push(Subnode::Op(input.read()));
        subnodes.push(Subnode::Node(parse_block(input)));
    }
    if subnodes.len() > 1 {
        // Implement associativity by reducing around operators. The earlier
        // reductions have higher associativity than later ones.
        reduce_subnodes(&mut subnodes, Token::Star);
        reduce_subnodes(&mut subnodes, Token::Plus);
    }
    // It better have fully reduced!
    assert_eq!(subnodes.len(), 1);
    subnodes.remove(0).into()
}

fn infix(token: Token) -> bool {
    match token {
        Token::Minus | Token::Plus | Token::Star => true,
        _ => false,
    }
}

fn parse_block(input: &mut TokenStream) -> Node {
    if let Token::BraceLeft = input.peek() {
        input.read(); // Opening brace
        let nodes = parse_statements(input, Token::BraceRight);
        expect_to_read(input, Token::BraceRight); // Closing brace
        Node::Block(Block { nodes })
    } else {
        parse_parentheses(input)
    }
}

fn parse_parentheses(input: &mut TokenStream) -> Node {
    if let Token::ParenthesesLeft = input.peek() {
        input.read(); // Opening parentheses
        let node = parse_expression(input);
        expect_to_read(input, Token::ParenthesesRight); // Closing parentheses
        node
    } else {
        parse_assignment(input)
    }
}

fn parse_assignment(input: &mut TokenStream) -> Node {
    let lhs = parse_postfix(input);
    if input.peek() == Token::Equals {
        input.read(); // Equals sign
        let rhs = parse_expression(input);
        Node::Assignment(Assignment::new(lhs, rhs))
    } else {
        lhs
    }
}

fn parse_postfix(input: &mut TokenStream) -> Node {
    let mut target = parse_atom(input);
    loop {
        if let Some(new_target) = try_parse_postfix_property(input, &target) {
            target = new_target;
            continue;
        }
        break;
    }
    target
}

fn try_parse_postfix_property(input: &mut TokenStream, target: &Node) -> Option<Node> {
    let mut needs_backtrack = false;
    let savepoint = input.clone();

    // Lookahead to for subsequent-line properties, eg:
    //   myCoolVariable
    //     .myCoolProperty
    if input.peek().newline() {
        input.read(); // Newline
        needs_backtrack = true;
    }

    if input.peek() == Token::Dot {
        input.read(); // Dot
        needs_backtrack = true;

        if let Token::Identifier(value) = input.peek() {
            input.read(); // Identifier
            return Some(Node::PostfixProperty(PostfixProperty::new(
                target.to_owned(),
                value,
            )));
        }
    }

    if needs_backtrack {
        input.backtrack(&savepoint);
    }
    None
}

fn parse_atom(input: &mut TokenStream) -> Node {
    let next = input.read();
    match next {
        Token::Identifier(value) => Node::Identifier(Identifier { value }),
        Token::Integer(value) => Node::Integer(Integer { value }),
        _ => {
            panic_unexpected(next, None);
            unreachable!()
        }
    }
}

fn consume_terminals(input: &mut TokenStream) {
    while let Token::Terminal(_) = input.peek() {
        input.read();
    }
}

fn expect_to_read(input: &mut TokenStream, token: Token) {
    let next = input.read();
    if next != token {
        panic_unexpected(next, Some(vec![token]));
    }
}

fn panic_unexpected(token: Token, expected_tokens: Option<Vec<Token>>) {
    let expected = match expected_tokens {
        Some(tokens) => format!(" (expected: {:?})", tokens),
        None => "".to_string(),
    };
    panic!("Unexpected token: {:?}{}", token, expected)
}

#[cfg(test)]
mod tests {
    use super::super::super::ast::nodes::{
        Block, Identifier, Infix, Integer, Node, PostfixProperty, Program,
    };

    use super::super::lexer::{Token, TokenStream};

    use super::{parse_block, parse_infix, parse_postfix, parse_program};

    fn input(input: &str) -> TokenStream {
        TokenStream::from_string(input.to_string())
    }

    #[test]
    fn it_parses_program() {
        // Test handling of multiple newlines (terminals).
        assert_eq!(
            parse_program(&mut input(
                "
                1

                2

            "
            )),
            Node::Program(Program {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
    }

    #[test]
    fn it_parses_block() {
        assert_eq!(
            parse_block(&mut input("{}")),
            Node::Block(Block { nodes: vec![] }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1 }")),
            Node::Block(Block {
                nodes: vec![Node::Integer(Integer { value: 1 })],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1; }")),
            Node::Block(Block {
                nodes: vec![Node::Integer(Integer { value: 1 })],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1; 2 }")),
            Node::Block(Block {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{ 1; 2; }")),
            Node::Block(Block {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
        assert_eq!(
            parse_block(&mut input("{\n  1\n  2;\n}")),
            Node::Block(Block {
                nodes: vec![
                    Node::Integer(Integer { value: 1 }),
                    Node::Integer(Integer { value: 2 }),
                ],
            }),
        );
    }

    #[test]
    fn it_parses_infix() {
        assert_eq!(
            parse_infix(&mut input("1 + 2")),
            Node::Infix(Infix::new(
                Node::Integer(Integer { value: 1 }),
                Token::Plus,
                Node::Integer(Integer { value: 2 }),
            )),
        );
        // Now with associativity!
        assert_eq!(
            parse_infix(&mut input("1 * 2 + 3 * 4")),
            Node::Infix(Infix::new(
                Node::Infix(Infix::new(
                    Node::Integer(Integer { value: 1 }),
                    Token::Star,
                    Node::Integer(Integer { value: 2 }),
                )),
                Token::Plus,
                Node::Infix(Infix::new(
                    Node::Integer(Integer { value: 3 }),
                    Token::Star,
                    Node::Integer(Integer { value: 4 }),
                )),
            )),
        );
    }

    #[test]
    fn it_parses_postfix() {
        assert_eq!(
            parse_postfix(&mut input("foo.bar")),
            Node::PostfixProperty(PostfixProperty::new(
                Node::Identifier(Identifier {
                    value: "foo".to_string()
                }),
                "bar".to_string(),
            ))
        )
    }
}
<|endoftext|>


<commit_before>use crate::{
    DynamicFlags1, DynamicInfo, ElfLoader, ElfLoaderErr, LoadableHeaders, RelocationEntry,
    RelocationType,
};
use core::fmt;
use log::*;
use xmas_elf::dynamic::Tag;
use xmas_elf::program::ProgramHeader::{self, Ph32, Ph64};
use xmas_elf::program::{ProgramIter, SegmentData, Type};
use xmas_elf::sections::SectionData;
pub use xmas_elf::symbol_table::{Entry, Entry64};
use xmas_elf::ElfFile;
use xmas_elf::*;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    /// The ELF file in question.
    pub file: ElfFile<'s>,
    /// Parsed information from the .dynamic section (if the binary has it).
    pub dynamic: Option<DynamicInfo>,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ [")?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    pub fn new(region: &'s [u8]) -> Result<ElfBinary<'s>, ElfLoaderErr> {
        let file = ElfFile::new(region)?;

        // Parse relevant parts out of the the .dynamic section
        let mut dynamic = None;
        for p in file.program_iter() {
            let typ = match p {
                Ph64(header) => header.get_type()?,
                Ph32(header) => header.get_type()?,
            };

            if typ == Type::Dynamic {
                dynamic = ElfBinary::parse_dynamic(&file, &p)?;
                break;
            }
        }

        Ok(ElfBinary { file, dynamic })
    }

    /// Returns true if the binary is compiled as position independent code or false otherwise.
    ///
    /// For the binary to be PIE it needs to have a .dynamic section with PIE set in the flags1
    /// field.
    pub fn is_pie(&self) -> bool {
        self.dynamic.as_ref().map_or(false, |d: &DynamicInfo| {
            d.flags1.contains(DynamicFlags1::PIE)
        })
    }

    /// Returns the dynamic loader if present.
    ///
    /// readelf -x .interp <binary>
    ///
    /// For a statically compiled binary this will return None
    pub fn interpreter(&'s self) -> Option<&'s str> {
        self.file
            .find_section_by_name(".interp")
            .and_then(|interp_section| {
                let data = interp_section.get_data(&self.file).ok()?;
                match data {
                    SectionData::Undefined(val) => {
                        if val.len() < 2 {
                            return None;
                        }
                        Some(core::str::from_utf8(&val[..val.len() - 1]).ok()?)
                    }
                    _ => None,
                }
            })
    }

    /// Returns the target architecture
    pub fn get_arch(&self) -> header::Machine {
        self.file.header.pt2.machine().as_machine()
    }

    /// Return the entry point of the ELF file.
    ///
    /// Note this may be zero in case of position independent executables.
    pub fn entry_point(&self) -> u64 {
        self.file.header.pt2.entry_point()
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> ProgramIter {
        self.file.program_iter()
    }

    /// Get the name of the sectione
    pub fn symbol_name(&self, symbol: &'s dyn Entry) -> &'s str {
        symbol.get_name(&self.file).unwrap_or("unknown")
    }

    /// Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s dyn Entry)>(
        &self,
        mut func: F,
    ) -> Result<(), ElfLoaderErr> {
        let symbol_section = self
            .file
            .find_section_by_name(".symtab")
            .ok_or(ElfLoaderErr::SymbolTableNotFound)?;
        let symbol_table = symbol_section.get_data(&self.file)?;
        match symbol_table {
            SectionData::SymbolTable32(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            SectionData::SymbolTable64(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            _ => Err(ElfLoaderErr::SymbolTableNotFound),
        }
    }

    /// Can we load this binary on our platform?
    fn is_loadable(&self) -> Result<(), ElfLoaderErr> {
        let header = self.file.header;
        let typ = header.pt2.type_().as_type();

        if header.pt1.version() != header::Version::Current {
            Err(ElfLoaderErr::UnsupportedElfVersion)
        } else if header.pt1.data() != header::Data::LittleEndian {
            Err(ElfLoaderErr::UnsupportedEndianness)
        } else if !(header.pt1.os_abi() == header::OsAbi::SystemV
            || header.pt1.os_abi() == header::OsAbi::Linux)
        {
            Err(ElfLoaderErr::UnsupportedAbi)
        } else if !(typ == header::Type::Executable || typ == header::Type::SharedObject) {
            error!("Invalid ELF type {:?}", typ);
            Err(ElfLoaderErr::UnsupportedElfType)
        } else {
            Ok(())
        }
    }

    /// Process the relocation entries for the ELF file.
    ///
    /// Issues call to `loader.relocate` and passes the relocation entry.
    fn maybe_relocate(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        // Relocation types are architecture specific
        let arch = self.get_arch();

        // It's easier to just locate the section by name, either:
        // - .rela.dyn
        // - .rel.dyn
        let relocation_section = self
            .file
            .find_section_by_name(".rela.dyn")
            .or_else(|| self.file.find_section_by_name(".rel.dyn"));

        // Helper macro to call loader.relocate() on all entries
        macro_rules! iter_entries_and_relocate {
            ($rela_entries:expr, $create_addend:ident) => {
                for entry in $rela_entries {
                    loader.relocate(RelocationEntry {
                        rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                        offset: entry.get_offset() as u64,
                        info: entry.get_symbol_table_index(),
                        addend: $create_addend!(entry),
                    })?;
                }
            };
        }

        // Construct from Rel<T> entries. Does not contain an addend.
        macro_rules! rel_entry {
            ($entry:ident) => {
                None
            };
        }

        // Construct from Rela<T> entries. Contains an addend.
        macro_rules! rela_entry {
            ($entry:ident) => {
                Some($entry.get_addend() as u64)
            };
        }

        // If either section exists apply the relocations
        relocation_section.map_or(Ok(()), |rela_section_dyn| {
            let data = rela_section_dyn.get_data(&self.file)?;
            match data {
                SectionData::Rel32(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela32(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                SectionData::Rel64(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela64(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                _ => return Err(ElfLoaderErr::UnsupportedSectionData),
            }
            Ok(())
        })
    }

    /// Processes a dynamic header section.
    ///
    /// This section contains mostly entry points to other section headers (like relocation).
    /// At the moment this just does sanity checking for relocation later.
    ///
    /// A human readable version of the dynamic section is best obtained with `readelf -d <binary>`.
    fn parse_dynamic<'a>(
        file: &ElfFile,
        dynamic_header: &'a ProgramHeader<'a>,
    ) -> Result<Option<DynamicInfo>, ElfLoaderErr> {
        trace!("load dynamic segement {:?}", dynamic_header);

        // Walk through the dynamic program header and find the rela and sym_tab section offsets:
        let segment = dynamic_header.get_data(file)?;

        // Init result
        let mut info = DynamicInfo {
            flags1: Default::default(),
            rela: 0,
            rela_size: 0,
        };

        // Each entry/section is parsed for the same information currently
        macro_rules! parse_entry_tags {
            ($info:ident, $entry:ident, $tag:ident) => {
                match $tag {
                    // Trace required libs
                    Tag::Needed => {
                        trace!(
                            "Required library {:?}",
                            file.get_dyn_string($entry.get_val()? as _)
                        )
                    }

                    // Rel<T>
                    Tag::Rel => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelSize => $info.rela_size = $entry.get_val()?.into(),

                    // Rela<T>
                    Tag::Rela => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelaSize => $info.rela_size = $entry.get_val()?.into(),
                    Tag::Flags1 => {
                        $info.flags1 =
                            unsafe { DynamicFlags1::from_bits_unchecked($entry.get_val()? as _) };
                    }
                    _ => trace!("unsupported {:?}", $entry),
                }
            };
        }

        // Helper macro to iterate all entries
        macro_rules! iter_entries_and_parse {
            ($info:ident, $dyn_entries:expr) => {
                for dyn_entry in $dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    parse_entry_tags!($info, dyn_entry, tag);
                }
            };
        }

        match segment {
            SegmentData::Dynamic32(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            SegmentData::Dynamic64(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            _ => {
                return Err(ElfLoaderErr::UnsupportedSectionData);
            }
        };

        trace!(
            "rela size {:?} rela off {:?} flags1 {:?}",
            info.rela_size,
            info.rela,
            info.flags1
        );

        Ok(Some(info))
    }

    /// Processing the program headers and issue commands to loader.
    ///
    /// Will tell loader to create space in the address space / region where the
    /// header is supposed to go, then copy it there, and finally relocate it.
    pub fn load(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        self.is_loadable()?;

        loader.allocate(self.iter_loadable_headers())?;

        // Load all headers
        for header in self.file.program_iter() {
            let raw = match header {
                Ph32(inner) => inner.raw_data(&self.file),
                Ph64(inner) => inner.raw_data(&self.file),
            };
            let typ = header.get_type()?;
            match typ {
                Type::Load => {
                    loader.load(header.flags(), header.virtual_addr(), raw)?;
                }
                Type::Tls => {
                    loader.tls(
                        header.virtual_addr(),
                        header.file_size(),
                        header.mem_size(),
                        header.align(),
                    )?;
                }
                _ => {} // skip for now
            }
        }

        // Relocate headers
        self.maybe_relocate(loader)?;

        // Process .data.rel.ro
        for header in self.file.program_iter() {
            if header.get_type()? == Type::GnuRelro {
                loader.make_readonly(header.virtual_addr(), header.mem_size() as usize)?
            }
        }

        Ok(())
    }

    fn iter_loadable_headers(&self) -> LoadableHeaders {
        // Trying to determine loadeable headers
        fn select_load(pheader: &ProgramHeader) -> bool {
            match pheader {
                Ph32(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
                Ph64(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
            }
        }

        // Create an iterator (well filter really) that has all loadeable
        // headers and pass it to the loader
        // TODO: This is pretty ugly, maybe we can do something with impl Trait?
        // https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator-or-any-other-trait
        self.file.program_iter().filter(select_load)
    }
}
<commit_msg>Cleanup ElfBinary::interpreter()<commit_after>use crate::{
    DynamicFlags1, DynamicInfo, ElfLoader, ElfLoaderErr, LoadableHeaders, RelocationEntry,
    RelocationType,
};
use core::fmt;
use log::*;
use xmas_elf::dynamic::Tag;
use xmas_elf::program::ProgramHeader::{self, Ph32, Ph64};
use xmas_elf::program::{ProgramIter, SegmentData, Type};
use xmas_elf::sections::SectionData;
pub use xmas_elf::symbol_table::{Entry, Entry64};
use xmas_elf::ElfFile;
use xmas_elf::*;

/// Abstract representation of a loadable ELF binary.
pub struct ElfBinary<'s> {
    /// The ELF file in question.
    pub file: ElfFile<'s>,
    /// Parsed information from the .dynamic section (if the binary has it).
    pub dynamic: Option<DynamicInfo>,
}

impl<'s> fmt::Debug for ElfBinary<'s> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ElfBinary{{ [")?;
        for p in self.program_headers() {
            write!(f, " pheader = {}", p)?;
        }
        write!(f, "] }}")
    }
}

impl<'s> ElfBinary<'s> {
    /// Create a new ElfBinary.
    pub fn new(region: &'s [u8]) -> Result<ElfBinary<'s>, ElfLoaderErr> {
        let file = ElfFile::new(region)?;

        // Parse relevant parts out of the the .dynamic section
        let mut dynamic = None;
        for p in file.program_iter() {
            let typ = match p {
                Ph64(header) => header.get_type()?,
                Ph32(header) => header.get_type()?,
            };

            if typ == Type::Dynamic {
                dynamic = ElfBinary::parse_dynamic(&file, &p)?;
                break;
            }
        }

        Ok(ElfBinary { file, dynamic })
    }

    /// Returns true if the binary is compiled as position independent code or false otherwise.
    ///
    /// For the binary to be PIE it needs to have a .dynamic section with PIE set in the flags1
    /// field.
    pub fn is_pie(&self) -> bool {
        self.dynamic.as_ref().map_or(false, |d: &DynamicInfo| {
            d.flags1.contains(DynamicFlags1::PIE)
        })
    }

    /// Returns the dynamic loader if present.
    ///
    /// readelf -x .interp <binary>
    ///
    /// For a statically compiled binary this will return None
    pub fn interpreter(&'s self) -> Option<&'s str> {
        let section = self.file.find_section_by_name(".interp");
        section.and_then(|interp_section| {
            let data = interp_section.get_data(&self.file).ok()?;
            let cstr = match data {
                SectionData::Undefined(val) => val,
                _ => return None,
            };

            // Validate there is room for a null terminator
            if val.len() < 2 {
                return None;
            }

            // Ensure it is a valid utf8 string
            Some(core::str::from_utf8(&val[..val.len() - 1]).ok()?)
        })
    }

    /// Returns the target architecture
    pub fn get_arch(&self) -> header::Machine {
        self.file.header.pt2.machine().as_machine()
    }

    /// Return the entry point of the ELF file.
    ///
    /// Note this may be zero in case of position independent executables.
    pub fn entry_point(&self) -> u64 {
        self.file.header.pt2.entry_point()
    }

    /// Create a slice of the program headers.
    pub fn program_headers(&self) -> ProgramIter {
        self.file.program_iter()
    }

    /// Get the name of the sectione
    pub fn symbol_name(&self, symbol: &'s dyn Entry) -> &'s str {
        symbol.get_name(&self.file).unwrap_or("unknown")
    }

    /// Enumerate all the symbols in the file
    pub fn for_each_symbol<F: FnMut(&'s dyn Entry)>(
        &self,
        mut func: F,
    ) -> Result<(), ElfLoaderErr> {
        let symbol_section = self
            .file
            .find_section_by_name(".symtab")
            .ok_or(ElfLoaderErr::SymbolTableNotFound)?;
        let symbol_table = symbol_section.get_data(&self.file)?;
        match symbol_table {
            SectionData::SymbolTable32(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            SectionData::SymbolTable64(entries) => {
                for entry in entries {
                    func(entry);
                }
                Ok(())
            }
            _ => Err(ElfLoaderErr::SymbolTableNotFound),
        }
    }

    /// Can we load this binary on our platform?
    fn is_loadable(&self) -> Result<(), ElfLoaderErr> {
        let header = self.file.header;
        let typ = header.pt2.type_().as_type();

        if header.pt1.version() != header::Version::Current {
            Err(ElfLoaderErr::UnsupportedElfVersion)
        } else if header.pt1.data() != header::Data::LittleEndian {
            Err(ElfLoaderErr::UnsupportedEndianness)
        } else if !(header.pt1.os_abi() == header::OsAbi::SystemV
            || header.pt1.os_abi() == header::OsAbi::Linux)
        {
            Err(ElfLoaderErr::UnsupportedAbi)
        } else if !(typ == header::Type::Executable || typ == header::Type::SharedObject) {
            error!("Invalid ELF type {:?}", typ);
            Err(ElfLoaderErr::UnsupportedElfType)
        } else {
            Ok(())
        }
    }

    /// Process the relocation entries for the ELF file.
    ///
    /// Issues call to `loader.relocate` and passes the relocation entry.
    fn maybe_relocate(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        // Relocation types are architecture specific
        let arch = self.get_arch();

        // It's easier to just locate the section by name, either:
        // - .rela.dyn
        // - .rel.dyn
        let relocation_section = self
            .file
            .find_section_by_name(".rela.dyn")
            .or_else(|| self.file.find_section_by_name(".rel.dyn"));

        // Helper macro to call loader.relocate() on all entries
        macro_rules! iter_entries_and_relocate {
            ($rela_entries:expr, $create_addend:ident) => {
                for entry in $rela_entries {
                    loader.relocate(RelocationEntry {
                        rtype: RelocationType::from(arch, entry.get_type() as u32)?,
                        offset: entry.get_offset() as u64,
                        info: entry.get_symbol_table_index(),
                        addend: $create_addend!(entry),
                    })?;
                }
            };
        }

        // Construct from Rel<T> entries. Does not contain an addend.
        macro_rules! rel_entry {
            ($entry:ident) => {
                None
            };
        }

        // Construct from Rela<T> entries. Contains an addend.
        macro_rules! rela_entry {
            ($entry:ident) => {
                Some($entry.get_addend() as u64)
            };
        }

        // If either section exists apply the relocations
        relocation_section.map_or(Ok(()), |rela_section_dyn| {
            let data = rela_section_dyn.get_data(&self.file)?;
            match data {
                SectionData::Rel32(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela32(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                SectionData::Rel64(rel_entries) => {
                    iter_entries_and_relocate!(rel_entries, rel_entry);
                }
                SectionData::Rela64(rela_entries) => {
                    iter_entries_and_relocate!(rela_entries, rela_entry);
                }
                _ => return Err(ElfLoaderErr::UnsupportedSectionData),
            }
            Ok(())
        })
    }

    /// Processes a dynamic header section.
    ///
    /// This section contains mostly entry points to other section headers (like relocation).
    /// At the moment this just does sanity checking for relocation later.
    ///
    /// A human readable version of the dynamic section is best obtained with `readelf -d <binary>`.
    fn parse_dynamic<'a>(
        file: &ElfFile,
        dynamic_header: &'a ProgramHeader<'a>,
    ) -> Result<Option<DynamicInfo>, ElfLoaderErr> {
        trace!("load dynamic segement {:?}", dynamic_header);

        // Walk through the dynamic program header and find the rela and sym_tab section offsets:
        let segment = dynamic_header.get_data(file)?;

        // Init result
        let mut info = DynamicInfo {
            flags1: Default::default(),
            rela: 0,
            rela_size: 0,
        };

        // Each entry/section is parsed for the same information currently
        macro_rules! parse_entry_tags {
            ($info:ident, $entry:ident, $tag:ident) => {
                match $tag {
                    // Trace required libs
                    Tag::Needed => {
                        trace!(
                            "Required library {:?}",
                            file.get_dyn_string($entry.get_val()? as _)
                        )
                    }

                    // Rel<T>
                    Tag::Rel => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelSize => $info.rela_size = $entry.get_val()?.into(),

                    // Rela<T>
                    Tag::Rela => $info.rela = $entry.get_ptr()?.into(),
                    Tag::RelaSize => $info.rela_size = $entry.get_val()?.into(),
                    Tag::Flags1 => {
                        $info.flags1 =
                            unsafe { DynamicFlags1::from_bits_unchecked($entry.get_val()? as _) };
                    }
                    _ => trace!("unsupported {:?}", $entry),
                }
            };
        }

        // Helper macro to iterate all entries
        macro_rules! iter_entries_and_parse {
            ($info:ident, $dyn_entries:expr) => {
                for dyn_entry in $dyn_entries {
                    let tag = dyn_entry.get_tag()?;
                    parse_entry_tags!($info, dyn_entry, tag);
                }
            };
        }

        match segment {
            SegmentData::Dynamic32(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            SegmentData::Dynamic64(dyn_entries) => {
                iter_entries_and_parse!(info, dyn_entries);
            }
            _ => {
                return Err(ElfLoaderErr::UnsupportedSectionData);
            }
        };

        trace!(
            "rela size {:?} rela off {:?} flags1 {:?}",
            info.rela_size,
            info.rela,
            info.flags1
        );

        Ok(Some(info))
    }

    /// Processing the program headers and issue commands to loader.
    ///
    /// Will tell loader to create space in the address space / region where the
    /// header is supposed to go, then copy it there, and finally relocate it.
    pub fn load(&self, loader: &mut dyn ElfLoader) -> Result<(), ElfLoaderErr> {
        self.is_loadable()?;

        loader.allocate(self.iter_loadable_headers())?;

        // Load all headers
        for header in self.file.program_iter() {
            let raw = match header {
                Ph32(inner) => inner.raw_data(&self.file),
                Ph64(inner) => inner.raw_data(&self.file),
            };
            let typ = header.get_type()?;
            match typ {
                Type::Load => {
                    loader.load(header.flags(), header.virtual_addr(), raw)?;
                }
                Type::Tls => {
                    loader.tls(
                        header.virtual_addr(),
                        header.file_size(),
                        header.mem_size(),
                        header.align(),
                    )?;
                }
                _ => {} // skip for now
            }
        }

        // Relocate headers
        self.maybe_relocate(loader)?;

        // Process .data.rel.ro
        for header in self.file.program_iter() {
            if header.get_type()? == Type::GnuRelro {
                loader.make_readonly(header.virtual_addr(), header.mem_size() as usize)?
            }
        }

        Ok(())
    }

    fn iter_loadable_headers(&self) -> LoadableHeaders {
        // Trying to determine loadeable headers
        fn select_load(pheader: &ProgramHeader) -> bool {
            match pheader {
                Ph32(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
                Ph64(header) => header
                    .get_type()
                    .map(|typ| typ == Type::Load)
                    .unwrap_or(false),
            }
        }

        // Create an iterator (well filter really) that has all loadeable
        // headers and pass it to the loader
        // TODO: This is pretty ugly, maybe we can do something with impl Trait?
        // https://stackoverflow.com/questions/27535289/what-is-the-correct-way-to-return-an-iterator-or-any-other-trait
        self.file.program_iter().filter(select_load)
    }
}
<|endoftext|>