1use alloc::vec::Vec;
2use core::fmt::Debug;
3
4use crate::read::{
5 self, Architecture, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind,
6 ObjectSection, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex,
7};
8use crate::{pe, LittleEndian as LE, Pod};
9
10use super::{
11 CoffComdat, CoffComdatIterator, CoffSection, CoffSectionIterator, CoffSegment,
12 CoffSegmentIterator, CoffSymbol, CoffSymbolIterator, CoffSymbolTable, ImageSymbol,
13 SectionTable, SymbolTable,
14};
15
16/// The common parts of `PeFile` and `CoffFile`.
17#[derive(Debug)]
18pub(crate) struct CoffCommon<'data, R: ReadRef<'data>, Coff: CoffHeader = pe::ImageFileHeader> {
19 pub(crate) sections: SectionTable<'data>,
20 pub(crate) symbols: SymbolTable<'data, R, Coff>,
21 pub(crate) image_base: u64,
22}
23
24/// A COFF bigobj object file with 32-bit section numbers.
25///
26/// This is a file that starts with [`pe::AnonObjectHeaderBigobj`], and corresponds
27/// to [`crate::FileKind::CoffBig`].
28///
29/// Most functionality is provided by the [`Object`] trait implementation.
30pub type CoffBigFile<'data, R = &'data [u8]> = CoffFile<'data, R, pe::AnonObjectHeaderBigobj>;
31
32/// A COFF object file.
33///
34/// This is a file that starts with [`pe::ImageFileHeader`], and corresponds
35/// to [`crate::FileKind::Coff`].
36///
37/// Most functionality is provided by the [`Object`] trait implementation.
38#[derive(Debug)]
39pub struct CoffFile<'data, R: ReadRef<'data> = &'data [u8], Coff: CoffHeader = pe::ImageFileHeader>
40{
41 pub(super) header: &'data Coff,
42 pub(super) common: CoffCommon<'data, R, Coff>,
43 pub(super) data: R,
44}
45
46impl<'data, R: ReadRef<'data>, Coff: CoffHeader> CoffFile<'data, R, Coff> {
47 /// Parse the raw COFF file data.
48 pub fn parse(data: R) -> Result<Self> {
49 let mut offset: u64 = 0;
50 let header: &Coff = Coff::parse(data, &mut offset)?;
51 let sections: SectionTable<'_> = header.sections(data, offset)?;
52 let symbols: SymbolTable<'_, R, Coff> = header.symbols(data)?;
53
54 Ok(CoffFile {
55 header,
56 common: CoffCommon {
57 sections,
58 symbols,
59 image_base: 0,
60 },
61 data,
62 })
63 }
64}
65
66impl<'data, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed
67 for CoffFile<'data, R, Coff>
68{
69}
70
71impl<'data, 'file, R, Coff> Object<'data, 'file> for CoffFile<'data, R, Coff>
72where
73 'data: 'file,
74 R: 'file + ReadRef<'data>,
75 Coff: CoffHeader,
76{
77 type Segment = CoffSegment<'data, 'file, R, Coff>;
78 type SegmentIterator = CoffSegmentIterator<'data, 'file, R, Coff>;
79 type Section = CoffSection<'data, 'file, R, Coff>;
80 type SectionIterator = CoffSectionIterator<'data, 'file, R, Coff>;
81 type Comdat = CoffComdat<'data, 'file, R, Coff>;
82 type ComdatIterator = CoffComdatIterator<'data, 'file, R, Coff>;
83 type Symbol = CoffSymbol<'data, 'file, R, Coff>;
84 type SymbolIterator = CoffSymbolIterator<'data, 'file, R, Coff>;
85 type SymbolTable = CoffSymbolTable<'data, 'file, R, Coff>;
86 type DynamicRelocationIterator = NoDynamicRelocationIterator;
87
88 fn architecture(&self) -> Architecture {
89 match self.header.machine() {
90 pe::IMAGE_FILE_MACHINE_ARMNT => Architecture::Arm,
91 pe::IMAGE_FILE_MACHINE_ARM64 | pe::IMAGE_FILE_MACHINE_ARM64EC => Architecture::Aarch64,
92 pe::IMAGE_FILE_MACHINE_I386 => Architecture::I386,
93 pe::IMAGE_FILE_MACHINE_AMD64 => Architecture::X86_64,
94 _ => Architecture::Unknown,
95 }
96 }
97
98 fn sub_architecture(&self) -> Option<SubArchitecture> {
99 match self.header.machine() {
100 pe::IMAGE_FILE_MACHINE_ARM64EC => Some(SubArchitecture::Arm64EC),
101 _ => None,
102 }
103 }
104
105 #[inline]
106 fn is_little_endian(&self) -> bool {
107 true
108 }
109
110 #[inline]
111 fn is_64(&self) -> bool {
112 // Windows COFF is always 32-bit, even for 64-bit architectures. This could be confusing.
113 false
114 }
115
116 fn kind(&self) -> ObjectKind {
117 ObjectKind::Relocatable
118 }
119
120 fn segments(&'file self) -> CoffSegmentIterator<'data, 'file, R, Coff> {
121 CoffSegmentIterator {
122 file: self,
123 iter: self.common.sections.iter(),
124 }
125 }
126
127 fn section_by_name_bytes(
128 &'file self,
129 section_name: &[u8],
130 ) -> Option<CoffSection<'data, 'file, R, Coff>> {
131 self.sections()
132 .find(|section| section.name_bytes() == Ok(section_name))
133 }
134
135 fn section_by_index(
136 &'file self,
137 index: SectionIndex,
138 ) -> Result<CoffSection<'data, 'file, R, Coff>> {
139 let section = self.common.sections.section(index.0)?;
140 Ok(CoffSection {
141 file: self,
142 index,
143 section,
144 })
145 }
146
147 fn sections(&'file self) -> CoffSectionIterator<'data, 'file, R, Coff> {
148 CoffSectionIterator {
149 file: self,
150 iter: self.common.sections.iter().enumerate(),
151 }
152 }
153
154 fn comdats(&'file self) -> CoffComdatIterator<'data, 'file, R, Coff> {
155 CoffComdatIterator {
156 file: self,
157 index: 0,
158 }
159 }
160
161 fn symbol_by_index(
162 &'file self,
163 index: SymbolIndex,
164 ) -> Result<CoffSymbol<'data, 'file, R, Coff>> {
165 let symbol = self.common.symbols.symbol(index.0)?;
166 Ok(CoffSymbol {
167 file: &self.common,
168 index,
169 symbol,
170 })
171 }
172
173 fn symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R, Coff> {
174 CoffSymbolIterator {
175 file: &self.common,
176 index: 0,
177 }
178 }
179
180 #[inline]
181 fn symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R, Coff>> {
182 Some(CoffSymbolTable { file: &self.common })
183 }
184
185 fn dynamic_symbols(&'file self) -> CoffSymbolIterator<'data, 'file, R, Coff> {
186 CoffSymbolIterator {
187 file: &self.common,
188 // Hack: don't return any.
189 index: self.common.symbols.len(),
190 }
191 }
192
193 #[inline]
194 fn dynamic_symbol_table(&'file self) -> Option<CoffSymbolTable<'data, 'file, R, Coff>> {
195 None
196 }
197
198 #[inline]
199 fn dynamic_relocations(&'file 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: &AnonObjectHeader = dataResult<&AnonObjectHeader, …>
240 .read_at::<pe::AnonObjectHeader>(offset: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}
382