implib/
msvc.rs

1use std::io::{Error, Seek, Write};
2use std::mem::size_of;
3
4use memoffset::offset_of;
5use object::endian::{LittleEndian as LE, U16Bytes, U32Bytes, U16, U32};
6use object::pe::*;
7use object::pod::bytes_of;
8
9use crate::def::{ModuleDef, ShortExport};
10use crate::{ar, ArchiveMember, MachineType};
11
12const NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME: &str = "__NULL_IMPORT_DESCRIPTOR";
13
14#[derive(Debug, Clone, Copy)]
15#[repr(u16)]
16enum ImportType {
17    /// Code, function
18    Code,
19    /// Data
20    Data,
21    /// Constant
22    Const,
23}
24
25impl ShortExport {
26    fn import_type(&self) -> ImportType {
27        if self.data {
28            ImportType::Data
29        } else if self.constant {
30            ImportType::Const
31        } else {
32            ImportType::Code
33        }
34    }
35}
36
37#[derive(Debug, Clone, Copy)]
38#[repr(u16)]
39enum ImportNameType {
40    /// Import is by ordinal. This indicates that the value in the Ordinal/Hint
41    /// field of the import header is the import's ordinal. If this constant is
42    /// not specified, then the Ordinal/Hint field should always be interpreted
43    /// as the import's hint.
44    Ordinal = IMPORT_OBJECT_ORDINAL,
45    /// The import name is identical to the public symbol name
46    Name = IMPORT_OBJECT_NAME,
47    /// The import name is the public symbol name, but skipping the leading ?,
48    /// @, or optionally _.
49    NameNoPrefix = IMPORT_OBJECT_NAME_NO_PREFIX,
50    /// The import name is the public symbol name, but skipping the leading ?,
51    /// @, or optionally _, and truncating at the first @.
52    NameUndecorate = IMPORT_OBJECT_NAME_UNDECORATE,
53}
54
55impl MachineType {
56    fn is_32bit(&self) -> bool {
57        matches!(self, Self::ARMNT | Self::I386)
58    }
59}
60
61/// MSVC flavored Windows import library generator
62#[derive(Debug, Clone)]
63pub struct MsvcImportLibrary {
64    def: ModuleDef,
65    machine: MachineType,
66}
67
68impl MsvcImportLibrary {
69    /// Create new import library generator from `ModuleDef`
70    pub fn new(mut def: ModuleDef, machine: MachineType) -> Self {
71        // If ext_name is set (if the "ext_name = name" syntax was used), overwrite
72        // name with ext_name and clear ext_name. When only creating an import
73        // library and not linking, the internal name is irrelevant.
74        for export in &mut def.exports {
75            if let Some(ext_name) = export.ext_name.take() {
76                export.name = ext_name;
77            }
78        }
79        // Skipped i386 handling
80        // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L197-L212
81        MsvcImportLibrary { def, machine }
82    }
83
84    fn get_name_type(&self, sym: &str, ext_name: &str) -> ImportNameType {
85        // Skipped mingw64 handling
86        // See https://github.com/llvm/llvm-project/blob/09c2b7c35af8c4bad39f03e9f60df8bd07323028/llvm/lib/Object/COFFImportFile.cpp#L96-L105
87        if ext_name.starts_with('_') && ext_name.contains('@') {
88            ImportNameType::Name
89        } else if sym != ext_name {
90            ImportNameType::NameUndecorate
91        } else if self.machine == MachineType::I386 && sym.starts_with('_') {
92            ImportNameType::NameNoPrefix
93        } else {
94            ImportNameType::Name
95        }
96    }
97
98    /// Write out the import library
99    pub fn write_to<W: Write + Seek>(&self, writer: &mut W) -> Result<(), Error> {
100        let mut members = Vec::new();
101        let factory = ObjectFactory::new(&self.def.import_name, self.machine);
102
103        let import_descriptor = factory.create_import_descriptor();
104        members.push(import_descriptor.create_archive_entry());
105
106        let null_import_descriptor = factory.create_null_import_descriptor();
107        members.push(null_import_descriptor.create_archive_entry());
108
109        let null_thunk = factory.create_null_thunk();
110        members.push(null_thunk.create_archive_entry());
111
112        for export in &self.def.exports {
113            if export.private {
114                continue;
115            }
116            let sym = if export.symbol_name.is_empty() {
117                &export.name
118            } else {
119                &export.symbol_name
120            };
121            let name_type = if export.no_name {
122                ImportNameType::Ordinal
123            } else {
124                self.get_name_type(sym, &export.name)
125            };
126            let name = if let Some(ext_name) = &export.ext_name {
127                replace(sym, &export.name, ext_name)?
128            } else {
129                sym.to_string()
130            };
131
132            if !export.alias_target.is_empty() && name != export.alias_target {
133                let weak_non_imp = factory.create_weak_external(&export.alias_target, &name, false);
134                members.push(weak_non_imp.create_archive_entry());
135
136                let weak_imp = factory.create_weak_external(&export.alias_target, &name, true);
137                members.push(weak_imp.create_archive_entry());
138            }
139            let short_import =
140                factory.create_short_import(&name, export.ordinal, export.import_type(), name_type);
141            members.push(short_import.create_archive_entry());
142        }
143
144        let identifiers = members
145            .iter()
146            .map(|(header, _)| header.identifier().to_vec())
147            .collect();
148        let symbol_table: Vec<Vec<Vec<u8>>> = members
149            .iter()
150            .map(|(_, member)| {
151                member
152                    .symbols
153                    .iter()
154                    .map(|s| s.to_string().into_bytes())
155                    .collect::<Vec<Vec<u8>>>()
156            })
157            .collect();
158        let mut archive =
159            ar::GnuBuilder::new_with_symbol_table(writer, true, identifiers, symbol_table)?;
160        for (header, member) in members {
161            archive.append(&header, &member.data[..])?;
162        }
163        Ok(())
164    }
165}
166
167fn replace(sym: &str, from: &str, to: &str) -> Result<String, Error> {
168    use std::io::ErrorKind;
169
170    match sym.find(from) {
171        Some(pos) => return Ok(format!("{}{}{}", &sym[..pos], to, &sym[pos + from.len()..])),
172        None => {
173            if from.starts_with('_') && to.starts_with('_') {
174                if let Some(pos) = sym.find(&from[1..]) {
175                    return Ok(format!(
176                        "{}{}{}",
177                        &sym[..pos],
178                        &to[1..],
179                        &sym[pos + from.len() - 1..]
180                    ));
181                }
182            }
183        }
184    }
185    Err(Error::new(
186        ErrorKind::InvalidInput,
187        format!("{}: replacing '{}' with '{}' failed", sym, from, to),
188    ))
189}
190
191/// Constructs various small object files necessary to support linking
192/// symbols imported from a DLL.  The contents are pretty strictly defined and
193/// nearly entirely static.  The details of the structures files are defined in
194/// WINNT.h and the PE/COFF specification.
195#[derive(Debug)]
196struct ObjectFactory<'a> {
197    machine: MachineType,
198    import_name: &'a str,
199    import_descriptor_symbol_name: String,
200    null_thunk_symbol_name: String,
201}
202
203impl<'a> ObjectFactory<'a> {
204    fn new(import_name: &'a str, machine: MachineType) -> Self {
205        let library = if import_name.ends_with(".dll") || import_name.ends_with(".exe") {
206            &import_name[..import_name.len() - 4]
207        } else {
208            import_name
209        };
210        Self {
211            machine,
212            import_name,
213            import_descriptor_symbol_name: format!("__IMPORT_DESCRIPTOR_{}", library),
214            null_thunk_symbol_name: format!("\x7f{}_NULL_THUNK_DATA", library),
215        }
216    }
217
218    fn write_string_table(buffer: &mut Vec<u8>, strings: &[&str]) {
219        // The COFF string table consists of a 4-byte value which is the size of the
220        // table, including the length field itself.  This value is followed by the
221        // string content itself, which is an array of null-terminated C-style
222        // strings.  The termination is important as they are referenced to by offset
223        // by the symbol entity in the file format.
224        let offset = buffer.len();
225
226        // Skip over the length field, we will fill it in later as we will have
227        // computed the length while emitting the string content itself.
228        buffer.extend_from_slice(&[0, 0, 0, 0]);
229
230        for s in strings {
231            buffer.extend(s.as_bytes());
232            buffer.push(b'\0');
233        }
234
235        // Backfill the length of the table now that it has been computed.
236        let size = (buffer.len() - offset) as u32;
237        buffer[offset..offset + 4].copy_from_slice(&size.to_le_bytes());
238    }
239
240    /// Creates an Import Descriptor.  This is a small object file which contains a
241    /// reference to the terminators and contains the library name (entry) for the
242    /// import name table.  It will force the linker to construct the necessary
243    /// structure to import symbols from the DLL.
244    fn create_import_descriptor(&self) -> ArchiveMember {
245        const NUM_SECTIONS: usize = 2;
246        const NUM_SYMBOLS: usize = 7;
247        const NUM_RELOCATIONS: usize = 3;
248
249        let mut buffer = Vec::new();
250
251        let pointer_to_symbol_table = size_of::<ImageFileHeader>()
252            + NUM_SECTIONS * size_of::<ImageSectionHeader>()
253            // .idata$2
254            + size_of::<ImageImportDescriptor>() + NUM_RELOCATIONS * size_of::<ImageRelocation>()
255            // .idata$4
256            + self.import_name.len() + 1;
257        let characteristics = if self.machine.is_32bit() {
258            IMAGE_FILE_32BIT_MACHINE
259        } else {
260            0
261        };
262        let header = ImageFileHeader {
263            machine: U16::new(LE, self.machine as u16),
264            number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
265            time_date_stamp: U32::new(LE, 0),
266            pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
267            number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
268            size_of_optional_header: U16::new(LE, 0),
269            characteristics: U16::new(LE, characteristics),
270        };
271        buffer.extend_from_slice(bytes_of(&header));
272
273        // Section Header Table
274        let section_header_table = [
275            ImageSectionHeader {
276                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'2'],
277                virtual_size: U32::new(LE, 0),
278                virtual_address: U32::new(LE, 0),
279                size_of_raw_data: U32::new(LE, size_of::<ImageImportDescriptor>() as _),
280                pointer_to_raw_data: U32::new(
281                    LE,
282                    (size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>())
283                        as _,
284                ),
285                pointer_to_relocations: U32::new(
286                    LE,
287                    (size_of::<ImageFileHeader>()
288                        + NUM_SECTIONS * size_of::<ImageSectionHeader>()
289                        + size_of::<ImageImportDescriptor>()) as _,
290                ),
291                pointer_to_linenumbers: U32::new(LE, 0),
292                number_of_relocations: U16::new(LE, NUM_RELOCATIONS as _),
293                number_of_linenumbers: U16::new(LE, 0),
294                characteristics: U32::new(
295                    LE,
296                    IMAGE_SCN_ALIGN_4BYTES
297                        | IMAGE_SCN_CNT_INITIALIZED_DATA
298                        | IMAGE_SCN_MEM_READ
299                        | IMAGE_SCN_MEM_WRITE,
300                ),
301            },
302            ImageSectionHeader {
303                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'6'],
304                virtual_size: U32::new(LE, 0),
305                virtual_address: U32::new(LE, 0),
306                size_of_raw_data: U32::new(LE, (self.import_name.len() + 1) as _),
307                pointer_to_raw_data: U32::new(
308                    LE,
309                    (size_of::<ImageFileHeader>()
310                        + NUM_SECTIONS * size_of::<ImageSectionHeader>()
311                        + size_of::<ImageImportDescriptor>()
312                        + NUM_RELOCATIONS * size_of::<ImageRelocation>()) as _,
313                ),
314                pointer_to_relocations: U32::new(LE, 0),
315                pointer_to_linenumbers: U32::new(LE, 0),
316                number_of_relocations: U16::new(LE, 0),
317                number_of_linenumbers: U16::new(LE, 0),
318                characteristics: U32::new(
319                    LE,
320                    IMAGE_SCN_ALIGN_2BYTES
321                        | IMAGE_SCN_CNT_INITIALIZED_DATA
322                        | IMAGE_SCN_MEM_READ
323                        | IMAGE_SCN_MEM_WRITE,
324                ),
325            },
326        ];
327        for section in section_header_table {
328            buffer.extend_from_slice(bytes_of(&section));
329        }
330
331        // .idata$2
332        let import_descriptor = ImageImportDescriptor {
333            original_first_thunk: U32Bytes::new(LE, 0),
334            time_date_stamp: U32Bytes::new(LE, 0),
335            forwarder_chain: U32Bytes::new(LE, 0),
336            name: U32Bytes::new(LE, 0),
337            first_thunk: U32Bytes::new(LE, 0),
338        };
339        buffer.extend_from_slice(bytes_of(&import_descriptor));
340
341        let relocation_table = [
342            ImageRelocation {
343                virtual_address: U32Bytes::new(LE, offset_of!(ImageImportDescriptor, name) as _),
344                symbol_table_index: U32Bytes::new(LE, 2),
345                typ: U16Bytes::new(LE, self.machine.img_rel_relocation()),
346            },
347            ImageRelocation {
348                virtual_address: U32Bytes::new(
349                    LE,
350                    offset_of!(ImageImportDescriptor, original_first_thunk) as _,
351                ),
352                symbol_table_index: U32Bytes::new(LE, 3),
353                typ: U16Bytes::new(LE, self.machine.img_rel_relocation()),
354            },
355            ImageRelocation {
356                virtual_address: U32Bytes::new(
357                    LE,
358                    offset_of!(ImageImportDescriptor, first_thunk) as _,
359                ),
360                symbol_table_index: U32Bytes::new(LE, 4),
361                typ: U16Bytes::new(LE, self.machine.img_rel_relocation()),
362            },
363        ];
364        for relocation in &relocation_table {
365            buffer.extend_from_slice(bytes_of(relocation));
366        }
367
368        // .idata$6
369        buffer.extend_from_slice(self.import_name.as_bytes());
370        buffer.push(b'\0');
371
372        // Symbol Table
373        let sym5_offset =
374            (size_of::<u32>() + self.import_descriptor_symbol_name.len() + 1).to_le_bytes();
375        let sym6_offset = (size_of::<u32>()
376            + self.import_descriptor_symbol_name.len()
377            + 1
378            + NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME.len()
379            + 1)
380        .to_le_bytes();
381        let symbol_table = [
382            ImageSymbol {
383                name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
384                value: U32Bytes::new(LE, 0),
385                section_number: U16Bytes::new(LE, 1),
386                typ: U16Bytes::new(LE, 0),
387                storage_class: IMAGE_SYM_CLASS_EXTERNAL,
388                number_of_aux_symbols: 0,
389            },
390            ImageSymbol {
391                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'2'],
392                value: U32Bytes::new(LE, 0),
393                section_number: U16Bytes::new(LE, 1),
394                typ: U16Bytes::new(LE, 0),
395                storage_class: IMAGE_SYM_CLASS_SECTION,
396                number_of_aux_symbols: 0,
397            },
398            ImageSymbol {
399                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'6'],
400                value: U32Bytes::new(LE, 0),
401                section_number: U16Bytes::new(LE, 2),
402                typ: U16Bytes::new(LE, 0),
403                storage_class: IMAGE_SYM_CLASS_STATIC,
404                number_of_aux_symbols: 0,
405            },
406            ImageSymbol {
407                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'4'],
408                value: U32Bytes::new(LE, 0),
409                section_number: U16Bytes::new(LE, 0),
410                typ: U16Bytes::new(LE, 0),
411                storage_class: IMAGE_SYM_CLASS_SECTION,
412                number_of_aux_symbols: 0,
413            },
414            ImageSymbol {
415                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'5'],
416                value: U32Bytes::new(LE, 0),
417                section_number: U16Bytes::new(LE, 0),
418                typ: U16Bytes::new(LE, 0),
419                storage_class: IMAGE_SYM_CLASS_SECTION,
420                number_of_aux_symbols: 0,
421            },
422            ImageSymbol {
423                name: [
424                    0,
425                    0,
426                    0,
427                    0,
428                    sym5_offset[0],
429                    sym5_offset[1],
430                    sym5_offset[2],
431                    sym5_offset[3],
432                ],
433                value: U32Bytes::new(LE, 0),
434                section_number: U16Bytes::new(LE, 0),
435                typ: U16Bytes::new(LE, 0),
436                storage_class: IMAGE_SYM_CLASS_EXTERNAL,
437                number_of_aux_symbols: 0,
438            },
439            ImageSymbol {
440                name: [
441                    0,
442                    0,
443                    0,
444                    0,
445                    sym6_offset[0],
446                    sym6_offset[1],
447                    sym6_offset[2],
448                    sym6_offset[3],
449                ],
450                value: U32Bytes::new(LE, 0),
451                section_number: U16Bytes::new(LE, 0),
452                typ: U16Bytes::new(LE, 0),
453                storage_class: IMAGE_SYM_CLASS_EXTERNAL,
454                number_of_aux_symbols: 0,
455            },
456        ];
457        for table in &symbol_table {
458            buffer.extend_from_slice(bytes_of(table));
459        }
460
461        Self::write_string_table(
462            &mut buffer,
463            &[
464                &self.import_descriptor_symbol_name,
465                NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME,
466                &self.null_thunk_symbol_name,
467            ],
468        );
469        ArchiveMember {
470            name: self.import_name.to_string(),
471            data: buffer,
472            symbols: vec![self.import_descriptor_symbol_name.to_string()],
473        }
474    }
475
476    /// Creates a NULL import descriptor.  This is a small object file whcih
477    /// contains a NULL import descriptor.  It is used to terminate the imports
478    /// from a specific DLL.
479    fn create_null_import_descriptor(&self) -> ArchiveMember {
480        const NUM_SECTIONS: usize = 1;
481        const NUM_SYMBOLS: usize = 1;
482
483        let mut buffer = Vec::new();
484
485        let pointer_to_symbol_table = size_of::<ImageFileHeader>()
486            + NUM_SECTIONS * size_of::<ImageSectionHeader>()
487            // .idata$3
488            + size_of::<ImageImportDescriptor>();
489        let characteristics = if self.machine.is_32bit() {
490            IMAGE_FILE_32BIT_MACHINE
491        } else {
492            0
493        };
494        let header = ImageFileHeader {
495            machine: U16::new(LE, self.machine as u16),
496            number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
497            time_date_stamp: U32::new(LE, 0),
498            pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
499            number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
500            size_of_optional_header: U16::new(LE, 0),
501            characteristics: U16::new(LE, characteristics),
502        };
503        buffer.extend_from_slice(bytes_of(&header));
504
505        // Section Header Table
506        let section_header_table = ImageSectionHeader {
507            name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'3'],
508            virtual_size: U32::new(LE, 0),
509            virtual_address: U32::new(LE, 0),
510            size_of_raw_data: U32::new(LE, size_of::<ImageImportDescriptor>() as _),
511            pointer_to_raw_data: U32::new(
512                LE,
513                (size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>())
514                    as _,
515            ),
516            pointer_to_relocations: U32::new(LE, 0),
517            pointer_to_linenumbers: U32::new(LE, 0),
518            number_of_relocations: U16::new(LE, 0),
519            number_of_linenumbers: U16::new(LE, 0),
520            characteristics: U32::new(
521                LE,
522                IMAGE_SCN_ALIGN_4BYTES
523                    | IMAGE_SCN_CNT_INITIALIZED_DATA
524                    | IMAGE_SCN_MEM_READ
525                    | IMAGE_SCN_MEM_WRITE,
526            ),
527        };
528        buffer.extend_from_slice(bytes_of(&section_header_table));
529
530        // .idata$3
531        let import_descriptor = ImageImportDescriptor {
532            original_first_thunk: U32Bytes::new(LE, 0),
533            time_date_stamp: U32Bytes::new(LE, 0),
534            forwarder_chain: U32Bytes::new(LE, 0),
535            name: U32Bytes::new(LE, 0),
536            first_thunk: U32Bytes::new(LE, 0),
537        };
538        buffer.extend_from_slice(bytes_of(&import_descriptor));
539
540        // Symbol Table
541        let symbol_table = ImageSymbol {
542            name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
543            value: U32Bytes::new(LE, 0),
544            section_number: U16Bytes::new(LE, 1),
545            typ: U16Bytes::new(LE, 0),
546            storage_class: IMAGE_SYM_CLASS_EXTERNAL,
547            number_of_aux_symbols: 0,
548        };
549        buffer.extend_from_slice(bytes_of(&symbol_table));
550
551        Self::write_string_table(&mut buffer, &[NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME]);
552        ArchiveMember {
553            name: self.import_name.to_string(),
554            data: buffer,
555            symbols: vec![NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME.to_string()],
556        }
557    }
558
559    /// Create a NULL Thunk Entry.  This is a small object file which contains a
560    /// NULL Import Address Table entry and a NULL Import Lookup Table Entry.  It
561    /// is used to terminate the IAT and ILT.
562    fn create_null_thunk(&self) -> ArchiveMember {
563        const NUM_SECTIONS: usize = 2;
564        const NUM_SYMBOLS: usize = 1;
565
566        let mut buffer = Vec::new();
567
568        let va_size = if self.machine.is_32bit() { 4 } else { 8 };
569        let pointer_to_symbol_table = size_of::<ImageFileHeader>()
570            + NUM_SECTIONS * size_of::<ImageSectionHeader>()
571            // .idata$5
572            + va_size
573            // .idata$4
574            + va_size;
575        let characteristics = if self.machine.is_32bit() {
576            IMAGE_FILE_32BIT_MACHINE
577        } else {
578            0
579        };
580        let header = ImageFileHeader {
581            machine: U16::new(LE, self.machine as u16),
582            number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
583            time_date_stamp: U32::new(LE, 0),
584            pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
585            number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
586            size_of_optional_header: U16::new(LE, 0),
587            characteristics: U16::new(LE, characteristics),
588        };
589        buffer.extend_from_slice(bytes_of(&header));
590
591        // Section Header Table
592        let align = if self.machine.is_32bit() {
593            IMAGE_SCN_ALIGN_4BYTES
594        } else {
595            IMAGE_SCN_ALIGN_8BYTES
596        };
597        let section_header_table = [
598            ImageSectionHeader {
599                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'5'],
600                virtual_size: U32::new(LE, 0),
601                virtual_address: U32::new(LE, 0),
602                size_of_raw_data: U32::new(LE, va_size as _),
603                pointer_to_raw_data: U32::new(
604                    LE,
605                    (size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>())
606                        as _,
607                ),
608                pointer_to_relocations: U32::new(LE, 0),
609                pointer_to_linenumbers: U32::new(LE, 0),
610                number_of_relocations: U16::new(LE, 0),
611                number_of_linenumbers: U16::new(LE, 0),
612                characteristics: U32::new(
613                    LE,
614                    align
615                        | IMAGE_SCN_CNT_INITIALIZED_DATA
616                        | IMAGE_SCN_MEM_READ
617                        | IMAGE_SCN_MEM_WRITE,
618                ),
619            },
620            ImageSectionHeader {
621                name: [b'.', b'i', b'd', b'a', b't', b'a', b'$', b'4'],
622                virtual_size: U32::new(LE, 0),
623                virtual_address: U32::new(LE, 0),
624                size_of_raw_data: U32::new(LE, va_size as _),
625                pointer_to_raw_data: U32::new(
626                    LE,
627                    (size_of::<ImageFileHeader>()
628                        + NUM_SECTIONS * size_of::<ImageSectionHeader>()
629                        + va_size) as _,
630                ),
631                pointer_to_relocations: U32::new(LE, 0),
632                pointer_to_linenumbers: U32::new(LE, 0),
633                number_of_relocations: U16::new(LE, 0),
634                number_of_linenumbers: U16::new(LE, 0),
635                characteristics: U32::new(
636                    LE,
637                    align
638                        | IMAGE_SCN_CNT_INITIALIZED_DATA
639                        | IMAGE_SCN_MEM_READ
640                        | IMAGE_SCN_MEM_WRITE,
641                ),
642            },
643        ];
644        for section in section_header_table {
645            buffer.extend_from_slice(bytes_of(&section));
646        }
647
648        // .idata$5, ILT
649        buffer.extend(0u32.to_le_bytes());
650        if !self.machine.is_32bit() {
651            buffer.extend(0u32.to_le_bytes());
652        }
653
654        // .idata$4, IAT
655        buffer.extend(0u32.to_le_bytes());
656        if !self.machine.is_32bit() {
657            buffer.extend(0u32.to_le_bytes());
658        }
659
660        // Symbol Table
661        let symbol_table = ImageSymbol {
662            name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
663            value: U32Bytes::new(LE, 0),
664            section_number: U16Bytes::new(LE, 1),
665            typ: U16Bytes::new(LE, 0),
666            storage_class: IMAGE_SYM_CLASS_EXTERNAL,
667            number_of_aux_symbols: 0,
668        };
669        buffer.extend_from_slice(bytes_of(&symbol_table));
670
671        Self::write_string_table(&mut buffer, &[&self.null_thunk_symbol_name]);
672        ArchiveMember {
673            name: self.import_name.to_string(),
674            data: buffer,
675            symbols: vec![self.null_thunk_symbol_name.to_string()],
676        }
677    }
678
679    /// Create a short import file which is described in PE/COFF spec 7. Import
680    /// Library Format.
681    fn create_short_import(
682        &self,
683        sym: &str,
684        ordinal: u16,
685        import_type: ImportType,
686        name_type: ImportNameType,
687    ) -> ArchiveMember {
688        // +2 for NULs
689        let import_name_size = self.import_name.len() + sym.len() + 2;
690        let size = size_of::<ImportObjectHeader>() + import_name_size;
691        let mut buffer = Vec::with_capacity(size);
692
693        // Write short import header
694        let import_header = ImportObjectHeader {
695            sig1: U16::new(LE, 0),
696            sig2: U16::new(LE, 0xFFFF),
697            version: U16::new(LE, 0),
698            machine: U16::new(LE, self.machine as _),
699            time_date_stamp: U32::new(LE, 0),
700            size_of_data: U32::new(LE, import_name_size as _),
701            ordinal_or_hint: if ordinal > 0 {
702                U16::new(LE, ordinal)
703            } else {
704                U16::new(LE, 0)
705            },
706            name_type: U16::new(LE, ((name_type as u16) << 2) | import_type as u16),
707        };
708        buffer.extend_from_slice(bytes_of(&import_header));
709
710        let symbols = if matches!(import_type, ImportType::Data) {
711            vec![format!("__imp_{}", sym)]
712        } else {
713            vec![format!("__imp_{}", sym), sym.to_string()]
714        };
715
716        // Write symbol name and DLL name
717        buffer.extend(sym.as_bytes());
718        buffer.push(b'\0');
719        buffer.extend(self.import_name.as_bytes());
720        buffer.push(b'\0');
721
722        ArchiveMember {
723            name: self.import_name.to_string(),
724            data: buffer,
725            symbols,
726        }
727    }
728
729    /// Create a weak external file which is described in PE/COFF Aux Format 3.
730    fn create_weak_external(&self, sym: &str, weak: &str, imp: bool) -> ArchiveMember {
731        const NUM_SECTIONS: usize = 1;
732        const NUM_SYMBOLS: usize = 5;
733
734        let mut buffer = Vec::new();
735
736        let pointer_to_symbol_table =
737            size_of::<ImageFileHeader>() + NUM_SECTIONS * size_of::<ImageSectionHeader>();
738        let header = ImageFileHeader {
739            machine: U16::new(LE, self.machine as u16),
740            number_of_sections: U16::new(LE, NUM_SECTIONS as u16),
741            time_date_stamp: U32::new(LE, 0),
742            pointer_to_symbol_table: U32::new(LE, pointer_to_symbol_table as u32),
743            number_of_symbols: U32::new(LE, NUM_SYMBOLS as u32),
744            size_of_optional_header: U16::new(LE, 0),
745            characteristics: U16::new(LE, 0),
746        };
747        buffer.extend_from_slice(bytes_of(&header));
748
749        // Section Header Table
750        let section_header_table = ImageSectionHeader {
751            name: [b'.', b'd', b'r', b'e', b'c', b't', b'v', b'e'],
752            virtual_size: U32::new(LE, 0),
753            virtual_address: U32::new(LE, 0),
754            size_of_raw_data: U32::new(LE, 0),
755            pointer_to_raw_data: U32::new(LE, 0),
756            pointer_to_relocations: U32::new(LE, 0),
757            pointer_to_linenumbers: U32::new(LE, 0),
758            number_of_relocations: U16::new(LE, 0),
759            number_of_linenumbers: U16::new(LE, 0),
760            characteristics: U32::new(LE, IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE),
761        };
762        buffer.extend_from_slice(bytes_of(&section_header_table));
763
764        // Symbol Table
765        let prefix = if imp { "__imp_" } else { "" };
766        let sym3_offset = (size_of::<u32>() + sym.len() + prefix.len() + 1).to_le_bytes();
767        let symbol_table = [
768            ImageSymbol {
769                name: [b'@', b'c', b'o', b'm', b'p', b'.', b'i', b'd'],
770                value: U32Bytes::new(LE, 0),
771                section_number: U16Bytes::new(LE, 0xFFFF),
772                typ: U16Bytes::new(LE, 0),
773                storage_class: IMAGE_SYM_CLASS_STATIC,
774                number_of_aux_symbols: 0,
775            },
776            ImageSymbol {
777                name: [b'@', b'f', b'e', b'a', b't', b'.', b'0', b'0'],
778                value: U32Bytes::new(LE, 0),
779                section_number: U16Bytes::new(LE, 0xFFFF),
780                typ: U16Bytes::new(LE, 0),
781                storage_class: IMAGE_SYM_CLASS_STATIC,
782                number_of_aux_symbols: 0,
783            },
784            ImageSymbol {
785                name: [0, 0, 0, 0, size_of::<u32>() as _, 0, 0, 0],
786                value: U32Bytes::new(LE, 0),
787                section_number: U16Bytes::new(LE, 0),
788                typ: U16Bytes::new(LE, 0),
789                storage_class: IMAGE_SYM_CLASS_EXTERNAL,
790                number_of_aux_symbols: 0,
791            },
792            ImageSymbol {
793                name: [
794                    0,
795                    0,
796                    0,
797                    0,
798                    sym3_offset[0],
799                    sym3_offset[1],
800                    sym3_offset[2],
801                    sym3_offset[3],
802                ],
803                value: U32Bytes::new(LE, 0),
804                section_number: U16Bytes::new(LE, 0),
805                typ: U16Bytes::new(LE, 0),
806                storage_class: IMAGE_SYM_CLASS_WEAK_EXTERNAL,
807                number_of_aux_symbols: 1,
808            },
809            ImageSymbol {
810                name: [2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS as u8, 0, 0, 0],
811                value: U32Bytes::new(LE, 0),
812                section_number: U16Bytes::new(LE, 0),
813                typ: U16Bytes::new(LE, 0),
814                storage_class: IMAGE_SYM_CLASS_NULL,
815                number_of_aux_symbols: 0,
816            },
817        ];
818        for table in &symbol_table {
819            buffer.extend_from_slice(bytes_of(table));
820        }
821
822        // __imp_ String Table
823        Self::write_string_table(
824            &mut buffer,
825            &[
826                &format!("{}{}", prefix, sym),
827                &format!("{}{}", prefix, weak),
828            ],
829        );
830        ArchiveMember {
831            name: self.import_name.to_string(),
832            data: buffer,
833            symbols: Vec::new(),
834        }
835    }
836}