From d39ebb5b51a9f5d3395364c905a4281683bc1e17 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 26 Feb 2022 19:58:30 +0100 Subject: [PATCH] Add a bundler module for parsing exported symbols We can use this to detect which plugin formats are supported by a plugin. Otherwise the bundler would be very awkward to use when supporting multiple formats. --- Cargo.lock | 38 ++++++++++++++++++++++++++++++++++++++ xtask/Cargo.toml | 1 + xtask/src/main.rs | 2 ++ xtask/src/symbols.rs | 31 +++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 xtask/src/symbols.rs diff --git a/Cargo.lock b/Cargo.lock index 9032c87c..52ff918f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -400,6 +400,17 @@ dependencies = [ "xml-rs", ] +[[package]] +name = "goblin" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c955ab4e0ad8c843ea653a3d143048b87490d9be56bd7132a435c2407846ac8f" +dependencies = [ + "log", + "plain", + "scroll", +] + [[package]] name = "itoa" version = "1.0.1" @@ -665,6 +676,12 @@ version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + [[package]] name = "proc-macro2" version = "1.0.36" @@ -728,6 +745,26 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serde" version = "1.0.136" @@ -1118,6 +1155,7 @@ name = "xtask" version = "0.1.0" dependencies = [ "anyhow", + "goblin", "serde", "toml", ] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index e27edc69..aa943fc5 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -7,5 +7,6 @@ license = "ISC" [dependencies] anyhow = "1.0" +goblin = "0.5" serde = { version = "1.0", features = ["derive"] } toml = "0.5" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 9dda1610..7c26728e 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -5,6 +5,8 @@ use std::fs; use std::path::Path; use std::process::Command; +mod symbols; + const USAGE_STRING: &str = "Usage: cargo xtask bundle [--release] [--target ] [--bundle-vst3]"; diff --git a/xtask/src/symbols.rs b/xtask/src/symbols.rs new file mode 100644 index 00000000..f1cec36f --- /dev/null +++ b/xtask/src/symbols.rs @@ -0,0 +1,31 @@ +use anyhow::{bail, Context, Result}; +use std::fs; +use std::path::Path; + +/// Check whether a binary exports the specified symbol. Used to detect the plugin formats supported +/// by a plugin library. Returns an error if the binary cuuld not be read. This function will also +/// parse non-native binaries. +pub fn exported>(binary: P, symbol: &str) -> Result { + // Parsing the raw binary instead of relying on nm-like tools makes cross compiling a bit easier + let bytes = fs::read(&binary) + .with_context(|| format!("Could not read '{}'", binary.as_ref().display()))?; + match goblin::Object::parse(&bytes)? { + goblin::Object::Elf(obj) => Ok(obj.dynsyms.iter().any(|sym| { + !sym.is_import() + && sym.is_function() + && obj.dynstrtab.get_at(sym.st_name) == Some(symbol) + })), + goblin::Object::Mach(obj) => { + let obj = match obj { + goblin::mach::Mach::Fat(arches) => arches + .get(0) + .context("Fat Mach-O binary without any binaries")?, + goblin::mach::Mach::Binary(obj) => obj, + }; + + Ok(obj.exports()?.into_iter().any(|sym| sym.name == symbol)) + } + goblin::Object::PE(obj) => Ok(obj.exports.iter().any(|sym| sym.name == Some(symbol))), + obj => bail!("Unsupported object type: {:?}", obj), + } +}