Skip to main content

object/write/coff/
writer.rs

1//! Helper for writing COFF files.
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::mem;
5
6use crate::pe;
7use crate::write::string::{StringId, StringTable};
8use crate::write::util;
9use crate::write::{Error, Result, WritableBuffer};
10
11/// A helper for writing COFF files.
12///
13/// Writing uses a two phase approach. The first phase builds up all of the information
14/// that may need to be known ahead of time:
15/// - build string table
16/// - reserve section indices
17/// - reserve symbol indices
18/// - reserve file ranges for headers and sections
19///
20/// Some of the information has ordering requirements. For example, strings must be added
21/// to the string table before reserving the file range for the string table. There are debug
22/// asserts to check some of these requirements.
23///
24/// The second phase writes everything out in order. Thus the caller must ensure writing
25/// is in the same order that file ranges were reserved. There are debug asserts to assist
26/// with checking this.
27#[allow(missing_debug_implementations)]
28pub struct Writer<'a> {
29    buffer: &'a mut dyn WritableBuffer,
30    len: usize,
31
32    section_num: u16,
33
34    symtab_offset: u32,
35    symtab_num: u32,
36
37    strtab: StringTable<'a>,
38    strtab_len: usize,
39    strtab_offset: u32,
40    strtab_data: Vec<u8>,
41}
42
43impl<'a> Writer<'a> {
44    /// Create a new `Writer`.
45    pub fn new(buffer: &'a mut dyn WritableBuffer) -> Self {
46        Writer {
47            buffer,
48            len: 0,
49
50            section_num: 0,
51
52            symtab_offset: 0,
53            symtab_num: 0,
54
55            strtab: StringTable::default(),
56            strtab_len: 0,
57            strtab_offset: 0,
58            strtab_data: Vec::new(),
59        }
60    }
61
62    /// Return the current file length that has been reserved.
63    pub fn reserved_len(&self) -> usize {
64        self.len
65    }
66
67    /// Return the current file length that has been written.
68    #[allow(clippy::len_without_is_empty)]
69    pub fn len(&self) -> usize {
70        self.buffer.len()
71    }
72
73    /// Reserve a file range with the given size and starting alignment.
74    ///
75    /// Returns the aligned offset of the start of the range.
76    ///
77    /// `align_start` must be a power of two.
78    pub fn reserve(&mut self, len: usize, align_start: usize) -> u32 {
79        if align_start > 1 {
80            self.len = util::align(self.len, align_start);
81        }
82        let offset = self.len;
83        self.len += len;
84        offset as u32
85    }
86
87    /// Write alignment padding bytes.
88    pub fn write_align(&mut self, align_start: usize) {
89        if align_start > 1 {
90            util::write_align(self.buffer, align_start);
91        }
92    }
93
94    /// Write data.
95    pub fn write(&mut self, data: &[u8]) {
96        self.buffer.write_bytes(data);
97    }
98
99    /// Reserve the file range up to the given file offset.
100    pub fn reserve_until(&mut self, offset: usize) {
101        debug_assert!(self.len <= offset);
102        self.len = offset;
103    }
104
105    /// Write padding up to the given file offset.
106    pub fn pad_until(&mut self, offset: usize) {
107        debug_assert!(self.buffer.len() <= offset);
108        self.buffer.resize(offset);
109    }
110
111    /// Reserve the range for the file header.
112    ///
113    /// This must be at the start of the file.
114    pub fn reserve_file_header(&mut self) {
115        debug_assert_eq!(self.len, 0);
116        self.reserve(mem::size_of::<pe::ImageFileHeader>(), 1);
117    }
118
119    /// Write the file header.
120    ///
121    /// This must be at the start of the file.
122    ///
123    /// Fields that can be derived from known information are automatically set by this function.
124    pub fn write_file_header(&mut self, header: FileHeader) -> Result<()> {
125        debug_assert_eq!(self.buffer.len(), 0);
126
127        // Start writing.
128        self.buffer
129            .reserve(self.len)
130            .map_err(|_| Error(String::from("Cannot allocate buffer")))?;
131
132        // Write file header.
133        let header = pe::ImageFileHeader {
134            machine: header.machine.into(),
135            number_of_sections: self.section_num.into(),
136            time_date_stamp: header.time_date_stamp.into(),
137            pointer_to_symbol_table: self.symtab_offset.into(),
138            number_of_symbols: self.symtab_num.into(),
139            size_of_optional_header: 0.into(),
140            characteristics: header.characteristics.into(),
141        };
142        self.buffer.write(&header);
143
144        Ok(())
145    }
146
147    /// Reserve the range for the section headers.
148    pub fn reserve_section_headers(&mut self, section_num: u16) {
149        debug_assert_eq!(self.section_num, 0);
150        self.section_num = section_num;
151        self.reserve(
152            section_num as usize * mem::size_of::<pe::ImageSectionHeader>(),
153            1,
154        );
155    }
156
157    /// Write a section header.
158    pub fn write_section_header(&mut self, section: SectionHeader) {
159        let mut coff_section = pe::ImageSectionHeader {
160            name: [0; 8],
161            virtual_size: 0.into(),
162            virtual_address: 0.into(),
163            size_of_raw_data: section.size_of_raw_data.into(),
164            pointer_to_raw_data: section.pointer_to_raw_data.into(),
165            pointer_to_relocations: section.pointer_to_relocations.into(),
166            pointer_to_linenumbers: section.pointer_to_linenumbers.into(),
167            number_of_relocations: if section.number_of_relocations > 0xffff {
168                0xffff.into()
169            } else {
170                (section.number_of_relocations as u16).into()
171            },
172            number_of_linenumbers: 0.into(),
173            characteristics: section.characteristics.into(),
174        };
175        match section.name {
176            Name::Short(name) => coff_section.name = name,
177            Name::Long(str_id) => {
178                let mut str_offset = self.strtab.get_offset(str_id);
179                if str_offset <= 9_999_999 {
180                    let mut name = [0; 7];
181                    let mut len = 0;
182                    if str_offset == 0 {
183                        name[6] = b'0';
184                        len = 1;
185                    } else {
186                        while str_offset != 0 {
187                            let rem = (str_offset % 10) as u8;
188                            str_offset /= 10;
189                            name[6 - len] = b'0' + rem;
190                            len += 1;
191                        }
192                    }
193                    coff_section.name = [0; 8];
194                    coff_section.name[0] = b'/';
195                    coff_section.name[1..][..len].copy_from_slice(&name[7 - len..]);
196                } else {
197                    debug_assert!(str_offset as u64 <= 0xf_ffff_ffff);
198                    coff_section.name[0] = b'/';
199                    coff_section.name[1] = b'/';
200                    for i in 0..6 {
201                        let rem = (str_offset % 64) as u8;
202                        str_offset /= 64;
203                        let c = match rem {
204                            0..=25 => b'A' + rem,
205                            26..=51 => b'a' + rem - 26,
206                            52..=61 => b'0' + rem - 52,
207                            62 => b'+',
208                            63 => b'/',
209                            _ => unreachable!(),
210                        };
211                        coff_section.name[7 - i] = c;
212                    }
213                }
214            }
215        }
216        self.buffer.write(&coff_section);
217    }
218
219    /// Reserve the range for the section data.
220    ///
221    /// Returns the aligned offset of the start of the range.
222    /// Does nothing and returns 0 if the length is zero.
223    pub fn reserve_section(&mut self, len: usize) -> u32 {
224        if len == 0 {
225            return 0;
226        }
227        // TODO: not sure what alignment is required here, but this seems to match LLVM
228        self.reserve(len, 4)
229    }
230
231    /// Write the alignment bytes prior to section data.
232    ///
233    /// This is unneeded if you are using `write_section` or `write_section_zeroes`
234    /// for the data.
235    pub fn write_section_align(&mut self) {
236        util::write_align(self.buffer, 4);
237    }
238
239    /// Write the section data.
240    ///
241    /// Writes alignment bytes prior to the data.
242    /// Does nothing if the data is empty.
243    pub fn write_section(&mut self, data: &[u8]) {
244        if data.is_empty() {
245            return;
246        }
247        self.write_section_align();
248        self.buffer.write_bytes(data);
249    }
250
251    /// Write the section data using zero bytes.
252    ///
253    /// Writes alignment bytes prior to the data.
254    /// Does nothing if the length is zero.
255    pub fn write_section_zeroes(&mut self, len: usize) {
256        if len == 0 {
257            return;
258        }
259        self.write_section_align();
260        self.buffer.resize(self.buffer.len() + len);
261    }
262
263    /// Reserve a file range for the given number of relocations.
264    ///
265    /// This will automatically reserve an extra relocation if there are more than 0xffff.
266    ///
267    /// Returns the offset of the range.
268    /// Does nothing and returns 0 if the count is zero.
269    pub fn reserve_relocations(&mut self, mut count: usize) -> u32 {
270        if count == 0 {
271            return 0;
272        }
273        if count > 0xffff {
274            count += 1;
275        }
276        self.reserve(count * mem::size_of::<pe::ImageRelocation>(), 1)
277    }
278
279    /// Write a relocation containing the count if required.
280    ///
281    /// This should be called before writing the first relocation for a section.
282    pub fn write_relocations_count(&mut self, count: usize) {
283        if count > 0xffff {
284            let coff_relocation = pe::ImageRelocation {
285                virtual_address: (count as u32 + 1).into(),
286                symbol_table_index: 0.into(),
287                typ: 0.into(),
288            };
289            self.buffer.write(&coff_relocation);
290        }
291    }
292
293    /// Write a relocation.
294    pub fn write_relocation(&mut self, reloc: Relocation) {
295        let coff_relocation = pe::ImageRelocation {
296            virtual_address: reloc.virtual_address.into(),
297            symbol_table_index: reloc.symbol.into(),
298            typ: reloc.typ.into(),
299        };
300        self.buffer.write(&coff_relocation);
301    }
302
303    /// Reserve a symbol table entry.
304    ///
305    /// This must be called before [`Self::reserve_symtab_strtab`].
306    pub fn reserve_symbol_index(&mut self) -> u32 {
307        debug_assert_eq!(self.symtab_offset, 0);
308        let index = self.symtab_num;
309        self.symtab_num += 1;
310        index
311    }
312
313    /// Reserve a number of symbol table entries.
314    pub fn reserve_symbol_indices(&mut self, count: u32) {
315        debug_assert_eq!(self.symtab_offset, 0);
316        self.symtab_num += count;
317    }
318
319    /// Write a symbol table entry.
320    pub fn write_symbol(&mut self, symbol: Symbol) {
321        let mut coff_symbol = pe::ImageSymbol {
322            name: [0; 8],
323            value: symbol.value.into(),
324            section_number: symbol.section_number.into(),
325            typ: symbol.typ.into(),
326            storage_class: symbol.storage_class,
327            number_of_aux_symbols: symbol.number_of_aux_symbols,
328        };
329        match symbol.name {
330            Name::Short(name) => coff_symbol.name = name,
331            Name::Long(str_id) => {
332                let str_offset = self.strtab.get_offset(str_id);
333                coff_symbol.name[4..8].copy_from_slice(&u32::to_le_bytes(str_offset as u32));
334            }
335        }
336        self.buffer.write(&coff_symbol);
337    }
338
339    /// Reserve auxiliary symbols for a file name.
340    ///
341    /// Returns the number of auxiliary symbols required.
342    ///
343    /// This must be called before [`Self::reserve_symtab_strtab`].
344    pub fn reserve_aux_file_name(&mut self, name: &[u8]) -> u8 {
345        debug_assert_eq!(self.symtab_offset, 0);
346        let aux_count = (name.len() + pe::IMAGE_SIZEOF_SYMBOL - 1) / pe::IMAGE_SIZEOF_SYMBOL;
347        self.symtab_num += aux_count as u32;
348        aux_count as u8
349    }
350
351    /// Write auxiliary symbols for a file name.
352    pub fn write_aux_file_name(&mut self, name: &[u8], aux_count: u8) {
353        let aux_len = aux_count as usize * pe::IMAGE_SIZEOF_SYMBOL;
354        debug_assert!(aux_len >= name.len());
355        let old_len = self.buffer.len();
356        self.buffer.write_bytes(name);
357        self.buffer.resize(old_len + aux_len);
358    }
359
360    /// Reserve an auxiliary symbol for a section.
361    ///
362    /// Returns the number of auxiliary symbols required.
363    ///
364    /// This must be called before [`Self::reserve_symtab_strtab`].
365    pub fn reserve_aux_section(&mut self) -> u8 {
366        debug_assert_eq!(self.symtab_offset, 0);
367        self.symtab_num += 1;
368        1
369    }
370
371    /// Write an auxiliary symbol for a section.
372    pub fn write_aux_section(&mut self, section: AuxSymbolSection) {
373        let aux = pe::ImageAuxSymbolSection {
374            length: section.length.into(),
375            number_of_relocations: if section.number_of_relocations > 0xffff {
376                0xffff.into()
377            } else {
378                (section.number_of_relocations as u16).into()
379            },
380            number_of_linenumbers: section.number_of_linenumbers.into(),
381            check_sum: section.check_sum.into(),
382            number: (section.number as u16).into(),
383            selection: section.selection,
384            reserved: 0,
385            high_number: ((section.number >> 16) as u16).into(),
386        };
387        self.buffer.write(&aux);
388    }
389
390    /// Reserve an auxiliary symbol for a weak external.
391    ///
392    /// Returns the number of auxiliary symbols required.
393    ///
394    /// This must be called before [`Self::reserve_symtab_strtab`].
395    pub fn reserve_aux_weak_external(&mut self) -> u8 {
396        debug_assert_eq!(self.symtab_offset, 0);
397        self.symtab_num += 1;
398        1
399    }
400
401    /// Write an auxiliary symbol for a weak external.
402    pub fn write_aux_weak_external(&mut self, weak: AuxSymbolWeak) {
403        let aux = pe::ImageAuxSymbolWeak {
404            weak_default_sym_index: weak.weak_default_sym_index.into(),
405            weak_search_type: weak.weak_search_type.into(),
406        };
407        self.buffer.write(&aux);
408        // write padding for the unused field
409        const PAD_LEN: usize = pe::IMAGE_SIZEOF_SYMBOL - mem::size_of::<pe::ImageAuxSymbolWeak>();
410        self.buffer.write_bytes(&[0u8; PAD_LEN]);
411    }
412
413    /// Return the number of reserved symbol table entries.
414    pub fn symbol_count(&self) -> u32 {
415        self.symtab_num
416    }
417
418    /// Add a string to the string table.
419    ///
420    /// This must be called before [`Self::reserve_symtab_strtab`].
421    pub fn add_string(&mut self, name: &'a [u8]) -> StringId {
422        debug_assert_eq!(self.strtab_offset, 0);
423        self.strtab.add(name)
424    }
425
426    /// Add a section or symbol name to the string table if required.
427    ///
428    /// This must be called before [`Self::reserve_symtab_strtab`].
429    pub fn add_name(&mut self, name: &'a [u8]) -> Name {
430        if name.len() > 8 {
431            Name::Long(self.add_string(name))
432        } else {
433            let mut short_name = [0; 8];
434            short_name[..name.len()].copy_from_slice(name);
435            Name::Short(short_name)
436        }
437    }
438
439    /// Reserve the range for the symbol table and string table.
440    ///
441    /// This must be called after functions that reserve symbol
442    /// indices or add strings.
443    pub fn reserve_symtab_strtab(&mut self) {
444        debug_assert_eq!(self.symtab_offset, 0);
445        self.symtab_offset = self.reserve(self.symtab_num as usize * pe::IMAGE_SIZEOF_SYMBOL, 1);
446
447        debug_assert_eq!(self.strtab_offset, 0);
448        // First 4 bytes of strtab are the length.
449        self.strtab.write(4, &mut self.strtab_data);
450        self.strtab_len = self.strtab_data.len() + 4;
451        self.strtab_offset = self.reserve(self.strtab_len, 1);
452    }
453
454    /// Write the string table.
455    pub fn write_strtab(&mut self) {
456        debug_assert_eq!(self.strtab_offset, self.buffer.len() as u32);
457        self.buffer
458            .write_bytes(&u32::to_le_bytes(self.strtab_len as u32));
459        self.buffer.write_bytes(&self.strtab_data);
460    }
461}
462
463/// Shortened and native endian version of [`pe::ImageFileHeader`].
464#[allow(missing_docs)]
465#[derive(Debug, Default, Clone)]
466pub struct FileHeader {
467    pub machine: u16,
468    pub time_date_stamp: u32,
469    pub characteristics: u16,
470}
471
472/// A section or symbol name.
473#[derive(Debug, Clone, Copy)]
474pub enum Name {
475    /// An inline name.
476    Short([u8; 8]),
477    /// An id of a string table entry.
478    Long(StringId),
479}
480
481impl Default for Name {
482    fn default() -> Name {
483        Name::Short([0; 8])
484    }
485}
486
487// From isn't useful.
488#[allow(clippy::from_over_into)]
489impl<'a> Into<Name> for &'a [u8; 8] {
490    fn into(self) -> Name {
491        Name::Short(*self)
492    }
493}
494
495/// Native endian version of [`pe::ImageSectionHeader`].
496#[allow(missing_docs)]
497#[derive(Debug, Default, Clone)]
498pub struct SectionHeader {
499    pub name: Name,
500    pub size_of_raw_data: u32,
501    pub pointer_to_raw_data: u32,
502    pub pointer_to_relocations: u32,
503    pub pointer_to_linenumbers: u32,
504    /// This will automatically be clamped if there are more than 0xffff.
505    pub number_of_relocations: u32,
506    pub number_of_linenumbers: u16,
507    pub characteristics: u32,
508}
509
510/// Native endian version of [`pe::ImageSymbol`].
511#[allow(missing_docs)]
512#[derive(Debug, Default, Clone)]
513pub struct Symbol {
514    pub name: Name,
515    pub value: u32,
516    pub section_number: u16,
517    pub typ: u16,
518    pub storage_class: u8,
519    pub number_of_aux_symbols: u8,
520}
521
522/// Native endian version of [`pe::ImageAuxSymbolSection`].
523#[allow(missing_docs)]
524#[derive(Debug, Default, Clone)]
525pub struct AuxSymbolSection {
526    pub length: u32,
527    /// This will automatically be clamped if there are more than 0xffff.
528    pub number_of_relocations: u32,
529    pub number_of_linenumbers: u16,
530    pub check_sum: u32,
531    pub number: u32,
532    pub selection: u8,
533}
534
535/// Native endian version of [`pe::ImageAuxSymbolWeak`].
536#[allow(missing_docs)]
537#[derive(Debug, Default, Clone)]
538pub struct AuxSymbolWeak {
539    pub weak_default_sym_index: u32,
540    pub weak_search_type: u32,
541}
542
543/// Native endian version of [`pe::ImageRelocation`].
544#[allow(missing_docs)]
545#[derive(Debug, Default, Clone)]
546pub struct Relocation {
547    pub virtual_address: u32,
548    pub symbol: u32,
549    pub typ: u16,
550}