Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARMv4T (GBA) and ARMv6K (3DS) support #75

Merged
merged 36 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
58cbb17
Initial ARM support
AetiasHax May 12, 2024
e9b8730
Merge remote-tracking branch 'origin/main' into arm
AetiasHax May 17, 2024
609a668
Disassemble const pool reloc
AetiasHax May 17, 2024
7733459
Disasm ARM/Thumb/data based on mapping symbols
AetiasHax May 18, 2024
2d06abe
Fallback to mapping symbol `$a`
AetiasHax May 18, 2024
ff910d9
Support multiple DWARF sequences
AetiasHax May 20, 2024
de744f3
Merge branch 'line-info' into arm
AetiasHax May 20, 2024
e5ee898
Update line info
AetiasHax May 20, 2024
916f6ea
Merge branch 'refs/heads/main' into line-info
encounter May 20, 2024
ee9cef4
Rework DWARF line info parsing
encounter May 21, 2024
88af321
Simplify line_info (no Option)
encounter May 21, 2024
8862d95
Merge branch 'line-info' into arm
AetiasHax May 21, 2024
5d42c24
Get line info from section; output formatted ins string
AetiasHax May 21, 2024
0f9dd7e
Merge branch 'main' into arm
AetiasHax May 23, 2024
71de776
Unwrap code section in `arm.rs`
AetiasHax May 23, 2024
d4c64d9
Handle reloc `R_ARM_SBREL32`
AetiasHax May 26, 2024
543bff7
Update ARM disassembler
AetiasHax Jun 1, 2024
8bbfcc6
Update README.md
AetiasHax Jun 1, 2024
6b6ae02
Format
AetiasHax Jun 1, 2024
902fdc9
Revert "Update README.md"
AetiasHax Jun 1, 2024
d626286
Update README.md
AetiasHax Jun 1, 2024
a6cc024
Merge branch 'main' into arm
AetiasHax Jun 6, 2024
3acc4be
Detect ARM version; support ARMv4T and v6K
AetiasHax Jun 10, 2024
ce6d124
Combobox to force ARM version
AetiasHax Jun 10, 2024
405701f
Clear LSB in ARM symbol addresses
AetiasHax Jun 15, 2024
23f9b65
Support big-endian ARM ELF files
AetiasHax Jun 15, 2024
46b0ef2
Bump `unarm`, `arm-attr`
AetiasHax Jun 15, 2024
6dc572b
Handle ARM implicit addends
AetiasHax Jun 15, 2024
0861e09
Update README.md
AetiasHax Jun 15, 2024
66d0ece
Explicitly handle all ARM argument types
AetiasHax Jun 15, 2024
65d153b
Format
AetiasHax Jun 15, 2024
ba2f8c6
Display more ARM relocs
AetiasHax Jun 16, 2024
d7ea694
Merge branch 'main' into arm
encounter Jun 19, 2024
2c01fa3
Mask LSB on ARM code symbols only
AetiasHax Jun 20, 2024
ccf01d0
Read ARM implicit addends
AetiasHax Jun 20, 2024
a456b7c
Format
AetiasHax Jun 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Supports:
- PowerPC 750CL (GameCube, Wii)
- MIPS (N64, PS1, PS2, PSP)
- x86 (COFF only at the moment)
- ARMv5 (DS)
- ARM (GBA, DS, 3DS)

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

Expand Down
1 change: 1 addition & 0 deletions objdiff-cli/src/cmd/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,7 @@ impl FunctionDiffUi {
x86_formatter: Default::default(), // TODO
mips_abi: Default::default(), // TODO
mips_instr_category: Default::default(), // TODO
arm_arch_version: Default::default(), // TODO
};
let result = diff::diff_objs(&config, target.as_ref(), base.as_ref(), prev.as_ref())?;

Expand Down
5 changes: 3 additions & 2 deletions objdiff-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dwarf = ["gimli"]
mips = ["any-arch", "rabbitizer"]
ppc = ["any-arch", "cwdemangle", "ppc750cl"]
x86 = ["any-arch", "cpp_demangle", "iced-x86", "msvc-demangler"]
arm = ["any-arch", "cpp_demangle", "unarm"]
arm = ["any-arch", "cpp_demangle", "unarm", "arm-attr"]

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

# arm
unarm = { version = "1.0.0", optional = true }
unarm = { version = "1.3.0", optional = true }
arm-attr = { version = "0.1.1", optional = true }
170 changes: 144 additions & 26 deletions objdiff-core/src/arch/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use std::{
};

