1 | use alloc::vec::Vec; |
2 | use core::fmt::Debug; |
3 | |
4 | use crate::read::{ |
5 | self, Architecture, Export, FileFlags, Import, NoDynamicRelocationIterator, Object, ObjectKind, |
6 | ObjectSection, ReadError, ReadRef, Result, SectionIndex, SubArchitecture, SymbolIndex, |
7 | }; |
8 | use crate::{pe, LittleEndian as LE, Pod}; |
9 | |
10 | use 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)] |
18 | pub(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. |
30 | pub 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)] |
39 | pub 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 | |
46 | impl<'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 | |
66 | impl<'data, R: ReadRef<'data>, Coff: CoffHeader> read::private::Sealed |
67 | for CoffFile<'data, R, Coff> |
68 | { |
69 | } |
70 | |
71 | impl<'data, 'file, R, Coff> Object<'data, 'file> for CoffFile<'data, R, Coff> |
72 | where |
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. |
238 | pub 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)] |
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 | 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 | |
294 | impl 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 | |
337 | impl 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 | |