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>
94        = CoffSegment<'data, 'file, R, Coff>
95    where
96        Self: 'file,
97        'data: 'file;
98    type SegmentIterator<'file>
99        = CoffSegmentIterator<'data, 'file, R, Coff>
100    where
101        Self: 'file,
102        'data: 'file;
103    type Section<'file>
104        = CoffSection<'data, 'file, R, Coff>
105    where
106        Self: 'file,
107        'data: 'file;
108    type SectionIterator<'file>
109        = CoffSectionIterator<'data, 'file, R, Coff>
110    where
111        Self: 'file,
112        'data: 'file;
113    type Comdat<'file>
114        = CoffComdat<'data, 'file, R, Coff>
115    where
116        Self: 'file,
117        'data: 'file;
118    type ComdatIterator<'file>
119        = CoffComdatIterator<'data, 'file, R, Coff>
120    where
121        Self: 'file,
122        'data: 'file;
123    type Symbol<'file>
124        = CoffSymbol<'data, 'file, R, Coff>
125    where
126        Self: 'file,
127        'data: 'file;
128    type SymbolIterator<'file>
129        = CoffSymbolIterator<'data, 'file, R, Coff>
130    where
131        Self: 'file,
132        'data: 'file;
133    type SymbolTable<'file>
134        = CoffSymbolTable<'data, 'file, R, Coff>
135    where
136        Self: 'file,
137        'data: 'file;
138    type DynamicRelocationIterator<'file>
139        = NoDynamicRelocationIterator
140    where
141        Self: 'file,
142        'data: 'file;
143
144    fn architecture(&self) -> Architecture {
145        match self.header.machine() {
146            pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm,
147            pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64,
148            pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386,
149            pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64,
150            pe::IMAGE_FILE_MACHINE_POWERPC
151            | pe::IMAGE_FILE_MACHINE_POWERPCFP
152            | pe::IMAGE_FILE_MACHINE_POWERPCBE => Architecture::PowerPc,
153            _ => Architecture::Unknown,
154        }
155    }
156
157    fn sub_architecture(&self) -> Option<SubArchitecture> {
158        match self.header.machine() {
159            pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC),
160            _ => None,
161        }
162    }
163
164    #[inline]
165    fn is_little_endian(&self) -> bool {
166        match self.header.machine() {
167            pe::IMAGE_FILE_MACHINE_POWERPCBE => false,
168            _ => true,
169        }
170    }
171
172    #[inline]
173    fn is_64(&self) -> bool {
174        // Windows COFF is always 32-bit, even for 64-bit architectures. This could be confusing.
175        false
176    }
177
178    fn kind(&self) -> ObjectKind {
179        ObjectKind::Relocatable
180    }
181
182    fn segments(&self) -> CoffSegmentIterator<'data, '_, R, Coff> {
183        CoffSegmentIterator {
184            file: self,
185            iter: self.common.sections.iter(),
186        }
187    }
188
189    fn section_by_name_bytes<'file>(
190        &'file self,
191        section_name: &[u8],
192    ) -> Option<CoffSection<'data, 'file, R, Coff>> {
193        self.sections()
194            .find(|section| section.name_bytes() == Ok(section_name))
195    }
196
197    fn section_by_index(&self, index: SectionIndex) -> Result<CoffSection<'data, '_, R, Coff>> {
198        let section = self.common.sections.section(index)?;
199        Ok(CoffSection {
200            file: self,
201            index,
202            section,
203        })
204    }
205
206    fn sections(&self) -> CoffSectionIterator<'data, '_, R, Coff> {
207        CoffSectionIterator {
208            file: self,
209            iter: self.common.sections.iter().enumerate(),
210        }
211    }
212
213    fn comdats(&self) -> CoffComdatIterator<'data, '_, R, Coff> {
214        CoffComdatIterator::new(self)
215    }
216
217    fn symbol_by_index(&self, index: SymbolIndex) -> Result<CoffSymbol<'data, '_, R, Coff>> {
218        let symbol = self.common.symbols.symbol(index)?;
219        Ok(CoffSymbol {
220            file: &self.common,
221            index,
222            symbol,
223        })
224    }
225
226    fn symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> {
227        CoffSymbolIterator::new(&self.common)
228    }
229
230    #[inline]
231    fn symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> {
232        Some(CoffSymbolTable { file: &self.common })
233    }
234
235    fn dynamic_symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> {
236        CoffSymbolIterator::empty(&self.common)
237    }
238
239    #[inline]
240    fn dynamic_symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> {
241        None
242    }
243
244    #[inline]
245    fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
246        None
247    }
248
249    #[inline]
250    fn imports(&self) -> Result<Vec<Import<'data>>> {
251        // TODO: this could return undefined symbols, but not needed yet.
252        Ok(Vec::new())
253    }
254
255    #[inline]
256    fn exports(&self) -> Result<Vec<Export<'data>>> {
257        // TODO: this could return global symbols, but not needed yet.
258        Ok(Vec::new())
259    }
260
261    fn has_debug_symbols(&self) -> bool {
262        self.section_by_name(".debug_info").is_some()
263    }
264
265    fn relative_address_base(&self) -> u64 {
266        0
267    }
268
269    #[inline]
270    fn entry(&self) -> u64 {
271        0
272    }
273
274    fn flags(&self) -> FileFlags {
275        FileFlags::Coff {
276            characteristics: self.header.characteristics(),
277        }
278    }
279}
280
281/// Read the `class_id` field from a [`pe::AnonObjectHeader`].
282///
283/// This can be used to determine the format of the header.
284pub fn anon_object_class_id<'data, R: ReadRef<'data>>(data: R) -> Result<pe::ClsId> {
285    let header = data
286        .read_at::<pe::AnonObjectHeader>(0)
287        .read_error("Invalid anon object header size or alignment")?;
288    Ok(header.class_id)
289}
290
291/// A trait for generic access to [`pe::ImageFileHeader`] and [`pe::AnonObjectHeaderBigobj`].
292#[allow(missing_docs)]
293pub trait CoffHeader: Debug + Pod {
294    type ImageSymbol: ImageSymbol;
295    type ImageSymbolBytes: Debug + Pod;
296
297    /// Return true if this type is [`pe::AnonObjectHeaderBigobj`].
298    ///
299    /// This is a property of the type, not a value in the header data.
300    fn is_type_bigobj() -> bool;
301
302    fn machine(&self) -> u16;
303    fn number_of_sections(&self) -> u32;
304    fn pointer_to_symbol_table(&self) -> u32;
305    fn number_of_symbols(&self) -> u32;
306    fn characteristics(&self) -> u16;
307
308    /// Read the file header.
309    ///
310    /// `data` must be the entire file data.
311    /// `offset` must be the file header offset. It is updated to point after the optional header,
312    /// which is where the section headers are located.
313    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self>;
314
315    /// Read the section table.
316    ///
317    /// `data` must be the entire file data.
318    /// `offset` must be after the optional file header.
319    #[inline]
320    fn sections<'data, R: ReadRef<'data>>(
321        &self,
322        data: R,
323        offset: u64,
324    ) -> read::Result<SectionTable<'data>> {
325        SectionTable::parse(self, data, offset)
326    }
327
328    /// Read the symbol table and string table.
329    ///
330    /// `data` must be the entire file data.
331    #[inline]
332    fn symbols<'data, R: ReadRef<'data>>(
333        &self,
334        data: R,
335    ) -> read::Result<SymbolTable<'data, R, Self>> {
336        SymbolTable::parse(self, data)
337    }
338}
339
340impl CoffHeader for pe::ImageFileHeader {
341    type ImageSymbol = pe::ImageSymbol;
342    type ImageSymbolBytes = pe::ImageSymbolBytes;
343
344    fn is_type_bigobj() -> bool {
345        false
346    }
347
348    fn machine(&self) -> u16 {
349        self.machine.get(LE)
350    }
351
352    fn number_of_sections(&self) -> u32 {
353        self.number_of_sections.get(LE).into()
354    }
355
356    fn pointer_to_symbol_table(&self) -> u32 {
357        self.pointer_to_symbol_table.get(LE)
358    }
359
360    fn number_of_symbols(&self) -> u32 {
361        self.number_of_symbols.get(LE)
362    }
363
364    fn characteristics(&self) -> u16 {
365        self.characteristics.get(LE)
366    }
367
368    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> {
369        let header = data
370            .read::<pe::ImageFileHeader>(offset)
371            .read_error("Invalid COFF file header size or alignment")?;
372
373        // Skip over the optional header.
374        *offset = offset
375            .checked_add(header.size_of_optional_header.get(LE).into())
376            .read_error("Invalid COFF optional header size")?;
377
378        // TODO: maybe validate that the machine is known?
379        Ok(header)
380    }
381}
382
383impl CoffHeader for pe::AnonObjectHeaderBigobj {
384    type ImageSymbol = pe::ImageSymbolEx;
385    type ImageSymbolBytes = pe::ImageSymbolExBytes;
386
387    fn is_type_bigobj() -> bool {
388        true
389    }
390
391    fn machine(&self) -> u16 {
392        self.machine.get(LE)
393    }
394
395    fn number_of_sections(&self) -> u32 {
396        self.number_of_sections.get(LE)
397    }
398
399    fn pointer_to_symbol_table(&self) -> u32 {
400        self.pointer_to_symbol_table.get(LE)
401    }
402
403    fn number_of_symbols(&self) -> u32 {
404        self.number_of_symbols.get(LE)
405    }
406
407    fn characteristics(&self) -> u16 {
408        0
409    }
410
411    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> {
412        let header = data
413            .read::<pe::AnonObjectHeaderBigobj>(offset)
414            .read_error("Invalid COFF bigobj file header size or alignment")?;
415
416        if header.sig1.get(LE) != pe::IMAGE_FILE_MACHINE_UNKNOWN
417            || header.sig2.get(LE) != 0xffff
418            || header.version.get(LE) < 2
419            || header.class_id != pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID
420        {
421            return Err(read::Error("Invalid COFF bigobj header values"));
422        }
423
424        // TODO: maybe validate that the machine is known?
425        Ok(header)
426    }
427}