object/write/coff/
object.rs

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