Skip to content

Commit 9798116

Browse files
AetiasHaxencounter
andauthored
ARMv4T (GBA) and ARMv6K (3DS) support (#75)
* Initial ARM support * Disassemble const pool reloc * Disasm ARM/Thumb/data based on mapping symbols * Fallback to mapping symbol `$a` * Support multiple DWARF sequences * Update line info * Rework DWARF line info parsing - Properly handles multiple sections in DWARF 1 - line_info moved into ObjSection - DWARF 2 parser no longer errors with no .text section - Both parsers properly skip empty sections * Simplify line_info (no Option) * Get line info from section; output formatted ins string * Unwrap code section in `arm.rs` * Handle reloc `R_ARM_SBREL32` * Update ARM disassembler * Update README.md * Format * Revert "Update README.md" This reverts commit 8bbfcc6. * Update README.md * Detect ARM version; support ARMv4T and v6K * Combobox to force ARM version * Clear LSB in ARM symbol addresses * Support big-endian ARM ELF files * Bump `unarm`, `arm-attr` * Handle ARM implicit addends * Update README.md * Explicitly handle all ARM argument types * Format * Display more ARM relocs * Mask LSB on ARM code symbols only * Read ARM implicit addends * Format --------- Co-authored-by: Luke Street <luke.street@encounterpc.com>
1 parent 1fd901a commit 9798116

File tree

9 files changed

+219
-41
lines changed

9 files changed

+219
-41
lines changed

Cargo.lock

Lines changed: 17 additions & 7 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
@@ -17,7 +17,7 @@ Supports:
1717
- PowerPC 750CL (GameCube, Wii)
1818
- MIPS (N64, PS1, PS2, PSP)
1919
- x86 (COFF only at the moment)
20-
- ARMv5 (DS)
20+
- ARM (GBA, DS, 3DS)
2121

2222
See [Usage](#usage) for more information.
2323

objdiff-cli/src/cmd/diff.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -816,6 +816,7 @@ impl FunctionDiffUi {
816816
x86_formatter: Default::default(), // TODO
817817
mips_abi: Default::default(), // TODO
818818
mips_instr_category: Default::default(), // TODO
819+
arm_arch_version: Default::default(), // TODO
819820
};
820821
let target = self
821822
.target_path

objdiff-core/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ dwarf = ["gimli"]
1919
mips = ["any-arch", "rabbitizer"]
2020
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
2121
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
22-
arm = ["any-arch", "cpp_demangle", "unarm"]
22+
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]
2323

2424
[dependencies]
2525
anyhow = "1.0.82"
@@ -56,4 +56,5 @@ iced-x86 = { version = "1.21.0", default-features = false, features = ["std", "d
5656
msvc-demangler = { version = "0.10.0", optional = true }
5757

5858
# arm
59-
unarm = { version = "1.0.0", optional = true }
59+
unarm = { version = "1.3.0", optional = true }
60+
arm-attr = { version = "0.1.1", optional = true }

objdiff-core/src/arch/arm.rs

Lines changed: 144 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ use std::{
44
};
55

66
use anyhow::{bail, Result};
7+
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
78
use object::{
8-
elf, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
9-
SectionKind, Symbol,
9+
elf::{self, SHT_ARM_ATTRIBUTES},
10+
Endian, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
11+
SectionKind, Symbol, SymbolKind,
1012
};
1113
use unarm::{
1214
args::{Argument, OffsetImm, OffsetReg, Register},
@@ -16,41 +18,93 @@ use unarm::{
1618

1719
use crate::{
1820
arch::{ObjArch, ProcessCodeResult},
19-
diff::DiffObjConfig,
21+
diff::{ArmArchVersion, DiffObjConfig},
2022
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
2123
};
2224

2325
pub struct ObjArchArm {
2426
/// Maps section index, to list of disasm modes (arm, thumb or data) sorted by address
2527
disasm_modes: HashMap<SectionIndex, Vec<DisasmMode>>,
28+
detected_version: Option<ArmVersion>,
29+
endianness: object::Endianness,
2630
}
2731

2832
impl ObjArchArm {
2933
pub fn new(file: &File) -> Result<Self> {
34+
let endianness = file.endianness();
3035
match file {
3136
File::Elf32(_) => {
32-
let disasm_modes: HashMap<_, _> = file
33-
.sections()
34-
.filter(|s| s.kind() == SectionKind::Text)
35-
.map(|s| {
36-
let index = s.index();
37-
let mut mapping_symbols: Vec<_> = file
38-
.symbols()
39-
.filter(|s| s.section_index().map(|i| i == index).unwrap_or(false))
40-
.filter_map(|s| DisasmMode::from_symbol(&s))
41-
.collect();
42-
mapping_symbols.sort_unstable_by_key(|x| x.address);
43-
(s.index(), mapping_symbols)
44-
})
45-
.collect();
46-
Ok(Self { disasm_modes })
37+
let disasm_modes = Self::elf_get_mapping_symbols(file);
38+
let detected_version = Self::elf_detect_arm_version(file)?;
39+
Ok(Self { disasm_modes, detected_version, endianness })
4740
}
4841
_ => bail!("Unsupported file format {:?}", file.format()),
4942
}
5043
}
44+
45+
fn elf_detect_arm_version(file: &File) -> Result<Option<ArmVersion>> {
46+
// Check ARM attributes
47+
if let Some(arm_attrs) = file.sections().find(|s| {
48+
s.kind() == SectionKind::Elf(SHT_ARM_ATTRIBUTES) && s.name() == Ok(".ARM.attributes")
49+
}) {
50+
let attr_data = arm_attrs.uncompressed_data()?;
51+
let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() {
52+
object::Endianness::Little => arm_attr::Endian::Little,
53+
object::Endianness::Big => arm_attr::Endian::Big,
54+
})?;
55+
for subsection in build_attrs.subsections() {
56+
let subsection = subsection?;
57+
if !subsection.is_aeabi() {
58+
continue;
59+
}
60+
// Only checking first CpuArch tag. Others may exist, but that's very unlikely.
61+
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
62+
if let Tag::CpuArch(cpu_arch) = tag {
63+
Some(cpu_arch)
64+
} else {
65+
None
66+
}
67+
});
68+
match cpu_arch {
69+
Some(CpuArch::V4T) => return Ok(Some(ArmVersion::V4T)),
70+
Some(CpuArch::V5TE) => return Ok(Some(ArmVersion::V5Te)),
71+
Some(CpuArch::V6K) => return Ok(Some(ArmVersion::V6K)),
72+
Some(arch) => bail!("ARM arch {} not supported", arch),
73+
None => {}
74+
};
75+
}
76+
}
77+
78+
Ok(None)
79+
}
80+
81+
fn elf_get_mapping_symbols(file: &File) -> HashMap<SectionIndex, Vec<DisasmMode>> {
82+
file.sections()
83+
.filter(|s| s.kind() == SectionKind::Text)
84+
.map(|s| {
85+
let index = s.index();
86+
let mut mapping_symbols: Vec<_> = file
87+
.symbols()
88+
.filter(|s| s.section_index().map(|i| i == index).unwrap_or(false))
89+
.filter_map(|s| DisasmMode::from_symbol(&s))
90+
.collect();
91+
mapping_symbols.sort_unstable_by_key(|x| x.address);
92+
(s.index(), mapping_symbols)
93+
})
94+
.collect()
95+
}
5196
}
5297

5398
impl ObjArch for ObjArchArm {
99+
fn symbol_address(&self, symbol: &Symbol) -> u64 {
100+
let address = symbol.address();
101+
if symbol.kind() == SymbolKind::Text {
102+
address & !1
103+
} else {
104+
address
105+
}
106+
}
107+
54108
fn process_code(
55109
&self,
56110
address: u64,
@@ -81,10 +135,21 @@ impl ObjArch for ObjArchArm {
81135
mapping_symbols.iter().skip(first_mapping_idx + 1).take_while(|x| x.address < end_addr);
82136
let mut next_mapping = mappings_iter.next();
83137

84-
let ins_count = code.len() / first_mapping.instruction_size();
138+
let ins_count = code.len() / first_mapping.instruction_size(start_addr);
85139
let mut ops = Vec::<u16>::with_capacity(ins_count);
86140
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
87-
let mut parser = Parser::new(ArmVersion::V5Te, first_mapping, start_addr, code);
141+
142+
let version = match config.arm_arch_version {
143+
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
144+
ArmArchVersion::V4T => ArmVersion::V4T,
145+
ArmArchVersion::V5TE => ArmVersion::V5Te,
146+
ArmArchVersion::V6K => ArmVersion::V6K,
147+
};
148+
let endian = match self.endianness {
149+
object::Endianness::Little => unarm::Endian::Little,
150+
object::Endianness::Big => unarm::Endian::Big,
151+
};
152+
let mut parser = Parser::new(version, first_mapping, start_addr, endian, code);
88153

89154
while let Some((address, op, ins)) = parser.next() {
90155
if let Some(next) = next_mapping {
@@ -95,19 +160,26 @@ impl ObjArch for ObjArchArm {
95160
next_mapping = mappings_iter.next();
96161
}
97162
}
98-
99163
let line = line_info.range(..=address as u64).last().map(|(_, &b)| b);
100164

101165
let reloc = relocations.iter().find(|r| (r.address as u32 & !1) == address).cloned();
102166

103167
let mut reloc_arg = None;
104168
if let Some(reloc) = &reloc {
105169
match reloc.flags {
170+
// Calls
106171
RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 }
107-
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 } => {
172+
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
173+
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
174+
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
175+
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
108176
reloc_arg =
109177
ins.args.iter().rposition(|a| matches!(a, Argument::BranchDest(_)));
110178
}
179+
// Data
180+
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
181+
reloc_arg = ins.args.iter().rposition(|a| matches!(a, Argument::UImm(_)));
182+
}
111183
_ => (),
112184
}
113185
};
@@ -138,11 +210,42 @@ impl ObjArch for ObjArchArm {
138210

139211
fn implcit_addend(
140212
&self,
141-
_section: &ObjSection,
213+
section: &ObjSection,
142214
address: u64,
143215
reloc: &Relocation,
144216
) -> anyhow::Result<i64> {
145-
bail!("Unsupported ARM implicit relocation {:#x}{:?}", address, reloc.flags())
217+
let address = address as usize;
218+
Ok(match reloc.flags() {
219+
// ARM calls
220+
RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
221+
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
222+
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
223+
let data = section.data[address..address + 4].try_into()?;
224+
let addend = self.endianness.read_i32_bytes(data);
225+
let imm24 = addend & 0xffffff;
226+
(imm24 << 2) << 8 >> 8
227+
}
228+
229+
// Thumb calls
230+
RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
231+
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 } => {
232+
let data = section.data[address..address + 2].try_into()?;
233+
let high = self.endianness.read_i16_bytes(data) as i32;
234+
let data = section.data[address + 2..address + 4].try_into()?;
235+
let low = self.endianness.read_i16_bytes(data) as i32;
236+
237+
let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
238+
(imm22 << 1) << 9 >> 9
239+
}
240+
241+
// Data
242+
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
243+
let data = section.data[address..address + 4].try_into()?;
244+
self.endianness.read_i32_bytes(data)
245+
}
246+
247+
flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
248+
} as i64)
146249
}
147250

148251
fn demangle(&self, name: &str) -> Option<String> {
@@ -209,6 +312,7 @@ fn push_args(
209312
args.push(ObjInsArg::Reloc);
210313
} else {
211314
match arg {
315+
Argument::None => {}
212316
Argument::Reg(reg) => {
213317
if reg.deref {
214318
deref = true;
@@ -242,7 +346,7 @@ fn push_args(
242346
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("^".to_string().into())));
243347
}
244348
}
245-
Argument::UImm(value) | Argument::CoOpcode(value) => {
349+
Argument::UImm(value) | Argument::CoOpcode(value) | Argument::SatImm(value) => {
246350
args.push(ObjInsArg::PlainText("#".into()));
247351
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
248352
}
@@ -282,7 +386,21 @@ fn push_args(
282386
offset.reg.to_string().into(),
283387
)));
284388
}
285-
_ => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into()))),
389+
Argument::CpsrMode(mode) => {
390+
args.push(ObjInsArg::PlainText("#".into()));
391+
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(mode.mode as u64)));
392+
if mode.writeback {
393+
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
394+
}
395+
}
396+
Argument::CoReg(_)
397+
| Argument::StatusReg(_)
398+
| Argument::StatusMask(_)
399+
| Argument::Shift(_)
400+
| Argument::CpsrFlags(_)
401+
| Argument::Endian(_) => {
402+
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into())))
403+
}
286404
}
287405
}
288406
}

objdiff-core/src/arch/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::{borrow::Cow, collections::BTreeMap};
22

33
use anyhow::{bail, Result};
4-
use object::{Architecture, Object, Relocation, RelocationFlags};
4+
use object::{Architecture, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};
55

66
use crate::{
77
diff::DiffObjConfig,
@@ -34,6 +34,8 @@ pub trait ObjArch: Send + Sync {
3434
fn demangle(&self, _name: &str) -> Option<String> { None }
3535

3636
fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;
37+
38+
fn symbol_address(&self, symbol: &Symbol) -> u64 { symbol.address() }
3739
}
3840

3941
pub struct ProcessCodeResult {

0 commit comments

Comments
 (0)