Implement context menu copy functionality for data values (#163)

* Implement context menu copy functionality for data values

* Clippy fixes
This commit is contained in:
Steven Casper 2025-02-10 00:24:52 -05:00 committed by GitHub
parent 6b7dcabbed
commit 674c942d7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 106 additions and 39 deletions

View File

@ -34,11 +34,36 @@ pub enum DataType {
String, String,
} }
impl std::fmt::Display for DataType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DataType::Int8 => write!(f, "Int8"),
DataType::Int16 => write!(f, "Int16"),
DataType::Int32 => write!(f, "Int32"),
DataType::Int64 => write!(f, "Int64"),
DataType::Int128 => write!(f, "Int128"),
DataType::Float => write!(f, "Float"),
DataType::Double => write!(f, "Double"),
DataType::Bytes => write!(f, "Bytes"),
DataType::String => write!(f, "String"),
}
}
}
impl DataType { impl DataType {
pub fn display_bytes<Endian: ByteOrder>(&self, bytes: &[u8]) -> Option<String> { pub fn display_labels<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
let mut strs = Vec::new();
for literal in self.display_literals::<Endian>(bytes) {
strs.push(format!("{}: {}", self, literal))
}
strs
}
pub fn display_literals<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
let mut strs = Vec::new();
if self.required_len().is_some_and(|l| bytes.len() < l) { if self.required_len().is_some_and(|l| bytes.len() < l) {
log::warn!("Failed to display a symbol value for a symbol whose size is too small for instruction referencing it."); log::warn!("Failed to display a symbol value for a symbol whose size is too small for instruction referencing it.");
return None; return strs;
} }
let mut bytes = bytes; let mut bytes = bytes;
if self.required_len().is_some_and(|l| bytes.len() > l) { if self.required_len().is_some_and(|l| bytes.len() > l) {
@ -56,58 +81,61 @@ impl DataType {
match self { match self {
DataType::Int8 => { DataType::Int8 => {
let i = i8::from_ne_bytes(bytes.try_into().unwrap()); let i = i8::from_ne_bytes(bytes.try_into().unwrap());
strs.push(format!("{:#x}", i));
if i < 0 { if i < 0 {
format!("Int8: {:#x} ({:#x})", i, ReallySigned(i)) strs.push(format!("{:#x}", ReallySigned(i)));
} else {
format!("Int8: {:#x}", i)
} }
} }
DataType::Int16 => { DataType::Int16 => {
let i = Endian::read_i16(bytes); let i = Endian::read_i16(bytes);
strs.push(format!("{:#x}", i));
if i < 0 { if i < 0 {
format!("Int16: {:#x} ({:#x})", i, ReallySigned(i)) strs.push(format!("{:#x}", ReallySigned(i)));
} else {
format!("Int16: {:#x}", i)
} }
} }
DataType::Int32 => { DataType::Int32 => {
let i = Endian::read_i32(bytes); let i = Endian::read_i32(bytes);
strs.push(format!("{:#x}", i));
if i < 0 { if i < 0 {
format!("Int32: {:#x} ({:#x})", i, ReallySigned(i)) strs.push(format!("{:#x}", ReallySigned(i)));
} else {
format!("Int32: {:#x}", i)
} }
} }
DataType::Int64 => { DataType::Int64 => {
let i = Endian::read_i64(bytes); let i = Endian::read_i64(bytes);
strs.push(format!("{:#x}", i));
if i < 0 { if i < 0 {
format!("Int64: {:#x} ({:#x})", i, ReallySigned(i)) strs.push(format!("{:#x}", ReallySigned(i)));
} else {
format!("Int64: {:#x}", i)
} }
} }
DataType::Int128 => { DataType::Int128 => {
let i = Endian::read_i128(bytes); let i = Endian::read_i128(bytes);
strs.push(format!("{:#x}", i));
if i < 0 { if i < 0 {
format!("Int128: {:#x} ({:#x})", i, ReallySigned(i)) strs.push(format!("{:#x}", ReallySigned(i)));
} else {
format!("Int128: {:#x}", i)
} }
} }
DataType::Float => { DataType::Float => {
format!("Float: {:?}f", Endian::read_f32(bytes)) strs.push(format!("{:?}f", Endian::read_f32(bytes)));
} }
DataType::Double => { DataType::Double => {
format!("Double: {:?}", Endian::read_f64(bytes)) strs.push(format!("{:?}", Endian::read_f64(bytes)));
} }
DataType::Bytes => { DataType::Bytes => {
format!("Bytes: {:#?}", bytes) strs.push(format!("{:#?}", bytes));
} }
DataType::String => { DataType::String => {
format!("String: {:?}", CStr::from_bytes_until_nul(bytes).ok()?) if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
strs.push(format!("{:?}", cstr));
}
} }
} }
.into()
strs
} }
fn required_len(&self) -> Option<usize> { fn required_len(&self) -> Option<usize> {
@ -154,19 +182,42 @@ pub trait ObjArch: Send + Sync {
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None } fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
fn display_data_type(&self, _ty: DataType, bytes: &[u8]) -> Option<String> { fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
Some(format!("Bytes: {:#x?}", bytes)) vec![format!("Bytes: {:#x?}", bytes)]
} }
fn display_ins_data(&self, ins: &ObjIns) -> Option<String> { fn display_data_literals(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
let reloc = ins.reloc.as_ref()?; vec![format!("{:#?}", bytes)]
}
fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec<String> {
let Some(reloc) = ins.reloc.as_ref() else {
return Vec::new();
};
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize { if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
self.guess_data_type(ins).and_then(|ty| { return self
self.display_data_type(ty, &reloc.target.bytes[reloc.addend as usize..]) .guess_data_type(ins)
}) .map(|ty| {
} else { self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
None })
.unwrap_or_default();
} }
Vec::new()
}
fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec<String> {
let Some(reloc) = ins.reloc.as_ref() else {
return Vec::new();
};
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
return self
.guess_data_type(ins)
.map(|ty| {
self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..])
})
.unwrap_or_default();
}
Vec::new()
} }
// Downcast methods // Downcast methods

