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.
This commit is contained in:
commit
8c71b67159
@ -3319,6 +3319,7 @@ dependencies = [
|
||||
"rustc_symbol_mangling",
|
||||
"rustc_target",
|
||||
"smallvec",
|
||||
"tempfile",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
|
@ -34,3 +34,4 @@ rustc_target = { path = "../rustc_target" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
rustc_ast = { path = "../rustc_ast" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
tempfile = "3.2.0"
|
||||
|
@ -2,16 +2,20 @@
|
||||
|
||||
use std::env;
|
||||
use std::ffi::{CStr, CString, OsString};
|
||||
use std::io;
|
||||
use std::fs;
|
||||
use std::io::{self, Write};
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
|
||||
use object::read::macho::FatArch;
|
||||
|
||||
use crate::common;
|
||||
use crate::llvm::archive_ro::{ArchiveRO, Child};
|
||||
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
|
||||
use rustc_codegen_ssa::back::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
|
||||
use rustc_data_structures::memmap::Mmap;
|
||||
use rustc_session::cstore::DllImport;
|
||||
use rustc_session::Session;
|
||||
|
||||
@ -53,13 +57,70 @@ fn llvm_machine_type(cpu: &str) -> LLVMMachineType {
|
||||
}
|
||||
}
|
||||
|
||||
fn try_filter_fat_archs(
|
||||
archs: object::read::Result<&[impl FatArch]>,
|
||||
target_arch: object::Architecture,
|
||||
archive_path: &Path,
|
||||
archive_map_data: &[u8],
|
||||
) -> io::Result<Option<PathBuf>> {
|
||||
let archs = archs.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
|
||||
let desired = match archs.iter().filter(|a| a.architecture() == target_arch).next() {
|
||||
Some(a) => a,
|
||||
None => return Ok(None),
|
||||
};
|
||||
|
||||
let (mut new_f, extracted_path) = tempfile::Builder::new()
|
||||
.suffix(archive_path.file_name().unwrap())
|
||||
.tempfile()?
|
||||
.keep()
|
||||
.unwrap();
|
||||
|
||||
new_f.write_all(
|
||||
desired.data(archive_map_data).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
|
||||
)?;
|
||||
|
||||
Ok(Some(extracted_path))
|
||||
}
|
||||
|
||||
fn try_extract_macho_fat_archive(
|
||||
sess: &Session,
|
||||
archive_path: &Path,
|
||||
) -> io::Result<Option<PathBuf>> {
|
||||
let archive_map = unsafe { Mmap::map(fs::File::open(&archive_path)?)? };
|
||||
let target_arch = match sess.target.arch.as_ref() {
|
||||
"aarch64" => object::Architecture::Aarch64,
|
||||
"x86_64" => object::Architecture::X86_64,
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
match object::macho::FatHeader::parse(&*archive_map) {
|
||||
Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC => {
|
||||
let archs = object::macho::FatHeader::parse_arch32(&*archive_map);
|
||||
try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
|
||||
}
|
||||
Ok(h) if h.magic.get(object::endian::BigEndian) == object::macho::FAT_MAGIC_64 => {
|
||||
let archs = object::macho::FatHeader::parse_arch64(&*archive_map);
|
||||
try_filter_fat_archs(archs, target_arch, archive_path, &*archive_map)
|
||||
}
|
||||
// Not a FatHeader at all, just return None.
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
|
||||
fn add_archive(
|
||||
&mut self,
|
||||
archive: &Path,
|
||||
skip: Box<dyn FnMut(&str) -> bool + 'static>,
|
||||
) -> io::Result<()> {
|
||||
let archive_ro = match ArchiveRO::open(archive) {
|
||||
let mut archive = archive.to_path_buf();
|
||||
if self.sess.target.llvm_target.contains("-apple-macosx") {
|
||||
if let Some(new_archive) = try_extract_macho_fat_archive(&self.sess, &archive)? {
|
||||
archive = new_archive
|
||||
}
|
||||
}
|
||||
let archive_ro = match ArchiveRO::open(&archive) {
|
||||
Ok(ar) => ar,
|
||||
Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)),
|
||||
};
|
||||
@ -67,7 +128,7 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
|
||||
return Ok(());
|
||||
}
|
||||
self.additions.push(Addition::Archive {
|
||||
path: archive.to_path_buf(),
|
||||
path: archive,
|
||||
archive: archive_ro,
|
||||
skip: Box::new(skip),
|
||||
});
|
||||
|
10
src/test/run-make/macos-fat-archive/Makefile
Normal file
10
src/test/run-make/macos-fat-archive/Makefile
Normal file
@ -0,0 +1,10 @@
|
||||
# only-macos
|
||||
|
||||
-include ../../run-make-fulldeps/tools.mk
|
||||
|
||||
"$(TMPDIR)"/libnative-library.a: native-library.c
|
||||
$(CC) -arch arm64 -arch x86_64 native-library.c -c -o "$(TMPDIR)"/native-library.o
|
||||
$(AR) crs "$(TMPDIR)"/libnative-library.a "$(TMPDIR)"/native-library.o
|
||||
|
||||
all: "$(TMPDIR)"/libnative-library.a
|
||||
$(RUSTC) lib.rs --crate-type=lib -L "native=$(TMPDIR)" -l static=native-library
|
3
src/test/run-make/macos-fat-archive/lib.rs
Normal file
3
src/test/run-make/macos-fat-archive/lib.rs
Normal file
@ -0,0 +1,3 @@
|
||||
extern "C" {
|
||||
pub fn native_func();
|
||||
}
|
1
src/test/run-make/macos-fat-archive/native-library.c
Normal file
1
src/test/run-make/macos-fat-archive/native-library.c
Normal file
@ -0,0 +1 @@
|
||||
void native_func() {}
|
Loading…
x
Reference in New Issue
Block a user