Sync ar_archive_writer to LLVM 18.1.3

From LLVM 15.0.0-rc3. This adds support for COFF archives containing
Arm64EC object files and has various fixes for AIX big archive files.
This commit is contained in:
bjorn3 2024-04-16 18:31:43 +00:00
parent 959a2eb275
commit 58e551433d
8 changed files with 201 additions and 98 deletions

View File

@ -236,11 +236,11 @@ checksum = "d67af77d68a931ecd5cbd8a3b5987d63a1d1d1278f7f6a60ae33db485cdebb69"
[[package]] [[package]]
name = "ar_archive_writer" name = "ar_archive_writer"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c269894b6fe5e9d7ada0cf69b5bf847ff35bc25fc271f08e1d080fce80339a" checksum = "f8412a2d690663356cba5a2532f3ed55d1e8090743bc6695b88403b27df67733"
dependencies = [ dependencies = [
"object 0.32.2", "object 0.35.0",
] ]
[[package]] [[package]]
@ -2637,6 +2637,15 @@ dependencies = [
"ruzstd 0.6.0", "ruzstd 0.6.0",
] ]
[[package]]
name = "object"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "object" name = "object"
version = "0.36.0" version = "0.36.0"

View File

@ -1,7 +1,7 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use rustc_codegen_ssa::back::archive::{ use rustc_codegen_ssa::back::archive::{
get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER,
}; };
use rustc_session::Session; use rustc_session::Session;
@ -9,7 +9,7 @@ pub(crate) struct ArArchiveBuilderBuilder;
impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols)) Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER))
} }
fn create_dll_import_lib( fn create_dll_import_lib(

View File

@ -1,7 +1,7 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use rustc_codegen_ssa::back::archive::{ use rustc_codegen_ssa::back::archive::{
get_native_object_symbols, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, ArArchiveBuilder, ArchiveBuilder, ArchiveBuilderBuilder, DEFAULT_OBJECT_READER,
}; };
use rustc_session::Session; use rustc_session::Session;
@ -11,7 +11,7 @@ pub(crate) struct ArArchiveBuilderBuilder;
impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder { impl ArchiveBuilderBuilder for ArArchiveBuilderBuilder {
fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> { fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder + 'a> {
Box::new(ArArchiveBuilder::new(sess, get_native_object_symbols)) Box::new(ArArchiveBuilder::new(sess, &DEFAULT_OBJECT_READER))
} }
fn create_dll_import_lib( fn create_dll_import_lib(

View File

@ -15,8 +15,8 @@ use crate::errors::{
use crate::llvm::archive_ro::{ArchiveRO, Child}; use crate::llvm::archive_ro::{ArchiveRO, Child};
use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport}; use crate::llvm::{self, ArchiveKind, LLVMMachineType, LLVMRustCOFFShortExport};
use rustc_codegen_ssa::back::archive::{ use rustc_codegen_ssa::back::archive::{
get_native_object_symbols, try_extract_macho_fat_archive, ArArchiveBuilder, try_extract_macho_fat_archive, ArArchiveBuilder, ArchiveBuildFailure, ArchiveBuilder,
ArchiveBuildFailure, ArchiveBuilder, ArchiveBuilderBuilder, UnknownArchiveKind, ArchiveBuilderBuilder, ObjectReader, UnknownArchiveKind, DEFAULT_OBJECT_READER,
}; };
use tracing::trace; use tracing::trace;
@ -115,7 +115,7 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
if true { if true {
Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() }) Box::new(LlvmArchiveBuilder { sess, additions: Vec::new() })
} else { } else {
Box::new(ArArchiveBuilder::new(sess, get_llvm_object_symbols)) Box::new(ArArchiveBuilder::new(sess, &LLVM_OBJECT_READER))
} }
} }
@ -291,57 +291,82 @@ impl ArchiveBuilderBuilder for LlvmArchiveBuilderBuilder {
// The object crate doesn't know how to get symbols for LLVM bitcode and COFF bigobj files. // The object crate doesn't know how to get symbols for LLVM bitcode and COFF bigobj files.
// As such we need to use LLVM for them. // As such we need to use LLVM for them.
#[deny(unsafe_op_in_unsafe_fn)]
fn get_llvm_object_symbols( static LLVM_OBJECT_READER: ObjectReader = ObjectReader {
buf: &[u8], get_symbols: get_llvm_object_symbols,
f: &mut dyn FnMut(&[u8]) -> io::Result<()>, is_64_bit_object_file: llvm_is_64_bit_object_file,
) -> io::Result<bool> { is_ec_object_file: llvm_is_ec_object_file,
get_xcoff_member_alignment: DEFAULT_OBJECT_READER.get_xcoff_member_alignment,
};
fn should_use_llvm_reader(buf: &[u8]) -> bool {
let is_bitcode = unsafe { llvm::LLVMRustIsBitcode(buf.as_ptr(), buf.len()) }; let is_bitcode = unsafe { llvm::LLVMRustIsBitcode(buf.as_ptr(), buf.len()) };
// COFF bigobj file, msvc LTO file or import library. See // COFF bigobj file, msvc LTO file or import library. See
// https://github.com/llvm/llvm-project/blob/453f27bc9/llvm/lib/BinaryFormat/Magic.cpp#L38-L51 // https://github.com/llvm/llvm-project/blob/453f27bc9/llvm/lib/BinaryFormat/Magic.cpp#L38-L51
let is_unsupported_windows_obj_file = buf.get(0..4) == Some(b"\0\0\xFF\xFF"); let is_unsupported_windows_obj_file = buf.get(0..4) == Some(b"\0\0\xFF\xFF");
if is_bitcode || is_unsupported_windows_obj_file { is_bitcode || is_unsupported_windows_obj_file
let mut state = Box::new(f); }
let err = unsafe { #[deny(unsafe_op_in_unsafe_fn)]
llvm::LLVMRustGetSymbols( fn get_llvm_object_symbols(
buf.as_ptr(), buf: &[u8],
buf.len(), f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
std::ptr::addr_of_mut!(*state) as *mut c_void, ) -> io::Result<bool> {
callback, if !should_use_llvm_reader(buf) {
error_callback, return (DEFAULT_OBJECT_READER.get_symbols)(buf, f);
)
};
if err.is_null() {
return Ok(true);
} else {
return Err(unsafe { *Box::from_raw(err as *mut io::Error) });
}
unsafe extern "C" fn callback(
state: *mut c_void,
symbol_name: *const c_char,
) -> *mut c_void {
let f = unsafe { &mut *(state as *mut &mut dyn FnMut(&[u8]) -> io::Result<()>) };
match f(unsafe { CStr::from_ptr(symbol_name) }.to_bytes()) {
Ok(()) => std::ptr::null_mut(),
Err(err) => Box::into_raw(Box::new(err)) as *mut c_void,
}
}
unsafe extern "C" fn error_callback(error: *const c_char) -> *mut c_void {
let error = unsafe { CStr::from_ptr(error) };
Box::into_raw(Box::new(io::Error::new(
io::ErrorKind::Other,
format!("LLVM error: {}", error.to_string_lossy()),
))) as *mut c_void
}
} else {
get_native_object_symbols(buf, f)
} }
let mut state = Box::new(f);
let err = unsafe {
llvm::LLVMRustGetSymbols(
buf.as_ptr(),
buf.len(),
std::ptr::addr_of_mut!(*state) as *mut c_void,
callback,
error_callback,
)
};
if err.is_null() {
return Ok(true);
} else {
return Err(unsafe { *Box::from_raw(err as *mut io::Error) });
}
unsafe extern "C" fn callback(state: *mut c_void, symbol_name: *const c_char) -> *mut c_void {
let f = unsafe { &mut *(state as *mut &mut dyn FnMut(&[u8]) -> io::Result<()>) };
match f(unsafe { CStr::from_ptr(symbol_name) }.to_bytes()) {
Ok(()) => std::ptr::null_mut(),
Err(err) => Box::into_raw(Box::new(err)) as *mut c_void,
}
}
unsafe extern "C" fn error_callback(error: *const c_char) -> *mut c_void {
let error = unsafe { CStr::from_ptr(error) };
Box::into_raw(Box::new(io::Error::new(
io::ErrorKind::Other,
format!("LLVM error: {}", error.to_string_lossy()),
))) as *mut c_void
}
}
fn llvm_is_64_bit_object_file(buf: &[u8]) -> bool {
if !should_use_llvm_reader(buf) {
return (DEFAULT_OBJECT_READER.is_64_bit_object_file)(buf);
}
unsafe { llvm::LLVMRustIs64BitSymbolicFile(buf.as_ptr(), buf.len()) }
}
fn llvm_is_ec_object_file(buf: &[u8]) -> bool {
if !should_use_llvm_reader(buf) {
return (DEFAULT_OBJECT_READER.is_ec_object_file)(buf);
}
unsafe { llvm::LLVMRustIsECObject(buf.as_ptr(), buf.len()) }
} }
impl<'a> LlvmArchiveBuilder<'a> { impl<'a> LlvmArchiveBuilder<'a> {

View File

@ -2440,4 +2440,8 @@ extern "C" {
callback: GetSymbolsCallback, callback: GetSymbolsCallback,
error_callback: GetSymbolsErrorCallback, error_callback: GetSymbolsErrorCallback,
) -> *mut c_void; ) -> *mut c_void;
pub fn LLVMRustIs64BitSymbolicFile(buf_ptr: *const u8, buf_len: usize) -> bool;
pub fn LLVMRustIsECObject(buf_ptr: *const u8, buf_len: usize) -> bool;
} }

View File

@ -5,7 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
# tidy-alphabetical-start # tidy-alphabetical-start
ar_archive_writer = "0.2.0" ar_archive_writer = "0.3.0"
arrayvec = { version = "0.7", default-features = false } arrayvec = { version = "0.7", default-features = false }
bitflags = "2.4.1" bitflags = "2.4.1"
cc = "1.0.90" cc = "1.0.90"

View File

@ -6,8 +6,8 @@ use rustc_span::symbol::Symbol;
use super::metadata::search_for_section; use super::metadata::search_for_section;
pub use ar_archive_writer::get_native_object_symbols;
use ar_archive_writer::{write_archive_to_stream, ArchiveKind, NewArchiveMember}; use ar_archive_writer::{write_archive_to_stream, ArchiveKind, NewArchiveMember};
pub use ar_archive_writer::{ObjectReader, DEFAULT_OBJECT_READER};
use object::read::archive::ArchiveFile; use object::read::archive::ArchiveFile;
use object::read::macho::FatArch; use object::read::macho::FatArch;
use tempfile::Builder as TempFileBuilder; use tempfile::Builder as TempFileBuilder;
@ -89,8 +89,7 @@ pub trait ArchiveBuilder {
#[must_use = "must call build() to finish building the archive"] #[must_use = "must call build() to finish building the archive"]
pub struct ArArchiveBuilder<'a> { pub struct ArArchiveBuilder<'a> {
sess: &'a Session, sess: &'a Session,
get_object_symbols: object_reader: &'static ObjectReader,
fn(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> io::Result<()>) -> io::Result<bool>,
src_archives: Vec<(PathBuf, Mmap)>, src_archives: Vec<(PathBuf, Mmap)>,
// Don't use an `HashMap` here, as the order is important. `lib.rmeta` needs // Don't use an `HashMap` here, as the order is important. `lib.rmeta` needs
@ -105,14 +104,8 @@ enum ArchiveEntry {
} }
impl<'a> ArArchiveBuilder<'a> { impl<'a> ArArchiveBuilder<'a> {
pub fn new( pub fn new(sess: &'a Session, object_reader: &'static ObjectReader) -> ArArchiveBuilder<'a> {
sess: &'a Session, ArArchiveBuilder { sess, object_reader, src_archives: vec![], entries: vec![] }
get_object_symbols: fn(
buf: &[u8],
f: &mut dyn FnMut(&[u8]) -> io::Result<()>,
) -> io::Result<bool>,
) -> ArArchiveBuilder<'a> {
ArArchiveBuilder { sess, get_object_symbols, src_archives: vec![], entries: vec![] }
} }
} }
@ -267,7 +260,7 @@ impl<'a> ArArchiveBuilder<'a> {
entries.push(NewArchiveMember { entries.push(NewArchiveMember {
buf: data, buf: data,
get_symbols: self.get_object_symbols, object_reader: self.object_reader,
member_name: String::from_utf8(entry_name).unwrap(), member_name: String::from_utf8(entry_name).unwrap(),
mtime: 0, mtime: 0,
uid: 0, uid: 0,
@ -294,7 +287,13 @@ impl<'a> ArArchiveBuilder<'a> {
let mut archive_tmpfile = File::create_new(&archive_tmpfile_path) let mut archive_tmpfile = File::create_new(&archive_tmpfile_path)
.map_err(|err| io_error_context("couldn't create the temp file", err))?; .map_err(|err| io_error_context("couldn't create the temp file", err))?;
write_archive_to_stream(&mut archive_tmpfile, &entries, archive_kind, false)?; write_archive_to_stream(
&mut archive_tmpfile,
&entries,
archive_kind,
false,
self.sess.target.arch == "arm64ec",
)?;
let any_entries = !entries.is_empty(); let any_entries = !entries.is_empty();
drop(entries); drop(entries);

View File

@ -4,12 +4,15 @@
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
// Derived from: // Derived from:
// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/include/llvm/Object/ArchiveWriter.h // * https://github.com/llvm/llvm-project/blob/ef6d1ec07c693352c4a60dd58db08d2d8558f6ea/llvm/include/llvm/Object/ArchiveWriter.h
// * https://github.com/llvm/llvm-project/blob/8ef3e895ad8ab1724e2b87cabad1dacdc7a397a3/llvm/lib/Object/ArchiveWriter.cpp // * https://github.com/llvm/llvm-project/blob/ef6d1ec07c693352c4a60dd58db08d2d8558f6ea/llvm/lib/Object/ArchiveWriter.cpp
#include "SuppressLLVMWarnings.h" #include "SuppressLLVMWarnings.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/IRObjectFile.h"
#include "llvm/Object/ObjectFile.h" #include "llvm/Object/ObjectFile.h"
#include <llvm/Support/raw_ostream.h> #include <llvm/Support/raw_ostream.h>
@ -34,6 +37,26 @@ static bool isArchiveSymbol(const object::BasicSymbolRef &S) {
typedef void *(*LLVMRustGetSymbolsCallback)(void *, const char *); typedef void *(*LLVMRustGetSymbolsCallback)(void *, const char *);
typedef void *(*LLVMRustGetSymbolsErrorCallback)(const char *); typedef void *(*LLVMRustGetSymbolsErrorCallback)(const char *);
static Expected<std::unique_ptr<SymbolicFile>>
getSymbolicFile(MemoryBufferRef Buf, LLVMContext &Context) {
const file_magic Type = identify_magic(Buf.getBuffer());
// Don't attempt to read non-symbolic file types.
if (!object::SymbolicFile::isSymbolicFile(Type, &Context))
return nullptr;
if (Type == file_magic::bitcode) {
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
Buf, file_magic::bitcode, &Context);
if (!ObjOrErr)
return ObjOrErr.takeError();
return std::move(*ObjOrErr);
} else {
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(Buf);
if (!ObjOrErr)
return ObjOrErr.takeError();
return std::move(*ObjOrErr);
}
}
// Note: This is implemented in C++ instead of using the C api from Rust as // Note: This is implemented in C++ instead of using the C api from Rust as
// IRObjectFile doesn't implement getSymbolName, only printSymbolName, which is // IRObjectFile doesn't implement getSymbolName, only printSymbolName, which is
// inaccessible from the C api. // inaccessible from the C api.
@ -49,36 +72,16 @@ LLVMRustGetSymbols(char *BufPtr, size_t BufLen, void *State,
// In the scenario when LLVMContext is populated SymbolicFile will contain a // In the scenario when LLVMContext is populated SymbolicFile will contain a
// reference to it, thus SymbolicFile should be destroyed first. // reference to it, thus SymbolicFile should be destroyed first.
LLVMContext Context; LLVMContext Context;
std::unique_ptr<object::SymbolicFile> Obj; Expected<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
getSymbolicFile(Buf->getMemBufferRef(), Context);
const file_magic Type = identify_magic(Buf->getBuffer()); if (!ObjOrErr) {
if (!object::SymbolicFile::isSymbolicFile(Type, &Context)) { Error E = ObjOrErr.takeError();
return 0; SmallString<0> ErrorBuf;
} auto Error = raw_svector_ostream(ErrorBuf);
Error << E << '\0';
if (Type == file_magic::bitcode) { return ErrorCallback(Error.str().data());
auto ObjOrErr = object::SymbolicFile::createSymbolicFile(
Buf->getMemBufferRef(), file_magic::bitcode, &Context);
if (!ObjOrErr) {
Error E = ObjOrErr.takeError();
SmallString<0> ErrorBuf;
auto Error = raw_svector_ostream(ErrorBuf);
Error << E << '\0';
return ErrorCallback(Error.str().data());
}
Obj = std::move(*ObjOrErr);
} else {
auto ObjOrErr =
object::SymbolicFile::createSymbolicFile(Buf->getMemBufferRef());
if (!ObjOrErr) {
Error E = ObjOrErr.takeError();
SmallString<0> ErrorBuf;
auto Error = raw_svector_ostream(ErrorBuf);
Error << E << '\0';
return ErrorCallback(Error.str().data());
}
Obj = std::move(*ObjOrErr);
} }
std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
for (const object::BasicSymbolRef &S : Obj->symbols()) { for (const object::BasicSymbolRef &S : Obj->symbols()) {
if (!isArchiveSymbol(S)) if (!isArchiveSymbol(S))
@ -97,3 +100,66 @@ LLVMRustGetSymbols(char *BufPtr, size_t BufLen, void *State,
} }
return 0; return 0;
} }
// Encoding true and false as invalid pointer values
#define TRUE_PTR (void *)1
#define FALSE_PTR (void *)0
extern "C" bool LLVMRustIs64BitSymbolicFile(char *BufPtr, size_t BufLen) {
std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(
StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"), false);
SmallString<0> SymNameBuf;
auto SymName = raw_svector_ostream(SymNameBuf);
// In the scenario when LLVMContext is populated SymbolicFile will contain a
// reference to it, thus SymbolicFile should be destroyed first.
LLVMContext Context;
Expected<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
getSymbolicFile(Buf->getMemBufferRef(), Context);
if (!ObjOrErr) {
return false;
}
std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
return Obj != nullptr ? Obj->is64Bit() : false;
}
extern "C" bool LLVMRustIsECObject(char *BufPtr, size_t BufLen) {
std::unique_ptr<MemoryBuffer> Buf = MemoryBuffer::getMemBuffer(
StringRef(BufPtr, BufLen), StringRef("LLVMRustGetSymbolsObject"), false);
SmallString<0> SymNameBuf;
auto SymName = raw_svector_ostream(SymNameBuf);
// In the scenario when LLVMContext is populated SymbolicFile will contain a
// reference to it, thus SymbolicFile should be destroyed first.
LLVMContext Context;
Expected<std::unique_ptr<object::SymbolicFile>> ObjOrErr =
getSymbolicFile(Buf->getMemBufferRef(), Context);
if (!ObjOrErr) {
return false;
}
std::unique_ptr<object::SymbolicFile> Obj = std::move(*ObjOrErr);
if (Obj == nullptr) {
return false;
}
if (Obj->isCOFF())
return cast<llvm::object::COFFObjectFile>(&*Obj)->getMachine() !=
COFF::IMAGE_FILE_MACHINE_ARM64;
if (Obj->isCOFFImportFile())
return cast<llvm::object::COFFImportFile>(&*Obj)->getMachine() !=
COFF::IMAGE_FILE_MACHINE_ARM64;
if (Obj->isIR()) {
Expected<std::string> TripleStr =
getBitcodeTargetTriple(Obj->getMemoryBufferRef());
if (!TripleStr)
return false;
Triple T(*TripleStr);
return T.isWindowsArm64EC() || T.getArch() == Triple::x86_64;
}
return false;
}