1use std::{collections::HashMap, io::Write};
2
3use object::{Object, ObjectSection, ObjectSymbol};
4use pdb::FallibleIterator;
5
6use crate::app::files::{filesystem::FileSystem, path};
7
8use super::{bitness::Bitness, section::Section};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum FileType {
12 Coff,
13 CoffBig,
14 Elf32,
15 Elf64,
16 MachO32,
17 MachO64,
18 Pe32,
19 Pe64,
20 Xcoff32,
21 Xcoff64,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct GenericHeader {
26 pub(super) file_type: FileType,
27 pub(super) architecture: object::Architecture,
28 pub(super) bitness: Bitness,
29 pub(super) endianness: object::Endianness,
30 pub(super) entry: u64,
31 pub(super) sections: Vec<Section>,
32 pub(super) symbols: HashMap<u64, String>,
33 pub(super) symbols_by_name: HashMap<String, u64>,
34}
35
36impl GenericHeader {
37 fn demangle_symbol(symbol: &str) -> String {
38 let name = symbolic_demangle::demangle(symbol);
39 name.to_string()
40 }
41
42 pub fn parse_header(bytes: &[u8], file_path: &str, filesystem: &FileSystem) -> Option<Self> {
43 let header = object::File::parse(bytes);
44 if let Ok(header) = header {
45 let file_type = match &header {
46 object::File::Coff(_) => FileType::Coff,
47 object::File::CoffBig(_) => FileType::CoffBig,
48 object::File::Elf32(_) => FileType::Elf32,
49 object::File::Elf64(_) => FileType::Elf64,
50 object::File::MachO32(_) => FileType::MachO32,
51 object::File::MachO64(_) => FileType::MachO64,
52 object::File::Pe32(_) => FileType::Pe32,
53 object::File::Pe64(_) => FileType::Pe64,
54 object::File::Xcoff32(_) => FileType::Xcoff32,
55 object::File::Xcoff64(_) => FileType::Xcoff64,
56 _ => return None,
57 };
58
59 let architecture = header.architecture();
60
61 let bitness = if header.is_64() {
62 Bitness::Bit64
63 } else {
64 Bitness::Bit32
65 };
66
67 let endianness = header.endianness();
68
69 let mut entry = header.entry();
70
71 let sections: Vec<Section> = header
72 .sections()
73 .map(|section| Section {
74 name: section.name().unwrap_or_default().to_string(),
75 file_offset: section.file_range().unwrap_or_default().0,
76 virtual_address: section.address(),
77 size: section.file_range().unwrap_or_default().1,
78 })
79 .filter(|section| section.size > 0)
80 .collect();
81
82 match header {
83 object::File::MachO32(_) | object::File::MachO64(_) => {
84 if let Some(text) = sections.iter().find(|section| section.name == "__text") {
85 let text_offset = text.virtual_address.saturating_sub(text.file_offset);
86 entry += text_offset;
87 }
88 }
89 _ => {}
90 }
91
92 let mut symbols: Vec<(u64, String)> = header
93 .symbols()
94 .map(|symbol| {
95 (
96 symbol.address(),
97 symbol.name().unwrap_or_default().to_string(),
98 )
99 })
100 .collect();
101
102 let address_base = header.relative_address_base();
103
104 let (debug_data_directory, section_table) = match &header {
106 object::File::Pe32(pe) => (
107 pe.data_directory(object::pe::IMAGE_DIRECTORY_ENTRY_DEBUG),
108 Some(pe.section_table()),
109 ),
110 object::File::Pe64(pe) => (
111 pe.data_directory(object::pe::IMAGE_DIRECTORY_ENTRY_DEBUG),
112 Some(pe.section_table()),
113 ),
114 _ => (None, None),
115 };
116
117 if let Some(debug_data_directory) = debug_data_directory {
118 let section_table = section_table.expect(&t!("errors.pe_section_table_missing"));
119 let debug_data_dir_content = debug_data_directory.data(bytes, §ion_table);
120 if let Ok(debug_data_dir_content) = debug_data_dir_content {
121 let mut pdb_file_path = None;
122 for entry in debug_data_dir_content.chunks_exact(28) {
123 let _characteristics =
124 u32::from_le_bytes([entry[0], entry[1], entry[2], entry[3]]);
125 let _time_date_stamp =
126 u32::from_le_bytes([entry[4], entry[5], entry[6], entry[7]]);
127 let _major_version = u16::from_le_bytes([entry[8], entry[9]]);
128 let _minor_version = u16::from_le_bytes([entry[10], entry[11]]);
129 let ty = u32::from_le_bytes([entry[12], entry[13], entry[14], entry[15]]);
130 let size_of_data =
131 u32::from_le_bytes([entry[16], entry[17], entry[18], entry[19]]);
132 let _address_of_raw_data =
133 u32::from_le_bytes([entry[20], entry[21], entry[22], entry[23]]);
134 let pointer_to_raw_data =
135 u32::from_le_bytes([entry[24], entry[25], entry[26], entry[27]]);
136
137 if ty == 2 {
138 let data_header_size = 24;
139 let path = String::from_utf8_lossy(
140 &bytes[data_header_size + pointer_to_raw_data as usize
141 ..(pointer_to_raw_data + size_of_data) as usize],
142 )
143 .trim_end_matches('\0')
144 .to_string();
145 pdb_file_path = Some(path);
146 break;
147 }
148 }
149 if let Some(pdb_file_path) = pdb_file_path {
150 let pdb_absolute_path = if path::is_absolute(&pdb_file_path) {
151 pdb_file_path
152 } else {
153 path::join(
154 path::parent(file_path).unwrap_or("./"),
155 &pdb_file_path,
156 filesystem.separator(),
157 )
158 };
159 let file = filesystem.read(&pdb_absolute_path);
160 if let Ok(file) = file {
161 let mut tmp_file =
163 tempfile::tempfile().expect(&t!("errors.create_temp_file"));
164 tmp_file
165 .write_all(&file)
166 .expect(&t!("errors.write_temp_file"));
167
168 let pdb = pdb::PDB::open(tmp_file);
169 if let Ok(mut pdb) = pdb {
170 let symbol_table = pdb.global_symbols();
171 let address_map = pdb.address_map();
172 if let (Ok(symbol_table), Ok(address_map)) =
173 (symbol_table, address_map)
174 {
175 let mut iter = symbol_table.iter();
176 while let Ok(Some(symbol)) = iter.next() {
177 if let Ok(pdb::SymbolData::Public(public_symbol)) =
178 symbol.parse()
179 {
180 let address = public_symbol.offset.to_rva(&address_map);
181 if let Some(address) = address {
182 let name =
183 public_symbol.name.to_string().to_string();
184 symbols
185 .push((address.0 as u64 + address_base, name));
186 }
187 }
188 }
189 }
190 }
191 }
192 }
193 }
194 }
195
196 let symbols: HashMap<u64, String> = symbols
197 .into_iter()
198 .map(|(address, name)| (address, Self::demangle_symbol(&name)))
199 .collect();
200
201 let symbols_by_name = symbols
202 .iter()
203 .map(|(address, name)| (name.clone(), *address))
204 .collect();
205
206 Some(GenericHeader {
207 file_type,
208 architecture,
209 bitness,
210 endianness,
211 entry,
212 sections,
213 symbols,
214 symbols_by_name,
215 })
216 } else {
217 None
218 }
219 }
220
221 pub fn file_type(&self) -> FileType {
222 self.file_type
223 }
224}