Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for creating archives with members from an import library #17

Merged
merged 1 commit into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ repository = "https://github.com/rust-lang/ar_archive_writer"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
object = { version = "0.35.0", default-features = false, features = ["std", "read"] }
object = { version = "0.36.2", default-features = false, features = ["std", "read"] }

[dev-dependencies]
cargo-binutils = "0.3.6"
object = { version = "0.35.0", default-features = false, features = ["write", "xcoff"] }
object = { version = "0.36.2", default-features = false, features = ["write", "xcoff"] }
pretty_assertions = "1.4.0"

[lints.rust]
Expand Down
5 changes: 4 additions & 1 deletion src/coff_import_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ macro_rules! u32 {
}

// Derived from COFFImportFile::printSymbolName and COFFImportFile::symbol_end.
fn get_short_import_symbol(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> Result<()>) -> Result<bool> {
pub(crate) fn get_short_import_symbol(
buf: &[u8],
f: &mut dyn FnMut(&[u8]) -> Result<()>,
) -> Result<bool> {
let mut offset = 0;
let header = ImportObjectHeader::parse(buf, &mut offset).map_err(Error::other)?;
let data = header.parse_data(buf, &mut offset).map_err(Error::other)?;
Expand Down
19 changes: 13 additions & 6 deletions src/object_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@

use std::{io, mem::offset_of};

use object::{xcoff, Object, ObjectSymbol};
use object::{pe::ImportObjectHeader, xcoff, Object, ObjectSymbol};

use crate::coff_import_file;

fn is_archive_symbol(sym: &object::read::Symbol<'_, '_>) -> bool {
// FIXME Use a better equivalent of LLVM's SymbolRef::SF_FormatSpecific
if sym.kind() == object::SymbolKind::Null
|| sym.kind() == object::SymbolKind::File
|| sym.kind() == object::SymbolKind::Section
{
if sym.kind() == object::SymbolKind::File || sym.kind() == object::SymbolKind::Section {
return false;
}
if !sym.is_global() {
Expand Down Expand Up @@ -42,7 +41,15 @@ pub fn get_native_object_symbols(
}
Ok(true)
}
Err(_) => Ok(false),
Err(_) => {
let mut offset = 0;
// Try to handle this as a COFF import library.
if ImportObjectHeader::parse(buf, &mut offset).is_ok() {
coff_import_file::get_short_import_symbol(buf, f).or(Ok(false))
} else {
Ok(false)
}
}
}
}

Expand Down
8 changes: 4 additions & 4 deletions tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ fn run_llvm_ar(

/// Creates an archive with the given objects using `llvm-ar`.
/// The generated archive is written to disk as `output_llvm_ar.a`.
pub fn create_archive_with_llvm_ar<'a>(
pub fn create_archive_with_llvm_ar<'name, 'data>(
tmpdir: &Path,
archive_kind: ArchiveKind,
input_objects: impl IntoIterator<Item = (&'static str, &'a [u8])>,
input_objects: impl IntoIterator<Item = (&'name str, &'data [u8])>,
thin: bool,
is_ec: bool,
) -> Vec<u8> {
Expand Down Expand Up @@ -140,10 +140,10 @@ pub fn create_archive_with_llvm_ar<'a>(

/// Creates an archive with the given objects using `ar_archive_writer`.
/// The generated archive is written to disk as `output_ar_archive_writer.a`.
pub fn create_archive_with_ar_archive_writer<'a>(
pub fn create_archive_with_ar_archive_writer<'name, 'data>(
tmpdir: &Path,
archive_kind: ArchiveKind,
input_objects: impl IntoIterator<Item = (&'static str, &'a [u8])>,
input_objects: impl IntoIterator<Item = (&'name str, &'data [u8])>,
thin: bool,
is_ec: bool,
) -> Vec<u8> {
Expand Down
72 changes: 69 additions & 3 deletions tests/import_library.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::fs;
use std::io::Cursor;
use std::path::Path;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::process::Command;

use ar_archive_writer::{COFFShortExport, MachineTypes};
use ar_archive_writer::{ArchiveKind, COFFShortExport, MachineTypes};
use common::{create_archive_with_ar_archive_writer, create_archive_with_llvm_ar};
use object::read::archive::ArchiveFile;
use object::{Architecture, SubArchitecture};
use pretty_assertions::assert_eq;

mod common;
Expand Down Expand Up @@ -215,3 +217,67 @@ fn compare_to_dlltool() {
);
}
}

/// Creates an import library and then wraps that in an archive.
#[test]
fn wrap_in_archive() {
for (architecture, subarch, machine_type) in [
(Architecture::I386, None, MachineTypes::I386),
(Architecture::X86_64, None, MachineTypes::AMD64),
(Architecture::Arm, None, MachineTypes::ARMNT),
(Architecture::Aarch64, None, MachineTypes::ARM64),
(
Architecture::Aarch64,
Some(SubArchitecture::Arm64EC),
MachineTypes::ARM64EC,
),
] {
let temp_dir = common::create_tmp_dir("import_library_wrap_in_archive");

let mut import_lib_bytes = Cursor::new(Vec::new());
ar_archive_writer::write_import_library(
&mut import_lib_bytes,
&temp_dir.join("MyLibrary.dll").to_string_lossy().to_string(),
&get_members(machine_type),
machine_type,
false,
)
.unwrap();
let import_lib_bytes = import_lib_bytes.into_inner();

let is_ec = subarch == Some(SubArchitecture::Arm64EC);
let llvm_ar_archive = create_archive_with_llvm_ar(
&temp_dir,
ArchiveKind::Coff,
[("MyLibrary.dll.lib", import_lib_bytes.as_slice())],
false,
is_ec,
);
// When a archive is passed into lib.exe, it is opened and the individual members are included
// in the new output library. Also, for whatever reason, the members are reversed.
let archive = ArchiveFile::parse(import_lib_bytes.as_slice()).unwrap();
let mut members = archive
.members()
.map(|m| {
let member = m.unwrap();
(
String::from_utf8(member.name().to_vec()).unwrap(),
member.data(import_lib_bytes.as_slice()).unwrap(),
)
})
.collect::<Vec<_>>();
members.reverse();
let ar_archive_writer_archive = create_archive_with_ar_archive_writer(
&temp_dir,
ArchiveKind::Coff,
members.iter().map(|(name, data)| (name.as_str(), *data)),
false,
is_ec,
);

assert_eq!(
llvm_ar_archive, ar_archive_writer_archive,
"Archives differ for architecture: {architecture:?}, subarch: {subarch:?}, machine type: {machine_type:?}",
);
}
}
6 changes: 0 additions & 6 deletions tests/round_trip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,6 @@ fn round_trip_and_diff(
let output_archive =
read::archive::ArchiveFile::parse(output_archive_bytes.as_slice()).unwrap();
let mut members = output_archive.members();
if is_ec && archive_kind == ArchiveKind::Coff {
// FIXME: Skip the EC Symbol Table, since `object` doesn't correctly
// handle it as a special member.
// Fix in object: https://github.com/gimli-rs/object/pull/669
members.next().unwrap().unwrap();
}
let output_member = members.next().unwrap().unwrap();
if archive_kind != ArchiveKind::Coff {
assert_eq!(output_member.name(), b"input.o");
Expand Down
Loading