1#[cfg(feature = "dwarf")]
2mod dwarf2;
3mod mdebug;
4pub mod read;
5pub mod split_meta;
6
7use alloc::{
8 borrow::Cow,
9 boxed::Box,
10 collections::BTreeMap,
11 string::{String, ToString},
12 vec,
13 vec::Vec,
14};
15use core::{
16 fmt,
17 num::{NonZeroU32, NonZeroU64},
18};
19
20use flagset::{FlagSet, flags};
21
22use crate::{
23 arch::{Arch, ArchDummy},
24 obj::split_meta::SplitMeta,
25 util::ReallySigned,
26};
27
28#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
29pub enum SectionKind {
30 #[default]
31 Unknown = -1,
32 Code,
33 Data,
34 Bss,
35 Common,
36}
37
38flags! {
39 #[derive(Hash)]
40 pub enum SymbolFlag: u16 {
41 Global,
42 Local,
43 Weak,
44 Common,
45 Hidden,
46 HasExtra,
49 SizeInferred,
51 Ignored,
53 CompilerGenerated,
55 }
56}
57
58pub type SymbolFlagSet = FlagSet<SymbolFlag>;
59
60flags! {
61 #[derive(Hash)]
62 pub enum SectionFlag: u8 {
63 Combined,
65 }
66}
67
68pub type SectionFlagSet = FlagSet<SectionFlag>;
69
70#[derive(Debug, Clone, Default)]
71pub struct Section {
72 pub id: String,
74 pub name: String,
75 pub address: u64,
76 pub size: u64,
77 pub kind: SectionKind,
78 pub data: SectionData,
79 pub flags: SectionFlagSet,
80 pub align: Option<NonZeroU64>,
81 pub relocations: Vec<Relocation>,
82 pub line_info: BTreeMap<u64, u32>,
84 pub virtual_address: Option<u64>,
86}
87
88#[derive(Clone, Default)]
89#[repr(transparent)]
90pub struct SectionData(pub Vec<u8>);
91
92impl core::ops::Deref for SectionData {
93 type Target = Vec<u8>;
94
95 fn deref(&self) -> &Self::Target { &self.0 }
96}
97
98impl fmt::Debug for SectionData {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 f.debug_tuple("SectionData").field(&self.0.len()).finish()
101 }
102}
103
104impl Section {
105 pub fn data_range(&self, address: u64, size: usize) -> Option<&[u8]> {
106 let offset = address.checked_sub(self.address)?;
107 self.data.get(offset as usize..offset as usize + size)
108 }
109
110 pub fn combined_alignment(&self) -> u64 {
112 const MIN_ALIGNMENT: u64 = 4;
113 self.align.map_or(MIN_ALIGNMENT, |align| align.get().max(MIN_ALIGNMENT))
114 }
115
116 pub fn relocation_at(&self, address: u64, size: u8) -> Option<&Relocation> {
117 match self.relocations.binary_search_by_key(&address, |r| r.address) {
118 Ok(mut i) => {
119 while i
121 .checked_sub(1)
122 .and_then(|n| self.relocations.get(n))
123 .is_some_and(|r| r.address == address)
124 {
125 i -= 1;
126 }
127 self.relocations.get(i)
128 }
129 Err(i) => self.relocations.get(i).filter(|r| r.address < address + size as u64),
130 }
131 }
132
133 pub fn resolve_relocation_at<'obj>(
134 &'obj self,
135 obj: &'obj Object,
136 address: u64,
137 size: u8,
138 ) -> Option<ResolvedRelocation<'obj>> {
139 self.relocation_at(address, size).and_then(|relocation| {
140 let symbol = obj.symbols.get(relocation.target_symbol)?;
141 Some(ResolvedRelocation { relocation, symbol })
142 })
143 }
144}
145
146#[derive(Debug, Clone, Eq, PartialEq)]
147pub enum InstructionArgValue<'a> {
148 Signed(i64),
149 Unsigned(u64),
150 Opaque(Cow<'a, str>),
151}
152
153impl InstructionArgValue<'_> {
154 pub fn loose_eq(&self, other: &InstructionArgValue) -> bool {
155 match (self, other) {
156 (InstructionArgValue::Signed(a), InstructionArgValue::Signed(b)) => a == b,
157 (InstructionArgValue::Unsigned(a), InstructionArgValue::Unsigned(b)) => a == b,
158 (InstructionArgValue::Signed(a), InstructionArgValue::Unsigned(b))
159 | (InstructionArgValue::Unsigned(b), InstructionArgValue::Signed(a)) => *a as u64 == *b,
160 (InstructionArgValue::Opaque(a), InstructionArgValue::Opaque(b)) => a == b,
161 _ => false,
162 }
163 }
164
165 pub fn to_static(&self) -> InstructionArgValue<'static> {
166 match self {
167 InstructionArgValue::Signed(v) => InstructionArgValue::Signed(*v),
168 InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(*v),
169 InstructionArgValue::Opaque(v) => InstructionArgValue::Opaque(v.to_string().into()),
170 }
171 }
172
173 pub fn into_static(self) -> InstructionArgValue<'static> {
174 match self {
175 InstructionArgValue::Signed(v) => InstructionArgValue::Signed(v),
176 InstructionArgValue::Unsigned(v) => InstructionArgValue::Unsigned(v),
177 InstructionArgValue::Opaque(v) => {
178 InstructionArgValue::Opaque(Cow::Owned(v.into_owned()))
179 }
180 }
181 }
182}
183
184impl fmt::Display for InstructionArgValue<'_> {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 match self {
187 InstructionArgValue::Signed(v) => write!(f, "{:#x}", ReallySigned(*v)),
188 InstructionArgValue::Unsigned(v) => write!(f, "{v:#x}"),
189 InstructionArgValue::Opaque(v) => write!(f, "{v}"),
190 }
191 }
192}
193
194#[derive(Debug, Clone, Eq, PartialEq)]
195pub enum InstructionArg<'a> {
196 Value(InstructionArgValue<'a>),
197 Reloc,
198 BranchDest(u64),
199}
200
201impl InstructionArg<'_> {
202 pub fn loose_eq(&self, other: &InstructionArg) -> bool {
203 match (self, other) {
204 (InstructionArg::Value(a), InstructionArg::Value(b)) => a.loose_eq(b),
205 (InstructionArg::Reloc, InstructionArg::Reloc) => true,
206 (InstructionArg::BranchDest(a), InstructionArg::BranchDest(b)) => a == b,
207 _ => false,
208 }
209 }
210
211 pub fn to_static(&self) -> InstructionArg<'static> {
212 match self {
213 InstructionArg::Value(v) => InstructionArg::Value(v.to_static()),
214 InstructionArg::Reloc => InstructionArg::Reloc,
215 InstructionArg::BranchDest(v) => InstructionArg::BranchDest(*v),
216 }
217 }
218
219 pub fn into_static(self) -> InstructionArg<'static> {
220 match self {
221 InstructionArg::Value(v) => InstructionArg::Value(v.into_static()),
222 InstructionArg::Reloc => InstructionArg::Reloc,
223 InstructionArg::BranchDest(v) => InstructionArg::BranchDest(v),
224 }
225 }
226}
227
228#[derive(Copy, Clone, Debug, Default)]
229pub struct InstructionRef {
230 pub address: u64,
231 pub size: u8,
232 pub opcode: u16,
233 pub branch_dest: Option<u64>,
234}
235
236#[derive(Debug, Clone)]
237pub struct ParsedInstruction {
238 pub ins_ref: InstructionRef,
239 pub mnemonic: Cow<'static, str>,
240 pub args: Vec<InstructionArg<'static>>,
241}
242
243#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)]
244pub enum SymbolKind {
245 #[default]
246 Unknown,
247 Function,
248 Object,
249 Section,
250}
251
252#[derive(Debug)]
253pub enum FlowAnalysisValue {
254 Text(String),
255}
256
257pub trait FlowAnalysisResult: core::fmt::Debug + Send {
258 fn get_argument_value_at_address(
259 &self,
260 address: u64,
261 argument: u8,
262 ) -> Option<&FlowAnalysisValue>;
263}
264
265#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
266pub struct Symbol {
267 pub name: String,
268 pub demangled_name: Option<String>,
269 pub normalized_name: Option<String>,
270 pub address: u64,
271 pub size: u64,
272 pub kind: SymbolKind,
273 pub section: Option<usize>,
274 pub flags: SymbolFlagSet,
275 pub align: Option<NonZeroU32>,
277 pub virtual_address: Option<u64>,
279}
280
281#[derive(Debug)]
282pub struct Object {
283 pub arch: Box<dyn Arch>,
284 pub endianness: object::Endianness,
285 pub symbols: Vec<Symbol>,
286 pub sections: Vec<Section>,
287 pub split_meta: Option<SplitMeta>,
289 #[cfg(feature = "std")]
290 pub path: Option<std::path::PathBuf>,
291 #[cfg(feature = "std")]
292 pub timestamp: Option<filetime::FileTime>,
293 pub flow_analysis_results: BTreeMap<u64, Box<dyn FlowAnalysisResult>>,
294}
295
296impl Default for Object {
297 fn default() -> Self {
298 Self {
299 arch: ArchDummy::new(),
300 endianness: object::Endianness::Little,
301 symbols: vec![],
302 sections: vec![],
303 split_meta: None,
304 #[cfg(feature = "std")]
305 path: None,
306 #[cfg(feature = "std")]
307 timestamp: None,
308 flow_analysis_results: BTreeMap::<u64, Box<dyn FlowAnalysisResult>>::new(),
309 }
310 }
311}
312
313impl Object {
314 pub fn resolve_instruction_ref(
315 &self,
316 symbol_index: usize,
317 ins_ref: InstructionRef,
318 ) -> Option<ResolvedInstructionRef<'_>> {
319 let symbol = self.symbols.get(symbol_index)?;
320 let section_index = symbol.section?;
321 let section = self.sections.get(section_index)?;
322 let offset = ins_ref.address.checked_sub(section.address)?;
323 let code = section.data.get(offset as usize..offset as usize + ins_ref.size as usize)?;
324 let relocation = section.resolve_relocation_at(self, ins_ref.address, ins_ref.size);
325 Some(ResolvedInstructionRef {
326 ins_ref,
327 symbol_index,
328 symbol,
329 section,
330 section_index,
331 code,
332 relocation,
333 })
334 }
335
336 pub fn symbol_data(&self, symbol_index: usize) -> Option<&[u8]> {
337 let symbol = self.symbols.get(symbol_index)?;
338 let section_index = symbol.section?;
339 let section = self.sections.get(section_index)?;
340 let offset = symbol.address.checked_sub(section.address)?;
341 section.data.get(offset as usize..offset as usize + symbol.size as usize)
342 }
343
344 pub fn symbol_by_name(&self, name: &str) -> Option<usize> {
345 self.symbols.iter().position(|symbol| symbol.section.is_some() && symbol.name == name)
346 }
347
348 pub fn get_flow_analysis_result(&self, symbol: &Symbol) -> Option<&dyn FlowAnalysisResult> {
349 let key = symbol.section.unwrap_or_default() as u64 * 1024 * 1024 * 1024 + symbol.address;
350 self.flow_analysis_results.get(&key).map(|result| result.as_ref())
351 }
352
353 pub fn add_flow_analysis_result(
354 &mut self,
355 symbol: &Symbol,
356 result: Box<dyn FlowAnalysisResult>,
357 ) {
358 let key = symbol.section.unwrap_or_default() as u64 * 1024 * 1024 * 1024 + symbol.address;
359 self.flow_analysis_results.insert(key, result);
360 }
361
362 pub fn has_flow_analysis_result(&self) -> bool { !self.flow_analysis_results.is_empty() }
363}
364
365#[derive(Debug, Clone, Eq, PartialEq, Hash)]
366pub struct Relocation {
367 pub flags: RelocationFlags,
368 pub address: u64,
369 pub target_symbol: usize,
370 pub addend: i64,
371}
372
373#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
374pub enum RelocationFlags {
375 Elf(u32),
376 Coff(u16),
377}
378
379#[derive(Debug, Copy, Clone)]
380pub struct ResolvedRelocation<'a> {
381 pub relocation: &'a Relocation,
382 pub symbol: &'a Symbol,
383}
384
385#[derive(Debug, Copy, Clone)]
386pub struct ResolvedSymbol<'obj> {
387 pub obj: &'obj Object,
388 pub symbol_index: usize,
389 pub symbol: &'obj Symbol,
390 pub section_index: usize,
391 pub section: &'obj Section,
392 pub data: &'obj [u8],
393}
394
395#[derive(Debug, Copy, Clone)]
396pub struct ResolvedInstructionRef<'obj> {
397 pub ins_ref: InstructionRef,
398 pub symbol_index: usize,
399 pub symbol: &'obj Symbol,
400 pub section_index: usize,
401 pub section: &'obj Section,
402 pub code: &'obj [u8],
403 pub relocation: Option<ResolvedRelocation<'obj>>,
404}
405
406static DUMMY_SYMBOL: Symbol = Symbol {
407 name: String::new(),
408 demangled_name: None,
409 normalized_name: None,
410 address: 0,
411 size: 0,
412 kind: SymbolKind::Unknown,
413 section: None,
414 flags: SymbolFlagSet::empty(),
415 align: None,
416 virtual_address: None,
417};
418
419static DUMMY_SECTION: Section = Section {
420 id: String::new(),
421 name: String::new(),
422 address: 0,
423 size: 0,
424 kind: SectionKind::Unknown,
425 data: SectionData(Vec::new()),
426 flags: SectionFlagSet::empty(),
427 align: None,
428 relocations: Vec::new(),
429 line_info: BTreeMap::new(),
430 virtual_address: None,
431};
432
433impl Default for ResolvedInstructionRef<'_> {
434 fn default() -> Self {
435 Self {
436 ins_ref: InstructionRef::default(),
437 symbol_index: 0,
438 symbol: &DUMMY_SYMBOL,
439 section_index: 0,
440 section: &DUMMY_SECTION,
441 code: &[],
442 relocation: None,
443 }
444 }
445}