Skip to content

Commit

Permalink
Librarify armerge
Browse files Browse the repository at this point in the history
  • Loading branch information
tux3 committed Jul 7, 2022
1 parent d35716f commit eefc5ba
Show file tree
Hide file tree
Showing 17 changed files with 430 additions and 182 deletions.
22 changes: 21 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object = "0.29"
goblin = "0.4"
regex = "1.3.9"
rayon = "1.4.0"
anyhow = { version = "1.0.56", features = ["backtrace"] }
thiserror = "1.0.31"

[features]
# EXPERIMENTAL. Uses objpoke instead of objcopy for localizing ELF symbols in-place. Very fast, but not stable for production use.
Expand Down
9 changes: 5 additions & 4 deletions src/arbuilder.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use anyhow::Result;
use crate::MergeError;
use std::fmt::Debug;
use std::path::Path;

pub mod common;
pub mod mac;

pub trait ArBuilder {
fn append_obj(&mut self, path: &Path) -> Result<()>;
fn close(self: Box<Self>) -> Result<()>;
pub trait ArBuilder: Debug {
fn append_obj(&mut self, path: &Path) -> Result<(), MergeError>;
fn close(self: Box<Self>) -> Result<(), MergeError>;
}

pub fn host_platform_builder(path: &Path, verbose: bool) -> Box<dyn ArBuilder> {
Expand Down
30 changes: 21 additions & 9 deletions src/arbuilder/common.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::arbuilder::ArBuilder;
use crate::archives;
use anyhow::Result;
use crate::{archives, MergeError};
use ar::Builder;
use std::fmt::{Debug, Formatter};
use std::fs::File;
use std::path::{Path, PathBuf};

Expand All @@ -12,28 +12,40 @@ pub struct CommonArBuilder {
verbose: bool,
}

impl Debug for CommonArBuilder {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CommonArBuilder")
.field("output_path", &self.output_path)
.field("closed", &self.closed)
.finish()
}
}

impl ArBuilder for CommonArBuilder {
fn append_obj(&mut self, path: &Path) -> Result<()> {
self.builder.append_path(path)?;
fn append_obj(&mut self, path: &Path) -> Result<(), MergeError> {
self.builder
.append_path(path)
.map_err(MergeError::WritingArchive)?;
Ok(())
}

fn close(mut self: Box<Self>) -> Result<()> {
fn close(mut self: Box<Self>) -> Result<(), MergeError> {
self.finalize_index()
}
}

impl CommonArBuilder {
pub fn new(path: &Path, verbose: bool) -> Self {
pub fn new(path: impl Into<PathBuf>, verbose: bool) -> Self {
let path = path.into();
Self {
builder: Builder::new(File::create(path).expect("Failed to create output library")),
output_path: path.to_owned(),
builder: Builder::new(File::create(&path).expect("Failed to create output library")),
output_path: path,
closed: false,
verbose,
}
}

fn finalize_index(&mut self) -> Result<()> {
fn finalize_index(&mut self) -> Result<(), MergeError> {
if self.closed {
return Ok(());
}
Expand Down
30 changes: 21 additions & 9 deletions src/arbuilder/mac.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::arbuilder::ArBuilder;
use anyhow::Result;
use crate::MergeError;
use crate::MergeError::ExternalToolLaunchError;
use std::ffi::OsString;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::Command;

#[derive(Debug)]
pub struct MacArBuilder {
output_path: PathBuf,
obj_paths: Vec<PathBuf>,
Expand All @@ -13,12 +14,12 @@ pub struct MacArBuilder {
}

impl ArBuilder for MacArBuilder {
fn append_obj(&mut self, path: &Path) -> Result<()> {
fn append_obj(&mut self, path: &Path) -> Result<(), MergeError> {
self.obj_paths.push(path.to_owned());
Ok(())
}

fn close(mut self: Box<Self>) -> Result<()> {
fn close(mut self: Box<Self>) -> Result<(), MergeError> {
self.write_obj()
}
}
Expand All @@ -33,7 +34,7 @@ impl MacArBuilder {
}
}

fn write_obj(&mut self) -> Result<()> {
fn write_obj(&mut self) -> Result<(), MergeError> {
if self.closed {
return Ok(());
}
Expand Down Expand Up @@ -63,13 +64,24 @@ impl MacArBuilder {
);
}

let output = Command::new("libtool").args(args).output()?;
let output =
Command::new("libtool")
.args(&args)
.output()
.map_err(|e| ExternalToolLaunchError {
tool: "libtool".to_string(),
inner: e,
})?;
if output.status.success() {
Ok(())
} else {
std::io::stdout().write_all(&output.stdout).unwrap();
std::io::stderr().write_all(&output.stderr).unwrap();
panic!("Failed to merged object files with `libtool`")
Err(MergeError::ExternalToolError {
reason: "Failed to merge object files with `libtool`".to_string(),
tool: "libtool".to_string(),
args,
stdout: output.stdout,
stderr: output.stderr,
})
}
}
}
120 changes: 80 additions & 40 deletions src/archives.rs
Original file line number Diff line number Diff line change
@@ -1,79 +1,109 @@
use crate::arbuilder::ArBuilder;
use crate::input_library::InputLibrary;
use crate::objects::ObjectTempDir;
use anyhow::{Context, Result};
use ar::{Archive, Entry};
use crate::MergeError::ExternalToolLaunchError;
use crate::{MergeError, ProcessInputError};
use ar::Archive;
use goblin::{peek_bytes, Hint};
use rand::distributions::{Alphanumeric, DistString};
use rand::thread_rng;
use std::fmt::{Debug, Formatter};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::PathBuf;
use std::str::from_utf8;
use std::io::{Read, Write};
use tempdir::TempDir;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ArchiveContents {
/// Only ELF files
Elf,
/// Only Mach-O files
MachO,
Other, // E.g. PE files
/// Only unsupported files (e.g. PE/COFF)
Other,

Empty,
/// Archives contain a mix of file types
Mixed,
/// No contents
Empty,
}

pub struct ExtractedArchive {
pub object_dir: ObjectTempDir,
pub contents_type: ArchiveContents,
}

fn archive_object_type(object: &mut Entry<File>) -> Result<ArchiveContents> {
let hint_bytes = &mut [0u8; 16];
object.read_exact(hint_bytes)?;
object.seek(SeekFrom::Start(0))?;
impl Debug for ExtractedArchive {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ExtractedArchive")
.field("object_dir", &self.object_dir.dir.path())
.field("contents_type", &self.contents_type)
.finish()
}
}

Ok(match peek_bytes(hint_bytes)? {
Hint::Elf(_) => ArchiveContents::Elf,
Hint::Mach(_) | Hint::MachFat(_) => ArchiveContents::MachO,
_ => ArchiveContents::Other,
})
fn archive_object_type(object_header: &[u8; 16]) -> ArchiveContents {
match peek_bytes(object_header) {
Ok(Hint::Elf(_)) => ArchiveContents::Elf,
Ok(Hint::Mach(_) | Hint::MachFat(_)) => ArchiveContents::MachO,
Ok(_) => ArchiveContents::Other,
Err(_) => ArchiveContents::Other, // Malformed input
}
}

pub fn extract_objects(archives: &[PathBuf]) -> Result<ExtractedArchive> {
let dir = TempDir::new("armerge")?;
pub fn extract_objects<I: IntoIterator<Item = InputLibrary<R>>, R: Read>(
input_libraries: I,
) -> Result<ExtractedArchive, ProcessInputError> {
let dir = TempDir::new("armerge").map_err(ProcessInputError::TempDir)?;
let mut objects = Vec::new();
let mut archive_contents = ArchiveContents::Empty;

for archive_path in archives {
let mut archive =
Archive::new(File::open(archive_path).with_context(|| {
format!("Failed to open input file '{}'", archive_path.display())
})?);
let archive_name = archive_path
.file_name()
.unwrap()
.to_string_lossy()
.replace('/', "_");
for input_lib in input_libraries {
let mut archive = Archive::new(input_lib.reader);
while let Some(entry_result) = archive.next_entry() {
let mut entry = entry_result?;
let mut entry = entry_result.map_err(|e| ProcessInputError::ReadingArchive {
name: input_lib.name.clone(),
inner: e,
})?;

let rnd: String = Alphanumeric.sample_string(&mut thread_rng(), 8);
let mut obj_path = dir.path().to_owned();
obj_path.push(format!(
"{}@{}.{}.o",
archive_name,
from_utf8(entry.header().identifier())?,
input_lib.name,
String::from_utf8_lossy(entry.header().identifier()),
&rnd
));

let obj_type = archive_object_type(&mut entry)?;
let hint_bytes = &mut [0u8; 16];
entry
.read_exact(hint_bytes)
.map_err(|e| ProcessInputError::ReadingArchive {
name: input_lib.name.clone(),
inner: e,
})?;
let obj_type = archive_object_type(hint_bytes);
if archive_contents == ArchiveContents::Empty {
archive_contents = obj_type;
} else if archive_contents != obj_type {
archive_contents = ArchiveContents::Mixed
}

let mut file = File::create(&obj_path)?;
std::io::copy(&mut entry, &mut file).unwrap();
let mut file =
File::create(&obj_path).map_err(|e| ProcessInputError::ExtractingObject {
path: obj_path.to_owned(),
inner: e,
})?;
file.write_all(hint_bytes)
.map_err(|e| ProcessInputError::ExtractingObject {
path: obj_path.to_owned(),
inner: e,
})?;
std::io::copy(&mut entry, &mut file).map_err(|e| {
ProcessInputError::ExtractingObject {
path: obj_path.to_owned(),
inner: e,
}
})?;
objects.push(obj_path);
}
}
Expand All @@ -84,24 +114,34 @@ pub fn extract_objects(archives: &[PathBuf]) -> Result<ExtractedArchive> {
})
}

pub fn create_index(archive_path: &std::path::Path, verbose: bool) -> Result<()> {
pub fn create_index(archive_path: &std::path::Path, verbose: bool) -> Result<(), MergeError> {
use std::process::Command;

if verbose {
println!("ranlib {}", archive_path.to_string_lossy());
}

let output = Command::new("ranlib").args(vec![archive_path]).output()?;
let output = Command::new("ranlib")
.args(vec![archive_path])
.output()
.map_err(|e| ExternalToolLaunchError {
tool: "ranlib".to_string(),
inner: e,
})?;
if output.status.success() {
Ok(())
} else {
std::io::stdout().write_all(&output.stdout).unwrap();
std::io::stderr().write_all(&output.stderr).unwrap();
panic!("Failed to create archive index with `ranlib`")
Err(MergeError::ExternalToolError {
reason: "Failed to create archive index with `ranlib`".to_string(),
tool: "ranlib".to_string(),
args: archive_path.iter().map(|p| p.to_owned()).collect(),
stdout: output.stdout,
stderr: output.stderr,
})
}
}

pub fn merge(mut output: Box<dyn ArBuilder>, objects_dir: ObjectTempDir) -> Result<()> {
pub fn merge(mut output: Box<dyn ArBuilder>, objects_dir: ObjectTempDir) -> Result<(), MergeError> {
for obj_path in objects_dir.objects {
output.append_obj(obj_path.as_path())?;
}
Expand Down
Loading

0 comments on commit eefc5ba

Please sign in to comment.