Skip to content

Commit eeaa5d2

Browse files
committed
When using system clang libs, filter out duplicate symbols
1 parent 940006d commit eeaa5d2

File tree

2 files changed

+67
-19
lines changed

2 files changed

+67
-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, version = "0.8" }
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

+65-18
Original file line numberDiff line numberDiff line change
@@ -459,10 +459,15 @@ mod c_vendor {
459459

460460
#[cfg(feature = "c-system")]
461461
mod c_system {
462+
extern crate ar;
463+
464+
use std::collections::HashMap;
462465
use std::env;
466+
use std::fs::File;
463467
use std::process::{Command, Output};
464468
use std::str;
465-
use std::path::Path;
469+
use std::path::{Path, PathBuf};
470+
466471
use sources;
467472

468473
fn success_output(err: &str, cmd: &mut Command) -> Output {
@@ -502,57 +507,99 @@ mod c_system {
502507
r.to_string()
503508
}
504509

510+
fn find_library<I>(dirs: I, libname: &str) -> Result<PathBuf, Vec<String>>
511+
where
512+
I: Iterator<Item = PathBuf>
513+
{
514+
let mut paths = Vec::new();
515+
for dir in dirs {
516+
let try_path = dir.join(format!("lib{}.a", libname));
517+
if try_path.exists() {
518+
return Ok(try_path.to_path_buf());
519+
} else {
520+
paths.push(format!("{:?}", try_path))
521+
}
522+
}
523+
Err(paths)
524+
}
525+
505526
/// Link against system clang runtime libraries
506527
pub fn compile(llvm_target: &[&str]) {
507528
let target = env::var("TARGET").unwrap();
508529
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
509530
let compiler_rt_arch = get_arch_name_for_compiler_rtlib();
531+
let out_dir = env::var("OUT_DIR").unwrap();
510532

511533
if ALL_SUPPORTED_ARCHES.split(";").find(|x| *x == compiler_rt_arch) == None {
512534
return;
513535
}
514536

515-
if let Ok(clang) = env::var("CLANG") {
537+
println!("cargo:rerun-if-env-changed=CLANG");
538+
println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
539+
540+
let fullpath = if let Ok(clang) = env::var("CLANG") {
516541
let output = success_output(
517542
"failed to find clang's compiler-rt",
518543
Command::new(clang)
519544
.arg(format!("--target={}", target))
520545
.arg("--rtlib=compiler-rt")
521546
.arg("--print-libgcc-file-name"),
522547
);
523-
let fullpath = Path::new(str::from_utf8(&output.stdout).unwrap());
524-
let libpath = fullpath.parent().unwrap().display();
525-
let libname = fullpath
526-
.file_stem()
527-
.unwrap()
528-
.to_str()
529-
.unwrap()
530-
.trim_start_matches("lib");
531-
println!("cargo:rustc-link-search=native={}", libpath);
532-
println!("cargo:rustc-link-lib=static={}", libname);
548+
let path = str::from_utf8(&output.stdout).unwrap().trim_end();
549+
Path::new(path).to_path_buf()
533550
} else if let Ok(llvm_config) = env::var("LLVM_CONFIG") {
534551
// fallback if clang is not installed
535552
let (subpath, libname) = match target_os.as_str() {
536553
"linux" => ("linux", format!("clang_rt.builtins-{}", &compiler_rt_arch)),
537554
"macos" => ("darwin", "clang_rt.builtins_osx_dynamic".to_string()),
538555
_ => panic!("unsupported target os: {}", target_os),
539556
};
540-
let cmd = format!("ls -1d $({} --libdir)/clang/*/lib/{}", llvm_config, subpath);
541557
let output = success_output(
542-
"failed to find clang's lib dir",
543-
Command::new("sh").args(&["-ec", &cmd]),
558+
"failed to find llvm-config's lib dir",
559+
Command::new(llvm_config).arg("--libdir"),
544560
);
545-
for search_dir in str::from_utf8(&output.stdout).unwrap().lines() {
546-
println!("cargo:rustc-link-search=native={}", search_dir);
561+
let libdir = str::from_utf8(&output.stdout).unwrap().trim_end();
562+
let paths = std::fs::read_dir(Path::new(libdir).join("clang")).unwrap().map(|e| {
563+
e.unwrap().path().join("lib").join(subpath)
564+
});
565+
match find_library(paths, &libname) {
566+
Ok(p) => p,
567+
Err(paths) => panic!("failed to find llvm-config's compiler-rt: {}", paths.join(":")),
547568
}
548-
println!("cargo:rustc-link-lib=static={}", libname);
549569
} else {
550570
panic!("neither CLANG nor LLVM_CONFIG could be read");
571+
};
572+
573+
let mut index = 0;
574+
let mut files = HashMap::new();
575+
let mut orig = ar::Archive::new(File::open(&fullpath).unwrap());
576+
while let Some(entry_result) = orig.next_entry() {
577+
let entry = entry_result.unwrap();
578+
let name = str::from_utf8(entry.header().identifier()).unwrap();
579+
files.insert(name.to_owned(), index);
580+
index += 1;
551581
}
552582

553583
let sources = sources::get_sources(llvm_target);
584+
let mut new = ar::Builder::new(File::create(Path::new(&out_dir).join("libcompiler-rt.a")).unwrap());
554585
for (sym, _src) in sources.map.iter() {
586+
let &i = {
587+
let sym_ = if sym.starts_with("__") { &sym[2..] } else { &sym };
588+
match files.get(&format!("{}.c.o", sym_)) {
589+
Some(i) => i,
590+
None => match files.get(&format!("{}.S.o", sym_)) {
591+
Some(i) => i,
592+
None => panic!("could not find expected symbol {} in {:?}", sym, &fullpath),
593+
},
594+
}
595+
};
596+
let mut entry = orig.jump_to_entry(i).unwrap();
597+
// TODO: ar really should have an append_entry to avoid the clone
598+
new.append(&entry.header().clone(), &mut entry).unwrap();
555599
println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
556600
}
601+
602+
println!("cargo:rustc-link-search=native={}", out_dir);
603+
println!("cargo:rustc-link-lib=static={}", "compiler-rt");
557604
}
558605
}

0 commit comments

Comments
 (0)