Initial commit

This commit is contained in:
Luke Street 2022-03-23 17:22:48 -04:00
commit ce247fb264
32 changed files with 1133268 additions and 0 deletions

10
.gitattributes vendored Normal file
View File

@ -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

38
.gitignore vendored Normal file
View File

@ -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

208
Makefile Normal file
View File

@ -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

View File

@ -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

1143
asm/bss.s Normal file

File diff suppressed because it is too large Load Diff

6
asm/ctors.s Normal file
View File

@ -0,0 +1,6 @@
.include "macros.inc"
.section .ctors, "wa" # 0x803CB1C0 - 0x803CB380
.global lbl_803CB1C0
lbl_803CB1C0:
.incbin "baserom.dol", 0x3C81C0, 0x1C0

2925
asm/data.s Normal file

File diff suppressed because it is too large Load Diff

6
asm/dtors.s Normal file
View File

@ -0,0 +1,6 @@
.include "macros.inc"
.section .dtors, "wa" # 0x803CB380 - 0x803CB3A0
.global lbl_803CB380
lbl_803CB380:
.incbin "baserom.dol", 0x3C8380, 0x10

6
asm/extab.s Normal file
View File

@ -0,0 +1,6 @@
.include "macros.inc"
.section extab_, "wa" # 0x800035A0 - 0x800035E0
.global lbl_extab
lbl_extab:
.incbin "baserom.dol", 0x3C8120, 0x28

8
asm/extabindex.s Normal file
View File

@ -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

31
asm/os/__ppc_eabi_init.s Normal file
View File

@ -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

1782
asm/rodata.s Normal file

File diff suppressed because it is too large Load Diff

2859
asm/sbss.s Normal file

File diff suppressed because it is too large Load Diff

69
asm/sbss2.s Normal file
View File

@ -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

5808
asm/sdata.s Normal file

File diff suppressed because it is too large Load Diff

14100
asm/sdata2.s Normal file

File diff suppressed because it is too large Load Diff

1101638
asm/text.s Normal file

File diff suppressed because it is too large Load Diff

7
asmdiff.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
OBJDUMP="$DEVKITPPC/bin/powerpc-eabi-objdump -D -bbinary -EB -mpowerpc -M gekko"
OPTIONS="--start-address=$(($1)) --stop-address=$(($1 + $2))"
$OBJDUMP $OPTIONS baserom.dol > baserom.dump
$OBJDUMP $OPTIONS build/mp1.0/main.dol > main.dump
diff -u --color=always baserom.dump main.dump

56
include/Dolphin/__start.h Normal file
View File

@ -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[];

23
include/Dolphin/db.h Normal file
View File

@ -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

92
include/macros.inc Normal file
View File

@ -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

71
include/types.h Normal file
View File

@ -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

42
ldscript.lcf Normal file
View File

@ -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
}

35
ldscript.ld Normal file
View File

@ -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
}

40
obj_files.mk Normal file
View File

@ -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

1
sha1/mp1.0.sha1 Normal file
View File

@ -0,0 +1 @@
949c5ed7368aef547e0b0db1c3678f466e2afbff build/mp1.0/main.dol

171
src/os/__start.c Normal file
View File

@ -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++;
}
}

16
tools/Makefile Normal file
View File

@ -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

120
tools/deincbin.py Normal file
View File

@ -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)

497
tools/elf2dol.c Normal file
View File

@ -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;
}

866
tools/elf2rel.c Normal file
View File

@ -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;
}

490
tools/reldisasm.py Executable file
View File

@ -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('')