object/write/coff/
object.rs

1use alloc::vec::Vec;
2
3use crate::endian::*;
4use crate::pe as coff;
5use crate::write::coff::writer;
6use crate::write::util::*;
7use crate::write::*;
8
9#[derive(Default, Clone, Copy)]
10struct SectionOffsets {
11    name: writer::Name,
12    offset: u32,
13    reloc_offset: u32,
14    selection: u8,
15    associative_section: u32,
16}
17
18#[derive(Default, Clone, Copy)]
19struct SymbolOffsets {
20    name: writer::Name,
21    index: u32,
22    aux_count: u8,
23}
24
25/// Internal format to use for the `.drectve` section containing linker
26/// directives for symbol exports.
27#[derive(Clone, Copy, Debug, PartialEq, Eq)]
28pub enum CoffExportStyle {
29    /// MSVC format supported by link.exe and LLD.
30    Msvc,
31    /// Gnu format supported by GNU LD and LLD.
32    Gnu,
33}
34
35impl<'a> Object<'a> {
36    pub(crate) fn coff_section_info(
37        &self,
38        section: StandardSection,
39    ) -> (&'static [u8], &'static [u8], SectionKind, SectionFlags) {
40        match section {
41            StandardSection::Text => (&[], &b".text"[..], SectionKind::Text, SectionFlags::None),
42            StandardSection::Data => (&[], &b".data"[..], SectionKind::Data, SectionFlags::None),
43            StandardSection::ReadOnlyData
44            | StandardSection::ReadOnlyDataWithRel
45            | StandardSection::ReadOnlyString => (
46                &[],
47                &b".rdata"[..],
48                SectionKind::ReadOnlyData,
49                SectionFlags::None,
50            ),
51            StandardSection::UninitializedData => (
52                &[],
53                &b".bss"[..],
54                SectionKind::UninitializedData,
55                SectionFlags::None,
56            ),
57            // TLS sections are data sections with a special name.
58            StandardSection::Tls => (&[], &b".tls$"[..], SectionKind::Data, SectionFlags::None),
59            StandardSection::UninitializedTls => {
60                // Unsupported section.
61                (&[], &[], SectionKind::UninitializedTls, SectionFlags::None)
62            }
63            StandardSection::TlsVariables => {
64                // Unsupported section.
65                (&[], &[], SectionKind::TlsVariables, SectionFlags::None)
66            }
67            StandardSection::Common => {
68                // Unsupported section.
69                (&[], &[], SectionKind::Common, SectionFlags::None)
70            }
71            StandardSection::GnuProperty => {
72                // Unsupported section.
73                (&[], &[], SectionKind::Note, SectionFlags::None)
74            }
75        }
76    }
77
78    pub(crate) fn coff_subsection_name(&self, section: &[u8], value: &[u8]) -> Vec<u8> {
79        let mut name = section.to_vec();
80        if !value.is_empty() {
81            name.push(b'$');
82            name.extend_from_slice(value);
83        }
84        name
85    }
86
87    pub(crate) fn coff_section_flags(&self, section: &Section<'_>) -> SectionFlags {
88        let characteristics = match section.kind {
89            SectionKind::Text => {
90                coff::IMAGE_SCN_CNT_CODE | coff::IMAGE_SCN_MEM_EXECUTE | coff::IMAGE_SCN_MEM_READ
91            }
92            SectionKind::Data => {
93                coff::IMAGE_SCN_CNT_INITIALIZED_DATA
94                    | coff::IMAGE_SCN_MEM_READ
95                    | coff::IMAGE_SCN_MEM_WRITE
96            }
97            SectionKind::UninitializedData => {
98                coff::IMAGE_SCN_CNT_UNINITIALIZED_DATA
99                    | coff::IMAGE_SCN_MEM_READ
100                    | coff::IMAGE_SCN_MEM_WRITE
101            }
102            SectionKind::ReadOnlyData
103            | SectionKind::ReadOnlyDataWithRel
104            | SectionKind::ReadOnlyString => {
105                coff::IMAGE_SCN_CNT_INITIALIZED_DATA | coff::IMAGE_SCN_MEM_READ
106            }
107            SectionKind::Debug
108            | SectionKind::DebugString
109            | SectionKind::Other
110            | SectionKind::OtherString => {
111                coff::IMAGE_SCN_CNT_INITIALIZED_DATA
112                    | coff::IMAGE_SCN_MEM_READ
113                    | coff::IMAGE_SCN_MEM_DISCARDABLE
114            }
115            SectionKind::Linker => coff::IMAGE_SCN_LNK_INFO | coff::IMAGE_SCN_LNK_REMOVE,
116            SectionKind::Common
117            | SectionKind::Tls
118            | SectionKind::UninitializedTls
119            | SectionKind::TlsVariables
120            | SectionKind::Note
121            | SectionKind::Unknown
122            | SectionKind::Metadata
123            | SectionKind::Elf(_) => {
124                return SectionFlags::None;
125            }
126        };
127        SectionFlags::Coff { characteristics }
128    }
129
130    pub(crate) fn coff_symbol_flags(&self, _symbol: &Symbol) -> SymbolFlags<SectionId, SymbolId> {
131        // TODO: Need SymbolFlags::Coff for COFF-specific flags (type and storage class).
132        SymbolFlags::None
133    }
134
135    pub(crate) fn coff_translate_relocation(&mut self, reloc: &mut Relocation) -> Result<()> {
136        use RelocationEncoding as E;
137        use RelocationKind as K;
138
139        let (mut kind, encoding, size) = if let RelocationFlags::Generic {
140            kind,
141            encoding,
142            size,
143        } = reloc.flags
144        {
145            (kind, encoding, size)
146        } else {
147            return Ok(());
148        };
149        if kind == K::GotRelative {
150            // Use a stub symbol for the relocation instead.
151            // This isn't really a GOT, but it's a similar purpose.
152            // TODO: need to handle DLL imports differently?
153            kind = K::Relative;
154            reloc.symbol = self.coff_add_stub_symbol(reloc.symbol)?;
155        } else if kind == K::PltRelative {
156            // Windows doesn't need a separate relocation type for
157            // references to functions in import libraries.
158            // For convenience, treat this the same as Relative.
159            kind = K::Relative;
160        }
161
162        let unsupported_reloc = || Err(Error(format!("unimplemented relocation {:?}", reloc)));
163        let typ = match self.architecture {
164            Architecture::I386 => match (kind, size) {
165                (K::Absolute, 16) => coff::IMAGE_REL_I386_DIR16,
166                (K::Relative, 16) => coff::IMAGE_REL_I386_REL16,
167                (K::Absolute, 32) => coff::IMAGE_REL_I386_DIR32,
168                (K::ImageOffset, 32) => coff::IMAGE_REL_I386_DIR32NB,
169                (K::SectionIndex, 16) => coff::IMAGE_REL_I386_SECTION,
170                (K::SectionOffset, 32) => coff::IMAGE_REL_I386_SECREL,
171                (K::SectionOffset, 7) => coff::IMAGE_REL_I386_SECREL7,
172                (K::Relative, 32) => coff::IMAGE_REL_I386_REL32,
173                _ => return unsupported_reloc(),
174            },
175            Architecture::X86_64 => match (kind, size) {
176                (K::Absolute, 64) => coff::IMAGE_REL_AMD64_ADDR64,
177                (K::Absolute, 32) => coff::IMAGE_REL_AMD64_ADDR32,
178                (K::ImageOffset, 32) => coff::IMAGE_REL_AMD64_ADDR32NB,
179                (K::Relative, 32) => match reloc.addend {
180                    -5 => coff::IMAGE_REL_AMD64_REL32_1,
181                    -6 => coff::IMAGE_REL_AMD64_REL32_2,
182                    -7 => coff::IMAGE_REL_AMD64_REL32_3,
183                    -8 => coff::IMAGE_REL_AMD64_REL32_4,
184                    -9 => coff::IMAGE_REL_AMD64_REL32_5,
185                    _ => coff::IMAGE_REL_AMD64_REL32,
186                },
187                (K::SectionIndex, 16) => coff::IMAGE_REL_AMD64_SECTION,
188                (K::SectionOffset, 32) => coff::IMAGE_REL_AMD64_SECREL,
189                (K::SectionOffset, 7) => coff::IMAGE_REL_AMD64_SECREL7,
190                _ => return unsupported_reloc(),
191            },
192            Architecture::Arm => match (kind, size) {
193                (K::Absolute, 32) => coff::IMAGE_REL_ARM_ADDR32,
194                (K::ImageOffset, 32) => coff::IMAGE_REL_ARM_ADDR32NB,
195                (K::Relative, 32) => coff::IMAGE_REL_ARM_REL32,
196                (K::SectionIndex, 16) => coff::IMAGE_REL_ARM_SECTION,
197                (K::SectionOffset, 32) => coff::IMAGE_REL_ARM_SECREL,
198                _ => return unsupported_reloc(),
199            },
200            Architecture::Aarch64 => match (kind, encoding, size) {
201                (K::Absolute, _, 32) => coff::IMAGE_REL_ARM64_ADDR32,
202                (K::ImageOffset, _, 32) => coff::IMAGE_REL_ARM64_ADDR32NB,
203                (K::SectionIndex, _, 16) => coff::IMAGE_REL_ARM64_SECTION,
204                (K::SectionOffset, _, 32) => coff::IMAGE_REL_ARM64_SECREL,
205                (K::Absolute, _, 64) => coff::IMAGE_REL_ARM64_ADDR64,
206                (K::Relative, _, 32) => coff::IMAGE_REL_ARM64_REL32,
207                (K::Relative, E::AArch64Call, 26) => coff::IMAGE_REL_ARM64_BRANCH26,
208                _ => return unsupported_reloc(),
209            },
210            _ => {
211                return Err(Error(format!(
212                    "unimplemented architecture {:?}",
213                    self.architecture
214                )));
215            }
216        };
217        reloc.flags = RelocationFlags::Coff { typ };
218        Ok(())
219    }
220
221    pub(crate) fn coff_adjust_addend(&self, relocation: &mut Relocation) -> Result<bool> {
222        let typ = if let RelocationFlags::Coff { typ } = relocation.flags {
223            typ
224        } else {
225            return Err(Error(format!("invalid relocation flags {:?}", relocation)));
226        };
227        let offset = match self.architecture {
228            Architecture::Arm => {
229                if typ == coff::IMAGE_REL_ARM_REL32 {
230                    4
231                } else {
232                    0
233                }
234            }
235            Architecture::Aarch64 => {
236                if typ == coff::IMAGE_REL_ARM64_REL32 {
237                    4
238                } else {
239                    0
240                }
241            }
242            Architecture::I386 => {
243                if typ == coff::IMAGE_REL_I386_REL32 {
244                    4
245                } else {
246                    0
247                }
248            }
249            Architecture::X86_64 => match typ {
250                coff::IMAGE_REL_AMD64_REL32 => 4,
251                coff::IMAGE_REL_AMD64_REL32_1 => 5,
252                coff::IMAGE_REL_AMD64_REL32_2 => 6,
253                coff::IMAGE_REL_AMD64_REL32_3 => 7,
254                coff::IMAGE_REL_AMD64_REL32_4 => 8,
255                coff::IMAGE_REL_AMD64_REL32_5 => 9,
256                _ => 0,
257            },
258            Architecture::PowerPc | Architecture::PowerPc64 => 0,
259            _ => return Err(Error(format!("unimplemented relocation {:?}", relocation))),
260        };
261        relocation.addend += offset;
262        Ok(true)
263    }
264
265    pub(crate) fn coff_relocation_size(&self, reloc: &Relocation) -> Result<u8> {
266        let typ = if let RelocationFlags::Coff { typ } = reloc.flags {
267            typ
268        } else {
269            return Err(Error(format!("unexpected relocation for size {:?}", reloc)));
270        };
271        let size = match self.architecture {
272            Architecture::I386 => match typ {
273                coff::IMAGE_REL_I386_DIR16
274                | coff::IMAGE_REL_I386_REL16
275                | coff::IMAGE_REL_I386_SECTION => Some(16),
276                coff::IMAGE_REL_I386_DIR32
277                | coff::IMAGE_REL_I386_DIR32NB
278                | coff::IMAGE_REL_I386_SECREL
279                | coff::IMAGE_REL_I386_TOKEN
280                | coff::IMAGE_REL_I386_REL32 => Some(32),
281                _ => None,
282            },
283            Architecture::X86_64 => match typ {
284                coff::IMAGE_REL_AMD64_SECTION => Some(16),
285                coff::IMAGE_REL_AMD64_ADDR32
286                | coff::IMAGE_REL_AMD64_ADDR32NB
287                | coff::IMAGE_REL_AMD64_REL32
288                | coff::IMAGE_REL_AMD64_REL32_1
289                | coff::IMAGE_REL_AMD64_REL32_2
290                | coff::IMAGE_REL_AMD64_REL32_3
291                | coff::IMAGE_REL_AMD64_REL32_4
292                | coff::IMAGE_REL_AMD64_REL32_5
293                | coff::IMAGE_REL_AMD64_SECREL
294                | coff::IMAGE_REL_AMD64_TOKEN => Some(32),
295                coff::IMAGE_REL_AMD64_ADDR64 => Some(64),
296                _ => None,
297            },
298            Architecture::Arm => match typ {
299                coff::IMAGE_REL_ARM_SECTION => Some(16),
300                coff::IMAGE_REL_ARM_ADDR32
301                | coff::IMAGE_REL_ARM_ADDR32NB
302                | coff::IMAGE_REL_ARM_TOKEN
303                | coff::IMAGE_REL_ARM_REL32
304                | coff::IMAGE_REL_ARM_SECREL => Some(32),
305                _ => None,
306            },
307            Architecture::Aarch64 => match typ {
308                coff::IMAGE_REL_ARM64_SECTION => Some(16),
309                coff::IMAGE_REL_ARM64_ADDR32
310                | coff::IMAGE_REL_ARM64_ADDR32NB
311                | coff::IMAGE_REL_ARM64_SECREL
312                | coff::IMAGE_REL_ARM64_TOKEN
313                | coff::IMAGE_REL_ARM64_REL32 => Some(32),
314                coff::IMAGE_REL_ARM64_ADDR64 => Some(64),
315                _ => None,
316            },
317            _ => None,
318        };
319        size.ok_or_else(|| Error(format!("unsupported relocation for size {:?}", reloc)))
320    }
321
322    fn coff_add_stub_symbol(&mut self, symbol_id: SymbolId) -> Result<SymbolId> {
323        if let Some(stub_id) = self.stub_symbols.get(&symbol_id) {
324            return Ok(*stub_id);
325        }
326        let stub_size = self.architecture.address_size().unwrap().bytes();
327
328        let name = b".rdata$.refptr".to_vec();
329        let section_id = self.add_section(Vec::new(), name, SectionKind::ReadOnlyData);
330        let section = self.section_mut(section_id);
331        section.set_data(vec![0; stub_size as usize], u64::from(stub_size));
332        self.add_relocation(
333            section_id,
334            Relocation {
335                offset: 0,
336                symbol: symbol_id,
337                addend: 0,
338                flags: RelocationFlags::Generic {
339                    kind: RelocationKind::Absolute,
340                    encoding: RelocationEncoding::Generic,
341                    size: stub_size * 8,
342                },
343            },
344        )?;
345
346        let mut name = b".refptr.".to_vec();
347        name.extend_from_slice(&self.symbol(symbol_id).name);
348        let stub_id = self.add_raw_symbol(Symbol {
349            name,
350            value: 0,
351            size: u64::from(stub_size),
352            kind: SymbolKind::Data,
353            scope: SymbolScope::Compilation,
354            weak: false,
355            section: SymbolSection::Section(section_id),
356            flags: SymbolFlags::None,
357        });
358        self.stub_symbols.insert(symbol_id, stub_id);
359
360        Ok(stub_id)
361    }
362
363    /// Appends linker directives to the `.drectve` section to tell the linker
364    /// to export all symbols with `SymbolScope::Dynamic`.
365    ///
366    /// This must be called after all symbols have been defined.
367    pub fn add_coff_exports(&mut self, style: CoffExportStyle) {
368        assert_eq!(self.format, BinaryFormat::Coff);
369
370        let mut directives = vec![];
371        for symbol in &self.symbols {
372            if symbol.scope == SymbolScope::Dynamic {
373                match style {
374                    CoffExportStyle::Msvc => directives.extend(b" /EXPORT:\""),
375                    CoffExportStyle::Gnu => directives.extend(b" -export:\""),
376                }
377                directives.extend(&symbol.name);
378                directives.extend(b"\"");
379                if symbol.kind != SymbolKind::Text {
380                    match style {
381                        CoffExportStyle::Msvc => directives.extend(b",DATA"),
382                        CoffExportStyle::Gnu => directives.extend(b",data"),
383                    }
384                }
385            }
386        }
387        let drectve = self.add_section(vec![], b".drectve".to_vec(), SectionKind::Linker);
388        self.append_section_data(drectve, &directives, 1);
389    }
390
391    pub(crate) fn coff_write(&self, buffer: &mut dyn WritableBuffer) -> Result<()> {
392        let mut writer = writer::Writer::new(buffer);
393
394        // Add section strings to strtab.
395        let mut section_offsets = vec![SectionOffsets::default(); self.sections.len()];
396        for (index, section) in self.sections.iter().enumerate() {
397            section_offsets[index].name = writer.add_name(&section.name);
398        }
399
400        // Set COMDAT flags.
401        for comdat in &self.comdats {
402            let symbol = &self.symbols[comdat.symbol.0];
403            let comdat_section = match symbol.section {
404                SymbolSection::Section(id) => id.0,
405                _ => {
406                    return Err(Error(format!(
407                        "unsupported COMDAT symbol `{}` section {:?}",
408                        symbol.name().unwrap_or(""),
409                        symbol.section
410                    )));
411                }
412            };
413            section_offsets[comdat_section].selection = match comdat.kind {
414                ComdatKind::NoDuplicates => coff::IMAGE_COMDAT_SELECT_NODUPLICATES,
415                ComdatKind::Any => coff::IMAGE_COMDAT_SELECT_ANY,
416                ComdatKind::SameSize => coff::IMAGE_COMDAT_SELECT_SAME_SIZE,
417                ComdatKind::ExactMatch => coff::IMAGE_COMDAT_SELECT_EXACT_MATCH,
418                ComdatKind::Largest => coff::IMAGE_COMDAT_SELECT_LARGEST,
419                ComdatKind::Newest => coff::IMAGE_COMDAT_SELECT_NEWEST,
420                ComdatKind::Unknown => {
421                    return Err(Error(format!(
422                        "unsupported COMDAT symbol `{}` kind {:?}",
423                        symbol.name().unwrap_or(""),
424                        comdat.kind
425                    )));
426                }
427            };
428            for id in &comdat.sections {
429                let section = &self.sections[id.0];
430                if section.symbol.is_none() {
431                    return Err(Error(format!(
432                        "missing symbol for COMDAT section `{}`",
433                        section.name().unwrap_or(""),
434                    )));
435                }
436                if id.0 != comdat_section {
437                    section_offsets[id.0].selection = coff::IMAGE_COMDAT_SELECT_ASSOCIATIVE;
438                    section_offsets[id.0].associative_section = comdat_section as u32 + 1;
439                }
440            }
441        }
442
443        // Prepare creation of weak default symbols
444        let weak_symbol_count = self.symbols.iter().filter(|symbol| symbol.weak).count();
445        let mut weak_default_names = HashMap::new();
446        let mut weak_default_offsets = HashMap::new();
447
448        if weak_symbol_count > 0 {
449            weak_default_names.reserve(weak_symbol_count);
450            weak_default_offsets.reserve(weak_symbol_count);
451
452            let defined_external_symbol = |symbol: &&Symbol| -> bool {
453                !symbol.weak
454                    && (symbol.scope == SymbolScope::Linkage
455                        || symbol.scope == SymbolScope::Dynamic)
456                    && (matches!(symbol.section, SymbolSection::Section(_))
457                        || matches!(symbol.section, SymbolSection::Absolute))
458            };
459
460            let mut weak_default_unique_name = Default::default();
461
462            // search for an external symbol defined in a non-COMDAT section to
463            // use for the weak default names
464            for symbol in self.symbols.iter().filter(defined_external_symbol) {
465                let SymbolSection::Section(section_id) = symbol.section else {
466                    weak_default_unique_name = &*symbol.name;
467                    break;
468                };
469
470                if !self
471                    .comdats
472                    .iter()
473                    .flat_map(|comdat| comdat.sections.iter())
474                    .any(|comdat_section| *comdat_section == section_id)
475                {
476                    weak_default_unique_name = &*symbol.name;
477                    break;
478                }
479            }
480
481            // fallback to also include COMDAT defined symbols
482            if weak_default_unique_name.is_empty() {
483                for symbol in self.symbols.iter().filter(defined_external_symbol) {
484                    if matches!(symbol.section, SymbolSection::Section(_)) {
485                        weak_default_unique_name = &*symbol.name;
486                        break;
487                    }
488                }
489            }
490
491            // create and store the names for the weak default symbols
492            for (index, symbol) in self
493                .symbols
494                .iter()
495                .enumerate()
496                .filter(|(_, symbol)| symbol.weak)
497            {
498                let mut weak_default_name = [b".weak.", symbol.name.as_slice()].concat();
499                if !weak_default_unique_name.is_empty() {
500                    weak_default_name.push(b'.');
501                    weak_default_name.extend(weak_default_unique_name);
502                }
503
504                weak_default_names.insert(index, weak_default_name);
505            }
506        }
507
508        // Reserve symbol indices and add symbol strings to strtab.
509        let mut symbol_offsets = vec![SymbolOffsets::default(); self.symbols.len()];
510        for (index, symbol) in self.symbols.iter().enumerate() {
511            if symbol.weak {
512                // Reserve the weak default symbol
513                let weak_default_name = weak_default_names.get(&index).unwrap_or_else(|| {
514                    unreachable!("weak default symbol name should have been created")
515                });
516
517                weak_default_offsets.insert(
518                    index,
519                    SymbolOffsets {
520                        name: writer.add_name(weak_default_name.as_slice()),
521                        index: writer.reserve_symbol_index(),
522                        aux_count: 0,
523                    },
524                );
525            }
526
527            symbol_offsets[index].index = writer.reserve_symbol_index();
528            let mut name = &*symbol.name;
529            match symbol.kind {
530                _ if symbol.weak => {
531                    symbol_offsets[index].aux_count = writer.reserve_aux_weak_external();
532                }
533                SymbolKind::File => {
534                    // Name goes in auxiliary symbol records.
535                    symbol_offsets[index].aux_count = writer.reserve_aux_file_name(&symbol.name);
536                    name = b".file";
537                }
538                SymbolKind::Section if symbol.section.id().is_some() => {
539                    symbol_offsets[index].aux_count = writer.reserve_aux_section();
540                }
541                _ => {}
542            };
543            symbol_offsets[index].name = writer.add_name(name);
544        }
545
546        // Reserve file ranges.
547        writer.reserve_file_header();
548        writer.reserve_section_headers(self.sections.len() as u16);
549        for (index, section) in self.sections.iter().enumerate() {
550            section_offsets[index].offset = writer.reserve_section(section.data.len());
551            section_offsets[index].reloc_offset =
552                writer.reserve_relocations(section.relocations.len());
553        }
554        writer.reserve_symtab_strtab();
555
556        // Start writing.
557        writer.write_file_header(writer::FileHeader {
558            machine: match (self.architecture, self.sub_architecture, self.endian) {
559                (Architecture::Arm, None, _) => coff::IMAGE_FILE_MACHINE_ARMNT,
560                (Architecture::Aarch64, None, _) => coff::IMAGE_FILE_MACHINE_ARM64,
561                (Architecture::Aarch64, Some(SubArchitecture::Arm64EC), _) => {
562                    coff::IMAGE_FILE_MACHINE_ARM64EC
563                }
564                (Architecture::I386, None, _) => coff::IMAGE_FILE_MACHINE_I386,
565                (Architecture::X86_64, None, _) => coff::IMAGE_FILE_MACHINE_AMD64,
566                (Architecture::PowerPc | Architecture::PowerPc64, None, Endianness::Little) => {
567                    coff::IMAGE_FILE_MACHINE_POWERPC
568                }
569                (Architecture::PowerPc | Architecture::PowerPc64, None, Endianness::Big) => {
570                    coff::IMAGE_FILE_MACHINE_POWERPCBE
571                }
572                _ => {
573                    return Err(Error(format!(
574                        "unimplemented architecture {:?} with sub-architecture {:?}",
575                        self.architecture, self.sub_architecture
576                    )));
577                }
578            },
579            time_date_stamp: 0,
580            characteristics: match self.flags {
581                FileFlags::Coff { characteristics } => characteristics,
582                _ => 0,
583            },
584        })?;
585
586        // Write section headers.
587        for (index, section) in self.sections.iter().enumerate() {
588            let SectionFlags::Coff {
589                mut characteristics,
590                ..
591            } = self.section_flags(section)
592            else {
593                return Err(Error(format!(
594                    "unimplemented section `{}` kind {:?}",
595                    section.name().unwrap_or(""),
596                    section.kind
597                )));
598            };
599            if section_offsets[index].selection != 0 {
600                characteristics |= coff::IMAGE_SCN_LNK_COMDAT;
601            };
602            if section.relocations.len() > 0xffff {
603                characteristics |= coff::IMAGE_SCN_LNK_NRELOC_OVFL;
604            }
605            characteristics |= match section.align {
606                1 => coff::IMAGE_SCN_ALIGN_1BYTES,
607                2 => coff::IMAGE_SCN_ALIGN_2BYTES,
608                4 => coff::IMAGE_SCN_ALIGN_4BYTES,
609                8 => coff::IMAGE_SCN_ALIGN_8BYTES,
610                16 => coff::IMAGE_SCN_ALIGN_16BYTES,
611                32 => coff::IMAGE_SCN_ALIGN_32BYTES,
612                64 => coff::IMAGE_SCN_ALIGN_64BYTES,
613                128 => coff::IMAGE_SCN_ALIGN_128BYTES,
614                256 => coff::IMAGE_SCN_ALIGN_256BYTES,
615                512 => coff::IMAGE_SCN_ALIGN_512BYTES,
616                1024 => coff::IMAGE_SCN_ALIGN_1024BYTES,
617                2048 => coff::IMAGE_SCN_ALIGN_2048BYTES,
618                4096 => coff::IMAGE_SCN_ALIGN_4096BYTES,
619                8192 => coff::IMAGE_SCN_ALIGN_8192BYTES,
620                _ => {
621                    return Err(Error(format!(
622                        "unimplemented section `{}` align {}",
623                        section.name().unwrap_or(""),
624                        section.align
625                    )));
626                }
627            };
628            writer.write_section_header(writer::SectionHeader {
629                name: section_offsets[index].name,
630                size_of_raw_data: section.size as u32,
631                pointer_to_raw_data: section_offsets[index].offset,
632                pointer_to_relocations: section_offsets[index].reloc_offset,
633                pointer_to_linenumbers: 0,
634                number_of_relocations: section.relocations.len() as u32,
635                number_of_linenumbers: 0,
636                characteristics,
637            });
638        }
639
640        // Write section data and relocations.
641        for section in &self.sections {
642            writer.write_section(&section.data);
643
644            if !section.relocations.is_empty() {
645                //debug_assert_eq!(section_offsets[index].reloc_offset, buffer.len());
646                writer.write_relocations_count(section.relocations.len());
647                for reloc in &section.relocations {
648                    let typ = if let RelocationFlags::Coff { typ } = reloc.flags {
649                        typ
650                    } else {
651                        return Err(Error("invalid relocation flags".into()));
652                    };
653                    writer.write_relocation(writer::Relocation {
654                        virtual_address: reloc.offset as u32,
655                        symbol: symbol_offsets[reloc.symbol.0].index,
656                        typ,
657                    });
658                }
659            }
660        }
661
662        // Write symbols.
663        for (index, symbol) in self.symbols.iter().enumerate() {
664            let SymbolFlags::None = symbol.flags else {
665                return Err(Error(format!(
666                    "unimplemented symbol `{}` kind {:?}",
667                    symbol.name().unwrap_or(""),
668                    symbol.kind
669                )));
670            };
671            let section_number = match symbol.section {
672                // weak symbols are always undefined
673                _ if symbol.weak => coff::IMAGE_SYM_UNDEFINED as u16,
674                SymbolSection::None => {
675                    debug_assert_eq!(symbol.kind, SymbolKind::File);
676                    coff::IMAGE_SYM_DEBUG as u16
677                }
678                SymbolSection::Undefined => coff::IMAGE_SYM_UNDEFINED as u16,
679                SymbolSection::Absolute => coff::IMAGE_SYM_ABSOLUTE as u16,
680                SymbolSection::Common => coff::IMAGE_SYM_UNDEFINED as u16,
681                SymbolSection::Section(id) => id.0 as u16 + 1,
682            };
683            let typ = if symbol.kind == SymbolKind::Text {
684                coff::IMAGE_SYM_DTYPE_FUNCTION << coff::IMAGE_SYM_DTYPE_SHIFT
685            } else {
686                coff::IMAGE_SYM_TYPE_NULL
687            };
688            let storage_class = match symbol.kind {
689                _ if symbol.weak => coff::IMAGE_SYM_CLASS_WEAK_EXTERNAL,
690                SymbolKind::File => coff::IMAGE_SYM_CLASS_FILE,
691                SymbolKind::Section => {
692                    if symbol.section.id().is_some() {
693                        coff::IMAGE_SYM_CLASS_STATIC
694                    } else {
695                        coff::IMAGE_SYM_CLASS_SECTION
696                    }
697                }
698                SymbolKind::Label => coff::IMAGE_SYM_CLASS_LABEL,
699                SymbolKind::Text | SymbolKind::Data | SymbolKind::Tls => match symbol.section {
700                    SymbolSection::None => {
701                        return Err(Error(format!(
702                            "missing section for symbol `{}`",
703                            symbol.name().unwrap_or("")
704                        )));
705                    }
706                    SymbolSection::Undefined | SymbolSection::Common => {
707                        coff::IMAGE_SYM_CLASS_EXTERNAL
708                    }
709                    SymbolSection::Absolute | SymbolSection::Section(_) => match symbol.scope {
710                        SymbolScope::Unknown => {
711                            return Err(Error(format!(
712                                "unimplemented symbol `{}` scope {:?}",
713                                symbol.name().unwrap_or(""),
714                                symbol.scope
715                            )));
716                        }
717                        SymbolScope::Compilation => coff::IMAGE_SYM_CLASS_STATIC,
718                        SymbolScope::Linkage | SymbolScope::Dynamic => {
719                            coff::IMAGE_SYM_CLASS_EXTERNAL
720                        }
721                    },
722                },
723                SymbolKind::Unknown => match symbol.section {
724                    SymbolSection::Undefined => coff::IMAGE_SYM_CLASS_EXTERNAL,
725                    _ => {
726                        return Err(Error(format!(
727                            "unimplemented symbol `{}` kind {:?}",
728                            symbol.name().unwrap_or(""),
729                            symbol.kind
730                        )))
731                    }
732                },
733            };
734            let number_of_aux_symbols = symbol_offsets[index].aux_count;
735            let value = if symbol.weak {
736                // weak symbols should have a value of 0
737                0
738            } else if symbol.section == SymbolSection::Common {
739                symbol.size as u32
740            } else {
741                symbol.value as u32
742            };
743
744            // write the weak default symbol before the weak symbol
745            if symbol.weak {
746                let weak_default_symbol = weak_default_offsets.get(&index).unwrap_or_else(|| {
747                    unreachable!("weak symbol should have a weak default offset")
748                });
749
750                writer.write_symbol(writer::Symbol {
751                    name: weak_default_symbol.name,
752                    value: symbol.value as u32,
753                    section_number: match symbol.section {
754                        SymbolSection::Section(id) => id.0 as u16 + 1,
755                        SymbolSection::Undefined => coff::IMAGE_SYM_ABSOLUTE as u16,
756                        o => {
757                            return Err(Error(format!(
758                                "invalid symbol section for weak external `{}` section {o:?}",
759                                symbol.name().unwrap_or("")
760                            )));
761                        }
762                    },
763                    number_of_aux_symbols: 0,
764                    typ: 0,
765                    storage_class: coff::IMAGE_SYM_CLASS_EXTERNAL,
766                });
767            }
768
769            writer.write_symbol(writer::Symbol {
770                name: symbol_offsets[index].name,
771                value,
772                section_number,
773                typ,
774                storage_class,
775                number_of_aux_symbols,
776            });
777
778            // Write auxiliary symbols.
779            match symbol.kind {
780                _ if symbol.weak => {
781                    let weak_default_offset =
782                        weak_default_offsets.get(&index).unwrap_or_else(|| {
783                            unreachable!("weak symbol should have a weak default offset")
784                        });
785
786                    let weak_default_sym_index = weak_default_offset.index;
787                    writer.write_aux_weak_external(writer::AuxSymbolWeak {
788                        weak_default_sym_index,
789                        weak_search_type: coff::IMAGE_WEAK_EXTERN_SEARCH_ALIAS,
790                    });
791                }
792                SymbolKind::File => {
793                    writer.write_aux_file_name(&symbol.name, number_of_aux_symbols);
794                }
795                SymbolKind::Section if symbol.section.id().is_some() => {
796                    debug_assert_eq!(number_of_aux_symbols, 1);
797                    let section_index = symbol.section.id().unwrap().0;
798                    let section = &self.sections[section_index];
799                    writer.write_aux_section(writer::AuxSymbolSection {
800                        length: section.size as u32,
801                        number_of_relocations: section.relocations.len() as u32,
802                        number_of_linenumbers: 0,
803                        check_sum: if section.is_bss() {
804                            0
805                        } else {
806                            checksum(section.data())
807                        },
808                        number: section_offsets[section_index].associative_section,
809                        selection: section_offsets[section_index].selection,
810                    });
811                }
812                _ => {
813                    debug_assert_eq!(number_of_aux_symbols, 0);
814                }
815            }
816        }
817
818        writer.write_strtab();
819
820        debug_assert_eq!(writer.reserved_len(), writer.len());
821
822        Ok(())
823    }
824}
825
826// JamCRC
827fn checksum(data: &[u8]) -> u32 {
828    let mut hasher = crc32fast::Hasher::new_with_initial(0xffff_ffff);
829    hasher.update(data);
830    !hasher.finalize()
831}