mirror of https://github.com/PrimeDecomp/prime.git
commit
dfe56fda27
|
@ -0,0 +1,10 @@
|
|||
# Auto detect text files and perform LF normalization
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files
|
||||
*.py text
|
||||
|
||||
# Enforce platform-specific encodings
|
||||
*.bat text eol=crlf
|
||||
*.sh text eol=lf
|
||||
*.sha1 text eol=lf
|
|
@ -0,0 +1,38 @@
|
|||
.vscode
|
||||
*.dat
|
||||
*.exe
|
||||
*.dll
|
||||
*.idb
|
||||
*.id0
|
||||
*.id1
|
||||
*.id2
|
||||
*.nam
|
||||
*.til
|
||||
*.o
|
||||
*.out
|
||||
*.elf
|
||||
*.dol
|
||||
*.a
|
||||
*.d
|
||||
*.map
|
||||
*.exe
|
||||
*.dump
|
||||
*.7z
|
||||
*.bat
|
||||
*.sln
|
||||
*.filters
|
||||
*.vcxproj
|
||||
*.user
|
||||
include/*.s
|
||||
|
||||
build/
|
||||
tools/mwcc_compiler/
|
||||
tools/elf2dol
|
||||
tools/elf2rel
|
||||
decomp/
|
||||
errors.txt
|
||||
output.asm
|
||||
Debug/
|
||||
.vs/
|
||||
|
||||
ctx.c
|
|
@ -0,0 +1,208 @@
|
|||
ifneq ($(findstring MINGW,$(shell uname)),)
|
||||
WINDOWS := 1
|
||||
endif
|
||||
ifneq ($(findstring MSYS,$(shell uname)),)
|
||||
WINDOWS := 1
|
||||
endif
|
||||
|
||||
# If 0, tells the console to chill out. (Quiets the make process.)
|
||||
VERBOSE ?= 1
|
||||
|
||||
# If MAPGENFLAG set to 1, tells LDFLAGS to generate a mapfile, which makes linking take several minutes.
|
||||
MAPGENFLAG ?= 1
|
||||
|
||||
ifeq ($(VERBOSE),0)
|
||||
QUIET := @
|
||||
endif
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Files
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
NAME := mp1
|
||||
VERSION ?= 0
|
||||
|
||||
# Overkill epilogue fixup strategy. Set to 1 if necessary.
|
||||
EPILOGUE_PROCESS := 0
|
||||
|
||||
BUILD_DIR := build/$(NAME).$(VERSION)
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
EPILOGUE_DIR := epilogue/$(NAME).$(VERSION)
|
||||
endif
|
||||
|
||||
# Inputs
|
||||
S_FILES := $(wildcard asm/*.s)
|
||||
C_FILES := $(wildcard src/*.c)
|
||||
CPP_FILES := $(wildcard src/*.cpp)
|
||||
CPP_FILES += $(wildcard src/*.cp)
|
||||
LDSCRIPT := $(BUILD_DIR)/ldscript.lcf
|
||||
|
||||
# Outputs
|
||||
DOL := $(BUILD_DIR)/main.dol
|
||||
ELF := $(DOL:.dol=.elf)
|
||||
MAP := $(BUILD_DIR)/MetroidPrime.MAP
|
||||
|
||||
|
||||
ifeq ($(MAPGENFLAG),1)
|
||||
MAPGEN := -map $(MAP)
|
||||
endif
|
||||
|
||||
include obj_files.mk
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
include e_files.mk
|
||||
endif
|
||||
|
||||
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)
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
E_FILES := $(EPILOGUE_UNSCHEDULED)
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
||||
# Tools
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
MWCC_VERSION := 2.6
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
MWCC_EPI_VERSION := 1.2.5
|
||||
MWCC_EPI_EXE := mwcceppc.exe
|
||||
endif
|
||||
MWLD_VERSION := 2.6
|
||||
|
||||
# Programs
|
||||
ifeq ($(WINDOWS),1)
|
||||
WINE :=
|
||||
AS := $(DEVKITPPC)/bin/powerpc-eabi-as.exe
|
||||
CPP := $(DEVKITPPC)/bin/powerpc-eabi-cpp.exe -P
|
||||
else
|
||||
WINE ?= wine
|
||||
AS := $(DEVKITPPC)/bin/powerpc-eabi-as
|
||||
CPP := $(DEVKITPPC)/bin/powerpc-eabi-cpp -P
|
||||
endif
|
||||
CC = $(WINE) tools/mwcc_compiler/$(MWCC_VERSION)/mwcceppc.exe
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
CC_EPI = $(WINE) tools/mwcc_compiler/$(MWCC_EPI_VERSION)/$(MWCC_EPI_EXE)
|
||||
endif
|
||||
LD := $(WINE) tools/mwcc_compiler/$(MWLD_VERSION)/mwldeppc.exe
|
||||
ELF2DOL := tools/elf2dol
|
||||
SHA1SUM := sha1sum
|
||||
PYTHON := python3
|
||||
|
||||
FRANK := tools/franklite.py
|
||||
|
||||
# Options
|
||||
INCLUDES := -i include/
|
||||
ASM_INCLUDES := -I include/
|
||||
|
||||
ASFLAGS := -mgekko $(ASM_INCLUDES) --defsym version=$(VERSION)
|
||||
ifeq ($(VERBOSE),1)
|
||||
# this set of LDFLAGS outputs warnings.
|
||||
LDFLAGS := $(MAPGEN) -fp hard -nodefaults
|
||||
endif
|
||||
ifeq ($(VERBOSE),0)
|
||||
# this set of LDFLAGS generates no warnings.
|
||||
LDFLAGS := $(MAPGEN) -fp hard -nodefaults -w off
|
||||
endif
|
||||
CFLAGS = -Cpp_exceptions off -enum int -inline auto -proc gekko -RTTI off -fp hard -fp_contract on -rostr -O4,p -use_lmw_stmw on -sdata 8 -sdata2 8 -nodefaults $(INCLUDES)
|
||||
|
||||
ifeq ($(VERBOSE),0)
|
||||
# this set of ASFLAGS generates no warnings.
|
||||
ASFLAGS += -W
|
||||
endif
|
||||
|
||||
$(BUILD_DIR)/src/os/__start.o: MWCC_VERSION := 1.2.5
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Recipes
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
### Default target ###
|
||||
|
||||
default: all
|
||||
|
||||
all: $(DOL)
|
||||
|
||||
ALL_DIRS := $(sort $(dir $(O_FILES)))
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
EPI_DIRS := $(sort $(dir $(E_FILES)))
|
||||
endif
|
||||
|
||||
# Make sure build directory exists before compiling anything
|
||||
DUMMY != mkdir -p $(ALL_DIRS)
|
||||
|
||||
# ifeq ($(EPILOGUE_PROCESS),1)
|
||||
# Make sure profile directory exists before compiling anything
|
||||
# DUMMY != mkdir -p $(EPI_DIRS)
|
||||
# endif
|
||||
|
||||
.PHONY: tools
|
||||
|
||||
$(LDSCRIPT): ldscript.lcf
|
||||
$(QUIET) $(CPP) -MMD -MP -MT $@ -MF $@.d -I include/ -I . -DBUILD_DIR=$(BUILD_DIR) -o $@ $<
|
||||
|
||||
$(DOL): $(ELF) | tools
|
||||
$(QUIET) $(ELF2DOL) $< $@
|
||||
$(QUIET) $(SHA1SUM) -c sha1/$(NAME).$(VERSION).sha1
|
||||
ifneq ($(findstring -map,$(LDFLAGS)),)
|
||||
$(QUIET) $(PYTHON) tools/calcprogress.py $@
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -f -d -r build
|
||||
rm -f -d -r epilogue
|
||||
find . -name '*.o' -exec rm {} +
|
||||
find . -name 'ctx.c' -exec rm {} +
|
||||
find ./include -name "*.s" -type f -delete
|
||||
$(MAKE) -C tools clean
|
||||
tools:
|
||||
$(MAKE) -C tools
|
||||
|
||||
# ELF creation makefile instructions
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
@echo Linking ELF $@
|
||||
$(ELF): $(O_FILES) $(E_FILES) $(LDSCRIPT)
|
||||
$(QUIET) @echo $(O_FILES) > build/o_files
|
||||
$(QUIET) $(LD) $(LDFLAGS) -o $@ -lcf $(LDSCRIPT) @build/o_files
|
||||
else
|
||||
$(ELF): $(O_FILES) $(LDSCRIPT)
|
||||
@echo Linking ELF $@
|
||||
$(QUIET) @echo $(O_FILES) > build/o_files
|
||||
$(QUIET) $(LD) $(LDFLAGS) -o $@ -lcf $(LDSCRIPT) @build/o_files
|
||||
endif
|
||||
|
||||
$(BUILD_DIR)/%.o: %.s
|
||||
@echo Assembling $<
|
||||
$(QUIET) $(AS) $(ASFLAGS) -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: %.c
|
||||
@echo "Compiling " $<
|
||||
$(QUIET) $(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: %.cp
|
||||
@echo "Compiling " $<
|
||||
$(QUIET) $(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(BUILD_DIR)/%.o: %.cpp
|
||||
@echo "Compiling " $<
|
||||
$(QUIET) $(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
$(EPILOGUE_DIR)/%.o: %.c $(BUILD_DIR)/%.o
|
||||
@echo Frank is fixing $<
|
||||
$(QUIET) $(PYTHON) $(FRANK) $(word 2,$^) $(word 2,$^)
|
||||
|
||||
$(EPILOGUE_DIR)/%.o: %.cp $(BUILD_DIR)/%.o
|
||||
@echo Frank is fixing $<
|
||||
$(QUIET) $(PYTHON) $(FRANK) $(word 2,$^) $(word 2,$^)
|
||||
|
||||
$(EPILOGUE_DIR)/%.o: %.cpp $(BUILD_DIR)/%.o
|
||||
@echo Frank is fixing $<
|
||||
$(QUIET) $(PYTHON) $(FRANK) $(word 2,$^) $(word 2,$^)
|
||||
endif
|
||||
# If we need Frank, add the following after the @echo
|
||||
# $(QUIET) $(CC_EPI) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
### Debug Print ###
|
||||
|
||||
print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true
|
|
@ -0,0 +1,104 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section .init, "ax" # 0x80003100 - 0x80005620
|
||||
|
||||
.global memset
|
||||
memset:
|
||||
/* 800033A8 000003A8 94 21 FF F0 */ stwu r1, -0x10(r1)
|
||||
/* 800033AC 000003AC 7C 08 02 A6 */ mflr r0
|
||||
/* 800033B0 000003B0 90 01 00 14 */ stw r0, 0x14(r1)
|
||||
/* 800033B4 000003B4 93 E1 00 0C */ stw r31, 0xc(r1)
|
||||
/* 800033B8 000003B8 7C 7F 1B 78 */ mr r31, r3
|
||||
/* 800033BC 000003BC 48 00 00 1D */ bl __fill_mem
|
||||
/* 800033C0 000003C0 80 01 00 14 */ lwz r0, 0x14(r1)
|
||||
/* 800033C4 000003C4 7F E3 FB 78 */ mr r3, r31
|
||||
/* 800033C8 000003C8 83 E1 00 0C */ lwz r31, 0xc(r1)
|
||||
/* 800033CC 000003CC 7C 08 03 A6 */ mtlr r0
|
||||
/* 800033D0 000003D0 38 21 00 10 */ addi r1, r1, 0x10
|
||||
/* 800033D4 000003D4 4E 80 00 20 */ blr
|
||||
|
||||
.global __fill_mem
|
||||
__fill_mem:
|
||||
/* 800033D8 000003D8 28 05 00 20 */ cmplwi r5, 0x20
|
||||
/* 800033DC 000003DC 54 84 06 3E */ clrlwi r4, r4, 0x18
|
||||
/* 800033E0 000003E0 38 C3 FF FF */ addi r6, r3, -1
|
||||
/* 800033E4 000003E4 7C 87 23 78 */ mr r7, r4
|
||||
/* 800033E8 000003E8 41 80 00 90 */ blt lbl_80003478
|
||||
/* 800033EC 000003EC 7C C0 30 F8 */ nor r0, r6, r6
|
||||
/* 800033F0 000003F0 54 03 07 BF */ clrlwi. r3, r0, 0x1e
|
||||
/* 800033F4 000003F4 41 82 00 14 */ beq lbl_80003408
|
||||
/* 800033F8 000003F8 7C A3 28 50 */ subf r5, r3, r5
|
||||
lbl_800033FC:
|
||||
/* 800033FC 000003FC 34 63 FF FF */ addic. r3, r3, -1
|
||||
/* 80003400 00000400 9C E6 00 01 */ stbu r7, 1(r6)
|
||||
/* 80003404 00000404 40 82 FF F8 */ bne lbl_800033FC
|
||||
lbl_80003408:
|
||||
/* 80003408 00000408 28 07 00 00 */ cmplwi r7, 0
|
||||
/* 8000340C 0000040C 41 82 00 1C */ beq lbl_80003428
|
||||
/* 80003410 00000410 54 E3 C0 0E */ slwi r3, r7, 0x18
|
||||
/* 80003414 00000414 54 E0 80 1E */ slwi r0, r7, 0x10
|
||||
/* 80003418 00000418 54 E4 40 2E */ slwi r4, r7, 8
|
||||
/* 8000341C 0000041C 7C 60 03 78 */ or r0, r3, r0
|
||||
/* 80003420 00000420 7C 80 03 78 */ or r0, r4, r0
|
||||
/* 80003424 00000424 7C E7 03 78 */ or r7, r7, r0
|
||||
lbl_80003428:
|
||||
/* 80003428 00000428 54 A3 D9 7F */ rlwinm. r3, r5, 0x1b, 5, 0x1f
|
||||
/* 8000342C 0000042C 38 86 FF FD */ addi r4, r6, -3
|
||||
/* 80003430 00000430 41 82 00 2C */ beq lbl_8000345C
|
||||
lbl_80003434:
|
||||
/* 80003434 00000434 90 E4 00 04 */ stw r7, 4(r4)
|
||||
/* 80003438 00000438 34 63 FF FF */ addic. r3, r3, -1
|
||||
/* 8000343C 0000043C 90 E4 00 08 */ stw r7, 8(r4)
|
||||
/* 80003440 00000440 90 E4 00 0C */ stw r7, 0xc(r4)
|
||||
/* 80003444 00000444 90 E4 00 10 */ stw r7, 0x10(r4)
|
||||
/* 80003448 00000448 90 E4 00 14 */ stw r7, 0x14(r4)
|
||||
/* 8000344C 0000044C 90 E4 00 18 */ stw r7, 0x18(r4)
|
||||
/* 80003450 00000450 90 E4 00 1C */ stw r7, 0x1c(r4)
|
||||
/* 80003454 00000454 94 E4 00 20 */ stwu r7, 0x20(r4)
|
||||
/* 80003458 00000458 40 82 FF DC */ bne lbl_80003434
|
||||
lbl_8000345C:
|
||||
/* 8000345C 0000045C 54 A3 F7 7F */ rlwinm. r3, r5, 0x1e, 0x1d, 0x1f
|
||||
/* 80003460 00000460 41 82 00 10 */ beq lbl_80003470
|
||||
lbl_80003464:
|
||||
/* 80003464 00000464 34 63 FF FF */ addic. r3, r3, -1
|
||||
/* 80003468 00000468 94 E4 00 04 */ stwu r7, 4(r4)
|
||||
/* 8000346C 0000046C 40 82 FF F8 */ bne lbl_80003464
|
||||
lbl_80003470:
|
||||
/* 80003470 00000470 38 C4 00 03 */ addi r6, r4, 3
|
||||
/* 80003474 00000474 54 A5 07 BE */ clrlwi r5, r5, 0x1e
|
||||
lbl_80003478:
|
||||
/* 80003478 00000478 28 05 00 00 */ cmplwi r5, 0
|
||||
/* 8000347C 0000047C 4D 82 00 20 */ beqlr
|
||||
lbl_80003480:
|
||||
/* 80003480 00000480 34 A5 FF FF */ addic. r5, r5, -1
|
||||
/* 80003484 00000484 9C E6 00 01 */ stbu r7, 1(r6)
|
||||
/* 80003488 00000488 40 82 FF F8 */ bne lbl_80003480
|
||||
/* 8000348C 0000048C 4E 80 00 20 */ blr
|
||||
|
||||
.global memcpy
|
||||
memcpy:
|
||||
/* 80003490 00000490 7C 04 18 40 */ cmplw r4, r3
|
||||
/* 80003494 00000494 41 80 00 28 */ blt lbl_800034BC
|
||||
/* 80003498 00000498 38 84 FF FF */ addi r4, r4, -1
|
||||
/* 8000349C 0000049C 38 C3 FF FF */ addi r6, r3, -1
|
||||
/* 800034A0 000004A0 38 A5 00 01 */ addi r5, r5, 1
|
||||
/* 800034A4 000004A4 48 00 00 0C */ b lbl_800034B0
|
||||
lbl_800034A8:
|
||||
/* 800034A8 000004A8 8C 04 00 01 */ lbzu r0, 1(r4)
|
||||
/* 800034AC 000004AC 9C 06 00 01 */ stbu r0, 1(r6)
|
||||
lbl_800034B0:
|
||||
/* 800034B0 000004B0 34 A5 FF FF */ addic. r5, r5, -1
|
||||
/* 800034B4 000004B4 40 82 FF F4 */ bne lbl_800034A8
|
||||
/* 800034B8 000004B8 4E 80 00 20 */ blr
|
||||
lbl_800034BC:
|
||||
/* 800034BC 000004BC 7C 84 2A 14 */ add r4, r4, r5
|
||||
/* 800034C0 000004C0 7C C3 2A 14 */ add r6, r3, r5
|
||||
/* 800034C4 000004C4 38 A5 00 01 */ addi r5, r5, 1
|
||||
/* 800034C8 000004C8 48 00 00 0C */ b lbl_800034D4
|
||||
lbl_800034CC:
|
||||
/* 800034CC 000004CC 8C 04 FF FF */ lbzu r0, -1(r4)
|
||||
/* 800034D0 000004D0 9C 06 FF FF */ stbu r0, -1(r6)
|
||||
lbl_800034D4:
|
||||
/* 800034D4 000004D4 34 A5 FF FF */ addic. r5, r5, -1
|
||||
/* 800034D8 000004D8 40 82 FF F4 */ bne lbl_800034CC
|
||||
/* 800034DC 000004DC 4E 80 00 20 */ blr
|
|
@ -0,0 +1,6 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section .ctors, "wa" # 0x803CB1C0 - 0x803CB380
|
||||
.global lbl_803CB1C0
|
||||
lbl_803CB1C0:
|
||||
.incbin "baserom.dol", 0x3C81C0, 0x1C0
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,6 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section .dtors, "wa" # 0x803CB380 - 0x803CB3A0
|
||||
.global lbl_803CB380
|
||||
lbl_803CB380:
|
||||
.incbin "baserom.dol", 0x3C8380, 0x10
|
|
@ -0,0 +1,6 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section extab_, "wa" # 0x800035A0 - 0x800035E0
|
||||
.global lbl_extab
|
||||
lbl_extab:
|
||||
.incbin "baserom.dol", 0x3C8120, 0x28
|
|
@ -0,0 +1,8 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section extabindex_, "wa" # 0x800035E0 - 0x80003640
|
||||
lbl_extabindex:
|
||||
.incbin "baserom.dol", 0x3C8160, 0x24
|
||||
.global lbl_80003604
|
||||
lbl_80003604:
|
||||
.incbin "baserom.dol", 0x3C8184, 0x20
|
|
@ -0,0 +1,31 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section .init, "ax" # 0x80003100 - 0x80005620
|
||||
|
||||
.global __init_hardware
|
||||
__init_hardware:
|
||||
/* 80003354 00000354 7C 00 00 A6 */ mfmsr r0
|
||||
/* 80003358 00000358 60 00 20 00 */ ori r0, r0, 0x2000
|
||||
/* 8000335C 0000035C 7C 00 01 24 */ mtmsr r0
|
||||
/* 80003360 00000360 7F E8 02 A6 */ mflr r31
|
||||
/* 80003364 00000364 48 0B 19 49 */ bl __OSPSInit
|
||||
/* 80003368 00000368 48 0B 32 8D */ bl __OSCacheInit
|
||||
/* 8000336C 0000036C 7F E8 03 A6 */ mtlr r31
|
||||
/* 80003370 00000370 4E 80 00 20 */ blr
|
||||
|
||||
.global __flush_cache
|
||||
__flush_cache:
|
||||
/* 80003374 00000374 3C A0 FF FF */ lis r5, 0xFFFFFFF1@h
|
||||
/* 80003378 00000378 60 A5 FF F1 */ ori r5, r5, 0xFFFFFFF1@l
|
||||
/* 8000337C 0000037C 7C A5 18 38 */ and r5, r5, r3
|
||||
/* 80003380 00000380 7C 65 18 50 */ subf r3, r5, r3
|
||||
/* 80003384 00000384 7C 84 1A 14 */ add r4, r4, r3
|
||||
lbl_80003388:
|
||||
/* 80003388 00000388 7C 00 28 6C */ dcbst 0, r5
|
||||
/* 8000338C 0000038C 7C 00 04 AC */ sync 0
|
||||
/* 80003390 00000390 7C 00 2F AC */ icbi 0, r5
|
||||
/* 80003394 00000394 30 A5 00 08 */ addic r5, r5, 8
|
||||
/* 80003398 00000398 34 84 FF F8 */ addic. r4, r4, -8
|
||||
/* 8000339C 0000039C 40 80 FF EC */ bge lbl_80003388
|
||||
/* 800033A0 000003A0 4C 00 01 2C */ isync
|
||||
/* 800033A4 000003A4 4E 80 00 20 */ blr
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,69 @@
|
|||
.include "macros.inc"
|
||||
|
||||
.section .sbss2, "", @nobits # 0x805AF460 - 0x805AF4C7
|
||||
.global lbl_805AF460
|
||||
lbl_805AF460:
|
||||
.skip 0x8
|
||||
.global lbl_805AF468
|
||||
lbl_805AF468:
|
||||
.skip 0x4
|
||||
.global lbl_805AF46C
|
||||
lbl_805AF46C:
|
||||
.skip 0x4
|
||||
.global lbl_805AF470
|
||||
lbl_805AF470:
|
||||
.skip 0x4
|
||||
.global lbl_805AF474
|
||||
lbl_805AF474:
|
||||
.skip 0x4
|
||||
.global lbl_805AF478
|
||||
lbl_805AF478:
|
||||
.skip 0x4
|
||||
.global lbl_805AF47C
|
||||
lbl_805AF47C:
|
||||
.skip 0x4
|
||||
.global lbl_805AF480
|
||||
lbl_805AF480:
|
||||
.skip 0x8
|
||||
.global lbl_805AF488
|
||||
lbl_805AF488:
|
||||
.skip 0x4
|
||||
.global lbl_805AF48C
|
||||
lbl_805AF48C:
|
||||
.skip 0x4
|
||||
.global lbl_805AF490
|
||||
lbl_805AF490:
|
||||
.skip 0x4
|
||||
.global lbl_805AF494
|
||||
lbl_805AF494:
|
||||
.skip 0x4
|
||||
.global lbl_805AF498
|
||||
lbl_805AF498:
|
||||
.skip 0x8
|
||||
.global lbl_805AF4A0
|
||||
lbl_805AF4A0:
|
||||
.skip 0x8
|
||||
.global lbl_805AF4A8
|
||||
lbl_805AF4A8:
|
||||
.skip 0x4
|
||||
.global lbl_805AF4AC
|
||||
lbl_805AF4AC:
|
||||
.skip 0x4
|
||||
.global lbl_805AF4B0
|
||||
lbl_805AF4B0:
|
||||
.skip 0x4
|
||||
.global lbl_805AF4B4
|
||||
lbl_805AF4B4:
|
||||
.skip 0x4
|
||||
.global lbl_805AF4B8
|
||||
lbl_805AF4B8:
|
||||
.skip 0x4
|
||||
.global lbl_805AF4BC
|
||||
lbl_805AF4BC:
|
||||
.skip 0x4
|
||||
.global lbl_805AF4C0
|
||||
lbl_805AF4C0:
|
||||
.skip 0x4
|
||||
.global lbl_805AF4C4
|
||||
lbl_805AF4C4:
|
||||
.skip 0x4
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1 @@
|
|||
4d836df38a8442529f8552aaca041fcfbcec58ac
|
|
@ -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/mp1.0/main.dol > main.dump
|
||||
diff -u --color=always baserom.dump main.dump
|
|
@ -0,0 +1,56 @@
|
|||
#include "types.h"
|
||||
#include "Dolphin/db.h"
|
||||
|
||||
#define PAD3_BUTTON_ADDR 0x800030E4
|
||||
#define OS_RESET_RESTART 0
|
||||
#define FALSE 0
|
||||
#define TRUE 1
|
||||
#define EXCEPTIONMASK_ADDR 0x80000044
|
||||
#define BOOTINFO2_ADDR 0x800000F4
|
||||
#define OS_BI2_DEBUGFLAG_OFFSET 0xC
|
||||
#define ARENAHI_ADDR 0x80000034
|
||||
#define DEBUGFLAG_ADDR 0x800030E8
|
||||
#define DVD_DEVICECODE_ADDR 0x800030E6
|
||||
|
||||
#define MSR_FP 0x2000
|
||||
|
||||
extern void InitMetroTRK();
|
||||
|
||||
u16 Pad3Button : PAD3_BUTTON_ADDR;
|
||||
static u8 Debug_BBA = 0;
|
||||
|
||||
extern void memset(void*,int,int);
|
||||
extern int main(int argc, char* argv[]);
|
||||
extern void exit(int);
|
||||
extern void __init_user(void);
|
||||
extern void OSInit(void);
|
||||
extern void DBInit(void);
|
||||
extern void OSResetSystem(BOOL reset, u32 resetCode, BOOL forceMenu);
|
||||
extern void __OSCacheInit ( void );
|
||||
extern void __OSPSInit ( void );
|
||||
|
||||
__declspec(section ".init") extern void __check_pad3(void);
|
||||
__declspec(section ".init") extern void __start(void);
|
||||
__declspec(section ".init") extern void __init_registers(void);
|
||||
__declspec(section ".init") extern void __init_data(void);
|
||||
__declspec(section ".init") extern void __init_hardware(void);
|
||||
__declspec(section ".init") extern void __flush_cache(void *address, unsigned int size);
|
||||
|
||||
__declspec(section ".init") extern char _stack_addr[];
|
||||
__declspec(section ".init") extern char _SDA_BASE_[];
|
||||
__declspec(section ".init") extern char _SDA2_BASE_[];
|
||||
|
||||
typedef struct __rom_copy_info {
|
||||
char* rom;
|
||||
char* addr;
|
||||
unsigned int size;
|
||||
} __rom_copy_info;
|
||||
|
||||
__declspec(section ".init") extern __rom_copy_info _rom_copy_info[];
|
||||
|
||||
typedef struct __bss_init_info {
|
||||
char* addr;
|
||||
unsigned int size;
|
||||
} __bss_init_info;
|
||||
|
||||
__declspec(section ".init") extern __bss_init_info _bss_init_info[];
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef _DOLPHIN_DB_H
|
||||
#define _DOLPHIN_DB_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define ExceptionHookDestination 0x80000048
|
||||
#define IsDebuggerPresent 0x80000040
|
||||
|
||||
// static int __DBInterface;
|
||||
|
||||
struct DBInterface {
|
||||
u8 filler0[4];
|
||||
u32 unk4;
|
||||
};
|
||||
|
||||
static struct DBInterface* __DBInterface;
|
||||
static int DBVerbose;
|
||||
|
||||
void DBInit(void);
|
||||
void DBInitComm(int* inputFlagPtr, int* mtrCallback);
|
||||
static void __DBExceptionDestination(void);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Code sections:
|
||||
.init: 0x00000100 0x80003100 0x800035A0
|
||||
.text: 0x000005A0 0x80003640 0x803CB1C0
|
||||
Data sections:
|
||||
extab_: 0x003C8120 0x800035A0 0x800035E0
|
||||
extabindex_: 0x003C8160 0x800035E0 0x80003640
|
||||
.ctors: 0x003C81C0 0x803CB1C0 0x803CB380
|
||||
.dtors: 0x003C8380 0x803CB380 0x803CB3A0
|
||||
.rodata: 0x003C83A0 0x803CB3A0 0x803D8D60
|
||||
.data: 0x003D5D60 0x803D8D60 0x803F7560
|
||||
.sdata: 0x003F4560 0x805A6BC0 0x805A8C20
|
||||
.sbss:
|
||||
.sdata2: 0x003F65C0 0x805A9D20 0x805AF460
|
||||
.sbss2:
|
||||
BSS section:
|
||||
.bss: 0x00000000 0x803F7560 0x805A6BBF
|
||||
Entry Point: 0x80003140
|
||||
*/
|
||||
# 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
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef _TYPES_H
|
||||
#define _TYPES_H
|
||||
|
||||
//#include "BuildSettings.h"
|
||||
|
||||
// r2 is 801DC960
|
||||
// r13 is 801DB420
|
||||
|
||||
typedef signed char s8;
|
||||
typedef signed short s16;
|
||||
typedef signed long s32;
|
||||
typedef signed long long s64;
|
||||
typedef unsigned char u8;
|
||||
typedef unsigned short u16;
|
||||
typedef unsigned long u32;
|
||||
typedef unsigned long size_t;
|
||||
typedef unsigned long long u64;
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
typedef volatile s8 vs8;
|
||||
typedef volatile s16 vs16;
|
||||
typedef volatile s32 vs32;
|
||||
typedef volatile s64 vs64;
|
||||
|
||||
typedef float f32;
|
||||
typedef double f64;
|
||||
typedef volatile f32 vf32;
|
||||
typedef volatile f64 vf64;
|
||||
|
||||
typedef int BOOL;
|
||||
|
||||
typedef unsigned int uintptr_t; // Manually added
|
||||
|
||||
// Pointer to unknown, to be determined at a later date.
|
||||
typedef void* unkptr;
|
||||
|
||||
typedef u32 unknown;
|
||||
|
||||
#ifndef TRUE
|
||||
#define TRUE 1
|
||||
#endif // ifndef TRUE
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif // ifndef FALSE
|
||||
|
||||
#ifndef NULL
|
||||
#define NULL ((void*)0)
|
||||
#endif // ifndef NULL
|
||||
|
||||
#ifndef nullptr
|
||||
#define nullptr 0
|
||||
#endif // ifndef nullptr
|
||||
|
||||
// For functions that return 0 on a success and -1 on failure
|
||||
#ifndef EXIT_SUCCESS
|
||||
#define EXIT_SUCCESS 0
|
||||
#define EXIT_FAILURE -1
|
||||
#endif // ifndef EXIT_SUCCESS
|
||||
|
||||
#define ASSERT_HANG(cond) \
|
||||
if (!(cond)) { \
|
||||
while (true) { } \
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,42 @@
|
|||
MEMORY
|
||||
{
|
||||
text : origin = 0x80003100
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
GROUP:
|
||||
{
|
||||
.init ALIGN(0x20):{}
|
||||
/* TODO: should be extab and extabindex */
|
||||
extab_ ALIGN(0x20):{}
|
||||
extabindex_ 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 size upped from the default of 65535 */
|
||||
_stack_addr = (_f_sbss2 + SIZEOF(.sbss2) + 68608 + 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;
|
||||
}
|
||||
|
||||
FORCEFILES
|
||||
{
|
||||
ctors.o
|
||||
dtors.o
|
||||
extab.o
|
||||
extabindex.o
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
ENTRY(__start)
|
||||
_SDA_BASE_ = 0x805AEBC0;
|
||||
_SDA2_BASE_ = 0x805B1D20;
|
||||
PHDRS
|
||||
{
|
||||
init PT_LOAD;
|
||||
text PT_LOAD;
|
||||
extab_ PT_LOAD;
|
||||
extabindex_ PT_LOAD;
|
||||
ctors PT_LOAD;
|
||||
dtors PT_LOAD;
|
||||
rodata PT_LOAD;
|
||||
data PT_LOAD;
|
||||
bss PT_LOAD;
|
||||
sdata PT_LOAD;
|
||||
sbss PT_LOAD;
|
||||
sdata2 PT_LOAD;
|
||||
sbss2 PT_LOAD;
|
||||
}
|
||||
SECTIONS
|
||||
{
|
||||
.init 0x80003100 : { *(.init) } : init
|
||||
.text 0x80003640 : { *(.text) } : text
|
||||
extab_ 0x800035A0 : { *(extab_) } : extab_
|
||||
extabindex_ 0x800035E0 : { *(extabindex_) } : extabindex_
|
||||
.ctors 0x803CB1C0 : { *(.ctors) } : ctors
|
||||
.dtors 0x803CB380 : { *(.dtors) } : dtors
|
||||
.rodata 0x803CB3A0 : { *(.rodata) } : rodata
|
||||
.data 0x803D8D60 : { *(.data) } : data
|
||||
.bss 0x803F7560c (NOLOAD) : { *(.bss) } : bss
|
||||
.sdata 0x805A6BC0 : { *(.sdata) } : sdata
|
||||
.sbss 0x805A8C20 (NOLOAD) : { *(.sbss) } : sbss
|
||||
.sdata2 0x805A9D20 : { *(.sdata2) } : sdata2
|
||||
.sbss2 0x805AF460 (NOLOAD) : { *(.sbss2) } : sbss2
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
INIT_O_FILES :=\
|
||||
$(BUILD_DIR)/src/os/__start.o\
|
||||
$(BUILD_DIR)/asm/os/__ppc_eabi_init.o\
|
||||
$(BUILD_DIR)/asm/Runtime/PPCEABI/H/__mem.o
|
||||
|
||||
TEXT_O_FILES :=\
|
||||
$(BUILD_DIR)/asm/text.o
|
||||
|
||||
EXTAB_O_FILES :=\
|
||||
$(BUILD_DIR)/asm/extab.o
|
||||
|
||||
EXTABINDEX_O_FILES :=\
|
||||
$(BUILD_DIR)/asm/extabindex.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
|
|
@ -0,0 +1 @@
|
|||
949c5ed7368aef547e0b0db1c3678f466e2afbff build/mp1.0/main.dol
|
|
@ -0,0 +1,171 @@
|
|||
#include "Dolphin/__start.h"
|
||||
|
||||
#pragma section code_type ".init"
|
||||
|
||||
void __check_pad3(void)
|
||||
{
|
||||
if ((Pad3Button & 0x0eef) == 0x0eef) {
|
||||
OSResetSystem(OS_RESET_RESTART, 0, FALSE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
|
||||
__declspec (weak) asm void __start(void)
|
||||
{
|
||||
nofralloc
|
||||
bl __init_registers
|
||||
bl __init_hardware
|
||||
li r0, -1
|
||||
stwu r1, -8(r1)
|
||||
stw r0, 4(r1)
|
||||
stw r0, 0(r1)
|
||||
bl __init_data
|
||||
li r0, 0
|
||||
lis r6, EXCEPTIONMASK_ADDR@ha
|
||||
addi r6, r6, EXCEPTIONMASK_ADDR@l
|
||||
stw r0, 0(r6)
|
||||
lis r6, BOOTINFO2_ADDR@ha
|
||||
addi r6, r6, BOOTINFO2_ADDR@l
|
||||
lwz r6, 0(r6)
|
||||
|
||||
_check_TRK:
|
||||
cmplwi r6, 0
|
||||
beq _load_lomem_debug_flag
|
||||
lwz r7, OS_BI2_DEBUGFLAG_OFFSET(r6)
|
||||
b _check_debug_flag
|
||||
|
||||
_load_lomem_debug_flag:
|
||||
lis r5, ARENAHI_ADDR@ha
|
||||
addi r5, r5, ARENAHI_ADDR@l
|
||||
lwz r5, 0(r5)
|
||||
cmplwi r5, 0
|
||||
beq _goto_main
|
||||
lis r7, DEBUGFLAG_ADDR@ha
|
||||
addi r7, r7, DEBUGFLAG_ADDR@l
|
||||
lwz r7, 0(r7)
|
||||
|
||||
_check_debug_flag:
|
||||
li r5, 0
|
||||
cmplwi r7, 2
|
||||
beq _goto_inittrk
|
||||
cmplwi r7, 3
|
||||
bne _goto_main
|
||||
li r5, 1
|
||||
|
||||
_goto_inittrk:
|
||||
lis r6, InitMetroTRK@ha
|
||||
addi r6, r6, InitMetroTRK@l
|
||||
mtlr r6
|
||||
blrl
|
||||
|
||||
_goto_main:
|
||||
lis r6, BOOTINFO2_ADDR@ha
|
||||
addi r6, r6, BOOTINFO2_ADDR@l
|
||||
lwz r5, 0(r6)
|
||||
cmplwi r5, 0
|
||||
beq+ _no_args
|
||||
lwz r6, 8(r5)
|
||||
cmplwi r6, 0
|
||||
beq+ _no_args
|
||||
add r6, r5, r6
|
||||
lwz r14, 0(r6)
|
||||
cmplwi r14, 0
|
||||
beq _no_args
|
||||
addi r15, r6, 4
|
||||
mtctr r14
|
||||
|
||||
_loop:
|
||||
addi r6, r6, 4
|
||||
lwz r7, 0(r6)
|
||||
add r7, r7, r5
|
||||
stw r7, 0(r6)
|
||||
bdnz _loop
|
||||
lis r5, ARENAHI_ADDR@ha
|
||||
addi r5, r5, ARENAHI_ADDR@l
|
||||
rlwinm r7, r15, 0, 0, 0x1a
|
||||
stw r7, 0(r5)
|
||||
b _end_of_parseargs
|
||||
|
||||
_no_args:
|
||||
li r14, 0
|
||||
li r15, 0
|
||||
|
||||
_end_of_parseargs:
|
||||
bl DBInit
|
||||
bl OSInit
|
||||
lis r4, DVD_DEVICECODE_ADDR@ha
|
||||
addi r4, r4, DVD_DEVICECODE_ADDR@l
|
||||
lhz r3, 0(r4)
|
||||
andi. r5, r3, 0x8000
|
||||
beq _check_pad3
|
||||
andi. r3, r3, 0x7fff
|
||||
cmplwi r3, 1
|
||||
bne _goto_skip_init_bba
|
||||
|
||||
_check_pad3:
|
||||
bl __check_pad3
|
||||
|
||||
_goto_skip_init_bba:
|
||||
bl __init_user
|
||||
mr r3, r14
|
||||
mr r4, r15
|
||||
bl main
|
||||
b exit
|
||||
}
|
||||
|
||||
asm static void __init_registers(void)
|
||||
{
|
||||
nofralloc
|
||||
lis r1, _stack_addr@h
|
||||
ori r1, r1, _stack_addr@l
|
||||
lis r2, _SDA2_BASE_@h
|
||||
ori r2, r2, _SDA2_BASE_@l
|
||||
lis r13, _SDA_BASE_@h
|
||||
ori r13, r13, _SDA_BASE_@l
|
||||
blr
|
||||
}
|
||||
|
||||
__declspec(section ".init") extern __rom_copy_info _rom_copy_info[];
|
||||
__declspec(section ".init") extern __bss_init_info _bss_init_info[];
|
||||
|
||||
// clang-format on
|
||||
|
||||
inline static void __copy_rom_section(void* dst, const void* src, unsigned long size)
|
||||
{
|
||||
if (size && (dst != src)) {
|
||||
memcpy(dst, src, size);
|
||||
__flush_cache(dst, size);
|
||||
}
|
||||
}
|
||||
|
||||
inline static void __init_bss_section(void* dst, unsigned long size)
|
||||
{
|
||||
if (size) {
|
||||
memset(dst, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma scheduling off
|
||||
void __init_data(void)
|
||||
{
|
||||
__rom_copy_info* dci;
|
||||
__bss_init_info* bii;
|
||||
|
||||
dci = _rom_copy_info;
|
||||
while (TRUE) {
|
||||
if (dci->size == 0)
|
||||
break;
|
||||
__copy_rom_section(dci->addr, dci->rom, dci->size);
|
||||
dci++;
|
||||
}
|
||||
|
||||
bii = _bss_init_info;
|
||||
while (TRUE) {
|
||||
if (bii->size == 0)
|
||||
break;
|
||||
__init_bss_section(bii->addr, bii->size);
|
||||
bii++;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
CC := gcc
|
||||
CFLAGS := -O3 -Wall -s
|
||||
|
||||
default: all
|
||||
|
||||
all: elf2dol elf2rel
|
||||
|
||||
elf2dol: elf2dol.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
elf2rel: elf2rel.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) elf2dol
|
||||
$(RM) elf2rel
|
|
@ -0,0 +1,120 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# Usage: dump_common_data.py file.s
|
||||
# Dumps all incbin data and prints the revised file to stdout.
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Reads a bytearray from baserom.dol
|
||||
def read_baserom(start, size):
|
||||
with open('baserom.dol', 'rb') as f:
|
||||
f.seek(start, os.SEEK_SET)
|
||||
return bytearray(f.read(size))
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print('Usage: %s ASM_FILE' % sys.argv[0])
|
||||
exit()
|
||||
|
||||
# reads a 32-bit big endian value starting at pos
|
||||
def read_u32(data, pos):
|
||||
return (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | (data[pos+3])
|
||||
|
||||
def is_ascii(code):
|
||||
if code >= 0x20 and code <= 0x7E: # normal characters
|
||||
return True
|
||||
if code in [0x09, 0x0A]: # tab, newline
|
||||
return True
|
||||
return False
|
||||
|
||||
# reads a string starting at pos
|
||||
def read_string(data, pos):
|
||||
text = ''
|
||||
while pos < len(data) and is_ascii(data[pos]):
|
||||
text += chr(data[pos])
|
||||
pos += 1
|
||||
if pos < len(data) and data[pos] == 0:
|
||||
return text
|
||||
return ''
|
||||
|
||||
# escapes special characters in the string for use in a C string literal
|
||||
def escape_string(text):
|
||||
return text.replace('\\','\\\\').replace('"','\\"').replace('\n','\\n').replace('\t','\\t')
|
||||
|
||||
# returns True if value is 4-byte aligned
|
||||
def is_aligned(num):
|
||||
return num % 4 == 0
|
||||
|
||||
# returns True if value is a possible pointer
|
||||
def is_pointer(num):
|
||||
return num >= 0x80003100 and num <= 0x802F6C80
|
||||
|
||||
# returns True if all elements are zero
|
||||
def is_all_zero(arr):
|
||||
for val in arr:
|
||||
if val != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# returns string of comma-separated hex bytes
|
||||
def hex_bytes(data):
|
||||
return ', '.join('0x%02X' % n for n in data)
|
||||
|
||||
def convert_data(data, offset):
|
||||
text = ''
|
||||
size = len(data)
|
||||
pos = 0
|
||||
while pos < size:
|
||||
# pad unaligned
|
||||
pad = []
|
||||
while not is_aligned(offset + pos) and pos < size:
|
||||
pad.append(data[pos])
|
||||
pos += 1
|
||||
if len(pad) > 0:
|
||||
if is_all_zero(pad):
|
||||
text += '\t.balign 4\n'
|
||||
else:
|
||||
text += '\t.byte %s\n' % hex_bytes(pad)
|
||||
|
||||
# string?
|
||||
string = read_string(data, pos)
|
||||
if len(string) > 3:
|
||||
text += '\t.asciz "%s"\n' % escape_string(string)
|
||||
pos += len(string) + 1
|
||||
continue
|
||||
|
||||
assert(is_aligned(offset + pos))
|
||||
|
||||
if pos + 4 <= size:
|
||||
val = read_u32(data, pos)
|
||||
if is_pointer(val):
|
||||
text += '\t.4byte 0x%08X ;# ptr\n' % val
|
||||
elif val == 0:
|
||||
text += '\t.4byte 0\n'
|
||||
else:
|
||||
text += '\t.4byte 0x%08X\n' % val
|
||||
pos += 4
|
||||
return text
|
||||
|
||||
currSection = ''
|
||||
|
||||
with open(sys.argv[1], 'rt') as f:
|
||||
for line in f.readlines():
|
||||
line = line.rstrip()
|
||||
# Section directive
|
||||
m = re.match(r'\s*\.section\s+([\._A-Za-z0-9]+)', line)
|
||||
if m:
|
||||
currSection = m.groups()[0]
|
||||
elif currSection in ['.rodata', '.data', '.sdata', '.sdata2', '.ctors', '.dtors', 'extab_', 'extabindex_']:
|
||||
# Incbin directive
|
||||
m = re.match(r'\s*\.incbin\s+"baserom.dol"\s*,\s*([^,]+),\s*([^,]+)', line)
|
||||
if m:
|
||||
g = m.groups()
|
||||
start = int(g[0], 0)
|
||||
size = int(g[1], 0)
|
||||
data = read_baserom(start, size)
|
||||
print('\t# ROM: 0x%X' % start)
|
||||
print(convert_data(data, start))
|
||||
continue
|
||||
print(line)
|
|
@ -0,0 +1,497 @@
|
|||
#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 ARRAY_COUNT(arr) (sizeof(arr)/sizeof((arr)[0]))
|
||||
|
||||
#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 swap16(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 swap16(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;
|
||||
|
||||
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 curr_start = swap32(map->header.bss_addr);
|
||||
uint32_t curr_size = swap32(map->header.bss_size);
|
||||
if (paddr < curr_start)
|
||||
map->header.bss_addr = swap32(paddr);
|
||||
// Total BSS size should be the end of the last bss section minus the
|
||||
// start of the first bss section.
|
||||
if (paddr + memsz > curr_start + curr_size)
|
||||
map->header.bss_size = swap32(paddr + memsz - curr_start);
|
||||
} else {
|
||||
map->header.bss_addr = swap32(paddr);
|
||||
map->header.bss_size = swap32(memsz);
|
||||
map->flags |= HAVE_BSS;
|
||||
}
|
||||
}
|
||||
|
||||
void read_elf_segments(DOL_map *map, const char *elf)
|
||||
{
|
||||
int read, i;
|
||||
Elf32_Ehdr ehdr;
|
||||
|
||||
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(swap16(ehdr.e_type) != ET_EXEC)
|
||||
die("ELF is not an executable");
|
||||
if(swap16(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 = swap16(ehdr.e_phnum);
|
||||
uint32_t phoff = swap32(ehdr.e_phoff);
|
||||
Elf32_Phdr *phdrs;
|
||||
|
||||
if(!phnum || !phoff)
|
||||
die("ELF has no program headers");
|
||||
|
||||
if(swap16(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_vaddr);
|
||||
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);
|
||||
} 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");
|
||||
}
|
||||
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));
|
||||
}
|
||||
}
|
||||
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 fpad(FILE *dst, uint32_t dst_off, uint32_t size)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
if(fseek(dst, dst_off, SEEK_SET) < 0)
|
||||
ferrordie(dst, "writing DOL segment data");
|
||||
for(i=0; i<size; i++)
|
||||
fputc(0, dst);
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
// Write DOL header with aligned text and data section sizes
|
||||
DOL_hdr aligned_header = map->header;
|
||||
for(i=0; i<ARRAY_COUNT(aligned_header.text_size); i++)
|
||||
aligned_header.text_size[i] = swap32(DOL_ALIGN(swap32(aligned_header.text_size[i])));
|
||||
for(i=0; i<ARRAY_COUNT(aligned_header.data_size); i++)
|
||||
aligned_header.data_size[i] = swap32(DOL_ALIGN(swap32(aligned_header.data_size[i])));
|
||||
written = fwrite(&aligned_header, sizeof(DOL_hdr), 1, dolf);
|
||||
if(written != 1)
|
||||
ferrordie(dolf, "writing DOL header");
|
||||
|
||||
for(i=0; i<map->text_cnt; i++) {
|
||||
uint32_t size = swap32(map->header.text_size[i]);
|
||||
uint32_t padded_size = DOL_ALIGN(size);
|
||||
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], size);
|
||||
if (padded_size > size)
|
||||
fpad(dolf, swap32(map->header.text_off[i]) + size, padded_size - size);
|
||||
}
|
||||
for(i=0; i<map->data_cnt; i++) {
|
||||
uint32_t size = swap32(map->header.data_size[i]);
|
||||
uint32_t padded_size = DOL_ALIGN(size);
|
||||
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], size);
|
||||
if (padded_size > size)
|
||||
fpad(dolf, swap32(map->header.data_off[i]) + size, padded_size - size);
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
DOL_map map;
|
||||
|
||||
memset(&map, 0, sizeof(map));
|
||||
|
||||
read_elf_segments(&map, elf_file);
|
||||
map_dol(&map);
|
||||
write_dol(&map, dol_file);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,866 @@
|
|||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifndef MAX
|
||||
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define ARRAY_COUNT(arr) (sizeof(arr)/sizeof((arr)[0]))
|
||||
|
||||
#define IS_LITTLE_ENDIAN (((char *)((uint32_t[]){1}))[0] == 1)
|
||||
|
||||
// Relevant portions of elf.h
|
||||
|
||||
typedef uint32_t Elf32_Addr;
|
||||
typedef uint32_t Elf32_Off;
|
||||
typedef uint32_t Elf32_Word;
|
||||
typedef int32_t Elf32_Sword;
|
||||
typedef uint16_t Elf32_Half;
|
||||
typedef uint16_t Elf32_Section;
|
||||
|
||||
#define EI_NIDENT (16)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
|
||||
Elf32_Half e_type; /* Object file type */
|
||||
Elf32_Half e_machine; /* Architecture */
|
||||
Elf32_Word e_version; /* Object file version */
|
||||
Elf32_Addr e_entry; /* Entry point virtual address */
|
||||
Elf32_Off e_phoff; /* Program header table file offset */
|
||||
Elf32_Off e_shoff; /* Section header table file offset */
|
||||
Elf32_Word e_flags; /* Processor-specific flags */
|
||||
Elf32_Half e_ehsize; /* ELF header size in bytes */
|
||||
Elf32_Half e_phentsize; /* Program header table entry size */
|
||||
Elf32_Half e_phnum; /* Program header table entry count */
|
||||
Elf32_Half e_shentsize; /* Section header table entry size */
|
||||
Elf32_Half e_shnum; /* Section header table entry count */
|
||||
Elf32_Half e_shstrndx; /* Section header string table index */
|
||||
} Elf32_Ehdr;
|
||||
|
||||
#define EI_CLASS 4 /* File class byte index */
|
||||
#define ELFCLASS32 1 /* 32-bit objects */
|
||||
|
||||
#define EI_DATA 5 /* Data encoding byte index */
|
||||
#define ELFDATA2MSB 2 /* 2's complement, big endian */
|
||||
|
||||
#define EM_PPC 20 /* PowerPC */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Elf32_Word sh_name; /* Section name (string tbl index) */
|
||||
Elf32_Word sh_type; /* Section type */
|
||||
Elf32_Word sh_flags; /* Section flags */
|
||||
Elf32_Addr sh_addr; /* Section virtual addr at execution */
|
||||
Elf32_Off sh_offset; /* Section file offset */
|
||||
Elf32_Word sh_size; /* Section size in bytes */
|
||||
Elf32_Word sh_link; /* Link to another section */
|
||||
Elf32_Word sh_info; /* Additional section information */
|
||||
Elf32_Word sh_addralign; /* Section alignment */
|
||||
Elf32_Word sh_entsize; /* Entry size if section holds table */
|
||||
} Elf32_Shdr;
|
||||
|
||||
#define SHT_PROGBITS 1 /* Program data */
|
||||
#define SHT_SYMTAB 2 /* Symbol table */
|
||||
#define SHT_STRTAB 3 /* String table */
|
||||
#define SHT_RELA 4 /* Relocation entries with addends */
|
||||
#define SHT_NOBITS 8 /* Program space with no data (bss) */
|
||||
|
||||
#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
|
||||
#define SHF_EXECINSTR (1 << 2) /* Executable */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Elf32_Word st_name; /* Symbol name (string tbl index) */
|
||||
Elf32_Addr st_value; /* Symbol value */
|
||||
Elf32_Word st_size; /* Symbol size */
|
||||
unsigned char st_info; /* Symbol type and binding */
|
||||
unsigned char st_other; /* Symbol visibility */
|
||||
Elf32_Section st_shndx; /* Section index */
|
||||
} Elf32_Sym;
|
||||
|
||||
#define ELF32_ST_TYPE(val) ((val) & 0xf)
|
||||
|
||||
/* Legal values for ST_TYPE subfield of st_info (symbol type). */
|
||||
|
||||
#define STT_NOTYPE 0 /* Symbol type is unspecified */
|
||||
#define STT_FUNC 2 /* Symbol is a code object */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
Elf32_Addr r_offset; /* Address */
|
||||
Elf32_Word r_info; /* Relocation type and symbol index */
|
||||
Elf32_Sword r_addend; /* Addend */
|
||||
} Elf32_Rela;
|
||||
|
||||
/* How to extract and insert information held in the r_info field. */
|
||||
|
||||
#define ELF32_R_SYM(val) ((val) >> 8)
|
||||
#define ELF32_R_TYPE(val) ((val) & 0xff)
|
||||
|
||||
#define R_PPC_NONE 0
|
||||
#define R_PPC_ADDR32 1 /* 32bit absolute address */
|
||||
#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
|
||||
#define R_PPC_ADDR16 3 /* 16bit absolute address */
|
||||
#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
|
||||
#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
|
||||
#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
|
||||
#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
|
||||
#define R_PPC_ADDR14_BRTAKEN 8
|
||||
#define R_PPC_ADDR14_BRNTAKEN 9
|
||||
#define R_PPC_REL24 10 /* PC relative 26 bit */
|
||||
#define R_PPC_REL14 11 /* PC relative 16 bit */
|
||||
|
||||
#define R_DOLPHIN_SECTION 202
|
||||
#define R_DOLPHIN_END 203
|
||||
|
||||
// end elf.h
|
||||
|
||||
struct RelHeader
|
||||
{
|
||||
uint32_t moduleId; // unique module identifier
|
||||
uint32_t nextModule; // always 0; filled in at runtime
|
||||
uint32_t prevModule; // always 0; filled in at runtime
|
||||
uint32_t sectionCount; // number of sections in the section table
|
||||
uint32_t sectionTableOffset; // file position of section table
|
||||
uint32_t moduleNameOffset; // offset of the module name in the string table (not in this file)
|
||||
uint32_t moduleNameSize; // size of the module name in the string table (not in this file)
|
||||
uint32_t formatVersion; // REL format version
|
||||
uint32_t bssSize; // size of the BSS section
|
||||
uint32_t relocationTableOffset; // file position of relocation entries
|
||||
uint32_t importTableOffset; // file position of import table
|
||||
uint32_t importTableSize; // size of import table
|
||||
uint8_t prologSection; // section in which the _prolog function is in, or 0 if absent
|
||||
uint8_t epilogSection; // section in which the _epilog function is in, or 0 if absent
|
||||
uint8_t unresolvedSection; // section in which the _unresolved function is in, or 0 if absent
|
||||
uint8_t pad33;
|
||||
uint32_t prologOffset; // offset of the _prolog function in its section
|
||||
uint32_t epilogOffset; // offset of the _epilog function in its section
|
||||
uint32_t unresolvedOffset; // offset of the _unresolved function in its section
|
||||
};
|
||||
|
||||
struct RelRelocEntry
|
||||
{
|
||||
int type; // relocation type
|
||||
int patchSection; // section which relocation patch applies to
|
||||
uint32_t patchOffset; // offset where this relocation patch applies
|
||||
int symbolSection; // section of symbol
|
||||
uint32_t symbolAddr; // for dol symbols, absolute address. for rel symbols, section offset
|
||||
};
|
||||
|
||||
struct RelImportEntry
|
||||
{
|
||||
int moduleId; // ID of module from which the relocation symbols are imported from
|
||||
struct RelRelocEntry *relocs; // relocation entries
|
||||
int relocsCount; // number of relocation entries
|
||||
size_t relocsOffset; // file offset to relocation entries
|
||||
};
|
||||
|
||||
struct Module
|
||||
{
|
||||
int moduleId; // unique identifier of the module; the id of the DOL is always 0
|
||||
const char *filename; // name of the module's ELF file
|
||||
FILE *file; // ELF file
|
||||
Elf32_Ehdr ehdr; // ELF header
|
||||
Elf32_Sym *symtab; // ELF symbol table entries
|
||||
int symtabCount; // number of ELF symbol table entries
|
||||
char *strtab; // ELF string table
|
||||
size_t strtabSize; // size of ELF string table
|
||||
};
|
||||
|
||||
static struct Module dolModule;
|
||||
static struct Module relModule;
|
||||
static struct RelImportEntry *imports;
|
||||
static int importsCount = 0;
|
||||
static int minSectionCount = 0;
|
||||
static int undefinedSymError = 0;
|
||||
|
||||
static void fatal_error(const char *msg, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
va_end(args);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Swaps byte order if the system is little endian
|
||||
|
||||
static void bswap32(uint32_t *ptr)
|
||||
{
|
||||
if (IS_LITTLE_ENDIAN)
|
||||
*ptr = (((*ptr >> 24) & 0xFF) << 0)
|
||||
| (((*ptr >> 16) & 0xFF) << 8)
|
||||
| (((*ptr >> 8) & 0xFF) << 16)
|
||||
| (((*ptr >> 0) & 0xFF) << 24);
|
||||
}
|
||||
|
||||
static void bswap16(uint16_t *ptr)
|
||||
{
|
||||
if (IS_LITTLE_ENDIAN)
|
||||
*ptr = (((*ptr >> 8) & 0xFF) << 0)
|
||||
| (((*ptr >> 0) & 0xFF) << 8);
|
||||
}
|
||||
|
||||
static int read_checked(FILE *f, size_t offset, void *out, size_t size)
|
||||
{
|
||||
return fseek(f, offset, SEEK_SET) == 0
|
||||
&& fread(out, size, 1, f) == 1;
|
||||
}
|
||||
|
||||
static int write_checked(FILE *f, size_t offset, const void *in, size_t size)
|
||||
{
|
||||
return fseek(f, offset, SEEK_SET) == 0
|
||||
&& fwrite(in, size, 1, f) == 1;
|
||||
}
|
||||
|
||||
static uint32_t align(uint32_t n, unsigned int alignment)
|
||||
{
|
||||
if (alignment == 0 || n % alignment == 0)
|
||||
return n;
|
||||
return n + alignment - (n % alignment);
|
||||
}
|
||||
|
||||
static int is_supported_reloc_type(int type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case R_PPC_ADDR32:
|
||||
case R_PPC_ADDR24:
|
||||
case R_PPC_ADDR16_LO:
|
||||
case R_PPC_ADDR16_HA:
|
||||
case R_PPC_REL24:
|
||||
case R_PPC_REL14:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *symbol_name(const struct Module *module, const Elf32_Sym *sym)
|
||||
{
|
||||
if (sym->st_name >= module->strtabSize)
|
||||
return NULL;
|
||||
return module->strtab + sym->st_name;
|
||||
}
|
||||
|
||||
static int get_symtab_entry(const struct Module *module, int index, Elf32_Sym *sym)
|
||||
{
|
||||
if (index >= module->symtabCount)
|
||||
return 0;
|
||||
*sym = module->symtab[index];
|
||||
bswap32(&sym->st_name);
|
||||
bswap32(&sym->st_value);
|
||||
bswap32(&sym->st_size);
|
||||
bswap16(&sym->st_shndx);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lookup_symbol_by_name(const struct Module *module, const char *name, Elf32_Sym *sym)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < module->symtabCount; i++)
|
||||
{
|
||||
get_symtab_entry(module, i, sym);
|
||||
const char *n = symbol_name(module, sym);
|
||||
if (n != NULL && strcmp(name, n) == 0)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct RelImportEntry *get_import_for_module_id(int moduleId)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < importsCount; i++)
|
||||
{
|
||||
if (imports[i].moduleId == moduleId)
|
||||
return &imports[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void add_reloc_entry(const struct Module *module, const Elf32_Rela *reloc, int relocSection)
|
||||
{
|
||||
Elf32_Sym sym;
|
||||
int symIdx = ELF32_R_SYM(reloc->r_info);
|
||||
int relocType = ELF32_R_TYPE(reloc->r_info);
|
||||
struct RelRelocEntry rentry;
|
||||
struct RelImportEntry *import;
|
||||
int moduleId; // module containing the symbol
|
||||
|
||||
if (!is_supported_reloc_type(relocType))
|
||||
fatal_error("relocation type %i not supported\n", relocType);
|
||||
|
||||
rentry.patchSection = relocSection;
|
||||
rentry.patchOffset = reloc->r_offset;
|
||||
rentry.type = relocType;
|
||||
|
||||
// look for symbol in this module
|
||||
if (!get_symtab_entry(module, symIdx, &sym))
|
||||
fatal_error("couldn't find symbol index %i\n", symIdx);
|
||||
if (sym.st_shndx != 0) // symbol is in this module
|
||||
{
|
||||
rentry.symbolSection = sym.st_shndx;
|
||||
rentry.symbolAddr = sym.st_value + reloc->r_addend;
|
||||
moduleId = module->moduleId;
|
||||
}
|
||||
else // symbol is in another module (the DOL)
|
||||
{
|
||||
const char *name = symbol_name(&relModule, &sym);
|
||||
if (!lookup_symbol_by_name(&dolModule, name, &sym))
|
||||
{
|
||||
undefinedSymError = 1;
|
||||
fprintf(stderr, "could not find symbol '%s' in any module\n", name);
|
||||
return;
|
||||
}
|
||||
if (sym.st_shndx >= dolModule.ehdr.e_shnum)
|
||||
fatal_error("bad section index %i\n", sym.st_shndx);
|
||||
|
||||
rentry.symbolSection = sym.st_shndx;
|
||||
rentry.symbolAddr = sym.st_value + reloc->r_addend;
|
||||
moduleId = dolModule.moduleId;
|
||||
}
|
||||
|
||||
import = get_import_for_module_id(moduleId);
|
||||
// add import entry if it does not exist.
|
||||
if (import == NULL)
|
||||
{
|
||||
imports = realloc(imports, (importsCount + 1) * sizeof(*imports));
|
||||
import = &imports[importsCount++];
|
||||
import->moduleId = moduleId;
|
||||
import->relocs = NULL;
|
||||
import->relocsCount = 0;
|
||||
}
|
||||
|
||||
// add relocation entry
|
||||
import->relocs = realloc(import->relocs, (import->relocsCount + 1) * sizeof(*import->relocs));
|
||||
import->relocs[import->relocsCount++] = rentry;
|
||||
}
|
||||
|
||||
static void module_get_section_header(const struct Module *module, int secNum, Elf32_Shdr *shdr)
|
||||
{
|
||||
size_t offset = module->ehdr.e_shoff + secNum * module->ehdr.e_shentsize;
|
||||
|
||||
if (secNum >= module->ehdr.e_shnum)
|
||||
fatal_error("no such section index %i\n", secNum);
|
||||
if (!read_checked(module->file, offset, shdr, sizeof(*shdr)))
|
||||
fatal_error("error reading section header\n");
|
||||
|
||||
// convert from big endian
|
||||
bswap32(&shdr->sh_name);
|
||||
bswap32(&shdr->sh_type);
|
||||
bswap32(&shdr->sh_flags);
|
||||
bswap32(&shdr->sh_addr);
|
||||
bswap32(&shdr->sh_offset);
|
||||
bswap32(&shdr->sh_size);
|
||||
bswap32(&shdr->sh_link);
|
||||
bswap32(&shdr->sh_info);
|
||||
bswap32(&shdr->sh_addralign);
|
||||
bswap32(&shdr->sh_entsize);
|
||||
}
|
||||
|
||||
static void module_read_relocs(struct Module *module)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
undefinedSymError = 0;
|
||||
|
||||
for (i = 0; i < (int)module->ehdr.e_shnum; i++)
|
||||
{
|
||||
Elf32_Shdr shdr;
|
||||
Elf32_Shdr forSection;
|
||||
|
||||
module_get_section_header(module, i, &shdr);
|
||||
if (shdr.sh_type == SHT_RELA)
|
||||
{
|
||||
module_get_section_header(module, shdr.sh_info, &forSection);
|
||||
if (!(forSection.sh_flags & SHF_ALLOC))
|
||||
continue;
|
||||
for (j = 0; j < shdr.sh_size / sizeof(Elf32_Rela); j++)
|
||||
{
|
||||
Elf32_Rela reloc;
|
||||
|
||||
read_checked(module->file, shdr.sh_offset + j * sizeof(Elf32_Rela), &reloc, sizeof(reloc));
|
||||
// convert from big endian
|
||||
bswap32(&reloc.r_offset);
|
||||
bswap32(&reloc.r_info);
|
||||
bswap32((uint32_t *)&reloc.r_addend);
|
||||
|
||||
add_reloc_entry(&relModule, &reloc, shdr.sh_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (undefinedSymError)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int open_module(struct Module *module)
|
||||
{
|
||||
int i;
|
||||
|
||||
// open file
|
||||
module->file = fopen(module->filename, "rb");
|
||||
if (module->file == NULL)
|
||||
fatal_error("could not open %s for reading: %s\n", module->filename, strerror(errno));
|
||||
|
||||
// read and verify ELF header
|
||||
if (!read_checked(module->file, 0, &module->ehdr, sizeof(module->ehdr)))
|
||||
fatal_error("error reading ELF header\n");
|
||||
if (memcmp(module->ehdr.e_ident, "\x7F""ELF", 4) != 0)
|
||||
fatal_error("%s is not a valid ELF file\n", module->filename);
|
||||
|
||||
// convert from big endian
|
||||
bswap16(&module->ehdr.e_type);
|
||||
bswap16(&module->ehdr.e_machine);
|
||||
bswap32(&module->ehdr.e_version);
|
||||
bswap32(&module->ehdr.e_entry);
|
||||
bswap32(&module->ehdr.e_phoff);
|
||||
bswap32(&module->ehdr.e_shoff);
|
||||
bswap32(&module->ehdr.e_flags);
|
||||
bswap16(&module->ehdr.e_ehsize);
|
||||
bswap16(&module->ehdr.e_phentsize);
|
||||
bswap16(&module->ehdr.e_phnum);
|
||||
bswap16(&module->ehdr.e_shentsize);
|
||||
bswap16(&module->ehdr.e_shnum);
|
||||
bswap16(&module->ehdr.e_shstrndx);
|
||||
|
||||
if (module->ehdr.e_shentsize < sizeof(Elf32_Shdr))
|
||||
fatal_error("invalid section header size");
|
||||
|
||||
// Verify architecture
|
||||
if (module->ehdr.e_ident[EI_CLASS] != ELFCLASS32
|
||||
|| module->ehdr.e_ident[EI_DATA] != ELFDATA2MSB
|
||||
|| module->ehdr.e_machine != EM_PPC)
|
||||
fatal_error("%s: wrong architecture. expected PowerPC 32-bit big endian.\n", module->filename);
|
||||
|
||||
// Read symbol table and string table
|
||||
for (i = 0; i < (int)module->ehdr.e_shnum; i++)
|
||||
{
|
||||
Elf32_Shdr shdr;
|
||||
|
||||
module_get_section_header(module, i, &shdr);
|
||||
if (shdr.sh_type == SHT_SYMTAB && module->symtab == NULL)
|
||||
{
|
||||
module->symtabCount = shdr.sh_size / sizeof(Elf32_Sym);
|
||||
module->symtab = malloc(shdr.sh_size);
|
||||
if (!read_checked(module->file, shdr.sh_offset, module->symtab, shdr.sh_size))
|
||||
fatal_error("error reading symbol table\n");
|
||||
}
|
||||
else if (shdr.sh_type == SHT_STRTAB && i != module->ehdr.e_shstrndx && module->strtab == NULL)
|
||||
{
|
||||
module->strtabSize = shdr.sh_size;
|
||||
module->strtab = malloc(shdr.sh_size);
|
||||
if (!read_checked(module->file, shdr.sh_offset, module->strtab, shdr.sh_size))
|
||||
fatal_error("error reading string table\n");
|
||||
}
|
||||
}
|
||||
if (module->symtab == NULL)
|
||||
fatal_error("%s does not have a symbol table.\n", module->filename);
|
||||
if (module->strtab == NULL)
|
||||
fatal_error("%s does not have a string table.\n", module->filename);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// searches for the special functions "_prolog", "_epliog", and "_unresolved"
|
||||
static void find_rel_entry_functions(const struct Module *module, struct RelHeader *relHdr)
|
||||
{
|
||||
int i;
|
||||
|
||||
//puts("finding entry points");
|
||||
for (i = 0; i < module->symtabCount; i++)
|
||||
{
|
||||
Elf32_Sym sym;
|
||||
Elf32_Shdr shdr;
|
||||
const char *name;
|
||||
|
||||
get_symtab_entry(module, i, &sym);
|
||||
name = symbol_name(module, &sym);
|
||||
if (name == NULL)
|
||||
continue;
|
||||
if (strcmp(name, "_prolog") == 0)
|
||||
{
|
||||
module_get_section_header(module, sym.st_shndx, &shdr);
|
||||
relHdr->prologSection = sym.st_shndx;
|
||||
relHdr->prologOffset = sym.st_value - shdr.sh_addr;
|
||||
}
|
||||
else if (strcmp(name, "_epilog") == 0)
|
||||
{
|
||||
module_get_section_header(module, sym.st_shndx, &shdr);
|
||||
relHdr->epilogSection = sym.st_shndx;
|
||||
relHdr->epilogOffset = sym.st_value - shdr.sh_addr;
|
||||
}
|
||||
else if (strcmp(name, "_unresolved") == 0)
|
||||
{
|
||||
module_get_section_header(module, sym.st_shndx, &shdr);
|
||||
relHdr->unresolvedSection = sym.st_shndx;
|
||||
relHdr->unresolvedOffset = sym.st_value - shdr.sh_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// patches the bl instruction at insnp to jump to offset
|
||||
static void patch_rel24_branch_offset(uint8_t *insnp, int32_t branchOffset)
|
||||
{
|
||||
const uint32_t offsetMask = 0x03FFFFFC; // bits of instruction that contain the offset
|
||||
uint32_t insn = (insnp[0] << 24) // read instruction
|
||||
| (insnp[1] << 16)
|
||||
| (insnp[2] << 8)
|
||||
| (insnp[3] << 0);
|
||||
|
||||
assert(((insn >> 26) & 0x3F) == 18); // TODO: do other instructions besides bl use R_PPC_REL24?
|
||||
insn = (insn & ~offsetMask) | (branchOffset & offsetMask); // patch instruction
|
||||
// write instruction
|
||||
insnp[0] = (insn >> 24) & 0xFF;
|
||||
insnp[1] = (insn >> 16) & 0xFF;
|
||||
insnp[2] = (insn >> 8) & 0xFF;
|
||||
insnp[3] = (insn >> 0) & 0xFF;
|
||||
}
|
||||
|
||||
static void patch_code_relocs(struct RelHeader *relHdr, int sectionId, uint8_t *code, size_t size)
|
||||
{
|
||||
struct RelImportEntry *import;
|
||||
struct RelRelocEntry *reloc;
|
||||
int i;
|
||||
|
||||
// Remove redundant R_PPC_REL24 relocations for calls to functions within
|
||||
// the same module
|
||||
import = get_import_for_module_id(relModule.moduleId);
|
||||
assert(import != NULL);
|
||||
for (i = 0, reloc = &import->relocs[0]; i < import->relocsCount; i++, reloc++)
|
||||
{
|
||||
if (reloc->patchSection == sectionId && reloc->type == R_PPC_REL24)
|
||||
{
|
||||
assert(reloc->patchOffset < size);
|
||||
patch_rel24_branch_offset(
|
||||
code + reloc->patchOffset,
|
||||
reloc->symbolAddr - reloc->patchOffset);
|
||||
// remove the relocation
|
||||
reloc->type = R_PPC_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
// Patch all calls to functions outside this module to jump to _unresolved
|
||||
// by default.
|
||||
if (relHdr->unresolvedSection == 0)
|
||||
return;
|
||||
import = get_import_for_module_id(0);
|
||||
assert(import != NULL);
|
||||
for (i = 0, reloc = &import->relocs[0]; i < import->relocsCount; i++, reloc++)
|
||||
{
|
||||
if (reloc->patchSection == sectionId && reloc->type == R_PPC_REL24)
|
||||
{
|
||||
assert(reloc->patchOffset < size);
|
||||
patch_rel24_branch_offset(
|
||||
code + reloc->patchOffset,
|
||||
relHdr->unresolvedOffset - reloc->patchOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int compare_relocs(const void *a, const void *b)
|
||||
{
|
||||
const struct RelRelocEntry *relocA = a;
|
||||
const struct RelRelocEntry *relocB = b;
|
||||
|
||||
// Sort by sections to which these relocations apply
|
||||
if (relocA->patchSection != relocB->patchSection)
|
||||
return relocA->patchSection - relocB->patchSection;
|
||||
// Sort by patch offset
|
||||
if (relocA->patchOffset != relocB->patchOffset)
|
||||
return relocA->patchOffset - relocB->patchOffset;
|
||||
// Otherwise, leave the order alone
|
||||
return (uintptr_t)a - (uintptr_t)b;
|
||||
}
|
||||
|
||||
static int compare_imports(const void *a, const void *b)
|
||||
{
|
||||
const struct RelImportEntry *impA = a;
|
||||
const struct RelImportEntry *impB = b;
|
||||
|
||||
return impA->moduleId - impB->moduleId;
|
||||
}
|
||||
|
||||
static void write_rel_file(struct Module *module, struct RelHeader *relHdr, const char *filename)
|
||||
{
|
||||
int i, j;
|
||||
size_t filePos = sizeof(struct RelHeader); // skip over header for now
|
||||
struct RelImportEntry *imp;
|
||||
FILE *fout = fopen(filename, "wb");
|
||||
|
||||
if (fout == NULL)
|
||||
fatal_error("could not open %s for writing: %s\n", filename, strerror(errno));
|
||||
|
||||
relHdr->moduleId = module->moduleId;
|
||||
relHdr->formatVersion = 1;
|
||||
|
||||
find_rel_entry_functions(module, relHdr);
|
||||
|
||||
// 1. Write section table and section contents
|
||||
|
||||
relHdr->sectionTableOffset = filePos;
|
||||
relHdr->sectionCount = MAX(module->ehdr.e_shnum, minSectionCount);
|
||||
// section contents follow section info table
|
||||
filePos = relHdr->sectionTableOffset + relHdr->sectionCount * 8;
|
||||
relHdr->bssSize = 0;
|
||||
for (i = 0; i < module->ehdr.e_shnum; i++)
|
||||
{
|
||||
Elf32_Shdr shdr;
|
||||
struct { uint32_t offset; uint32_t size; } secEntry = {0};
|
||||
|
||||
module_get_section_header(module, i, &shdr);
|
||||
// write section contents
|
||||
if (shdr.sh_type == SHT_PROGBITS && (shdr.sh_flags & SHF_ALLOC))
|
||||
{
|
||||
size_t sizeAligned = align(shdr.sh_size, 4);
|
||||
uint32_t execflg = (shdr.sh_flags & SHF_EXECINSTR) ? 1 : 0;
|
||||
|
||||
filePos = align(filePos, shdr.sh_addralign);
|
||||
if (shdr.sh_size > 0)
|
||||
{
|
||||
uint8_t *data = calloc(sizeAligned, 1);
|
||||
|
||||
if (!read_checked(module->file, shdr.sh_offset, data, shdr.sh_size))
|
||||
fatal_error("error reading section\n");
|
||||
if (execflg)
|
||||
patch_code_relocs(relHdr, i, data, sizeAligned);
|
||||
if (!write_checked(fout, filePos, data, shdr.sh_size))
|
||||
fatal_error("error writing rel section\n");
|
||||
free(data);
|
||||
}
|
||||
secEntry.offset = filePos | execflg;
|
||||
filePos += shdr.sh_size;
|
||||
}
|
||||
if (shdr.sh_flags & SHF_ALLOC)
|
||||
secEntry.size = shdr.sh_size;
|
||||
|
||||
// write section table entry
|
||||
bswap32(&secEntry.offset);
|
||||
bswap32(&secEntry.size);
|
||||
write_checked(fout, relHdr->sectionTableOffset + i * 8, &secEntry, sizeof(secEntry));
|
||||
|
||||
// calculate total BSS size
|
||||
if ((shdr.sh_flags & SHF_ALLOC) && shdr.sh_type == SHT_NOBITS)
|
||||
relHdr->bssSize += shdr.sh_size;
|
||||
}
|
||||
|
||||
// 2. Write relocation data
|
||||
|
||||
relHdr->relocationTableOffset = filePos;
|
||||
// sort imports by module id
|
||||
qsort(imports, importsCount, sizeof(struct RelImportEntry), compare_imports);
|
||||
for (i = 0, imp = &imports[0]; i < importsCount; i++, imp++)
|
||||
{
|
||||
struct RelRelocEntry *reloc;
|
||||
int currSection = -1;
|
||||
uint32_t prevOffset = 0;
|
||||
struct
|
||||
{
|
||||
uint16_t offset;
|
||||
uint8_t type;
|
||||
uint8_t section;
|
||||
uint32_t symaddr;
|
||||
} ent;
|
||||
|
||||
imp->relocsOffset = filePos;
|
||||
// sort relocation entries by section
|
||||
qsort(imp->relocs, imp->relocsCount, sizeof(struct RelRelocEntry), compare_relocs);
|
||||
for (j = 0, reloc = &imp->relocs[0]; j < imp->relocsCount; j++, reloc++)
|
||||
{
|
||||
if (reloc->type == R_PPC_NONE) // ignore null relocations
|
||||
continue;
|
||||
|
||||
if (reloc->patchSection != currSection) // section changed
|
||||
{
|
||||
currSection = reloc->patchSection;
|
||||
|
||||
// write section change
|
||||
ent.offset = 0;
|
||||
ent.type = R_DOLPHIN_SECTION;
|
||||
ent.section = reloc->patchSection;
|
||||
ent.symaddr = 0;
|
||||
bswap16(&ent.offset);
|
||||
bswap32(&ent.symaddr);
|
||||
if (!write_checked(fout, filePos, &ent, sizeof(ent)))
|
||||
fatal_error("error writing relocation entry");
|
||||
|
||||
filePos += sizeof(ent);
|
||||
prevOffset = 0;
|
||||
}
|
||||
|
||||
// write relocation
|
||||
assert(reloc->patchOffset >= prevOffset);
|
||||
ent.offset = reloc->patchOffset - prevOffset;
|
||||
ent.type = reloc->type;
|
||||
ent.section = reloc->symbolSection;
|
||||
ent.symaddr = reloc->symbolAddr;
|
||||
bswap16(&ent.offset);
|
||||
bswap32(&ent.symaddr);
|
||||
if (!write_checked(fout, filePos, &ent, sizeof(ent)))
|
||||
fatal_error("error writing relocation entry");
|
||||
|
||||
filePos += sizeof(ent);
|
||||
prevOffset = reloc->patchOffset;
|
||||
}
|
||||
|
||||
// write end
|
||||
ent.offset = 0;
|
||||
ent.type = R_DOLPHIN_END;
|
||||
ent.section = 0;
|
||||
ent.symaddr = 0;
|
||||
bswap16(&ent.offset);
|
||||
bswap32(&ent.symaddr);
|
||||
if (!write_checked(fout, filePos, &ent, sizeof(ent)))
|
||||
fatal_error("error writing relocation entry");
|
||||
|
||||
filePos += sizeof(ent);
|
||||
}
|
||||
|
||||
// 3. Write module import table
|
||||
|
||||
relHdr->importTableOffset = filePos;
|
||||
for (i = 0, imp = &imports[0]; i < importsCount; i++, imp++)
|
||||
{
|
||||
// write import table entry
|
||||
struct { uint32_t moduleId; uint32_t relocsOffset; } ent;
|
||||
ent.moduleId = imp->moduleId;
|
||||
ent.relocsOffset = imp->relocsOffset;
|
||||
bswap32(&ent.moduleId);
|
||||
bswap32(&ent.relocsOffset);
|
||||
write_checked(fout, relHdr->importTableOffset + i * 8, &ent, sizeof(ent));
|
||||
}
|
||||
relHdr->importTableSize = importsCount * 8;
|
||||
|
||||
// 4. Write REL header.
|
||||
|
||||
// convert to big endian
|
||||
bswap32(&relHdr->moduleId);
|
||||
bswap32(&relHdr->sectionCount);
|
||||
bswap32(&relHdr->sectionTableOffset);
|
||||
bswap32(&relHdr->moduleNameOffset);
|
||||
bswap32(&relHdr->moduleNameSize);
|
||||
bswap32(&relHdr->formatVersion);
|
||||
bswap32(&relHdr->bssSize);
|
||||
bswap32(&relHdr->relocationTableOffset);
|
||||
bswap32(&relHdr->importTableOffset);
|
||||
bswap32(&relHdr->importTableSize);
|
||||
bswap32(&relHdr->prologOffset);
|
||||
bswap32(&relHdr->epilogOffset);
|
||||
bswap32(&relHdr->unresolvedOffset);
|
||||
write_checked(fout, 0, relHdr, sizeof(*relHdr));
|
||||
|
||||
fclose(fout);
|
||||
}
|
||||
|
||||
static int parse_number(const char *str, int *n)
|
||||
{
|
||||
char *end;
|
||||
*n = strtol(str, &end, 0);
|
||||
return end > str && *end == 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
int moduleId = -1;
|
||||
int nameOffset = 0;
|
||||
int nameLen = 0;
|
||||
const char *relName = NULL;
|
||||
struct RelHeader relHdr = {0};
|
||||
|
||||
// Read command-line args
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--pad-section-count") == 0)
|
||||
{
|
||||
if (i + 1 < argc && parse_number(argv[i + 1], &minSectionCount))
|
||||
i++;
|
||||
else
|
||||
goto usage;
|
||||
}
|
||||
else if (strcmp(argv[i], "-i") == 0 || strcmp(argv[i], "--module-id") == 0)
|
||||
{
|
||||
if (i + 1 < argc && parse_number(argv[i + 1], &moduleId))
|
||||
i++;
|
||||
else
|
||||
goto usage;
|
||||
}
|
||||
else if (strcmp(argv[i], "-o") == 0 || strcmp(argv[i], "--name-offset") == 0)
|
||||
{
|
||||
if (i + 1 < argc && parse_number(argv[i + 1], &nameOffset))
|
||||
i++;
|
||||
else
|
||||
goto usage;
|
||||
}
|
||||
else if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--name-length") == 0)
|
||||
{
|
||||
if (i + 1 < argc && parse_number(argv[i + 1], &nameLen))
|
||||
i++;
|
||||
else
|
||||
goto usage;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (relModule.filename == NULL)
|
||||
relModule.filename = argv[i];
|
||||
else if (dolModule.filename == NULL)
|
||||
dolModule.filename = argv[i];
|
||||
else if (relName == NULL)
|
||||
relName = argv[i];
|
||||
else
|
||||
goto usage;
|
||||
}
|
||||
}
|
||||
if (relModule.filename == NULL || dolModule.filename == NULL || relName == NULL)
|
||||
goto usage;
|
||||
|
||||
open_module(&relModule);
|
||||
open_module(&dolModule);
|
||||
|
||||
dolModule.moduleId = 0;
|
||||
relModule.moduleId = moduleId;
|
||||
|
||||
module_read_relocs(&relModule);
|
||||
|
||||
// TODO: Read this information from string table
|
||||
relHdr.moduleNameOffset = nameOffset;
|
||||
relHdr.moduleNameSize = nameLen;
|
||||
write_rel_file(&relModule, &relHdr, relName);
|
||||
|
||||
fclose(relModule.file);
|
||||
fclose(dolModule.file);
|
||||
|
||||
free(dolModule.strtab);
|
||||
free(dolModule.symtab);
|
||||
for (i = 0; i < importsCount; i++)
|
||||
free(imports[i].relocs);
|
||||
free(imports);
|
||||
return 0;
|
||||
|
||||
usage:
|
||||
fprintf(stderr,
|
||||
"usage: %s reloc_elf static_elf rel_file\n"
|
||||
"options:\n"
|
||||
" -i, --module-id <n> sets the module ID in the rel header to <n>\n"
|
||||
" -c, --pad-section-count <n> ensures that the rel will have at least <n>\n"
|
||||
" sections\n"
|
||||
" -o, --name-offset <offset> sets the name offset in the rel header to\n"
|
||||
" <offset>\n"
|
||||
" -l, --name-length <len> sets the name length in the rel header to <len>\n",
|
||||
argv[0]);
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,490 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from capstone import *
|
||||
from capstone.ppc import *
|
||||
from elftools.elf.elffile import *
|
||||
from elftools.elf.sections import *
|
||||
import sys
|
||||
|
||||
# addr -> name
|
||||
labels = {}
|
||||
|
||||
# fileOffset -> {addr, type}
|
||||
relocations = {}
|
||||
|
||||
# index -> {offset, flags, length, is_bss, name}
|
||||
sectionInfo = []
|
||||
|
||||
R_PPC_NONE = 0
|
||||
R_PPC_ADDR32 = 1
|
||||
R_PPC_ADDR24 = 2
|
||||
R_PPC_ADDR16_LO = 4
|
||||
R_PPC_ADDR16_HA = 6
|
||||
R_PPC_REL24 = 10
|
||||
R_DOLPHIN_SECTION = 202
|
||||
R_DOLPHIN_END = 203
|
||||
|
||||
relocationTypeNames = {
|
||||
R_PPC_NONE: 'R_PPC_NONE',
|
||||
R_PPC_ADDR32: 'R_PPC_ADDR32',
|
||||
R_PPC_ADDR24: 'R_PPC_ADDR24',
|
||||
R_PPC_ADDR16_LO: 'R_PPC_ADDR16_LO',
|
||||
R_PPC_ADDR16_HA: 'R_PPC_ADDR16_HA',
|
||||
R_PPC_REL24: 'R_PPC_REL24',
|
||||
R_DOLPHIN_SECTION: 'R_DOLPHIN_SECTION',
|
||||
R_DOLPHIN_END: 'R_DOLPHIN_END'
|
||||
}
|
||||
|
||||
def read_u8(offset):
|
||||
return filecontent[offset]
|
||||
|
||||
def read_u16(offset):
|
||||
return (filecontent[offset + 0] << 8) | filecontent[offset + 1]
|
||||
|
||||
def read_u32(offset):
|
||||
return (filecontent[offset + 0] << 24) | (filecontent[offset + 1] << 16) | (filecontent[offset + 2] << 8) | filecontent[offset + 3]
|
||||
|
||||
def add_label(addr, name=None):
|
||||
if addr in labels:
|
||||
return labels[addr]
|
||||
if name == None:
|
||||
name = 'lbl_%08X' % addr
|
||||
labels[addr] = name
|
||||
return name
|
||||
|
||||
with open(sys.argv[1], 'rb') as file:
|
||||
filecontent = bytearray(file.read())
|
||||
|
||||
if len(sys.argv) >= 3:
|
||||
# Why is this so slow?
|
||||
with open(sys.argv[2], 'rb') as f:
|
||||
elf = ELFFile(f)
|
||||
elfsymtab = elf.get_section_by_name('.symtab')
|
||||
for i in range(0, elfsymtab.num_symbols()):
|
||||
sym = elfsymtab.get_symbol(i)
|
||||
if len(sym.name) > 0 and not sym.name[0] in {'.', '@'}:
|
||||
add_label(sym['st_value'], sym.name)
|
||||
|
||||
id = read_u32(0)
|
||||
numSections = read_u32(0x0C)
|
||||
sectionInfoOffset = read_u32(0x10)
|
||||
nameOffset = read_u32(0x14)
|
||||
nameSize = read_u32(0x18)
|
||||
version = read_u32(0x1C)
|
||||
bssSize = read_u32(0x20)
|
||||
relOffset = read_u32(0x24)
|
||||
impOffset = read_u32(0x28)
|
||||
impSize = read_u32(0x2C)
|
||||
prologSection = read_u8(0x30)
|
||||
epilogSection = read_u8(0x31)
|
||||
unresolvedSection = read_u8(0x32)
|
||||
prolog = read_u32(0x34)
|
||||
epilog = read_u32(0x38)
|
||||
unresolved = read_u32(0x3C)
|
||||
|
||||
print("# id: %i" % id)
|
||||
print("# version: %i" % version)
|
||||
print("# nameoffset: 0x%X, size: 0x%X" % (nameOffset, nameSize))
|
||||
print("# section table: 0x%X, size: 0x%X" % (sectionInfoOffset, numSections*8))
|
||||
print("# imp table: 0x%X" % impOffset)
|
||||
print("# relocs offset: 0x%X" % relOffset)
|
||||
print("# _prolog: %i:0x%X" % (prologSection, prolog))
|
||||
print("# _epilog: %i:0x%X" % (epilogSection, epilog))
|
||||
print("# _unresolved: %i:0x%X" % (unresolvedSection, unresolved))
|
||||
print("# num sections: %i" % numSections)
|
||||
print('.include "macros.inc"')
|
||||
|
||||
#print("%i sections:" % numSections)
|
||||
# Read sections
|
||||
for i in range(0, numSections):
|
||||
o = sectionInfoOffset + i * 8
|
||||
section = {
|
||||
'offset': read_u32(o + 0) & ~3,
|
||||
'flags': read_u32(o + 0) & 3,
|
||||
'length': read_u32(o + 4)
|
||||
}
|
||||
if section['offset'] == 0 and section['length'] > 0:
|
||||
section['is_bss'] = True
|
||||
else:
|
||||
section['is_bss'] = False
|
||||
# Hack: if bss, then set file offset to something unique as to not
|
||||
# clash with other symbols
|
||||
if section['is_bss']:
|
||||
section['offset'] = 0x10000000
|
||||
# Determine name
|
||||
if section['is_bss']:
|
||||
section['name'] = '.bss%i' % i
|
||||
elif section['flags'] & 1:
|
||||
section['name'] = '.text%i' % i
|
||||
else:
|
||||
section['name'] = '.data%i' % i
|
||||
sectionInfo.append(section)
|
||||
print("# offset: 0x%08X\tlength: 0x%08X\tflags: %i" %
|
||||
(section['offset'], section['length'], section['flags']))
|
||||
|
||||
|
||||
sectionInfo[1]['name'] = '.text'
|
||||
sectionInfo[2]['name'] = '.ctors'
|
||||
sectionInfo[3]['name'] = '.dtors'
|
||||
sectionInfo[4]['name'] = '.rodata'
|
||||
sectionInfo[5]['name'] = '.data'
|
||||
sectionInfo[6]['name'] = '.bss'
|
||||
|
||||
# Add labels for prologue and epilogue
|
||||
if prologSection != 0:
|
||||
labels[sectionInfo[prologSection]['offset'] + prolog] = '_prolog'
|
||||
if epilogSection != 0:
|
||||
labels[sectionInfo[epilogSection]['offset'] + epilog] = '_epilog'
|
||||
if unresolvedSection != 0:
|
||||
labels[sectionInfo[unresolvedSection]['offset'] + unresolved] = '_unresolved'
|
||||
|
||||
def read_relocation_info(module, o):
|
||||
currSection = None
|
||||
missingSymbols = False
|
||||
while True:
|
||||
offset = read_u16(o + 0)
|
||||
type = read_u8(o + 2)
|
||||
section = read_u8(o + 3)
|
||||
addend = read_u32(o + 4)
|
||||
|
||||
# Get address of symbol and add label
|
||||
symAddr = 0
|
||||
if type == R_DOLPHIN_SECTION: # R_DOLPHIN_SECTION
|
||||
currSection = sectionInfo[section]
|
||||
relocOffset = currSection['offset']
|
||||
if type < 200:
|
||||
if module == 0: # dol
|
||||
symAddr = addend
|
||||
if symAddr not in labels:
|
||||
print('error: symbol for 0x%08X not found' % symAddr)
|
||||
missingSymbols = True
|
||||
else: # rel
|
||||
symAddr = sectionInfo[section]['offset'] + addend
|
||||
labels[symAddr] = 'lbl_%08X' % symAddr
|
||||
|
||||
# Get file offset for relocation
|
||||
relocOffset += offset
|
||||
|
||||
if type < 200:
|
||||
reloc = {
|
||||
'addr': symAddr,
|
||||
'type': type,
|
||||
}
|
||||
relocations[relocOffset] = reloc
|
||||
|
||||
#print(" offset: 0x%04X(+0x%X)\ttype: %s\tsection: %i\tsym_addr: 0x%08X" % (relocOffset, offset, relocationTypeNames[type], section, symAddr))
|
||||
#print(" offset: 0x%04X(+0x%X)\ttype: %s\tsection: %i\tsym_addr: ?" % (relocOffset, offset, relocationTypeNames[type], section))
|
||||
if type == R_DOLPHIN_END:
|
||||
break
|
||||
o += 8
|
||||
if missingSymbols:
|
||||
exit(1)
|
||||
|
||||
numImpEntries = impSize / 8
|
||||
#print("%i imports" % numImpEntries)
|
||||
for i in range(0, int(numImpEntries)):
|
||||
o = impOffset + i * 8
|
||||
module = read_u32(o + 0)
|
||||
relocation = read_u32(o + 4)
|
||||
#print("module: %i, offset: 0x%08X" % (module, relocation))
|
||||
read_relocation_info(module, relocation)
|
||||
|
||||
|
||||
cs = Cs(CS_ARCH_PPC, CS_MODE_32 | CS_MODE_BIG_ENDIAN)
|
||||
cs.detail = True
|
||||
cs.imm_unsigned = False
|
||||
|
||||
def get_relocation_for_offset(o):
|
||||
for i in range(o, o + 4):
|
||||
if i in relocations:
|
||||
return relocations[i]
|
||||
return None
|
||||
|
||||
|
||||
def get_label(addr):
|
||||
if addr in labels:
|
||||
return labels[addr]
|
||||
return '0x%08X' % addr
|
||||
|
||||
def print_label(label):
|
||||
if label in ['_prolog', '_epilog', '_unresolved']:
|
||||
label = '.global %s\n%s' % (label, label)
|
||||
print('%s:' % label)
|
||||
|
||||
def sign_extend_16(value):
|
||||
if value > 0 and (value & 0x8000):
|
||||
value -= 0x10000
|
||||
return value
|
||||
|
||||
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
|
||||
|
||||
def disassemble_insn_that_capstone_cant_handle(o, reloc):
|
||||
if reloc:
|
||||
relocComment = '\t;# %s:%s' % (get_label(reloc['addr']), relocationTypeNames[reloc['type']])
|
||||
else:
|
||||
relocComment = ''
|
||||
raw = read_u32(o)
|
||||
asm = None
|
||||
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:
|
||||
return asm
|
||||
return '.4byte 0x%08X ;# (error: unknown instruction) %s' % (read_u32(o), relocComment)
|
||||
|
||||
def disassemble_insn(o, reloc):
|
||||
if reloc:
|
||||
relocComment = '\t;# %s:%s' % (get_label(reloc['addr']), relocationTypeNames[reloc['type']])
|
||||
else:
|
||||
relocComment = ''
|
||||
try:
|
||||
insn = next(cs.disasm(filecontent[o : o+4], o))
|
||||
except StopIteration:
|
||||
return disassemble_insn_that_capstone_cant_handle(o, reloc)
|
||||
if reloc:
|
||||
relocType = reloc['type']
|
||||
else:
|
||||
relocType = -1
|
||||
|
||||
# handle relocs label
|
||||
if insn.id in {PPC_INS_BL, PPC_INS_BC} and relocType == R_PPC_REL24:
|
||||
return '%s %s' % (insn.mnemonic, get_label(reloc['addr']))
|
||||
if insn.id == PPC_INS_LIS and relocType == R_PPC_ADDR16_HA:
|
||||
return '%s %s, %s@ha' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), get_label(reloc['addr']))
|
||||
if insn.id == PPC_INS_ADDI and relocType == R_PPC_ADDR16_LO:
|
||||
return '%s %s, %s, %s@l' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].reg), get_label(reloc['addr']))
|
||||
if insn.id in {
|
||||
PPC_INS_LWZ, PPC_INS_LHZ, PPC_INS_LHA, PPC_INS_LBZ,
|
||||
PPC_INS_LWZU, PPC_INS_LHZU, PPC_INS_LHAU, PPC_INS_LBZU,
|
||||
PPC_INS_LFS, PPC_INS_LFD,
|
||||
PPC_INS_LFSU, PPC_INS_LFDU,
|
||||
PPC_INS_STW, PPC_INS_STH, PPC_INS_STB,
|
||||
PPC_INS_STWU, PPC_INS_STHU, PPC_INS_STBU,
|
||||
PPC_INS_STFS, PPC_INS_STFD,
|
||||
PPC_INS_STFSU, PPC_INS_STFDU} \
|
||||
and relocType == R_PPC_ADDR16_LO:
|
||||
return '%s %s, %s@l(%s)' % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), get_label(reloc['addr']), insn.reg_name(insn.operands[1].mem.base))
|
||||
|
||||
# branch target labels
|
||||
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BDZ, PPC_INS_BDNZ, PPC_INS_BC}:
|
||||
if reloc:
|
||||
return '%s %s' % (insn.mnemonic, get_label(reloc['addr']))
|
||||
#add_label(insn.operands[0].imm)
|
||||
#label = labels[insn.operands[0].imm]
|
||||
#if label:
|
||||
# WTF, capstone?
|
||||
if o == 0xAD8C:
|
||||
return '%s lbl_0000ADB0' % insn.mnemonic
|
||||
return '%s %s' % (insn.mnemonic, get_label(insn.operands[0].imm))
|
||||
|
||||
# misc. fixes
|
||||
|
||||
# Sign-extend immediate values because Capstone is an idiot and thinks all immediates are unsigned
|
||||
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 ;# fixed addi" % (insn.mnemonic, insn.reg_name(insn.operands[0].reg), insn.reg_name(insn.operands[1].value.reg), insn.operands[2].imm - 0x10000)
|
||||
if (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
|
||||
if insn.id == PPC_INS_CNTLZW:
|
||||
return "cntlzw %s" % insn.op_str
|
||||
|
||||
return '%s %s%s' % (insn.mnemonic, insn.op_str, relocComment)
|
||||
|
||||
def scan_local_labels(o, size):
|
||||
end = o + size
|
||||
while o < end:
|
||||
reloc = get_relocation_for_offset(o)
|
||||
if reloc:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
insn = next(cs.disasm(filecontent[o:o+4], o))
|
||||
if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BC, PPC_INS_BDZ, PPC_INS_BDNZ}:
|
||||
for op in insn.operands:
|
||||
if op.type == PPC_OP_IMM:
|
||||
l = add_label(op.imm)
|
||||
#print('adding local label %s(0x%X) from offset 0x%X' % (l, op.imm, o))
|
||||
except StopIteration:
|
||||
pass
|
||||
o += 4
|
||||
#for insn in cs.disasm(filecontent[o:o+size], o):
|
||||
# # branch labels
|
||||
# if insn.id in {PPC_INS_B, PPC_INS_BL, PPC_INS_BC, PPC_INS_BDZ, PPC_INS_BDNZ}:
|
||||
# for op in insn.operands:
|
||||
# if op.type == PPC_OP_IMM:
|
||||
# l = add_label(op.imm)
|
||||
# print('adding local label %s(0x%X) from offset 0x%X' % (l, op.imm, o))
|
||||
|
||||
def dump_code(o, size):
|
||||
scan_local_labels(o, size)
|
||||
end = o + size
|
||||
code = filecontent[o : end]
|
||||
while o < end:
|
||||
if o in labels:
|
||||
print_label(labels[o])
|
||||
asm = disassemble_insn(o, get_relocation_for_offset(o))
|
||||
print('/* %08X %08X */ %s' % (o, read_u32(o), asm))
|
||||
#print('/* %08X */ %s' % (read_u32(o), asm))
|
||||
o += 4
|
||||
if o < end:
|
||||
print('incomplete')
|
||||
|
||||
# returns True if value is 4-byte aligned
|
||||
def is_aligned(num):
|
||||
return num % 4 == 0
|
||||
|
||||
def align(num):
|
||||
return (num + 3) & ~3
|
||||
|
||||
def is_ascii(code):
|
||||
if code >= 0x20 and code <= 0x7E: # normal characters
|
||||
return True
|
||||
if code in [0x09, 0x0A]: # tab, newline
|
||||
return True
|
||||
return False
|
||||
|
||||
# returns True if all elements are zero
|
||||
def is_all_zero(arr):
|
||||
for val in arr:
|
||||
if val != 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
# returns string of comma-separated hex bytes
|
||||
def hex_bytes(data):
|
||||
return ', '.join('0x%02X' % n for n in data)
|
||||
|
||||
# reads a string starting at pos
|
||||
def read_string(data, pos):
|
||||
text = ''
|
||||
while pos < len(data) and is_ascii(data[pos]):
|
||||
text += chr(data[pos])
|
||||
pos += 1
|
||||
if pos < len(data) and data[pos] == 0:
|
||||
return text
|
||||
return ''
|
||||
|
||||
# escapes special characters in the string for use in a C string literal
|
||||
def escape_string(text):
|
||||
return text.replace('\\','\\\\').replace('"','\\"').replace('\n','\\n').replace('\t','\\t')
|
||||
|
||||
def output_data_range(secNum, o, end):
|
||||
print(' # 0x%X' % o)
|
||||
if not is_aligned(o):
|
||||
print(' .byte ' + hex_bytes(filecontent[o:align(o)]))
|
||||
o = align(o)
|
||||
while o < (end & ~3):
|
||||
# Try to see if this is a string.
|
||||
string = read_string(filecontent, o)
|
||||
if len(string) >= 4 and secNum == 5: # strings are only in .data
|
||||
strEnd = o + len(string)+1
|
||||
if is_aligned(strEnd) or is_all_zero(filecontent[strEnd : align(strEnd)-strEnd]):
|
||||
print(' .asciz \"%s"' % escape_string(string))
|
||||
if not is_aligned(strEnd):
|
||||
print(' .balign 4')
|
||||
o = align(strEnd)
|
||||
continue
|
||||
# Not a string
|
||||
reloc = get_relocation_for_offset(o)
|
||||
if reloc:
|
||||
type = reloc['type']
|
||||
if type == R_PPC_ADDR32:
|
||||
value = labels[reloc['addr']]
|
||||
else:
|
||||
print('dunno what to do about %s here' % relocationTypeNames[type])
|
||||
else:
|
||||
value = '0x%08X' % read_u32(o)
|
||||
print(' .4byte %s' % value)
|
||||
o += 4
|
||||
if o < end:
|
||||
print(' .byte ' + hex_bytes(filecontent[o:end]))
|
||||
return
|
||||
|
||||
|
||||
def dump_data(secNum, o, size):
|
||||
end = o + size
|
||||
lastPos = o
|
||||
while o < end:
|
||||
if o in labels:
|
||||
if o - lastPos > 0:
|
||||
output_data_range(secNum, lastPos, o)
|
||||
print_label(labels[o])
|
||||
lastPos = o
|
||||
o += 1
|
||||
if o - lastPos > 0:
|
||||
output_data_range(secNum, lastPos, o)
|
||||
return
|
||||
|
||||
|
||||
def output_bss_range(start, end):
|
||||
print(' .skip 0x%X' % (end - start))
|
||||
|
||||
def dump_bss(o, size):
|
||||
end = o + size
|
||||
lastPos = o
|
||||
while o < end:
|
||||
if o in labels:
|
||||
if o - lastPos > 0:
|
||||
output_bss_range(lastPos, o)
|
||||
print_label(labels[o])
|
||||
lastPos = o
|
||||
o += 1
|
||||
if o - lastPos > 0:
|
||||
output_bss_range(lastPos, o)
|
||||
return
|
||||
|
||||
|
||||
for i in range(0, numSections):
|
||||
section = sectionInfo[i]
|
||||
if section['offset'] == 0 and section['length'] == 0:
|
||||
continue
|
||||
print('# %i' % i)
|
||||
print('.section %s' % section['name'])
|
||||
if section['is_bss']:
|
||||
# bss section
|
||||
dump_bss(section['offset'], section['length'])
|
||||
elif section['flags'] & 1:
|
||||
# code section
|
||||
dump_code(section['offset'], section['length'])
|
||||
elif section['offset'] != 0:
|
||||
# data section
|
||||
dump_data(i, section['offset'], section['length'])
|
||||
print('')
|
Loading…
Reference in New Issue