Skip to content

Commit 8c71b67

Browse files
committed
Auto merge of #98736 - alex:lipo-magic, r=bjorn3
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 invoking `lipo -thin` to extract a library for the target platform when passed a univeral library. Fixes #55235 It's worth acknowledging that this implementation is kind of a horrible hack. Unfortunately I don't know how to do anything better, hopefully this PR will be a jumping off point.
2 parents dd8c3a8 + c65c362 commit 8c71b67

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
@@ -3319,6 +3319,7 @@ dependencies = [
33193319
"rustc_symbol_mangling",
33203320
"rustc_target",
33213321
"smallvec",
3322+
"tempfile",
33223323
"tracing",
33233324
]
33243325

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)