Skip to content

Commit 60b227f

Browse files
committed
Support PowerPC COFF (Xenon, Xbox 360)
ppc750cl is superseded by the powerpc crate, which supports PPC64, AltiVec and VMX128 extensions.
1 parent 127ae5a commit 60b227f

File tree

13 files changed

+636
-547
lines changed

13 files changed

+636
-547
lines changed

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Supports:
1919
- ARM (GBA, DS, 3DS)
2020
- ARM64 (Switch)
2121
- MIPS (N64, PS1, PS2, PSP)
22-
- PowerPC (GameCube, Wii)
22+
- PowerPC (GameCube, Wii, Xbox 360)
2323
- SuperH (Saturn, Dreamcast)
2424
- x86, x86_64 (PC)
2525

objdiff-core/Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ ppc = [
9292
"any-arch",
9393
"dep:cwdemangle",
9494
"dep:cwextab",
95-
"dep:ppc750cl",
95+
"dep:powerpc",
9696
"dep:rlwinmdec",
9797
]
9898
x86 = [
@@ -128,7 +128,7 @@ itertools = { version = "0.14", default-features = false, features = ["use_alloc
128128
log = { version = "0.4", default-features = false, optional = true }
129129
memmap2 = { version = "0.9", optional = true }
130130
num-traits = { version = "0.2", default-features = false, optional = true }
131-
object = { git = "https://github.com/gimli-rs/object", rev = "a74579249e21ab8fcd3a86be588de336f18297cb", default-features = false, features = ["read_core", "elf", "pe"] }
131+
object = { git = "https://github.com/gimli-rs/object", rev = "16ff70aa6fbd97d6bb7b92375929f4d72414c32b", default-features = false, features = ["read_core", "elf", "pe"] }
132132
pbjson = { version = "0.7", default-features = false, optional = true }
133133
prost = { version = "0.13", default-features = false, features = ["prost-derive"], optional = true }
134134
regex = { version = "1.11", default-features = false, features = [], optional = true }
@@ -147,7 +147,7 @@ gimli = { version = "0.31", default-features = false, features = ["read"], optio
147147
# ppc
148148
cwdemangle = { version = "1.0", optional = true }
149149
cwextab = { version = "1.0", optional = true }
150-
ppc750cl = { version = "0.3", optional = true }
150+
powerpc = { version = "0.4", optional = true }
151151
rlwinmdec = { version = "1.1", optional = true }
152152

153153
# mips

objdiff-core/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ objdiff-core contains the core functionality of [objdiff](https://github.com/enc
1111
- **`arm64`**: Enables the ARM64 backend powered by [yaxpeax-arm](https://github.com/iximeow/yaxpeax-arm).
1212
- **`arm`**: Enables the ARM backend powered by [unarm](https://github.com/AetiasHax/unarm).
1313
- **`mips`**: Enables the MIPS backend powered by [rabbitizer](https://github.com/Decompollaborate/rabbitizer).
14-
- **`ppc`**: Enables the PowerPC backend powered by [ppc750cl](https://github.com/encounter/ppc750cl).
14+
- **`ppc`**: Enables the PowerPC backend powered by [powerpc](https://github.com/encounter/powerpc-rs).
1515
- **`superh`**: Enables the SuperH backend powered by an included disassembler.
1616
- **`x86`**: Enables the x86 backend powered by [iced-x86](https://crates.io/crates/iced-x86).

objdiff-core/src/arch/mod.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,16 @@ pub enum DataType {
5555

5656
impl fmt::Display for DataType {
5757
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58-
match self {
59-
DataType::Int8 => f.write_str("Int8"),
60-
DataType::Int16 => f.write_str("Int16"),
61-
DataType::Int32 => f.write_str("Int32"),
62-
DataType::Int64 => f.write_str("Int64"),
63-
DataType::Float => f.write_str("Float"),
64-
DataType::Double => f.write_str("Double"),
65-
DataType::Bytes => f.write_str("Bytes"),
66-
DataType::String => f.write_str("String"),
67-
}
58+
f.write_str(match self {
59+
DataType::Int8 => "Int8",
60+
DataType::Int16 => "Int16",
61+
DataType::Int32 => "Int32",
62+
DataType::Int64 => "Int64",
63+
DataType::Float => "Float",
64+
DataType::Double => "Double",
65+
DataType::Bytes => "Bytes",
66+
DataType::String => "String",
67+
})
6868
}
6969
}
7070

objdiff-core/src/arch/ppc/flow_analysis.rs

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ use core::{
1111
};
1212

1313
use itertools::Itertools;
14-
use ppc750cl::Simm;
14+
use powerpc::{Extensions, Simm};
1515

1616
use crate::{
1717
arch::DataType,
1818
obj::{FlowAnalysisResult, FlowAnalysisValue, Object, Relocation, Symbol},
1919
util::{RawDouble, RawFloat},
2020
};
2121

22-
fn is_store_instruction(op: ppc750cl::Opcode) -> bool {
23-
use ppc750cl::Opcode;
22+
fn is_store_instruction(op: powerpc::Opcode) -> bool {
23+
use powerpc::Opcode;
2424
matches!(
2525
op,
2626
Opcode::Stbux
@@ -52,8 +52,8 @@ fn is_store_instruction(op: ppc750cl::Opcode) -> bool {
5252
)
5353
}
5454

55-
pub fn guess_data_type_from_load_store_inst_op(inst_op: ppc750cl::Opcode) -> Option<DataType> {
56-
use ppc750cl::Opcode;
55+
pub fn guess_data_type_from_load_store_inst_op(inst_op: powerpc::Opcode) -> Option<DataType> {
56+
use powerpc::Opcode;
5757
match inst_op {
5858
Opcode::Lbz | Opcode::Lbzu | Opcode::Lbzux | Opcode::Lbzx => Some(DataType::Int8),
5959
Opcode::Lhz | Opcode::Lhzu | Opcode::Lhzux | Opcode::Lhzx => Some(DataType::Int16),
@@ -118,12 +118,12 @@ impl RegisterState {
118118

119119
// During a function call, these registers must be assumed trashed.
120120
fn clear_volatile(&mut self) {
121-
self[ppc750cl::GPR(0)] = RegisterContent::Unknown;
121+
self[powerpc::GPR(0)] = RegisterContent::Unknown;
122122
for i in 0..=13 {
123-
self[ppc750cl::GPR(i)] = RegisterContent::Unknown;
123+
self[powerpc::GPR(i)] = RegisterContent::Unknown;
124124
}
125125
for i in 0..=13 {
126-
self[ppc750cl::FPR(i)] = RegisterContent::Unknown;
126+
self[powerpc::FPR(i)] = RegisterContent::Unknown;
127127
}
128128
}
129129

@@ -132,10 +132,10 @@ impl RegisterState {
132132
// they get overwritten with another value before getting read.
133133
fn set_potential_inputs(&mut self) {
134134
for g_reg in 3..=13 {
135-
self[ppc750cl::GPR(g_reg)] = RegisterContent::InputRegister(g_reg);
135+
self[powerpc::GPR(g_reg)] = RegisterContent::InputRegister(g_reg);
136136
}
137137
for f_reg in 1..=13 {
138-
self[ppc750cl::FPR(f_reg)] = RegisterContent::InputRegister(f_reg);
138+
self[powerpc::FPR(f_reg)] = RegisterContent::InputRegister(f_reg);
139139
}
140140
}
141141

@@ -172,34 +172,34 @@ impl RegisterState {
172172
}
173173
}
174174

175-
impl Index<ppc750cl::GPR> for RegisterState {
175+
impl Index<powerpc::GPR> for RegisterState {
176176
type Output = RegisterContent;
177177

178-
fn index(&self, gpr: ppc750cl::GPR) -> &Self::Output { &self.gpr[gpr.0 as usize] }
178+
fn index(&self, gpr: powerpc::GPR) -> &Self::Output { &self.gpr[gpr.0 as usize] }
179179
}
180-
impl IndexMut<ppc750cl::GPR> for RegisterState {
181-
fn index_mut(&mut self, gpr: ppc750cl::GPR) -> &mut Self::Output {
180+
impl IndexMut<powerpc::GPR> for RegisterState {
181+
fn index_mut(&mut self, gpr: powerpc::GPR) -> &mut Self::Output {
182182
&mut self.gpr[gpr.0 as usize]
183183
}
184184
}
185185

186-
impl Index<ppc750cl::FPR> for RegisterState {
186+
impl Index<powerpc::FPR> for RegisterState {
187187
type Output = RegisterContent;
188188

189-
fn index(&self, fpr: ppc750cl::FPR) -> &Self::Output { &self.fpr[fpr.0 as usize] }
189+
fn index(&self, fpr: powerpc::FPR) -> &Self::Output { &self.fpr[fpr.0 as usize] }
190190
}
191-
impl IndexMut<ppc750cl::FPR> for RegisterState {
192-
fn index_mut(&mut self, fpr: ppc750cl::FPR) -> &mut Self::Output {
191+
impl IndexMut<powerpc::FPR> for RegisterState {
192+
fn index_mut(&mut self, fpr: powerpc::FPR) -> &mut Self::Output {
193193
&mut self.fpr[fpr.0 as usize]
194194
}
195195
}
196196

197197
fn execute_instruction(
198198
registers: &mut RegisterState,
199-
op: &ppc750cl::Opcode,
200-
args: &ppc750cl::Arguments,
199+
op: &powerpc::Opcode,
200+
args: &powerpc::Arguments,
201201
) {
202-
use ppc750cl::{Argument, GPR, Opcode};
202+
use powerpc::{Argument, GPR, Opcode};
203203
match (op, args[0], args[1], args[2]) {
204204
(Opcode::Or, Argument::GPR(a), Argument::GPR(b), Argument::GPR(c)) => {
205205
// Move is implemented as or with self for ints
@@ -270,11 +270,11 @@ fn execute_instruction(
270270
}
271271
}
272272

273-
fn get_branch_offset(args: &ppc750cl::Arguments) -> i32 {
273+
fn get_branch_offset(args: &powerpc::Arguments) -> i32 {
274274
for arg in args.iter() {
275275
match arg {
276-
ppc750cl::Argument::BranchDest(dest) => return dest.0 / 4,
277-
ppc750cl::Argument::None => break,
276+
powerpc::Argument::BranchDest(dest) => return dest.0 / 4,
277+
powerpc::Argument::None => break,
278278
_ => {}
279279
}
280280
}
@@ -316,7 +316,7 @@ fn clamp_text_length(s: String, max: usize) -> String {
316316
fn get_register_content_from_reloc(
317317
reloc: &Relocation,
318318
obj: &Object,
319-
op: ppc750cl::Opcode,
319+
op: powerpc::Opcode,
320320
) -> RegisterContent {
321321
if let Some(bytes) = obj.symbol_data(reloc.target_symbol) {
322322
match guess_data_type_from_load_store_inst_op(op) {
@@ -354,18 +354,18 @@ fn fill_registers_from_relocation(
354354
reloc: &Relocation,
355355
current_state: &mut RegisterState,
356356
obj: &Object,
357-
op: ppc750cl::Opcode,
358-
args: &ppc750cl::Arguments,
357+
op: powerpc::Opcode,
358+
args: &powerpc::Arguments,
359359
) {
360360
// Only update the register state for loads. We may store to a reloc
361361
// address but that doesn't update register contents.
362362
if !is_store_instruction(op) {
363363
match (op, args[0]) {
364364
// Everything else is a load of some sort
365-
(_, ppc750cl::Argument::GPR(gpr)) => {
365+
(_, powerpc::Argument::GPR(gpr)) => {
366366
current_state[gpr] = get_register_content_from_reloc(reloc, obj, op);
367367
}
368-
(_, ppc750cl::Argument::FPR(fpr)) => {
368+
(_, powerpc::Argument::FPR(fpr)) => {
369369
current_state[fpr] = get_register_content_from_reloc(reloc, obj, op);
370370
}
371371
_ => {}
@@ -384,11 +384,12 @@ pub fn ppc_data_flow_analysis(
384384
func_symbol: &Symbol,
385385
code: &[u8],
386386
relocations: &[Relocation],
387+
extensions: Extensions,
387388
) -> Box<dyn FlowAnalysisResult> {
388389
use alloc::collections::VecDeque;
389390

390-
use ppc750cl::InsIter;
391-
let instructions = InsIter::new(code, func_symbol.address as u32)
391+
use powerpc::InsIter;
392+
let instructions = InsIter::new(code, func_symbol.address as u32, extensions)
392393
.map(|(_addr, ins)| (ins.op, ins.basic().args))
393394
.collect_vec();
394395

@@ -449,7 +450,7 @@ pub fn ppc_data_flow_analysis(
449450
// Only take a given (address, register state) combination once. If
450451
// the known register state is different we have to take the branch
451452
// again to stabilize the known values for backwards branches.
452-
if op == &ppc750cl::Opcode::Bc {
453+
if op == &powerpc::Opcode::Bc {
453454
let branch_state = (index, current_state.clone());
454455
if !taken_branches.contains(&branch_state) {
455456
let offset = get_branch_offset(args);
@@ -468,7 +469,7 @@ pub fn ppc_data_flow_analysis(
468469
}
469470

470471
// Update index
471-
if op == &ppc750cl::Opcode::B {
472+
if op == &powerpc::Opcode::B {
472473
// Unconditional branch
473474
let offset = get_branch_offset(args);
474475
if offset > 0 {
@@ -502,7 +503,14 @@ pub fn ppc_data_flow_analysis(
502503
}
503504

504505
// Store the relevant data flow values for simplified instructions
505-
generate_flow_analysis_result(obj, func_address, code, register_state_at, relocations)
506+
generate_flow_analysis_result(
507+
obj,
508+
func_address,
509+
code,
510+
register_state_at,
511+
relocations,
512+
extensions,
513+
)
506514
}
507515

508516
fn get_string_data(obj: &Object, symbol_index: usize, offset: Simm) -> Option<&str> {
@@ -530,14 +538,15 @@ fn generate_flow_analysis_result(
530538
code: &[u8],
531539
register_state_at: Vec<RegisterState>,
532540
relocations: &[Relocation],
541+
extensions: Extensions,
533542
) -> Box<PPCFlowAnalysisResult> {
534-
use ppc750cl::{Argument, InsIter};
543+
use powerpc::{Argument, InsIter};
535544
let mut analysis_result = PPCFlowAnalysisResult::new();
536545
let default_register_state = RegisterState::new();
537-
for (addr, ins) in InsIter::new(code, 0) {
546+
for (addr, ins) in InsIter::new(code, 0, extensions) {
538547
let ins_address = base_address + (addr as u64);
539548
let index = addr / 4;
540-
let ppc750cl::ParsedIns { mnemonic: _, args } = ins.simplified();
549+
let powerpc::ParsedIns { mnemonic: _, args } = ins.simplified();
541550

542551
// If we're already showing relocations on a line don't also show data flow
543552
let reloc = relocations.iter().find(|r| (r.address & !3) == ins_address);
@@ -546,7 +555,7 @@ fn generate_flow_analysis_result(
546555
// they are being loaded.
547556
// We need to do this before we break out on showing relocations in the
548557
// subsequent if statement.
549-
if let (ppc750cl::Opcode::Lfs | ppc750cl::Opcode::Lfd, Some(reloc)) = (ins.op, reloc) {
558+
if let (powerpc::Opcode::Lfs | powerpc::Opcode::Lfd, Some(reloc)) = (ins.op, reloc) {
550559
let content = get_register_content_from_reloc(reloc, obj, ins.op);
551560
if matches!(
552561
content,
@@ -566,7 +575,7 @@ fn generate_flow_analysis_result(
566575
// Special case to show string constants on the line where they are
567576
// being indexed to. This will typically be "addi t, stringbase, offset"
568577
let registers = register_state_at.get(index as usize).unwrap_or(&default_register_state);
569-
if let (ppc750cl::Opcode::Addi, Argument::GPR(rel), Argument::Simm(offset)) =
578+
if let (powerpc::Opcode::Addi, Argument::GPR(rel), Argument::Simm(offset)) =
570579
(ins.op, args[1], args[2])
571580
{
572581
if let RegisterContent::Symbol(sym_index) = registers[rel] {

0 commit comments

Comments
 (0)