Remove LlvmArchiveBuilder and supporting code/bindings

This commit is contained in:
Zalathar 2025-08-16 13:14:52 +10:00
parent 1ae7c49072
commit cf8ec6798f
6 changed files with 6 additions and 543 deletions

View file

@ -1,104 +1,21 @@
//! A helper class for dealing with static archives
use std::ffi::{CStr, CString, c_char, c_void};
use std::path::{Path, PathBuf};
use std::{io, mem, ptr, str};
use std::ffi::{CStr, c_char, c_void};
use std::io;
use rustc_codegen_ssa::back::archive::{
ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder,
DEFAULT_OBJECT_READER, ObjectReader, UnknownArchiveKind, try_extract_macho_fat_archive,
ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER, ObjectReader,
};
use rustc_session::Session;
use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, last_error};
/// Helper for adding many files to an archive.
#[must_use = "must call build() to finish building the archive"]
pub(crate) struct LlvmArchiveBuilder<'a> {
sess: &'a Session,
additions: Vec<Addition>,
}
enum Addition {
File { path: PathBuf, name_in_archive: String },
Archive { path: PathBuf, archive: ArchiveRO, skip: Box<dyn FnMut(&str) -> bool> },
}
impl Addition {
fn path(&self) -> &Path {
match self {
Addition::File { path, .. } | Addition::Archive { path, .. } => path,
}
}
}
fn is_relevant_child(c: &Child<'_>) -> bool {
match c.name() {
Some(name) => !name.contains("SYMDEF"),
None => false,
}
}
impl<'a> ArchiveBuilder for LlvmArchiveBuilder<'a> {
fn add_archive(
&mut self,
archive: &Path,
skip: Box<dyn FnMut(&str) -> bool + 'static>,
) -> io::Result<()> {
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,
archive: archive_ro,
skip: Box::new(skip),
});
Ok(())
}
/// Adds an arbitrary file to this archive
fn add_file(&mut self, file: &Path) {
let name = file.file_name().unwrap().to_str().unwrap();
self.additions
.push(Addition::File { path: file.to_path_buf(), name_in_archive: name.to_owned() });
}
/// Combine the provided files, rlibs, and native libraries into a single
/// `Archive`.
fn build(mut self: Box<Self>, output: &Path) -> bool {
match self.build_with_llvm(output) {
Ok(any_members) => any_members,
Err(error) => {
self.sess.dcx().emit_fatal(ArchiveBuildFailure { path: output.to_owned(), error })
}
}
}
}
use crate::llvm;
pub(crate) struct LlvmArchiveBuilderBuilder;
impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
// Keeping LlvmArchiveBuilder around in case of a regression caused by using
// ArArchiveBuilder.
// FIXME(#128955) remove a couple of months after #128936 gets merged in case
// no regression is found.
if false {
Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
} else {
Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
}
// Use the `object` crate to build archives, with a little bit of help from LLVM.
Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
}
}
@ -178,91 +95,3 @@ fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool {
fn llvm_is_ec_object_file(buf: &[u8]) -> bool {
unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) }
}
impl<'a> LlvmArchiveBuilder<'a> {
fn build_with_llvm(&mut self, output: &Path) -> io::Result<bool> {
let kind = &*self.sess.target.archive_format;
let kind = kind
.parse::<ArchiveKind>()
.map_err(|_| kind)
.unwrap_or_else(|kind| self.sess.dcx().emit_fatal(UnknownArchiveKind { kind }));
let mut additions = mem::take(&mut self.additions);
// Values in the `members` list below will contain pointers to the strings allocated here.
// So they need to get dropped after all elements of `members` get freed.
let mut strings = Vec::new();
let mut members = Vec::new();
let dst = CString::new(output.to_str().unwrap())?;
unsafe {
for addition in &mut additions {
match addition {
Addition::File { path, name_in_archive } => {
let path = CString::new(path.to_str().unwrap())?;
let name = CString::new(name_in_archive.as_bytes())?;
members.push(llvm::LLVMRustArchiveMemberNew(
path.as_ptr(),
name.as_ptr(),
None,
));
strings.push(path);
strings.push(name);
}
Addition::Archive { archive, skip, .. } => {
for child in archive.iter() {
let child = child.map_err(string_to_io_error)?;
if !is_relevant_child(&child) {
continue;
}
let child_name = child.name().unwrap();
if skip(child_name) {
continue;
}
// It appears that LLVM's archive writer is a little
// buggy if the name we pass down isn't just the
// filename component, so chop that off here and
// pass it in.
//
// See LLVM bug 25877 for more info.
let child_name =
Path::new(child_name).file_name().unwrap().to_str().unwrap();
let name = CString::new(child_name)?;
let m = llvm::LLVMRustArchiveMemberNew(
ptr::null(),
name.as_ptr(),
Some(child.raw),
);
members.push(m);
strings.push(name);
}
}
}
}
let r = llvm::LLVMRustWriteArchive(
dst.as_ptr(),
members.len() as libc::size_t,
members.as_ptr() as *const &_,
true,
kind,
self.sess.target.arch == "arm64ec",
);
let ret = if r.into_result().is_err() {
let msg = last_error().unwrap_or_else(|| "failed to write archive".into());
Err(io::Error::new(io::ErrorKind::Other, msg))
} else {
Ok(!members.is_empty())
};
for member in members {
llvm::LLVMRustArchiveMemberFree(member);
}
ret
}
}
}
fn string_to_io_error(s: String) -> io::Error {
io::Error::new(io::ErrorKind::Other, format!("bad archive: {s}"))
}

