diff --git a/Cargo.lock b/Cargo.lock index 72b826b..0fceebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,23 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -131,6 +148,7 @@ dependencies = [ "libc", "log", "nix", + "object", "serde", "serde_json", "tempfile", @@ -147,6 +165,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "criterion" version = "0.3.5" @@ -284,6 +311,29 @@ dependencies = [ "instant", ] +[[package]] +name = "flate2" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "getrandom" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "half" version = "1.8.2" @@ -295,6 +345,9 @@ name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] [[package]] name = "hermit-abi" @@ -405,6 +458,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miniz_oxide" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +dependencies = [ + "adler", +] + [[package]] name = "nix" version = "0.23.1" @@ -437,6 +499,25 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" +dependencies = [ + "crc32fast", + "flate2", + "hashbrown", + "indexmap", + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" + [[package]] name = "oorandom" version = "11.1.3" @@ -735,6 +816,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.3.2" @@ -746,6 +833,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + [[package]] name = "wasm-bindgen" version = "0.2.79" diff --git a/Cargo.toml b/Cargo.toml index cc1b6a4..bcd0f2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ nix = "0.23.1" close_fds = "0.3.2" tempfile = "3.3" +object = { version = "0.28", features = ["write_core"] } [dev-dependencies] criterion = "0.3" diff --git a/src/error.rs b/src/error.rs index abdcab4..80e0b1e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -18,13 +18,22 @@ pub enum Error { #[error("bincode: {0}")] Bincode(#[from] bincode::Error), + #[error("elf: read: {0}")] + ElfRead(#[from] object::read::Error), + + #[error("elf: write: {0}")] + ElfWrite(#[from] object::write::Error), + #[error("bad pipe specification: a pipe must have exactly one reader and one writer: {0}")] BadPipe(String), #[error("bad socket specification: a socket must have exactly one reader and one writer: {0}")] BadFileSocket(String), - #[error("bad specification type: only .json files are supported")] + #[error("no specification provided")] + NoSpecification, + + #[error("bad specification type: only json files are supported")] BadSpecType, #[error("bad trigger argument: this entrypoint is not triggered by something with arguments")] diff --git a/src/lib.rs b/src/lib.rs index 8a7e6d5..f2a51ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ use log::{debug, info}; pub mod clone; mod error; +mod pack; mod spawner; mod specification; mod void; @@ -18,7 +19,6 @@ use std::path::Path; use nix::fcntl::OFlag; use nix::sys::socket; use nix::unistd; - pub struct PackArgs<'a> { pub spec: &'a Path, pub binary: &'a Path, @@ -26,16 +26,14 @@ pub struct PackArgs<'a> { } pub fn pack(args: &PackArgs) -> Result<()> { - let spec: Specification = if args.spec.ends_with(".json") { + let spec: Specification = if args.spec.extension().map(|e| e == "json") == Some(true) { let f = std::fs::File::open(args.spec)?; Ok(serde_json::from_reader(f)?) } else { Err(Error::BadSpecType) }?; - let spec_bin = bincode::serialize(&spec)?; - - Ok(()) + pack::pack_binary(args.binary, &spec, args.output) } pub struct RunArgs<'a> { @@ -56,7 +54,12 @@ pub fn run(args: &RunArgs) -> Result<()> { Err(Error::BadSpecType) } } else { - unimplemented!("reading spec from the elf is unimplemented") + let spec = pack::extract_specification(args.binary)?; + if let Some(s) = spec { + Ok(s) + } else { + Err(Error::NoSpecification) + } }?; debug!("specification read: {:?}", &spec); diff --git a/src/pack.rs b/src/pack.rs new file mode 100644 index 0000000..4cbd72a --- /dev/null +++ b/src/pack.rs @@ -0,0 +1,75 @@ +use crate::{Result, Specification}; + +use std::fs::File; +use std::path::Path; + +use bincode::Options; +use object::endian::Endianness; +use object::read::ReadCache; +use object::read::{Object, ObjectSection}; +use object::write::{StandardSegment, StreamingBuffer}; +use object::SectionKind; + +const SPECIFICATION_SECTION_NAME: &str = "void_specification"; + +pub(crate) fn pack_binary(binary: &Path, spec: &Specification, output: &Path) -> Result<()> { + let binary = File::open(binary)?; + let binary = ReadCache::new(binary); + + let output = File::create(output)?; + let mut output = StreamingBuffer::new(output); + + let input_object = object::File::parse(&binary)?; + + let format = input_object.format(); + let architecture = input_object.architecture(); + let endianness = if input_object.is_little_endian() { + Endianness::Little + } else { + Endianness::Big + }; + + let mut output_object = object::write::Object::new(format, architecture, endianness); + + for input_section in input_object.sections() { + let output_section = output_object.add_section( + input_section.segment_name_bytes()?.unwrap_or(&[]).to_vec(), + input_section.name_bytes()?.to_vec(), + input_section.kind(), + ); + + output_object.set_section_data(output_section, input_section.data()?, 0); + } + + let spec_section = output_object.add_section( + output_object.segment_name(StandardSegment::Debug).to_vec(), + SPECIFICATION_SECTION_NAME.to_string().into(), + SectionKind::Other, + ); + + let spec = bincode_options().serialize(spec)?; + output_object.set_section_data(spec_section, spec, 0); + + output_object.emit(&mut output)?; + Ok(()) +} + +pub(crate) fn extract_specification(binary: &Path) -> Result> { + let binary = File::open(binary)?; + let binary = ReadCache::new(binary); + let input_object = object::File::parse(&binary)?; + + let spec_section = if let Some(s) = input_object.section_by_name(SPECIFICATION_SECTION_NAME) { + s + } else { + return Ok(None); + }; + + let spec_data = spec_section.data()?; + + Ok(Some(bincode_options().deserialize(spec_data)?)) +} + +fn bincode_options() -> impl bincode::Options { + bincode::DefaultOptions::new() +}