Skip to content

Commit 0e1e204

Browse files
committed
Replace the nm symbol check with a Rust implementation
1 parent 8ee1aa9 commit 0e1e204

File tree

4 files changed

+126
-0
lines changed

4 files changed

+126
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ members = [
77
"builtins-test",
88
"builtins-test-intrinsics",
99
"compiler-builtins",
10+
"crates/symbol-check",
1011
]
1112

1213
default-members = [

ci/run.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ fi
9494

9595
# Look out for duplicated symbols when we include the compiler-rt (C) implementation
9696
update_rlib_paths
97+
98+
cargo run -p symbol-check -- check-duplicates "${rlib_paths[@]}"
99+
97100
for rlib in "${rlib_paths[@]}"; do
98101
set +x
99102
echo "================================================================"

crates/symbol-check/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "symbol-check"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
object = "0.36.7"

crates/symbol-check/src/main.rs

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#![allow(unused)]
2+
3+
// use ar::Archive;
4+
use object::read::archive::{ArchiveFile, ArchiveMember};
5+
use object::read::elf::FileHeader;
6+
use object::{Object, ObjectSection, ObjectSymbol, Symbol, SymbolKind, SymbolScope};
7+
use std::collections::{BTreeMap, HashSet};
8+
use std::error::Error;
9+
use std::fs;
10+
use std::hash::Hash;
11+
use std::path::{Path, PathBuf};
12+
13+
type Result<T, E = Box<dyn Error>> = std::result::Result<T, E>;
14+
15+
const USAGE: &str = "Usage:
16+
symbol-check check-duplicates PATH...
17+
symbol-check check-core-syms PATH...
18+
";
19+
20+
fn main() {
21+
let args = std::env::args().collect::<Vec<_>>();
22+
let args_ref = args.iter().map(|arg| arg.as_str()).collect::<Vec<_>>();
23+
24+
match &args_ref[1..] {
25+
["check-duplicates", rest @ ..] if !rest.is_empty() => {
26+
rest.iter().for_each(verify_no_duplicates)
27+
}
28+
["check-core-syms", rest @ ..] if !rest.is_empty() => {
29+
rest.iter().for_each(verify_no_duplicates)
30+
}
31+
_ => {
32+
println!("{USAGE}");
33+
std::process::exit(1);
34+
}
35+
}
36+
37+
// Raise an error if the same symbol is present in multiple object files
38+
}
39+
40+
#[derive(Debug, Clone)]
41+
struct SymInfo {
42+
name: String,
43+
kind: SymbolKind,
44+
scope: SymbolScope,
45+
address: u64,
46+
is_weak: bool,
47+
object: String,
48+
}
49+
50+
impl SymInfo {
51+
fn new(sym: Symbol, member: &ArchiveMember) -> Self {
52+
Self {
53+
name: sym.name().expect("missing name").to_owned(),
54+
kind: sym.kind(),
55+
scope: sym.scope(),
56+
address: sym.address(),
57+
is_weak: sym.is_weak(),
58+
object: String::from_utf8_lossy(member.name()).into_owned(),
59+
}
60+
}
61+
}
62+
63+
fn verify_no_duplicates(path: impl AsRef<Path>) {
64+
println!("Checking for duplicates at {:?}", path.as_ref());
65+
66+
// Global defined symbols
67+
let mut syms = BTreeMap::<String, SymInfo>::new();
68+
let mut dups = Vec::new();
69+
70+
for_each_symbol(path, |sym, member| {
71+
if sym.is_global() && !sym.is_undefined() {
72+
let info = SymInfo::new(sym, member);
73+
match syms.get(&info.name) {
74+
Some(existing) => {
75+
dups.push(info);
76+
dups.push(existing.clone());
77+
}
78+
None => {
79+
syms.insert(info.name.clone(), info);
80+
}
81+
}
82+
}
83+
Ok(())
84+
})
85+
.unwrap();
86+
87+
if !dups.is_empty() {
88+
dups.sort_unstable_by(|a, b| a.name.cmp(&b.name));
89+
panic!("Found duplicate symbols: {dups:#?}");
90+
}
91+
}
92+
93+
fn verify_core_symbols(path: impl AsRef<Path>) {
94+
todo!()
95+
}
96+
97+
/// For a given archive path, do something with each symbol.
98+
fn for_each_symbol(
99+
path: impl AsRef<Path>,
100+
mut f: impl FnMut(Symbol, &ArchiveMember) -> Result<()>,
101+
) -> Result<()> {
102+
let archive_data = fs::read(path)?;
103+
let x = ArchiveFile::parse(archive_data.as_slice())?;
104+
for member in x.members() {
105+
let member = member.unwrap();
106+
let data = member.data(&*archive_data).unwrap();
107+
let obj = object::File::parse(data)?;
108+
109+
for sym in obj.symbols() {
110+
f(sym, &member)?;
111+
}
112+
}
113+
114+
Ok(())
115+
}

0 commit comments

Comments
 (0)