View file

@ -1,94 +0,0 @@
//! A wrapper around LLVM's archive (.a) code
use std::path::Path;
use std::{slice, str};
use rustc_fs_util::path_to_c_string;
pub(crate) struct ArchiveRO {
pub raw: &'static mut super::Archive,
}
unsafe impl Send for ArchiveRO {}
pub(crate) struct Iter<'a> {
raw: &'a mut super::ArchiveIterator<'a>,
}
pub(crate) struct Child<'a> {
pub raw: &'a mut super::ArchiveChild<'a>,
}
impl ArchiveRO {
/// Opens a static archive for read-only purposes. This is more optimized
/// than the `open` method because it uses LLVM's internal `Archive` class
/// rather than shelling out to `ar` for everything.
///
/// If this archive is used with a mutable method, then an error will be
/// raised.
pub(crate) fn open(dst: &Path) -> Result<ArchiveRO, String> {
unsafe {
let s = path_to_c_string(dst);
let ar = super::LLVMRustOpenArchive(s.as_ptr()).ok_or_else(|| {
super::last_error().unwrap_or_else(|| "failed to open archive".to_owned())
})?;
Ok(ArchiveRO { raw: ar })
}
}
pub(crate) fn iter(&self) -> Iter<'_> {
unsafe { Iter { raw: super::LLVMRustArchiveIteratorNew(self.raw) } }
}
}
impl Drop for ArchiveRO {
fn drop(&mut self) {
unsafe {
super::LLVMRustDestroyArchive(&mut *(self.raw as *mut _));
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = Result<Child<'a>, String>;
fn next(&mut self) -> Option<Result<Child<'a>, String>> {
unsafe {
match super::LLVMRustArchiveIteratorNext(self.raw) {
Some(raw) => Some(Ok(Child { raw })),
None => super::last_error().map(Err),
}
}
}
}
impl<'a> Drop for Iter<'a> {
fn drop(&mut self) {
unsafe {
super::LLVMRustArchiveIteratorFree(&mut *(self.raw as *mut _));
}
}
}
impl<'a> Child<'a> {
pub(crate) fn name(&self) -> Option<&'a str> {
unsafe {
let mut name_len = 0;
let name_ptr = super::LLVMRustArchiveChildName(self.raw, &mut name_len);
if name_ptr.is_null() {
None
} else {
let name = slice::from_raw_parts(name_ptr as *const u8, name_len as usize);
str::from_utf8(name).ok().map(|s| s.trim())
}
}
}
}
impl<'a> Drop for Child<'a> {
fn drop(&mut self) {
unsafe {
super::LLVMRustArchiveChildFree(&mut *(self.raw as *mut _));
}
}
}

View file

