Skip to content

Commit a614590

Browse files
committed
symcheck: Support both archives and object files
If parsing as an archive is unsuccessful, try parsing as an object instead before erroring out.
1 parent 686dc87 commit a614590

File tree

1 file changed

+54
-28
lines changed

1 file changed

+54
-28
lines changed

crates/symbol-check/src/main.rs

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ use std::io::{BufRead, BufReader};
77
use std::path::{Path, PathBuf};
88
use std::process::{Command, Stdio};
99

10-
use object::read::archive::{ArchiveFile, ArchiveMember};
10+
use object::read::archive::ArchiveFile;
1111
use object::{
12-
File as ObjFile, Object, ObjectSection, ObjectSymbol, Symbol, SymbolKind, SymbolScope,
12+
File as ObjFile, Object, ObjectSection, ObjectSymbol, Result as ObjResult, Symbol, SymbolKind,
13+
SymbolScope,
1314
};
1415
use serde_json::Value;
1516

@@ -25,9 +26,10 @@ Cargo will get invoked with `CARGO_ARGS` and the specified target. All output
2526
2627
If TARGET is not specified, the host target is used.
2728
28-
check ARCHIVE_PATHS ...
29+
check PATHS ...
2930
30-
Run the same checks on the given set of paths, without invoking Cargo.
31+
Run the same checks on the given set of paths, without invoking Cargo. Paths
32+
may be either archives or object files.
3133
";
3234

3335
fn main() {
@@ -71,7 +73,7 @@ fn check_paths<P: AsRef<Path>>(paths: &[P]) {
7173
for path in paths {
7274
let path = path.as_ref();
7375
println!("Checking {}", path.display());
74-
let archive = Archive::from_path(path);
76+
let archive = BinFile::from_path(path);
7577

7678
verify_no_duplicates(&archive);
7779
verify_core_symbols(&archive);
@@ -174,7 +176,7 @@ struct SymInfo {
174176
}
175177

176178
impl SymInfo {
177-
fn new(sym: &Symbol, obj: &ObjFile, member: &ArchiveMember) -> Self {
179+
fn new(sym: &Symbol, obj: &ObjFile, obj_path: &str) -> Self {
178180
// Include the section name if possible. Fall back to the `Section` debug impl if not.
179181
let section = sym.section();
180182
let section_name = sym
@@ -196,7 +198,7 @@ impl SymInfo {
196198
is_weak: sym.is_weak(),
197199
is_common: sym.is_common(),
198200
address: sym.address(),
199-
object: String::from_utf8_lossy(member.name()).into_owned(),
201+
object: obj_path.to_owned(),
200202
}
201203
}
202204
}
@@ -206,7 +208,7 @@ impl SymInfo {
206208
/// Note that this will also locate cases where a symbol is weakly defined in more than one place.
207209
/// Technically there are no linker errors that will come from this, but it keeps our binary more
208210
/// straightforward and saves some distribution size.
209-
fn verify_no_duplicates(archive: &Archive) {
211+
fn verify_no_duplicates(archive: &BinFile) {
210212
let mut syms = BTreeMap::<String, SymInfo>::new();
211213
let mut dups = Vec::new();
212214
let mut found_any = false;
@@ -263,7 +265,7 @@ fn verify_no_duplicates(archive: &Archive) {
263265
}
264266

265267
/// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined.
266-
fn verify_core_symbols(archive: &Archive) {
268+
fn verify_core_symbols(archive: &BinFile) {
267269
let mut defined = BTreeSet::new();
268270
let mut undefined = Vec::new();
269271
let mut has_symbols = false;
@@ -298,39 +300,63 @@ fn verify_core_symbols(archive: &Archive) {
298300
}
299301

300302
/// Thin wrapper for owning data used by `object`.
301-
struct Archive {
303+
struct BinFile {
304+
path: PathBuf,
302305
data: Vec<u8>,
303306
}
304307

305-
impl Archive {
308+
impl BinFile {
306309
fn from_path(path: &Path) -> Self {
307310
Self {
311+
path: path.to_owned(),
308312
data: fs::read(path).expect("reading file failed"),
309313
}
310314
}
311315

312-
fn file(&self) -> ArchiveFile<'_> {
313-
ArchiveFile::parse(self.data.as_slice()).expect("archive parse failed")
316+
fn as_archive_file(&self) -> ObjResult<ArchiveFile<'_>> {
317+
ArchiveFile::parse(self.data.as_slice())
314318
}
315319

316-
/// For a given archive, do something with each object file.
317-
fn for_each_object(&self, mut f: impl FnMut(ObjFile, &ArchiveMember)) {
318-
let archive = self.file();
319-
320-
for member in archive.members() {
321-
let member = member.expect("failed to access member");
322-
let obj_data = member
323-
.data(self.data.as_slice())
324-
.expect("failed to access object");
325-
let obj = ObjFile::parse(obj_data).expect("failed to parse object");
326-
f(obj, &member);
320+
fn as_obj_file(&self) -> ObjResult<ObjFile<'_>> {
321+
ObjFile::parse(self.data.as_slice())
322+
}
323+
324+
/// For a given archive, do something with each object file. For an object file, do
325+
/// something once.
326+
fn for_each_object(&self, mut f: impl FnMut(ObjFile, &str)) {
327+
// Try as an archive first.
328+
let as_archive = self.as_archive_file();
329+
if let Ok(archive) = as_archive {
330+
for member in archive.members() {
331+
let member = member.expect("failed to access member");
332+
let obj_data = member
333+
.data(self.data.as_slice())
334+
.expect("failed to access object");
335+
let obj = ObjFile::parse(obj_data).expect("failed to parse object");
336+
f(obj, &String::from_utf8_lossy(member.name()));
337+
}
338+
339+
return;
327340
}
341+
342+
// Fall back to parsing as an object file.
343+
let as_obj = self.as_obj_file();
344+
if let Ok(obj) = as_obj {
345+
f(obj, &self.path.to_string_lossy());
346+
return;
347+
}
348+
349+
panic!(
350+
"failed to parse as either archive or object file: {:?}, {:?}",
351+
as_archive.unwrap_err(),
352+
as_obj.unwrap_err(),
353+
);
328354
}
329355

330-
/// For a given archive, do something with each symbol.
331-
fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ObjFile, &ArchiveMember)) {
332-
self.for_each_object(|obj, member| {
333-
obj.symbols().for_each(|sym| f(sym, &obj, member));
356+
/// D something with each symbol in an archive or object file.
357+
fn for_each_symbol(&self, mut f: impl FnMut(Symbol, &ObjFile, &str)) {
358+
self.for_each_object(|obj, obj_path| {
359+
obj.symbols().for_each(|sym| f(sym, &obj, obj_path));
334360
});
335361
}
336362
}

0 commit comments

Comments
 (0)