Skip to main content

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