Skip to content

Commit c632d0c

Browse files
authored
Merge pull request #13 from dpaoliello/thin
Add tests for thin archives, fix bugs found in testing
2 parents 381b2aa + 88b5dac commit c632d0c

File tree

3 files changed

+82
-22
lines changed

3 files changed

+82
-22
lines changed

src/archive_writer.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,8 @@ fn compute_member_data<'a, S: Write + Seek>(
490490
for m in new_members {
491491
let mut header = Vec::new();
492492

493-
let data: &[u8] = if thin { &[][..] } else { (*m.buf).as_ref() };
493+
let buf = m.buf.as_ref().as_ref();
494+
let data = if thin { &[][..] } else { buf };
494495

495496
// ld64 expects the members to be 8-byte aligned for 64-bit content and at
496497
// least 4-byte aligned for 32-bit content. Opt for the larger encoding
@@ -513,7 +514,7 @@ fn compute_member_data<'a, S: Write + Seek>(
513514
m.mtime
514515
};
515516

516-
let size = u64::try_from(data.len()).unwrap() + member_padding;
517+
let size = u64::try_from(buf.len()).unwrap() + member_padding;
517518
if size > MAX_MEMBER_SIZE {
518519
return Err(io::Error::new(
519520
io::ErrorKind::Other,
@@ -552,7 +553,7 @@ fn compute_member_data<'a, S: Write + Seek>(
552553
)?;
553554
}
554555

555-
let symbols = write_symbols(data, m.get_symbols, sym_names, &mut has_object)?;
556+
let symbols = write_symbols(buf, m.get_symbols, sym_names, &mut has_object)?;
556557

557558
pos += u64::try_from(header.len() + data.len() + padding.len()).unwrap();
558559
ret.push(MemberData {

tests/common.rs

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,29 @@ pub fn create_tmp_dir(test_name: &str) -> PathBuf {
2525
tmpdir
2626
}
2727

28-
fn run_llvm_ar(object_paths: &[PathBuf], archive_path: &Path, archive_kind: ArchiveKind) {
28+
fn run_llvm_ar(
29+
object_paths: &[PathBuf],
30+
archive_path: &Path,
31+
archive_kind: ArchiveKind,
32+
thin: bool,
33+
) {
2934
let ar_path = cargo_binutils::Tool::Ar.path().unwrap();
3035

36+
let mut command = Command::new(ar_path);
37+
3138
let format_arg = match archive_kind {
3239
ArchiveKind::Gnu => "gnu",
3340
ArchiveKind::Darwin => "darwin",
3441
ArchiveKind::AixBig => "bigarchive",
3542
_ => panic!("unsupported archive kind: {:?}", archive_kind),
3643
};
44+
command.arg(format!("--format={}", format_arg));
3745

38-
let output = Command::new(ar_path)
39-
.arg(format!("--format={}", format_arg))
46+
if thin {
47+
command.arg("--thin");
48+
}
49+
50+
let output = command
4051
.arg("rcs")
4152
.arg(archive_path)
4253
.args(object_paths)
@@ -55,6 +66,7 @@ pub fn create_archive_with_llvm_ar<'a>(
5566
tmpdir: &Path,
5667
archive_kind: ArchiveKind,
5768
input_objects: impl IntoIterator<Item = (&'static str, &'a [u8])>,
69+
thin: bool,
5870
) -> Vec<u8> {
5971
let archive_file_path = tmpdir.join("output_llvm_ar.a");
6072

@@ -70,7 +82,7 @@ pub fn create_archive_with_llvm_ar<'a>(
7082
})
7183
.collect::<Vec<_>>();
7284

73-
run_llvm_ar(&input_file_paths, &archive_file_path, archive_kind);
85+
run_llvm_ar(&input_file_paths, &archive_file_path, archive_kind, thin);
7486
fs::read(archive_file_path).unwrap()
7587
}
7688

@@ -80,24 +92,37 @@ pub fn create_archive_with_ar_archive_writer<'a>(
8092
tmpdir: &Path,
8193
archive_kind: ArchiveKind,
8294
input_objects: impl IntoIterator<Item = (&'static str, &'a [u8])>,
95+
thin: bool,
8396
) -> Vec<u8> {
8497
let members = input_objects
8598
.into_iter()
86-
.map(|(name, bytes)| NewArchiveMember {
87-
buf: Box::new(bytes) as Box<dyn AsRef<[u8]>>,
88-
get_symbols: ar_archive_writer::get_native_object_symbols,
89-
member_name: name
90-
.rsplit_once('/')
91-
.map_or(name, |(_, filename)| filename)
92-
.to_string(),
93-
mtime: 0,
94-
uid: 0,
95-
gid: 0,
96-
perms: 0o644,
99+
.map(|(name, bytes)| {
100+
let member_name = if thin {
101+
// Thin archives use the full path to the object file.
102+
tmpdir
103+
.join(name)
104+
.to_string_lossy()
105+
.replace(std::path::MAIN_SEPARATOR, "/")
106+
} else {
107+
// Non-thin archives use the file name only.
108+
name.rsplit_once('/')
109+
.map_or(name, |(_, filename)| filename)
110+
.to_string()
111+
};
112+
113+
NewArchiveMember {
114+
buf: Box::new(bytes) as Box<dyn AsRef<[u8]>>,
115+
get_symbols: ar_archive_writer::get_native_object_symbols,
116+
member_name,
117+
mtime: 0,
118+
uid: 0,
119+
gid: 0,
120+
perms: 0o644,
121+
}
97122
})
98123
.collect::<Vec<_>>();
99124
let mut output_bytes = Cursor::new(Vec::new());
100-
ar_archive_writer::write_archive_to_stream(&mut output_bytes, &members, archive_kind, false)
125+
ar_archive_writer::write_archive_to_stream(&mut output_bytes, &members, archive_kind, thin)
101126
.unwrap();
102127

103128
let output_archive_bytes = output_bytes.into_inner();
@@ -112,58 +137,88 @@ pub fn generate_archive_and_compare<F>(test_name: &str, generate_objects: F)
112137
where
113138
F: Fn(Architecture, Endianness, BinaryFormat) -> Vec<(&'static str, Vec<u8>)>,
114139
{
115-
for (architecture, endianness, binary_format, archive_kind) in [
116-
// Elf + GNU
140+
for (architecture, endianness, binary_format, archive_kind, thin) in [
141+
// Elf + GNU + non-thin
142+
(
143+
Architecture::X86_64,
144+
Endianness::Little,
145+
BinaryFormat::Elf,
146+
ArchiveKind::Gnu,
147+
false,
148+
),
149+
(
150+
Architecture::I386,
151+
Endianness::Little,
152+
BinaryFormat::Elf,
153+
ArchiveKind::Gnu,
154+
false,
155+
),
156+
(
157+
Architecture::Aarch64,
158+
Endianness::Little,
159+
BinaryFormat::Elf,
160+
ArchiveKind::Gnu,
161+
false,
162+
),
163+
// Elf + GNU + thin
117164
(
118165
Architecture::X86_64,
119166
Endianness::Little,
120167
BinaryFormat::Elf,
121168
ArchiveKind::Gnu,
169+
true,
122170
),
123171
(
124172
Architecture::I386,
125173
Endianness::Little,
126174
BinaryFormat::Elf,
127175
ArchiveKind::Gnu,
176+
true,
128177
),
129178
(
130179
Architecture::Aarch64,
131180
Endianness::Little,
132181
BinaryFormat::Elf,
133182
ArchiveKind::Gnu,
183+
true,
134184
),
135185
// AIX Big
136186
(
137187
Architecture::PowerPc64,
138188
Endianness::Big,
139189
BinaryFormat::Elf,
140190
ArchiveKind::AixBig,
191+
false,
141192
),
142193
// PE + GNU
143194
(
144195
Architecture::X86_64,
145196
Endianness::Little,
146197
BinaryFormat::Coff,
147198
ArchiveKind::Gnu,
199+
false,
148200
),
149201
(
150202
Architecture::I386,
151203
Endianness::Little,
152204
BinaryFormat::Coff,
153205
ArchiveKind::Gnu,
206+
false,
154207
),
155208
// MachO
156209
(
157210
Architecture::X86_64,
158211
Endianness::Little,
159212
BinaryFormat::MachO,
160213
ArchiveKind::Darwin,
214+
false,
161215
),
162216
(
163217
Architecture::Aarch64,
164218
Endianness::Little,
165219
BinaryFormat::MachO,
166220
ArchiveKind::Darwin,
221+
false,
167222
),
168223
] {
169224
let tmpdir = create_tmp_dir(test_name);
@@ -174,18 +229,20 @@ where
174229
input_objects
175230
.iter()
176231
.map(|(name, bytes)| (*name, bytes.as_slice())),
232+
thin,
177233
);
178234
let ar_archive_writer_archive = create_archive_with_ar_archive_writer(
179235
&tmpdir,
180236
archive_kind,
181237
input_objects
182238
.iter()
183239
.map(|(name, bytes)| (*name, bytes.as_slice())),
240+
thin,
184241
);
185242

186243
assert_eq!(
187244
llvm_ar_archive, ar_archive_writer_archive,
188-
"Archives differ for architecture: {architecture:?}, binary_format: {binary_format:?}, archive_kind: {archive_kind:?}",
245+
"Archives differ for architecture: {architecture:?}, binary_format: {binary_format:?}, archive_kind: {archive_kind:?}, thin: {thin}",
189246
);
190247
}
191248
}

tests/round_trip.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ fn round_trip_and_diff(
2525
&tmpdir,
2626
archive_kind,
2727
[("input.o", input_bytes.as_slice())],
28+
false,
2829
);
2930

3031
// Read the archive and member using object and diff with original data.
@@ -50,6 +51,7 @@ fn round_trip_and_diff(
5051
&tmpdir,
5152
archive_kind,
5253
[("input.o", input_bytes.as_slice())],
54+
false,
5355
);
5456
assert_eq!(
5557
output_archive_bytes, output_llvm_ar_bytes,

0 commit comments

Comments
 (0)