From c65c36242e99ddbf787dcdd9bd2d14f42f7cfce6 Mon Sep 17 00:00:00 2001 From: Alex Gaynor Date: Thu, 30 Jun 2022 15:01:38 -0400 Subject: [PATCH] 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 #55235 --- Cargo.lock | 1 + compiler/rustc_codegen_llvm/Cargo.toml | 1 + .../rustc_codegen_llvm/src/back/archive.rs | 67 ++++++++++++++++++- src/test/run-make/macos-fat-archive/Makefile | 10 +++ src/test/run-make/macos-fat-archive/lib.rs | 3 + .../macos-fat-archive/native-library.c | 1 + 6 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 src/test/run-make/macos-fat-archive/Makefile create mode 100644 src/test/run-make/macos-fat-archive/lib.rs create mode 100644 src/test/run-make/macos-fat-archive/native-library.c diff --git a/Cargo.lock b/Cargo.lock index 6b2146ad3ed26..6814d533d1cce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3297,6 +3297,7 @@ dependencies = [ "rustc_symbol_mangling", "rustc_target", "smallvec", + "tempfile", "tracing", ] diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index a068aa2ec6244..df4a9fea19d74 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -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" diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs index 38a366095b41d..20a063f80fd00 100644 --- a/compiler/rustc_codegen_llvm/src/back/archive.rs +++ b/compiler/rustc_codegen_llvm/src/back/archive.rs @@ -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> { + 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> { + 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 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), }); diff --git a/src/test/run-make/macos-fat-archive/Makefile b/src/test/run-make/macos-fat-archive/Makefile new file mode 100644 index 0000000000000..cc99375db6902 --- /dev/null +++ b/src/test/run-make/macos-fat-archive/Makefile @@ -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 diff --git a/src/test/run-make/macos-fat-archive/lib.rs b/src/test/run-make/macos-fat-archive/lib.rs new file mode 100644 index 0000000000000..9943a266c3e4c --- /dev/null +++ b/src/test/run-make/macos-fat-archive/lib.rs @@ -0,0 +1,3 @@ +extern "C" { + pub fn native_func(); +} diff --git a/src/test/run-make/macos-fat-archive/native-library.c b/src/test/run-make/macos-fat-archive/native-library.c new file mode 100644 index 0000000000000..d300fdf1c1742 --- /dev/null +++ b/src/test/run-make/macos-fat-archive/native-library.c @@ -0,0 +1 @@ +void native_func() {}