object/read/coff/
file.rs

1use alloc::vec::Vec;
2use core::fmt::Debug;
3
4use crate::endian::LittleEndian as LE;
5use crate::pe;
6use crate::pod::Pod;
7use crate::read::{
8    self, Architecture, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind,
9    ObjectSection, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex,
10};
11
12use super::{
13    CoffComdat, CoffComdatIterator, CoffSection, CoffSectionIterator, CoffSegment,
14    CoffSegmentIterator, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, ImageSymbol,
15    SectionTable, SymbolTable,
16};
17
18/// The common parts of `PeFile` and `CoffFile`.
19#[derive(Debug)]
20pub(crate) struct CoffCommon<'data, R: ReadRef<'data>, Coff: CoffHeader = pe::ImageFileHeader> {
21    pub(crate) sections: SectionTable<'data>,
22    pub(crate) symbols: SymbolTable<'data, R, Coff>,
23    pub(crate) image_base: u64,
24}
25
26/// A COFF bigobj object file with 32-bit section numbers.
27///
28/// This is a file that starts with [`pe::AnonObjectHeaderBigobj`], and corresponds
29/// to [`crate::FileKind::CoffBig`].
30///
31/// Most functionality is provided by the [`Object`] trait implementation.
32pub type CoffBigFile<'data, R = &'data [u8]> = CoffFile<'data, R, pe::AnonObjectHeaderBigobj>;
33
34/// A COFF object file.
35///
36/// This is a file that starts with [`pe::ImageFileHeader`], and corresponds
37/// to [`crate::FileKind::Coff`].
38///
39/// Most functionality is provided by the [`Object`] trait implementation.
40#[derive(Debug)]
41pub struct CoffFile<'data, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader>
42{
43    pub(super) header: &'data Coff,
44    pub(super) common: CoffCommon<'data, R, Coff>,
45    pub(super) data: R,
46}
47
48impl<'data, R: ReadRef<'data>, Coff: CoffHeader> CoffFile<'data, R, Coff> {
49    /// Parse the raw COFF file data.
50    pub fn parse(data: R) -> Result<Self> {
51        let mut offset = 0;
52        let header = Coff::parse(data, &mut offset)?;
53        let sections = header.sections(data, offset)?;
54        let symbols = header.symbols(data)?;
55
56        Ok(CoffFile {
57            header,
58            common: CoffCommon {
59                sections,
60                symbols,
61                image_base: 0,
62            },
63            data,
64        })
65    }
66
67    /// Get the raw COFF file header.
68    pub fn coff_header(&self) -> &'data Coff {
69        self.header
70    }
71
72    /// Get the COFF section table.
73    pub fn coff_section_table(&self) -> SectionTable<'data> {
74        self.common.sections
75    }
76
77    /// Get the COFF symbol table.
78    pub fn coff_symbol_table(&self) -> &SymbolTable<'data, R, Coff> {
79        &self.common.symbols
80    }
81}
82
83impl<'data, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed
84    for CoffFile<'data, R, Coff>
85{
86}
87
88impl<'data, R, Coff> Object<'data> for CoffFile<'data, R, Coff>
89where
90    R: ReadRef<'data>,
91    Coff: CoffHeader,
92{
93    type Segment<'file> = CoffSegment<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
94    type SegmentIterator<'file> = CoffSegmentIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
95    type Section<'file> = CoffSection<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
96    type SectionIterator<'file> = CoffSectionIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
97    type Comdat<'file> = CoffComdat<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
98    type ComdatIterator<'file> = CoffComdatIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
99    type Symbol<'file> = CoffSymbol<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
100    type SymbolIterator<'file> = CoffSymbolIterator<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
101    type SymbolTable<'file> = CoffSymbolTable<'data, 'file, R, Coff> where Self: 'file, 'data: 'file;
102    type DynamicRelocationIterator<'file> = NoDynamicRelocationIterator where Self: 'file, 'data: 'file;
103
104    fn architecture(&self) -> Architecture {
105        match self.header.machine() {
106            pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm,
107            pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64,
108            pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386,
109            pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64,
110            _ => Architecture::Unknown,
111        }
112    }
113
114    fn sub_architecture(&self) -> Option<SubArchitecture> {
115        match self.header.machine() {
116            pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC),
117            _ => None,
118        }
119    }
120
121    #[inline]
122    fn is_little_endian(&self) -> bool {
123        true
124    }
125
126    #[inline]
127    fn is_64(&self) -> bool {
128        // Windows COFF is always 32-bit, even for 64-bit architectures. This could be confusing.
129        false
130    }
131
132    fn kind(&self) -> ObjectKind {
133        ObjectKind::Relocatable
134    }
135
136    fn segments(&self) -> CoffSegmentIterator<'data, '_, R, Coff> {
137        CoffSegmentIterator {
138            file: self,
139            iter: self.common.sections.iter(),
140        }
141    }
142
143    fn section_by_name_bytes<'file>(
144        &'file self,
145        section_name: &[u8],
146    ) -> Option<CoffSection<'data, 'file, R, Coff>> {
147        self.sections()
148            .find(|section| section.name_bytes() == Ok(section_name))
149    }
150
151    fn section_by_index(&self, index: SectionIndex) -> Result<CoffSection<'data, '_, R, Coff>> {
152        let section = self.common.sections.section(index)?;
153        Ok(CoffSection {
154            file: self,
155            index,
156            section,
157        })
158    }
159
160    fn sections(&self) -> CoffSectionIterator<'data, '_, R, Coff> {
161        CoffSectionIterator {
162            file: self,
163            iter: self.common.sections.iter().enumerate(),
164        }
165    }
166
167    fn comdats(&self) -> CoffComdatIterator<'data, '_, R, Coff> {
168        CoffComdatIterator::new(self)
169    }
170
171    fn symbol_by_index(&self, index: SymbolIndex) -> Result<CoffSymbol<'data, '_, R, Coff>> {
172        let symbol = self.common.symbols.symbol(index)?;
173        Ok(CoffSymbol {
174            file: &self.common,
175            index,
176            symbol,
177        })
178    }
179
180    fn symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> {
181        CoffSymbolIterator::new(&self.common)
182    }
183
184    #[inline]
185    fn symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> {
186        Some(CoffSymbolTable { file: &self.common })
187    }
188
189    fn dynamic_symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> {
190        CoffSymbolIterator::empty(&self.common)
191    }
192
193    #[inline]
194    fn dynamic_symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> {
195        None
196    }
197
198    #[inline]
199    fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
200        None
201    }
202
203    #[inline]
204    fn imports(&self) -> Result<Vec<Import<'data>>> {
205        // TODO: this could return undefined symbols, but not needed yet.
206        Ok(Vec::new())
207    }
208
209    #[inline]
210    fn exports(&self) -> Result<Vec<Export<'data>>> {
211        // TODO: this could return global symbols, but not needed yet.
212        Ok(Vec::new())
213    }
214
215    fn has_debug_symbols(&self) -> bool {
216        self.section_by_name(".debug_info").is_some()
217    }
218
219    fn relative_address_base(&self) -> u64 {
220        0
221    }
222
223    #[inline]
224    fn entry(&self) -> u64 {
225        0
226    }
227
228    fn flags(&self) -> FileFlags {
229        FileFlags::Coff {
230            characteristics: self.header.characteristics(),
231        }
232    }
233}
234
235/// Read the `class_id` field from a [`pe::AnonObjectHeader`].
236///
237/// This can be used to determine the format of the header.
238pub fn anon_object_class_id<'data, R: ReadRef<'data>>(data: R) -> Result<pe::ClsId> {
239    let header = data
240        .read_at::<pe::AnonObjectHeader>(0)
241        .read_error("Invalid anon object header size or alignment")?;
242    Ok(header.class_id)
243}
244
245/// A trait for generic access to [`pe::ImageFileHeader`] and [`pe::AnonObjectHeaderBigobj`].
246#[allow(missing_docs)]
247pub trait CoffHeader: Debug + Pod {
248    type ImageSymbol: ImageSymbol;
249    type ImageSymbolBytes: Debug + Pod;
250
251    /// Return true if this type is [`pe::AnonObjectHeaderBigobj`].
252    ///
253    /// This is a property of the type, not a value in the header data.
254    fn is_type_bigobj() -> bool;
255
256    fn machine(&self) -> u16;
257    fn number_of_sections(&self) -> u32;
258    fn pointer_to_symbol_table(&self) -> u32;
259    fn number_of_symbols(&self) -> u32;
260    fn characteristics(&self) -> u16;
261
262    /// Read the file header.
263    ///
264    /// `data` must be the entire file data.
265    /// `offset` must be the file header offset. It is updated to point after the optional header,
266    /// which is where the section headers are located.
267    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self>;
268
269    /// Read the section table.
270    ///
271    /// `data` must be the entire file data.
272    /// `offset` must be after the optional file header.
273    #[inline]
274    fn sections<'data, R: ReadRef<'data>>(
275        &self,
276        data: R,
277        offset: u64,
278    ) -> read::Result<SectionTable<'data>> {
279        SectionTable::parse(self, data, offset)
280    }
281
282    /// Read the symbol table and string table.
283    ///
284    /// `data` must be the entire file data.
285    #[inline]
286    fn symbols<'data, R: ReadRef<'data>>(
287        &self,
288        data: R,
289    ) -> read::Result<SymbolTable<'data, R, Self>> {
290        SymbolTable::parse(self, data)
291    }
292}
293
294impl CoffHeader for pe::ImageFileHeader {
295    type ImageSymbol = pe::ImageSymbol;
296    type ImageSymbolBytes = pe::ImageSymbolBytes;
297
298    fn is_type_bigobj() -> bool {
299        false
300    }
301
302    fn machine(&self) -> u16 {
303        self.machine.get(LE)
304    }
305
306    fn number_of_sections(&self) -> u32 {
307        self.number_of_sections.get(LE).into()
308    }
309
310    fn pointer_to_symbol_table(&self) -> u32 {
311        self.pointer_to_symbol_table.get(LE)
312    }
313
314    fn number_of_symbols(&self) -> u32 {
315        self.number_of_symbols.get(LE)
316    }
317
318    fn characteristics(&self) -> u16 {
319        self.characteristics.get(LE)
320    }
321
322    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> {
323        let header = data
324            .read::<pe::ImageFileHeader>(offset)
325            .read_error("Invalid COFF file header size or alignment")?;
326
327        // Skip over the optional header.
328        *offset = offset
329            .checked_add(header.size_of_optional_header.get(LE).into())
330            .read_error("Invalid COFF optional header size")?;
331
332        // TODO: maybe validate that the machine is known?
333        Ok(header)
334    }
335}
336
337impl CoffHeader for pe::AnonObjectHeaderBigobj {
338    type ImageSymbol = pe::ImageSymbolEx;
339    type ImageSymbolBytes = pe::ImageSymbolExBytes;
340
341    fn is_type_bigobj() -> bool {
342        true
343    }
344
345    fn machine(&self) -> u16 {
346        self.machine.get(LE)
347    }
348
349    fn number_of_sections(&self) -> u32 {
350        self.number_of_sections.get(LE)
351    }
352
353    fn pointer_to_symbol_table(&self) -> u32 {
354        self.pointer_to_symbol_table.get(LE)
355    }
356
357    fn number_of_symbols(&self) -> u32 {
358        self.number_of_symbols.get(LE)
359    }
360
361    fn characteristics(&self) -> u16 {
362        0
363    }
364
365    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> {
366        let header = data
367            .read::<pe::AnonObjectHeaderBigobj>(offset)
368            .read_error("Invalid COFF bigobj file header size or alignment")?;
369
370        if header.sig1.get(LE) != pe::IMAGE_FILE_MACHINE_UNKNOWN
371            || header.sig2.get(LE) != 0xffff
372            || header.version.get(LE) < 2
373            || header.class_id != pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID
374        {
375            return Err(read::Error("Invalid COFF bigobj header values"));
376        }
377
378        // TODO: maybe validate that the machine is known?
379        Ok(header)
380    }
381}