Skip to content

Commit a43320a

Browse files
authored
PPC: Guess reloc data type based on the instruction. (#108)
* Guess reloc data type based on the instruction. Adds an entry to the reloc tooltip to show the inferred data type and value. * Fix clippy warning * Match on Opcode rather than mnemonic string
1 parent 35bbd40 commit a43320a

File tree

5 files changed

+154
-3
lines changed

5 files changed

+154
-3
lines changed

objdiff-core/src/arch/mod.rs

+100-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
use std::{borrow::Cow, collections::BTreeMap};
1+
use std::{borrow::Cow, collections::BTreeMap, ffi::CStr};
22

33
use anyhow::{bail, Result};
4+
use byteorder::ByteOrder;
45
use object::{Architecture, File, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
56

67
use crate::{
78
diff::DiffObjConfig,
89
obj::{ObjIns, ObjReloc, ObjSection},
10+
util::ReallySigned,
911
};
1012

1113
#[cfg(feature = "arm")]
@@ -17,6 +19,97 @@ pub mod ppc;
1719
#[cfg(feature = "x86")]
1820
pub mod x86;
1921

22+
/// Represents the type of data associated with an instruction
23+
pub enum DataType {
24+
Int8,
25+
Int16,
26+
Int32,
27+
Int64,
28+
Int128,
29+
Float,
30+
Double,
31+
Bytes,
32+
String,
33+
}
34+
35+
impl DataType {
36+
pub fn display_bytes<Endian: ByteOrder>(&self, bytes: &[u8]) -> Option<String> {
37+
if self.required_len().is_some_and(|l| bytes.len() < l) {
38+
return None;
39+
}
40+
41+
match self {
42+
DataType::Int8 => {
43+
let i = i8::from_ne_bytes(bytes.try_into().unwrap());
44+
if i < 0 {
45+
format!("Int8: {:#x} ({:#x})", i, ReallySigned(i))
46+
} else {
47+
format!("Int8: {:#x}", i)
48+
}
49+
}
50+
DataType::Int16 => {
51+
let i = Endian::read_i16(bytes);
52+
if i < 0 {
53+
format!("Int16: {:#x} ({:#x})", i, ReallySigned(i))
54+
} else {
55+
format!("Int16: {:#x}", i)
56+
}
57+
}
58+
DataType::Int32 => {
59+
let i = Endian::read_i32(bytes);
60+
if i < 0 {
61+
format!("Int32: {:#x} ({:#x})", i, ReallySigned(i))
62+
} else {
63+
format!("Int32: {:#x}", i)
64+
}
65+
}
66+
DataType::Int64 => {
67+
let i = Endian::read_i64(bytes);
68+
if i < 0 {
69+
format!("Int64: {:#x} ({:#x})", i, ReallySigned(i))
70+
} else {
71+
format!("Int64: {:#x}", i)
72+
}
73+
}
74+
DataType::Int128 => {
75+
let i = Endian::read_i128(bytes);
76+
if i < 0 {
77+
format!("Int128: {:#x} ({:#x})", i, ReallySigned(i))
78+
} else {
79+
format!("Int128: {:#x}", i)
80+
}
81+
}
82+
DataType::Float => {
83+
format!("Float: {}", Endian::read_f32(bytes))
84+
}
85+
DataType::Double => {
86+
format!("Double: {}", Endian::read_f64(bytes))
87+
}
88+
DataType::Bytes => {
89+
format!("Bytes: {:#?}", bytes)
90+
}
91+
DataType::String => {
92+
format!("String: {:?}", CStr::from_bytes_until_nul(bytes).ok()?)
93+
}
94+
}
95+
.into()
96+
}
97+
98+
fn required_len(&self) -> Option<usize> {
99+
match self {
100+
DataType::Int8 => Some(1),
101+
DataType::Int16 => Some(2),
102+
DataType::Int32 => Some(4),
103+
DataType::Int64 => Some(8),
104+
DataType::Int128 => Some(16),
105+
DataType::Float => Some(4),
106+
DataType::Double => Some(8),
107+
DataType::Bytes => None,
108+
DataType::String => None,
109+
}
110+
}
111+
}
112+
20113
pub trait ObjArch: Send + Sync {
21114
fn process_code(
22115
&self,
@@ -42,6 +135,12 @@ pub trait ObjArch: Send + Sync {
42135

43136
fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() }
44137

138+
fn guess_data_type(&self, _instruction: &ObjIns) -> Option<DataType> { None }
139+
140+
fn display_data_type(&self, _ty: DataType, bytes: &[u8]) -> Option<String> {
141+
Some(format!("Bytes: {:#x?}", bytes))
142+
}
143+
45144
// Downcast methods
46145
#[cfg(feature = "ppc")]
47146
fn ppc(&self) -> Option<&ppc::ObjArchPpc> { None }

objdiff-core/src/arch/ppc.rs

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use std::{borrow::Cow, collections::BTreeMap};
22

33
use anyhow::{bail, ensure, Result};
4+
use byteorder::BigEndian;
45
use cwextab::{decode_extab, ExceptionTableData};
56
use object::{
67
elf, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, RelocationTarget,
78
Symbol, SymbolKind,
89
};
9-
use ppc750cl::{Argument, InsIter, GPR};
10+
use ppc750cl::{Argument, InsIter, Opcode, GPR};
1011

1112
use crate::{
12-
arch::{ObjArch, ProcessCodeResult},
13+
arch::{DataType, ObjArch, ProcessCodeResult},
1314
diff::DiffObjConfig,
1415
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection, ObjSymbol},
1516
};
@@ -186,6 +187,36 @@ impl ObjArch for ObjArchPpc {
186187
}
187188
}
188189

190+
fn guess_data_type(&self, instruction: &ObjIns) -> Option<super::DataType> {
191+
// Always shows the first string of the table. Not ideal, but it's really hard to find
192+
// the actual string being referenced.
193+
if instruction.reloc.as_ref().is_some_and(|r| r.target.name.starts_with("@stringBase")) {
194+
return Some(DataType::String);
195+
}
196+
197+
// SAFETY: ppc750cl::Opcode is repr(u8) and op is originally obtained on PPC from casting
198+
// an Opcode to a u8 so we know it's a valid value for Opcode.
199+
match unsafe { std::mem::transmute::<u8, Opcode>(instruction.op as u8) } {
200+
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
201+
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
202+
Opcode::Lha | Opcode::Lhau | Opcode::Lhaux | Opcode::Lhax => Some(DataType::Int16),
203+
Opcode::Lwz | Opcode::Lwzu | Opcode::Lwzux | Opcode::Lwzx => Some(DataType::Int32),
204+
Opcode::Lfs | Opcode::Lfsu | Opcode::Lfsux | Opcode::Lfsx => Some(DataType::Float),
205+
Opcode::Lfd | Opcode::Lfdu | Opcode::Lfdux | Opcode::Lfdx => Some(DataType::Double),
206+
207+
Opcode::Stb | Opcode::Stbu | Opcode::Stbux | Opcode::Stbx => Some(DataType::Int8),
208+
Opcode::Sth | Opcode::Sthu | Opcode::Sthux | Opcode::Sthx => Some(DataType::Int16),
209+
Opcode::Stw | Opcode::Stwu | Opcode::Stwux | Opcode::Stwx => Some(DataType::Int32),
210+
Opcode::Stfs | Opcode::Stfsu | Opcode::Stfsux | Opcode::Stfsx => Some(DataType::Float),
211+
Opcode::Stfd | Opcode::Stfdu | Opcode::Stfdux | Opcode::Stfdx => Some(DataType::Double),
212+
_ => None,
213+
}
214+
}
215+
216+
fn display_data_type(&self, ty: DataType, bytes: &[u8]) -> Option<String> {
217+
ty.display_bytes::<BigEndian>(bytes)
218+
}
219+
189220
fn ppc(&self) -> Option<&ObjArchPpc> { Some(self) }
190221
}
191222

objdiff-core/src/obj/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pub struct ObjSymbol {
126126
pub virtual_address: Option<u64>,
127127
/// Original index in object symbol table
128128
pub original_index: Option<usize>,
129+
pub bytes: Vec<u8>,
129130
}
130131

131132
pub struct ObjInfo {

objdiff-core/src/obj/read.rs

+14
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,16 @@ fn to_obj_symbol(
7878
let virtual_address = split_meta
7979
.and_then(|m| m.virtual_addresses.as_ref())
8080
.and_then(|v| v.get(symbol.index().0).cloned());
81+
82+
let bytes = symbol
83+
.section_index()
84+
.and_then(|idx| obj_file.section_by_index(idx).ok())
85+
.and_then(|section| section.data().ok())
86+
.and_then(|data| {
87+
data.get(section_address as usize..(section_address + symbol.size()) as usize)
88+
})
89+
.unwrap_or(&[]);
90+
8191
Ok(ObjSymbol {
8292
name: name.to_string(),
8393
demangled_name,
@@ -89,6 +99,7 @@ fn to_obj_symbol(
8999
addend,
90100
virtual_address,
91101
original_index: Some(symbol.index().0),
102+
bytes: bytes.to_vec(),
92103
})
93104
}
94105

@@ -179,6 +190,7 @@ fn symbols_by_section(
179190
addend: 0,
180191
virtual_address: None,
181192
original_index: None,
193+
bytes: Vec::new(),
182194
});
183195
}
184196
Ok(result)
@@ -239,6 +251,7 @@ fn find_section_symbol(
239251
addend: offset_addr as i64,
240252
virtual_address: None,
241253
original_index: None,
254+
bytes: Vec::new(),
242255
})
243256
}
244257

@@ -521,6 +534,7 @@ fn update_combined_symbol(symbol: ObjSymbol, address_change: i64) -> Result<ObjS
521534
None
522535
},
523536
original_index: symbol.original_index,
537+
bytes: symbol.bytes,
524538
})
525539
}
526540

objdiff-gui/src/views/function_diff.rs

+6
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ fn ins_hover_ui(
7979
appearance.highlight_color,
8080
format!("Size: {:x}", reloc.target.size),
8181
);
82+
if let Some(s) = arch
83+
.guess_data_type(ins)
84+
.and_then(|ty| arch.display_data_type(ty, &reloc.target.bytes))
85+
{
86+
ui.colored_label(appearance.highlight_color, s);
87+
}
8288
} else {
8389
ui.colored_label(appearance.highlight_color, "Extern".to_string());
8490
}

0 commit comments

Comments
 (0)