@ -610,17 +610,6 @@ pub(crate) enum DiagnosticLevel {
Remark,
}
/// LLVMRustArchiveKind
#[derive(Copy, Clone)]
#[repr(C)]
pub(crate) enum ArchiveKind {
K_GNU,
K_BSD,
K_DARWIN,
K_COFF,
K_AIXBIG,
}
unsafe extern "C" {
// LLVMRustThinLTOData
pub(crate) type ThinLTOData;
@ -769,19 +758,12 @@ pub(crate) struct Builder<'a>(InvariantOpaque<'a>);
pub(crate) struct PassManager<'a>(InvariantOpaque<'a>);
unsafe extern "C" {
pub type TargetMachine;
pub(crate) type Archive;
}
#[repr(C)]
pub(crate) struct ArchiveIterator<'a>(InvariantOpaque<'a>);
#[repr(C)]
pub(crate) struct ArchiveChild<'a>(InvariantOpaque<'a>);
unsafe extern "C" {
pub(crate) type Twine;
pub(crate) type DiagnosticInfo;
pub(crate) type SMDiagnostic;
}
#[repr(C)]
pub(crate) struct RustArchiveMember<'a>(InvariantOpaque<'a>);
/// Opaque pointee of `LLVMOperandBundleRef`.
#[repr(C)]
pub(crate) struct OperandBundle<'a>(InvariantOpaque<'a>);
@ -2505,19 +2487,6 @@ unsafe extern "C" {
pub(crate) fn LLVMRustSetNormalizedTarget(M: &Module, triple: *const c_char);
pub(crate) fn LLVMRustRunRestrictionPass(M: &Module, syms: *const *const c_char, len: size_t);
pub(crate) fn LLVMRustOpenArchive(path: *const c_char) -> Option<&'static mut Archive>;
pub(crate) fn LLVMRustArchiveIteratorNew(AR: &Archive) -> &mut ArchiveIterator<'_>;
pub(crate) fn LLVMRustArchiveIteratorNext<'a>(
AIR: &ArchiveIterator<'a>,
) -> Option<&'a mut ArchiveChild<'a>>;
pub(crate) fn LLVMRustArchiveChildName(
ACR: &ArchiveChild<'_>,
size: &mut size_t,
) -> *const c_char;
pub(crate) fn LLVMRustArchiveChildFree<'a>(ACR: &'a mut ArchiveChild<'a>);
pub(crate) fn LLVMRustArchiveIteratorFree<'a>(AIR: &'a mut ArchiveIterator<'a>);
pub(crate) fn LLVMRustDestroyArchive(AR: &'static mut Archive);
pub(crate) fn LLVMRustWriteTwineToString(T: &Twine, s: &RustString);
pub(crate) fn LLVMRustUnpackOptimizationDiagnostic<'a>(
@ -2555,21 +2524,6 @@ unsafe extern "C" {
num_ranges: &mut usize,
) -> bool;
pub(crate) fn LLVMRustWriteArchive(
Dst: *const c_char,
NumMembers: size_t,
Members: *const &RustArchiveMember<'_>,
WriteSymbtab: bool,
Kind: ArchiveKind,
isEC: bool,
) -> LLVMRustResult;
pub(crate) fn LLVMRustArchiveMemberNew<'a>(
Filename: *const c_char,
Name: *const c_char,
Child: Option<&ArchiveChild<'a>>,
) -> &'a mut RustArchiveMember<'a>;
pub(crate) fn LLVMRustArchiveMemberFree<'a>(Member: &'a mut RustArchiveMember<'a>);
pub(crate) fn LLVMRustSetDataLayoutFromTargetMachine<'a>(M: &'a Module, TM: &'a TargetMachine);
pub(crate) fn LLVMRustPositionBuilderPastAllocas<'a>(B: &Builder<'a>, Fn: &'a Value);

View file

