1// Derived from code in LLVM, which is:
2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
3// See https://llvm.org/LICENSE.txt for license information.
4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5
6use std::borrow::Cow;
7use std::io::{Error, ErrorKind, Result, Seek, Write};
8use std::mem::{offset_of, size_of};
9use std::path::PathBuf;
10use std::str::from_utf8;
11
12use 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,
19 IMAGE_SYM_CLASS_STATIC, IMAGE_SYM_CLASS_WEAK_EXTERNAL, IMAGE_WEAK_EXTERN_SEARCH_ALIAS,
20};
21use object::pod::bytes_of;
22
23use crate::coff::{is_arm64ec, ImportNameType, ImportType, MachineTypes};
24use crate::mangler::{get_arm64ec_demangled_function_name, get_arm64ec_mangled_function_name};
25use crate::{write_archive_to_stream, ArchiveKind, NewArchiveMember, DEFAULT_OBJECT_READER};
26
27pub(crate) const IMPORT_DESCRIPTOR_PREFIX: &[u8] = b"__IMPORT_DESCRIPTOR_";
28pub(crate) const NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME: &[u8] = b"__NULL_IMPORT_DESCRIPTOR";
29pub(crate) const NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME_PREFIX: &[u8] = b"__NULL_IMPORT_DESCRIPTOR_";
30pub(crate) const NULL_THUNK_DATA_PREFIX: &[u8] = b"\x7f";
31pub(crate) const NULL_THUNK_DATA_SUFFIX: &[u8] = b"_NULL_THUNK_DATA";
32
33macro_rules! u16 {
34 ($val:expr) => {
35 object::U16::new(object::LittleEndian, $val)
36 };
37}
38
39macro_rules! u32 {
40 ($val:expr) => {
41 object::U32::new(object::LittleEndian, $val)
42 };
43}
44
45// Derived from COFFImportFile::printSymbolName and COFFImportFile::symbol_end.
46pub(crate) fn get_short_import_symbol(
47 buf: &[u8],
48 f: &mut dyn FnMut(&[u8]) -> Result<()>,
49) -> Result<bool> {
50 let mut offset = 0;
51 let header = ImportObjectHeader::parse(buf, &mut offset).map_err(Error::other)?;
52 let data = header.parse_data(buf, &mut offset).map_err(Error::other)?;
53 let is_ec = header.machine.get(object::LittleEndian) == object::pe::IMAGE_FILE_MACHINE_ARM64EC;
54
55 let name = data.symbol();
56 let demangled_name = is_ec
57 .then(|| get_arm64ec_demangled_function_name(from_utf8(name).unwrap()))
58 .flatten()
59 .map_or_else(
60 || Cow::Borrowed(name),
61 |demangled_name| Cow::Owned(demangled_name.into_bytes()),
62 );
63
64 // Import symbol is first.
65 const IMP_PREFIX: &[u8] = b"__imp_";
66 f(&IMP_PREFIX
67 .iter()
68 .chain(demangled_name.as_ref())
69 .copied()
70 .collect::<Vec<_>>())?;
71
72 // For data, only the import symbol is needed.
73 if header.import_type() == ImportType::Data.into() {
74 return Ok(true);
75 }
76
77 // Next, thunk.
78 f(demangled_name.as_ref())?;
79
80 // For Arm64EC, also the EC import symbol and thunk.
81 if header.machine.get(object::LittleEndian) == object::pe::IMAGE_FILE_MACHINE_ARM64EC {
82 const IMP_PREFIX: &[u8] = b"__imp_aux_";
83 f(&IMP_PREFIX
84 .iter()
85 .chain(demangled_name.as_ref())
86 .copied()
87 .collect::<Vec<_>>())?;
88
89 f(name)?;
90 }
91
92 Ok(true)
93}
94
95const READER_FOR_SHORT_IMPORT: crate::ObjectReader = crate::ObjectReader {
96 get_symbols: get_short_import_symbol,
97 ..crate::DEFAULT_OBJECT_READER
98};
99
100pub struct COFFShortExport {
101 /// The name of the export as specified in the .def file or on the command
102 /// line, i.e. "foo" in "/EXPORT:foo", and "bar" in "/EXPORT:foo=bar". This
103 /// may lack mangling, such as underscore prefixing and stdcall suffixing.
104 pub name: String,
105
106 /// The external, exported name. Only non-empty when export renaming is in
107 /// effect, i.e. "foo" in "/EXPORT:foo=bar".
108 pub ext_name: Option<String>,
109
110 /// The real, mangled symbol name from the object file. Given
111 /// "/export:foo=bar", this could be "_bar@8" if bar is stdcall.
112 pub symbol_name: Option<String>,
113
114 /// Creates a weak alias. This is the name of the weak aliasee. In a .def
115 /// file, this is "baz" in "EXPORTS\nfoo = bar == baz".
116 pub alias_target: Option<String>,
117
118 pub ordinal: u16,
119 pub noname: bool,
120 pub data: bool,
121 pub private: bool,
122 pub constant: bool,
123}
124
125fn set_name_to_string_table_entry(symbol: &mut ImageSymbol, offset: usize) {
126 // If first 4 bytes are 0, then second 4 bytes are offset into string table.
127 symbol.name[..4].copy_from_slice(&[0; 4]);
128 symbol.name[4..].copy_from_slice(&u32::try_from(offset).unwrap().to_le_bytes());
129}
130
131fn get_img_rel_relocation(machine: MachineTypes) -> object::U16<object::LittleEndian> {
132 u16!(match machine {
133 MachineTypes::AMD64 => IMAGE_REL_AMD64_ADDR32NB,
134 MachineTypes::ARMNT => IMAGE_REL_ARM_ADDR32NB,
135 MachineTypes::ARM64 | MachineTypes::ARM64EC | MachineTypes::ARM64X =>
136 IMAGE_REL_ARM64_ADDR32NB,
137 MachineTypes::I386 => IMAGE_REL_I386_DIR32NB,
138 })
139}
140
141fn write_string_table(b: &mut Vec<u8>, strings: &[&[u8]]) -> Result<()> {
142 // The COFF string table consists of a 4-byte value which is the size of the
143 // table, including the length field itself. This value is followed by the
144 // string content itself, which is an array of null-terminated C-style
145 // strings. The termination is important as they are referenced to by offset
146 // by the symbol entity in the file format.
147
148 let offset: usize = b.len();
149
150 // Skip over the length field, we will fill it in later as we will have
151 // computed the length while emitting the string content itself.
152 b.extend(iter:0u32.to_le_bytes());
153
154 for s: &&[u8] in strings {
155 b.write_all(buf:s)?;
156 b.write_all(&[0])?;
157 }
158
159 // Backfill the length of the table now that it has been computed.
160 let length: u32 = (b.len() - offset).try_into().unwrap();
161 b[offset..offset + size_of::<u32>()].copy_from_slice(&length.to_le_bytes());
162
163 Ok(())
164}
165
166fn get_name_type(sym: &str, ext_name: &str, machine: MachineTypes, mingw: bool) -> ImportNameType {
167 // A decorated stdcall function in MSVC is exported with the
168 // type IMPORT_NAME, and the exported function name includes the
169 // the leading underscore. In MinGW on the other hand, a decorated
170 // stdcall function still omits the underscore (IMPORT_NAME_NOPREFIX).
171 // See the comment in isDecorated in COFFModuleDefinition.cpp for more
172 // details.
173 if ext_name.starts_with('_') && ext_name.contains('@') && !mingw {
174 ImportNameType::Name
175 } else if sym != ext_name {
176 ImportNameType::NameUndecorate
177 } else if machine == MachineTypes::I386 && sym.starts_with('_') {
178 ImportNameType::NameNoprefix
179 } else {
180 ImportNameType::Name
181 }
182}
183
184fn replace(s: &str, mut from: &str, mut to: &str) -> Result<String> {
185 if let Some((before: &str, after: &str)) = s.split_once(delimiter:from) {
186 return Ok(format!("{before}{to}{after}"));
187 }
188
189 // From and To may be mangled, but substrings in S may not.
190 if from.starts_with('_') && to.starts_with('_') {
191 from = &from[1..];
192 to = &to[1..];
193 if let Some((before: &str, after: &str)) = s.split_once(delimiter:from) {
194 return Ok(format!("{before}{to}{after}"));
195 }
196 }
197
198 Err(Error::other(error:format!(
199 "{s}: replacing '{from}' with '{to}' failed"
200 )))
201}
202
203/// This class constructs various small object files necessary to support linking
204/// symbols imported from a DLL. The contents are pretty strictly defined and
205/// nearly entirely static. The details of the structures files are defined in
206/// WINNT.h and the PE/COFF specification.
207struct ObjectFactory<'a> {
208 native_machine: MachineTypes,
209 import_name: &'a str,
210 import_descriptor_symbol_name: Vec<u8>,
211 null_thunk_symbol_name: Vec<u8>,
212 null_import_descriptor_symbol_name: Vec<u8>,
213}
214
215impl<'a> ObjectFactory<'a> {
216 fn new(s: &'a str, m: MachineTypes, whole_archive_compat: bool) -> Result<Self> {
217 let import_as_path = PathBuf::from(s);
218 let library = import_as_path
219 .file_stem()
220 .ok_or_else(|| {
221 Error::new(
222 ErrorKind::InvalidInput,
223 "Import name did not end with a file name",
224 )
225 })?
226 .to_str()
227 .ok_or_else(|| Error::new(ErrorKind::InvalidInput, "Import name is not valid UTF-8"))?;
228 let library = library.as_bytes();
229 Ok(Self {
230 native_machine: m,
231 import_name: s,
232 import_descriptor_symbol_name: IMPORT_DESCRIPTOR_PREFIX
233 .iter()
234 .chain(library)
235 .copied()
236 .collect(),
237 null_thunk_symbol_name: NULL_THUNK_DATA_PREFIX
238 .iter()
239 .chain(library)
240 .chain(NULL_THUNK_DATA_SUFFIX)
241 .copied()
242 .collect(),
243 null_import_descriptor_symbol_name: if whole_archive_compat {
244 NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME_PREFIX
245 .iter()
246 .chain(library)
247 .copied()
248 .collect()
249 } else {
250 NULL_IMPORT_DESCRIPTOR_SYMBOL_NAME.into()
251 },
252 })
253 }
254
255 fn is_64_bit(&self) -> bool {
256 crate::coff::is_64_bit(self.native_machine)
257 }
258
259 /// Creates an Import Descriptor. This is a small object file which contains a
260 /// reference to the terminators and contains the library name (entry) for the
261 /// import name table. It will force the linker to construct the necessary
262 /// structure to import symbols from the DLL.
263 fn create_import_descriptor(&self) -> Result<NewArchiveMember<'_>> {
264 let mut buffer = Vec::new();
265
266 const NUMBER_OF_SECTIONS: usize = 2;
267 const NUMBER_OF_SYMBOLS: usize = 7;
268 const NUMBER_OF_RELOCATIONS: usize = 3;
269
270 // COFF Header
271 let header = ImageFileHeader {
272 machine: u16!(self.native_machine.into()),
273 number_of_sections: u16!(NUMBER_OF_SECTIONS.try_into().unwrap()),
274 time_date_stamp: u32!(0),
275 pointer_to_symbol_table: u32!((size_of::<ImageFileHeader>() + (NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()) +
276 // .idata$2
277 size_of::<ImageImportDescriptor>() +
278 NUMBER_OF_RELOCATIONS * size_of::<ImageRelocation>() +
279 // .idata$4
280 (self.import_name.len() + 1)).try_into().unwrap()),
281 number_of_symbols: u32!(NUMBER_OF_SYMBOLS.try_into().unwrap()),
282 size_of_optional_header: u16!(0),
283 characteristics: u16!(if self.is_64_bit() {
284 0
285 } else {
286 IMAGE_FILE_32BIT_MACHINE
287 }),
288 };
289 buffer.write_all(bytes_of(&header))?;
290
291 // Section Header Table
292 let section_table: [_; NUMBER_OF_SECTIONS] = [
293 ImageSectionHeader {
294 name: *b".idata$2",
295 virtual_size: u32!(0),
296 virtual_address: u32!(0),
297 size_of_raw_data: u32!(size_of::<ImageImportDescriptor>().try_into().unwrap()),
298 pointer_to_raw_data: u32!((size_of::<ImageFileHeader>()
299 + NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>())
300 .try_into()
301 .unwrap()),
302 pointer_to_relocations: u32!((size_of::<ImageFileHeader>()
303 + NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()
304 + size_of::<ImageImportDescriptor>())
305 .try_into()
306 .unwrap()),
307 pointer_to_linenumbers: u32!(0),
308 number_of_relocations: u16!(NUMBER_OF_RELOCATIONS.try_into().unwrap()),
309 number_of_linenumbers: u16!(0),
310 characteristics: u32!(
311 IMAGE_SCN_ALIGN_4BYTES
312 | IMAGE_SCN_CNT_INITIALIZED_DATA
313 | IMAGE_SCN_MEM_READ
314 | IMAGE_SCN_MEM_WRITE
315 ),
316 },
317 ImageSectionHeader {
318 name: *b".idata$6",
319 virtual_size: u32!(0),
320 virtual_address: u32!(0),
321 size_of_raw_data: u32!((self.import_name.len() + 1).try_into().unwrap()),
322 pointer_to_raw_data: u32!((size_of::<ImageFileHeader>()
323 + NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()
324 + size_of::<ImageImportDescriptor>()
325 + NUMBER_OF_RELOCATIONS * size_of::<ImageRelocation>())
326 .try_into()
327 .unwrap()),
328 pointer_to_relocations: u32!(0),
329 pointer_to_linenumbers: u32!(0),
330 number_of_relocations: u16!(0),
331 number_of_linenumbers: u16!(0),
332 characteristics: u32!(
333 IMAGE_SCN_ALIGN_2BYTES
334 | IMAGE_SCN_CNT_INITIALIZED_DATA
335 | IMAGE_SCN_MEM_READ
336 | IMAGE_SCN_MEM_WRITE
337 ),
338 },
339 ];
340 buffer.write_all(bytes_of(&section_table))?;
341
342 // .idata$2
343 let import_descriptor = ImageImportDescriptor {
344 original_first_thunk: u32!(0),
345 time_date_stamp: u32!(0),
346 forwarder_chain: u32!(0),
347 name: u32!(0),
348 first_thunk: u32!(0),
349 };
350 buffer.write_all(bytes_of(&import_descriptor))?;
351
352 let relocation_table: [_; NUMBER_OF_RELOCATIONS] = [
353 ImageRelocation {
354 virtual_address: u32!((offset_of!(ImageImportDescriptor, name))
355 .try_into()
356 .unwrap()),
357 symbol_table_index: u32!(2),
358 typ: get_img_rel_relocation(self.native_machine),
359 },
360 ImageRelocation {
361 virtual_address: u32!(offset_of!(ImageImportDescriptor, original_first_thunk)
362 .try_into()
363 .unwrap()),
364 symbol_table_index: u32!(3),
365 typ: get_img_rel_relocation(self.native_machine),
366 },
367 ImageRelocation {
368 virtual_address: u32!(offset_of!(ImageImportDescriptor, first_thunk)
369 .try_into()
370 .unwrap()),
371 symbol_table_index: u32!(4),
372 typ: get_img_rel_relocation(self.native_machine),
373 },
374 ];
375 buffer.write_all(bytes_of(&relocation_table))?;
376
377 // .idata$6
378 buffer.write_all(self.import_name.as_bytes())?;
379 buffer.write_all(&[0])?;
380
381 // Symbol Table
382 let mut symbol_table: [_; NUMBER_OF_SYMBOLS] = [
383 ImageSymbol {
384 name: [0; 8],
385 value: u32!(0),
386 section_number: u16!(1),
387 typ: u16!(0),
388 storage_class: IMAGE_SYM_CLASS_EXTERNAL,
389 number_of_aux_symbols: 0,
390 },
391 ImageSymbol {
392 name: *b".idata$2",
393 value: u32!(0),
394 section_number: u16!(1),
395 typ: u16!(0),
396 storage_class: IMAGE_SYM_CLASS_SECTION,
397 number_of_aux_symbols: 0,
398 },
399 ImageSymbol {
400 name: *b".idata$6",
401 value: u32!(0),
402 section_number: u16!(2),
403 typ: u16!(0),
404 storage_class: IMAGE_SYM_CLASS_STATIC,
405 number_of_aux_symbols: 0,
406 },
407 ImageSymbol {
408 name: *b".idata$4",
409 value: u32!(0),
410 section_number: u16!(0),
411 typ: u16!(0),
412 storage_class: IMAGE_SYM_CLASS_SECTION,
413 number_of_aux_symbols: 0,
414 },
415 ImageSymbol {
416 name: *b".idata$5",
417 value: u32!(0),
418 section_number: u16!(0),
419 typ: u16!(0),
420 storage_class: IMAGE_SYM_CLASS_SECTION,
421 number_of_aux_symbols: 0,
422 },
423 ImageSymbol {
424 name: [0; 8],
425 value: u32!(0),
426 section_number: u16!(0),
427 typ: u16!(0),
428 storage_class: IMAGE_SYM_CLASS_EXTERNAL,
429 number_of_aux_symbols: 0,
430 },
431 ImageSymbol {
432 name: [0; 8],
433 value: u32!(0),
434 section_number: u16!(0),
435 typ: u16!(0),
436 storage_class: IMAGE_SYM_CLASS_EXTERNAL,
437 number_of_aux_symbols: 0,
438 },
439 ];
440 // TODO: Name.Offset.Offset here and in the all similar places below
441 // suggests a names refactoring. Maybe StringTableOffset.Value?
442 set_name_to_string_table_entry(&mut symbol_table[0], size_of::<u32>());
443 set_name_to_string_table_entry(
444 &mut symbol_table[5],
445 size_of::<u32>() + self.import_descriptor_symbol_name.len() + 1,
446 );
447 set_name_to_string_table_entry(
448 &mut symbol_table[6],
449 size_of::<u32>()
450 + self.import_descriptor_symbol_name.len()
451 + 1
452 + self.null_import_descriptor_symbol_name.len()
453 + 1,
454 );
455 buffer.write_all(bytes_of(&symbol_table))?;
456
457 // String Table
458 write_string_table(
459 &mut buffer,
460 &[
461 &self.import_descriptor_symbol_name,
462 &self.null_import_descriptor_symbol_name,
463 &self.null_thunk_symbol_name,
464 ],
465 )?;
466
467 Ok(NewArchiveMember::new(
468 buffer.into_boxed_slice(),
469 &DEFAULT_OBJECT_READER,
470 self.import_name.to_string(),
471 ))
472 }
473
474 /// Creates a NULL import descriptor. This is a small object file whcih
475 /// contains a NULL import descriptor. It is used to terminate the imports
476 /// from a specific DLL.
477 fn create_null_import_descriptor(&self) -> Result<NewArchiveMember<'_>> {
478 let mut buffer = Vec::new();
479
480 const NUMBER_OF_SECTIONS: usize = 1;
481 const NUMBER_OF_SYMBOLS: usize = 1;
482
483 // COFF Header
484 let header = ImageFileHeader {
485 machine: u16!(self.native_machine.into()),
486 number_of_sections: u16!(NUMBER_OF_SECTIONS.try_into().unwrap()),
487 time_date_stamp: u32!(0),
488 pointer_to_symbol_table: u32!((size_of::<ImageFileHeader>() + (NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()) +
489 // .idata$3
490 size_of::<ImageImportDescriptor>()).try_into().unwrap()),
491 number_of_symbols: u32!(NUMBER_OF_SYMBOLS.try_into().unwrap()),
492 size_of_optional_header: u16!(0),
493 characteristics: u16!(if self.is_64_bit() {
494 0
495 } else {
496 IMAGE_FILE_32BIT_MACHINE
497 }),
498 };
499 buffer.write_all(bytes_of(&header))?;
500
501 // Section Header Table
502 let section_table: [_; NUMBER_OF_SECTIONS] = [ImageSectionHeader {
503 name: *b".idata$3",
504 virtual_size: u32!(0),
505 virtual_address: u32!(0),
506 size_of_raw_data: u32!(size_of::<ImageImportDescriptor>().try_into().unwrap()),
507 pointer_to_raw_data: u32!((size_of::<ImageFileHeader>()
508 + NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>())
509 .try_into()
510 .unwrap()),
511 pointer_to_relocations: u32!(0),
512 pointer_to_linenumbers: u32!(0),
513 number_of_relocations: u16!(0),
514 number_of_linenumbers: u16!(0),
515 characteristics: u32!(
516 IMAGE_SCN_ALIGN_4BYTES
517 | IMAGE_SCN_CNT_INITIALIZED_DATA
518 | IMAGE_SCN_MEM_READ
519 | IMAGE_SCN_MEM_WRITE
520 ),
521 }];
522 buffer.write_all(bytes_of(&section_table))?;
523
524 // .idata$3
525 let import_descriptor = ImageImportDescriptor {
526 original_first_thunk: u32!(0),
527 time_date_stamp: u32!(0),
528 forwarder_chain: u32!(0),
529 name: u32!(0),
530 first_thunk: u32!(0),
531 };
532 buffer.write_all(bytes_of(&import_descriptor))?;
533
534 // Symbol Table
535 let mut symbol_table: [_; NUMBER_OF_SYMBOLS] = [ImageSymbol {
536 name: [0; 8],
537 value: u32!(0),
538 section_number: u16!(1),
539 typ: u16!(0),
540 storage_class: IMAGE_SYM_CLASS_EXTERNAL,
541 number_of_aux_symbols: 0,
542 }];
543 set_name_to_string_table_entry(&mut symbol_table[0], size_of::<u32>());
544 buffer.write_all(bytes_of(&symbol_table))?;
545
546 // String Table
547 write_string_table(&mut buffer, &[&self.null_import_descriptor_symbol_name])?;
548
549 Ok(NewArchiveMember::new(
550 buffer.into_boxed_slice(),
551 &DEFAULT_OBJECT_READER,
552 self.import_name.to_string(),
553 ))
554 }
555
556 /// Create a NULL Thunk Entry. This is a small object file which contains a
557 /// NULL Import Address Table entry and a NULL Import Lookup Table Entry. It
558 /// is used to terminate the IAT and ILT.
559 fn create_null_thunk(&self) -> Result<NewArchiveMember<'_>> {
560 let mut buffer = Vec::new();
561
562 const NUMBER_OF_SECTIONS: usize = 2;
563 const NUMBER_OF_SYMBOLS: usize = 1;
564 let va_size = if self.is_64_bit() { 8 } else { 4 };
565
566 // COFF Header
567 let header = ImageFileHeader {
568 machine: u16!(self.native_machine.into()),
569 number_of_sections: u16!(NUMBER_OF_SECTIONS.try_into().unwrap()),
570 time_date_stamp: u32!(0),
571 pointer_to_symbol_table: u32!((size_of::<ImageFileHeader>() + (NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()) +
572 // .idata$5
573 va_size +
574 // .idata$4
575 va_size).try_into().unwrap()),
576 number_of_symbols: u32!(NUMBER_OF_SYMBOLS.try_into().unwrap()),
577 size_of_optional_header: u16!(0),
578 characteristics: u16!(if self.is_64_bit() {
579 0
580 } else {
581 IMAGE_FILE_32BIT_MACHINE
582 }),
583 };
584 buffer.write_all(bytes_of(&header))?;
585
586 // Section Header Table
587 let alignment = if self.is_64_bit() {
588 IMAGE_SCN_ALIGN_8BYTES
589 } else {
590 IMAGE_SCN_ALIGN_4BYTES
591 };
592 let section_table: [_; NUMBER_OF_SECTIONS] = [
593 ImageSectionHeader {
594 name: *b".idata$5",
595 virtual_size: u32!(0),
596 virtual_address: u32!(0),
597 size_of_raw_data: u32!(va_size.try_into().unwrap()),
598 pointer_to_raw_data: u32!((size_of::<ImageFileHeader>()
599 + NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>())
600 .try_into()
601 .unwrap()),
602 pointer_to_relocations: u32!(0),
603 pointer_to_linenumbers: u32!(0),
604 number_of_relocations: u16!(0),
605 number_of_linenumbers: u16!(0),
606 characteristics: u32!(
607 alignment
608 | IMAGE_SCN_CNT_INITIALIZED_DATA
609 | IMAGE_SCN_MEM_READ
610 | IMAGE_SCN_MEM_WRITE
611 ),
612 },
613 ImageSectionHeader {
614 name: *b".idata$4",
615 virtual_size: u32!(0),
616 virtual_address: u32!(0),
617 size_of_raw_data: u32!(va_size.try_into().unwrap()),
618 pointer_to_raw_data: u32!((size_of::<ImageFileHeader>()
619 + NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()
620 + va_size)
621 .try_into()
622 .unwrap()),
623 pointer_to_relocations: u32!(0),
624 pointer_to_linenumbers: u32!(0),
625 number_of_relocations: u16!(0),
626 number_of_linenumbers: u16!(0),
627 characteristics: u32!(
628 alignment
629 | IMAGE_SCN_CNT_INITIALIZED_DATA
630 | IMAGE_SCN_MEM_READ
631 | IMAGE_SCN_MEM_WRITE
632 ),
633 },
634 ];
635 buffer.write_all(bytes_of(&section_table))?;
636
637 // .idata$5, ILT
638 buffer.write_all(&vec![0; va_size])?;
639
640 // .idata$4, IAT
641 buffer.write_all(&vec![0; va_size])?;
642
643 // Symbol Table
644 let mut symbol_table: [_; NUMBER_OF_SYMBOLS] = [ImageSymbol {
645 name: [0; 8],
646 value: u32!(0),
647 section_number: u16!(1),
648 typ: u16!(0),
649 storage_class: IMAGE_SYM_CLASS_EXTERNAL,
650 number_of_aux_symbols: 0,
651 }];
652 set_name_to_string_table_entry(&mut symbol_table[0], size_of::<u32>());
653 buffer.write_all(bytes_of(&symbol_table))?;
654
655 // String Table
656 write_string_table(&mut buffer, &[&self.null_thunk_symbol_name])?;
657
658 Ok(NewArchiveMember::new(
659 buffer.into_boxed_slice(),
660 &DEFAULT_OBJECT_READER,
661 self.import_name.to_string(),
662 ))
663 }
664
665 /// Create a short import file which is described in PE/COFF spec 7. Import
666 /// Library Format.
667 fn create_short_import(
668 &self,
669 sym: &str,
670 ordinal: u16,
671 import_type: ImportType,
672 name_type: ImportNameType,
673 export_name: Option<&str>,
674 machine: MachineTypes,
675 ) -> Result<NewArchiveMember<'_>> {
676 let mut imp_size = self.import_name.len() + sym.len() + 2; // +2 for NULs
677 if let Some(export_name) = export_name {
678 imp_size += export_name.len() + 1;
679 }
680 let size = size_of::<ImportObjectHeader>() + imp_size;
681 let mut buf = Vec::new();
682 buf.reserve_exact(size);
683
684 // Write short import library.
685 let imp = ImportObjectHeader {
686 sig1: u16!(0),
687 sig2: u16!(0xFFFF),
688 version: u16!(0),
689 machine: u16!(machine.into()),
690 time_date_stamp: u32!(0),
691 size_of_data: u32!(imp_size.try_into().unwrap()),
692 ordinal_or_hint: u16!(ordinal),
693 name_type: u16!((u16::from(name_type) << 2) | u16::from(import_type)),
694 };
695 buf.write_all(bytes_of(&imp))?;
696
697 // Write symbol name and DLL name.
698 buf.write_all(sym.as_bytes())?;
699 buf.write_all(&[0])?;
700 buf.write_all(self.import_name.as_bytes())?;
701 buf.write_all(&[0])?;
702 if let Some(export_name) = export_name {
703 buf.write_all(export_name.as_bytes())?;
704 buf.write_all(&[0])?;
705 }
706
707 Ok(NewArchiveMember::new(
708 buf.into_boxed_slice(),
709 &READER_FOR_SHORT_IMPORT,
710 self.import_name.to_string(),
711 ))
712 }
713
714 /// Create a weak external file which is described in PE/COFF Aux Format 3.
715 fn create_weak_external(
716 &self,
717 sym: &str,
718 weak: &str,
719 imp: bool,
720 machine: MachineTypes,
721 ) -> Result<NewArchiveMember<'_>> {
722 let mut buffer = Vec::new();
723 const NUMBER_OF_SECTIONS: usize = 1;
724 const NUMBER_OF_SYMBOLS: usize = 5;
725
726 // COFF Header
727 let header = ImageFileHeader {
728 machine: u16!(machine.into()),
729 number_of_sections: u16!(NUMBER_OF_SECTIONS.try_into().unwrap()),
730 time_date_stamp: u32!(0),
731 pointer_to_symbol_table: u32!((size_of::<ImageFileHeader>()
732 + (NUMBER_OF_SECTIONS * size_of::<ImageSectionHeader>()))
733 .try_into()
734 .unwrap()),
735 number_of_symbols: u32!(NUMBER_OF_SYMBOLS.try_into().unwrap()),
736 size_of_optional_header: u16!(0),
737 characteristics: u16!(0),
738 };
739 buffer.write_all(bytes_of(&header))?;
740
741 // Section Header Table
742 let section_table: [_; NUMBER_OF_SECTIONS] = [ImageSectionHeader {
743 name: *b".drectve",
744 virtual_size: u32!(0),
745 virtual_address: u32!(0),
746 size_of_raw_data: u32!(0),
747 pointer_to_raw_data: u32!(0),
748 pointer_to_relocations: u32!(0),
749 pointer_to_linenumbers: u32!(0),
750 number_of_relocations: u16!(0),
751 number_of_linenumbers: u16!(0),
752 characteristics: u32!(IMAGE_SCN_LNK_INFO | IMAGE_SCN_LNK_REMOVE),
753 }];
754 buffer.write_all(bytes_of(&section_table))?;
755
756 // Symbol Table
757 let mut symbol_table: [_; NUMBER_OF_SYMBOLS] = [
758 ImageSymbol {
759 name: *b"@comp.id",
760 value: u32!(0),
761 section_number: u16!(0xFFFF),
762 typ: u16!(0),
763 storage_class: IMAGE_SYM_CLASS_STATIC,
764 number_of_aux_symbols: 0,
765 },
766 ImageSymbol {
767 name: *b"@feat.00",
768 value: u32!(0),
769 section_number: u16!(0xFFFF),
770 typ: u16!(0),
771 storage_class: IMAGE_SYM_CLASS_STATIC,
772 number_of_aux_symbols: 0,
773 },
774 ImageSymbol {
775 name: [0; 8],
776 value: u32!(0),
777 section_number: u16!(0),
778 typ: u16!(0),
779 storage_class: IMAGE_SYM_CLASS_EXTERNAL,
780 number_of_aux_symbols: 0,
781 },
782 ImageSymbol {
783 name: [0; 8],
784 value: u32!(0),
785 section_number: u16!(0),
786 typ: u16!(0),
787 storage_class: IMAGE_SYM_CLASS_WEAK_EXTERNAL,
788 number_of_aux_symbols: 1,
789 },
790 ImageSymbol {
791 name: [2, 0, 0, 0, IMAGE_WEAK_EXTERN_SEARCH_ALIAS as u8, 0, 0, 0],
792 value: u32!(0),
793 section_number: u16!(0),
794 typ: u16!(0),
795 storage_class: IMAGE_SYM_CLASS_NULL,
796 number_of_aux_symbols: 0,
797 },
798 ];
799 set_name_to_string_table_entry(&mut symbol_table[2], size_of::<u32>());
800
801 //__imp_ String Table
802 let prefix: &[u8] = if imp { b"__imp_" } else { b"" };
803 set_name_to_string_table_entry(
804 &mut symbol_table[3],
805 size_of::<u32>() + sym.len() + prefix.len() + 1,
806 );
807 buffer.write_all(bytes_of(&symbol_table))?;
808
809 write_string_table(
810 &mut buffer,
811 &[
812 &prefix
813 .iter()
814 .chain(sym.as_bytes())
815 .copied()
816 .collect::<Vec<_>>(),
817 &prefix
818 .iter()
819 .chain(weak.as_bytes())
820 .copied()
821 .collect::<Vec<_>>(),
822 ],
823 )?;
824
825 // Copied here so we can still use writeStringTable
826 Ok(NewArchiveMember::new(
827 buffer.into_boxed_slice(),
828 &DEFAULT_OBJECT_READER,
829 self.import_name.to_string(),
830 ))
831 }
832}
833
834pub fn write_import_library<W: Write + Seek>(
835 w: &mut W,
836 import_name: &str,
837 exports: &[COFFShortExport],
838 machine: MachineTypes,
839 mingw: bool,
840 whole_archive_compat: bool,
841) -> Result<()> {
842 let native_machine = if machine == MachineTypes::ARM64EC {
843 MachineTypes::ARM64
844 } else {
845 machine
846 };
847
848 let of = ObjectFactory::new(import_name, native_machine, whole_archive_compat)?;
849 let mut members = Vec::new();
850
851 members.push(of.create_import_descriptor()?);
852
853 members.push(of.create_null_import_descriptor()?);
854
855 members.push(of.create_null_thunk()?);
856
857 for e in exports {
858 if e.private {
859 continue;
860 }
861
862 let mut import_type = ImportType::Code;
863 if e.data {
864 import_type = ImportType::Data;
865 }
866 if e.constant {
867 import_type = ImportType::Const;
868 }
869
870 let symbol_name = if let Some(symbol_name) = e.symbol_name.as_ref() {
871 symbol_name
872 } else {
873 &e.name
874 };
875
876 let mut name: Cow<'_, str> = if let Some(ext_name) = e.ext_name.as_ref() {
877 Cow::Owned(replace(symbol_name, &e.name, ext_name)?)
878 } else {
879 Cow::Borrowed(symbol_name)
880 };
881
882 if let Some(alias_target) = e.alias_target.as_ref() {
883 if name.as_ref() != alias_target {
884 members.push(of.create_weak_external(alias_target, &name, false, machine)?);
885 members.push(of.create_weak_external(alias_target, &name, true, machine)?);
886 continue;
887 }
888 }
889
890 let mut name_type = if e.noname {
891 ImportNameType::Ordinal
892 } else {
893 get_name_type(symbol_name, &e.name, machine, mingw)
894 };
895
896 // On ARM64EC, use EXPORTAS to import demangled name for mangled symbols.
897 let export_name = if import_type == ImportType::Code && crate::coff::is_arm64ec(machine) {
898 if let Some(mangled_name) = get_arm64ec_mangled_function_name(&name) {
899 name_type = ImportNameType::NameExportas;
900 let export_name = name;
901 name = Cow::Owned(mangled_name);
902 Some(export_name)
903 } else {
904 name_type = ImportNameType::NameExportas;
905 get_arm64ec_demangled_function_name(&name).map(Cow::Owned)
906 }
907 } else {
908 None
909 };
910
911 members.push(of.create_short_import(
912 &name,
913 e.ordinal,
914 import_type,
915 name_type,
916 export_name.as_deref(),
917 machine,
918 )?);
919 }
920
921 write_archive_to_stream(
922 w,
923 &members,
924 if mingw {
925 ArchiveKind::Gnu
926 } else {
927 ArchiveKind::Coff
928 },
929 false,
930 is_arm64ec(machine),
931 )
932}
933

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more