Coverage Report

Created: 2024-10-16 07:58

/rust/registry/src/index.crates.io-6f17d22bba15001f/object-0.36.1/src/read/coff/file.rs
Line
Count
Source (jump to first uncovered line)
1
use alloc::vec::Vec;
2
use core::fmt::Debug;
3
4
use crate::endian::LittleEndian as LE;
5
use crate::pe;
6
use crate::pod::Pod;
7
use crate::read::{
8
    self, Architecture, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind,
9
    ObjectSection, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex,
10
};
11
12
use 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)]
20
pub(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.
32
pub 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)]
41
pub 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
48
impl<'data, R: ReadRef<'data>, Coff: CoffHeader> CoffFile<'data, R, Coff> {
49
    /// Parse the raw COFF file data.
50
0
    pub fn parse(data: R) -> Result<Self> {
51
0
        let mut offset = 0;
52
0
        let header = Coff::parse(data, &mut offset)?;
53
0
        let sections = header.sections(data, offset)?;
54
0
        let symbols = header.symbols(data)?;
55
56
0
        Ok(CoffFile {
57
0
            header,
58
0
            common: CoffCommon {
59
0
                sections,
60
0
                symbols,
61
0
                image_base: 0,
62
0
            },
63
0
            data,
64
0
        })
65
0
    }
66
67
    /// Get the raw COFF file header.
68
0
    pub fn coff_header(&self) -> &'data Coff {
69
0
        self.header
70
0
    }
71
72
    /// Get the COFF section table.
73
0
    pub fn coff_section_table(&self) -> SectionTable<'data> {
74
0
        self.common.sections
75
0
    }
76
77
    /// Get the COFF symbol table.
78
0
    pub fn coff_symbol_table(&self) -> &SymbolTable<'data, R, Coff> {
79
0
        &self.common.symbols
80
0
    }
81
}
82
83
impl<'data, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed
84
    for CoffFile<'data, R, Coff>
85
{
86
}
87
88
impl<'data, R, Coff> Object<'data> for CoffFile<'data, R, Coff>
89
where
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
0
    fn architecture(&self) -> Architecture {
105
0
        match self.header.machine() {
106
0
            pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm,
107
0
            pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64,
108
0
            pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386,
109
0
            pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64,
110
0
            _ => Architecture::Unknown,
111
        }
112
0
    }
113
114
0
    fn sub_architecture(&self) -> Option<SubArchitecture> {
115
0
        match self.header.machine() {
116
0
            pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC),
117
0
            _ => None,
118
        }
119
0
    }
120
121
    #[inline]
122
0
    fn is_little_endian(&self) -> bool {
123
0
        true
124
0
    }
125
126
    #[inline]
127
0
    fn is_64(&self) -> bool {
128
0
        // Windows COFF is always 32-bit, even for 64-bit architectures. This could be confusing.
129
0
        false
130
0
    }
131
132
0
    fn kind(&self) -> ObjectKind {
133
0
        ObjectKind::Relocatable
134
0
    }
135
136
0
    fn segments(&self) -> CoffSegmentIterator<'data, '_, R, Coff> {
137
0
        CoffSegmentIterator {
138
0
            file: self,
139
0
            iter: self.common.sections.iter(),
140
0
        }
141
0
    }
142
143
0
    fn section_by_name_bytes<'file>(
144
0
        &'file self,
145
0
        section_name: &[u8],
146
0
    ) -> Option<CoffSection<'data, 'file, R, Coff>> {
147
0
        self.sections()
148
0
            .find(|section| section.name_bytes() == Ok(section_name))
149
0
    }
150
151
0
    fn section_by_index(&self, index: SectionIndex) -> Result<CoffSection<'data, '_, R, Coff>> {
152
0
        let section = self.common.sections.section(index)?;
153
0
        Ok(CoffSection {
154
0
            file: self,
155
0
            index,
156
0
            section,
157
0
        })
158
0
    }
159
160
0
    fn sections(&self) -> CoffSectionIterator<'data, '_, R, Coff> {
161
0
        CoffSectionIterator {
162
0
            file: self,
163
0
            iter: self.common.sections.iter().enumerate(),
164
0
        }
165
0
    }
166
167
0
    fn comdats(&self) -> CoffComdatIterator<'data, '_, R, Coff> {
168
0
        CoffComdatIterator::new(self)
169
0
    }
170
171
0
    fn symbol_by_index(&self, index: SymbolIndex) -> Result<CoffSymbol<'data, '_, R, Coff>> {
172
0
        let symbol = self.common.symbols.symbol(index)?;
173
0
        Ok(CoffSymbol {
174
0
            file: &self.common,
175
0
            index,
176
0
            symbol,
177
0
        })
178
0
    }
179
180
0
    fn symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> {
181
0
        CoffSymbolIterator::new(&self.common)
182
0
    }
183
184
    #[inline]
185
0
    fn symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> {
186
0
        Some(CoffSymbolTable { file: &self.common })
187
0
    }
188
189
0
    fn dynamic_symbols(&self) -> CoffSymbolIterator<'data, '_, R, Coff> {
190
0
        CoffSymbolIterator::empty(&self.common)
191
0
    }
192
193
    #[inline]
194
0
    fn dynamic_symbol_table(&self) -> Option<CoffSymbolTable<'data, '_, R, Coff>> {
195
0
        None
196
0
    }
197
198
    #[inline]
199
0
    fn dynamic_relocations(&self) -> Option<NoDynamicRelocationIterator> {
200
0
        None
201
0
    }
202
203
    #[inline]