@ -3,7 +3,6 @@
use std::ffi::{CStr, CString};
use std::num::NonZero;
use std::ptr;
use std::str::FromStr;
use std::string::FromUtf8Error;
use libc::c_uint;
@ -16,7 +15,6 @@ pub(crate) use self::MetadataType::*;
pub(crate) use self::ffi::*;
use crate::common::AsCCharPtr;
pub(crate) mod archive_ro;
pub(crate) mod diagnostic;
pub(crate) mod enzyme_ffi;
mod ffi;
@ -152,21 +150,6 @@ pub(crate) enum CodeGenOptSize {
CodeGenOptSizeAggressive = 2,
}
impl FromStr for ArchiveKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"gnu" => Ok(ArchiveKind::K_GNU),
"bsd" => Ok(ArchiveKind::K_BSD),
"darwin" => Ok(ArchiveKind::K_DARWIN),
"coff" => Ok(ArchiveKind::K_COFF),
"aix_big" => Ok(ArchiveKind::K_AIXBIG),
_ => Err(()),
}
}
}
pub(crate) fn SetInstructionCallConv(instr: &Value, cc: CallConv) {
unsafe {
LLVMSetInstructionCallConv(instr, cc as c_uint);

View file

@ -226,7 +226,6 @@ fn main() {
rerun_if_changed_anything_in_dir(Path::new("llvm-wrapper"));
cfg.file("llvm-wrapper/PassWrapper.cpp")
.file("llvm-wrapper/RustWrapper.cpp")
.file("llvm-wrapper/ArchiveWrapper.cpp")
.file("llvm-wrapper/CoverageMappingWrapper.cpp")
.file("llvm-wrapper/SymbolWrapper.cpp")
.file("llvm-wrapper/Linker.cpp")

View file

@ -1,208 +0,0 @@
#include "LLVMWrapper.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::object;
struct RustArchiveMember {
const char *Filename;
const char *Name;
Archive::Child Child;
RustArchiveMember()
: Filename(nullptr), Name(nullptr), Child(nullptr, nullptr, nullptr) {}
~RustArchiveMember() {}
};
struct RustArchiveIterator {
bool First;
Archive::child_iterator Cur;
Archive::child_iterator End;
std::unique_ptr<Error> Err;
RustArchiveIterator(Archive::child_iterator Cur, Archive::child_iterator End,
std::unique_ptr<Error> Err)
: First(true), Cur(Cur), End(End), Err(std::move(Err)) {}
};
enum class LLVMRustArchiveKind {
GNU,
BSD,
DARWIN,
COFF,
AIX_BIG,
};
static Archive::Kind fromRust(LLVMRustArchiveKind Kind) {
switch (Kind) {
case LLVMRustArchiveKind::GNU:
return Archive::K_GNU;
case LLVMRustArchiveKind::BSD:
return Archive::K_BSD;
case LLVMRustArchiveKind::DARWIN:
return Archive::K_DARWIN;
case LLVMRustArchiveKind::COFF:
return Archive::K_COFF;
case LLVMRustArchiveKind::AIX_BIG:
return Archive::K_AIXBIG;
default:
report_fatal_error("Bad ArchiveKind.");
}
}
typedef OwningBinary<Archive> *LLVMRustArchiveRef;
typedef RustArchiveMember *LLVMRustArchiveMemberRef;
typedef Archive::Child *LLVMRustArchiveChildRef;
typedef Archive::Child const *LLVMRustArchiveChildConstRef;
typedef RustArchiveIterator *LLVMRustArchiveIteratorRef;
extern "C" LLVMRustArchiveRef LLVMRustOpenArchive(char *Path) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOr = MemoryBuffer::getFile(
Path, /*IsText*/ false, /*RequiresNullTerminator=*/false);
if (!BufOr) {
LLVMRustSetLastError(BufOr.getError().message().c_str());
return nullptr;
}
Expected<std::unique_ptr<Archive>> ArchiveOr =
Archive::create(BufOr.get()->getMemBufferRef());
if (!ArchiveOr) {
LLVMRustSetLastError(toString(ArchiveOr.takeError()).c_str());
return nullptr;
}
OwningBinary<Archive> *Ret = new OwningBinary<Archive>(
std::move(ArchiveOr.get()), std::move(BufOr.get()));
return Ret;
}
extern "C" void LLVMRustDestroyArchive(LLVMRustArchiveRef RustArchive) {
delete RustArchive;
}
extern "C" LLVMRustArchiveIteratorRef
LLVMRustArchiveIteratorNew(LLVMRustArchiveRef RustArchive) {
Archive *Archive = RustArchive->getBinary();
std::unique_ptr<Error> Err = std::make_unique<Error>(Error::success());
auto Cur = Archive->child_begin(*Err);
if (*Err) {
LLVMRustSetLastError(toString(std::move(*Err)).c_str());
return nullptr;
}
auto End = Archive->child_end();
return new RustArchiveIterator(Cur, End, std::move(Err));
}
extern "C" LLVMRustArchiveChildConstRef
LLVMRustArchiveIteratorNext(LLVMRustArchiveIteratorRef RAI) {
if (RAI->Cur == RAI->End)
return nullptr;
// Advancing the iterator validates the next child, and this can
// uncover an error. LLVM requires that we check all Errors,
// so we only advance the iterator if we actually need to fetch
// the next child.
// This means we must not advance the iterator in the *first* call,
// but instead advance it *before* fetching the child in all later calls.
if (!RAI->First) {
++RAI->Cur;
if (*RAI->Err) {
LLVMRustSetLastError(toString(std::move(*RAI->Err)).c_str());
return nullptr;
}
} else {
RAI->First = false;
}
if (RAI->Cur == RAI->End)
return nullptr;
const Archive::Child &Child = *RAI->Cur.operator->();
Archive::Child *Ret = new Archive::Child(Child);
return Ret;
}
extern "C" void LLVMRustArchiveChildFree(LLVMRustArchiveChildRef Child) {
delete Child;
}
extern "C" void LLVMRustArchiveIteratorFree(LLVMRustArchiveIteratorRef RAI) {
delete RAI;
}
extern "C" const char *
LLVMRustArchiveChildName(LLVMRustArchiveChildConstRef Child, size_t *Size) {
Expected<StringRef> NameOrErr = Child->getName();
if (!NameOrErr) {
// rustc_codegen_llvm currently doesn't use this error string, but it might
// be useful in the future, and in the meantime this tells LLVM that the
// error was not ignored and that it shouldn't abort the process.
LLVMRustSetLastError(toString(NameOrErr.takeError()).c_str());
return nullptr;
}
StringRef Name = NameOrErr.get();
*Size = Name.size();
return Name.data();
}
extern "C" LLVMRustArchiveMemberRef
LLVMRustArchiveMemberNew(char *Filename, char *Name,
LLVMRustArchiveChildRef Child) {
RustArchiveMember *Member = new RustArchiveMember;
Member->Filename = Filename;
Member->Name = Name;
if (Child)
Member->Child = *Child;
return Member;
}
extern "C" void LLVMRustArchiveMemberFree(LLVMRustArchiveMemberRef Member) {
delete Member;
}
extern "C" LLVMRustResult LLVMRustWriteArchive(
char *Dst, size_t NumMembers, const LLVMRustArchiveMemberRef *NewMembers,
bool WriteSymbtab, LLVMRustArchiveKind RustKind, bool isEC) {
std::vector<NewArchiveMember> Members;
auto Kind = fromRust(RustKind);
for (size_t I = 0; I < NumMembers; I++) {
auto Member = NewMembers[I];
assert(Member->Name);
if (Member->Filename) {
Expected<NewArchiveMember> MOrErr =
NewArchiveMember::getFile(Member->Filename, true);
if (!MOrErr) {
LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
return LLVMRustResult::Failure;
}
MOrErr->MemberName = sys::path::filename(MOrErr->MemberName);
Members.push_back(std::move(*MOrErr));
} else {
Expected<NewArchiveMember> MOrErr =
NewArchiveMember::getOldMember(Member->Child, true);
if (!MOrErr) {
LLVMRustSetLastError(toString(MOrErr.takeError()).c_str());
return LLVMRustResult::Failure;
}
Members.push_back(std::move(*MOrErr));
}
}
auto SymtabMode = WriteSymbtab ? SymtabWritingMode::NormalSymtab
: SymtabWritingMode::NoSymtab;
auto Result =
writeArchive(Dst, Members, SymtabMode, Kind, true, false, nullptr, isEC);
if (!Result)
return LLVMRustResult::Success;
LLVMRustSetLastError(toString(std::move(Result)).c_str());
return LLVMRustResult::Failure;
}