Skip to content

Commit c65c362

Browse files
committed
resolve error when attempting to link a universal library on macOS
Previously attempting to link universal libraries into libraries (but not binaries) would produce an error that "File too small to be an archive". This works around this by using `object` to extract a library for the target platform when passed a univeral library. Fixes rust-lang#55235
1 parent 6b139c5 commit c65c362

File tree

6 files changed

+80
-3
lines changed

6 files changed

+80
-3
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3297,6 +3297,7 @@ dependencies = [
32973297
"rustc_symbol_mangling",
32983298
"rustc_target",
32993299
"smallvec",
3300+
"tempfile",
33003301
"tracing",
33013302
]
33023303

compiler/rustc_codegen_llvm/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ rustc_target = { path = "../rustc_target" }
3434
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
3535
rustc_ast = { path = "../rustc_ast" }
3636
rustc_span = { path = "../rustc_span" }
37+
tempfile = "3.2.0"

compiler/rustc_codegen_llvm/src/back/archive.rs

+64-3
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22
33
use std::env;
44
use std::ffi::{CStr, CString, OsString};
5-
use std::io;
5+
use std::fs;
6+
use std::io::{self, Write};
67
use std::mem;
78
use std::path::{Path, PathBuf};
89
use std::ptr;
910
use std::str;
1011

12+
use object::read::macho::FatArch;
13+
1114
use crate::common;
1215
use crate::llvm::archive_ro::{ArchiveRO, Child};
1316
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
1417
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
18+
use rustc_data_structures::memmap::Mmap;
1519
use rustc_session::cstore::DllImport;
1620
use rustc_session::Session;
1721

@@ -53,21 +57,78 @@ fn llvm_machine_type(cpu: &str) -> LLVMMachineType {
5357
}
5458
}
5559

60+
fn try_filter_fat_archs(
61+
archs: object::read::Result<&[impl FatArch]>,
62+
target_arch: object::Architecture,
63+
archive_path: &Path,
64+
archive_map_data: &[u8],
65+
) -> io::Result<Option<PathBuf>> {
66+
let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
67+
68+
let desired = match archs.iter().filter(|a| a.architecture() == target_arch).next() {
69+
Some(a) => a,
70+
None => return Ok(None),
71+
};
72+
73+
let (mut new_f, extracted_path) = tempfile::Builder::new()
74+
.suffix(archive_path.file_name().unwrap())
75+
.tempfile()?
76+
.keep()
77+
.unwrap();
78+
79+
new_f.write_all(
80+
desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
81+
)?;
82+
83+
Ok(Some(extracted_path))
84+
}
85+
86+
fn try_extract_macho_fat_archive(
87+
sess: &Session,
88+
archive_path: &Path,
89+
) -> io::Result<Option<PathBuf>> {
90+
let archive_map = unsafe { Mmap::map(fs::File::open(&archive_path)?)? };
91+
let target_arch = match sess.target.arch.as_ref() {
92+
"aarch64" => object::Architecture::Aarch64,
93+
"x86_64" => object::Architecture::X86_64,
94+
_ => return Ok(None),
95+
};
96+
97+
match object::macho::FatHeader::parse(&*archive_map) {
98+
Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC => {
99+
let archs = object::macho::FatHeader::parse_arch32(&*archive_map);
100+
try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
101+
}
102+
Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC_64 => {
103+
let archs = object::macho::FatHeader::parse_arch64(&*archive_map);
104+
try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
105+
}
106+
// Not a FatHeader at all, just return None.
107+
_ => Ok(None),
108+
}
109+
}
110+
56111
impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
57112
fn add_archive(
58113
&mut self,
59114
archive: &Path,
60115
skip: Box<dyn FnMut(&str) -> bool + 'static>,
61116
) -> io::Result<()> {
62-
let archive_ro = match ArchiveRO::open(archive) {
117+
let mut archive = archive.to_path_buf();
118+
if self.sess.target.llvm_target.contains("-apple-macosx") {
119+
if let Some(new_archive) = try_extract_macho_fat_archive(&self.sess, &archive)? {
120+
archive = new_archive
121+
}
122+
}
123+
let archive_ro = match ArchiveRO::open(&archive) {
63124
Ok(ar) => ar,
64125
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
65126
};
66127
if self.additions.iter().any(|ar| ar.path() == archive) {
67128
return Ok(());
68129
}
69130
self.additions.push(Addition::Archive {
70-
path: archive.to_path_buf(),
131+
path: archive,
71132
archive: archive_ro,
72133
skip: Box::new(skip),
73134
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# only-macos
2+
3+
-include ../../run-make-fulldeps/tools.mk
4+
5+
"$(TMPDIR)"/libnative-library.a: native-library.c
6+
$(CC) -arch arm64 -arch x86_64 native-library.c -c -o "$(TMPDIR)"/native-library.o
7+
$(AR) crs "$(TMPDIR)"/libnative-library.a "$(TMPDIR)"/native-library.o
8+
9+
all: "$(TMPDIR)"/libnative-library.a
10+
$(RUSTC) lib.rs --crate-type=lib -L "native=$(TMPDIR)" -l static=native-library
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
extern "C" {
2+
pub fn native_func();
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
void native_func() {}

0 commit comments

Comments
 (0)