mirror of https://github.com/encounter/objdiff.git
Exception table diff view (#82)
* Basic integration * Implement basic right click option Needs lotsa work * nothing to worry about * Convert extab diff to separate view * Make clippy and fmt shut up * Make clippy fmt shut up for real this time * Print extab/extabindex symbol names in extab view * I hate fmt * Basic integration * Implement basic right click option Needs lotsa work * nothing to worry about * Convert extab diff to separate view * Make clippy and fmt shut up * Make clippy fmt shut up for real this time * Print extab/extabindex symbol names in extab view * I hate fmt * Fix scroll position not being maintained from extab view * Silly me * Add rlwinm decoder window * Remove extra files * Create Cargo.lock * Show extab symbol names in hover window * Appease fmt * Update symbol_diff.rs * Update symbol_diff.rs * Get extab symbol from extabindex relocations instead * Update Cargo.lock * Update Cargo.lock
This commit is contained in:
parent
9f71ce9fea
commit
75b0e7d9e5
|
@ -22,3 +22,4 @@ android.keystore
|
||||||
*.frag
|
*.frag
|
||||||
*.vert
|
*.vert
|
||||||
*.metal
|
*.metal
|
||||||
|
.vscode/launch.json
|
||||||
|
|
|
@ -970,6 +970,15 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285"
|
checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cwextab"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a92e840be31d11ead5f357b8fc503133d3657845d0ccf539afcc7d1212fad226"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "d3d12"
|
name = "d3d12"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
|
@ -2815,6 +2824,7 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"cpp_demangle",
|
"cpp_demangle",
|
||||||
"cwdemangle",
|
"cwdemangle",
|
||||||
|
"cwextab",
|
||||||
"filetime",
|
"filetime",
|
||||||
"flagset",
|
"flagset",
|
||||||
"gimli 0.29.0",
|
"gimli 0.29.0",
|
||||||
|
@ -2846,6 +2856,7 @@ dependencies = [
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"const_format",
|
"const_format",
|
||||||
"cwdemangle",
|
"cwdemangle",
|
||||||
|
"cwextab",
|
||||||
"dirs",
|
"dirs",
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui",
|
"egui",
|
||||||
|
|
|
@ -17,7 +17,7 @@ any-arch = [] # Implicit, used to check if any arch is enabled
|
||||||
config = ["globset", "semver", "serde_json", "serde_yaml"]
|
config = ["globset", "semver", "serde_json", "serde_yaml"]
|
||||||
dwarf = ["gimli"]
|
dwarf = ["gimli"]
|
||||||
mips = ["any-arch", "rabbitizer"]
|
mips = ["any-arch", "rabbitizer"]
|
||||||
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
|
ppc = ["any-arch", "cwdemangle", "cwextab", "ppc750cl"]
|
||||||
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
|
||||||
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ gimli = { version = "0.29.0", default-features = false, features = ["read-all"],
|
||||||
|
|
||||||
# ppc
|
# ppc
|
||||||
cwdemangle = { version = "1.0.0", optional = true }
|
cwdemangle = { version = "1.0.0", optional = true }
|
||||||
|
cwextab = { version = "0.2.3", optional = true }
|
||||||
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "6cbd7d888c7082c2c860f66cbb9848d633f753ed", optional = true }
|
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "6cbd7d888c7082c2c860f66cbb9848d633f753ed", optional = true }
|
||||||
|
|
||||||
# mips
|
# mips
|
||||||
|
|
|
@ -3,6 +3,7 @@ pub mod split_meta;
|
||||||
|
|
||||||
use std::{borrow::Cow, collections::BTreeMap, fmt, path::PathBuf};
|
use std::{borrow::Cow, collections::BTreeMap, fmt, path::PathBuf};
|
||||||
|
|
||||||
|
use cwextab::*;
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use flagset::{flags, FlagSet};
|
use flagset::{flags, FlagSet};
|
||||||
use object::RelocationFlags;
|
use object::RelocationFlags;
|
||||||
|
@ -113,6 +114,9 @@ pub struct ObjIns {
|
||||||
pub struct ObjSymbol {
|
pub struct ObjSymbol {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub demangled_name: Option<String>,
|
pub demangled_name: Option<String>,
|
||||||
|
pub has_extab: bool,
|
||||||
|
pub extab_name: Option<String>,
|
||||||
|
pub extabindex_name: Option<String>,
|
||||||
pub address: u64,
|
pub address: u64,
|
||||||
pub section_address: u64,
|
pub section_address: u64,
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
|
@ -123,6 +127,13 @@ pub struct ObjSymbol {
|
||||||
pub virtual_address: Option<u64>,
|
pub virtual_address: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ObjExtab {
|
||||||
|
pub func: ObjSymbol,
|
||||||
|
pub data: ExceptionTableData,
|
||||||
|
pub dtors: Vec<ObjSymbol>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ObjInfo {
|
pub struct ObjInfo {
|
||||||
pub arch: Box<dyn ObjArch>,
|
pub arch: Box<dyn ObjArch>,
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
|
@ -130,6 +141,8 @@ pub struct ObjInfo {
|
||||||
pub sections: Vec<ObjSection>,
|
pub sections: Vec<ObjSection>,
|
||||||
/// Common BSS symbols
|
/// Common BSS symbols
|
||||||
pub common: Vec<ObjSymbol>,
|
pub common: Vec<ObjSymbol>,
|
||||||
|
/// Exception tables
|
||||||
|
pub extab: Option<Vec<ObjExtab>>,
|
||||||
/// Split object metadata (.note.split section)
|
/// Split object metadata (.note.split section)
|
||||||
pub split_meta: Option<SplitMeta>,
|
pub split_meta: Option<SplitMeta>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,12 @@ use std::{collections::HashSet, fs, io::Cursor, path::Path};
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||||
use byteorder::{BigEndian, ReadBytesExt};
|
use byteorder::{BigEndian, ReadBytesExt};
|
||||||
|
use cwextab::decode_extab;
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use flagset::Flags;
|
use flagset::Flags;
|
||||||
use object::{
|
use object::{
|
||||||
BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget, SectionIndex,
|
Architecture, BinaryFormat, File, Object, ObjectSection, ObjectSymbol, RelocationTarget,
|
||||||
SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection,
|
SectionIndex, SectionKind, Symbol, SymbolKind, SymbolScope, SymbolSection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -14,7 +15,8 @@ use crate::{
|
||||||
diff::DiffObjConfig,
|
diff::DiffObjConfig,
|
||||||
obj::{
|
obj::{
|
||||||
split_meta::{SplitMeta, SPLITMETA_SECTION},
|
split_meta::{SplitMeta, SPLITMETA_SECTION},
|
||||||
ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
|
ObjExtab, ObjInfo, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
|
||||||
|
ObjSymbolFlags,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -71,6 +73,9 @@ fn to_obj_symbol(
|
||||||
Ok(ObjSymbol {
|
Ok(ObjSymbol {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
demangled_name,
|
demangled_name,
|
||||||
|
has_extab: false,
|
||||||
|
extab_name: None,
|
||||||
|
extabindex_name: None,
|
||||||
address,
|
address,
|
||||||
section_address,
|
section_address,
|
||||||
size: symbol.size(),
|
size: symbol.size(),
|
||||||
|
@ -170,6 +175,111 @@ fn common_symbols(
|
||||||
.collect::<Result<Vec<ObjSymbol>>>()
|
.collect::<Result<Vec<ObjSymbol>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn section_by_name<'a>(sections: &'a mut [ObjSection], name: &str) -> Option<&'a mut ObjSection> {
|
||||||
|
sections.iter_mut().find(|section| section.name == name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exception_tables(
|
||||||
|
sections: &mut [ObjSection],
|
||||||
|
obj_file: &File<'_>,
|
||||||
|
) -> Result<Option<Vec<ObjExtab>>> {
|
||||||
|
//PowerPC only
|
||||||
|
if obj_file.architecture() != Architecture::PowerPc {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Find the extab/extabindex sections
|
||||||
|
let extab_section = match section_by_name(sections, "extab") {
|
||||||
|
Some(section) => section.clone(),
|
||||||
|
None => {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let extabindex_section = match section_by_name(sections, "extabindex") {
|
||||||
|
Some(section) => section.clone(),
|
||||||
|
None => {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let text_section = match section_by_name(sections, ".text") {
|
||||||
|
Some(section) => section,
|
||||||
|
None => bail!(".text section is somehow missing, this should not happen"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result: Vec<ObjExtab> = vec![];
|
||||||
|
let extab_symbol_count = extab_section.symbols.len();
|
||||||
|
let extabindex_symbol_count = extabindex_section.symbols.len();
|
||||||
|
let extab_reloc_count = extab_section.relocations.len();
|
||||||
|
let table_count = extab_symbol_count;
|
||||||
|
let mut extab_reloc_index: usize = 0;
|
||||||
|
|
||||||
|
//Make sure that the number of symbols in the extab/extabindex section matches. If not, exit early
|
||||||
|
if extab_symbol_count != extabindex_symbol_count {
|
||||||
|
bail!("Extab/Extabindex symbol counts do not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert the extab/extabindex section data
|
||||||
|
|
||||||
|
//Go through each extabindex entry
|
||||||
|
for i in 0..table_count {
|
||||||
|
let extabindex = &extabindex_section.symbols[i];
|
||||||
|
|
||||||
|
/* Get the function symbol and extab symbol from the extabindex relocations array. Each extabindex
|
||||||
|
entry has two relocations (the first for the function, the second for the extab entry) */
|
||||||
|
let extab_func = extabindex_section.relocations[i * 2].target.clone();
|
||||||
|
let extab = &extabindex_section.relocations[(i * 2) + 1].target;
|
||||||
|
|
||||||
|
let extab_start_addr = extab.address;
|
||||||
|
let extab_end_addr = extab_start_addr + extab.size;
|
||||||
|
|
||||||
|
//Find the function in the text section, and set the has extab flag
|
||||||
|
for i in 0..text_section.symbols.len() {
|
||||||
|
let func = &mut text_section.symbols[i];
|
||||||
|
if func.name == extab_func.name {
|
||||||
|
func.has_extab = true;
|
||||||
|
func.extab_name = Some(extab.name.clone());
|
||||||
|
func.extabindex_name = Some(extabindex.name.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Iterate through the list of extab relocations, continuing until we hit a relocation
|
||||||
|
that isn't within the current extab symbol. Get the target dtor function symbol from
|
||||||
|
each relocation used, and add them to the list. */
|
||||||
|
let mut dtors: Vec<ObjSymbol> = vec![];
|
||||||
|
|
||||||
|
while extab_reloc_index < extab_reloc_count {
|
||||||
|
let extab_reloc = &extab_section.relocations[extab_reloc_index];
|
||||||
|
//If the current entry is past the current extab table, stop here
|
||||||
|
if extab_reloc.address >= extab_end_addr {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise, the current relocation is used by the current table
|
||||||
|
dtors.push(extab_reloc.target.clone());
|
||||||
|
//Go to the next entry
|
||||||
|
extab_reloc_index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Decode the extab data
|
||||||
|
let start_index = extab_start_addr as usize;
|
||||||
|
let end_index = extab_end_addr as usize;
|
||||||
|
let extab_data = extab_section.data[start_index..end_index].try_into().unwrap();
|
||||||
|
let data = match decode_extab(extab_data) {
|
||||||
|
Some(decoded_data) => decoded_data,
|
||||||
|
None => {
|
||||||
|
log::warn!("Exception table decoding failed for function {}", extab_func.name);
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Add the new entry to the list
|
||||||
|
let entry = ObjExtab { func: extab_func, data, dtors };
|
||||||
|
result.push(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Some(result))
|
||||||
|
}
|
||||||
|
|
||||||
fn find_section_symbol(
|
fn find_section_symbol(
|
||||||
arch: &dyn ObjArch,
|
arch: &dyn ObjArch,
|
||||||
obj_file: &File<'_>,
|
obj_file: &File<'_>,
|
||||||
|
@ -205,6 +315,9 @@ fn find_section_symbol(
|
||||||
Ok(ObjSymbol {
|
Ok(ObjSymbol {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
demangled_name: None,
|
demangled_name: None,
|
||||||
|
has_extab: false,
|
||||||
|
extab_name: None,
|
||||||
|
extabindex_name: None,
|
||||||
address: offset,
|
address: offset,
|
||||||
section_address: address - section.address(),
|
section_address: address - section.address(),
|
||||||
size: 0,
|
size: 0,
|
||||||
|
@ -367,6 +480,9 @@ fn update_combined_symbol(symbol: ObjSymbol, address_change: i64) -> Result<ObjS
|
||||||
Ok(ObjSymbol {
|
Ok(ObjSymbol {
|
||||||
name: symbol.name,
|
name: symbol.name,
|
||||||
demangled_name: symbol.demangled_name,
|
demangled_name: symbol.demangled_name,
|
||||||
|
has_extab: symbol.has_extab,
|
||||||
|
extab_name: symbol.extab_name,
|
||||||
|
extabindex_name: symbol.extabindex_name,
|
||||||
address: (symbol.address as i64 + address_change).try_into()?,
|
address: (symbol.address as i64 + address_change).try_into()?,
|
||||||
section_address: (symbol.section_address as i64 + address_change).try_into()?,
|
section_address: (symbol.section_address as i64 + address_change).try_into()?,
|
||||||
size: symbol.size,
|
size: symbol.size,
|
||||||
|
@ -482,7 +598,8 @@ pub fn read(obj_path: &Path, config: &DiffObjConfig) -> Result<ObjInfo> {
|
||||||
}
|
}
|
||||||
line_info(&obj_file, &mut sections)?;
|
line_info(&obj_file, &mut sections)?;
|
||||||
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
let common = common_symbols(arch.as_ref(), &obj_file, split_meta.as_ref())?;
|
||||||
Ok(ObjInfo { arch, path: obj_path.to_owned(), timestamp, sections, common, split_meta })
|
let extab = exception_tables(&mut sections, &obj_file)?;
|
||||||
|
Ok(ObjInfo { arch, path: obj_path.to_owned(), timestamp, sections, common, extab, split_meta })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_function(obj_path: &Path, symbol_name: &str) -> Result<bool> {
|
pub fn has_function(obj_path: &Path, symbol_name: &str) -> Result<bool> {
|
||||||
|
|
|
@ -30,6 +30,7 @@ cfg-if = "1.0.0"
|
||||||
const_format = "0.2.32"
|
const_format = "0.2.32"
|
||||||
cwdemangle = "1.0.0"
|
cwdemangle = "1.0.0"
|
||||||
rlwinmdec = "1.0.1"
|
rlwinmdec = "1.0.1"
|
||||||
|
cwextab = "0.2.3"
|
||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
egui = "0.27.2"
|
egui = "0.27.2"
|
||||||
egui_extras = "0.27.2"
|
egui_extras = "0.27.2"
|
||||||
|
|
|
@ -35,6 +35,7 @@ use crate::{
|
||||||
data_diff::data_diff_ui,
|
data_diff::data_diff_ui,
|
||||||
debug::debug_window,
|
debug::debug_window,
|
||||||
demangle::{demangle_window, DemangleViewState},
|
demangle::{demangle_window, DemangleViewState},
|
||||||
|
extab_diff::extab_diff_ui,
|
||||||
frame_history::FrameHistory,
|
frame_history::FrameHistory,
|
||||||
function_diff::function_diff_ui,
|
function_diff::function_diff_ui,
|
||||||
graphics::{graphics_window, GraphicsConfig, GraphicsViewState},
|
graphics::{graphics_window, GraphicsConfig, GraphicsViewState},
|
||||||
|
@ -591,6 +592,10 @@ impl eframe::App for App {
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
data_diff_ui(ui, diff_state, appearance);
|
data_diff_ui(ui, diff_state, appearance);
|
||||||
});
|
});
|
||||||
|
} else if diff_state.current_view == View::ExtabDiff && build_success {
|
||||||
|
egui::CentralPanel::default().show(ctx, |ui| {
|
||||||
|
extab_diff_ui(ui, diff_state, appearance);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
egui::SidePanel::left("side_panel").show(ctx, |ui| {
|
||||||
egui::ScrollArea::both().show(ui, |ui| {
|
egui::ScrollArea::both().show(ui, |ui| {
|
||||||
|
|
|
@ -0,0 +1,218 @@
|
||||||
|
use egui::{text::LayoutJob, Align, Layout, ScrollArea, Ui, Vec2};
|
||||||
|
use egui_extras::{Size, StripBuilder};
|
||||||
|
use objdiff_core::{
|
||||||
|
diff::ObjDiff,
|
||||||
|
obj::{ObjExtab, ObjInfo, ObjSymbol, SymbolRef},
|
||||||
|
};
|
||||||
|
use time::format_description;
|
||||||
|
|
||||||
|
use crate::views::{
|
||||||
|
appearance::Appearance,
|
||||||
|
symbol_diff::{match_color_for_symbol, DiffViewState, SymbolRefByName, View},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn find_symbol(obj: &ObjInfo, selected_symbol: &SymbolRefByName) -> Option<SymbolRef> {
|
||||||
|
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||||
|
for (symbol_idx, symbol) in section.symbols.iter().enumerate() {
|
||||||
|
if symbol.name == selected_symbol.symbol_name {
|
||||||
|
return Some(SymbolRef { section_idx, symbol_idx });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_extab(extab: &ObjExtab) -> String {
|
||||||
|
let mut text = String::from("");
|
||||||
|
|
||||||
|
let mut dtor_names: Vec<&str> = vec![];
|
||||||
|
for dtor in &extab.dtors {
|
||||||
|
//For each function name, use the demangled name by default,
|
||||||
|
//and if not available fallback to the original name
|
||||||
|
let name = match &dtor.demangled_name {
|
||||||
|
Some(demangled_name) => demangled_name,
|
||||||
|
None => &dtor.name,
|
||||||
|
};
|
||||||
|
dtor_names.push(name.as_str());
|
||||||
|
}
|
||||||
|
if let Some(decoded) = extab.data.to_string(&dtor_names) {
|
||||||
|
text += decoded.as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
text
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_extab_entry(obj: &ObjInfo, symbol: &ObjSymbol) -> Option<ObjExtab> {
|
||||||
|
if let Some(extab_array) = &obj.extab {
|
||||||
|
for extab_entry in extab_array {
|
||||||
|
if extab_entry.func.name == symbol.name {
|
||||||
|
return Some(extab_entry.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extab_text_ui(
|
||||||
|
ui: &mut Ui,
|
||||||
|
obj: &(ObjInfo, ObjDiff),
|
||||||
|
symbol_ref: SymbolRef,
|
||||||
|
appearance: &Appearance,
|
||||||
|
) -> Option<()> {
|
||||||
|
let (_section, symbol) = obj.0.section_symbol(symbol_ref);
|
||||||
|
|
||||||
|
if let Some(extab_entry) = find_extab_entry(&obj.0, symbol) {
|
||||||
|
let text = decode_extab(&extab_entry);
|
||||||
|
ui.colored_label(appearance.replace_color, &text);
|
||||||
|
return Some(());
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extab_ui(
|
||||||
|
ui: &mut Ui,
|
||||||
|
obj: Option<&(ObjInfo, ObjDiff)>,
|
||||||
|
selected_symbol: &SymbolRefByName,
|
||||||
|
appearance: &Appearance,
|
||||||
|
_left: bool,
|
||||||
|
) {
|
||||||
|
ScrollArea::both().auto_shrink([false, false]).show(ui, |ui| {
|
||||||
|
ui.scope(|ui| {
|
||||||
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
|
ui.style_mut().wrap = Some(false);
|
||||||
|
|
||||||
|
let symbol = obj.and_then(|(obj, _)| find_symbol(obj, selected_symbol));
|
||||||
|
|
||||||
|
if let (Some(object), Some(symbol_ref)) = (obj, symbol) {
|
||||||
|
extab_text_ui(ui, object, symbol_ref, appearance);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extab_diff_ui(ui: &mut egui::Ui, state: &mut DiffViewState, appearance: &Appearance) {
|
||||||
|
let (Some(result), Some(selected_symbol)) = (&state.build, &state.symbol_state.selected_symbol)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Header
|
||||||
|
let available_width = ui.available_width();
|
||||||
|
let column_width = available_width / 2.0;
|
||||||
|
ui.allocate_ui_with_layout(
|
||||||
|
Vec2 { x: available_width, y: 100.0 },
|
||||||
|
Layout::left_to_right(Align::Min),
|
||||||
|
|ui| {
|
||||||
|
// Left column
|
||||||
|
ui.allocate_ui_with_layout(
|
||||||
|
Vec2 { x: column_width, y: 100.0 },
|
||||||
|
Layout::top_down(Align::Min),
|
||||||
|
|ui| {
|
||||||
|
ui.set_width(column_width);
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui.button("⏴ Back").clicked() {
|
||||||
|
state.current_view = View::SymbolDiff;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let name = selected_symbol
|
||||||
|
.demangled_symbol_name
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or(&selected_symbol.symbol_name);
|
||||||
|
let mut job = LayoutJob::simple(
|
||||||
|
name.to_string(),
|
||||||
|
appearance.code_font.clone(),
|
||||||
|
appearance.highlight_color,
|
||||||
|
column_width,
|
||||||
|
);
|
||||||
|
job.wrap.break_anywhere = true;
|
||||||
|
job.wrap.max_rows = 1;
|
||||||
|
ui.label(job);
|
||||||
|
|
||||||
|
ui.scope(|ui| {
|
||||||
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
|
ui.label("Diff target:");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Right column
|
||||||
|
ui.allocate_ui_with_layout(
|
||||||
|
Vec2 { x: column_width, y: 100.0 },
|
||||||
|
Layout::top_down(Align::Min),
|
||||||
|
|ui| {
|
||||||
|
ui.set_width(column_width);
|
||||||
|
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
if ui
|
||||||
|
.add_enabled(!state.build_running, egui::Button::new("Build"))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
state.queue_build = true;
|
||||||
|
}
|
||||||
|
ui.scope(|ui| {
|
||||||
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
|
ui.style_mut().wrap = Some(false);
|
||||||
|
if state.build_running {
|
||||||
|
ui.colored_label(appearance.replace_color, "Building…");
|
||||||
|
} else {
|
||||||
|
ui.label("Last built:");
|
||||||
|
let format =
|
||||||
|
format_description::parse("[hour]:[minute]:[second]").unwrap();
|
||||||
|
ui.label(
|
||||||
|
result
|
||||||
|
.time
|
||||||
|
.to_offset(appearance.utc_offset)
|
||||||
|
.format(&format)
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.scope(|ui| {
|
||||||
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
|
if let Some(match_percent) = result
|
||||||
|
.second_obj
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|(obj, diff)| {
|
||||||
|
find_symbol(obj, selected_symbol).map(|sref| {
|
||||||
|
&diff.sections[sref.section_idx].symbols[sref.symbol_idx]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.and_then(|symbol| symbol.match_percent)
|
||||||
|
{
|
||||||
|
ui.colored_label(
|
||||||
|
match_color_for_symbol(match_percent, appearance),
|
||||||
|
&format!("{match_percent:.0}%"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
ui.colored_label(appearance.replace_color, "Missing");
|
||||||
|
}
|
||||||
|
ui.label("Diff base:");
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
// Table
|
||||||
|
StripBuilder::new(ui).size(Size::remainder()).vertical(|mut strip| {
|
||||||
|
strip.strip(|builder| {
|
||||||
|
builder.sizes(Size::remainder(), 2).horizontal(|mut strip| {
|
||||||
|
strip.cell(|ui| {
|
||||||
|
extab_ui(ui, result.first_obj.as_ref(), selected_symbol, appearance, true);
|
||||||
|
});
|
||||||
|
strip.cell(|ui| {
|
||||||
|
extab_ui(ui, result.second_obj.as_ref(), selected_symbol, appearance, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ pub(crate) mod config;
|
||||||
pub(crate) mod data_diff;
|
pub(crate) mod data_diff;
|
||||||
pub(crate) mod debug;
|
pub(crate) mod debug;
|
||||||
pub(crate) mod demangle;
|
pub(crate) mod demangle;
|
||||||
|
pub(crate) mod extab_diff;
|
||||||
pub(crate) mod file;
|
pub(crate) mod file;
|
||||||
pub(crate) mod frame_history;
|
pub(crate) mod frame_history;
|
||||||
pub(crate) mod function_diff;
|
pub(crate) mod function_diff;
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub enum View {
|
||||||
SymbolDiff,
|
SymbolDiff,
|
||||||
FunctionDiff,
|
FunctionDiff,
|
||||||
DataDiff,
|
DataDiff,
|
||||||
|
ExtabDiff,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
@ -57,6 +58,7 @@ pub struct SymbolViewState {
|
||||||
pub reverse_fn_order: bool,
|
pub reverse_fn_order: bool,
|
||||||
pub disable_reverse_fn_order: bool,
|
pub disable_reverse_fn_order: bool,
|
||||||
pub show_hidden_symbols: bool,
|
pub show_hidden_symbols: bool,
|
||||||
|
pub queue_extab_decode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiffViewState {
|
impl DiffViewState {
|
||||||
|
@ -131,7 +133,12 @@ pub fn match_color_for_symbol(match_percent: f32, appearance: &Appearance) -> Co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn symbol_context_menu_ui(ui: &mut Ui, symbol: &ObjSymbol) {
|
fn symbol_context_menu_ui(
|
||||||
|
ui: &mut Ui,
|
||||||
|
state: &mut SymbolViewState,
|
||||||
|
symbol: &ObjSymbol,
|
||||||
|
section: Option<&ObjSection>,
|
||||||
|
) {
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
|
||||||
ui.style_mut().wrap = Some(false);
|
ui.style_mut().wrap = Some(false);
|
||||||
|
@ -152,6 +159,17 @@ fn symbol_context_menu_ui(ui: &mut Ui, symbol: &ObjSymbol) {
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(section) = section {
|
||||||
|
if symbol.has_extab && ui.button("Decode exception table").clicked() {
|
||||||
|
state.queue_extab_decode = true;
|
||||||
|
state.selected_symbol = Some(SymbolRefByName {
|
||||||
|
symbol_name: symbol.name.clone(),
|
||||||
|
demangled_symbol_name: symbol.demangled_name.clone(),
|
||||||
|
section_name: section.name.clone(),
|
||||||
|
});
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +191,20 @@ fn symbol_hover_ui(ui: &mut Ui, symbol: &ObjSymbol, appearance: &Appearance) {
|
||||||
if let Some(address) = symbol.virtual_address {
|
if let Some(address) = symbol.virtual_address {
|
||||||
ui.colored_label(appearance.replace_color, format!("Virtual address: {:#x}", address));
|
ui.colored_label(appearance.replace_color, format!("Virtual address: {:#x}", address));
|
||||||
}
|
}
|
||||||
|
if symbol.has_extab {
|
||||||
|
if let (Some(extab_name), Some(extabindex_name)) =
|
||||||
|
(&symbol.extab_name, &symbol.extabindex_name)
|
||||||
|
{
|
||||||
|
ui.colored_label(
|
||||||
|
appearance.highlight_color,
|
||||||
|
format!("Extab Symbol: {}", extab_name),
|
||||||
|
);
|
||||||
|
ui.colored_label(
|
||||||
|
appearance.highlight_color,
|
||||||
|
format!("Extabindex Symbol: {}", extabindex_name),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,7 +260,7 @@ fn symbol_ui(
|
||||||
let response = SelectableLabel::new(selected, job)
|
let response = SelectableLabel::new(selected, job)
|
||||||
.ui(ui)
|
.ui(ui)
|
||||||
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol, appearance));
|
.on_hover_ui_at_pointer(|ui| symbol_hover_ui(ui, symbol, appearance));
|
||||||
response.context_menu(|ui| symbol_context_menu_ui(ui, symbol));
|
response.context_menu(|ui| symbol_context_menu_ui(ui, state, symbol, section));
|
||||||
if response.clicked() {
|
if response.clicked() {
|
||||||
if let Some(section) = section {
|
if let Some(section) = section {
|
||||||
if section.kind == ObjSectionKind::Code {
|
if section.kind == ObjSectionKind::Code {
|
||||||
|
@ -258,6 +290,13 @@ fn symbol_ui(
|
||||||
(None, None)
|
(None, None)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//If the decode extab context menu option was clicked, switch to the extab view
|
||||||
|
if state.queue_extab_decode {
|
||||||
|
ret = Some(View::ExtabDiff);
|
||||||
|
state.queue_extab_decode = false;
|
||||||
|
}
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue