symcheck: Add a wrapper around an archive
Rather than re-opening the archive file for each check, add a wrapper that keeps the data in memory. Additionally, collect the `--target` argument so it can be used within this crate.
This commit is contained in:
parent
bcc96da3c3
commit
9f4670fd2b
1 changed files with 80 additions and 24 deletions
|
|
@ -8,7 +8,9 @@ use std::path::{Path, PathBuf};
|
|||
use std::process::{Command, Stdio};
|
||||
|
||||
use object::read::archive::{ArchiveFile, ArchiveMember};
|
||||
use object::{Object, ObjectSymbol, Symbol, SymbolKind, SymbolScope, SymbolSection};
|
||||
use object::{
|
||||
File as ObjFile, Object, ObjectSymbol, Symbol, SymbolKind, SymbolScope, SymbolSection,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
const CHECK_LIBRARIES: &[&str] = &["compiler_builtins", "builtins_test_intrinsics"];
|
||||
|
|
@ -28,13 +30,11 @@ fn main() {
|
|||
let args_ref = args.iter().map(String::as_str).collect::<Vec<_>>();
|
||||
|
||||
match &args_ref[1..] {
|
||||
["build-and-check", rest @ ..] if !rest.is_empty() => {
|
||||
let paths = exec_cargo_with_args(rest);
|
||||
for path in paths {
|
||||
println!("Checking {}", path.display());
|
||||
verify_no_duplicates(&path);
|
||||
verify_core_symbols(&path);
|
||||
}
|
||||
["build-and-check", "--target", target, args @ ..] if !args.is_empty() => {
|
||||
run_build_and_check(Some(target), args);
|
||||
}
|
||||
["build-and-check", args @ ..] if !args.is_empty() => {
|
||||
run_build_and_check(None, args);
|
||||
}
|
||||
_ => {
|
||||
println!("{USAGE}");
|
||||
|
|
@ -43,12 +43,42 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
fn run_build_and_check(target: Option<&str>, args: &[&str]) {
|
||||
let paths = exec_cargo_with_args(target, args);
|
||||
for path in paths {
|
||||
println!("Checking {}", path.display());
|
||||
let archive = Archive::from_path(&path);
|
||||
|
||||
verify_no_duplicates(&archive);
|
||||
verify_core_symbols(&archive);
|
||||
}
|
||||
}
|
||||
|
||||
fn host_target() -> String {
|
||||
let out = Command::new("rustc")
|
||||
.arg("--version")
|
||||
.arg("--verbose")
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(out.status.success());
|
||||
let out = String::from_utf8(out.stdout).unwrap();
|
||||
out.lines()
|
||||
.find_map(|s| s.strip_prefix("host: "))
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
/// Run `cargo build` with the provided additional arguments, collecting the list of created
|
||||
/// libraries.
|
||||
fn exec_cargo_with_args(args: &[&str]) -> Vec<PathBuf> {
|
||||
fn exec_cargo_with_args(target: Option<&str>, args: &[&str]) -> Vec<PathBuf> {
|
||||
let mut host = String::new();
|
||||
let target = target.unwrap_or_else(|| {
|
||||
host = host_target();
|
||||
host.as_str()
|
||||
});
|
||||
|
||||
let mut cmd = Command::new("cargo");
|
||||
cmd.arg("build")
|
||||
.arg("--message-format=json")
|
||||
cmd.args(["build", "--target", target, "--message-format=json"])
|
||||
.args(args)
|
||||
.stdout(Stdio::piped());
|
||||
|
||||
|
|
@ -133,12 +163,12 @@ impl SymInfo {
|
|||
/// Note that this will also locate cases where a symbol is weakly defined in more than one place.
|
||||
/// Technically there are no linker errors that will come from this, but it keeps our binary more
|
||||
/// straightforward and saves some distribution size.
|
||||
fn verify_no_duplicates(path: &Path) {
|
||||
fn verify_no_duplicates(archive: &Archive) {
|
||||
let mut syms = BTreeMap::<String, SymInfo>::new();
|
||||
let mut dups = Vec::new();
|
||||
let mut found_any = false;
|
||||
|
||||
for_each_symbol(path, |symbol, member| {
|
||||
archive.for_each_symbol(|symbol, member| {
|
||||
// Only check defined globals
|
||||
if !symbol.is_global() || symbol.is_undefined() {
|
||||
return;
|
||||
|
|
@ -185,12 +215,12 @@ fn verify_no_duplicates(path: &Path) {
|
|||
}
|
||||
|
||||
/// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined.
|
||||
fn verify_core_symbols(path: &Path) {
|
||||
fn verify_core_symbols(archive: &Archive) {
|
||||
let mut defined = BTreeSet::new();
|
||||
let mut undefined = Vec::new();
|
||||
let mut has_symbols = false;
|
||||
|
||||
for_each_symbol(path, |symbol, member| {
|
||||
archive.for_each_symbol(|symbol, member| {
|
||||
has_symbols = true;
|
||||
|
||||
// Find only symbols from `core`
|
||||
|
|
@ -219,14 +249,40 @@ fn verify_core_symbols(path: &Path) {
|
|||
println!(" success: no undefined references to core found");
|
||||
}
|
||||
|
||||
/// For a given archive path, do something with each symbol.
|
||||
fn for_each_symbol(path: &Path, mut f: impl FnMut(Symbol, &ArchiveMember)) {
|
||||
let data = fs::read(path).expect("reading file failed");
|
||||
let archive = ArchiveFile::parse(data.as_slice()).expect("archive parse failed");
|
||||
for member in archive.members() {
|
||||
let member = member.expect("failed to access member");
|
||||
let obj_data = member.data(&*data).expect("failed to access object");
|
||||
let obj = object::File::parse(obj_data).expect("failed to parse object");
|
||||
obj.symbols().for_each(|sym| f(sym, &member));
|
||||
/// Thin wrapper for owning data used by `object`.
|
||||
struct Archive {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Archive {
|
||||
fn from_path(path: &Path) -> Self {
|
||||
Self {
|
||||
data: fs::read(path).expect("reading file failed"),
|
||||
}
|
||||
}
|
||||
|
||||
fn file(&self) -> ArchiveFile<'_> {
|
||||
ArchiveFile::parse(self.data.as_slice()).expect("archive parse failed")
|
||||
}
|
||||
|
||||
/// For a given archive, do something with each object file.
|
||||
fn for_each_object(&self, mut f: impl FnMut(ObjFile, &ArchiveMember)) {
|
||||
let archive = self.file();
|
||||
|
||||
for member in archive.members() {
|
||||
let member = member.expect("failed to access member");
|
||||
let obj_data = member
|
||||
.data(self.data.as_slice())
|
||||
.expect("failed to access object");
|
||||
let obj = ObjFile::parse(obj_data).expect("failed to parse object");
|
||||
f(obj, &member);
|
||||
}
|
||||
}
|
||||
|
||||
/// For a given archive, do something with each symbol.
|
||||
fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ArchiveMember)) {
|
||||
self.for_each_object(|obj, member| {
|
||||
obj.symbols().for_each(|sym| f(sym, member));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue