Skip to content

Commit

Permalink
use object as base loader
Browse files Browse the repository at this point in the history
  • Loading branch information
xorpse committed May 5, 2024
1 parent b14cde6 commit c2ba9bc
Show file tree
Hide file tree
Showing 5 changed files with 273 additions and 0 deletions.
2 changes: 2 additions & 0 deletions fugue-high/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ fugue-bv = { version = "0.3", path = "../fugue-bv" }
fugue-bytes = { version = "0.3", path = "../fugue-bytes" }
fugue-ir = { version = "0.3", path = "../fugue-ir" }
nom = "7"
memmap2 = "0.9"
object = "0.35"
ouroboros = "0.18"
regex = "1"
rustc-hash = "1.1"
Expand Down
1 change: 1 addition & 0 deletions fugue-high/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ pub mod icfg;
pub mod ir;
pub mod language;
pub mod lifter;
pub mod loader;
pub mod prelude;
pub mod util;
32 changes: 32 additions & 0 deletions fugue-high/src/loader/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use std::fmt::{Debug, Display};

use thiserror::Error;

use crate::language::LanguageBuilderError;

pub mod object;
pub use self::object::Object;

#[derive(Debug, Error)]
pub enum LoaderError {
#[error("cannot load object: {0}")]
Format(anyhow::Error),
#[error(transparent)]
Language(#[from] LanguageBuilderError),
}

impl LoaderError {
pub fn format<E>(e: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::Format(e.into())
}

pub fn format_with<M>(m: M) -> Self
where
M: Debug + Display + Send + Sync + 'static,
{
Self::Format(anyhow::Error::msg(m))
}
}
92 changes: 92 additions & 0 deletions fugue-high/src/loader/object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
use fugue_bytes::Endian;
use object::{File, Object as _};

use crate::language::{Language, LanguageBuilder, LanguageBuilderError};
use crate::loader::LoaderError;
use crate::util::BytesOrMapping;

#[ouroboros::self_referencing]
struct ObjectInner<'a> {
data: BytesOrMapping<'a>,
#[borrows(data)]
#[covariant]
view: File<'this, &'this BytesOrMapping<'a>>,
}

pub struct Object<'a>(ObjectInner<'a>);

impl<'a> Object<'a> {
pub fn new(data: impl Into<BytesOrMapping<'a>>) -> Result<Self, LoaderError> {
ObjectInner::try_new(data.into(), |data| {
File::parse(data).map_err(LoaderError::format)
})
.map(Self)
}

pub fn endian(&self) -> Endian {
if self.0.borrow_view().is_little_endian() {
Endian::Little
} else {
Endian::Big
}
}

pub fn language(&self, builder: &LanguageBuilder) -> Result<Language, LoaderError> {
let convention = match self.0.borrow_view() {
File::Pe32(_) | File::Pe64(_) => "windows",
File::Elf32(_) | File::Elf64(_) => "gcc",
_ => "default",
};
self.language_with(builder, convention)
}

pub fn language_with(
&self,
builder: &LanguageBuilder,
convention: impl AsRef<str>,
) -> Result<Language, LoaderError> {
use object::{Architecture as A, Endianness as E};

let view = self.0.borrow_view();
let bits = if view.is_64() { 64 } else { 32 };
let conv = convention.as_ref();

let language = match (view.architecture(), view.endianness(), bits) {
(A::Arm, E::Big, 32) => builder.build_with("ARM", Endian::Big, 32, "v7", conv)?,
(A::Arm, E::Little, 32) => builder.build_with("ARM", Endian::Little, 32, "v7", conv)?,
(A::Arm, E::Big, 64) => builder.build_with("AARCH64", Endian::Big, 64, "v8A", conv)?,
(A::Arm, E::Little, 64) => {
builder.build_with("AARCH64", Endian::Little, 64, "v8A", conv)?
}
(A::I386, E::Little, 32) => {
builder.build_with("x86", Endian::Little, 32, "default", conv)?
}
(A::X86_64, E::Little, 64) => {
builder.build_with("x86", Endian::Little, 64, "default", conv)?
}
_ => return Err(LanguageBuilderError::UnsupportedArch.into()),
};

Ok(language)
}
}