204
0
    fn imports(&self) -> Result<Vec<Import<'data>>> {
205
0
        // TODO: this could return undefined symbols, but not needed yet.
206
0
        Ok(Vec::new())
207
0
    }
208
209
    #[inline]
210
0
    fn exports(&self) -> Result<Vec<Export<'data>>> {
211
0
        // TODO: this could return global symbols, but not needed yet.
212
0
        Ok(Vec::new())
213
0
    }
214
215
0
    fn has_debug_symbols(&self) -> bool {
216
0
        self.section_by_name(".debug_info").is_some()
217
0
    }
218
219
0
    fn relative_address_base(&self) -> u64 {
220
0
        0
221
0
    }
222
223
    #[inline]
224
0
    fn entry(&self) -> u64 {
225
0
        0
226
0
    }
227
228
0
    fn flags(&self) -> FileFlags {
229
0
        FileFlags::Coff {
230
0
            characteristics: self.header.characteristics(),
231
0
        }
232
0
    }
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.
238
0
pub fn anon_object_class_id<'data, R: ReadRef<'data>>(data: R) -> Result<pe::ClsId> {
239
0
    let header = data
240
0
        .read_at::<pe::AnonObjectHeader>(0)
241
0
        .read_error("Invalid anon object header size or alignment")?;
242
0
    Ok(header.class_id)
243
0
}
244
245
/// A trait for generic access to [`pe::ImageFileHeader`] and [`pe::AnonObjectHeaderBigobj`].
246
#[allow(missing_docs)]
247
pub 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
0
    fn sections<'data, R: ReadRef<'data>>(
275
0
        &self,
276
0
        data: R,
277
0
        offset: u64,
278
0
    ) -> read::Result<SectionTable<'data>> {
279
0
        SectionTable::parse(self, data, offset)
280
0
    }
281
282
    /// Read the symbol table and string table.
283
    ///
284
    /// `data` must be the entire file data.
285
    #[inline]
286
0
    fn symbols<'data, R: ReadRef<'data>>(
287
0
        &self,
288
0
        data: R,
289
0
    ) -> read::Result<SymbolTable<'data, R, Self>> {
290
0
        SymbolTable::parse(self, data)
291
0
    }
292
}
293
294
impl CoffHeader for pe::ImageFileHeader {
295
    type ImageSymbol = pe::ImageSymbol;
296
    type ImageSymbolBytes = pe::ImageSymbolBytes;
297
298
0
    fn is_type_bigobj() -> bool {
299
0
        false
300
0
    }
301
302
0
    fn machine(&self) -> u16 {
303
0
        self.machine.get(LE)
304
0
    }
305
306
0
    fn number_of_sections(&self) -> u32 {
307
0
        self.number_of_sections.get(LE).into()
308
0
    }
309
310
0
    fn pointer_to_symbol_table(&self) -> u32 {
311
0
        self.pointer_to_symbol_table.get(LE)
312
0
    }
313
314
0
    fn number_of_symbols(&self) -> u32 {
315
0
        self.number_of_symbols.get(LE)
316
0
    }
317
318
0
    fn characteristics(&self) -> u16 {
319
0
        self.characteristics.get(LE)
320
0
    }
321
322
0
    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> {
323
0
        let header = data
324
0
            .read::<pe::ImageFileHeader>(offset)
325
0
            .read_error("Invalid COFF file header size or alignment")?;
326
327
        // Skip over the optional header.
328
0
        *offset = offset
329
0
            .checked_add(header.size_of_optional_header.get(LE).into())
330
0
            .read_error("Invalid COFF optional header size")?;
331
332
        // TODO: maybe validate that the machine is known?
333
0
        Ok(header)
334
0
    }
335
}
336
337
impl CoffHeader for pe::AnonObjectHeaderBigobj {
338
    type ImageSymbol = pe::ImageSymbolEx;
339
    type ImageSymbolBytes = pe::ImageSymbolExBytes;
340
341
0
    fn is_type_bigobj() -> bool {
342
0
        true
343
0
    }
344
345
0
    fn machine(&self) -> u16 {
346
0
        self.machine.get(LE)
347
0
    }
348
349
0
    fn number_of_sections(&self) -> u32 {
350
0
        self.number_of_sections.get(LE)
351
0
    }
352
353
0
    fn pointer_to_symbol_table(&self) -> u32 {
354
0
        self.pointer_to_symbol_table.get(LE)
355
0
    }
356
357
0
    fn number_of_symbols(&self) -> u32 {
358
0
        self.number_of_symbols.get(LE)
359
0
    }
360
361
0
    fn characteristics(&self) -> u16 {
362
0
        0
363
0
    }
364
365
0
    fn parse<'data, R: ReadRef<'data>>(data: R, offset: &mut u64) -> read::Result<&'data Self> {
366
0
        let header = data
367
0
            .read::<pe::AnonObjectHeaderBigobj>(offset)
368
0
            .read_error("Invalid COFF bigobj file header size or alignment")?;
369
370
0
        if header.sig1.get(LE) != pe::IMAGE_FILE_MACHINE_UNKNOWN
371
0
            || header.sig2.get(LE) != 0xffff
372
0
            || header.version.get(LE) < 2
373
0
            || header.class_id != pe::ANON_OBJECT_HEADER_BIGOBJ_CLASS_ID
374
        {
375
0
            return Err(read::Error("Invalid COFF bigobj header values"));
376
0
        }
377
0
378
0
        // TODO: maybe validate that the machine is known?
379
0
        Ok(header)
380
0
    }
381
}