use anyhow::{bail, Result};
use arm_attr::{enums::CpuArch, tag::Tag, BuildAttrs};
use object::{
elf, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
SectionKind, Symbol,
elf::{self, SHT_ARM_ATTRIBUTES},
Endian, File, Object, ObjectSection, ObjectSymbol, Relocation, RelocationFlags, SectionIndex,
SectionKind, Symbol, SymbolKind,
};
use unarm::{
args::{Argument, OffsetImm, OffsetReg, Register},
Expand All @@ -16,41 +18,93 @@ use unarm::{

use crate::{
arch::{ObjArch, ProcessCodeResult},
diff::DiffObjConfig,
diff::{ArmArchVersion, DiffObjConfig},
obj::{ObjIns, ObjInsArg, ObjInsArgValue, ObjReloc, ObjSection},
};

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

impl ObjArchArm {
pub fn new(file: &File) -> Result<Self> {
let endianness = file.endianness();
match file {
File::Elf32(_) => {
let disasm_modes: HashMap<_, _> = file
.sections()
.filter(|s| s.kind() == SectionKind::Text)
.map(|s| {
let index = s.index();
let mut mapping_symbols: Vec<_> = file
.symbols()
.filter(|s| s.section_index().map(|i| i == index).unwrap_or(false))
.filter_map(|s| DisasmMode::from_symbol(&s))
.collect();
mapping_symbols.sort_unstable_by_key(|x| x.address);
(s.index(), mapping_symbols)
})
.collect();
Ok(Self { disasm_modes })
let disasm_modes = Self::elf_get_mapping_symbols(file);
let detected_version = Self::elf_detect_arm_version(file)?;
Ok(Self { disasm_modes, detected_version, endianness })
}
_ => bail!("Unsupported file format {:?}", file.format()),
}
}

fn elf_detect_arm_version(file: &File) -> Result<Option<ArmVersion>> {
// Check ARM attributes
if let Some(arm_attrs) = file.sections().find(|s| {
s.kind() == SectionKind::Elf(SHT_ARM_ATTRIBUTES) && s.name() == Ok(".ARM.attributes")
}) {
let attr_data = arm_attrs.uncompressed_data()?;
let build_attrs = BuildAttrs::new(&attr_data, match file.endianness() {
object::Endianness::Little => arm_attr::Endian::Little,
object::Endianness::Big => arm_attr::Endian::Big,
})?;
for subsection in build_attrs.subsections() {
let subsection = subsection?;
if !subsection.is_aeabi() {
continue;
}
// Only checking first CpuArch tag. Others may exist, but that's very unlikely.
let cpu_arch = subsection.into_public_tag_iter()?.find_map(|(_, tag)| {
if let Tag::CpuArch(cpu_arch) = tag {
Some(cpu_arch)
} else {
None
}
});
match cpu_arch {
Some(CpuArch::V4T) => return Ok(Some(ArmVersion::V4T)),
Some(CpuArch::V5TE) => return Ok(Some(ArmVersion::V5Te)),
Some(CpuArch::V6K) => return Ok(Some(ArmVersion::V6K)),
Some(arch) => bail!("ARM arch {} not supported", arch),
None => {}
};
}
}

Ok(None)
}

fn elf_get_mapping_symbols(file: &File) -> HashMap<SectionIndex, Vec<DisasmMode>> {
file.sections()
.filter(|s| s.kind() == SectionKind::Text)
.map(|s| {
let index = s.index();
let mut mapping_symbols: Vec<_> = file
.symbols()
.filter(|s| s.section_index().map(|i| i == index).unwrap_or(false))
.filter_map(|s| DisasmMode::from_symbol(&s))
.collect();
mapping_symbols.sort_unstable_by_key(|x| x.address);
(s.index(), mapping_symbols)
})
.collect()
}
}

impl ObjArch for ObjArchArm {
fn symbol_address(&self, symbol: &Symbol) -> u64 {
let address = symbol.address();
if symbol.kind() == SymbolKind::Text {
address & !1
} else {
address
}
}

fn process_code(
&self,
address: u64,
Expand Down Expand Up @@ -81,10 +135,21 @@ impl ObjArch for ObjArchArm {
mapping_symbols.iter().skip(first_mapping_idx + 1).take_while(|x| x.address < end_addr);
let mut next_mapping = mappings_iter.next();

let ins_count = code.len() / first_mapping.instruction_size();
let ins_count = code.len() / first_mapping.instruction_size(start_addr);
let mut ops = Vec::<u16>::with_capacity(ins_count);
let mut insts = Vec::<ObjIns>::with_capacity(ins_count);
let mut parser = Parser::new(ArmVersion::V5Te, first_mapping, start_addr, code);

let version = match config.arm_arch_version {
ArmArchVersion::Auto => self.detected_version.unwrap_or(ArmVersion::V5Te),
ArmArchVersion::V4T => ArmVersion::V4T,
ArmArchVersion::V5TE => ArmVersion::V5Te,
ArmArchVersion::V6K => ArmVersion::V6K,
};
let endian = match self.endianness {
object::Endianness::Little => unarm::Endian::Little,
object::Endianness::Big => unarm::Endian::Big,
};
let mut parser = Parser::new(version, first_mapping, start_addr, endian, code);

while let Some((address, op, ins)) = parser.next() {
if let Some(next) = next_mapping {
Expand All @@ -95,19 +160,26 @@ impl ObjArch for ObjArchArm {
next_mapping = mappings_iter.next();
}
}

let line = line_info.range(..=address as u64).last().map(|(_, &b)| b);

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

let mut reloc_arg = None;
if let Some(reloc) = &reloc {
match reloc.flags {
// Calls
RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 }
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 } => {
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
| RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
reloc_arg =
ins.args.iter().rposition(|a| matches!(a, Argument::BranchDest(_)));
}
// Data
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
reloc_arg = ins.args.iter().rposition(|a| matches!(a, Argument::UImm(_)));
}
_ => (),
}
};
Expand Down Expand Up @@ -138,11 +210,42 @@ impl ObjArch for ObjArchArm {

fn implcit_addend(
&self,
_section: &ObjSection,
section: &ObjSection,
address: u64,
reloc: &Relocation,
) -> anyhow::Result<i64> {
bail!("Unsupported ARM implicit relocation {:#x}{:?}", address, reloc.flags())
let address = address as usize;
Ok(match reloc.flags() {
// ARM calls
RelocationFlags::Elf { r_type: elf::R_ARM_PC24 }
| RelocationFlags::Elf { r_type: elf::R_ARM_XPC25 }
| RelocationFlags::Elf { r_type: elf::R_ARM_CALL } => {
let data = section.data[address..address + 4].try_into()?;
let addend = self.endianness.read_i32_bytes(data);
let imm24 = addend & 0xffffff;
(imm24 << 2) << 8 >> 8
}

// Thumb calls
RelocationFlags::Elf { r_type: elf::R_ARM_THM_PC22 }
| RelocationFlags::Elf { r_type: elf::R_ARM_THM_XPC22 } => {
let data = section.data[address..address + 2].try_into()?;
let high = self.endianness.read_i16_bytes(data) as i32;
let data = section.data[address + 2..address + 4].try_into()?;
let low = self.endianness.read_i16_bytes(data) as i32;

let imm22 = ((high & 0x7ff) << 11) | (low & 0x7ff);
(imm22 << 1) << 9 >> 9
}

// Data
RelocationFlags::Elf { r_type: elf::R_ARM_ABS32 } => {
let data = section.data[address..address + 4].try_into()?;
self.endianness.read_i32_bytes(data)
}

flags => bail!("Unsupported ARM implicit relocation {flags:?}"),
} as i64)
}

fn demangle(&self, name: &str) -> Option<String> {
Expand Down Expand Up @@ -209,6 +312,7 @@ fn push_args(
args.push(ObjInsArg::Reloc);
} else {
match arg {
Argument::None => {}
Argument::Reg(reg) => {
if reg.deref {
deref = true;
Expand Down Expand Up @@ -242,7 +346,7 @@ fn push_args(
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("^".to_string().into())));
}
}
Argument::UImm(value) | Argument::CoOpcode(value) => {
Argument::UImm(value) | Argument::CoOpcode(value) | Argument::SatImm(value) => {
args.push(ObjInsArg::PlainText("#".into()));
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(*value as u64)));
}
Expand Down Expand Up @@ -282,7 +386,21 @@ fn push_args(
offset.reg.to_string().into(),
)));
}
_ => args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into()))),
Argument::CpsrMode(mode) => {
args.push(ObjInsArg::PlainText("#".into()));
args.push(ObjInsArg::Arg(ObjInsArgValue::Unsigned(mode.mode as u64)));
if mode.writeback {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque("!".into())));
}
}
Argument::CoReg(_)
| Argument::StatusReg(_)
| Argument::StatusMask(_)
| Argument::Shift(_)
| Argument::CpsrFlags(_)
| Argument::Endian(_) => {
args.push(ObjInsArg::Arg(ObjInsArgValue::Opaque(arg.to_string().into())))
}
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion objdiff-core/src/arch/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{borrow::Cow, collections::BTreeMap};

use anyhow::{bail, Result};
use object::{Architecture, Object, Relocation, RelocationFlags};
use object::{Architecture, Object, ObjectSymbol, Relocation, RelocationFlags, Symbol};

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

fn display_reloc(&self, flags: RelocationFlags) -> Cow<'static, str>;

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

pub struct ProcessCodeResult {
Expand Down
Loading