#[cfg(test)]
mod test {
use super::Object;

use crate::language::LanguageBuilder;
use crate::util::BytesOrMapping;

#[test]
fn test_elf() -> Result<(), Box<dyn std::error::Error>> {
let lb = LanguageBuilder::new("data/processors")?;
let elf = Object::new(BytesOrMapping::from_file("tests/ls.elf")?)?;

let lang = elf.language(&lb)?;

assert_eq!(lang.translator().architecture().processor(), "x86");

Ok(())
}
}
146 changes: 146 additions & 0 deletions fugue-high/src/util/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,147 @@
use std::borrow::Cow;
use std::fs::File;
use std::io::Error;
use std::ops::Deref;
use std::ops::Range;
use std::path::Path;
use std::sync::Arc;

use memmap2::Mmap;
use object::ReadRef;

pub mod patfind;

pub enum OwnedOrRef<'a, T> {
Owned(T),
Ref(&'a T),
}

impl<'a, T> AsRef<T> for OwnedOrRef<'a, T> {
fn as_ref(&self) -> &T {
match self {
Self::Owned(ref t) => t,
Self::Ref(t) => t,
}
}
}

impl<'a, T> Deref for OwnedOrRef<'a, T> {
type Target = T;

fn deref(&self) -> &Self::Target {
self.as_ref()
}
}

impl<'a, T> From<&'a T> for OwnedOrRef<'a, T> {
fn from(value: &'a T) -> Self {
Self::Ref(value)
}
}

impl<'a, T> From<T> for OwnedOrRef<'a, T> {
fn from(value: T) -> Self {
Self::Owned(value)
}
}

pub enum BytesOrMapping<'a> {
Bytes(Cow<'a, [u8]>),
Mapping(Mmap),
}

impl<'a> AsRef<[u8]> for BytesOrMapping<'a> {
fn as_ref(&self) -> &[u8] {
match self {
Self::Bytes(bytes) => bytes.as_ref(),
Self::Mapping(mapping) => mapping.as_ref(),
}
}
}

impl<'a> Deref for BytesOrMapping<'a> {
type Target = [u8];

fn deref(&self) -> &Self::Target {
self.as_ref()
}
}

impl<'a, T> From<T> for BytesOrMapping<'a>
where
T: Into<Cow<'a, [u8]>> + 'a,
{
fn from(value: T) -> Self {
Self::Bytes(value.into())
}
}

impl<'a> BytesOrMapping<'a> {
pub fn from_bytes(bytes: impl Into<Cow<'a, [u8]>>) -> Self {
BytesOrMapping::Bytes(bytes.into())
}

pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
Ok(BytesOrMapping::Mapping(unsafe {
Mmap::map(&File::open(&path)?)?
}))
}

pub fn into_owned(self) -> BytesOrMapping<'static> {
match self {
Self::Bytes(bytes) => BytesOrMapping::Bytes(Cow::Owned(bytes.into_owned())),
Self::Mapping(mapping) => BytesOrMapping::Mapping(mapping),
}
}

pub fn into_shared(self) -> SharedBytesOrMapping<'a> {
SharedBytesOrMapping(Arc::new(self))
}
}

impl<'a> ReadRef<'a> for &'a BytesOrMapping<'_> {
fn len(self) -> Result<u64, ()> {
<&'a [u8] as ReadRef<'a>>::len(<[u8]>::as_ref(self))
}

fn read_bytes_at(self, offset: u64, size: u64) -> Result<&'a [u8], ()> {
<&'a [u8] as ReadRef<'a>>::read_bytes_at(<[u8]>::as_ref(self), offset, size)
}

fn read_bytes_at_until(self, range: Range<u64>, delimiter: u8) -> Result<&'a [u8], ()> {
<&'a [u8] as ReadRef<'a>>::read_bytes_at_until(<[u8]>::as_ref(self), range, delimiter)
}
}

#[derive(Clone)]
#[repr(transparent)]
pub struct SharedBytesOrMapping<'a>(Arc<BytesOrMapping<'a>>);

impl<'a> AsRef<[u8]> for SharedBytesOrMapping<'a> {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}

impl<'a> Deref for SharedBytesOrMapping<'a> {
type Target = [u8];

fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}

impl<'a, T> From<T> for SharedBytesOrMapping<'a>
where
T: Into<Cow<'a, [u8]>> + 'a,
{
fn from(value: T) -> Self {
BytesOrMapping::Bytes(value.into()).into_shared()
}
}

impl<'a> From<BytesOrMapping<'a>> for SharedBytesOrMapping<'a> {
fn from(value: BytesOrMapping<'a>) -> Self {
value.into_shared()
}
}

0 comments on commit c2ba9bc

Please sign in to comment.