2022-02-27 05:58:30 +11:00
|
|
|
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<P: AsRef<Path>>(binary: P, symbol: &str) -> Result<bool> {
|
|
|
|
// 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)? {
|
2022-03-01 01:17:04 +11:00
|
|
|
goblin::Object::Elf(obj) => Ok(obj
|
|
|
|
.dynsyms
|
|
|
|
.iter()
|
|
|
|
// We don't filter by functions here since we need to export a constant for CLAP
|
|
|
|
.any(|sym| !sym.is_import() && obj.dynstrtab.get_at(sym.st_name) == Some(symbol))),
|
2022-02-27 05:58:30 +11:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
|
2022-02-27 06:55:33 +11:00
|
|
|
// XXX: Why are all exported symbols on macOS prefixed with an underscore?
|
2023-01-07 02:07:42 +11:00
|
|
|
let symbol = format!("_{symbol}");
|
2022-02-27 06:55:33 +11:00
|
|
|
|
2022-02-27 05:58:30 +11:00
|
|
|
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),
|
|
|
|
}
|
|
|
|
}
|