pe_assembler/helpers/coff_reader/
mod.rs1use crate::types::coff::{
2 CoffFileType, CoffHeader, CoffInfo, CoffObject, CoffRelocation, CoffSection, CoffSymbol, SectionHeader,
3};
4use byteorder::{LittleEndian, ReadBytesExt};
5use gaia_types::{helpers::Architecture, BinaryReader, GaiaError};
6use std::io::{Read, Seek};
7
8#[derive(Debug)]
10pub struct CoffViewer<W> {
11 pub viewer: BinaryReader<W, LittleEndian>,
12}
13
14impl<W> CoffViewer<W> {
15 pub fn new(reader: W) -> Self {
16 Self { viewer: BinaryReader::new(reader) }
17 }
18}
19
20pub trait CoffReader<R: Read + Seek> {
22 fn get_viewer(&mut self) -> &mut BinaryReader<R, LittleEndian>;
24
25 fn add_diagnostics(&mut self, error: impl Into<GaiaError>);
27
28 fn get_cached_section_headers(&self) -> Option<&Vec<SectionHeader>>;
30
31 fn set_cached_section_headers(&mut self, headers: Vec<SectionHeader>);
33
34 fn read_header_once(&mut self) -> Result<&CoffHeader, GaiaError>;
36
37 fn read_header_force(&mut self) -> Result<CoffHeader, GaiaError> {
39 let original_pos = self.get_viewer().get_position();
41
42 self.get_viewer().set_position(0)?;
44
45 let coff_header = CoffHeader::read(self.get_viewer())?;
47
48 match coff_header.machine {
50 0x014c | 0x8664 | 0x01c0 | 0xaa64 => {} unknown => {
52 let error = GaiaError::invalid_data(&format!("不支持的机器类型: 0x{:04x}", unknown));
53 self.add_diagnostics(error);
54 }
55 }
56
57 if coff_header.number_of_sections == 0 {
59 let error = GaiaError::invalid_data("COFF 文件必须至少有一个节");
60 self.add_diagnostics(error);
61 }
62
63 self.get_viewer().set_position(original_pos)?;
65
66 Ok(coff_header)
67 }
68
69 fn read_section_headers(&mut self) -> Result<Vec<SectionHeader>, GaiaError> {
71 if let Some(sections) = self.get_cached_section_headers() {
72 return Ok(sections.clone());
73 }
74
75 let header = self.read_header_once()?.clone();
77 let original_pos = self.get_viewer().get_position();
78
79 let mut section_headers = Vec::with_capacity(header.number_of_sections as usize);
81
82 let section_header_offset = std::mem::size_of::<CoffHeader>() as u64 + header.size_of_optional_header as u64;
84
85 self.get_viewer().set_position(section_header_offset)?;
86
87 for _ in 0..header.number_of_sections {
88 let section_header = SectionHeader::read(self.get_viewer())?;
89 section_headers.push(section_header);
90 }
91
92 self.get_viewer().set_position(original_pos)?;
94
95 self.set_cached_section_headers(section_headers.clone());
96 Ok(section_headers)
97 }
98
99 fn read_section_from_header(&mut self, header: &SectionHeader) -> Result<CoffSection, GaiaError> {
101 let viewer = self.get_viewer();
102
103 let mut data = Vec::new();
104 let mut relocations = Vec::new();
105
106 if header.size_of_raw_data > 0 && header.pointer_to_raw_data > 0 {
108 let original_pos = viewer.get_position();
109 viewer.set_position(header.pointer_to_raw_data as u64)?;
110 data.resize(header.size_of_raw_data as usize, 0);
111 viewer.read_exact(&mut data)?;
112 viewer.set_position(original_pos)?;
113 }
114
115 if header.number_of_relocations > 0 && header.pointer_to_relocations > 0 {
117 let original_pos = viewer.get_position();
118 viewer.set_position(header.pointer_to_relocations as u64)?;
119
120 for _ in 0..header.number_of_relocations {
121 let mut coff_viewer = CoffViewer::new(&mut *viewer);
122 let relocation = CoffRelocation::read(&mut coff_viewer)?;
123 relocations.push(relocation);
124 }
125
126 viewer.set_position(original_pos)?;
127 }
128
129 Ok(CoffSection { header: *header, data, relocations })
130 }
131
132 fn read_symbols(&mut self) -> Result<Vec<CoffSymbol>, GaiaError> {
134 let header = self.read_header_once()?.clone();
135
136 if header.number_of_symbols == 0 || header.pointer_to_symbol_table == 0 {
137 return Ok(Vec::new());
138 }
139
140 let original_pos = self.get_viewer().get_position();
141 self.get_viewer().set_position(header.pointer_to_symbol_table as u64)?;
142
143 let mut symbols = Vec::new();
144 for _ in 0..header.number_of_symbols {
145 let mut coff_viewer = CoffViewer::new(&mut *self.get_viewer());
146 let symbol = CoffSymbol::read(&mut coff_viewer)?;
147 symbols.push(symbol);
148 }
149
150 self.get_viewer().set_position(original_pos)?;
151 Ok(symbols)
152 }
153
154 fn create_coff_info(&mut self) -> Result<CoffInfo, GaiaError> {
156 let header = self.read_header_once()?.clone();
157 let viewer = self.get_viewer();
158
159 let target_arch = match header.machine {
161 0x014c => Architecture::X86,
162 0x8664 => Architecture::X86_64,
163 0x01c0 => Architecture::ARM32,
164 0xaa64 => Architecture::ARM64,
165 _ => Architecture::Unknown,
166 };
167
168 let current_pos = viewer.get_position();
170 viewer.seek(std::io::SeekFrom::End(0))?;
171 let file_size = viewer.get_position();
172 viewer.set_position(current_pos)?;
173
174 Ok(CoffInfo {
175 file_type: CoffFileType::Object,
176 target_arch,
177 section_count: header.number_of_sections,
178 symbol_count: header.number_of_symbols,
179 file_size,
180 timestamp: header.time_date_stamp,
181 })
182 }
183
184 fn read_object_once(&mut self) -> Result<&CoffObject, GaiaError>;
186
187 fn read_object_force(&mut self) -> Result<CoffObject, GaiaError> {
189 let header = self.read_header_once()?.clone();
190 let section_headers = self.read_section_headers()?;
191
192 let mut sections = Vec::new();
194 for section_header in section_headers {
195 let section = self.read_section_from_header(§ion_header)?;
196 sections.push(section);
197 }
198
199 let symbols = self.read_symbols()?;
201
202 let string_table = Vec::new();
204
205 Ok(CoffObject { header, sections, symbols, string_table })
206 }
207}
208
209impl CoffRelocation {
210 pub fn read<R: ReadBytesExt>(reader: &mut CoffViewer<R>) -> Result<Self, GaiaError> {
212 Ok(CoffRelocation {
213 virtual_address: reader.viewer.read_u32()?,
214 symbol_table_index: reader.viewer.read_u32()?,
215 relocation_type: reader.viewer.read_u16()?,
216 })
217 }
218}
219
220impl CoffSymbol {
221 pub fn read<R: ReadBytesExt>(reader: &mut CoffViewer<R>) -> Result<Self, GaiaError> {
223 let mut name_bytes = [0u8; 8];
224 reader.viewer.read_exact(&mut name_bytes)?;
225
226 let name = if name_bytes[0..4] == [0, 0, 0, 0] {
227 format!("symbol_{}", u32::from_le_bytes([name_bytes[4], name_bytes[5], name_bytes[6], name_bytes[7]]))
229 }
230 else {
231 String::from_utf8_lossy(&name_bytes).trim_end_matches('\0').to_string()
232 };
233
234 Ok(CoffSymbol {
235 name,
236 value: reader.viewer.read_u32()?,
237 section_number: reader.viewer.read_i16()?,
238 symbol_type: reader.viewer.read_u16()?,
239 storage_class: reader.viewer.read_u8()?,
240 number_of_aux_symbols: reader.viewer.read_u8()?,
241 })
242 }
243}
244
245impl SectionHeader {
246 pub fn read<R: Read>(mut reader: R) -> Result<Self, GaiaError> {
248 let mut name = [0u8; 8];
249 reader.read_exact(&mut name)?;
250
251 Ok(SectionHeader {
252 name,
253 virtual_size: reader.read_u32::<LittleEndian>()?,
254 virtual_address: reader.read_u32::<LittleEndian>()?,
255 size_of_raw_data: reader.read_u32::<LittleEndian>()?,
256 pointer_to_raw_data: reader.read_u32::<LittleEndian>()?,
257 pointer_to_relocations: reader.read_u32::<LittleEndian>()?,
258 pointer_to_line_numbers: reader.read_u32::<LittleEndian>()?,
259 number_of_relocations: reader.read_u16::<LittleEndian>()?,
260 number_of_line_numbers: reader.read_u16::<LittleEndian>()?,
261 characteristics: reader.read_u32::<LittleEndian>()?,
262 })
263 }
264}