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