1use alloc::{
2 borrow::Cow,
3 collections::BTreeSet,
4 format,
5 string::{String, ToString},
6 vec::Vec,
7};
8use core::cmp::Ordering;
9
10use anyhow::Result;
11use itertools::Itertools;
12use regex::Regex;
13
14use crate::{
15 arch::LiteralInfo,
16 diff::{
17 DataDiffKind, DataDiffRow, DiffObjConfig, InstructionDiffKind, InstructionDiffRow,
18 ObjectDiff, SymbolDiff, data::resolve_relocation,
19 },
20 obj::{
21 FlowAnalysisValue, InstructionArg, InstructionArgValue, Object, ParsedInstruction,
22 ResolvedInstructionRef, ResolvedRelocation, SectionFlag, SectionKind, Symbol, SymbolFlag,
23 SymbolKind,
24 },
25};
26
27#[derive(Debug, Clone)]
28pub enum DiffText<'a> {
29 Basic(&'a str),
31 Line(u32),
33 Address(u64),
35 Opcode(&'a str, u16),
37 Argument(InstructionArgValue<'a>),
39 BranchDest(u64),
41 BranchArrow(u32),
43 Symbol(&'a Symbol),
45 Addend(i64),
47 Spacing(u8),
49 Eol,
51}
52
53#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Hash)]
54pub enum DiffTextColor {
55 #[default]
56 Normal, Dim, Bright, DataFlow, Replace, Delete, Insert, Rotating(u8),
64}
65
66#[derive(Debug, Clone)]
67pub struct DiffTextSegment<'a> {
68 pub text: DiffText<'a>,
69 pub color: DiffTextColor,
70 pub pad_to: u8,
71}
72
73impl<'a> DiffTextSegment<'a> {
74 #[inline(always)]
75 pub fn basic(text: &'a str, color: DiffTextColor) -> Self {
76 Self { text: DiffText::Basic(text), color, pad_to: 0 }
77 }
78
79 #[inline(always)]
80 pub fn spacing(spaces: u8) -> Self {
81 Self { text: DiffText::Spacing(spaces), color: DiffTextColor::Normal, pad_to: 0 }
82 }
83}
84
85const EOL_SEGMENT: DiffTextSegment<'static> =
86 DiffTextSegment { text: DiffText::Eol, color: DiffTextColor::Normal, pad_to: 0 };
87
88#[derive(Debug, Default, Clone)]
89pub enum HighlightKind {
90 #[default]
91 None,
92 Opcode(u16),
93 Argument(InstructionArgValue<'static>),
94 Symbol(String),
95 Address(u64),
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum InstructionPart<'a> {
100 Basic(Cow<'a, str>),
101 Opcode(Cow<'a, str>, u16),
102 Arg(InstructionArg<'a>),
103 Separator,
104}
105
106impl<'a> InstructionPart<'a> {
107 #[inline(always)]
108 pub fn basic<T>(s: T) -> Self
109 where T: Into<Cow<'a, str>> {
110 InstructionPart::Basic(s.into())
111 }
112
113 #[inline(always)]
114 pub fn opcode<T>(s: T, o: u16) -> Self
115 where T: Into<Cow<'a, str>> {
116 InstructionPart::Opcode(s.into(), o)
117 }
118
119 #[inline(always)]
120 pub fn opaque<T>(s: T) -> Self
121 where T: Into<Cow<'a, str>> {
122 InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Opaque(s.into())))
123 }
124
125 #[inline(always)]
126 pub fn signed<T>(v: T) -> InstructionPart<'static>
127 where T: Into<i64> {
128 InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Signed(v.into())))
129 }
130
131 #[inline(always)]
132 pub fn unsigned<T>(v: T) -> InstructionPart<'static>
133 where T: Into<u64> {
134 InstructionPart::Arg(InstructionArg::Value(InstructionArgValue::Unsigned(v.into())))
135 }
136
137 #[inline(always)]
138 pub fn branch_dest<T>(v: T) -> InstructionPart<'static>
139 where T: Into<u64> {
140 InstructionPart::Arg(InstructionArg::BranchDest(v.into()))
141 }
142
143 #[inline(always)]
144 pub fn reloc() -> InstructionPart<'static> { InstructionPart::Arg(InstructionArg::Reloc) }
145
146 #[inline(always)]
147 pub fn separator() -> InstructionPart<'static> { InstructionPart::Separator }
148
149 pub fn into_static(self) -> InstructionPart<'static> {
150 match self {
151 InstructionPart::Basic(s) => InstructionPart::Basic(Cow::Owned(s.into_owned())),
152 InstructionPart::Opcode(s, o) => InstructionPart::Opcode(Cow::Owned(s.into_owned()), o),
153 InstructionPart::Arg(a) => InstructionPart::Arg(a.into_static()),
154 InstructionPart::Separator => InstructionPart::Separator,
155 }
156 }
157}
158
159pub fn display_row(
160 obj: &Object,
161 symbol_index: usize,
162 ins_row: &InstructionDiffRow,
163 diff_config: &DiffObjConfig,
164 mut cb: impl FnMut(DiffTextSegment) -> Result<()>,
165) -> Result<()> {
166 let Some(ins_ref) = ins_row.ins_ref else {
167 cb(EOL_SEGMENT)?;
168 return Ok(());
169 };
170 let Some(resolved) = obj.resolve_instruction_ref(symbol_index, ins_ref) else {
171 cb(DiffTextSegment::basic("<invalid>", DiffTextColor::Delete))?;
172 cb(EOL_SEGMENT)?;
173 return Ok(());
174 };
175 let base_color = match ins_row.kind {
176 InstructionDiffKind::Replace => DiffTextColor::Replace,
177 InstructionDiffKind::Delete => DiffTextColor::Delete,
178 InstructionDiffKind::Insert => DiffTextColor::Insert,
179 _ => DiffTextColor::Normal,
180 };
181 if let Some(line) = resolved.section.line_info.range(..=ins_ref.address).last().map(|(_, &b)| b)
182 {
183 cb(DiffTextSegment { text: DiffText::Line(line), color: DiffTextColor::Dim, pad_to: 5 })?;
184 }
185 cb(DiffTextSegment {
186 text: DiffText::Address(ins_ref.address.saturating_sub(resolved.symbol.address)),
187 color: DiffTextColor::Dim,
188 pad_to: 5,
189 })?;
190 if let Some(branch) = &ins_row.branch_from {
191 let ins_idx = branch.ins_idx[0];
193 cb(DiffTextSegment {
194 text: DiffText::BranchArrow(ins_idx),
195 color: DiffTextColor::Rotating(branch.branch_idx as u8),
196 pad_to: 0,
197 })?;
198 } else {
199 cb(DiffTextSegment::spacing(4))?;
200 }
201 let mut arg_idx = 0;
202 let mut displayed_relocation = false;
203 let analysis_result = if diff_config.show_data_flow {
204 obj.get_flow_analysis_result(resolved.symbol)
205 } else {
206 None
207 };
208 obj.arch.display_instruction(resolved, diff_config, &mut |part| match part {
209 InstructionPart::Basic(text) => {
210 if text.chars().all(|c| c == ' ') {
211 cb(DiffTextSegment::spacing(text.len() as u8))
212 } else {
213 cb(DiffTextSegment::basic(&text, base_color))
214 }
215 }
216 InstructionPart::Opcode(mnemonic, opcode) => cb(DiffTextSegment {
217 text: DiffText::Opcode(mnemonic.as_ref(), opcode),
218 color: match ins_row.kind {
219 InstructionDiffKind::OpMismatch => DiffTextColor::Replace,
220 _ => base_color,
221 },
222 pad_to: 10,
223 }),
224 InstructionPart::Arg(arg) => {
225 let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
226 arg_idx += 1;
227 if arg == InstructionArg::Reloc {
228 displayed_relocation = true;
229 }
230 let data_flow_value =
231 analysis_result.and_then(|result|
232 result.get_argument_value_at_address(
233 ins_ref.address, (arg_idx - 1) as u8));
234 match (arg, data_flow_value, resolved.ins_ref.branch_dest) {
235 (InstructionArg::Value(_) | InstructionArg::Reloc, Some(FlowAnalysisValue::Text(text)), _) => {
237 cb(DiffTextSegment {
238 text: DiffText::Argument(InstructionArgValue::Opaque(Cow::Borrowed(text))),
239 color: DiffTextColor::DataFlow,
240 pad_to: 0,
241 })
242 },
243 (InstructionArg::Value(value), None, _) => {
244 let color = diff_index
245 .get()
246 .map_or(base_color, |i| DiffTextColor::Rotating(i as u8));
247 cb(DiffTextSegment {
248 text: DiffText::Argument(value),
249 color,
250 pad_to: 0,
251 })
252 },
253 (InstructionArg::Reloc, _, None) => {
254 let resolved = resolved.relocation.unwrap();
255 let color = diff_index
256 .get()
257 .map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
258 cb(DiffTextSegment {
259 text: DiffText::Symbol(resolved.symbol),
260 color,
261 pad_to: 0,
262 })?;
263 if resolved.relocation.addend != 0 {
264 cb(DiffTextSegment {
265 text: DiffText::Addend(resolved.relocation.addend),
266 color,
267 pad_to: 0,
268 })?;
269 }
270 Ok(())
271 }
272 (InstructionArg::BranchDest(dest), _, _) |
273 (InstructionArg::Reloc, _, Some(dest)) => {
275 if let Some(addr) = dest.checked_sub(resolved.symbol.address) {
276 cb(DiffTextSegment {
277 text: DiffText::BranchDest(addr),
278 color: diff_index
279 .get()
280 .map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
281 pad_to: 0,
282 })
283 } else {
284 cb(DiffTextSegment {
285 text: DiffText::Argument(InstructionArgValue::Opaque(Cow::Borrowed(
286 "<invalid>",
287 ))),
288 color: diff_index
289 .get()
290 .map_or(base_color, |i| DiffTextColor::Rotating(i as u8)),
291 pad_to: 0,
292 })
293 }
294 }
295 }
296 }
297 InstructionPart::Separator => {
298 cb(DiffTextSegment::basic(diff_config.separator(), base_color))
299 }
300 })?;
301 if !displayed_relocation && let Some(resolved) = resolved.relocation {
303 cb(DiffTextSegment::basic(" <", base_color))?;
304 let diff_index = ins_row.arg_diff.get(arg_idx).copied().unwrap_or_default();
305 let color =
306 diff_index.get().map_or(DiffTextColor::Bright, |i| DiffTextColor::Rotating(i as u8));
307 cb(DiffTextSegment { text: DiffText::Symbol(resolved.symbol), color, pad_to: 0 })?;
308 if resolved.relocation.addend != 0 {
309 cb(DiffTextSegment {
310 text: DiffText::Addend(resolved.relocation.addend),
311 color,
312 pad_to: 0,
313 })?;
314 }
315 cb(DiffTextSegment::basic(">", base_color))?;
316 }
317 if let Some(branch) = &ins_row.branch_to {
318 cb(DiffTextSegment {
319 text: DiffText::BranchArrow(branch.ins_idx),
320 color: DiffTextColor::Rotating(branch.branch_idx as u8),
321 pad_to: 0,
322 })?;
323 }
324 cb(EOL_SEGMENT)?;
325 Ok(())
326}
327
328impl PartialEq<HighlightKind> for HighlightKind {
329 fn eq(&self, other: &HighlightKind) -> bool {
330 match (self, other) {
331 (HighlightKind::Opcode(a), HighlightKind::Opcode(b)) => a == b,
332 (HighlightKind::Argument(a), HighlightKind::Argument(b)) => a.loose_eq(b),
333 (HighlightKind::Symbol(a), HighlightKind::Symbol(b)) => a == b,
334 (HighlightKind::Address(a), HighlightKind::Address(b)) => a == b,
335 _ => false,
336 }
337 }
338}
339
340impl PartialEq<DiffText<'_>> for HighlightKind {
341 fn eq(&self, other: &DiffText) -> bool {
342 match (self, other) {
343 (HighlightKind::Opcode(a), DiffText::Opcode(_, b)) => a == b,
344 (HighlightKind::Argument(a), DiffText::Argument(b)) => a.loose_eq(b),
345 (HighlightKind::Symbol(a), DiffText::Symbol(b)) => a == &b.name,
346 (HighlightKind::Address(a), DiffText::Address(b) | DiffText::BranchDest(b)) => a == b,
347 _ => false,
348 }
349 }
350}
351
352impl PartialEq<HighlightKind> for DiffText<'_> {
353 fn eq(&self, other: &HighlightKind) -> bool { other.eq(self) }
354}
355
356impl From<&DiffText<'_>> for HighlightKind {
357 fn from(value: &DiffText<'_>) -> Self {
358 match value {
359 DiffText::Opcode(_, op) => HighlightKind::Opcode(*op),
360 DiffText::Argument(arg) => HighlightKind::Argument(arg.to_static()),
361 DiffText::Symbol(sym) => HighlightKind::Symbol(sym.name.to_string()),
362 DiffText::Address(addr) | DiffText::BranchDest(addr) => HighlightKind::Address(*addr),
363 _ => HighlightKind::None,
364 }
365 }
366}
367
368pub enum ContextItem {
369 Copy { value: String, label: Option<String>, copy_string: Option<String> },
370 Navigate { label: String, symbol_index: usize, kind: SymbolNavigationKind },
371 Separator,
372}
373
374#[derive(Debug, Clone, Default, Eq, PartialEq)]
375pub enum SymbolNavigationKind {
376 #[default]
377 Normal,
378 Extab,
379}
380
381#[derive(Debug, Clone, Default, Eq, PartialEq)]
382pub enum HoverItemColor {
383 #[default]
384 Normal, Emphasized, Special, Delete, Insert, }
390
391pub enum HoverItem {
392 Text { label: String, value: String, color: HoverItemColor },
393 Separator,
394}
395
396pub fn symbol_context(obj: &Object, symbol_index: usize) -> Vec<ContextItem> {
397 let Some(symbol) = obj.symbols.get(symbol_index) else {
398 return Vec::new();
399 };
400 let mut out = Vec::new();
401 out.push(ContextItem::Copy { value: symbol.name.clone(), label: None, copy_string: None });
402 if let Some(name) = &symbol.demangled_name {
403 out.push(ContextItem::Copy { value: name.clone(), label: None, copy_string: None });
404 }
405 if symbol.section.is_some()
406 && let Some(address) = symbol.virtual_address
407 {
408 out.push(ContextItem::Copy {
409 value: format!("{address:x}"),
410 label: Some("virtual address".to_string()),
411 copy_string: None,
412 });
413 }
414 out.append(&mut obj.arch.symbol_context(obj, symbol_index));
415 out
416}
417
418pub fn symbol_hover(
419 obj: &Object,
420 symbol_index: usize,
421 addend: i64,
422 override_color: Option<HoverItemColor>,
423) -> Vec<HoverItem> {
424 let Some(symbol) = obj.symbols.get(symbol_index) else {
425 return Vec::new();
426 };
427 let addend_str = match addend.cmp(&0i64) {
428 Ordering::Greater => format!("+{addend:x}"),
429 Ordering::Less => format!("-{:x}", -addend),
430 _ => String::new(),
431 };
432 let mut out = Vec::new();
433 out.push(HoverItem::Text {
434 label: "Name".into(),
435 value: format!("{}{}", symbol.name, addend_str),
436 color: override_color.clone().unwrap_or_default(),
437 });
438 if let Some(demangled_name) = &symbol.demangled_name {
439 out.push(HoverItem::Text {
440 label: "Demangled".into(),
441 value: demangled_name.into(),
442 color: override_color.clone().unwrap_or_default(),
443 });
444 }
445 if let Some(section) = symbol.section {
446 out.push(HoverItem::Text {
447 label: "Section".into(),
448 value: obj.sections[section].name.clone(),
449 color: override_color.clone().unwrap_or_default(),
450 });
451 out.push(HoverItem::Text {
452 label: "Address".into(),
453 value: format!("{:x}{}", symbol.address, addend_str),
454 color: override_color.clone().unwrap_or_default(),
455 });
456 if symbol.flags.contains(SymbolFlag::SizeInferred) {
457 out.push(HoverItem::Text {
458 label: "Size".into(),
459 value: format!("{:x} (inferred)", symbol.size),
460 color: override_color.clone().unwrap_or_default(),
461 });
462 } else {
463 out.push(HoverItem::Text {
464 label: "Size".into(),
465 value: format!("{:x}", symbol.size),
466 color: override_color.clone().unwrap_or_default(),
467 });
468 }
469 if let Some(align) = symbol.align {
470 out.push(HoverItem::Text {
471 label: "Alignment".into(),
472 value: align.get().to_string(),
473 color: override_color.clone().unwrap_or_default(),
474 });
475 }
476 if let Some(address) = symbol.virtual_address {
477 out.push(HoverItem::Text {
478 label: "Virtual address".into(),
479 value: format!("{address:x}"),
480 color: override_color.clone().unwrap_or(HoverItemColor::Special),
481 });
482 }
483 } else {
484 out.push(HoverItem::Text {
485 label: Default::default(),
486 value: "Extern".into(),
487 color: HoverItemColor::Emphasized,
488 });
489 }
490 out.append(&mut obj.arch.symbol_hover(obj, symbol_index));
491 out
492}
493
494pub fn relocation_context(
495 obj: &Object,
496 reloc: ResolvedRelocation,
497 ins: Option<ResolvedInstructionRef>,
498 diff_config: Option<&DiffObjConfig>,
499) -> Vec<ContextItem> {
500 let mut out = Vec::new();
501 out.append(&mut symbol_context(obj, reloc.relocation.target_symbol));
502 if let Some(ins) = ins {
503 let mut literals = display_ins_data_literals(obj, ins);
504 literals.retain(|lit_info| !lit_info.hidden(diff_config));
505 if !literals.is_empty() {
506 out.push(ContextItem::Separator);
507 for lit_info in literals {
508 out.push(ContextItem::Copy {
509 value: lit_info.literal,
510 label: lit_info.label_override,
511 copy_string: lit_info.copy_string,
512 });
513 }
514 }
515 }
516 out
517}
518
519pub fn data_row_hover(obj: &Object, diff_row: &DataDiffRow) -> Vec<HoverItem> {
520 let mut out = Vec::new();
521 let mut prev_reloc = None;
522 let mut first = true;
523 for reloc_diff in diff_row.relocations.iter() {
524 let reloc = &reloc_diff.reloc;
525 if prev_reloc == Some(reloc) {
526 continue;
530 }
531 prev_reloc = Some(reloc);
532
533 if first {
534 first = false;
535 } else {
536 out.push(HoverItem::Separator);
537 }
538
539 let reloc = resolve_relocation(&obj.symbols, reloc);
540 let color = match reloc_diff.kind {
541 DataDiffKind::None => HoverItemColor::Normal,
542 DataDiffKind::Replace => HoverItemColor::Special,
543 DataDiffKind::Delete => HoverItemColor::Delete,
544 DataDiffKind::Insert => HoverItemColor::Insert,
545 };
546 out.append(&mut relocation_hover(obj, reloc, Some(color)));
547 }
548 out
549}
550
551pub fn data_row_context(obj: &Object, diff_row: &DataDiffRow) -> Vec<ContextItem> {
552 let mut out = Vec::new();
553 let mut prev_reloc = None;
554 for reloc_diff in diff_row.relocations.iter() {
555 let reloc = &reloc_diff.reloc;
556 if prev_reloc == Some(reloc) {
557 continue;
561 }
562 prev_reloc = Some(reloc);
563
564 let reloc = resolve_relocation(&obj.symbols, reloc);
565 out.append(&mut relocation_context(obj, reloc, None, None));
566 }
567 out
568}
569
570pub fn relocation_hover(
571 obj: &Object,
572 reloc: ResolvedRelocation,
573 override_color: Option<HoverItemColor>,
574) -> Vec<HoverItem> {
575 let mut out = Vec::new();
576 if let Some(name) = obj.arch.reloc_name(reloc.relocation.flags) {
577 out.push(HoverItem::Text {
578 label: "Relocation".into(),
579 value: name.to_string(),
580 color: override_color.clone().unwrap_or_default(),
581 });
582 } else {
583 out.push(HoverItem::Text {
584 label: "Relocation".into(),
585 value: format!("<{:?}>", reloc.relocation.flags),
586 color: override_color.clone().unwrap_or_default(),
587 });
588 }
589 out.append(&mut symbol_hover(
590 obj,
591 reloc.relocation.target_symbol,
592 reloc.relocation.addend,
593 override_color,
594 ));
595 out
596}
597
598pub fn instruction_context(
599 obj: &Object,
600 resolved: ResolvedInstructionRef,
601 ins: &ParsedInstruction,
602 diff_config: &DiffObjConfig,
603) -> Vec<ContextItem> {
604 let mut out = Vec::new();
605 let mut hex_string = String::new();
606 for byte in resolved.code {
607 hex_string.push_str(&format!("{byte:02x}"));
608 }
609 out.push(ContextItem::Copy {
610 value: hex_string,
611 label: Some("instruction bytes".to_string()),
612 copy_string: None,
613 });
614 out.append(&mut obj.arch.instruction_context(obj, resolved));
615 if let Some(virtual_address) = resolved.symbol.virtual_address {
616 let offset = resolved.ins_ref.address - resolved.symbol.address;
617 out.push(ContextItem::Copy {
618 value: format!("{:x}", virtual_address + offset),
619 label: Some("virtual address".to_string()),
620 copy_string: None,
621 });
622 }
623 for arg in &ins.args {
624 if let InstructionArg::Value(arg) = arg {
625 out.push(ContextItem::Copy { value: arg.to_string(), label: None, copy_string: None });
626 match arg {
627 InstructionArgValue::Signed(v) => {
628 out.push(ContextItem::Copy {
629 value: v.to_string(),
630 label: None,
631 copy_string: None,
632 });
633 }
634 InstructionArgValue::Unsigned(v) => {
635 out.push(ContextItem::Copy {
636 value: v.to_string(),
637 label: None,
638 copy_string: None,
639 });
640 }
641 _ => {}
642 }
643 }
644 }
645 if let Some(reloc) = resolved.relocation {
646 out.push(ContextItem::Separator);
647 out.append(&mut relocation_context(obj, reloc, Some(resolved), Some(diff_config)));
648 }
649 out
650}
651
652pub fn instruction_hover(
653 obj: &Object,
654 resolved: ResolvedInstructionRef,
655 ins: &ParsedInstruction,
656 diff_config: &DiffObjConfig,
657) -> Vec<HoverItem> {
658 let mut out = Vec::new();
659 out.push(HoverItem::Text {
660 label: Default::default(),
661 value: format!("{:02x?}", resolved.code),
662 color: HoverItemColor::Normal,
663 });
664 out.append(&mut obj.arch.instruction_hover(obj, resolved));
665 if let Some(virtual_address) = resolved.symbol.virtual_address {
666 let offset = resolved.ins_ref.address - resolved.symbol.address;
667 out.push(HoverItem::Text {
668 label: "Virtual address".into(),
669 value: format!("{:x}", virtual_address + offset),
670 color: HoverItemColor::Special,
671 });
672 }
673 for arg in &ins.args {
674 if let InstructionArg::Value(arg) = arg {
675 match arg {
676 InstructionArgValue::Signed(v) => {
677 out.push(HoverItem::Text {
678 label: Default::default(),
679 value: format!("{arg} == {v}"),
680 color: HoverItemColor::Normal,
681 });
682 }
683 InstructionArgValue::Unsigned(v) => {
684 out.push(HoverItem::Text {
685 label: Default::default(),
686 value: format!("{arg} == {v}"),
687 color: HoverItemColor::Normal,
688 });
689 }
690 _ => {}
691 }
692 }
693 }
694 if let Some(reloc) = resolved.relocation {
695 out.push(HoverItem::Separator);
696 out.append(&mut relocation_hover(obj, reloc, None));
697 let bytes = obj.symbol_data(reloc.relocation.target_symbol).unwrap_or(&[]);
698 if let Some(ty) = obj.arch.guess_data_type(resolved, bytes) {
699 let mut literals = display_ins_data_literals(obj, resolved);
700 literals.retain(|lit_info| !lit_info.hidden(Some(diff_config)));
701 if !literals.is_empty() {
702 out.push(HoverItem::Separator);
703 for lit_info in literals {
704 out.push(HoverItem::Text {
705 label: lit_info.label_override.unwrap_or_else(|| ty.to_string()),
706 value: format!("{:?}", lit_info.literal),
707 color: HoverItemColor::Normal,
708 });
709 }
710 }
711 }
712 }
713 out
714}
715
716#[derive(Debug, Copy, Clone)]
717pub enum SymbolFilter<'a> {
718 None,
719 Search(&'a Regex),
720 Mapping(usize, Option<&'a Regex>),
721}
722
723fn symbol_matches_filter(
724 symbol: &Symbol,
725 diff: &SymbolDiff,
726 filter: SymbolFilter<'_>,
727 show_hidden_symbols: bool,
728) -> bool {
729 if symbol.section.is_none() && !symbol.flags.contains(SymbolFlag::Common) {
731 return false;
732 }
733 if !show_hidden_symbols
734 && (symbol.size == 0
735 || symbol.flags.contains(SymbolFlag::Hidden)
736 || symbol.flags.contains(SymbolFlag::Ignored))
737 {
738 return false;
739 }
740 match filter {
741 SymbolFilter::None => true,
742 SymbolFilter::Search(regex) => {
743 regex.is_match(&symbol.name)
744 || symbol.demangled_name.as_deref().is_some_and(|s| regex.is_match(s))
745 }
746 SymbolFilter::Mapping(symbol_ref, regex) => {
747 diff.target_symbol == Some(symbol_ref)
748 && regex.is_none_or(|r| {
749 r.is_match(&symbol.name)
750 || symbol.demangled_name.as_deref().is_some_and(|s| r.is_match(s))
751 })
752 }
753 }
754}
755
756#[derive(Debug, Clone, Copy, Eq, PartialEq)]
757pub struct SectionDisplaySymbol {
758 pub symbol: usize,
759 pub is_mapping_symbol: bool,
760}
761
762#[derive(Debug, Clone)]
763pub struct SectionDisplay {
764 pub id: String,
765 pub name: String,
766 pub size: u64,
767 pub match_percent: Option<f32>,
768 pub symbols: Vec<SectionDisplaySymbol>,
769 pub kind: SectionKind,
770}
771
772pub fn display_sections(
773 obj: &Object,
774 diff: &ObjectDiff,
775 filter: SymbolFilter<'_>,
776 show_hidden_symbols: bool,
777 show_mapped_symbols: bool,
778 reverse_fn_order: bool,
779) -> Vec<SectionDisplay> {
780 let mut mapping = BTreeSet::new();
781 let is_mapping_symbol = if let SymbolFilter::Mapping(_, _) = filter {
782 for mapping_diff in &diff.mapping_symbols {
783 let symbol = &obj.symbols[mapping_diff.symbol_index];
784 if !symbol_matches_filter(
785 symbol,
786 &mapping_diff.symbol_diff,
787 filter,
788 show_hidden_symbols,
789 ) {
790 continue;
791 }
792 if !show_mapped_symbols {
793 let symbol_diff = &diff.symbols[mapping_diff.symbol_index];
794 if symbol_diff.target_symbol.is_some() {
795 continue;
796 }
797 }
798 mapping.insert((symbol.section, mapping_diff.symbol_index));
799 }
800 true
801 } else {
802 for (symbol_idx, (symbol, symbol_diff)) in obj.symbols.iter().zip(&diff.symbols).enumerate()
803 {
804 if !symbol_matches_filter(symbol, symbol_diff, filter, show_hidden_symbols) {
805 continue;
806 }
807 mapping.insert((symbol.section, symbol_idx));
808 }
809 false
810 };
811 let num_sections = mapping.iter().map(|(section_idx, _)| *section_idx).dedup().count();
812 let mut sections = Vec::with_capacity(num_sections);
813 for (section_idx, group) in &mapping.iter().chunk_by(|(section_idx, _)| *section_idx) {
814 let mut symbols = group
815 .map(|&(_, symbol)| SectionDisplaySymbol { symbol, is_mapping_symbol })
816 .collect::<Vec<_>>();
817 if let Some(section_idx) = section_idx {
818 let section = &obj.sections[section_idx];
819 if section.kind == SectionKind::Unknown {
820 continue;
822 }
823 let section_diff = &diff.sections[section_idx];
824 let reverse_fn_order = section.kind == SectionKind::Code && reverse_fn_order;
825 if reverse_fn_order {
826 symbols.sort_by(|a, b| {
827 let a = &obj.symbols[a.symbol];
828 let b = &obj.symbols[b.symbol];
829 section_symbol_sort(a, b)
830 .then_with(|| b.address.cmp(&a.address))
831 .then_with(|| a.size.cmp(&b.size))
832 });
833 }
834 sections.push(SectionDisplay {
835 id: section.id.clone(),
836 name: if section.flags.contains(SectionFlag::Combined) {
837 format!("{} [combined]", section.name)
838 } else {
839 section.name.clone()
840 },
841 size: section.size,
842 match_percent: section_diff.match_percent,
843 symbols,
844 kind: section.kind,
845 });
846 } else {
847 sections.push(SectionDisplay {
849 id: ".comm".to_string(),
850 name: ".comm".to_string(),
851 size: 0,
852 match_percent: None,
853 symbols,
854 kind: SectionKind::Common,
855 });
856 }
857 }
858 sections.sort_by(|a, b| a.name.cmp(&b.name));
859 sections
860}
861
862fn section_symbol_sort(a: &Symbol, b: &Symbol) -> Ordering {
863 if a.kind == SymbolKind::Section {
864 if b.kind != SymbolKind::Section {
865 return Ordering::Less;
866 }
867 } else if b.kind == SymbolKind::Section {
868 return Ordering::Greater;
869 }
870 Ordering::Equal
871}
872
873pub fn display_ins_data_labels(obj: &Object, resolved: ResolvedInstructionRef) -> Vec<String> {
874 let Some(reloc) = resolved.relocation else {
875 return Vec::new();
876 };
877 if reloc.relocation.addend < 0 || reloc.relocation.addend as u64 >= reloc.symbol.size {
878 return Vec::new();
879 }
880 let Some(data) = obj.symbol_data(reloc.relocation.target_symbol) else {
881 return Vec::new();
882 };
883 let bytes = &data[reloc.relocation.addend as usize..];
884 obj.arch
885 .guess_data_type(resolved, bytes)
886 .map(|ty| ty.display_labels(obj.endianness, bytes))
887 .unwrap_or_default()
888}
889
890pub fn display_ins_data_literals(
891 obj: &Object,
892 resolved: ResolvedInstructionRef,
893) -> Vec<LiteralInfo> {
894 let Some(reloc) = resolved.relocation else {
895 return Vec::new();
896 };
897 if reloc.relocation.addend < 0 || reloc.relocation.addend as u64 >= reloc.symbol.size {
898 return Vec::new();
899 }
900 let Some(data) = obj.symbol_data(reloc.relocation.target_symbol) else {
901 return Vec::new();
902 };
903 let bytes = &data[reloc.relocation.addend as usize..];
904 obj.arch
905 .guess_data_type(resolved, bytes)
906 .map(|ty| ty.display_literals(obj.endianness, bytes))
907 .unwrap_or_default()
908}