Skip to content

Commit

Permalink
resolve error when attempting to link a universal library on macOS
Browse files Browse the repository at this point in the history
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
  • Loading branch information
alex committed Oct 4, 2022
1 parent 6b139c5 commit c65c362
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3297,6 +3297,7 @@ dependencies = [
"rustc_symbol_mangling",
"rustc_target",
"smallvec",
"tempfile",
"tracing",
]

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_codegen_llvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
67 changes: 64 additions & 3 deletions compiler/rustc_codegen_llvm/src/back/archive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -53,21 +57,78 @@ 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)),
};
if self.additions.iter().any(|ar| ar.path() == archive) {
return Ok(());
}
self.additions.push(Addition::Archive {
path: archive.to_path_buf(),
path: archive,
archive: archive_ro,
skip: Box::new(skip),
});
Expand Down
10 changes: 10 additions & 0 deletions src/test/run-make/macos-fat-archive/Makefile
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions src/test/run-make/macos-fat-archive/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
extern "C" {
pub fn native_func();
}
1 change: 1 addition & 0 deletions src/test/run-make/macos-fat-archive/native-library.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
void native_func() {}

0 comments on commit c65c362

Please sign in to comment.