Skip to content

Commit cbb4f13

Browse files
committed
When using system clang libs, filter out duplicate symbols
1 parent a0e66c4 commit cbb4f13

File tree

3 files changed

+100
-19
lines changed

3 files changed

+100
-19
lines changed

Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ core = { version = "1.0.0", optional = true, package = 'rustc-std-workspace-core
3333

3434
[build-dependencies]
3535
cc = { optional = true, version = "1.0" }
36+
ar = { optional = true }
3637

3738
[dev-dependencies]
3839
panic-handler = { path = 'crates/panic-handler' }
@@ -46,7 +47,7 @@ c-vendor = ["cc"]
4647

4748
# Link against system clang_rt.* libraries.
4849
# LLVM_CONFIG or CLANG (more reliable) must be set.
49-
c-system = []
50+
c-system = ["ar"]
5051

5152
c = ["c-vendor"]
5253

build.rs

+77-18
Original file line numberDiff line numberDiff line change
@@ -456,10 +456,15 @@ mod c_vendor {
456456

457457
#[cfg(feature = "c-system")]
458458
mod c_system {
459+
extern crate ar;
460+
461+
use std::collections::HashMap;
459462
use std::env;
463+
use std::fs::File;
460464
use std::process::{Command, Output};
461465
use std::str;
462-
use std::path::Path;
466+
use std::path::{Path, PathBuf};
467+
463468
use sources;
464469

465470
fn success_output(err: &str, cmd: &mut Command) -> Output {
@@ -499,57 +504,111 @@ mod c_system {
499504
r.to_string()
500505
}
501506

507+
// TODO: this really should be part of ar
508+
fn clone_header(h0: &ar::Header) -> ar::Header {
509+
let mut h = ar::Header::new(h0.identifier().to_vec(), h0.size());
510+
h.set_mtime(h0.mtime());
511+
h.set_uid(h0.uid());
512+
h.set_gid(h0.gid());
513+
h.set_mode(h0.mode());
514+
h
515+
}
516+
517+
fn find_library<I>(dirs: I, libname: &str) -> Result<PathBuf, Vec<String>>
518+
where
519+
I: Iterator<Item = PathBuf>
520+
{
521+
let mut paths = Vec::new();
522+
for dir in dirs {
523+
let try_path = dir.join(format!("lib{}.a", libname));
524+
if try_path.exists() {
525+
return Ok(try_path.to_path_buf());
526+
} else {
527+
paths.push(format!("{:?}", try_path))
528+
}
529+
}
530+
Err(paths)
531+
}
532+
502533
/// Link against system clang runtime libraries
503534
pub fn compile(llvm_target: &[&str]) {
504535
let target = env::var("TARGET").unwrap();
505536
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
506537
let compiler_rt_arch = get_arch_name_for_compiler_rtlib();
538+
let out_dir = env::var("OUT_DIR").unwrap();
507539

508540
if ALL_SUPPORTED_ARCHES.split(";").find(|x| *x == compiler_rt_arch) == None {
509541
return;
510542
}
511543

512-
if let Ok(clang) = env::var("CLANG") {
544+
println!("cargo:rerun-if-env-changed=CLANG");
545+
println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
546+
547+
let fullpath = if let Ok(clang) = env::var("CLANG") {
513548
let output = success_output(
514549
"failed to find clang's compiler-rt",
515550
Command::new(clang)
516551
.arg(format!("--target={}", target))
517552
.arg("--rtlib=compiler-rt")
518553
.arg("--print-libgcc-file-name"),
519554
);
520-
let fullpath = Path::new(str::from_utf8(&output.stdout).unwrap());
521-
let libpath = fullpath.parent().unwrap().display();
522-
let libname = fullpath
523-
.file_stem()
524-
.unwrap()
525-
.to_str()
526-
.unwrap()
527-
.trim_start_matches("lib");
528-
println!("cargo:rustc-link-search=native={}", libpath);
529-
println!("cargo:rustc-link-lib=static={}", libname);
555+
let path = str::from_utf8(&output.stdout).unwrap().trim_end();
556+
Path::new(path).to_path_buf()
530557
} else if let Ok(llvm_config) = env::var("LLVM_CONFIG") {
531558
// fallback if clang is not installed
532559
let (subpath, libname) = match target_os.as_str() {
533560
"linux" => ("linux", format!("clang_rt.builtins-{}", &compiler_rt_arch)),
534561
"macos" => ("darwin", "clang_rt.builtins_osx_dynamic".to_string()),
535562
_ => panic!("unsupported target os: {}", target_os),
536563
};
537-
let cmd = format!("ls -1d $({} --libdir)/clang/*/lib/{}", llvm_config, subpath);
538564
let output = success_output(
539-
"failed to find clang's lib dir",
540-
Command::new("sh").args(&["-ec", &cmd]),
565+
"failed to find llvm-config's lib dir",
566+
Command::new(llvm_config).arg("--libdir"),
541567
);
542-
for search_dir in str::from_utf8(&output.stdout).unwrap().lines() {
543-
println!("cargo:rustc-link-search=native={}", search_dir);
568+
let libdir = str::from_utf8(&output.stdout).unwrap().trim_end();
569+
let paths = std::fs::read_dir(Path::new(libdir).join("clang")).unwrap().map(|e| {
570+
e.unwrap().path().join("lib").join(subpath)
571+
});
572+
match find_library(paths, &libname) {
573+
Ok(p) => p,
574+
Err(paths) => panic!("failed to find llvm-config's compiler-rt: {}", paths.join(":")),
544575
}
545-
println!("cargo:rustc-link-lib=static={}", libname);
546576
} else {
547577
panic!("neither CLANG nor LLVM_CONFIG could be read");
578+
};
579+
580+
let mut index = 0;
581+
let mut files = HashMap::new();
582+
let mut orig = ar::Archive::new(File::open(&fullpath).unwrap());
583+
while let Some(entry_result) = orig.next_entry() {
584+
let entry = entry_result.unwrap();
585+
let name = str::from_utf8(entry.header().identifier()).unwrap();
586+
files.insert(name.to_owned(), index);
587+
index += 1;
548588
}
549589

550590
let sources = sources::get_sources(llvm_target);
591+
let mut new = ar::Builder::new(File::create(Path::new(&out_dir).join("libcompiler-rt.a")).unwrap());
551592
for (sym, _src) in sources.map.iter() {
593+
let &i = {
594+
let sym_ = if sym.starts_with("__") { &sym[2..] } else { &sym };
595+
match files.get(&format!("{}.c.o", sym_)) {
596+
Some(i) => i,
597+
None => match files.get(&format!("{}.S.o", sym_)) {
598+
Some(i) => i,
599+
None => panic!("could not find expected symbol {} in {:?}", sym, &fullpath),
600+
},
601+
}
602+
};
603+
orig.jump_to_entry(i).unwrap(); // Work around a stupid rust-ar bug
604+
let mut entry = orig.jump_to_entry(i).unwrap();
605+
let header = clone_header(entry.header());
606+
// TODO: ar really should have an append_entry to avoid the clone
607+
new.append(&header, &mut entry).unwrap();
552608
println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
553609
}
610+
611+
println!("cargo:rustc-link-search=native={}", out_dir);
612+
println!("cargo:rustc-link-lib=static={}", "compiler-rt");
554613
}
555614
}

ci/check_no-duplicate-symbols.sh

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/python3
2+
3+
import subprocess
4+
import sys
5+
6+
out = subprocess.check_output(["nm", "--print-file-name", sys.argv[1]], stderr=subprocess.DEVNULL)
7+
8+
symbols = {}
9+
for i in out.decode("utf-8").splitlines():
10+
(f, o, s) = i.split(":", 2)
11+
(v, t, n) = s.split(" ", 2)
12+
if t == "T":
13+
symbols.setdefault(n, []).append((f, o))
14+
15+
ec = 0
16+
for n, where in symbols.items():
17+
if len(where) > 1:
18+
print(n, where)
19+
ec = 1
20+
21+
sys.exit(ec)

0 commit comments

Comments
 (0)