mirror of
https://github.com/projectPiki/pikmin.git
synced 2024-11-23 05:19:50 +00:00
Initial commit
This commit is contained in:
commit
01851b93c4
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
.vscode
|
||||
*.dat
|
||||
*.exe
|
||||
*.dll
|
||||
*.idb
|
||||
*.id0
|
||||
*.id1
|
||||
*.id2
|
||||
*.nam
|
||||
*.til
|
||||
*.o
|
||||
*.out
|
||||
*.elf
|
||||
*.dol
|
||||
*.a
|
||||
*.d
|
||||
*.map
|
||||
*.exe
|
||||
*.dump
|
||||
*.7z
|
||||
*.bat
|
||||
build
|
||||
tools/mwcc_compiler/
|
117
Makefile
Normal file
117
Makefile
Normal file
@ -0,0 +1,117 @@
|
||||
ifneq ($(findstring MINGW,$(shell uname)),)
|
||||
WINDOWS := 1
|
||||
endif
|
||||
ifneq ($(findstring MSYS,$(shell uname)),)
|
||||
WINDOWS := 1
|
||||
endif
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Files
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
TARGET_COL := gc
|
||||
|
||||
NAME := pikmin
|
||||
VERSION := usa.1
|
||||
#VERSION := usa.0
|
||||
|
||||
BUILD_DIR := build/$(NAME).$(VERSION)
|
||||
|
||||
SRC_DIRS := src
|
||||
ASM_DIRS := asm
|
||||
|
||||
# Inputs
|
||||
S_FILES := $(wildcard asm/*.s)
|
||||
C_FILES := $(wildcard src/*.c)
|
||||
LDSCRIPT := $(BUILD_DIR)/ldscript.lcf
|
||||
|
||||
# Outputs
|
||||
DOL := $(BUILD_DIR)/main.dol
|
||||
ELF := $(DOL:.dol=.elf)
|
||||
MAP := $(BUILD_DIR)/pikmin1.map
|
||||
|
||||
include obj_files.mk
|
||||
|
||||
O_FILES := $(INIT_O_FILES) $(EXTAB_O_FILES) $(EXTABINDEX_O_FILES) $(TEXT_O_FILES) \
|
||||
$(CTORS_O_FILES) $(DTORS_O_FILES) $(RODATA_O_FILES) $(DATA_O_FILES) \
|
||||
$(BSS_O_FILES) $(SDATA_O_FILES) $(SBSS_O_FILES) $(SDATA2_O_FILES) \
|
||||
$(SBSS2_O_FILES) \
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Tools
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
MWCC_VERSION := 1.0
|
||||
MWLD_VERSION := 1.1
|
||||
|
||||
# Programs
|
||||
ifeq ($(WINDOWS),1)
|
||||
WINE :=
|
||||
else
|
||||
WINE := wine
|
||||
endif
|
||||
AS := $(DEVKITPPC)/bin/powerpc-eabi-as.exe
|
||||
OBJCOPY := $(DEVKITPPC)/bin/powerpc-eabi-objcopy.exe
|
||||
CPP := $(DEVKITPPC)/bin/powerpc-eabi-cpp.exe -P
|
||||
CC := $(WINE) tools/mwcc_compiler/$(MWCC_VERSION)/mwcceppc.exe
|
||||
LD := $(WINE) tools/mwcc_compiler/$(MWLD_VERSION)/mwldeppc.exe
|
||||
ELF2DOL := tools/elf2dol
|
||||
SHA1SUM := sha1sum
|
||||
PYTHON := python3
|
||||
|
||||
POSTPROC := tools/postprocess.py
|
||||
|
||||
# Options
|
||||
INCLUDES := -i include/ -i include/dolphin/ -i src/sysdolphin/ -i include/dolphin/mtx/
|
||||
|
||||
ASFLAGS := -mgekko -I include/
|
||||
LDFLAGS := -map $(MAP) -fp hard -nodefaults
|
||||
CFLAGS := -Cpp_exceptions off -proc gekko -fp hard -O4,p -nodefaults -msgstyle gcc $(INCLUDES)
|
||||
|
||||
# for postprocess.py
|
||||
PROCFLAGS := -fprologue-fixup=old_stack
|
||||
|
||||
# elf2dol needs to know these in order to calculate sbss correctly.
|
||||
SDATA_PDHR := 9
|
||||
SBSS_PDHR := 10
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Recipes
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
### Default target ###
|
||||
|
||||
default: all
|
||||
|
||||
all: $(DOL)
|
||||
|
||||
ALL_DIRS := build $(BUILD_DIR) $(addprefix $(BUILD_DIR)/,$(SRC_DIRS) $(ASM_DIRS))
|
||||
|
||||
# Make sure build directory exists before compiling anything
|
||||
DUMMY != mkdir -p $(ALL_DIRS)
|
||||
|
||||
$(LDSCRIPT): ldscript.lcf
|
||||
$(CPP) -MMD -MP -MT $@ -MF $@.d -I include/ -I . -DBUILD_DIR=$(BUILD_DIR) -o $@ $<
|
||||
|
||||
$(DOL): $(ELF) | tools
|
||||
$(ELF2DOL) $< $@ $(SDATA_PDHR) $(SBSS_PDHR) $(TARGET_COL)
|
||||
$(SHA1SUM) -c sha1/$(NAME).$(VERSION).sha1
|
||||
|
||||
clean:
|
||||
rm -f -d -r build
|
||||
|
||||
$(ELF): $(O_FILES) $(LDSCRIPT)
|
||||
$(LD) $(LDFLAGS) -lcf $(LDSCRIPT) $(O_FILES) -o $@
|
||||
# The Metrowerks linker doesn't generate physical addresses in the ELF program headers. This fixes it somehow.
|
||||
$(OBJCOPY) $@ $@
|
||||
|
||||
$(BUILD_DIR)/%.o: %.s
|
||||
$(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
$(PYTHON) $(POSTPROC) $(PROCFLAGS) $@
|
||||
|
||||
### Debug Print ###
|
||||
|
||||
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
|
3
README.MD
Normal file
3
README.MD
Normal file
@ -0,0 +1,3 @@
|
||||
Pikmin 1 (USA Revision 1) disassembly/decompilation
|
||||
|
||||
The elf2dol in tools should be compiled once for Windows.
|
606
asm/bss.s
Normal file
606
asm/bss.s
Normal file
@ -0,0 +1,606 @@
|
||||
.include "macros.inc"
|
||||
.section .bss, "wa" # 0x802E9640 - 0x803E81E5
|
||||
.global dac
|
||||
dac:
|
||||
.skip 0xC
|
||||
.global audio_hp
|
||||
audio_hp:
|
||||
.skip 0x14
|
||||
.global audioproc_mq
|
||||
audioproc_mq:
|
||||
.skip 0x20
|
||||
.global msgbuf
|
||||
msgbuf:
|
||||
.skip 0x40
|
||||
.global finfo$42
|
||||
finfo$42:
|
||||
.skip 0x3C
|
||||
.global cmd$43
|
||||
cmd$43:
|
||||
.skip 0x34
|
||||
.global dsp_buf
|
||||
dsp_buf:
|
||||
.skip 0x10
|
||||
.global dsp_buf_1
|
||||
dsp_buf_1:
|
||||
.skip 0x10
|
||||
.global PLAYER_CALLLIST
|
||||
PLAYER_CALLLIST:
|
||||
.skip 0xC0
|
||||
.global mq
|
||||
mq:
|
||||
.skip 0x20
|
||||
.global msgbuf_1
|
||||
msgbuf_1:
|
||||
.skip 0x200
|
||||
.global CALLSTACK
|
||||
CALLSTACK:
|
||||
.skip 0x8000
|
||||
.global finfo$221
|
||||
finfo$221:
|
||||
.skip 0x3C
|
||||
.global req$222
|
||||
req$222:
|
||||
.skip 0x80
|
||||
.global finfo$264
|
||||
finfo$264:
|
||||
.skip 0x3C
|
||||
.global req$265
|
||||
req$265:
|
||||
.skip 0x20
|
||||
.global finfo$272
|
||||
finfo$272:
|
||||
.skip 0x3C
|
||||
.global req$273
|
||||
req$273:
|
||||
.skip 0x20
|
||||
.global finfo$290
|
||||
finfo$290:
|
||||
.skip 0x3C
|
||||
.global finfo$307
|
||||
finfo$307:
|
||||
.skip 0x3C
|
||||
.global finfo$312
|
||||
finfo$312:
|
||||
.skip 0x3C
|
||||
.global dvd_file
|
||||
dvd_file:
|
||||
.skip 0x800
|
||||
.global dvd_entrynum
|
||||
dvd_entrynum:
|
||||
.skip 0x88
|
||||
.global GLOBAL_CHANNEL
|
||||
GLOBAL_CHANNEL:
|
||||
.skip 0x80
|
||||
.global CHANNEL
|
||||
CHANNEL:
|
||||
.skip 0x14000
|
||||
.global waitp
|
||||
waitp:
|
||||
.skip 0x80
|
||||
.global waittime
|
||||
waittime:
|
||||
.skip 0x80
|
||||
.global DSPCH
|
||||
DSPCH:
|
||||
.skip 0x400
|
||||
.global CH_BUF
|
||||
CH_BUF:
|
||||
.skip 0x6000
|
||||
.global FX_BUF
|
||||
FX_BUF:
|
||||
.skip 0x80
|
||||
.global bankp
|
||||
bankp:
|
||||
.skip 0x400
|
||||
.global wavearc
|
||||
wavearc:
|
||||
.skip 0x400
|
||||
.global wavegroup
|
||||
wavegroup:
|
||||
.skip 0x400
|
||||
.global WS_V2P_TABLE
|
||||
WS_V2P_TABLE:
|
||||
.skip 0x200
|
||||
.global BNK_V2P_TABLE
|
||||
BNK_V2P_TABLE:
|
||||
.skip 0x200
|
||||
.global SINTABLE
|
||||
SINTABLE:
|
||||
.skip 0x408
|
||||
.global FH_TO_FAT
|
||||
FH_TO_FAT:
|
||||
.skip 0x400
|
||||
.global FAT
|
||||
FAT:
|
||||
.skip 0x800
|
||||
.global fattmp
|
||||
fattmp:
|
||||
.skip 0x800
|
||||
.global JV_DIR_NAME
|
||||
JV_DIR_NAME:
|
||||
.skip 0x400
|
||||
.global JV_ARC_NAME
|
||||
JV_ARC_NAME:
|
||||
.skip 0x200
|
||||
.global JV_ARC
|
||||
JV_ARC:
|
||||
.skip 0x40
|
||||
.global finfo$150
|
||||
finfo$150:
|
||||
.skip 0x58
|
||||
.global dmabuffer
|
||||
dmabuffer:
|
||||
.skip 0x10000
|
||||
.global TRACK_LIST
|
||||
TRACK_LIST:
|
||||
.skip 0x100
|
||||
.global SEQ_ARG
|
||||
SEQ_ARG:
|
||||
.skip 0x20
|
||||
.global seq
|
||||
seq:
|
||||
.skip 0x560
|
||||
.global lbl_80320000
|
||||
lbl_80320000:
|
||||
.skip 0x4
|
||||
.global lbl_80320004
|
||||
lbl_80320004:
|
||||
.skip 0x42E9C
|
||||
.global ROOT_OUTER
|
||||
ROOT_OUTER:
|
||||
.skip 0x400
|
||||
.global rootseq
|
||||
rootseq:
|
||||
.skip 0x40
|
||||
.global FREE_SEQP_QUEUE
|
||||
FREE_SEQP_QUEUE:
|
||||
.skip 0x400
|
||||
.global process_stack
|
||||
process_stack:
|
||||
.skip 0x10
|
||||
.global system_se
|
||||
system_se:
|
||||
.skip 0x6C
|
||||
.global system_se_stop
|
||||
system_se_stop:
|
||||
.skip 0x6C
|
||||
.global player_se$123
|
||||
player_se$123:
|
||||
.skip 0x6C
|
||||
.global player_se_stop$189
|
||||
player_se_stop$189:
|
||||
.skip 0x6C
|
||||
.global outerparam$219
|
||||
outerparam$219:
|
||||
.skip 0x40
|
||||
.global bgm
|
||||
bgm:
|
||||
.skip 0xD38
|
||||
.global demo_q
|
||||
demo_q:
|
||||
.skip 0x70
|
||||
.global seq_loadbuffer
|
||||
seq_loadbuffer:
|
||||
.skip 0x400
|
||||
.global rootseq_1
|
||||
rootseq_1:
|
||||
.skip 0x4340
|
||||
.global rootseqhandle
|
||||
rootseqhandle:
|
||||
.skip 0x40
|
||||
.global as
|
||||
as:
|
||||
.skip 0x98
|
||||
.global SC
|
||||
SC:
|
||||
.skip 0x21A50
|
||||
.global copyinfo
|
||||
copyinfo:
|
||||
.skip 0x18
|
||||
.global interleavebuf
|
||||
interleavebuf:
|
||||
.skip 0x38
|
||||
.global filename
|
||||
filename:
|
||||
.skip 0x40
|
||||
.global dvd_buf
|
||||
dvd_buf:
|
||||
.skip 0xC
|
||||
.global file_header
|
||||
file_header:
|
||||
.skip 0x44
|
||||
.global gop_header
|
||||
gop_header:
|
||||
.skip 0x18
|
||||
.global jac_hvqmThread
|
||||
jac_hvqmThread:
|
||||
.skip 0x318
|
||||
.global hvqmStack
|
||||
hvqmStack:
|
||||
.skip 0x1000
|
||||
.global clipTable
|
||||
clipTable:
|
||||
.skip 0x200
|
||||
.global divTable
|
||||
divTable:
|
||||
.skip 0x40
|
||||
.global mcdivTable
|
||||
mcdivTable:
|
||||
.skip 0x800
|
||||
.global fnVerts
|
||||
fnVerts:
|
||||
.skip 0x1800
|
||||
.global fnNorms
|
||||
fnNorms:
|
||||
.skip 0x1800
|
||||
.global fnTexs
|
||||
fnTexs:
|
||||
.skip 0x1000
|
||||
.global matUsed
|
||||
matUsed:
|
||||
.skip 0x100
|
||||
.global sintable
|
||||
sintable:
|
||||
.skip 0x4000
|
||||
.global costable
|
||||
costable:
|
||||
.skip 0x4000
|
||||
.global ident__8Matrix4f
|
||||
ident__8Matrix4f:
|
||||
.skip 0x40
|
||||
.global lbl_80398880
|
||||
lbl_80398880:
|
||||
.skip 0xC
|
||||
.global sys
|
||||
sys:
|
||||
.skip 0x334
|
||||
.global dvdMesgQueue
|
||||
dvdMesgQueue:
|
||||
.skip 0x20
|
||||
.global loadMesgQueue
|
||||
loadMesgQueue:
|
||||
.skip 0x20
|
||||
.global sysMesgQueue
|
||||
sysMesgQueue:
|
||||
.skip 0x20
|
||||
.global aramStream
|
||||
aramStream:
|
||||
.skip 0x14
|
||||
.global lastName
|
||||
lastName:
|
||||
.skip 0x100
|
||||
.global dvdStream
|
||||
dvdStream:
|
||||
.skip 0x54
|
||||
.global dvdBufferedStream
|
||||
dvdBufferedStream:
|
||||
.skip 0x20
|
||||
.global mMemoryTable$1026
|
||||
mMemoryTable$1026:
|
||||
.skip 0x10
|
||||
.global Thread
|
||||
Thread:
|
||||
.skip 0x328
|
||||
.global ThreadStack
|
||||
ThreadStack:
|
||||
.skip 0x2000
|
||||
.global dvdThread
|
||||
dvdThread:
|
||||
.skip 0x320
|
||||
.global dvdThreadStack
|
||||
dvdThreadStack:
|
||||
.skip 0x2000
|
||||
.global sControllerPad
|
||||
sControllerPad:
|
||||
.skip 0x30
|
||||
.global GColors
|
||||
GColors:
|
||||
.skip 0x8
|
||||
.global CardThread
|
||||
CardThread:
|
||||
.skip 0x310
|
||||
.global CardControl
|
||||
CardControl:
|
||||
.skip 0x70
|
||||
.global gameflow
|
||||
gameflow:
|
||||
.skip 0x368
|
||||
.global flowCont
|
||||
flowCont:
|
||||
.skip 0x260
|
||||
.global resultTable
|
||||
resultTable:
|
||||
.skip 0x40
|
||||
.global collExtents
|
||||
collExtents:
|
||||
.skip 0x18
|
||||
.global bcs
|
||||
bcs:
|
||||
.skip 0x408
|
||||
.global cst
|
||||
cst:
|
||||
.skip 0x80
|
||||
.global CardWorkArea
|
||||
CardWorkArea:
|
||||
.skip 0xA000
|
||||
.global cardData
|
||||
cardData:
|
||||
.skip 0x26000
|
||||
.global CardStack
|
||||
CardStack:
|
||||
.skip 0x1DA4
|
||||
.global lbl_803D0004
|
||||
lbl_803D0004:
|
||||
.skip 0x25C
|
||||
.global YtexObj
|
||||
YtexObj:
|
||||
.skip 0x20
|
||||
.global UVtexObj
|
||||
UVtexObj:
|
||||
.skip 0x20
|
||||
.global playbackThread
|
||||
playbackThread:
|
||||
.skip 0x310
|
||||
.global playbackThreadStack
|
||||
playbackThreadStack:
|
||||
.skip 0x1000
|
||||
.global invCamMat
|
||||
invCamMat:
|
||||
.skip 0x40
|
||||
.global triList
|
||||
triList:
|
||||
.skip 0x800
|
||||
.global pikiInfMgr
|
||||
pikiInfMgr:
|
||||
.skip 0x28
|
||||
.global pikiColors__4Piki
|
||||
pikiColors__4Piki:
|
||||
.skip 0x18
|
||||
.global kinokoColors__4Piki
|
||||
kinokoColors__4Piki:
|
||||
.skip 0x18
|
||||
.global _instances__15PikiShapeObject
|
||||
_instances__15PikiShapeObject:
|
||||
.skip 0x10
|
||||
.global deadPikis__8GameStat
|
||||
deadPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global fallPikis__8GameStat
|
||||
fallPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global formationPikis__8GameStat
|
||||
formationPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global freePikis__8GameStat
|
||||
freePikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global workPikis__8GameStat
|
||||
workPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global mePikis__8GameStat
|
||||
mePikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global containerPikis__8GameStat
|
||||
containerPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global bornPikis__8GameStat
|
||||
bornPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global victimPikis__8GameStat
|
||||
victimPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global mapPikis__8GameStat
|
||||
mapPikis__8GameStat:
|
||||
.skip 0xC
|
||||
.global allPikis__8GameStat
|
||||
allPikis__8GameStat:
|
||||
.skip 0x10
|
||||
.global workString__3zen
|
||||
workString__3zen:
|
||||
.skip 0x400
|
||||
.global bloFile_Diary_Table__3zen
|
||||
bloFile_Diary_Table__3zen:
|
||||
.skip 0x28
|
||||
.global pTexTable__Q23zen13StickCallBack
|
||||
pTexTable__Q23zen13StickCallBack:
|
||||
.skip 0x58
|
||||
.global texTable__Q23zen9NumberTex
|
||||
texTable__Q23zen9NumberTex:
|
||||
.skip 0x28
|
||||
.global shadowTexTable__Q23zen9NumberTex
|
||||
shadowTexTable__Q23zen9NumberTex:
|
||||
.skip 0x28
|
||||
.global OSErrorTable
|
||||
OSErrorTable:
|
||||
.skip 0x40
|
||||
.global Ecb
|
||||
Ecb:
|
||||
.skip 0xB0
|
||||
.global Header
|
||||
Header:
|
||||
.skip 0x20
|
||||
.global Scb
|
||||
Scb:
|
||||
.skip 0x58
|
||||
.global Packet
|
||||
Packet:
|
||||
.skip 0x80
|
||||
.global Alarm
|
||||
Alarm:
|
||||
.skip 0xA0
|
||||
.global RunQueue
|
||||
RunQueue:
|
||||
.skip 0x100
|
||||
.global IdleThread
|
||||
IdleThread:
|
||||
.skip 0x310
|
||||
.global DefaultThread
|
||||
DefaultThread:
|
||||
.skip 0x310
|
||||
.global IdleContext
|
||||
IdleContext:
|
||||
.skip 0x2C8
|
||||
.global CommandList
|
||||
CommandList:
|
||||
.skip 0x40
|
||||
.global AlarmForWA
|
||||
AlarmForWA:
|
||||
.skip 0x28
|
||||
.global AlarmForTimeout
|
||||
AlarmForTimeout:
|
||||
.skip 0x28
|
||||
.global AlarmForBreak
|
||||
AlarmForBreak:
|
||||
.skip 0x28
|
||||
.global Prev
|
||||
Prev:
|
||||
.skip 0xC
|
||||
.global Curr
|
||||
Curr:
|
||||
.skip 0x1C
|
||||
.global tmpBuffer
|
||||
tmpBuffer:
|
||||
.skip 0x80
|
||||
.global DummyCommandBlock
|
||||
DummyCommandBlock:
|
||||
.skip 0x30
|
||||
.global ResetAlarm
|
||||
ResetAlarm:
|
||||
.skip 0x28
|
||||
.global WaitingQueue
|
||||
WaitingQueue:
|
||||
.skip 0x20
|
||||
.global bb2Buf
|
||||
bb2Buf:
|
||||
.skip 0x40
|
||||
.global block$16
|
||||
block$16:
|
||||
.skip 0x30
|
||||
.global regs
|
||||
regs:
|
||||
.skip 0x78
|
||||
.global shdwRegs
|
||||
shdwRegs:
|
||||
.skip 0x78
|
||||
.global HorVer
|
||||
HorVer:
|
||||
.skip 0x58
|
||||
.global PADType
|
||||
PADType:
|
||||
.skip 0x10
|
||||
.global Type
|
||||
Type:
|
||||
.skip 0x10
|
||||
.global Origin
|
||||
Origin:
|
||||
.skip 0x30
|
||||
.global cmdProbeDevice
|
||||
cmdProbeDevice:
|
||||
.skip 0x10
|
||||
.global cmdFixDevice
|
||||
cmdFixDevice:
|
||||
.skip 0x10
|
||||
.global __CARDBlock
|
||||
__CARDBlock:
|
||||
.skip 0x210
|
||||
.global __CARDDiskNone
|
||||
__CARDDiskNone:
|
||||
.skip 0x20
|
||||
.global gxData
|
||||
gxData:
|
||||
.skip 0x4F4
|
||||
.global FifoObj
|
||||
FifoObj:
|
||||
.skip 0x84
|
||||
.global DisplayListFifo
|
||||
DisplayListFifo:
|
||||
.skip 0x24
|
||||
.global __savedGXdata
|
||||
__savedGXdata:
|
||||
.skip 0x4F4
|
||||
.global fragmentinfo
|
||||
fragmentinfo:
|
||||
.skip 0x10
|
||||
.global atexit_funcs
|
||||
atexit_funcs:
|
||||
.skip 0x100
|
||||
.global __atexit_funcs
|
||||
__atexit_funcs:
|
||||
.skip 0x100
|
||||
.global gTRKEventQueue
|
||||
gTRKEventQueue:
|
||||
.skip 0x28
|
||||
.global gTRKBigEndian
|
||||
gTRKBigEndian:
|
||||
.skip 0x8
|
||||
.global gTRKMsgBufs
|
||||
gTRKMsgBufs:
|
||||
.skip 0x19B0
|
||||
.global gTRKFramingState
|
||||
gTRKFramingState:
|
||||
.skip 0x14
|
||||
.global gTRKInputPendingPtr
|
||||
gTRKInputPendingPtr:
|
||||
.skip 0x4
|
||||
.global gTRKDispatchTableSize
|
||||
gTRKDispatchTableSize:
|
||||
.skip 0x8
|
||||
.global TRK_saved_exceptionID
|
||||
TRK_saved_exceptionID:
|
||||
.skip 0x4
|
||||
.global gTRKSaveState
|
||||
gTRKSaveState:
|
||||
.skip 0x94
|
||||
.global TRKvalue128_temp
|
||||
TRKvalue128_temp:
|
||||
.skip 0x10
|
||||
.global gTRKState
|
||||
gTRKState:
|
||||
.skip 0xA8
|
||||
.global gTRKCPUState
|
||||
gTRKCPUState:
|
||||
.skip 0x430
|
||||
.global lc_base
|
||||
lc_base:
|
||||
.skip 0x8
|
||||
.global TRK_mainError
|
||||
TRK_mainError:
|
||||
.skip 0x8
|
||||
.global Ecb_1
|
||||
Ecb_1:
|
||||
.skip 0x18
|
||||
.global aram_hp
|
||||
aram_hp:
|
||||
.skip 0x28
|
||||
.global jac_audioThread
|
||||
jac_audioThread:
|
||||
.skip 0x2940
|
||||
.global jac_audioStack
|
||||
jac_audioStack:
|
||||
.skip 0x1000
|
||||
.global jac_dvdThread
|
||||
jac_dvdThread:
|
||||
.skip 0x320
|
||||
.global jac_dvdStack
|
||||
jac_dvdStack:
|
||||
.skip 0x1000
|
||||
.global EX_DSPTASK
|
||||
EX_DSPTASK:
|
||||
.skip 0x40
|
||||
.global CGRP_ARRAY
|
||||
CGRP_ARRAY:
|
||||
.skip 0x40
|
||||
.global aram_mother
|
||||
aram_mother:
|
||||
.skip 0x2C
|
||||
.global EVENT
|
||||
EVENT:
|
||||
.skip 0x1B64
|
||||
.global pic_ctrl
|
||||
pic_ctrl:
|
||||
.skip 0x1B0
|
||||
.global dvd_ctrl
|
||||
dvd_ctrl:
|
||||
# .skip 0xAB40
|
||||
.skip 0x40
|
5
asm/ctors.s
Normal file
5
asm/ctors.s
Normal file
@ -0,0 +1,5 @@
|
||||
.include "macros.inc"
|
||||
.section .ctors, "wa" # 0x80221F60 - 0x80221FC0
|
||||
.global __init_cpp_exceptions_reference
|
||||
__init_cpp_exceptions_reference:
|
||||
.incbin "baserom.dol", 0x21EF60, 0x60
|
6329
asm/data.s
Normal file
6329
asm/data.s
Normal file
File diff suppressed because it is too large
Load Diff
12
asm/dtors.s
Normal file
12
asm/dtors.s
Normal file
@ -0,0 +1,12 @@
|
||||
.include "macros.inc"
|
||||
.section ._dtors, "wa" # 0x80221FC0 - 0x80221FE0
|
||||
.global __destroy_global_chain_reference
|
||||
__destroy_global_chain_reference:
|
||||
.incbin "baserom.dol", 0x21EFC0, 0x4
|
||||
.global __fini_cpp_exceptions_reference
|
||||
__fini_cpp_exceptions_reference:
|
||||
.incbin "baserom.dol", 0x21EFC4, 0x4
|
||||
.global __destroy_global_chain_reference_1
|
||||
__destroy_global_chain_reference_1:
|
||||
.incbin "baserom.dol", 0x21EFC8, 0x18
|
||||
|
6
asm/exidx.s
Normal file
6
asm/exidx.s
Normal file
@ -0,0 +1,6 @@
|
||||
.include "macros.inc"
|
||||
.section ._exidx, "wa" # 0x80005500 - 0x80005560
|
||||
.incbin "baserom.dol", 0x21EF00, 0x30
|
||||
.global lbl_80005530
|
||||
lbl_80005530:
|
||||
.incbin "baserom.dol", 0x21EF30, 0x30
|
6
asm/extab.s
Normal file
6
asm/extab.s
Normal file
@ -0,0 +1,6 @@
|
||||
.include "macros.inc"
|
||||
.section ._extab, "wa" # 0x800054C0 - 0x80005500
|
||||
.global extab
|
||||
extab:
|
||||
.incbin "baserom.dol", 0x21EEC0, 0x40
|
||||
|
2352
asm/init.s
Normal file
2352
asm/init.s
Normal file
File diff suppressed because it is too large
Load Diff
266
asm/rodata.s
Normal file
266
asm/rodata.s
Normal file
@ -0,0 +1,266 @@
|
||||
.include "macros.inc"
|
||||
.section .rodata, "a" # 0x80221FE0 - 0x80222DC0
|
||||
.global lbl_80221FE0
|
||||
lbl_80221FE0:
|
||||
.incbin "baserom.dol", 0x21EFE0, 0xC
|
||||
.global lbl_80221FEC
|
||||
lbl_80221FEC:
|
||||
.incbin "baserom.dol", 0x21EFEC, 0xC
|
||||
.global lbl_80221FF8
|
||||
lbl_80221FF8:
|
||||
.incbin "baserom.dol", 0x21EFF8, 0x10
|
||||
.global lbl_80222008
|
||||
lbl_80222008:
|
||||
.incbin "baserom.dol", 0x21F008, 0xC
|
||||
.global lbl_80222014
|
||||
lbl_80222014:
|
||||
.incbin "baserom.dol", 0x21F014, 0xC
|
||||
.global lbl_80222020
|
||||
lbl_80222020:
|
||||
.incbin "baserom.dol", 0x21F020, 0x20
|
||||
.global lbl_80222040
|
||||
lbl_80222040:
|
||||
.incbin "baserom.dol", 0x21F040, 0x34
|
||||
.global lbl_80222074
|
||||
lbl_80222074:
|
||||
.incbin "baserom.dol", 0x21F074, 0x34
|
||||
.global lbl_802220A8
|
||||
lbl_802220A8:
|
||||
.incbin "baserom.dol", 0x21F0A8, 0x20
|
||||
.global lbl_802220C8
|
||||
lbl_802220C8:
|
||||
.incbin "baserom.dol", 0x21F0C8, 0x14
|
||||
.global lbl_802220DC
|
||||
lbl_802220DC:
|
||||
.incbin "baserom.dol", 0x21F0DC, 0x2C
|
||||
.global lbl_80222108
|
||||
lbl_80222108:
|
||||
.incbin "baserom.dol", 0x21F108, 0x10
|
||||
.global lbl_80222118
|
||||
lbl_80222118:
|
||||
.incbin "baserom.dol", 0x21F118, 0x14
|
||||
.global lbl_8022212C
|
||||
lbl_8022212C:
|
||||
.incbin "baserom.dol", 0x21F12C, 0x14C
|
||||
.global lbl_80222278
|
||||
lbl_80222278:
|
||||
.incbin "baserom.dol", 0x21F278, 0x14
|
||||
.global lbl_8022228C
|
||||
lbl_8022228C:
|
||||
.incbin "baserom.dol", 0x21F28C, 0x14
|
||||
.global mcbtypetrans$1481
|
||||
mcbtypetrans$1481:
|
||||
.incbin "baserom.dol", 0x21F2A0, 0x18
|
||||
.global lbl_802222B8
|
||||
lbl_802222B8:
|
||||
.incbin "baserom.dol", 0x21F2B8, 0x60
|
||||
.global lbl_80222318
|
||||
lbl_80222318:
|
||||
.incbin "baserom.dol", 0x21F318, 0x60
|
||||
.global lbl_80222378
|
||||
lbl_80222378:
|
||||
.incbin "baserom.dol", 0x21F378, 0xC
|
||||
.global lbl_80222384
|
||||
lbl_80222384:
|
||||
.incbin "baserom.dol", 0x21F384, 0xC
|
||||
.global lbl_80222390
|
||||
lbl_80222390:
|
||||
.incbin "baserom.dol", 0x21F390, 0x10
|
||||
.global lbl_802223A0
|
||||
lbl_802223A0:
|
||||
.incbin "baserom.dol", 0x21F3A0, 0x14
|
||||
.global lbl_802223B4
|
||||
lbl_802223B4:
|
||||
.incbin "baserom.dol", 0x21F3B4, 0x1C
|
||||
.global lbl_802223D0
|
||||
lbl_802223D0:
|
||||
.incbin "baserom.dol", 0x21F3D0, 0x10
|
||||
.global lbl_802223E0
|
||||
lbl_802223E0:
|
||||
.incbin "baserom.dol", 0x21F3E0, 0xC
|
||||
.global lbl_802223EC
|
||||
lbl_802223EC:
|
||||
.incbin "baserom.dol", 0x21F3EC, 0xC
|
||||
.global lbl_802223F8
|
||||
lbl_802223F8:
|
||||
.incbin "baserom.dol", 0x21F3F8, 0x10
|
||||
.global lbl_80222408
|
||||
lbl_80222408:
|
||||
.incbin "baserom.dol", 0x21F408, 0x10
|
||||
.global lbl_80222418
|
||||
lbl_80222418:
|
||||
.incbin "baserom.dol", 0x21F418, 0x28
|
||||
.global lbl_80222440
|
||||
lbl_80222440:
|
||||
.incbin "baserom.dol", 0x21F440, 0x20
|
||||
.global lbl_80222460
|
||||
lbl_80222460:
|
||||
.incbin "baserom.dol", 0x21F460, 0x20
|
||||
.global lbl_80222480
|
||||
lbl_80222480:
|
||||
.incbin "baserom.dol", 0x21F480, 0x20
|
||||
.global lbl_802224A0
|
||||
lbl_802224A0:
|
||||
.incbin "baserom.dol", 0x21F4A0, 0x10
|
||||
.global lbl_802224B0
|
||||
lbl_802224B0:
|
||||
.incbin "baserom.dol", 0x21F4B0, 0x1C
|
||||
.global lbl_802224CC
|
||||
lbl_802224CC:
|
||||
.incbin "baserom.dol", 0x21F4CC, 0xC
|
||||
.global lbl_802224D8
|
||||
lbl_802224D8:
|
||||
.incbin "baserom.dol", 0x21F4D8, 0xA8
|
||||
.global lbl_80222580
|
||||
lbl_80222580:
|
||||
.incbin "baserom.dol", 0x21F580, 0x18
|
||||
.global lbl_80222598
|
||||
lbl_80222598:
|
||||
.incbin "baserom.dol", 0x21F598, 0xC
|
||||
.global lbl_802225A4
|
||||
lbl_802225A4:
|
||||
.incbin "baserom.dol", 0x21F5A4, 0x18
|
||||
.global lbl_802225BC
|
||||
lbl_802225BC:
|
||||
.incbin "baserom.dol", 0x21F5BC, 0x18
|
||||
.global lbl_802225D4
|
||||
lbl_802225D4:
|
||||
.incbin "baserom.dol", 0x21F5D4, 0x10
|
||||
.global lbl_802225E4
|
||||
lbl_802225E4:
|
||||
.incbin "baserom.dol", 0x21F5E4, 0x18
|
||||
.global lbl_802225FC
|
||||
lbl_802225FC:
|
||||
.incbin "baserom.dol", 0x21F5FC, 0x18
|
||||
.global lbl_80222614
|
||||
lbl_80222614:
|
||||
.incbin "baserom.dol", 0x21F614, 0x2C
|
||||
.global lbl_80222640
|
||||
lbl_80222640:
|
||||
.incbin "baserom.dol", 0x21F640, 0xC
|
||||
.global lbl_8022264C
|
||||
lbl_8022264C:
|
||||
.incbin "baserom.dol", 0x21F64C, 0xC
|
||||
.global lbl_80222658
|
||||
lbl_80222658:
|
||||
.incbin "baserom.dol", 0x21F658, 0x10
|
||||
.global lbl_80222668
|
||||
lbl_80222668:
|
||||
.incbin "baserom.dol", 0x21F668, 0xC
|
||||
.global lbl_80222674
|
||||
lbl_80222674:
|
||||
.incbin "baserom.dol", 0x21F674, 0xC
|
||||
.global lbl_80222680
|
||||
lbl_80222680:
|
||||
.incbin "baserom.dol", 0x21F680, 0x20
|
||||
.global lbl_802226A0
|
||||
lbl_802226A0:
|
||||
.incbin "baserom.dol", 0x21F6A0, 0x30
|
||||
.global lbl_802226D0
|
||||
lbl_802226D0:
|
||||
.incbin "baserom.dol", 0x21F6D0, 0x10
|
||||
.global lbl_802226E0
|
||||
lbl_802226E0:
|
||||
.incbin "baserom.dol", 0x21F6E0, 0x10
|
||||
.global lbl_802226F0
|
||||
lbl_802226F0:
|
||||
.incbin "baserom.dol", 0x21F6F0, 0x18
|
||||
.global lbl_80222708
|
||||
lbl_80222708:
|
||||
.incbin "baserom.dol", 0x21F708, 0x40
|
||||
.global lbl_80222748
|
||||
lbl_80222748:
|
||||
.incbin "baserom.dol", 0x21F748, 0x10
|
||||
.global __ptmf_null
|
||||
__ptmf_null:
|
||||
.incbin "baserom.dol", 0x21F758, 0x10
|
||||
.global __constants
|
||||
__constants:
|
||||
.incbin "baserom.dol", 0x21F768, 0x18
|
||||
.global bit_values
|
||||
bit_values:
|
||||
.incbin "baserom.dol", 0x21F780, 0x48
|
||||
.global digit_values
|
||||
digit_values:
|
||||
.incbin "baserom.dol", 0x21F7C8, 0x40
|
||||
.global __ctype_map
|
||||
__ctype_map:
|
||||
.incbin "baserom.dol", 0x21F808, 0x100
|
||||
.global __lower_map
|
||||
__lower_map:
|
||||
.incbin "baserom.dol", 0x21F908, 0x100
|
||||
.global __upper_map
|
||||
__upper_map:
|
||||
.incbin "baserom.dol", 0x21FA08, 0x100
|
||||
.global $$2stringBase0
|
||||
$$2stringBase0:
|
||||
.incbin "baserom.dol", 0x21FB08, 0x8
|
||||
.global $$2stringBase0_1
|
||||
$$2stringBase0_1:
|
||||
.incbin "baserom.dol", 0x21FB10, 0x10
|
||||
.global lbl_80222B20
|
||||
lbl_80222B20:
|
||||
.incbin "baserom.dol", 0x21FB20, 0x58
|
||||
.global bp
|
||||
bp:
|
||||
.incbin "baserom.dol", 0x21FB78, 0x10
|
||||
.global dp_h
|
||||
dp_h:
|
||||
.incbin "baserom.dol", 0x21FB88, 0x10
|
||||
.global dp_l
|
||||
dp_l:
|
||||
.incbin "baserom.dol", 0x21FB98, 0x10
|
||||
.global atanhi
|
||||
atanhi:
|
||||
.incbin "baserom.dol", 0x21FBA8, 0x20
|
||||
.global atanlo
|
||||
atanlo:
|
||||
.incbin "baserom.dol", 0x21FBC8, 0x20
|
||||
.global aT
|
||||
aT:
|
||||
.incbin "baserom.dol", 0x21FBE8, 0x58
|
||||
.global atan_coeff$96
|
||||
atan_coeff$96:
|
||||
.incbin "baserom.dol", 0x21FC40, 0x1C
|
||||
.global onep_one_over_xisqr_hi$97
|
||||
onep_one_over_xisqr_hi$97:
|
||||
.incbin "baserom.dol", 0x21FC5C, 0x18
|
||||
.global onep_one_over_xisqr_lo$98
|
||||
onep_one_over_xisqr_lo$98:
|
||||
.incbin "baserom.dol", 0x21FC74, 0x18
|
||||
.global atan_xi_hi$99
|
||||
atan_xi_hi$99:
|
||||
.incbin "baserom.dol", 0x21FC8C, 0x1C
|
||||
.global atan_xi_lo$100
|
||||
atan_xi_lo$100:
|
||||
.incbin "baserom.dol", 0x21FCA8, 0x1C
|
||||
.global one_over_xi_hi$101
|
||||
one_over_xi_hi$101:
|
||||
.incbin "baserom.dol", 0x21FCC4, 0x18
|
||||
.global one_over_xi_lo$102
|
||||
one_over_xi_lo$102:
|
||||
.incbin "baserom.dol", 0x21FCDC, 0x1C
|
||||
.global tmp_float
|
||||
tmp_float:
|
||||
.incbin "baserom.dol", 0x21FCF8, 0x10
|
||||
.global __sincos_on_quadrant
|
||||
__sincos_on_quadrant:
|
||||
.incbin "baserom.dol", 0x21FD08, 0x20
|
||||
.global __sincos_poly
|
||||
__sincos_poly:
|
||||
.incbin "baserom.dol", 0x21FD28, 0x28
|
||||
.global lbl_80222D50
|
||||
lbl_80222D50:
|
||||
.incbin "baserom.dol", 0x21FD50, 0x20
|
||||
.global gTRKMemMap
|
||||
gTRKMemMap:
|
||||
.incbin "baserom.dol", 0x21FD70, 0x10
|
||||
.global lbl_80222D80
|
||||
lbl_80222D80:
|
||||
.incbin "baserom.dol", 0x21FD80, 0x14
|
||||
.global lbl_80222D94
|
||||
lbl_80222D94:
|
||||
.incbin "baserom.dol", 0x21FD94, 0x14
|
||||
.global lbl_80222DA8
|
||||
lbl_80222DA8:
|
||||
.incbin "baserom.dol", 0x21FDA8, 0x18
|
1676
asm/sbss.s
Normal file
1676
asm/sbss.s
Normal file
File diff suppressed because it is too large
Load Diff
16481
asm/sdata.s
Normal file
16481
asm/sdata.s
Normal file
File diff suppressed because it is too large
Load Diff
11345
asm/sdata2.s
Normal file
11345
asm/sdata2.s
Normal file
File diff suppressed because it is too large
Load Diff
607791
asm/text.s
Normal file
607791
asm/text.s
Normal file
File diff suppressed because it is too large
Load Diff
7
asmdiff.sh
Normal file
7
asmdiff.sh
Normal file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
OBJDUMP="$DEVKITPPC/bin/powerpc-eabi-objdump -D -bbinary -EB -mpowerpc -M gekko"
|
||||
OPTIONS="--start-address=$(($1)) --stop-address=$(($1 + $2))"
|
||||
$OBJDUMP $OPTIONS baserom.dol > baserom.dump
|
||||
$OBJDUMP $OPTIONS build/pikmin2.us.demo/main.dol > main.dump
|
||||
diff -u --color=always baserom.dump main.dump
|
686
doldisasm.py
Normal file
686
doldisasm.py
Normal file
@ -0,0 +1,686 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# GameCube .dol file disassembler
|
||||
# Usage: doldisasm.py DOL_FILE > assembly_file.s
|
||||
#
|
||||
|
||||
from capstone import *
|
||||
from capstone.ppc import *
|
||||
import re
|
||||
import sys
|
||||
|
||||
substitutions = (
|
||||
('<', '$$0'),
|
||||
('>', '$$1'),
|
||||
('@', '$$2'),
|
||||
('\\', '$$3'),
|
||||
(',', '$$4'),
|
||||
('-', '$$5')
|
||||
)
|
||||
|
||||
def format(symbol):
|
||||
for sub in substitutions:
|
||||
symbol = symbol.replace(sub[0], sub[1])
|
||||
|
||||
return symbol
|
||||
|
||||
def decodeformat(symbol):
|
||||
for sub in substitutions:
|
||||
symbol = symbol.replace(sub[1], sub[0])
|
||||
|
||||
return symbol
|
||||
|
||||
r13_addr = None
|
||||
r2_addr = None
|
||||
|
||||
labels = set()
|
||||
labelNames = {}
|
||||
|
||||
#argshift = 1
|
||||
|
||||
#if sys.argv[argshift] == '-m':
|
||||
with open('pik1excised.map', 'r') as mapfile:
|
||||
for line in mapfile:
|
||||
match = re.match(' [0-9a-f]{8} [0-9a-f]{6} ([0-9a-f]{8}) [ 0-9][0-9] ([^ 0-9.][^ ]*)', line)
|
||||
if match:
|
||||
addr = int(match.group(1), 16)
|
||||
name = format(match.group(2))
|
||||
labels.add(addr)
|
||||
labelNames[addr] = name
|
||||
|
||||
#argshift += 2
|
||||
|
||||
argshift = 1 #This is just to auto-find the mapfile. I prefer to drag and drop the dol onto this script.
|
||||
|
||||
with open(sys.argv[argshift], 'rb') as dolfile:
|
||||
filecontent = bytearray(dolfile.read())
|
||||
|
||||
def read_u8(offset):
|
||||
return filecontent[offset]
|
||||
|
||||
def read_u32(offset):
|
||||
return (filecontent[offset + 0] << 24) | (filecontent[offset + 1] << 16) | (filecontent[offset + 2] << 8) | filecontent[offset + 3]
|
||||
|
||||
def sign_extend_16(value):
|
||||
if value > 0 and (value & 0x8000):
|
||||
value -= 0x10000
|
||||
return value
|
||||
|
||||
def sign_extend_12(value):
|
||||
if value > 0 and (value & 0x800):
|
||||
value -= 0x1000
|
||||
return value
|
||||
|
||||
textOffsets = []
|
||||
textAddresses = []
|
||||
textSizes = []
|
||||
|
||||
dataOffsets = []
|
||||
dataAddresses = []
|
||||
dataSizes = []
|
||||
|
||||
for i in range(0, 7):
|
||||
textOffsets.append(read_u32(0x00 + 4 * i))
|
||||
textAddresses.append(read_u32(0x48 + 4 * i))
|
||||
textSizes.append(read_u32(0x90 + 4 * i))
|
||||
|
||||
for i in range(0, 11):
|
||||
dataOffsets.append(read_u32(0x1C + 4 * i))
|
||||
dataAddresses.append(read_u32(0x64 + 4 * i))
|
||||
dataSizes.append(read_u32(0xAC + 4 * i))
|
||||
|
||||
bssAddress = read_u32(0xD8)
|
||||
bssSize = read_u32(0xDC)
|
||||
entryPoint = read_u32(0xE0)
|
||||
|
||||
origStdout = sys.stdout
|
||||
with open('asm\disasm.s', 'w') as out:
|
||||
sys.stdout = out
|
||||
|
||||
print('/*')
|
||||
print('Code sections:')
|
||||
for i in range(0, 7):
|
||||
if textOffsets[i] != 0 and textAddresses[i] != 0 and textSizes[i] != 0:
|
||||
print('\t.text%i:\t0x%08X\t0x%08X\t0x%08X' % (i, textOffsets[i], textAddresses[i], textAddresses[i] + textSizes[i]))
|
||||
print('Data sections:')
|
||||
for i in range(0, 11):
|
||||
if dataOffsets[i] != 0 and dataAddresses[i] != 0 and dataSizes[i] != 0:
|
||||
print('\t.data%i:\t0x%08X\t0x%08X\t0x%08X' % (i, dataOffsets[i], dataAddresses[i], dataAddresses[i] + dataSizes[i]))
|
||||
print('BSS section:')
|
||||
print('\t.bss:\t0x%08X\t0x%08X\t0x%08X' % (0, bssAddress, bssAddress + bssSize))
|
||||
print('Entry Point: 0x%08X' % entryPoint)
|
||||
print('*/')
|
||||
|
||||
# Add entry point
|
||||
labels.add(entryPoint)
|
||||
labelNames[entryPoint] = '__start'
|
||||
|
||||
def addr_to_label(addr):
|
||||
if addr in labels:
|
||||
if addr in labelNames:
|
||||
return labelNames[addr]
|
||||
else:
|
||||
return "lbl_%08X" % addr
|
||||
else:
|
||||
return "0x%08X" % addr
|
||||
|
||||
def add_label(addr, name):
|
||||
labels.add(addr)
|
||||
if name != None and not addr in labelNames:
|
||||
labelNames[addr] = name
|
||||
|
||||
def is_label_candidate(addr):
|
||||
for i in range(0, 7):
|
||||
if addr >= textAddresses[i] and addr < textAddresses[i] + textSizes[i] and (addr & 3) == 0:
|
||||
return True
|
||||
for i in range(0, 11):
|
||||
if addr >= dataAddresses[i] and addr < dataAddresses[i] + dataSizes[i]:
|
||||
return True
|
||||
if addr >= bssAddress and addr < bssAddress + bssSize:
|
||||
return True
|
||||
return False
|
||||
|
||||
# TODO: find all of them
|
||||
loadStoreInsns = {
|
||||
PPC_INS_LWZ,
|
||||
PPC_INS_LMW,
|
||||
PPC_INS_LHA,
|
||||
PPC_INS_LHAU,
|
||||
PPC_INS_LHZ,
|
||||
PPC_INS_LHZU,
|
||||
PPC_INS_LBZ,
|
||||
PPC_INS_LBZU,
|
||||
PPC_INS_LFD,
|
||||
PPC_INS_LFDU,
|
||||
PPC_INS_LFS,
|
||||
PPC_INS_LFSU,
|
||||
PPC_INS_STW,
|
||||
PPC_INS_STWU,
|
||||
PPC_INS_STMW,
|
||||
PPC_INS_STH,
|
||||
PPC_INS_STHU,
|
||||
PPC_INS_STB,
|
||||
PPC_INS_STBU,
|
||||
PPC_INS_STFS,
|
||||
PPC_INS_STFSU,
|
||||
PPC_INS_STFD,
|
||||
PPC_INS_STDU,
|
||||
}
|
||||
|
||||
# Returns true if the instruction is a load or store with the given register as a base
|
||||
def is_load_store_reg_offset(insn, reg):
|
||||
return insn.id in loadStoreInsns and (reg == None or insn.operands[1].mem.base == reg)
|
||||
|
||||
cs = Cs(CS_ARCH_PPC, CS_MODE_32 | CS_MODE_BIG_ENDIAN)
|
||||
cs.detail = True
|
||||
cs.imm_unsigned = False
|
||||
|
||||
blacklistedInsns = {
|
||||
# Unsupported instructions
|
||||
PPC_INS_VMSUMSHM, PPC_INS_VMHADDSHS, PPC_INS_XXSLDWI, PPC_INS_VSEL,
|
||||
PPC_INS_XVSUBSP, PPC_INS_XXSEL, PPC_INS_XVMULSP, PPC_INS_XVDIVSP,
|
||||
PPC_INS_VADDUHM, PPC_INS_XXPERMDI, PPC_INS_XVMADDASP, PPC_INS_XVMADDMSP,
|
||||
PPC_INS_XVCMPGTSP, PPC_INS_XXMRGHD, PPC_INS_XSMSUBMDP, PPC_INS_XSTDIVDP,
|
||||
PPC_INS_XVADDSP, PPC_INS_XVCMPEQSP, PPC_INS_XVMSUBASP, PPC_INS_XVCMPGESP,
|
||||
PPC_INS_VMRGHB, PPC_INS_XXSPLTW,
|
||||
|
||||
# Found during disassemble attempts
|
||||
PPC_INS_XVCMPEQDP, PPC_INS_XVCMPEQDP, PPC_INS_XVMADDADP, PPC_INS_XVMADDMDP,
|
||||
PPC_INS_VPKUHUM, PPC_INS_XSMADDMDP, PPC_INS_XSMADDADP, PPC_INS_XSCMPUDP,
|
||||
PPC_INS_XSMSUBADP, PPC_INS_XSCMPODP, PPC_INS_XSCMPUDP, PPC_INS_XVMSUBMSP,
|
||||
PPC_INS_XVMSUBMDP, PPC_INS_XVCMPGEDP, PPC_INS_XVMSUBADP, PPC_INS_XVCMPGTDP,
|
||||
PPC_INS_XVMSUBADP, PPC_INS_XVNMSUBMSP,
|
||||
|
||||
# Instructions that Capstone gets wrong
|
||||
PPC_INS_MFESR, PPC_INS_MFDEAR, PPC_INS_MTESR, PPC_INS_MTDEAR, PPC_INS_MFICCR, PPC_INS_MFASR,
|
||||
# Sus
|
||||
PPC_INS_ATTN
|
||||
}
|
||||
|
||||
# Calls callback for every instruction in the specified code section
|
||||
def disasm_iter(offset, address, size, callback):
|
||||
if size == 0:
|
||||
return
|
||||
start = address
|
||||
end = address + size
|
||||
while address < end:
|
||||
code = filecontent[offset + (address-start) : offset + size]
|
||||
for insn in cs.disasm(code, address):
|
||||
address = insn.address
|
||||
if insn.id in blacklistedInsns:
|
||||
callback(address, offset + address - start, None, insn.bytes)
|
||||
else:
|
||||
callback(address, offset + address - start, insn, insn.bytes)
|
||||
address += 4
|
||||
if address < end:
|
||||
o = offset + address - start
|
||||
callback(address, offset + address - start, None, filecontent[o : o + 4])
|
||||
address += 4
|
||||
|
||||
lisInsns = {} # register : insn
|
||||
|
||||
splitDataLoads = {} # address of load insn (both high and low) : data
|
||||
|
||||
linkedInsns = {} # addr of lis insn : ori/addi insn
|
||||
|
||||
# Returns true if the instruction writes to the specified register
|
||||
def reg_modified(insn, reg):
|
||||
if insn.op[0].type == PPC_OP_REG and insn.op[0].reg == reg:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# Computes the combined value from a lis, addi/ori instruction pairr
|
||||
def combine_split_load_value(hiLoadInsn, loLoadInsn):
|
||||
assert hiLoadInsn.id == PPC_INS_LIS
|
||||
#assert loLoadInsn.id in {PPC_INS_ADDI, PPC_INS_ORI}
|
||||
#assert loLoadInsn.operands[1].reg == hiLoadInsn.operands[0].reg
|
||||
# hiLoadInsn must be "lis rX, hiPart"
|
||||
value = hiLoadInsn.operands[1].imm << 16
|
||||
# loLoadInsn must be "addi rY, rX, loPart"
|
||||
if loLoadInsn.id == PPC_INS_ORI:
|
||||
value |= loLoadInsn.operands[2].imm
|
||||
elif loLoadInsn.id == PPC_INS_ADDI:
|
||||
value += sign_extend_16(loLoadInsn.operands[2].imm)
|
||||
elif is_load_store_reg_offset(loLoadInsn, hiLoadInsn.operands[0].reg):
|
||||
value += sign_extend_16(loLoadInsn.operands[1].mem.disp)
|
||||
else:
|
||||
assert False
|
||||
return value
|
||||
|
||||
def is_store_insn(insn):
|
||||
# TODO: all store instructions
|
||||
return insn.id in {PPC_INS_STW}
|
||||
|
||||
# Get labels
|
||||
def get_label_callback(address, offset, insn, bytes):
|
||||
global r13_addr
|
||||
global r2_addr
|
||||
if insn == None:
|
||||
return
|
||||
#print("%s %s" % (insn.mnemonic, insn.op_str))
|
||||
# if branch instruction
|
||||
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BC, PPC_INS_BDZ, PPC_INS_BDNZ}:
|
||||
lisInsns.clear()
|
||||
for op in insn.operands:
|
||||
if op.type == PPC_OP_IMM:
|
||||
#print("label 0x%08X" % op.imm)
|
||||
labels.add(op.imm)
|
||||
if insn.id == PPC_INS_BL:
|
||||
#labelNames[op.imm] = 'func_%08X' % op.imm
|
||||
add_label(op.imm, 'func_%08X' % op.imm)
|
||||
|
||||
# Detect split load (high part)
|
||||
# this is 'lis rX, hipart'
|
||||
if insn.id == PPC_INS_LIS:
|
||||
# Record instruction that loads into register with 'lis'
|
||||
lisInsns[insn.operands[0].reg] = insn
|
||||
# Detect split load (low part)
|
||||
# this is either 'addi/ori rY, rX, lopart' or 'load/store rY, lopart(rX)'
|
||||
elif (insn.id in {PPC_INS_ADDI, PPC_INS_ORI} and insn.operands[1].reg in lisInsns) \
|
||||
or (is_load_store_reg_offset(insn, None) and insn.operands[1].mem.base in lisInsns):
|
||||
hiLoadInsn = lisInsns[insn.operands[1].reg]
|
||||
# Compute combined value
|
||||
value = combine_split_load_value(hiLoadInsn, insn)
|
||||
if is_label_candidate(value):
|
||||
labels.add(value)
|
||||
# Record linked instruction
|
||||
linkedInsns[hiLoadInsn.address] = insn
|
||||
splitDataLoads[hiLoadInsn.address] = value
|
||||
splitDataLoads[insn.address] = value
|
||||
lisInsns.pop(insn.operands[1].reg, None)
|
||||
# detect r2/r13 initialization
|
||||
if insn.id == PPC_INS_ORI and insn.operands[0].reg == insn.operands[1].reg:
|
||||
if r2_addr == None and insn.operands[0].reg == PPC_REG_R2:
|
||||
r2_addr = value
|
||||
#print('# DEBUG: set r2 to 0x%08X' % value)
|
||||
elif r13_addr == None and insn.operands[0].reg == PPC_REG_R13:
|
||||
r13_addr = value
|
||||
#print('# DEBUG: set r13 to 0x%08X' % value)
|
||||
# Remove record if register is overwritten
|
||||
elif (not is_store_insn(insn)) and len(insn.operands) >= 1 and insn.operands[0].type == PPC_OP_REG:
|
||||
lisInsns.pop(insn.operands[0].reg, None)
|
||||
|
||||
# Handle r13 offset values
|
||||
if r13_addr != None:
|
||||
if insn.id == PPC_INS_ADDI and insn.operands[1].value.reg == PPC_REG_R13: # r13 offset
|
||||
value = r13_addr + sign_extend_16(insn.operands[2].imm)
|
||||
if is_label_candidate(value):
|
||||
labels.add(value)
|
||||
#labelNames[value] = 'r13_%08X' % value
|
||||
if is_load_store_reg_offset(insn, PPC_REG_R13):
|
||||
value = r13_addr + sign_extend_16(insn.operands[1].mem.disp)
|
||||
if is_label_candidate(value):
|
||||
labels.add(value)
|
||||
#labelNames[value] = 'r13_%08X' % value
|
||||
|
||||
# Handle r2 offset values
|
||||
if r2_addr != None:
|
||||
if insn.id == PPC_INS_ADDI and insn.operands[1].value.reg == PPC_REG_R2: # r13 offset
|
||||
value = r2_addr + sign_extend_16(insn.operands[2].imm)
|
||||
if is_label_candidate(value):
|
||||
labels.add(value)
|
||||
#labelNames[value] = 'r2_%08X' % value
|
||||
if is_load_store_reg_offset(insn, PPC_REG_R2):
|
||||
value = r2_addr + sign_extend_16(insn.operands[1].mem.disp)
|
||||
if is_label_candidate(value):
|
||||
labels.add(value)
|
||||
#labelNames[value] = 'r2_%08X' % value
|
||||
|
||||
for i in range(0, 7):
|
||||
if textSizes[i] != 0:
|
||||
disasm_iter(textOffsets[i], textAddresses[i], textSizes[i], get_label_callback)
|
||||
|
||||
# Write macros
|
||||
print('# PowerPC Register Constants')
|
||||
for i in range(0, 32):
|
||||
print(".set r%i, %i" % (i, i))
|
||||
for i in range(0, 32):
|
||||
print(".set f%i, %i" % (i, i))
|
||||
for i in range(0, 8):
|
||||
print(".set qr%i, %i" % (i, i))
|
||||
if r13_addr != None:
|
||||
print('# Small Data Area (read/write) Base')
|
||||
print(".set _SDA_BASE_, 0x%08X" % r13_addr)
|
||||
if r2_addr != None:
|
||||
print('# Small Data Area (read only) Base')
|
||||
print(".set _SDA2_BASE_, 0x%08X" % r2_addr)
|
||||
print('')
|
||||
|
||||
# Converts the instruction to a string, fixing various issues with Capstone
|
||||
def insn_to_text(insn, raw):
|
||||
# Probably data, not a real instruction
|
||||
if insn.id == PPC_INS_BDNZ and (insn.bytes[0] & 1):
|
||||
return None
|
||||
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BDZ, PPC_INS_BDNZ}:
|
||||
return "%s %s" % (insn.mnemonic, addr_to_label(insn.operands[0].imm))
|
||||
elif insn.id == PPC_INS_BC:
|
||||
branchPred = '+' if (insn.bytes[1] & 0x20) else ''
|
||||
if insn.operands[0].type == PPC_OP_IMM:
|
||||
return "%s%s %s" % (insn.mnemonic, branchPred, addr_to_label(insn.operands[0].imm))
|
||||
elif insn.operands[1].type == PPC_OP_IMM:
|
||||
return "%s%s %s, %s" % (insn.mnemonic, branchPred, insn.reg_name(insn.operands[0].value.reg), addr_to_label(insn.operands[1].imm))
|
||||
# Handle split loads (high part)
|
||||
if insn.address in splitDataLoads and insn.id == PPC_INS_LIS:
|
||||
loLoadInsn = linkedInsns[insn.address]
|
||||
#assert loLoadInsn.id in {PPC_INS_ADDI, PPC_INS_ORI}
|
||||
value = splitDataLoads[insn.address]
|
||||
suffix = 'h' if loLoadInsn.id == PPC_INS_ORI else 'ha'
|
||||
return '%s %s, %s@%s' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), addr_to_label(value), suffix)
|
||||
# Handle split loads (low part)
|
||||
elif insn.address in splitDataLoads and insn.id in {PPC_INS_ADDI, PPC_INS_ORI}:
|
||||
value = splitDataLoads[insn.address]
|
||||
return '%s %s, %s, %s@l' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), addr_to_label(value))
|
||||
elif insn.address in splitDataLoads and is_load_store_reg_offset(insn, None):
|
||||
value = splitDataLoads[insn.address]
|
||||
return '%s %s, %s@l(%s)' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), addr_to_label(value), insn.reg_name(insn.operands[1].mem.base))
|
||||
|
||||
# r13 offset loads
|
||||
if r13_addr != None:
|
||||
if insn.id == PPC_INS_ADDI and insn.operands[1].reg == PPC_REG_R13:
|
||||
value = r13_addr + sign_extend_16(insn.operands[2].imm)
|
||||
if value in labels:
|
||||
return "%s %s, %s, %s@sda21" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), addr_to_label(value))
|
||||
if is_load_store_reg_offset(insn, PPC_REG_R13):
|
||||
value = r13_addr + sign_extend_16(insn.operands[1].mem.disp)
|
||||
if value in labels:
|
||||
return "%s %s, %s@sda21(%s)" % (insn.mnemonic, insn.reg_name(insn.operands[0].value.reg), addr_to_label(value), insn.reg_name(insn.operands[1].mem.base))
|
||||
|
||||
# r2 offset loads
|
||||
if r2_addr != None:
|
||||
if insn.id == PPC_INS_ADDI and insn.operands[1].reg == PPC_REG_R2:
|
||||
value = r2_addr + sign_extend_16(insn.operands[2].imm)
|
||||
if value in labels:
|
||||
return "%s %s, %s, %s@sda21" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), addr_to_label(value))
|
||||
if is_load_store_reg_offset(insn, PPC_REG_R2):
|
||||
value = r2_addr + sign_extend_16(insn.operands[1].mem.disp)
|
||||
if value in labels:
|
||||
return "%s %s, %s@sda21(%s)" % (insn.mnemonic, insn.reg_name(insn.operands[0].value.reg), addr_to_label(value), insn.reg_name(insn.operands[1].mem.base))
|
||||
|
||||
# Sign-extend immediate values because Capstone is an idiot and doesn't do that automatically
|
||||
if insn.id in {PPC_INS_ADDI, PPC_INS_ADDIC, PPC_INS_SUBFIC, PPC_INS_MULLI} and (insn.operands[2].imm & 0x8000):
|
||||
return "%s %s, %s, %i" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].value.reg), insn.operands[2].imm - 0x10000)
|
||||
elif (insn.id == PPC_INS_LI or insn.id == PPC_INS_CMPWI) and (insn.operands[1].imm & 0x8000):
|
||||
return "%s %s, %i" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.operands[1].imm - 0x10000)
|
||||
# cntlz -> cntlzw
|
||||
elif insn.id == PPC_INS_CNTLZW:
|
||||
return "cntlzw %s" % insn.op_str
|
||||
elif insn.id == PPC_INS_MTICCR:
|
||||
return 'mtictc %s' % insn.op_str
|
||||
# Dunno why GNU assembler doesn't accept this
|
||||
elif insn.id == PPC_INS_LMW and insn.operands[0].reg == PPC_REG_R0:
|
||||
return '.4byte 0x%08X /* illegal %s %s */' % (raw, insn.mnemonic, insn.op_str)
|
||||
return '%s %s' % (insn.mnemonic, insn.op_str)
|
||||
|
||||
def disasm_ps(inst):
|
||||
RA = ((inst >> 16) & 0x1f)
|
||||
RB = ((inst >> 11) & 0x1f)
|
||||
FA = ((inst >> 16) & 0x1f)
|
||||
FB = ((inst >> 11) & 0x1f)
|
||||
FC = ((inst >> 6) & 0x1f)
|
||||
FD = ((inst >> 21) & 0x1f)
|
||||
FS = ((inst >> 21) & 0x1f)
|
||||
IX = ((inst >> 7) & 0x7)
|
||||
WX = ((inst >> 10) & 0x1)
|
||||
|
||||
opcode = (inst >> 1) & 0x1F
|
||||
if opcode == 6: # doesn't seem to be used
|
||||
mnemonic = 'psq_lux' if inst & 0x40 else 'psq_lx'
|
||||
return '%s f%i, r%i, r%i, %i, qr%i' % (mnemonic, FD, RA, RB, WX, IX)
|
||||
if opcode == 7:
|
||||
mnemonic = 'psq_stux' if inst & 0x40 else 'psq_stx'
|
||||
return '%s f%i, r%i, r%i, %i, qr%i' % (mnemonic, FS, RA, RB, WX, IX)
|
||||
if opcode == 18:
|
||||
return 'ps_div f%i, f%i, f%i' % (FD, FA, FB)
|
||||
if opcode == 20:
|
||||
return 'ps_sub f%i, f%i, f%i' % (FD, FA, FB)
|
||||
if opcode == 21:
|
||||
return 'ps_add f%i, f%i, f%i' % (FD, FA, FB)
|
||||
if opcode == 23:
|
||||
return 'ps_sel f%i, f%i, f%i' % (FD, FA, FC)
|
||||
if opcode == 24:
|
||||
return 'ps_res f%i, f%i' % (FD, FB)
|
||||
if opcode == 25:
|
||||
return 'ps_mul f%i, f%i, f%i' % (FD, FA, FC)
|
||||
if opcode == 26:
|
||||
return 'ps_rsqrte f%i, f%i' % (FD, FB)
|
||||
if opcode == 28:
|
||||
return 'ps_msub f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
if opcode == 29:
|
||||
return 'ps_madd f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
if opcode == 30:
|
||||
return 'ps_nmsub f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
if opcode == 31:
|
||||
return 'ps_nmadd f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
if opcode == 10:
|
||||
return 'ps_sum0 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
if opcode == 11:
|
||||
return 'ps_sum1 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
if opcode == 12:
|
||||
return 'ps_muls0 f%i, f%i, f%i' % (FD, FA, FC)
|
||||
if opcode == 13:
|
||||
return 'ps_muls1 f%i, f%i, f%i' % (FD, FA, FC)
|
||||
if opcode == 14:
|
||||
return 'ps_madds0 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
if opcode == 15:
|
||||
return 'ps_madds1 f%i, f%i, f%i, f%i' % (FD, FA, FC, FB)
|
||||
|
||||
opcode = (inst >> 1) & 0x3FF
|
||||
if opcode == 40:
|
||||
return 'ps_neg f%i, f%i' % (FD, FB)
|
||||
if opcode == 72:
|
||||
return 'ps_mr f%i, f%i' % (FD, FB)
|
||||
if opcode == 136:
|
||||
return 'ps_nabs f%i, f%i' % (FD, FB)
|
||||
if opcode == 264:
|
||||
return 'ps_abs f%i, f%i' % (FD, FB)
|
||||
if opcode in {0, 32, 64, 96}:
|
||||
mnemonics = ['ps_cmpu0', 'ps_cmpo0', 'ps_cmpu1', 'ps_cmpo1']
|
||||
mnemonic = mnemonics[(inst >> 6) & 3]
|
||||
i = (inst & 0x03800000) >> 23
|
||||
return '%s cr%i, f%i, f%i' % (mnemonic, i, FA, FB)
|
||||
if opcode == 528:
|
||||
return 'ps_merge00 f%i, f%i, f%i' % (FD, FA, FB)
|
||||
if opcode == 560:
|
||||
return 'ps_merge01 f%i, f%i, f%i' % (FD, FA, FB)
|
||||
if opcode == 592:
|
||||
return 'ps_merge10 f%i, f%i, f%i' % (FD, FA, FB)
|
||||
if opcode == 624:
|
||||
return 'ps_merge11 f%i, f%i, f%i' % (FD, FA, FB)
|
||||
if opcode == 1014:
|
||||
if not (inst & 0x03e00000):
|
||||
if (inst & 1) == 0:
|
||||
return 'dcbz_l r%i, r%i' % ((inst & 0x001f0000) >> 16, (inst & 0x0000f800) >> 11)
|
||||
return None
|
||||
|
||||
def disasm_ps_mem(inst, idx):
|
||||
RA = ((inst >> 16) & 0x1f)
|
||||
RS = ((inst >> 21) & 0x1f)
|
||||
I = ((inst >> 12) & 0x7)
|
||||
W = ((inst >> 15) & 0x1)
|
||||
disp = sign_extend_12(inst & 0xFFF)
|
||||
if idx == 56:
|
||||
mnemonic = 'psq_l'
|
||||
if idx == 57:
|
||||
mnemonic = 'psq_lu'
|
||||
if idx == 60:
|
||||
mnemonic = 'psq_st'
|
||||
if idx == 61:
|
||||
mnemonic = 'psq_stu'
|
||||
return '%s f%i, %i(r%i), %i, qr%i' % (mnemonic, RS, disp, RA, W, I)
|
||||
|
||||
def disasm_fcmp(inst):
|
||||
crd = (inst & 0x03800000) >> 23
|
||||
a = (inst & 0x001f0000) >> 16
|
||||
b = (inst & 0x0000f800) >> 11
|
||||
return 'fcmpo cr%i, f%i, f%i' % (crd, a, b)
|
||||
|
||||
def disasm_mspr(inst, mode):
|
||||
if (inst & 1):
|
||||
return None
|
||||
d = (inst & 0x03e00000) >> 21
|
||||
a = (inst & 0x001f0000) >> 16
|
||||
b = (inst & 0x0000f800) >>11
|
||||
spr = (b << 5) + a
|
||||
if mode:
|
||||
return 'mtspr 0x%X, r%i' % (spr, d)
|
||||
else:
|
||||
return 'mfspr r%i, 0x%X' % (d, spr)
|
||||
|
||||
def disasm_mcrxr(inst):
|
||||
if (inst & 0x007ff801):
|
||||
return None
|
||||
crd = (inst & 0x03800000) >> 23
|
||||
return 'mcrxr cr%i' % crd
|
||||
|
||||
# Disassemble code
|
||||
def disassemble_callback(address, offset, insn, bytes):
|
||||
# Output label (if any)
|
||||
if address in labels:
|
||||
if address in labelNames:
|
||||
print("\n.global %s" % addr_to_label(address))
|
||||
print("%s:" % addr_to_label(address))
|
||||
prefixComment = '/* %08X %08X %02X %02X %02X %02X */' % (address, offset, bytes[0], bytes[1], bytes[2], bytes[3])
|
||||
asm = None
|
||||
raw = read_u32(offset)
|
||||
if insn != None:
|
||||
asm = insn_to_text(insn, raw)
|
||||
else: # Capstone couldn't disassemble it
|
||||
idx = (raw & 0xfc000000) >> 26
|
||||
idx2 = (raw & 0x000007fe) >> 1
|
||||
# mtspr
|
||||
if idx == 31 and idx2 == 467:
|
||||
asm = disasm_mspr(raw, 1)
|
||||
# mfspr
|
||||
elif idx == 31 and idx2 == 339:
|
||||
asm = disasm_mspr(raw, 0)
|
||||
# mcrxr
|
||||
elif idx == 31 and idx2 == 512:
|
||||
asm = disasm_mcrxr(raw)
|
||||
# fcmpo
|
||||
elif idx == 63 and idx2 == 32:
|
||||
asm = disasm_fcmp(raw)
|
||||
# Paired singles
|
||||
elif idx == 4:
|
||||
asm = disasm_ps(raw)
|
||||
elif idx in {56, 57, 60, 61}:
|
||||
asm = disasm_ps_mem(raw, idx)
|
||||
if asm == None:
|
||||
asm = '.4byte 0x%08X /* unknown instruction */' % raw
|
||||
print('%s\t%s' % (prefixComment, asm))
|
||||
|
||||
for i in range(0, 7):
|
||||
if textSizes[i] != 0:
|
||||
print("\n.section .text%i, \"ax\" # 0x%08X - 0x%08X" % (i, textAddresses[i], textAddresses[i] + textSizes[i]))
|
||||
disasm_iter(textOffsets[i], textAddresses[i], textSizes[i], disassemble_callback)
|
||||
|
||||
# Disassemble data
|
||||
for i in range(0, 11):
|
||||
offset = dataOffsets[i]
|
||||
address = dataAddresses[i]
|
||||
size = dataSizes[i]
|
||||
start = address
|
||||
end = start + size
|
||||
if size == 0:
|
||||
continue
|
||||
print("\n.section .data%i, \"wa\" # 0x%08X - 0x%08X" % (i, start, end))
|
||||
# Get a sorted list of labels in this data section
|
||||
sectionLabels = []
|
||||
for l in labels:
|
||||
if l >= start and l < end:
|
||||
sectionLabels.append(l)
|
||||
sectionLabels.sort()
|
||||
# Split incbins by labels
|
||||
j = 0
|
||||
while address < end:
|
||||
if j < len(sectionLabels):
|
||||
incbinSize = sectionLabels[j] - address
|
||||
if incbinSize != 0:
|
||||
print("\t.incbin \"baserom.dol\", 0x%X, 0x%X" % (offset, incbinSize))
|
||||
l = addr_to_label(sectionLabels[j])
|
||||
print(".global %s\n%s:" % (l, l))
|
||||
j += 1
|
||||
else:
|
||||
incbinSize = end - address
|
||||
if incbinSize != 0:
|
||||
print("\t.incbin \"baserom.dol\", 0x%X, 0x%X" % (offset, incbinSize))
|
||||
offset += incbinSize
|
||||
address += incbinSize
|
||||
# Remove labels to avoid duplicates in case of overlap with other sections
|
||||
for l in sectionLabels:
|
||||
labels.remove(l)
|
||||
|
||||
# Disassemble bss
|
||||
start = bssAddress
|
||||
end = bssAddress + bssSize
|
||||
address = bssAddress
|
||||
print("\n.section .bss, \"wa\" # 0x%08X - 0x%08X" % (start, end))
|
||||
# Get a sorted list of labels in this bss section
|
||||
sectionLabels = []
|
||||
for l in labels:
|
||||
if l >= start and l < end:
|
||||
sectionLabels.append(l)
|
||||
sectionLabels.sort()
|
||||
# Split incbins by labels
|
||||
j = 0
|
||||
while address < end:
|
||||
if j < len(sectionLabels):
|
||||
gapSize = sectionLabels[j] - address
|
||||
if gapSize != 0:
|
||||
print("\t.skip 0x%X" % gapSize)
|
||||
l = addr_to_label(sectionLabels[j])
|
||||
print(".global %s\n%s:" % (l, l))
|
||||
j += 1
|
||||
else:
|
||||
gapSize = end - address
|
||||
if gapSize != 0:
|
||||
print("\t.skip 0x%X" % gapSize)
|
||||
address += gapSize
|
||||
|
||||
# Output linker script
|
||||
origStdout = sys.stdout
|
||||
with open('ldscript.ld', 'w') as out:
|
||||
sys.stdout = out
|
||||
print("ENTRY(__start)")
|
||||
if r13_addr != None:
|
||||
print("_SDA_BASE_ = 0x%08X;" % r13_addr)
|
||||
if r2_addr != None:
|
||||
print("_SDA2_BASE_ = 0x%08X;" % r2_addr)
|
||||
print("PHDRS\n{")
|
||||
for i in range(0, 7):
|
||||
if textSizes[i] != 0:
|
||||
print(" text%i PT_LOAD;" % i)
|
||||
for i in range(0, 11):
|
||||
if dataSizes[i] != 0:
|
||||
print(" data%i PT_LOAD;" % i)
|
||||
print(" bss PT_LOAD;")
|
||||
print("}")
|
||||
print("SECTIONS\n{")
|
||||
for i in range(0, 7):
|
||||
if textSizes[i] != 0:
|
||||
print(" .text%i 0x%08X : { *(.text%i) } : text%i" % (i, textAddresses[i], i, i))
|
||||
for i in range(0, 11):
|
||||
if dataSizes[i] != 0:
|
||||
print(" .data%i 0x%08X : { *(.data%i) } : data%i" % (i, dataAddresses[i], i, i))
|
||||
print(" .bss 0x%08X (NOLOAD) : { *(.bss) } : bss" % bssAddress)
|
||||
print("}")
|
||||
sys.stdout = origStdout
|
||||
|
||||
# Output linker script (Metrowerks)
|
||||
origStdout = sys.stdout
|
||||
with open('ldscript.lcf', 'w') as out:
|
||||
sys.stdout = out
|
||||
if r13_addr != None:
|
||||
print("_SDA_BASE_ = 0x%08X;" % r13_addr)
|
||||
if r2_addr != None:
|
||||
print("_SDA2_BASE_ = 0x%08X;" % r2_addr)
|
||||
print("SECTIONS\n{")
|
||||
for i in range(0, 7):
|
||||
if textSizes[i] != 0:
|
||||
print(" .text%i BIND(0x%08X) : { *(.text%i) }" % (i, textAddresses[i], i))
|
||||
for i in range(0, 11):
|
||||
if dataSizes[i] != 0:
|
||||
print(" .data%i BIND(0x%08X) : { *(.data%i) }" % (i, dataAddresses[i], i))
|
||||
print(" .bss BIND(0x%08X) : { *(.bss) }" % bssAddress)
|
||||
print("}")
|
||||
sys.stdout = origStdout
|
95
include/macros.inc
Normal file
95
include/macros.inc
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
Code sections:
|
||||
.init: 0x00000100 0x80003100 0x800054C0
|
||||
.text: 0x000024C0 0x80005560 0x80221F60
|
||||
Data sections:
|
||||
extab: 0x0021EEC0 0x800054C0 0x80005500
|
||||
extabindex: 0x0021EF00 0x80005500 0x80005560
|
||||
.ctors: 0x0021EF60 0x80221F60 0x80221FC0
|
||||
.dtors: 0x0021EFC0 0x80221FC0 0x80221FE0
|
||||
.rodata: 0x0021EFE0 0x80221FE0 0x80222DC0
|
||||
.data: 0x0021FDC0 0x80222DC0 0x802E9640
|
||||
.sdata: 0x002E6640 0x803DCD20 0x803E7820
|
||||
.sdata2: 0x002F1140 0x803E8200 0x803EC840
|
||||
BSS section:
|
||||
.bss: 0x00000000 0x802E9640 0x803E81E5
|
||||
.sbss:
|
||||
Entry Point: 0x80003100
|
||||
*/
|
||||
# PowerPC Register Constants
|
||||
.set r0, 0
|
||||
.set r1, 1
|
||||
.set r2, 2
|
||||
.set r3, 3
|
||||
.set r4, 4
|
||||
.set r5, 5
|
||||
.set r6, 6
|
||||
.set r7, 7
|
||||
.set r8, 8
|
||||
.set r9, 9
|
||||
.set r10, 10
|
||||
.set r11, 11
|
||||
.set r12, 12
|
||||
.set r13, 13
|
||||
.set r14, 14
|
||||
.set r15, 15
|
||||
.set r16, 16
|
||||
.set r17, 17
|
||||
.set r18, 18
|
||||
.set r19, 19
|
||||
.set r20, 20
|
||||
.set r21, 21
|
||||
.set r22, 22
|
||||
.set r23, 23
|
||||
.set r24, 24
|
||||
.set r25, 25
|
||||
.set r26, 26
|
||||
.set r27, 27
|
||||
.set r28, 28
|
||||
.set r29, 29
|
||||
.set r30, 30
|
||||
.set r31, 31
|
||||
.set f0, 0
|
||||
.set f1, 1
|
||||
.set f2, 2
|
||||
.set f3, 3
|
||||
.set f4, 4
|
||||
.set f5, 5
|
||||
.set f6, 6
|
||||
.set f7, 7
|
||||
.set f8, 8
|
||||
.set f9, 9
|
||||
.set f10, 10
|
||||
.set f11, 11
|
||||
.set f12, 12
|
||||
.set f13, 13
|
||||
.set f14, 14
|
||||
.set f15, 15
|
||||
.set f16, 16
|
||||
.set f17, 17
|
||||
.set f18, 18
|
||||
.set f19, 19
|
||||
.set f20, 20
|
||||
.set f21, 21
|
||||
.set f22, 22
|
||||
.set f23, 23
|
||||
.set f24, 24
|
||||
.set f25, 25
|
||||
.set f26, 26
|
||||
.set f27, 27
|
||||
.set f28, 28
|
||||
.set f29, 29
|
||||
.set f30, 30
|
||||
.set f31, 31
|
||||
.set qr0, 0
|
||||
.set qr1, 1
|
||||
.set qr2, 2
|
||||
.set qr3, 3
|
||||
.set qr4, 4
|
||||
.set qr5, 5
|
||||
.set qr6, 6
|
||||
.set qr7, 7
|
||||
# Small Data Area (read/write) Base
|
||||
.set _SDA_BASE_, 0x803E4D20
|
||||
# Small Data Area (read only) Base
|
||||
.set _SDA2_BASE_, 0x803F0200
|
29
ldscript.lcf
Normal file
29
ldscript.lcf
Normal file
@ -0,0 +1,29 @@
|
||||
_SDA_BASE_ = 0x803E4D20;
|
||||
_SDA2_BASE_ = 0x803F0200;
|
||||
MEMORY {
|
||||
text : origin = 0x80003100
|
||||
}
|
||||
SECTIONS {
|
||||
GROUP: {
|
||||
.init ALIGN(0x20) : {}
|
||||
._extab ALIGN(0x20) : {}
|
||||
._exidx ALIGN(0x20) : {}
|
||||
.text ALIGN(0x20) : {}
|
||||
.ctors ALIGN(0x20) : {}
|
||||
.dtors ALIGN(0x20) : {}
|
||||
.rodata ALIGN(0x20) : {}
|
||||
.data ALIGN(0x20) : {}
|
||||
.bss ALIGN(0x20) : {}
|
||||
.sdata ALIGN(0x20) : {}
|
||||
.sbss ALIGN(0x20) : {}
|
||||
.sdata2 ALIGN(0x20): {}
|
||||
.sbss2 ALIGN(0x20) : {}
|
||||
.stack ALIGN(0x100) : {}
|
||||
} > text
|
||||
_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 65536 + 0x7) & ~0x7;
|
||||
_stack_end = _f_sbss2 + SIZEOF(.sbss2);
|
||||
_db_stack_addr = (_stack_addr + 0x2000);
|
||||
_db_stack_end = _stack_addr;
|
||||
__ArenaLo = (_db_stack_addr + 0x1f) & ~0x1f;
|
||||
__ArenaHi = 0x81700000;
|
||||
}
|
40
obj_files.mk
Normal file
40
obj_files.mk
Normal file
@ -0,0 +1,40 @@
|
||||
# Linker order for every file, passed to the Metrowerks linker.
|
||||
|
||||
INIT_O_FILES := \
|
||||
$(BUILD_DIR)/asm/init.o \
|
||||
|
||||
EXTAB_O_FILES := \
|
||||
$(BUILD_DIR)/asm/extab.o \
|
||||
|
||||
EXTABINDEX_O_FILES := \
|
||||
$(BUILD_DIR)/asm/exidx.o \
|
||||
|
||||
TEXT_O_FILES := \
|
||||
$(BUILD_DIR)/asm/text.o \
|
||||
|
||||
CTORS_O_FILES := \
|
||||
$(BUILD_DIR)/asm/ctors.o \
|
||||
|
||||
DTORS_O_FILES := \
|
||||
$(BUILD_DIR)/asm/dtors.o \
|
||||
|
||||
RODATA_O_FILES := \
|
||||
$(BUILD_DIR)/asm/rodata.o \
|
||||
|
||||
DATA_O_FILES := \
|
||||
$(BUILD_DIR)/asm/data.o \
|
||||
|
||||
BSS_O_FILES := \
|
||||
$(BUILD_DIR)/asm/bss.o \
|
||||
|
||||
SDATA_O_FILES := \
|
||||
$(BUILD_DIR)/asm/sdata.o \
|
||||
|
||||
SBSS_O_FILES := \
|
||||
$(BUILD_DIR)/asm/sbss.o \
|
||||
|
||||
SDATA2_O_FILES := \
|
||||
$(BUILD_DIR)/asm/sdata2.o \
|
||||
|
||||
SBSS2_O_FILES := \
|
||||
# $(BUILD_DIR)/asm/sbss2.o \
|
206
ptr_checker.py
Normal file
206
ptr_checker.py
Normal file
@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# ====================================
|
||||
# Ptr checker v0.0.9 (by RevoSucks)
|
||||
# ====================================
|
||||
# WARNING, seriously, get a puke bucket. This is just a file i am writing for my own sole use.
|
||||
# If you want to use it, I have some advice for you: D.O.N.T.
|
||||
# It stands for dooooooonnnnnn't use it. Ever.
|
||||
|
||||
# ------------------
|
||||
# Imports
|
||||
# ------------------
|
||||
import os, fnmatch, math
|
||||
|
||||
# ------------------
|
||||
# Defines
|
||||
# ------------------
|
||||
|
||||
# Path to baserom.dol
|
||||
baserom_path = "baserom.dol"
|
||||
map_path = "build/pikmin.usa.1/pikmin1.map"
|
||||
|
||||
# ------------------
|
||||
# Methods
|
||||
# ------------------
|
||||
|
||||
# Return a list of searchable '.s' files in a given folder recursively.
|
||||
def findExt(folder):
|
||||
matches = []
|
||||
for root, dirnames, filenames in os.walk(folder):
|
||||
for filename in filenames:
|
||||
if filename.endswith('.s'):
|
||||
matches.append(os.path.join(root, filename))
|
||||
#print(matches)
|
||||
return matches
|
||||
|
||||
# Name is passed in as the address initially. If we cannot find it, return the string of the address.
|
||||
def getMatchingTextSymbolFromAddress(name):
|
||||
# Strip the 0x from the start.
|
||||
#print("DEBUG: ", str(hex(name))[2:].zfill(8))
|
||||
result = str(hex(name))[2:].zfill(8)
|
||||
matches = [result, '(entry of ']
|
||||
# First open the .map file.
|
||||
map = open(map_path, 'r')
|
||||
Lines = map.readlines()
|
||||
# Parse over the map until a match is found.
|
||||
for line in Lines:
|
||||
# Does the line have the 2 expected strings?
|
||||
if all(x in line for x in matches):
|
||||
# We found the line.
|
||||
#print("Match found! Line: ", line)
|
||||
# Split the name out and return it.
|
||||
return line.split(' ')[5]
|
||||
# We're done, close it.
|
||||
map.close()
|
||||
return hex(name)
|
||||
|
||||
# Build a list of the pointers and scan each word.
|
||||
def dumpVirtualTable(line):
|
||||
# Init the table.
|
||||
table = ""
|
||||
# Open baserom.dol for processing.
|
||||
f = open(baserom_path, 'rb')
|
||||
address = line.split(' ')[2][:-1]
|
||||
size = line.split(' ')[3]
|
||||
#print("ADDR: ", address)
|
||||
#print("SIZE: ", size)
|
||||
f.seek(int(address, 16))
|
||||
entries = int(size, 16) / 4
|
||||
while entries != 0:
|
||||
entry = int.from_bytes(f.read(4), byteorder='big')
|
||||
if entry == 0:
|
||||
str_to_print = "0"
|
||||
elif (entry & 0xFF000000) == 0x80000000:
|
||||
str_to_print = str(getMatchingTextSymbolFromAddress(entry))
|
||||
else:
|
||||
str_to_print = str(hex(entry))
|
||||
table = table + " .4byte " + str_to_print + "\n"
|
||||
entries = entries - 1
|
||||
return table
|
||||
|
||||
def isBaseromEntrySus(line):
|
||||
with open(baserom_path, 'rb') as f: # context management to close file for us
|
||||
address = line.split(' ')[2][:-1]
|
||||
size = line.split(' ')[3]
|
||||
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
|
||||
entries = int(size, 16) / 4
|
||||
f.seek(int(address, 16))
|
||||
while entries > 0:
|
||||
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
|
||||
if (entry & 0xFF000000) == 0x80000000 and entry != 0x80000000:
|
||||
print("Suspicious Ptr: ", str(hex(entry)))
|
||||
return True
|
||||
entries -= 1
|
||||
return False
|
||||
|
||||
# Same as isBaseromEntrySus but check for rodata str. Man this file is a mess.
|
||||
def isBaseromEntrySus_ShouldDump(line):
|
||||
with open(baserom_path, 'rb') as f: # context management to close file for us
|
||||
address = line.split(' ')[2][:-1]
|
||||
size = line.split(' ')[3]
|
||||
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
|
||||
entries = int(size, 16) / 4
|
||||
f.seek(int(address, 16))
|
||||
while entries > 0:
|
||||
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
|
||||
if (entry & 0xFF000000) == 0x80000000 and entry != 0x80000000:
|
||||
text_sym = getMatchingTextSymbolFromAddress(entry)
|
||||
if text_sym != str(hex(entry)):
|
||||
if text_sym[:2] == "$$":
|
||||
return True
|
||||
entries -= 1
|
||||
return False
|
||||
|
||||
def isBaseromEntryPurePtrTable(line):
|
||||
table = ""
|
||||
with open(baserom_path, 'rb') as f: # context management to close file for us
|
||||
address = line.split(' ')[2][:-1]
|
||||
size = line.split(' ')[3]
|
||||
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
|
||||
entries = int(size, 16) / 4
|
||||
f.seek(int(address, 16))
|
||||
# If any values are neither 0 nor a
|
||||
while entries > 0:
|
||||
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
|
||||
if entry == 0:
|
||||
str_to_print = "0"
|
||||
elif (entry & 0xFF000000) == 0x80000000:
|
||||
str_to_print = getMatchingTextSymbolFromAddress(entry)
|
||||
print("DEBUG: ", str_to_print)
|
||||
elif entry == 0xFFFFFFFF:
|
||||
str_to_print = "-1"
|
||||
else:
|
||||
return line # Not a pure pure table. Keep the entry.
|
||||
if str_to_print[:2] == "0x":
|
||||
return line # Not a pure pure table. Keep the entry.
|
||||
table = table + " .4byte " + str_to_print + "\n"
|
||||
entries = entries - 1
|
||||
return table
|
||||
|
||||
def dumpActorTable(line):
|
||||
table = ""
|
||||
with open(baserom_path, 'rb') as f: # context management to close file for us
|
||||
address = line.split(' ')[2][:-1]
|
||||
size = line.split(' ')[3]
|
||||
lower_four = 4 * math.floor(int(size, 16)/4) # Floor the size to the lower multiple of 4 if needed.
|
||||
entries = int(size, 16) / 4
|
||||
f.seek(int(address, 16))
|
||||
# If any values are neither 0 nor a
|
||||
while entries > 0:
|
||||
entry = int.from_bytes(f.read(4), byteorder='big', signed=False)
|
||||
if entry == 0:
|
||||
str_to_print = "0"
|
||||
elif (entry & 0xFF000000) == 0x80000000:
|
||||
str_to_print = getMatchingTextSymbolFromAddress(entry)
|
||||
print("DEBUG: ", str_to_print)
|
||||
elif entry == 0xFFFFFFFF:
|
||||
str_to_print = "-1"
|
||||
else:
|
||||
str_to_print = str(entry)
|
||||
table = table + " .4byte " + str_to_print + "\n"
|
||||
entries = entries - 1
|
||||
return table
|
||||
|
||||
# -----------------------------------
|
||||
# Config
|
||||
# -----------------------------------
|
||||
print_baserom_calls = True
|
||||
# -----------------------------------
|
||||
|
||||
# ------------------
|
||||
# Main code
|
||||
# ------------------
|
||||
|
||||
asm_files = findExt("asm")
|
||||
#asm_files = ['/c/sms/asm/NPC/NpcInitData.s']
|
||||
|
||||
print("Checking all files for possible ptrs...")
|
||||
|
||||
# Using readlines()
|
||||
for i in asm_files:
|
||||
file1 = open(i, 'r')
|
||||
Lines = file1.readlines()
|
||||
file1.close()
|
||||
file1 = open(i, 'w')
|
||||
count = 0
|
||||
line_array = [] #array of current line and last two lines
|
||||
line_array.append("")
|
||||
line_array.append("")
|
||||
line_array.append("")
|
||||
file_sus = False
|
||||
for line in Lines:
|
||||
line_array[0] = line_array[1]
|
||||
line_array[1] = line_array[2]
|
||||
line_array[2] = line
|
||||
# Do something with the line here. Does the line belong to a baserom.dol call for a __vt__?
|
||||
if "__vt__" in line_array[1] and "baserom.dol" in line:
|
||||
file1.write(dumpVirtualTable(line))
|
||||
else:
|
||||
file1.write(line)
|
||||
if file_sus == True:
|
||||
print("Sus: ", i, "Count: ", count, "\n")
|
||||
file1.close()
|
||||
|
||||
print("Done")
|
||||
#print(os.getcwd())
|
53
resolveLinkerErrors.py
Normal file
53
resolveLinkerErrors.py
Normal file
@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# very experimental python script that goes through a linker error file and resolves any undefined labels while splitting
|
||||
|
||||
with open("errors.txt", "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
lbls = []
|
||||
|
||||
undefcount = 0
|
||||
|
||||
for line in lines:
|
||||
if "undefined" in line:
|
||||
undefcount = undefcount + 1
|
||||
splitLine = line.split(" ")
|
||||
|
||||
for spl in splitLine:
|
||||
if spl.startswith("\'lbl_"):
|
||||
localstr = spl.strip("\n")
|
||||
localstr = localstr.strip("\'lbl_")
|
||||
lbls.append(localstr)
|
||||
|
||||
with open("asm/MSound/MSoundBGM.s", "r") as asm:
|
||||
asms = asm.readlines()
|
||||
|
||||
prevLine = ""
|
||||
output = []
|
||||
|
||||
for l in asms:
|
||||
l = l.strip("\n")
|
||||
|
||||
for lbl in lbls:
|
||||
if l.startswith("/* " + lbl):
|
||||
output.append("lbl_" + lbl + ":\n")
|
||||
|
||||
#if prevLine.startswith("func_"):
|
||||
# prevLine = l
|
||||
# output.append(l + "\n")
|
||||
# continue
|
||||
#else:
|
||||
# for lbl in lbls:
|
||||
# lstr = l.strip(":")
|
||||
# if lstr == lbl:
|
||||
# output.append(f".global {lbl}\n")
|
||||
# prevLine = l
|
||||
# break
|
||||
|
||||
output.append(l + "\n")
|
||||
prevLine = l
|
||||
|
||||
with open("output.asm", "w") as w:
|
||||
for o in output:
|
||||
w.write(o)
|
1
sha1/pikmin.usa.1.sha1
Normal file
1
sha1/pikmin.usa.1.sha1
Normal file
@ -0,0 +1 @@
|
||||
02204260B7EFE8742D34572E58BA3DFECD92E4E9 build/pikmin.usa.1/main.dol
|
3
tools/.gitignore
vendored
Normal file
3
tools/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Build artifacts
|
||||
*.exe
|
||||
elf2dol
|
506
tools/elf2dol.c
Normal file
506
tools/elf2dol.c
Normal file
@ -0,0 +1,506 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#ifndef MAX
|
||||
//! Get the maximum of two values
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
//! Get the minimum of two values
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define EI_NIDENT 16
|
||||
|
||||
typedef struct {
|
||||
unsigned char e_ident[EI_NIDENT];
|
||||
uint16_t e_type;
|
||||
uint16_t e_machine;
|
||||
uint32_t e_version;
|
||||
uint32_t e_entry;
|
||||
uint32_t e_phoff;
|
||||
uint32_t e_shoff;
|
||||
uint32_t e_flags;
|
||||
uint16_t e_ehsize;
|
||||
uint16_t e_phentsize;
|
||||
uint16_t e_phnum;
|
||||
uint16_t e_shentsize;
|
||||
uint16_t e_shnum;
|
||||
uint16_t e_shstrndx;
|
||||
} Elf32_Ehdr;
|
||||
|
||||
#define EI_CLASS 4
|
||||
#define EI_DATA 5
|
||||
#define EI_VERSION 6
|
||||
#define EI_PAD 7
|
||||
#define EI_NIDENT 16
|
||||
|
||||
#define ELFCLASS32 1
|
||||
#define ELFDATA2MSB 2
|
||||
#define EV_CURRENT 1
|
||||
|
||||
#define ET_EXEC 2
|
||||
#define EM_PPC 20
|
||||
|
||||
typedef struct {
|
||||
uint32_t p_type;
|
||||
uint32_t p_offset;
|
||||
uint32_t p_vaddr;
|
||||
uint32_t p_paddr;
|
||||
uint32_t p_filesz;
|
||||
uint32_t p_memsz;
|
||||
uint32_t p_flags;
|
||||
uint32_t p_align;
|
||||
} Elf32_Phdr;
|
||||
|
||||
#define PT_LOAD 1
|
||||
#define PF_R 4
|
||||
#define PF_W 2
|
||||
#define PF_X 1
|
||||
|
||||
int verbosity = 0;
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
|
||||
#define swap32(x) (x)
|
||||
#define swaf16(x) (x)
|
||||
|
||||
#else
|
||||
|
||||
static inline uint32_t swap32(uint32_t v)
|
||||
{
|
||||
return (v >> 24) |
|
||||
((v >> 8) & 0x0000FF00) |
|
||||
((v << 8) & 0x00FF0000) |
|
||||
(v << 24);
|
||||
}
|
||||
|
||||
static inline uint16_t swaf16(uint16_t v)
|
||||
{
|
||||
return (v >> 8) | (v << 8);
|
||||
}
|
||||
|
||||
#endif /* BIG_ENDIAN */
|
||||
|
||||
typedef struct {
|
||||
|
||||
uint32_t text_off[7];
|
||||
uint32_t data_off[11];
|
||||
uint32_t text_addr[7];
|
||||
uint32_t data_addr[11];
|
||||
uint32_t text_size[7];
|
||||
uint32_t data_size[11];
|
||||
uint32_t bss_addr;
|
||||
uint32_t bss_size;
|
||||
uint32_t entry;
|
||||
uint32_t pad[7];
|
||||
} DOL_hdr;
|
||||
|
||||
#define HAVE_BSS 1
|
||||
|
||||
#define MAX_TEXT_SEGMENTS 7
|
||||
#define MAX_DATA_SEGMENTS 11
|
||||
|
||||
#define DOL_ALIGNMENT 32
|
||||
|
||||
#define DOL_ALIGN(x) (((x) + DOL_ALIGNMENT - 1) & ~(DOL_ALIGNMENT - 1))
|
||||
|
||||
typedef struct {
|
||||
DOL_hdr header;
|
||||
int text_cnt;
|
||||
int data_cnt;
|
||||
uint32_t text_elf_off[7];
|
||||
uint32_t data_elf_off[11];
|
||||
uint32_t flags;
|
||||
FILE *elf;
|
||||
} DOL_map;
|
||||
|
||||
// We need to track 2 PDHR sizes in the event this is for Wii, but for Gamecube
|
||||
// we only need the first element(s).
|
||||
uint32_t sdataSizes[2] = {0, 0};
|
||||
uint32_t sbssSizes[2] = {0, 0};
|
||||
|
||||
void usage(const char *name)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [-h] [-v] [--] elf-file dol-file\n", name);
|
||||
fprintf(stderr, " Convert an ELF file to a DOL file (by segments)\n");
|
||||
fprintf(stderr, " Options:\n");
|
||||
fprintf(stderr, " -h Show this help\n");
|
||||
fprintf(stderr, " -v Be more verbose (twice for even more)\n");
|
||||
}
|
||||
|
||||
#define die(x) { fprintf(stderr, x "\n"); exit(1); }
|
||||
#define perrordie(x) { perror(x); exit(1); }
|
||||
|
||||
void ferrordie(FILE *f, const char *str)
|
||||
{
|
||||
if(ferror(f)) {
|
||||
fprintf(stderr, "Error while ");
|
||||
perrordie(str);
|
||||
} else if(feof(f)) {
|
||||
fprintf(stderr, "EOF while %s\n", str);
|
||||
exit(1);
|
||||
} else {
|
||||
fprintf(stderr, "Unknown error while %s\n", str);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
void add_bss(DOL_map *map, uint32_t paddr, uint32_t memsz)
|
||||
{
|
||||
if(map->flags & HAVE_BSS) {
|
||||
uint32_t start = swap32(map->header.bss_addr);
|
||||
uint32_t size = swap32(map->header.bss_size);
|
||||
if ( (start+size) == paddr) {
|
||||
map->header.bss_size = swap32(size+memsz);
|
||||
}
|
||||
} else {
|
||||
map->header.bss_addr = swap32(paddr);
|
||||
map->header.bss_size = swap32(memsz);
|
||||
map->flags |= HAVE_BSS;
|
||||
}
|
||||
}
|
||||
|
||||
void increment_bss_size(DOL_map *map, uint32_t memsz)
|
||||
{
|
||||
// because it can be byte swapped, we need to force the add via a temporary.
|
||||
uint32_t preAdd = swap32(map->header.bss_size);
|
||||
preAdd += memsz;
|
||||
map->header.bss_size = swap32(preAdd);
|
||||
}
|
||||
|
||||
void read_elf_segments(DOL_map *map, const char *elf, uint32_t sdata_pdhr, uint32_t sbss_pdhr, const char *platform)
|
||||
{
|
||||
int read, i;
|
||||
Elf32_Ehdr ehdr;
|
||||
//int isWii = !(strcmp(platform, "wii")) ? 1 : 0;
|
||||
int isWii = 1;
|
||||
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "Reading ELF file...\n");
|
||||
|
||||
map->elf = fopen(elf, "rb");
|
||||
if(!map->elf)
|
||||
perrordie("Could not open ELF file");
|
||||
|
||||
read = fread(&ehdr, sizeof(ehdr), 1, map->elf);
|
||||
if(read != 1)
|
||||
ferrordie(map->elf, "reading ELF header");
|
||||
|
||||
if(memcmp(&ehdr.e_ident[0], "\177ELF", 4))
|
||||
die("Invalid ELF header");
|
||||
if(ehdr.e_ident[EI_CLASS] != ELFCLASS32)
|
||||
die("Invalid ELF class");
|
||||
if(ehdr.e_ident[EI_DATA] != ELFDATA2MSB)
|
||||
die("Invalid ELF byte order");
|
||||
if(ehdr.e_ident[EI_VERSION] != EV_CURRENT)
|
||||
die("Invalid ELF ident version");
|
||||
if(swap32(ehdr.e_version) != EV_CURRENT)
|
||||
die("Invalid ELF version");
|
||||
if(swaf16(ehdr.e_type) != ET_EXEC)
|
||||
die("ELF is not an executable");
|
||||
if(swaf16(ehdr.e_machine) != EM_PPC)
|
||||
die("Machine is not PowerPC");
|
||||
if(!swap32(ehdr.e_entry))
|
||||
die("ELF has no entrypoint");
|
||||
|
||||
map->header.entry = ehdr.e_entry;
|
||||
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "Valid ELF header found\n");
|
||||
|
||||
uint16_t phnum = swaf16(ehdr.e_phnum);
|
||||
uint32_t phoff = swap32(ehdr.e_phoff);
|
||||
Elf32_Phdr *phdrs;
|
||||
|
||||
if(!phnum || !phoff)
|
||||
die("ELF has no program headers");
|
||||
|
||||
if(swaf16(ehdr.e_phentsize) != sizeof(Elf32_Phdr))
|
||||
die("Invalid program header entry size");
|
||||
|
||||
phdrs = malloc(phnum * sizeof(Elf32_Phdr));
|
||||
|
||||
if(fseek(map->elf, phoff, SEEK_SET) < 0)
|
||||
ferrordie(map->elf, "reading ELF program headers");
|
||||
read = fread(phdrs, sizeof(Elf32_Phdr), phnum, map->elf);
|
||||
if(read != phnum)
|
||||
ferrordie(map->elf, "reading ELF program headers");
|
||||
|
||||
for(i=0; i<phnum; i++) {
|
||||
if(swap32(phdrs[i].p_type) == PT_LOAD) {
|
||||
uint32_t offset = swap32(phdrs[i].p_offset);
|
||||
uint32_t paddr = swap32(phdrs[i].p_paddr);
|
||||
uint32_t filesz = swap32(phdrs[i].p_filesz);
|
||||
uint32_t memsz = swap32(phdrs[i].p_memsz);
|
||||
uint32_t flags = swap32(phdrs[i].p_flags);
|
||||
if(memsz) {
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "PHDR %d: 0x%x [0x%x] -> 0x%08x [0x%x] flags 0x%x\n",
|
||||
i, offset, filesz, paddr, memsz, flags);
|
||||
if(flags & PF_X) {
|
||||
// TEXT segment
|
||||
if(!(flags & PF_R))
|
||||
fprintf(stderr, "Warning: non-readable segment %d\n", i);
|
||||
if(flags & PF_W)
|
||||
fprintf(stderr, "Warning: writable and executable segment %d\n", i);
|
||||
if(filesz > memsz) {
|
||||
fprintf(stderr, "Error: TEXT segment %d memory size (0x%x) smaller than file size (0x%x)\n",
|
||||
i, memsz, filesz);
|
||||
exit(1);
|
||||
} else if (memsz > filesz) {
|
||||
add_bss(map, paddr + filesz, memsz - filesz);
|
||||
}
|
||||
if(map->text_cnt >= MAX_TEXT_SEGMENTS) {
|
||||
die("Error: Too many TEXT segments");
|
||||
}
|
||||
map->header.text_addr[map->text_cnt] = swap32(paddr);
|
||||
map->header.text_size[map->text_cnt] = swap32(filesz);
|
||||
map->text_elf_off[map->text_cnt] = offset;
|
||||
map->text_cnt++;
|
||||
} else {
|
||||
// DATA or BSS segment
|
||||
if(!(flags & PF_R))
|
||||
fprintf(stderr, "Warning: non-readable segment %d\n", i);
|
||||
if(filesz == 0) {
|
||||
// BSS segment
|
||||
add_bss(map, paddr, memsz);
|
||||
|
||||
// We need to keep PHDF sizes, so track these.
|
||||
if(i == sbss_pdhr) {
|
||||
sbssSizes[0] = memsz;
|
||||
} else if(isWii && i == sbss_pdhr + 2) {
|
||||
sbssSizes[1] = memsz;
|
||||
}
|
||||
} else {
|
||||
// DATA segment
|
||||
if(filesz > memsz) {
|
||||
fprintf(stderr, "Error: segment %d memory size (0x%x) is smaller than file size (0x%x)\n",
|
||||
i, memsz, filesz);
|
||||
exit(1);
|
||||
}
|
||||
if(map->data_cnt >= MAX_DATA_SEGMENTS) {
|
||||
die("Error: Too many DATA segments");
|
||||
}
|
||||
// Track sdata as well.
|
||||
if(i == sdata_pdhr) {
|
||||
sdataSizes[0] = memsz;
|
||||
} else if(isWii && i == sdata_pdhr + 2) {
|
||||
sdataSizes[1] = memsz;
|
||||
}
|
||||
|
||||
map->header.data_addr[map->data_cnt] = swap32(paddr);
|
||||
map->header.data_size[map->data_cnt] = swap32(filesz);
|
||||
map->data_elf_off[map->data_cnt] = offset;
|
||||
map->data_cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if(verbosity >= 1)
|
||||
fprintf(stderr, "Skipping empty program header %d\n", i);
|
||||
}
|
||||
} else if(verbosity >= 1) {
|
||||
fprintf(stderr, "Skipping program header %d of type %d\n", i, swap32(phdrs[i].p_type));
|
||||
}
|
||||
}
|
||||
increment_bss_size(map, sdataSizes[0]);
|
||||
//increment_bss_size(map, sdataSizes[1]);
|
||||
increment_bss_size(map, sbssSizes[0]);
|
||||
//increment_bss_size(map, sbssSizes[1]);
|
||||
if(verbosity >= 2) {
|
||||
fprintf(stderr, "Segments:\n");
|
||||
for(i=0; i<map->text_cnt; i++) {
|
||||
fprintf(stderr, " TEXT %d: 0x%08x [0x%x] from ELF offset 0x%x\n",
|
||||
i, swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]),
|
||||
map->text_elf_off[i]);
|
||||
}
|
||||
for(i=0; i<map->data_cnt; i++) {
|
||||
fprintf(stderr, " DATA %d: 0x%08x [0x%x] from ELF offset 0x%x\n",
|
||||
i, swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]),
|
||||
map->data_elf_off[i]);
|
||||
}
|
||||
if(map->flags & HAVE_BSS)
|
||||
fprintf(stderr, " BSS segment: 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
|
||||
swap32(map->header.bss_size));
|
||||
}
|
||||
}
|
||||
|
||||
void map_dol(DOL_map *map)
|
||||
{
|
||||
uint32_t fpos;
|
||||
int i;
|
||||
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "Laying out DOL file...\n");
|
||||
|
||||
fpos = DOL_ALIGN(sizeof(DOL_hdr));
|
||||
|
||||
for(i=0; i<map->text_cnt; i++) {
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, " TEXT segment %d at 0x%x\n", i, fpos);
|
||||
map->header.text_off[i] = swap32(fpos);
|
||||
fpos = DOL_ALIGN(fpos + swap32(map->header.text_size[i]));
|
||||
}
|
||||
for(i=0; i<map->data_cnt; i++) {
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, " DATA segment %d at 0x%x\n", i, fpos);
|
||||
map->header.data_off[i] = swap32(fpos);
|
||||
fpos = DOL_ALIGN(fpos + swap32(map->header.data_size[i]));
|
||||
}
|
||||
|
||||
if(map->text_cnt == 0) {
|
||||
if(verbosity >= 1)
|
||||
fprintf(stderr, "Note: adding dummy TEXT segment to work around IOS bug\n");
|
||||
map->header.text_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
|
||||
}
|
||||
if(map->data_cnt == 0) {
|
||||
if(verbosity >= 1)
|
||||
fprintf(stderr, "Note: adding dummy DATA segment to work around IOS bug\n");
|
||||
map->header.data_off[0] = swap32(DOL_ALIGN(sizeof(DOL_hdr)));
|
||||
}
|
||||
}
|
||||
|
||||
#define BLOCK (1024*1024)
|
||||
|
||||
void fcpy(FILE *dst, FILE *src, uint32_t dst_off, uint32_t src_off, uint32_t size)
|
||||
{
|
||||
int left = size;
|
||||
int read;
|
||||
int written;
|
||||
int block;
|
||||
void *blockbuf;
|
||||
|
||||
if(fseek(src, src_off, SEEK_SET) < 0)
|
||||
ferrordie(src, "reading ELF segment data");
|
||||
if(fseek(dst, dst_off, SEEK_SET) < 0)
|
||||
ferrordie(dst, "writing DOL segment data");
|
||||
|
||||
blockbuf = malloc(MIN(BLOCK, left));
|
||||
|
||||
while(left) {
|
||||
block = MIN(BLOCK, left);
|
||||
read = fread(blockbuf, 1, block, src);
|
||||
if(read != block) {
|
||||
free(blockbuf);
|
||||
ferrordie(src, "reading ELF segment data");
|
||||
}
|
||||
written = fwrite(blockbuf, 1, block, dst);
|
||||
if(written != block) {
|
||||
free(blockbuf);
|
||||
ferrordie(dst, "writing DOL segment data");
|
||||
}
|
||||
left -= block;
|
||||
}
|
||||
free(blockbuf);
|
||||
}
|
||||
|
||||
void write_dol(DOL_map *map, const char *dol)
|
||||
{
|
||||
FILE *dolf;
|
||||
int written;
|
||||
int i;
|
||||
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "Writing DOL file...\n");
|
||||
|
||||
dolf = fopen(dol, "wb");
|
||||
if(!dolf)
|
||||
perrordie("Could not open DOL file");
|
||||
|
||||
if(verbosity >= 2) {
|
||||
fprintf(stderr, "DOL header:\n");
|
||||
for(i=0; i<MAX(1,map->text_cnt); i++)
|
||||
fprintf(stderr, " TEXT %d @ 0x%08x [0x%x] off 0x%x\n", i,
|
||||
swap32(map->header.text_addr[i]), swap32(map->header.text_size[i]),
|
||||
swap32(map->header.text_off[i]));
|
||||
for(i=0; i<MAX(1,map->data_cnt); i++)
|
||||
fprintf(stderr, " DATA %d @ 0x%08x [0x%x] off 0x%x\n", i,
|
||||
swap32(map->header.data_addr[i]), swap32(map->header.data_size[i]),
|
||||
swap32(map->header.data_off[i]));
|
||||
if(swap32(map->header.bss_addr) && swap32(map->header.bss_size))
|
||||
fprintf(stderr, " BSS @ 0x%08x [0x%x]\n", swap32(map->header.bss_addr),
|
||||
swap32(map->header.bss_size));
|
||||
fprintf(stderr, " Entry: 0x%08x\n", swap32(map->header.entry));
|
||||
fprintf(stderr, "Writing DOL header...\n");
|
||||
}
|
||||
|
||||
written = fwrite(&map->header, sizeof(DOL_hdr), 1, dolf);
|
||||
if(written != 1)
|
||||
ferrordie(dolf, "writing DOL header");
|
||||
|
||||
for(i=0; i<map->text_cnt; i++) {
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "Writing TEXT segment %d...\n", i);
|
||||
fcpy(dolf, map->elf, swap32(map->header.text_off[i]), map->text_elf_off[i],
|
||||
swap32(map->header.text_size[i]));
|
||||
}
|
||||
for(i=0; i<map->data_cnt; i++) {
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "Writing DATA segment %d...\n", i);
|
||||
fcpy(dolf, map->elf, swap32(map->header.data_off[i]), map->data_elf_off[i],
|
||||
swap32(map->header.data_size[i]));
|
||||
}
|
||||
|
||||
if(verbosity >= 2)
|
||||
fprintf(stderr, "All done!\n");
|
||||
|
||||
fclose(map->elf);
|
||||
fclose(dolf);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char **arg;
|
||||
|
||||
if(argc < 2) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
arg = &argv[1];
|
||||
argc--;
|
||||
|
||||
while(argc && *arg[0] == '-') {
|
||||
if(!strcmp(*arg, "-h")) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
} else if(!strcmp(*arg, "-v")) {
|
||||
verbosity++;
|
||||
} else if(!strcmp(*arg, "--")) {
|
||||
arg++;
|
||||
argc--;
|
||||
break;
|
||||
} else {
|
||||
fprintf(stderr, "Unrecognized option %s\n", *arg);
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
arg++;
|
||||
argc--;
|
||||
}
|
||||
if(argc < 2) {
|
||||
usage(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char *elf_file = arg[0];
|
||||
const char *dol_file = arg[1];
|
||||
uint32_t sdata_pdhr = atoi(arg[2]);
|
||||
uint32_t sbss_pdhr = atoi(arg[3]);
|
||||
const char *platform = arg[4];
|
||||
|
||||
DOL_map map;
|
||||
|
||||
memset(&map, 0, sizeof(map));
|
||||
|
||||
read_elf_segments(&map, elf_file, sdata_pdhr, sbss_pdhr, platform);
|
||||
map_dol(&map);
|
||||
write_dol(&map, dol_file);
|
||||
|
||||
return 0;
|
||||
}
|
318
tools/postprocess.py
Normal file
318
tools/postprocess.py
Normal file
@ -0,0 +1,318 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
BANNER = """
|
||||
# This script is the culmination of three patches supporting decompilation
|
||||
# with the CodeWarrior compiler.
|
||||
# - riidefi, 2020
|
||||
#
|
||||
# postprocess.py [args] file
|
||||
#
|
||||
# 1) Certain versions have a bug where the ctor alignment is ignored and set incorrectly.
|
||||
# This option is enabled with -fctor-realign, and disabled by default with -fno-ctor-realign
|
||||
#
|
||||
# 2) Certain C++ symbols cannot be assembled normally.
|
||||
# To support the buildsystem, a simple substitution system has been devised
|
||||
#
|
||||
# ?<ID> -> CHAR
|
||||
#
|
||||
# IDs (all irregular symbols in mangled names):
|
||||
# 0: <
|
||||
# 1: >
|
||||
# 2: @
|
||||
# 3: \\
|
||||
# 4: ,
|
||||
# 5: -
|
||||
#
|
||||
# This option is enabled with -fsymbol-fixup, and disabled by default with -fno-symbol-fixup
|
||||
#
|
||||
# 3) CodeWarrior versions below 2.3 used a different scheduler model.
|
||||
# The script can currently adjust function epilogues with the old_stack option.
|
||||
# -fprologue-fixup=[default=none, none, old_stack]
|
||||
"""
|
||||
|
||||
import struct
|
||||
|
||||
# Substitutions
|
||||
substitutions = (
|
||||
('<', '?0'),
|
||||
('>', '?1'),
|
||||
('@', '?2'),
|
||||
('\\', '?3'),
|
||||
(',', '?4'),
|
||||
('-', '?5')
|
||||
)
|
||||
|
||||
def format(symbol):
|
||||
for sub in substitutions:
|
||||
symbol = symbol.replace(sub[0], sub[1])
|
||||
|
||||
return symbol
|
||||
|
||||
def decodeformat(symbol):
|
||||
for sub in substitutions:
|
||||
symbol = symbol.replace(sub[1], sub[0])
|
||||
|
||||
return symbol
|
||||
|
||||
# Stream utilities
|
||||
|
||||
def read_u8(f):
|
||||
return struct.unpack("B", f.read(1))[0]
|
||||
|
||||
def read_u32(f):
|
||||
return struct.unpack(">I", f.read(4))[0]
|
||||
|
||||
def read_u16(f):
|
||||
return struct.unpack(">H", f.read(2))[0]
|
||||
|
||||
def write_u32(f, val):
|
||||
f.write(struct.pack(">I", val))
|
||||
|
||||
class ToReplace:
|
||||
def __init__(self, position, dest, src_size):
|
||||
self.position = position # Where in file
|
||||
self.dest = dest # String to patch
|
||||
self.src_size = src_size # Pad rest with zeroes
|
||||
|
||||
# print("To replace: %s %s %s" % (self.position, self.dest, self.src_size))
|
||||
|
||||
def read_string(f):
|
||||
tmp = ""
|
||||
c = 0xff
|
||||
while c != 0x00:
|
||||
c = read_u8(f)
|
||||
if c != 0:
|
||||
tmp += chr(c)
|
||||
return tmp
|
||||
|
||||
def ctor_realign(f, ofsSecHeader, nSecHeader, idxSegNameSeg):
|
||||
patch_align_ofs = []
|
||||
|
||||
for i in range(nSecHeader):
|
||||
f.seek(ofsSecHeader + i * 0x28)
|
||||
ofsname = read_u32(f)
|
||||
if not ofsname: continue
|
||||
|
||||
back = f.tell()
|
||||
|
||||
f.seek(ofsSecHeader + (idxSegNameSeg * 0x28) + 0x10)
|
||||
ofsShST = read_u32(f)
|
||||
f.seek(ofsShST + ofsname)
|
||||
name = read_string(f)
|
||||
if name == ".ctors" or name == ".dtors":
|
||||
patch_align_ofs.append(ofsSecHeader + i * 0x28 + 0x20)
|
||||
|
||||
f.seek(back)
|
||||
|
||||
return patch_align_ofs
|
||||
|
||||
SHT_PROGBITS = 1
|
||||
SHT_STRTAB = 3
|
||||
|
||||
def impl_postprocess_elf(f, do_ctor_realign, do_old_stack, do_symbol_fixup):
|
||||
result = []
|
||||
|
||||
f.seek(0x20)
|
||||
ofsSecHeader = read_u32(f)
|
||||
f.seek(0x30)
|
||||
nSecHeader = read_u16(f)
|
||||
idxSegNameSeg = read_u16(f)
|
||||
secF = False # First instance the section names
|
||||
|
||||
# Header: 0x32:
|
||||
patch_align_ofs = []
|
||||
|
||||
if do_ctor_realign:
|
||||
patch_align_ofs = ctor_realign(f, ofsSecHeader, nSecHeader, idxSegNameSeg)
|
||||
|
||||
for i in range(nSecHeader):
|
||||
f.seek(ofsSecHeader + i * 0x28)
|
||||
sh_name = read_u32(f)
|
||||
sh_type = read_u32(f)
|
||||
|
||||
if sh_type == SHT_STRTAB and do_symbol_fixup:
|
||||
if not secF:
|
||||
secF = True
|
||||
continue
|
||||
f.seek(ofsSecHeader + i * 0x28 + 0x10)
|
||||
ofs = read_u32(f)
|
||||
size = read_u32(f)
|
||||
|
||||
f.seek(ofs)
|
||||
string = ""
|
||||
str_spos = ofs
|
||||
for i in range(ofs, ofs+size):
|
||||
c = read_u8(f)
|
||||
if c == 0:
|
||||
if len(string):
|
||||
fixed = decodeformat(string)
|
||||
if fixed != string:
|
||||
result.append(ToReplace(str_spos, fixed, len(string)))
|
||||
string = ""
|
||||
str_spos = i+1
|
||||
else:
|
||||
string += chr(c)
|
||||
else:
|
||||
f.seek(ofsSecHeader + (idxSegNameSeg * 0x28) + 0x10)
|
||||
ofsShST = read_u32(f)
|
||||
f.seek(ofsShST + sh_name)
|
||||
name = read_string(f)
|
||||
|
||||
if name == ".text" and do_old_stack:
|
||||
f.seek(ofsSecHeader + i * 0x28 + 0x10)
|
||||
ofs = read_u32(f)
|
||||
size = read_u32(f)
|
||||
|
||||
# We assume
|
||||
# 1) Only instructions are in the .text section
|
||||
# 2) These instructions are 4-byte aligned
|
||||
assert ofs != 0
|
||||
assert ofs % 4 == 0
|
||||
assert size % 4 == 0
|
||||
|
||||
f.seek(ofs)
|
||||
|
||||
mtlr_pos = 0
|
||||
|
||||
# (mtlr position, blr position)
|
||||
epilogues = []
|
||||
|
||||
for _ in range(ofs, ofs+size, 4):
|
||||
it = f.tell()
|
||||
instr = read_u32(f)
|
||||
|
||||
# Skip padding
|
||||
if instr == 0: continue
|
||||
|
||||
# Call analysis is not actually required
|
||||
# No mtlr will exist without a blr; mtctr/bctr* is used for dynamic dispatch
|
||||
|
||||
# FUN_A:
|
||||
# li r3, 0
|
||||
# blr <---- No mtlr, move onto the next function
|
||||
# FUN_B:
|
||||
# ; complex function, stack manip
|
||||
# mtlr r0 <---- Expect a blr
|
||||
# addi r1, r1, 24
|
||||
# blr <---- Confirm patch above
|
||||
|
||||
# mtlr alias for mtspr
|
||||
if instr == 0x7C0803A6:
|
||||
assert mtlr_pos == 0
|
||||
mtlr_pos = it
|
||||
# blr
|
||||
elif instr == 0x4E800020:
|
||||
if mtlr_pos:
|
||||
epilogues.append((mtlr_pos, it))
|
||||
mtlr_pos = 0
|
||||
|
||||
|
||||
# Check for a lone mtlr
|
||||
assert mtlr_pos == 0
|
||||
|
||||
# Reunify mtlr/blr instructions, shifting intermediary instructions up
|
||||
for mtlr_pos, blr_pos in epilogues:
|
||||
# Check if we need to do anything
|
||||
if mtlr_pos + 4 == blr_pos: continue
|
||||
|
||||
# As the processor can only hold 6 instructions at once in the pipeline,
|
||||
# it's unlikely for the mtlr be shifted up more instructions than that--usually,
|
||||
# only one:
|
||||
# mtlr r0
|
||||
# addi r1, r1, 24
|
||||
# blr
|
||||
assert blr_pos - 4 > mtlr_pos
|
||||
assert blr_pos - mtlr_pos <= 6 * 4
|
||||
|
||||
print("Patching old epilogue: %s %s" % (mtlr_pos, blr_pos))
|
||||
|
||||
f.seek(mtlr_pos)
|
||||
mtlr = read_u32(f)
|
||||
|
||||
for it in range(mtlr_pos, blr_pos - 4, 4):
|
||||
f.seek(it + 4)
|
||||
next_instr = read_u32(f)
|
||||
f.seek(it)
|
||||
write_u32(f, next_instr)
|
||||
|
||||
f.seek(blr_pos - 4)
|
||||
write_u32(f, mtlr)
|
||||
|
||||
return (result, patch_align_ofs)
|
||||
|
||||
def postprocess_elf(f, do_ctor_realign, do_old_stack, do_symbol_fixup):
|
||||
patches = impl_postprocess_elf(f, do_ctor_realign, do_old_stack, do_symbol_fixup)
|
||||
|
||||
f.seek(0)
|
||||
source_bytes = list(f.read())
|
||||
for patch in patches[0]:
|
||||
assert len(patch.dest) <= patch.src_size
|
||||
for j in range(patch.src_size):
|
||||
if j >= len(patch.dest):
|
||||
c = 0
|
||||
else:
|
||||
c = ord(patch.dest[j])
|
||||
source_bytes[patch.position + j] = c
|
||||
|
||||
# Patch ctor align
|
||||
nP = 0
|
||||
for p in patches[1]:
|
||||
print("Patching ctors")
|
||||
source_bytes[p + 0] = 0
|
||||
source_bytes[p + 1] = 0
|
||||
source_bytes[p + 2] = 0
|
||||
source_bytes[p + 3] = 4
|
||||
nP += 1
|
||||
if nP > 1:
|
||||
print("Patched ctors + dtors")
|
||||
|
||||
f.seek(0)
|
||||
f.write(bytes(source_bytes))
|
||||
|
||||
def frontend(args):
|
||||
inplace = ""
|
||||
do_ctor_realign = False
|
||||
do_old_stack = False
|
||||
do_symbol_fixup = False
|
||||
|
||||
for arg in args:
|
||||
if arg.startswith('-f'):
|
||||
negated = False
|
||||
if arg.startswith('-fno-'):
|
||||
negated = True
|
||||
arg = arg[len('-fno-'):]
|
||||
else:
|
||||
arg = arg[len('-f'):]
|
||||
|
||||
if arg == 'ctor_realign':
|
||||
do_ctor_realign = not negated
|
||||
elif arg == 'symbol-fixup':
|
||||
do_symbol_fixup = not negated
|
||||
elif arg.startswith('prologue-fixup='):
|
||||
do_old_stack = arg[len('prologue-fixup='):] == 'old_stack'
|
||||
else:
|
||||
print("Unknown argument: %s" % arg)
|
||||
elif arg.startswith('-'):
|
||||
print("Unknown argument: %s. Perhaps you meant -f%s?" % (arg, arg))
|
||||
else:
|
||||
if inplace:
|
||||
print("Cannot process %s. Only one source file may be specified." % arg)
|
||||
else:
|
||||
inplace = arg
|
||||
|
||||
if not inplace:
|
||||
print("A file must be specified!")
|
||||
return
|
||||
|
||||
try:
|
||||
postprocess_elf(open(inplace, 'rb+'), do_ctor_realign, do_old_stack, do_symbol_fixup)
|
||||
except FileNotFoundError:
|
||||
print("Cannot open file %s" % inplace)
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
print(BANNER)
|
||||
else:
|
||||
frontend(sys.argv[1:])
|
Loading…
Reference in New Issue
Block a user