Skip to content

Commit 002e259

Browse files
authored
Merge pull request #20 from ChrisDenton/comdat
Add an optional COMDAT record for the `.idata$3` section
2 parents 504d89a + 32d9046 commit 002e259

File tree

2 files changed

+119
-18
lines changed

2 files changed

+119
-18
lines changed

src/coff_import_file.rs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ use std::path::PathBuf;
1010
use std::str::from_utf8;
1111

1212
use object::pe::{
13-
ImageFileHeader, ImageImportDescriptor, ImageRelocation, ImageSectionHeader, ImageSymbol,
14-
ImportObjectHeader, IMAGE_FILE_32BIT_MACHINE, IMAGE_REL_AMD64_ADDR32NB,
15-
IMAGE_REL_ARM64_ADDR32NB, IMAGE_REL_ARM_ADDR32NB, IMAGE_REL_I386_DIR32NB,
16-
IMAGE_SCN_ALIGN_2BYTES, IMAGE_SCN_ALIGN_4BYTES, IMAGE_SCN_ALIGN_8BYTES,
17-
IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE, IMAGE_SCN_MEM_READ,
18-
IMAGE_SCN_MEM_WRITE, IMAGE_SYM_CLASS_EXTERNAL, IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_SECTION,
13+
ImageAuxSymbolSection, ImageFileHeader, ImageImportDescriptor, ImageRelocation,
14+
ImageSectionHeader, ImageSymbol, ImportObjectHeader, IMAGE_COMDAT_SELECT_ANY,
15+
IMAGE_FILE_32BIT_MACHINE, IMAGE_REL_AMD64_ADDR32NB, IMAGE_REL_ARM64_ADDR32NB,
16+
IMAGE_REL_ARM_ADDR32NB, IMAGE_REL_I386_DIR32NB, IMAGE_SCN_ALIGN_2BYTES, IMAGE_SCN_ALIGN_4BYTES,
17+
IMAGE_SCN_ALIGN_8BYTES, IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT,
18+
IMAGE_SCN_LNK_INFO, IMAGE_SCN_LNK_REMOVE, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE,
19+
IMAGE_SYM_CLASS_EXTERNAL, IMAGE_SYM_CLASS_NULL, IMAGE_SYM_CLASS_SECTION,
1920
IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_WEAK_EXTERNAL, IMAGE_WEAK_EXTERN_SEARCH_ALIAS,
2021
};
2122
use object::pod::bytes_of;
@@ -463,11 +464,14 @@ impl<'a> ObjectFactory<'a> {
463464
/// Creates a NULL import descriptor. This is a small object file whcih
464465
/// contains a NULL import descriptor. It is used to terminate the imports
465466
/// from a specific DLL.
466-
fn create_null_import_descriptor(&self) -> Result<NewArchiveMember<'_>> {
467+
///
468+
/// If `comdat` is set to true, the NULL import descriptor's section (`.idata$3`) will be
469+
/// turned into a COMDAT section.
470+
fn create_null_import_descriptor(&self, comdat: bool) -> Result<NewArchiveMember<'_>> {
467471
let mut buffer = Vec::new();
468472

469473
const NUMBER_OF_SECTIONS: usize = 1;
470-
const NUMBER_OF_SYMBOLS: usize = 1;
474+
let number_of_symbols = if comdat { 3 } else { 1 };
471475

472476
// COFF Header
473477
let header = ImageFileHeader {
@@ -477,7 +481,7 @@ impl<'a> ObjectFactory<'a> {
477481
pointer_to_symbol_table: u32!((size_of::<ImageFileHeader>() + (NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()) +
478482
// .idata$3
479483
size_of::<ImageImportDescriptor>()).try_into().unwrap()),
480-
number_of_symbols: u32!(NUMBER_OF_SYMBOLS.try_into().unwrap()),
484+
number_of_symbols: u32!(number_of_symbols.try_into().unwrap()),
481485
size_of_optional_header: u16!(0),
482486
characteristics: u16!(if self.is_64_bit() {
483487
0
@@ -506,6 +510,7 @@ impl<'a> ObjectFactory<'a> {
506510
| IMAGE_SCN_CNT_INITIALIZED_DATA
507511
| IMAGE_SCN_MEM_READ
508512
| IMAGE_SCN_MEM_WRITE
513+
| if comdat { IMAGE_SCN_LNK_COMDAT } else { 0 }
509514
),
510515
}];
511516
buffer.write_all(bytes_of(&section_table))?;
@@ -521,16 +526,38 @@ impl<'a> ObjectFactory<'a> {
521526
buffer.write_all(bytes_of(&import_descriptor))?;
522527

523528
// Symbol Table
524-
let mut symbol_table: [_; NUMBER_OF_SYMBOLS] = [ImageSymbol {
529+
if comdat {
530+
let symbol = ImageSymbol {
531+
name: *b".idata$3",
532+
value: u32!(0),
533+
section_number: u16!(1),
534+
typ: u16!(0),
535+
storage_class: IMAGE_SYM_CLASS_STATIC,
536+
number_of_aux_symbols: 1,
537+
};
538+
let aux = ImageAuxSymbolSection {
539+
length: u32!(size_of::<ImageImportDescriptor>().try_into().unwrap()),
540+
number_of_relocations: u16!(0),
541+
number_of_linenumbers: u16!(0),
542+
check_sum: u32!(0),
543+
selection: IMAGE_COMDAT_SELECT_ANY,
544+
number: u16!(0),
545+
reserved: 0,
546+
high_number: u16!(0),
547+
};
548+
buffer.write_all(bytes_of(&symbol))?;
549+
buffer.write_all(bytes_of(&aux))?;
550+
}
551+
let mut null_descriptor_symbol = ImageSymbol {
525552
name: [0; 8],
526553
value: u32!(0),
527554
section_number: u16!(1),
528555
typ: u16!(0),
529556
storage_class: IMAGE_SYM_CLASS_EXTERNAL,
530557
number_of_aux_symbols: 0,
531-
}];
532-
set_name_to_string_table_entry(&mut symbol_table[0], size_of::<u32>());
533-
buffer.write_all(bytes_of(&symbol_table))?;
558+
};
559+
set_name_to_string_table_entry(&mut null_descriptor_symbol, size_of::<u32>());
560+
buffer.write_all(bytes_of(&null_descriptor_symbol))?;
534561

535562
// String Table
536563
write_string_table(&mut buffer, &[NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME])?;
@@ -826,6 +853,7 @@ pub fn write_import_library<W: Write + Seek>(
826853
exports: &[COFFShortExport],
827854
machine: MachineTypes,
828855
mingw: bool,
856+
comdat: bool,
829857
) -> Result<()> {
830858
let native_machine = if machine == MachineTypes::ARM64EC {
831859
MachineTypes::ARM64
@@ -838,7 +866,7 @@ pub fn write_import_library<W: Write + Seek>(
838866

839867
members.push(of.create_import_descriptor()?);
840868

841-
members.push(of.create_null_import_descriptor()?);
869+
members.push(of.create_null_import_descriptor(comdat)?);
842870

843871
members.push(of.create_null_thunk()?);
844872

tests/import_library.rs

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ use std::process::Command;
55

66
use ar_archive_writer::{ArchiveKind, COFFShortExport, MachineTypes};
77
use common::{create_archive_with_ar_archive_writer, create_archive_with_llvm_ar};
8+
use object::coff::CoffHeader;
9+
use object::pe::ImageFileHeader;
810
use object::read::archive::ArchiveFile;
9-
use object::{Architecture, SubArchitecture};
11+
use object::{bytes_of, Architecture, Object, SubArchitecture, U32Bytes};
1012
use pretty_assertions::assert_eq;
1113

1214
mod common;
@@ -99,6 +101,7 @@ fn create_import_library_with_ar_archive_writer(
99101
temp_dir: &Path,
100102
machine_type: MachineTypes,
101103
mingw: bool,
104+
comdat: bool,
102105
) -> Vec<u8> {
103106
let mut output_bytes = Cursor::new(Vec::new());
104107
ar_archive_writer::write_import_library(
@@ -107,6 +110,7 @@ fn create_import_library_with_ar_archive_writer(
107110
&get_members(machine_type),
108111
machine_type,
109112
mingw,
113+
comdat,
110114
)
111115
.unwrap();
112116

@@ -129,7 +133,7 @@ fn compare_to_lib() {
129133
let temp_dir = common::create_tmp_dir("import_library_compare_to_lib");
130134

131135
let archive_writer_bytes =
132-
create_import_library_with_ar_archive_writer(&temp_dir, machine_type, false);
136+
create_import_library_with_ar_archive_writer(&temp_dir, machine_type, false, false);
133137

134138
let llvm_lib_bytes = {
135139
let machine_arg = match machine_type {
@@ -164,6 +168,11 @@ fn compare_to_lib() {
164168
llvm_lib_bytes, archive_writer_bytes,
165169
"Import library differs. Machine type: {machine_type:?}",
166170
);
171+
172+
compare_comdat(
173+
&create_import_library_with_ar_archive_writer(&temp_dir, machine_type, false, true),
174+
&llvm_lib_bytes,
175+
);
167176
}
168177
}
169178

@@ -178,7 +187,7 @@ fn compare_to_dlltool() {
178187
let temp_dir = common::create_tmp_dir("import_library_compare_to_dlltool");
179188

180189
let archive_writer_bytes =
181-
create_import_library_with_ar_archive_writer(&temp_dir, machine_type, true);
190+
create_import_library_with_ar_archive_writer(&temp_dir, machine_type, true, false);
182191

183192
let llvm_lib_bytes = {
184193
let machine_arg = match machine_type {
@@ -215,6 +224,11 @@ fn compare_to_dlltool() {
215224
llvm_lib_bytes, archive_writer_bytes,
216225
"Import library differs. Machine type: {machine_type:?}",
217226
);
227+
228+
compare_comdat(
229+
&create_import_library_with_ar_archive_writer(&temp_dir, machine_type, true, true),
230+
&llvm_lib_bytes,
231+
);
218232
}
219233
}
220234

@@ -237,10 +251,11 @@ fn wrap_in_archive() {
237251
let mut import_lib_bytes = Cursor::new(Vec::new());
238252
ar_archive_writer::write_import_library(
239253
&mut import_lib_bytes,
240-
&temp_dir.join("MyLibrary.dll").to_string_lossy().to_string(),
254+
&temp_dir.join("MyLibrary.dll").to_string_lossy(),
241255
&get_members(machine_type),
242256
machine_type,
243257
false,
258+
false,
244259
)
245260
.unwrap();
246261
let import_lib_bytes = import_lib_bytes.into_inner();
@@ -281,3 +296,61 @@ fn wrap_in_archive() {
281296
);
282297
}
283298
}
299+
300+
fn compare_comdat(archive_writer_bytes: &[u8], llvm_bytes: &[u8]) {
301+
let archive_writer = ArchiveFile::parse(archive_writer_bytes).unwrap();
302+
let llvm = ArchiveFile::parse(llvm_bytes).unwrap();
303+
304+
for (archive_member, llvm_member) in archive_writer.members().zip(llvm.members()) {
305+
let archive_member = archive_member.unwrap();
306+
let llvm_member = llvm_member.unwrap();
307+
308+
if archive_member.size() != llvm_member.size() {
309+
// Ensure that the member header is the same except for the file size.
310+
let mut llvm_file_header = *llvm_member.header().unwrap();
311+
llvm_file_header.size = *b"163 ";
312+
assert_eq!(
313+
bytes_of(archive_member.header().unwrap()),
314+
bytes_of(&llvm_file_header)
315+
);
316+
317+
// Ensure that the LLVM generated object contains .idata$3
318+
object::File::parse(llvm_member.data(llvm_bytes).unwrap())
319+
.unwrap()
320+
.section_by_name_bytes(b".idata$3")
321+
.unwrap();
322+
323+
// Ensure the COFF file headers are the same except for the symbol count.
324+
let llvm_data = llvm_member.data(llvm_bytes).unwrap();
325+
let archive_data = archive_member.data(archive_writer_bytes).unwrap();
326+
let mut offset = 0;
327+
let mut header = *ImageFileHeader::parse(llvm_data, &mut offset).unwrap();
328+
header.number_of_symbols = U32Bytes::from_bytes(3_u32.to_le_bytes());
329+
assert_eq!(
330+
&archive_data[..size_of::<ImageFileHeader>()],
331+
bytes_of(&header)
332+
);
333+
334+
// The rest of the object file will always be the same as `expected`
335+
// for all import files no matter the platform.
336+
let expected = [
337+
46, 105, 100, 97, 116, 97, 36, 51, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 60, 0, 0,
338+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 16, 48, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0,
339+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 105, 100, 97, 116, 97, 36, 51, 0, 0, 0, 0, 1,
340+
0, 0, 0, 3, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0,
341+
4, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 29, 0, 0, 0, 95, 95, 78, 85, 76, 76, 95,
342+
73, 77, 80, 79, 82, 84, 95, 68, 69, 83, 67, 82, 73, 80, 84, 79, 82, 0,
343+
];
344+
assert_eq!(&archive_data[size_of::<ImageFileHeader>()..], &expected);
345+
} else {
346+
assert_eq!(
347+
bytes_of(archive_member.header().unwrap()),
348+
bytes_of(llvm_member.header().unwrap())
349+
);
350+
assert_eq!(
351+
archive_member.data(archive_writer_bytes).unwrap(),
352+
llvm_member.data(llvm_bytes).unwrap()
353+
);
354+
}
355+
}
356+
}

0 commit comments

Comments
 (0)