Skip to content

Commit ac1da45

Browse files
authored
Merge pull request #17 from dpaoliello/parseimp
Add support for creating archives with members from an import library
2 parents b93c68b + 3f6ad99 commit ac1da45

File tree

6 files changed

+92
-22
lines changed

6 files changed

+92
-22
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ repository = "https://github.com/rust-lang/ar_archive_writer"
1111
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1212

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

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

2121
[lints.rust]

src/coff_import_file.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@ macro_rules! u32 {
4242
}
4343

4444
// Derived from COFFImportFile::printSymbolName and COFFImportFile::symbol_end.
45-
fn get_short_import_symbol(buf: &[u8], f: &mut dyn FnMut(&[u8]) -> Result<()>) -> Result<bool> {
45+
pub(crate) fn get_short_import_symbol(
46+
buf: &[u8],
47+
f: &mut dyn FnMut(&[u8]) -> Result<()>,
48+
) -> Result<bool> {
4649
let mut offset = 0;
4750
let header = ImportObjectHeader::parse(buf, &mut offset).map_err(Error::other)?;
4851
let data = header.parse_data(buf, &mut offset).map_err(Error::other)?;

src/object_reader.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
88
use std::{io, mem::offset_of};
99

10-
use object::{xcoff, Object, ObjectSymbol};
10+
use object::{pe::ImportObjectHeader, xcoff, Object, ObjectSymbol};
11+
12+
use crate::coff_import_file;
1113

1214
fn is_archive_symbol(sym: &object::read::Symbol<'_, '_>) -> bool {
1315
// FIXME Use a better equivalent of LLVM's SymbolRef::SF_FormatSpecific
14-
if sym.kind() == object::SymbolKind::Null
15-
|| sym.kind() == object::SymbolKind::File
16-
|| sym.kind() == object::SymbolKind::Section
17-
{
16+
if sym.kind() == object::SymbolKind::File || sym.kind() == object::SymbolKind::Section {
1817
return false;
1918
}
2019
if !sym.is_global() {
@@ -42,7 +41,15 @@ pub fn get_native_object_symbols(
4241
}
4342
Ok(true)
4443
}
45-
Err(_) => Ok(false),
44+
Err(_) => {
45+
let mut offset = 0;
46+
// Try to handle this as a COFF import library.
47+
if ImportObjectHeader::parse(buf, &mut offset).is_ok() {
48+
coff_import_file::get_short_import_symbol(buf, f).or(Ok(false))
49+
} else {
50+
Ok(false)
51+
}
52+
}
4653
}
4754
}
4855

tests/common.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ fn run_llvm_ar(
107107

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

141141
/// Creates an archive with the given objects using `ar_archive_writer`.
142142
/// The generated archive is written to disk as `output_ar_archive_writer.a`.
143-
pub fn create_archive_with_ar_archive_writer<'a>(
143+
pub fn create_archive_with_ar_archive_writer<'name, 'data>(
144144
tmpdir: &Path,
145145
archive_kind: ArchiveKind,
146-
input_objects: impl IntoIterator<Item = (&'static str, &'a [u8])>,
146+
input_objects: impl IntoIterator<Item = (&'name str, &'data [u8])>,
147147
thin: bool,
148148
is_ec: bool,
149149
) -> Vec<u8> {

tests/import_library.rs

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use std::fs;
22
use std::io::Cursor;
3-
use std::path::Path;
4-
use std::path::PathBuf;
3+
use std::path::{Path, PathBuf};
54
use std::process::Command;
65

7-
use ar_archive_writer::{COFFShortExport, MachineTypes};
6+
use ar_archive_writer::{ArchiveKind, COFFShortExport, MachineTypes};
7+
use common::{create_archive_with_ar_archive_writer, create_archive_with_llvm_ar};
8+
use object::read::archive::ArchiveFile;
9+
use object::{Architecture, SubArchitecture};
810
use pretty_assertions::assert_eq;
911

1012
mod common;
@@ -215,3 +217,67 @@ fn compare_to_dlltool() {
215217
);
216218
}
217219
}
220+
221+
/// Creates an import library and then wraps that in an archive.
222+
#[test]
223+
fn wrap_in_archive() {
224+
for (architecture, subarch, machine_type) in [
225+
(Architecture::I386, None, MachineTypes::I386),
226+
(Architecture::X86_64, None, MachineTypes::AMD64),
227+
(Architecture::Arm, None, MachineTypes::ARMNT),
228+
(Architecture::Aarch64, None, MachineTypes::ARM64),
229+
(
230+
Architecture::Aarch64,
231+
Some(SubArchitecture::Arm64EC),
232+
MachineTypes::ARM64EC,
233+
),
234+
] {
235+
let temp_dir = common::create_tmp_dir("import_library_wrap_in_archive");
236+
237+
let mut import_lib_bytes = Cursor::new(Vec::new());
238+
ar_archive_writer::write_import_library(
239+
&mut import_lib_bytes,
240+
&temp_dir.join("MyLibrary.dll").to_string_lossy().to_string(),
241+
&get_members(machine_type),
242+
machine_type,
243+
false,
244+
)
245+
.unwrap();
246+
let import_lib_bytes = import_lib_bytes.into_inner();
247+
248+
let is_ec = subarch == Some(SubArchitecture::Arm64EC);
249+
let llvm_ar_archive = create_archive_with_llvm_ar(
250+
&temp_dir,
251+
ArchiveKind::Coff,
252+
[("MyLibrary.dll.lib", import_lib_bytes.as_slice())],
253+
false,
254+
is_ec,
255+
);
256+
// When a archive is passed into lib.exe, it is opened and the individual members are included
257+
// in the new output library. Also, for whatever reason, the members are reversed.
258+
let archive = ArchiveFile::parse(import_lib_bytes.as_slice()).unwrap();
259+
let mut members = archive
260+
.members()
261+
.map(|m| {
262+
let member = m.unwrap();
263+
(
264+
String::from_utf8(member.name().to_vec()).unwrap(),
265+
member.data(import_lib_bytes.as_slice()).unwrap(),
266+
)
267+
})
268+
.collect::<Vec<_>>();
269+
members.reverse();
270+
let ar_archive_writer_archive = create_archive_with_ar_archive_writer(
271+
&temp_dir,
272+
ArchiveKind::Coff,
273+
members.iter().map(|(name, data)| (name.as_str(), *data)),
274+
false,
275+
is_ec,
276+
);
277+
278+
assert_eq!(
279+
llvm_ar_archive, ar_archive_writer_archive,
280+
"Archives differ for architecture: {architecture:?}, subarch: {subarch:?}, machine type: {machine_type:?}",
281+
);
282+
}
283+
}

tests/round_trip.rs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,6 @@ fn round_trip_and_diff(
4949
let output_archive =
5050
read::archive::ArchiveFile::parse(output_archive_bytes.as_slice()).unwrap();
5151
let mut members = output_archive.members();
52-
if is_ec && archive_kind == ArchiveKind::Coff {
53-
// FIXME: Skip the EC Symbol Table, since `object` doesn't correctly
54-
// handle it as a special member.
55-
// Fix in object: https://github.com/gimli-rs/object/pull/669
56-
members.next().unwrap().unwrap();
57-
}
5852
let output_member = members.next().unwrap().unwrap();
5953
if archive_kind != ArchiveKind::Coff {
6054
assert_eq!(output_member.name(), b"input.o");

0 commit comments

Comments
 (0)