mirror of
https://github.com/pret/pokerevo.git
synced 2026-08-28 03:34:48 -05:00
fixed bug in #pragma regswap where swapping rA and rB would also swap fA and fB, and added #pragma iswap
This commit is contained in:
4
Makefile
4
Makefile
@@ -155,7 +155,7 @@ $(BUILD_DIR)/%.o: %.s
|
||||
$(PYTHON) $(POSTPROC) $(PROCFLAGS) $@
|
||||
|
||||
$(BUILD_DIR)/%.o: %.cpp
|
||||
$(PYTHON) $(PRAGMAPROC) "$(CC)" "$(CFLAGS) -lang c++ -c" $@ $< -fix-regswaps
|
||||
$(PYTHON) $(PRAGMAPROC) "$(CC)" "$(CFLAGS) -lang c++ -c" $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: %.c
|
||||
$(PYTHON) $(PRAGMAPROC) "$(CC)" "$(CFLAGS) -lang c99 -c" $@ $< -fix-regswaps
|
||||
$(PYTHON) $(PRAGMAPROC) "$(CC)" "$(CFLAGS) -lang c99 -c" $@ $<
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
#### Pragma processor for GameCube/Wii decompilation projects
|
||||
###### by Max Parisi
|
||||
|
||||
This is a custom pragma processor meant for use with matching decompilation projects for GameCube and Wii games. It currently adds support for `#pragma regswap`.
|
||||
This is a custom pragma processor meant for use with matching decompilation projects for GameCube and Wii games. It currently adds support for `#pragma regswap` and `#pragma iswap`.
|
||||
|
||||
If some reconstructed C/C++ source code compiles to object binary whose only differences with a target binary are misallocated registers, you can use `#pragma regswap`
|
||||
to correct the register allocation. This is an alternative to the strategy of inlining the desired assembly that aims to be more compact, self-documenting, and convenient.
|
||||
If some reconstructed C/C++ source code compiles to object binary whose only differences with a target binary are misassigned registers, you can use `#pragma regswap`
|
||||
to correct the register assignment. This is an alternative to the strategy of inlining the desired assembly that aims to be more compact, self-documenting, and convenient.
|
||||
|
||||
`pragma.py` will compile your source code, then fix the regswaps by patching the range of the object file specified in each pragma invocation.
|
||||
`pragma.py` will compile your source code, then perform the register and instruction swaps by patching the range of the object file specified in each pragma invocation.
|
||||
|
||||
## Usage
|
||||
Invoke `#pragma regswap` one or more times in a C or C++ source file with the following parameters:
|
||||
@@ -22,14 +22,19 @@ where,
|
||||
* `regB` - register to swapped with `regA` (r0-r31 or f0-f31)
|
||||
* `startFile` - absolute address of the first function provided by this file (in hex)
|
||||
|
||||
|
||||
|
||||
Then, invoke the pragma processor with the `-fix-regswaps` option:
|
||||
Invoke `#pragma iswap` with the following parameters:
|
||||
```
|
||||
python3 pragma.py [-h] [-fix-regswaps] cc cflags output source
|
||||
#pragma iswap addrA addrB startFile
|
||||
```
|
||||
where,
|
||||
* `addrA` and `addrB` - absolute addresses of the instructions to be swapped
|
||||
|
||||
|
||||
Then, invoke the pragma processor:
|
||||
```
|
||||
python3 pragma.py [-h] cc cflags output source
|
||||
```
|
||||
|
||||
|
||||
## Future Work
|
||||
* Add support for an instruction-swap pragma (`#pragma iswap ...`)
|
||||
* Support for more architectures besides Gekko/Broadway PowerPC
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
# regB: register to swap (r0-r31 or f0-f31)
|
||||
# startFile: absolute address of the first function provided by this file (hex)
|
||||
|
||||
# TODO: add support for an instruction swap pragma
|
||||
# TODO: add "#pragma startaddr <hex-addr>" to avoid rewriting the start address in each regswap pragma?
|
||||
#pragma iswap addrA addrB startFile
|
||||
|
||||
import os
|
||||
import sys
|
||||
@@ -138,6 +137,11 @@ misc_opcode_map = {
|
||||
}
|
||||
}
|
||||
|
||||
class FloatInfo:
|
||||
def __init__(self, is_float, int_regs):
|
||||
self.is_float = is_float
|
||||
self.int_regs = int_regs
|
||||
|
||||
class PPCInstr:
|
||||
|
||||
INSTR_SIZE = 32
|
||||
@@ -184,20 +188,33 @@ class PPCInstr:
|
||||
else:
|
||||
return self.search_opcode_maps(opcode, misc_opcode_map)
|
||||
|
||||
def uses_float_regs(self):
|
||||
op = self.get_opcode()
|
||||
ext_op = self.get_ext_opcode()
|
||||
if op in {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61}:
|
||||
return FloatInfo(True, (11,))
|
||||
elif (op == 4 and ext_op & 0x3F in {6, 7, 38, 39}) or (op == 31 and ext_op in {535, 567, 599, 631, 663, 695, 727, 759, 983}):
|
||||
return FloatInfo(True, (11, 16))
|
||||
elif op in {4, 59, 63}:
|
||||
return FloatInfo(True, ())
|
||||
return FloatInfo(False, ())
|
||||
|
||||
# edit the PPC instruction to swap the registers
|
||||
def swap_registers(self, regA, regB):
|
||||
# DEBUG_v = hex(self.v)
|
||||
info = self.uses_float_regs()
|
||||
reg_fields = self.get_reg_fields()
|
||||
# print(str(reg_fields) + ", " + DEBUG_v)
|
||||
if reg_fields is None:
|
||||
if not reg_fields:
|
||||
return
|
||||
for left in reg_fields:
|
||||
right = left + self.REG_FIELD_SIZE - 1
|
||||
currReg = self.get_field(left, right)
|
||||
if currReg == regA:
|
||||
self.set_field(left, right, regB)
|
||||
elif currReg == regB:
|
||||
self.set_field(left, right, regA)
|
||||
# since r0-r31 occupy 0-31 and f0-31 occupy 32-63,
|
||||
# subtract 32 from regA/regB if the next register field is for a floating point register
|
||||
dec = 0 if not info.is_float or left in info.int_regs else -32
|
||||
if currReg == regA + dec:
|
||||
self.set_field(left, right, regB + dec)
|
||||
elif currReg == regB + dec:
|
||||
self.set_field(left, right, regA + dec)
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
@@ -209,15 +226,13 @@ parser.add_argument("output",
|
||||
help="path to the outputted object file")
|
||||
parser.add_argument("source",
|
||||
help="path to the C/C++ source file")
|
||||
parser.add_argument("-fix-regswaps",
|
||||
help="execute #pragma regswap", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
def parse_reg(str):
|
||||
if str[0] == 'r' or str[0] == 'f':
|
||||
reg = int(str[1:])
|
||||
if reg >= 0 and reg <= 31:
|
||||
return reg
|
||||
return reg if str[0] == 'r' else reg + 32
|
||||
raise ValueError("Failed to parse register argument (can be r0...r31 or f0...f31)")
|
||||
|
||||
class RegswapTask:
|
||||
@@ -227,32 +242,51 @@ class RegswapTask:
|
||||
self.regA = regA
|
||||
self.regB = regB
|
||||
|
||||
class IswapTask:
|
||||
def __init__(self, src, dst):
|
||||
self.src = src # .text section byte offset
|
||||
self.dst = dst # .text section byte offset
|
||||
|
||||
regswap_tasks = []
|
||||
iswap_tasks = []
|
||||
with open(args.source, "r") as src:
|
||||
regswap_pattern = re.compile("[ \t]*#pragma[ \t]+regswap[ \t]+")
|
||||
iswap_pattern = re.compile("[ \t]*#pragma[ \t]+iswap[ \t]+")
|
||||
for line in src:
|
||||
if regswap_pattern.match(line):
|
||||
if args.fix_regswaps:
|
||||
params = line.split()[2:]
|
||||
if len(params) != 5:
|
||||
raise ValueError("ERROR: " + len(params) + " arguments passed to #pragma regswap (expected 5)")
|
||||
start = int(params[0], base=16)
|
||||
end = int(params[1], base=16)
|
||||
regA = parse_reg(params[2])
|
||||
regB = parse_reg(params[3])
|
||||
start_file = int(params[4], base=16)
|
||||
if not (start % 4 == 0 and end % 4 == 0 and start_file % 4 == 0):
|
||||
raise ValueError("Invalid start, end, or start_file arguments (should have 4 byte aligment)")
|
||||
if not (start >= start_file and end > start):
|
||||
raise ValueError("Invalid start, end, or start_file arguments (end must be > start, and start >= start_file)")
|
||||
regswap_tasks.append(RegswapTask(start-start_file, end-start_file, regA, regB))
|
||||
params = line.split()[2:]
|
||||
if len(params) != 5:
|
||||
raise ValueError("ERROR: " + str(len(params)) + " arguments passed to #pragma regswap (expected 5)")
|
||||
start = int(params[0], base=16)
|
||||
end = int(params[1], base=16)
|
||||
regA = parse_reg(params[2])
|
||||
regB = parse_reg(params[3])
|
||||
start_file = int(params[4], base=16)
|
||||
if not (start % 4 == 0 and end % 4 == 0 and start_file % 4 == 0):
|
||||
raise ValueError("Invalid start, end, or start_file arguments (should have 4 byte aligment)")
|
||||
if not (start >= start_file and end > start):
|
||||
raise ValueError("Invalid start, end, or start_file arguments (end must be > start, and start >= start_file)")
|
||||
regswap_tasks.append(RegswapTask(start-start_file, end-start_file, regA, regB))
|
||||
elif iswap_pattern.match(line):
|
||||
params = line.split()[2:]
|
||||
if len(params) != 3:
|
||||
raise ValueError("ERROR: " + str(len(params)) + " arguments passed to #pragma iswap (expected 3)")
|
||||
src = int(params[0], base=16)
|
||||
dst = int(params[1], base=16)
|
||||
start_file = int(params[2], base=16)
|
||||
if not (src % 4 == 0 and dst % 4 == 0 and start_file % 4 == 0):
|
||||
raise ValueError("Invalid src, dst, or start_file arguments (should have 4 byte aligment)")
|
||||
if not (src >= start_file and dst > src):
|
||||
raise ValueError("Invalid src, dst, or start_file arguments (dst must be > src, and src >= start_file)")
|
||||
iswap_tasks.append(IswapTask(src-start_file, dst-start_file))
|
||||
|
||||
subprocess.run([*args.cc.strip().split(' '), *args.cflags.split(' '), "-o", args.output, args.source])
|
||||
|
||||
instrs = []
|
||||
TEXT_INDEX = 1 # NOTE: assumes that mwcceppc always places the .text section header at index 1
|
||||
SHDR_32_SIZE = 40 # size of an Elf32_Shdr object
|
||||
|
||||
if args.fix_regswaps and len(regswap_tasks) != 0:
|
||||
if regswap_tasks or iswap_tasks:
|
||||
with open(args.output, "rb") as f:
|
||||
if f.read(7) != b'\x7FELF\x01\x02\x01':
|
||||
raise ValueError("compiler output is not an current version ELF file for a 32-bit big endian architecture")
|
||||
@@ -279,6 +313,14 @@ if args.fix_regswaps and len(regswap_tasks) != 0:
|
||||
raise ValueError("End address " + (task.end + start_file) + " is past the end of the ELF file's .text section")
|
||||
for i in range(task.start // 4, task.end // 4):
|
||||
instrs[i].swap_registers(task.regA, task.regB)
|
||||
|
||||
# perform iswap tasks
|
||||
for task in iswap_tasks:
|
||||
if task.dst > text_size:
|
||||
raise ValueError("End address " + (task.dst + start_file) + " is past the end of the ELF file's .text section")
|
||||
a = task.src // 4
|
||||
b = task.dst // 4
|
||||
instrs[a], instrs[b] = instrs[b], instrs[a]
|
||||
|
||||
# write patched .text section back to the ELF
|
||||
with open(args.output, "rb+") as f:
|
||||
|
||||
Reference in New Issue
Block a user