Skip to content

Commit 4b12863

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

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
@@ -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,99 @@ mod c_system {
499504
r.to_string()
500505
}
501506

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

508530
if ALL_SUPPORTED_ARCHES.split(";").find(|x| *x == compiler_rt_arch) == None {
509531
return;
510532
}
511533

512-
if let Ok(clang) = env::var("CLANG") {
534+
println!("cargo:rerun-if-env-changed=CLANG");
535+
println!("cargo:rerun-if-env-changed=LLVM_CONFIG");
536+
537+
let fullpath = if let Ok(clang) = env::var("CLANG") {
513538
let output = success_output(
514539
"failed to find clang's compiler-rt",
515540
Command::new(clang)
516541
.arg(format!("--target={}", target))
517542
.arg("--rtlib=compiler-rt")
518543
.arg("--print-libgcc-file-name"),
519544
);
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);
545+
let path = str::from_utf8(&output.stdout).unwrap().trim_end();
546+
Path::new(path).to_path_buf()
530547
} else if let Ok(llvm_config) = env::var("LLVM_CONFIG") {
531548
// fallback if clang is not installed
532549
let (subpath, libname) = match target_os.as_str() {
533550
"linux" => ("linux", format!("clang_rt.builtins-{}", &compiler_rt_arch)),
534551
"macos" => ("darwin", "clang_rt.builtins_osx_dynamic".to_string()),
535552
_ => panic!("unsupported target os: {}", target_os),
536553
};
537-
let cmd = format!("ls -1d $({} --libdir)/clang/*/lib/{}", llvm_config, subpath);
538554
let output = success_output(
539-
"failed to find clang's lib dir",
540-
Command::new("sh").args(&["-ec", &cmd]),
555+
"failed to find llvm-config's lib dir",
556+
Command::new(llvm_config).arg("--libdir"),
541557
);
542-
for search_dir in str::from_utf8(&output.stdout).unwrap().lines() {
543-
println!("cargo:rustc-link-search=native={}", search_dir);
558+
let libdir = str::from_utf8(&output.stdout).unwrap().trim_end();
559+
let paths = std::fs::read_dir(Path::new(libdir).join("clang")).unwrap().map(|e| {
560+
e.unwrap().path().join("lib").join(subpath)
561+
});
562+
match find_library(paths, &libname) {
563+
Ok(p) => p,
564+
Err(paths) => panic!("failed to find llvm-config's compiler-rt: {}", paths.join(":")),
544565
}
545-
println!("cargo:rustc-link-lib=static={}", libname);
546566
} else {
547567
panic!("neither CLANG nor LLVM_CONFIG could be read");
568+
};
569+
570+
let mut index = 0;
571+
let mut files = HashMap::new();
572+
let mut orig = ar::Archive::new(File::open(&fullpath).unwrap());
573+
while let Some(entry_result) = orig.next_entry() {
574+
let entry = entry_result.unwrap();
575+
let name = str::from_utf8(entry.header().identifier()).unwrap();
576+
files.insert(name.to_owned(), index);
577+
index += 1;
548578
}
549579

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

0 commit comments

Comments
 (0)