View File

@ -221,8 +221,12 @@ impl ObjArch for ObjArchPpc {
guess_data_type_from_load_store_inst_op(Opcode::from(instruction.op as u8)) guess_data_type_from_load_store_inst_op(Opcode::from(instruction.op as u8))
} }
fn display_data_type(&self, ty: DataType, bytes: &[u8]) -> Option<String> { fn display_data_labels(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
ty.display_bytes::<BigEndian>(bytes) ty.display_labels::<BigEndian>(bytes)
}
fn display_data_literals(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
ty.display_literals::<BigEndian>(bytes)
} }
fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) } fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) }

View File

@ -245,8 +245,8 @@ fn reloc_eq(
|| address_eq(left, right)) || address_eq(left, right))
&& (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress && (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
|| left.target.kind != ObjSymbolKind::Object || left.target.kind != ObjSymbolKind::Object
|| left_obj.arch.display_ins_data(left_ins) || left_obj.arch.display_ins_data_labels(left_ins)
== left_obj.arch.display_ins_data(right_ins)) == left_obj.arch.display_ins_data_labels(right_ins))
} }
(Some(_), None) => false, (Some(_), None) => false,
(None, Some(_)) => { (None, Some(_)) => {

View File

@ -149,8 +149,8 @@ fn ins_hover_ui(
appearance.highlight_color, appearance.highlight_color,
format!("Size: {:x}", reloc.target.size), format!("Size: {:x}", reloc.target.size),
); );
if let Some(s) = obj.arch.display_ins_data(ins) { for label in obj.arch.display_ins_data_labels(ins) {
ui.colored_label(appearance.highlight_color, s); ui.colored_label(appearance.highlight_color, label);
} }
} else { } else {
ui.colored_label(appearance.highlight_color, "Extern".to_string()); ui.colored_label(appearance.highlight_color, "Extern".to_string());
@ -163,7 +163,13 @@ fn ins_hover_ui(
}); });
} }
fn ins_context_menu(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, symbol: &ObjSymbol) { fn ins_context_menu(
ui: &mut egui::Ui,
obj: &ObjInfo,
section: &ObjSection,
ins: &ObjIns,
symbol: &ObjSymbol,
) {
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_mode = Some(egui::TextWrapMode::Extend); ui.style_mut().wrap_mode = Some(egui::TextWrapMode::Extend);
@ -219,6 +225,12 @@ fn ins_context_menu(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, symbo
} }
} }
if let Some(reloc) = &ins.reloc { if let Some(reloc) = &ins.reloc {
for literal in obj.arch.display_ins_data_literals(ins) {
if ui.button(format!("Copy \"{literal}\"")).clicked() {
ui.output_mut(|output| output.copied_text.clone_from(&literal));
ui.close_menu();
}
}
if let Some(name) = &reloc.target.demangled_name { if let Some(name) = &reloc.target.demangled_name {
if ui.button(format!("Copy \"{name}\"")).clicked() { if ui.button(format!("Copy \"{name}\"")).clicked() {
ui.output_mut(|output| output.copied_text.clone_from(name)); ui.output_mut(|output| output.copied_text.clone_from(name));
@ -390,7 +402,7 @@ fn asm_col_ui(
let ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()]; let ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()];
let response_cb = |response: Response| { let response_cb = |response: Response| {
if let Some(ins) = &ins_diff.ins { if let Some(ins) = &ins_diff.ins {
response.context_menu(|ui| ins_context_menu(ui, section, ins, symbol)); response.context_menu(|ui| ins_context_menu(ui, ctx.obj, section, ins, symbol));
response.on_hover_ui_at_pointer(|ui| { response.on_hover_ui_at_pointer(|ui| {
ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance) ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance)
}) })