mirror of
https://github.com/encounter/decomp-permuter.git
synced 2026-07-10 03:18:46 -07:00
Add arch settings for ARM32 (#127)
This commit is contained in:
@@ -8,9 +8,7 @@ The modes can also be combined, by using the `PERM_RANDOMIZE` macro.
|
||||
|
||||
[<img src="https://asciinema.org/a/232846.svg" height="300">](https://asciinema.org/a/232846)
|
||||
|
||||
The main target for the tool is MIPS code compiled by old compilers (IDO, possibly GCC).
|
||||
Getting it to work on other architectures shouldn't be too hard, however.
|
||||
https://github.com/laqieer/decomp-permuter-arm has an ARM port.
|
||||
This tool supports MIPS (compiled by IDO, possibly GCC), PowerPC, and ARM32 assembly.
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
### NOTE: Ensure that `arm-none-eabi-objdump` is in your environment PATH var.
|
||||
|
||||
### Set this to the root of your decomp project repository.
|
||||
WORKING_DIR="/home/user/pokeheartgold"
|
||||
|
||||
### Set this to the relative path of the C compiler in your repo.
|
||||
CC="tools/mwccarm/2.0/sp2p2/mwccarm.exe"
|
||||
|
||||
C_NAME="$1"
|
||||
O_NAME="$3"
|
||||
|
||||
pushd $WORKING_DIR
|
||||
|
||||
### Change any compile flags here, and remove wine if you are using windows ofc.
|
||||
wine $CC -O4,p -enum int -lang c99 -Cpp_exceptions off -gccext,on -proc arm946e -msgstyle gcc -gccinc -i ./include -ipa file -interworking -inline on,noauto -char signed -gccdep -MD -c -o $O_NAME $C_NAME
|
||||
|
||||
popd
|
||||
+124
-54
@@ -40,6 +40,7 @@ class ArchSettings:
|
||||
re_reg: Pattern[str]
|
||||
re_sprel: Pattern[str]
|
||||
re_includes_sp: Pattern[str]
|
||||
sp_ref_insns: List[str]
|
||||
reloc_str: str
|
||||
branch_instructions: Set[str]
|
||||
forbidden: Set[str] = field(default_factory=lambda: set(string.ascii_letters + "_"))
|
||||
@@ -100,6 +101,34 @@ PPC_BRANCH_INSTRUCTIONS = {
|
||||
|
||||
PPC_BRANCH_LIKELY_INSTRUCTIONS: Set[str] = set()
|
||||
|
||||
ARM32_PREFIXES = {"b", "bl"}
|
||||
ARM32_CONDS = {
|
||||
"",
|
||||
"eq",
|
||||
"ne",
|
||||
"cs",
|
||||
"cc",
|
||||
"mi",
|
||||
"pl",
|
||||
"vs",
|
||||
"vc",
|
||||
"hi",
|
||||
"ls",
|
||||
"ge",
|
||||
"lt",
|
||||
"gt",
|
||||
"le",
|
||||
"al",
|
||||
}
|
||||
ARM32_SUFFIXES = {"", ".n", ".w"}
|
||||
ARM32_BRANCH_INSTRUCTIONS = {
|
||||
f"{prefix}{cond}{suffix}"
|
||||
for prefix in ARM32_PREFIXES
|
||||
for cond in ARM32_CONDS
|
||||
for suffix in ARM32_SUFFIXES
|
||||
}
|
||||
|
||||
|
||||
MIPS_SETTINGS: ArchSettings = ArchSettings(
|
||||
name="mips",
|
||||
re_comment=re.compile(r"<.*?>"),
|
||||
@@ -108,6 +137,7 @@ MIPS_SETTINGS: ArchSettings = ArchSettings(
|
||||
),
|
||||
re_sprel=re.compile(r"(?<=,)([0-9]+|0x[0-9a-f]+)\((sp|s8)\)"),
|
||||
re_includes_sp=re.compile(r"\b(sp|s8)\b"),
|
||||
sp_ref_insns=["addiu"],
|
||||
reloc_str="R_MIPS_",
|
||||
objdump=["mips-linux-gnu-objdump", "-drz", "-m", "mips:4300"],
|
||||
branch_likely_instructions=MIPS_BRANCH_LIKELY_INSTRUCTIONS,
|
||||
@@ -118,6 +148,7 @@ MIPS_SETTINGS: ArchSettings = ArchSettings(
|
||||
PPC_SETTINGS: ArchSettings = ArchSettings(
|
||||
name="ppc",
|
||||
re_includes_sp=re.compile(r"\b(r1)\b"),
|
||||
sp_ref_insns=[],
|
||||
re_comment=re.compile(r"(<.*>|//.*$)"),
|
||||
re_reg=re.compile(r"\$?\b([rf][0-9]+)\b"),
|
||||
re_sprel=re.compile(r"(?<=,)(-?[0-9]+|-?0x[0-9a-f]+)\(r1\)"),
|
||||
@@ -128,6 +159,27 @@ PPC_SETTINGS: ArchSettings = ArchSettings(
|
||||
)
|
||||
|
||||
|
||||
ARM32_SETTINGS: ArchSettings = ArchSettings(
|
||||
name="arm32",
|
||||
re_includes_sp=re.compile(r"\b(sp)\b"),
|
||||
sp_ref_insns=["add", "sub"],
|
||||
re_comment=re.compile(r"(<.*>|//.*$)"),
|
||||
# Includes:
|
||||
# - General purpose registers: r0..13
|
||||
# - Frame pointer registers: lr (r14), pc (r15)
|
||||
# - VFP/NEON registers: s0..31, d0..31, q0..15, fpscr, fpexc, fpsid
|
||||
# SP should not be in this list.
|
||||
re_reg=re.compile(
|
||||
r"\$?\b([rq][0-9]|[rq]1[0-5]|pc|lr|[ds][12]?[0-9]|[ds]3[01]|fp(scr|exc|sid))\b"
|
||||
),
|
||||
re_sprel=re.compile(r"sp, #-?(0x[0-9a-fA-F]+|[0-9]+)\b"),
|
||||
reloc_str="R_ARM_",
|
||||
objdump=["arm-none-eabi-objdump", "-drz"],
|
||||
branch_instructions=ARM32_BRANCH_INSTRUCTIONS,
|
||||
branch_likely_instructions=set(),
|
||||
)
|
||||
|
||||
|
||||
def get_arch(o_file: str) -> ArchSettings:
|
||||
# https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
|
||||
with open(o_file, "rb") as f:
|
||||
@@ -140,7 +192,8 @@ def get_arch(o_file: str) -> ArchSettings:
|
||||
return MIPS_SETTINGS
|
||||
if arch == 20:
|
||||
return PPC_SETTINGS
|
||||
# TODO: support ARM (0x28)
|
||||
if arch == 40:
|
||||
return ARM32_SETTINGS
|
||||
raise Exception("Bad ELF")
|
||||
|
||||
|
||||
@@ -189,6 +242,67 @@ def pre_process(mnemonic: str, args: str, next_row: Optional[str]) -> Tuple[str,
|
||||
return mnemonic, args
|
||||
|
||||
|
||||
def process_mips_reloc(reloc_row: str, prev: str, repl: str, imm: str) -> str:
|
||||
# Sometimes s8 is used as a non-framepointer, but we've already lost
|
||||
# the immediate value by pretending it is one. This isn't too bad,
|
||||
# since it's rare and applies consistently. But we do need to handle it
|
||||
# here to avoid a crash, by pretending that lost imms are zero for
|
||||
# relocations.
|
||||
if imm != "0" and imm != "imm" and imm != "addr":
|
||||
repl += "+" + imm if int(imm, 0) > 0 else imm
|
||||
if any(
|
||||
reloc in reloc_row
|
||||
for reloc in ["R_MIPS_LO16", "R_MIPS_LITERAL", "R_MIPS_GPREL16"]
|
||||
):
|
||||
repl = f"%lo({repl})"
|
||||
elif "R_MIPS_HI16" in reloc_row:
|
||||
# Ideally we'd pair up R_MIPS_LO16 and R_MIPS_HI16 to generate a
|
||||
# correct addend for each, but objdump doesn't give us the order of
|
||||
# the relocations, so we can't find the right LO16. :(
|
||||
repl = f"%hi({repl})"
|
||||
else:
|
||||
assert "R_MIPS_26" in reloc_row, f"unknown relocation type '{reloc_row}'"
|
||||
return repl
|
||||
|
||||
|
||||
def process_ppc_reloc(reloc_row: str, prev: str, repl: str) -> str:
|
||||
assert any(
|
||||
r in reloc_row for r in ["R_PPC_REL24", "R_PPC_ADDR16", "R_PPC_EMB_SDA21"]
|
||||
), f"unknown relocation type '{reloc_row}' for line '{prev}'"
|
||||
|
||||
if "R_PPC_REL24" in reloc_row:
|
||||
# function calls
|
||||
return repl
|
||||
elif "R_PPC_ADDR16_HI" in reloc_row:
|
||||
# absolute hi of addr
|
||||
return f"{repl}@h"
|
||||
elif "R_PPC_ADDR16_HA" in reloc_row:
|
||||
# adjusted hi of addr
|
||||
return f"{repl}@ha"
|
||||
elif "R_PPC_ADDR16_LO" in reloc_row:
|
||||
# lo of addr
|
||||
return f"{repl}@l"
|
||||
elif "R_PPC_ADDR16" in reloc_row:
|
||||
# 16-bit absolute addr
|
||||
if "+0x7" in repl:
|
||||
# remove the very large addends as they are an artifact of (label-_SDA(2)_BASE_)
|
||||
# computations and are unimportant in a diff setting.
|
||||
if int(repl.split("+")[1], 16) > 0x70000000:
|
||||
return repl.split("+")[0]
|
||||
elif "R_PPC_EMB_SDA21" in reloc_row:
|
||||
# sda21 relocations; r2/r13 --> 0 swaps are performed in an earlier processing step
|
||||
return f"{repl}@sda21"
|
||||
return repl
|
||||
|
||||
|
||||
def process_arm32_reloc(reloc_row: str, prev: str, repl: str) -> str:
|
||||
assert any(
|
||||
r in reloc_row for r in ["R_ARM_THM_CALL", "R_ARM_CALL", "R_ARM_ABS32"]
|
||||
), f"unknown relocation type '{reloc_row}' for line '{prev}'"
|
||||
|
||||
return repl
|
||||
|
||||
|
||||
def process_reloc(reloc_row: str, prev: str) -> Optional[str]:
|
||||
if prev == "<skipped>":
|
||||
return None
|
||||
@@ -201,60 +315,16 @@ def process_reloc(reloc_row: str, prev: str) -> Optional[str]:
|
||||
assert ign_branch_targets
|
||||
return None
|
||||
|
||||
if "R_PPC_" in reloc_row:
|
||||
|
||||
assert any(
|
||||
r in reloc_row for r in ["R_PPC_REL24", "R_PPC_ADDR16", "R_PPC_EMB_SDA21"]
|
||||
), f"unknown relocation type '{reloc_row}' for line '{prev}'"
|
||||
|
||||
if "R_PPC_REL24" in reloc_row:
|
||||
# function calls
|
||||
pass
|
||||
elif "R_PPC_ADDR16_HI" in reloc_row:
|
||||
# absolute hi of addr
|
||||
repl = f"{repl}@h"
|
||||
elif "R_PPC_ADDR16_HA" in reloc_row:
|
||||
# adjusted hi of addr
|
||||
repl = f"{repl}@ha"
|
||||
elif "R_PPC_ADDR16_LO" in reloc_row:
|
||||
# lo of addr
|
||||
repl = f"{repl}@l"
|
||||
elif "R_PPC_ADDR16" in reloc_row:
|
||||
# 16-bit absolute addr
|
||||
if "+0x7" in repl:
|
||||
# remove the very large addends as they are an artifact of (label-_SDA(2)_BASE_)
|
||||
# computations and are unimportant in a diff setting.
|
||||
if int(repl.split("+")[1], 16) > 0x70000000:
|
||||
repl = repl.split("+")[0]
|
||||
elif "R_PPC_EMB_SDA21" in reloc_row:
|
||||
# sda21 relocations; r2/r13 --> 0 swaps are performed in an earlier processing step
|
||||
repl = f"{repl}@sda21"
|
||||
|
||||
return before + repl + after
|
||||
|
||||
if "R_MIPS_" in reloc_row:
|
||||
# Sometimes s8 is used as a non-framepointer, but we've already lost
|
||||
# the immediate value by pretending it is one. This isn't too bad,
|
||||
# since it's rare and applies consistently. But we do need to handle it
|
||||
# here to avoid a crash, by pretending that lost imms are zero for
|
||||
# relocations.
|
||||
if imm != "0" and imm != "imm" and imm != "addr":
|
||||
repl += "+" + imm if int(imm, 0) > 0 else imm
|
||||
if any(
|
||||
reloc in reloc_row
|
||||
for reloc in ["R_MIPS_LO16", "R_MIPS_LITERAL", "R_MIPS_GPREL16"]
|
||||
):
|
||||
repl = f"%lo({repl})"
|
||||
elif "R_MIPS_HI16" in reloc_row:
|
||||
# Ideally we'd pair up R_MIPS_LO16 and R_MIPS_HI16 to generate a
|
||||
# correct addend for each, but objdump doesn't give us the order of
|
||||
# the relocations, so we can't find the right LO16. :(
|
||||
repl = f"%hi({repl})"
|
||||
else:
|
||||
assert "R_MIPS_26" in reloc_row, f"unknown relocation type '{reloc_row}'"
|
||||
return before + repl + after
|
||||
new_repl = process_mips_reloc(reloc_row, prev, repl, imm)
|
||||
elif "R_PPC_" in reloc_row:
|
||||
new_repl = process_ppc_reloc(reloc_row, prev, repl)
|
||||
elif "R_ARM_" in reloc_row:
|
||||
new_repl = process_arm32_reloc(reloc_row, prev, repl)
|
||||
else:
|
||||
raise Exception(f"unknown relocation type: {reloc_row}")
|
||||
|
||||
raise Exception(f"unknown relocation type: {reloc_row}")
|
||||
return before + new_repl + after
|
||||
|
||||
|
||||
def simplify_objdump(
|
||||
@@ -305,7 +375,7 @@ def simplify_objdump(
|
||||
row = re.sub(arch.re_reg, "<reg>", row)
|
||||
|
||||
if not stack_differences:
|
||||
if mnemonic == "addiu" and arch.re_includes_sp.search(args):
|
||||
if mnemonic in arch.sp_ref_insns and arch.re_includes_sp.search(args):
|
||||
row = re.sub(re_int_full, "imm", row)
|
||||
if mnemonic in arch.branch_instructions:
|
||||
if ign_branch_targets:
|
||||
|
||||
@@ -59,6 +59,10 @@ class Scorer:
|
||||
if arch.name == "mips":
|
||||
return "." in field
|
||||
|
||||
# Example: ".text+0x34"
|
||||
if arch.name == "arm32":
|
||||
return "." in field
|
||||
|
||||
return False
|
||||
|
||||
def diff_sameline(old_line: Line, new_line: Line) -> None:
|
||||
|
||||
Reference in New Issue
Block a user