Skip to content

Commit

Permalink
Auto merge of rust-lang#98736 - alex:lipo-magic, r=bjorn3
Browse files Browse the repository at this point in the history
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 rust-lang#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.
  • Loading branch information
bors committed Oct 5, 2022
2 parents dd8c3a8 + c65c362 commit 8c71b67
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 @@ -3319,6 +3319,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 8c71b67

Please sign in to comment.