Skip to content

Commit 674c942

Browse files
authored
Implement context menu copy functionality for data values (#163)
* Implement context menu copy functionality for data values * Clippy fixes
1 parent 6b7dcab commit 674c942

File tree

4 files changed

+106
-39
lines changed

4 files changed

+106
-39
lines changed

objdiff-core/src/arch/mod.rs

Lines changed: 82 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,36 @@ pub enum DataType {
3434
String,
3535
}
3636

37+
impl std::fmt::Display for DataType {
38+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39+
match self {
40+
DataType::Int8 => write!(f, "Int8"),
41+
DataType::Int16 => write!(f, "Int16"),
42+
DataType::Int32 => write!(f, "Int32"),
43+
DataType::Int64 => write!(f, "Int64"),
44+
DataType::Int128 => write!(f, "Int128"),
45+
DataType::Float => write!(f, "Float"),
46+
DataType::Double => write!(f, "Double"),
47+
DataType::Bytes => write!(f, "Bytes"),
48+
DataType::String => write!(f, "String"),
49+
}
50+
}
51+
}
52+
3753
impl DataType {
38-
pub fn display_bytes<Endian: ByteOrder>(&self, bytes: &[u8]) -> Option<String> {
54+
pub fn display_labels<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
55+
let mut strs = Vec::new();
56+
for literal in self.display_literals::<Endian>(bytes) {
57+
strs.push(format!("{}: {}", self, literal))
58+
}
59+
strs
60+
}
61+
62+
pub fn display_literals<Endian: ByteOrder>(&self, bytes: &[u8]) -> Vec<String> {
63+
let mut strs = Vec::new();
3964
if self.required_len().is_some_and(|l| bytes.len() < l) {
4065
log::warn!("Failed to display a symbol value for a symbol whose size is too small for instruction referencing it.");
41-
return None;
66+
return strs;
4267
}
4368
let mut bytes = bytes;
4469
if self.required_len().is_some_and(|l| bytes.len() > l) {
@@ -56,58 +81,61 @@ impl DataType {
5681
match self {
5782
DataType::Int8 => {
5883
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
84+
strs.push(format!("{:#x}", i));
85+
5986
if i < 0 {
60-
format!("Int8: {:#x} ({:#x})", i, ReallySigned(i))
61-
} else {
62-
format!("Int8: {:#x}", i)
87+
strs.push(format!("{:#x}", ReallySigned(i)));
6388
}
6489
}
6590
DataType::Int16 => {
6691
let i = Endian::read_i16(bytes);
92+
strs.push(format!("{:#x}", i));
93+
6794
if i < 0 {
68-
format!("Int16: {:#x} ({:#x})", i, ReallySigned(i))
69-
} else {
70-
format!("Int16: {:#x}", i)
95+
strs.push(format!("{:#x}", ReallySigned(i)));
7196
}
7297
}
7398
DataType::Int32 => {
7499
let i = Endian::read_i32(bytes);
100+
strs.push(format!("{:#x}", i));
101+
75102
if i < 0 {
76-
format!("Int32: {:#x} ({:#x})", i, ReallySigned(i))
77-
} else {
78-
format!("Int32: {:#x}", i)
103+
strs.push(format!("{:#x}", ReallySigned(i)));
79104
}
80105
}
81106
DataType::Int64 => {
82107
let i = Endian::read_i64(bytes);
108+
strs.push(format!("{:#x}", i));
109+
83110
if i < 0 {
84-
format!("Int64: {:#x} ({:#x})", i, ReallySigned(i))
85-
} else {
86-
format!("Int64: {:#x}", i)
111+
strs.push(format!("{:#x}", ReallySigned(i)));
87112
}
88113
}
89114
DataType::Int128 => {
90115
let i = Endian::read_i128(bytes);
116+
strs.push(format!("{:#x}", i));
117+
91118
if i < 0 {
92-
format!("Int128: {:#x} ({:#x})", i, ReallySigned(i))
93-
} else {
94-
format!("Int128: {:#x}", i)
119+
strs.push(format!("{:#x}", ReallySigned(i)));
95120
}
96121
}
97122
DataType::Float => {
98-
format!("Float: {:?}f", Endian::read_f32(bytes))
123+
strs.push(format!("{:?}f", Endian::read_f32(bytes)));
99124
}
100125
DataType::Double => {
101-
format!("Double: {:?}", Endian::read_f64(bytes))
126+
strs.push(format!("{:?}", Endian::read_f64(bytes)));
102127
}
103128
DataType::Bytes => {
104-
format!("Bytes: {:#?}", bytes)
129+
strs.push(format!("{:#?}", bytes));
105130
}
106131
DataType::String => {
107-
format!("String: {:?}", CStr::from_bytes_until_nul(bytes).ok()?)
132+
if let Ok(cstr) = CStr::from_bytes_until_nul(bytes) {
133+
strs.push(format!("{:?}", cstr));
134+
}
108135
}
109136
}
110-
.into()
137+
138+
strs
111139
}
112140

113141
fn required_len(&self) -> Option<usize> {
@@ -154,19 +182,42 @@ pub trait ObjArch: Send + Sync {
154182

155183
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
156184

157-
fn display_data_type(&self, _ty: DataType, bytes: &[u8]) -> Option<String> {
158-
Some(format!("Bytes: {:#x?}", bytes))
185+
fn display_data_labels(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
186+
vec![format!("Bytes: {:#x?}", bytes)]
187+
}
188+
189+
fn display_data_literals(&self, _ty: DataType, bytes: &[u8]) -> Vec<String> {
190+
vec![format!("{:#?}", bytes)]
191+
}
192+
193+
fn display_ins_data_labels(&self, ins: &ObjIns) -> Vec<String> {
194+
let Some(reloc) = ins.reloc.as_ref() else {
195+
return Vec::new();
196+
};
197+
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
198+
return self
199+
.guess_data_type(ins)
200+
.map(|ty| {
201+
self.display_data_labels(ty, &reloc.target.bytes[reloc.addend as usize..])
202+
})
203+
.unwrap_or_default();
204+
}
205+
Vec::new()
159206
}
160207

161-
fn display_ins_data(&self, ins: &ObjIns) -> Option<String> {
162-
let reloc = ins.reloc.as_ref()?;
208+
fn display_ins_data_literals(&self, ins: &ObjIns) -> Vec<String> {
209+
let Some(reloc) = ins.reloc.as_ref() else {
210+
return Vec::new();
211+
};
163212
if reloc.addend >= 0 && reloc.target.bytes.len() > reloc.addend as usize {
164-
self.guess_data_type(ins).and_then(|ty| {
165-
self.display_data_type(ty, &reloc.target.bytes[reloc.addend as usize..])
166-
})
167-
} else {
168-
None
213+
return self
214+
.guess_data_type(ins)
215+
.map(|ty| {
216+
self.display_data_literals(ty, &reloc.target.bytes[reloc.addend as usize..])
217+
})
218+
.unwrap_or_default();
169219
}
220+
Vec::new()
170221
}
171222

172223
// Downcast methods

objdiff-core/src/arch/ppc.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,12 @@ impl ObjArch for ObjArchPpc {
221221
guess_data_type_from_load_store_inst_op(Opcode::from(instruction.op as u8))
222222
}
223223

224-
fn display_data_type(&self, ty: DataType, bytes: &[u8]) -> Option<String> {
225-
ty.display_bytes::<BigEndian>(bytes)
224+
fn display_data_labels(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
225+
ty.display_labels::<BigEndian>(bytes)
226+
}
227+
228+
fn display_data_literals(&self, ty: DataType, bytes: &[u8]) -> Vec<String> {
229+
ty.display_literals::<BigEndian>(bytes)
226230
}
227231

228232
fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) }

objdiff-core/src/diff/code.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ fn reloc_eq(
245245
|| address_eq(left, right))
246246
&& (config.function_reloc_diffs == FunctionRelocDiffs::NameAddress
247247
|| left.target.kind != ObjSymbolKind::Object
248-
|| left_obj.arch.display_ins_data(left_ins)
249-
== left_obj.arch.display_ins_data(right_ins))
248+
|| left_obj.arch.display_ins_data_labels(left_ins)
249+
== left_obj.arch.display_ins_data_labels(right_ins))
250250
}
251251
(Some(_), None) => false,
252252
(None, Some(_)) => {

objdiff-gui/src/views/function_diff.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ fn ins_hover_ui(
149149
appearance.highlight_color,
150150
format!("Size: {:x}", reloc.target.size),
151151
);
152-
if let Some(s) = obj.arch.display_ins_data(ins) {
153-
ui.colored_label(appearance.highlight_color, s);
152+
for label in obj.arch.display_ins_data_labels(ins) {
153+
ui.colored_label(appearance.highlight_color, label);
154154
}
155155
} else {
156156
ui.colored_label(appearance.highlight_color, "Extern".to_string());
@@ -163,7 +163,13 @@ fn ins_hover_ui(
163163
});
164164
}
165165

166-
fn ins_context_menu(ui: &mut egui::Ui, section: &ObjSection, ins: &ObjIns, symbol: &ObjSymbol) {
166+
fn ins_context_menu(
167+
ui: &mut egui::Ui,
168+
obj: &ObjInfo,
169+
section: &ObjSection,
170+
ins: &ObjIns,
171+
symbol: &ObjSymbol,
172+
) {
167173
ui.scope(|ui| {
168174
ui.style_mut().override_text_style = Some(egui::TextStyle::Monospace);
169175
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
219225
}
220226
}
221227
if let Some(reloc) = &ins.reloc {
228+
for literal in obj.arch.display_ins_data_literals(ins) {
229+
if ui.button(format!("Copy \"{literal}\"")).clicked() {
230+
ui.output_mut(|output| output.copied_text.clone_from(&literal));
231+
ui.close_menu();
232+
}
233+
}
222234
if let Some(name) = &reloc.target.demangled_name {
223235
if ui.button(format!("Copy \"{name}\"")).clicked() {
224236
ui.output_mut(|output| output.copied_text.clone_from(name));
@@ -390,7 +402,7 @@ fn asm_col_ui(
390402
let ins_diff = &ctx.diff.symbol_diff(symbol_ref).instructions[row.index()];
391403
let response_cb = |response: Response| {
392404
if let Some(ins) = &ins_diff.ins {
393-
response.context_menu(|ui| ins_context_menu(ui, section, ins, symbol));
405+
response.context_menu(|ui| ins_context_menu(ui, ctx.obj, section, ins, symbol));
394406
response.on_hover_ui_at_pointer(|ui| {
395407
ins_hover_ui(ui, ctx.obj, section, ins, symbol, appearance)
396408
})

0 commit comments

Comments
 (0)