add build system from https://github.com/PrimeDecomp/prime
This commit is contained in:
parent
0e3447d877
commit
a2d582d388
26 changed files with 1276 additions and 14 deletions
10
.gitattributes
vendored
Normal file
10
.gitattributes
vendored
Normal 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
|
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
|
||||
.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
208
Makefile
Normal 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 := donut
|
||||
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)/donut.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 := 4.3
|
||||
ifeq ($(EPILOGUE_PROCESS),1)
|
||||
MWCC_EPI_VERSION := 1.2.5
|
||||
MWCC_EPI_EXE := mwcceppc.exe
|
||||
endif
|
||||
MWLD_VERSION := 4.3
|
||||
|
||||
# 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 $(DOL) $(MAP)
|
||||
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
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .bss, "", @nobits # 0x80496700 - 0x80556420 ; 0x000BFD20
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .ctors, "wa" # 0x80406260 - 0x80406540 ; 0x000002E0
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .data, "wa" # 0x80421040 - 0x80496700 ; 0x000756C0
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .dtors, "wa" # 0x80406540 - 0x80406560 ; 0x00000020
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section extab_, "wa" # 0x80006740 - 0x800068E0 ; 0x000001A0
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section extabindex_, "wa" # 0x800068E0 - 0x80006A00 ; 0x00000120
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .init, "ax" # 0x80004000 - 0x80006740 ; 0x00002740
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .rodata, "wa" # 0x80406560 - 0x80421040 ; 0x0001AAE0
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .sbss, "", @nobits # 0x8055C6E0 - 0x8055DF80 ; 0x000018A0
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .sbss2, "", @nobits # 0x805643C0 - 0x805643FC ; 0x0000003C
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .sdata, "wa" # 0x80556420 - 0x8055C6E0 ; 0x000062C0
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .sdata2, "wa" # 0x8055DF80 - 0x805643C0 ; 0x00006440
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.include "macros.s"
|
||||
.include "macros.inc"
|
||||
|
||||
.section .text, "ax" # 0x80006A00 - 0x80406260 ; 0x003FF860
|
||||
|
||||
|
|
76
asmsplit.py
Executable file
76
asmsplit.py
Executable file
|
@ -0,0 +1,76 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i python3 -p python3
|
||||
#
|
||||
# Usage: ./asmsplit.py MAPFILE < ASMFILE
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
basedir = 'asm/'
|
||||
macros = 'macros.inc'
|
||||
|
||||
filename_counter = 0
|
||||
filenames = {}
|
||||
lastfile = None
|
||||
|
||||
with open(sys.argv[1]) as mapfile:
|
||||
for mapline in mapfile:
|
||||
match = re.match(' [0-9a-f]{8} [0-9a-f]{6} ([0-9a-f]{8}) [0-9a-f]{8} [ 0-9][0-9] [^ ]+ \t(.+)', mapline)
|
||||
if match and match.group(2) != lastfile:
|
||||
lastfile = match.group(2)
|
||||
addr = int(match.group(1), 16)
|
||||
fname = basedir + '/'.join(map(lambda s: os.path.splitext(s)[0], match.group(2).strip().split(' '))) + f"_{filename_counter}" + '.s'
|
||||
filename_counter += 1
|
||||
filenames[addr] = fname
|
||||
|
||||
curfile = open(macros, 'w')
|
||||
curaddr = 0
|
||||
section = ''
|
||||
remainder = None
|
||||
|
||||
while asmline := remainder or sys.stdin.readline():
|
||||
remainder = None
|
||||
trim = asmline.strip()
|
||||
if trim.startswith('.section'):
|
||||
curaddr = int(trim[-23:-13], 0)
|
||||
section = asmline
|
||||
curfile.close()
|
||||
else:
|
||||
if trim != "":
|
||||
if curfile.closed:
|
||||
fname = filenames[curaddr]
|
||||
if os.path.exists(fname):
|
||||
curfile = open(fname, 'a')
|
||||
curfile.write('\n')
|
||||
else:
|
||||
os.makedirs(os.path.dirname(fname), exist_ok = True)
|
||||
curfile = open(fname, 'x')
|
||||
curfile.write('.include "' + macros + '"\n\n')
|
||||
|
||||
curfile.write(section)
|
||||
|
||||
if trim.startswith('.skip'):
|
||||
curaddr += int(trim[6:], 0)
|
||||
elif trim.startswith('.incbin'):
|
||||
f, a, s = asmline.split(', ')
|
||||
a = int(a, 0)
|
||||
s = int(s, 0)
|
||||
if s < 0: raise ValueError()
|
||||
elif s == 0: continue
|
||||
|
||||
k = 1
|
||||
while (curaddr + k) not in filenames and k < s: k += 1
|
||||
curaddr += k
|
||||
|
||||
if k < s:
|
||||
asmline = f + ', 0x' + format(a, 'X') + ', 0x' + format(k, 'X') + '\n'
|
||||
remainder = f + ', 0x' + format(a + k, 'X') + ', 0x' + format(s - k, 'X') + '\n'
|
||||
|
||||
elif not trim.startswith('.global') and not trim.endswith(':'):
|
||||
curaddr += 4
|
||||
|
||||
if not curfile.closed: curfile.write(asmline)
|
||||
|
||||
if curaddr in filenames and filenames[curaddr] != curfile.name:
|
||||
curfile.close()
|
56
deptr.py
Normal file
56
deptr.py
Normal file
|
@ -0,0 +1,56 @@
|
|||
#!/usr/bin/env nix-shell
|
||||
#! nix-shell -i python3 -p python3
|
||||
#
|
||||
# Usage: deptr.py mapfile file.s
|
||||
# Replaces pointers with symbol names
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
substitutions = (
|
||||
('<', '$$0'),
|
||||
('>', '$$1'),
|
||||
('@', '$$2'),
|
||||
('\\', '\\\\'),
|
||||
(',', '$$4'),
|
||||
('-', '$$5')
|
||||
)
|
||||
|
||||
def format(symbol):
|
||||
illegal_symbols = ('<', '>', '@', '\\', ',', '-')
|
||||
symbol.replace("\\", "\\\\")
|
||||
for c in illegal_symbols:
|
||||
if c in symbol:
|
||||
symbol = f'"{symbol}"'
|
||||
break
|
||||
|
||||
return symbol
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print('Usage: %s MAP_FILE ASM_FILE' % sys.argv[0])
|
||||
exit()
|
||||
|
||||
labels = set()
|
||||
labelNames = {}
|
||||
|
||||
with open(sys.argv[1], "r") as mapfile:
|
||||
for line in mapfile:
|
||||
match = re.match(' [0-9a-f]{8} [0-9a-f]{6} ([0-9a-f]{8}) [0-9a-f]{8} [ 0-9][0-9] ([^ 0-9.][^ ]*)', line)
|
||||
if match:
|
||||
addr = int(match.group(1), 16)
|
||||
name = format(match.group(2))
|
||||
labels.add(addr)
|
||||
labelNames[addr] = name
|
||||
|
||||
with open(sys.argv[2], 'rt') as f:
|
||||
for line in f.readlines():
|
||||
line = line.rstrip('\n')
|
||||
# Section directive
|
||||
m = re.match(r'.*((lbl|func)_(80[0-5][0-9A-F]{5})).*', line)
|
||||
if m:
|
||||
label_address = int(m.group(3), 16)
|
||||
label = m.group(1)
|
||||
if label_address in labelNames:
|
||||
line = line.replace(label, labelNames[label_address])
|
||||
print(line)
|
6
docker/Dockerfile
Normal file
6
docker/Dockerfile
Normal file
|
@ -0,0 +1,6 @@
|
|||
FROM devkitpro/devkitppc
|
||||
|
||||
RUN dpkg --add-architecture i386
|
||||
RUN apt update -yy
|
||||
RUN apt install wine32 -yy
|
||||
RUN apt install build-essential -yy
|
|
@ -92,4 +92,3 @@ Entry Point: 0x80006310
|
|||
.set _SDA_BASE_, 0x8055E420
|
||||
# Small Data Area (read only) Base
|
||||
.set _SDA2_BASE_, 0x80565F80
|
||||
|
38
obj_files.mk
Normal file
38
obj_files.mk
Normal file
|
@ -0,0 +1,38 @@
|
|||
INIT_O_FILES := \
|
||||
$(BUILD_DIR)/asm/init.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/donut.0.sha1
Normal file
1
sha1/donut.0.sha1
Normal file
|
@ -0,0 +1 @@
|
|||
14bf34bafa177caf1eada83bbe26f1c130ed3c4a build/donut.0/main.dol
|
13
tools/Makefile
Normal file
13
tools/Makefile
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
CC := gcc
|
||||
CFLAGS := -O3 -Wall -s
|
||||
|
||||
default: all
|
||||
|
||||
all: elf2dol
|
||||
|
||||
elf2dol: elf2dol.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
clean:
|
||||
$(RM) elf2dol
|
199
tools/calcprogress.py
Normal file
199
tools/calcprogress.py
Normal file
|
@ -0,0 +1,199 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
################################################################################
|
||||
# Description #
|
||||
################################################################################
|
||||
# calcprogress: Used to calculate the progress of the Metroid Prime decomp. #
|
||||
# Prints to stdout for now, but eventually will have some form of storage, #
|
||||
# i.e. CSV, so that it can be used for a webpage display. #
|
||||
# #
|
||||
# Usage: No arguments needed #
|
||||
################################################################################
|
||||
|
||||
|
||||
|
||||
|
||||
###############################################
|
||||
# #
|
||||
# Imports #
|
||||
# #
|
||||
###############################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
import re
|
||||
import math
|
||||
|
||||
###############################################
|
||||
# #
|
||||
# Constants #
|
||||
# #
|
||||
###############################################
|
||||
|
||||
DOL_PATH = sys.argv[1]
|
||||
MAP_PATH = sys.argv[2]
|
||||
|
||||
MEM1_HI = 0x81200000
|
||||
MEM1_LO = 0x80004000
|
||||
|
||||
MW_WII_SYMBOL_REGEX = r"^\s*"\
|
||||
r"(?P<SectOfs>\w{8})\s+"\
|
||||
r"(?P<Size>\w{6})\s+"\
|
||||
r"(?P<VirtOfs>\w{8})\s+"\
|
||||
r"(?P<FileOfs>\w{8})\s+"\
|
||||
r"(\w{1,2})\s+"\
|
||||
r"(?P<Symbol>[0-9A-Za-z_<>$@.*]*)\s*"\
|
||||
r"(?P<Object>\S*)"
|
||||
|
||||
MW_GC_SYMBOL_REGEX = r"^\s*"\
|
||||
r"(?P<SectOfs>\w{8})\s+"\
|
||||
r"(?P<Size>\w{6})\s+"\
|
||||
r"(?P<VirtOfs>\w{8})\s+"\
|
||||
r"(\w{1,2})\s+"\
|
||||
r"(?P<Symbol>[0-9A-Za-z_<>$@.*]*)\s*"\
|
||||
r"(?P<Object>\S*)"
|
||||
|
||||
REGEX_TO_USE = MW_GC_SYMBOL_REGEX
|
||||
|
||||
TEXT_SECTIONS = ["init", "text"]
|
||||
DATA_SECTIONS = [
|
||||
"rodata", "data", "bss", "sdata", "sbss", "sdata2", "sbss2",
|
||||
"ctors", "_ctors", "dtors", "ctors$99", "_ctors$99", "ctors$00", "dtors$99",
|
||||
"extab_", "extabindex_", "_extab", "_exidx"
|
||||
]
|
||||
|
||||
# DOL info
|
||||
TEXT_SECTION_COUNT = 7
|
||||
DATA_SECTION_COUNT = 11
|
||||
|
||||
SECTION_TEXT = 0
|
||||
SECTION_DATA = 1
|
||||
|
||||
# Progress flavor
|
||||
codeFrac = 120 # total code "item" amount
|
||||
dataFrac = 37 # total data "item" amount
|
||||
codeItem = "energy spheres" # code flavor item
|
||||
dataItem = "stages" # data flavor item
|
||||
|
||||
###############################################
|
||||
# #
|
||||
# Entrypoint #
|
||||
# #
|
||||
###############################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Sum up DOL section sizes
|
||||
dol_handle = open(DOL_PATH, "rb")
|
||||
|
||||
# Seek to virtual addresses
|
||||
dol_handle.seek(0x48)
|
||||
|
||||
# Read virtual addresses
|
||||
text_starts = list()
|
||||
for i in range(TEXT_SECTION_COUNT):
|
||||
text_starts.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
|
||||
data_starts = list()
|
||||
for i in range(DATA_SECTION_COUNT):
|
||||
data_starts.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
|
||||
|
||||
# Read lengths
|
||||
text_sizes = list()
|
||||
for i in range(TEXT_SECTION_COUNT):
|
||||
text_sizes.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
|
||||
data_sizes = list()
|
||||
for i in range(DATA_SECTION_COUNT):
|
||||
data_sizes.append(int.from_bytes(dol_handle.read(4), byteorder='big'))
|
||||
|
||||
|
||||
|
||||
# BSS address + length
|
||||
bss_start = int.from_bytes(dol_handle.read(4), byteorder='big')
|
||||
bss_size = int.from_bytes(dol_handle.read(4), byteorder='big')
|
||||
bss_end = bss_start + bss_size
|
||||
|
||||
|
||||
dol_code_size = 0
|
||||
dol_data_size = 0
|
||||
for i in range(DATA_SECTION_COUNT):
|
||||
# Ignore sections inside BSS
|
||||
if (data_starts[i] >= bss_start) and (data_starts[i] + data_sizes[i] <= bss_end): continue
|
||||
dol_data_size += data_sizes[i]
|
||||
|
||||
dol_data_size += bss_size
|
||||
|
||||
for i in text_sizes:
|
||||
dol_code_size += i
|
||||
|
||||
# Open map file
|
||||
mapfile = open(MAP_PATH, "r")
|
||||
symbols = mapfile.readlines()
|
||||
|
||||
decomp_code_size = 0
|
||||
decomp_data_size = 0
|
||||
section_type = None
|
||||
|
||||
# Find first section
|
||||
first_section = 0
|
||||
while (symbols[first_section].startswith(".") == False and "section layout" not in symbols[first_section]): first_section += 1
|
||||
assert(first_section < len(symbols)), "Map file contains no sections!!!"
|
||||
|
||||
cur_object = None
|
||||
cur_size = 0
|
||||
j = 0
|
||||
for i in range(first_section, len(symbols)):
|
||||
# New section
|
||||
if (symbols[i].startswith(".") == True or "section layout" in symbols[i]):
|
||||
# Grab section name (i.e. ".init section layout" -> "init")
|
||||
sectionName = re.search(r"\.*(?P<Name>\w+)\s", symbols[i]).group("Name")
|
||||
# Determine type of section
|
||||
section_type = SECTION_DATA if (sectionName in DATA_SECTIONS) else SECTION_TEXT
|
||||
# Parse symbols until we hit the next section declaration
|
||||
else:
|
||||
if "UNUSED" in symbols[i]: continue
|
||||
if "entry of" in symbols[i]:
|
||||
if j == i - 1:
|
||||
if section_type == SECTION_TEXT:
|
||||
decomp_code_size -= cur_size
|
||||
else:
|
||||
decomp_data_size -= cur_size
|
||||
cur_size = 0
|
||||
#print(f"Line* {j}: {symbols[j]}")
|
||||
#print(f"Line {i}: {symbols[i]}")
|
||||
continue
|
||||
assert(section_type != None), f"Symbol found outside of a section!!!\n{symbols[i]}"
|
||||
match_obj = re.search(REGEX_TO_USE, symbols[i])
|
||||
# Should be a symbol in ASM (so we discard it)
|
||||
if (match_obj == None):
|
||||
#print(f"Line {i}: {symbols[i]}")
|
||||
continue
|
||||
# Has the object file changed?
|
||||
last_object = cur_object
|
||||
cur_object = match_obj.group("Object").strip()
|
||||
if last_object != cur_object: continue
|
||||
# Is the symbol a file-wide section?
|
||||
symb = match_obj.group("Symbol")
|
||||
if (symb.startswith("*fill*")) or (symb.startswith(".") and symb[1:] in TEXT_SECTIONS or symb[1:] in DATA_SECTIONS): continue
|
||||
# For sections that don't start with "."
|
||||
if (symb in DATA_SECTIONS): continue
|
||||
# If not, we accumulate the file size
|
||||
cur_size = int(match_obj.group("Size"), 16)
|
||||
j = i
|
||||
if (section_type == SECTION_TEXT):
|
||||
decomp_code_size += cur_size
|
||||
else:
|
||||
decomp_data_size += cur_size
|
||||
|
||||
# Calculate percentages
|
||||
codeCompletionPcnt = (decomp_code_size / dol_code_size) # code completion percent
|
||||
dataCompletionPcnt = (decomp_data_size / dol_data_size) # data completion percent
|
||||
bytesPerCodeItem = dol_code_size / codeFrac # bytes per code item
|
||||
bytesPerDataItem = dol_data_size / dataFrac # bytes per data item
|
||||
|
||||
codeCount = math.floor(decomp_code_size / bytesPerCodeItem)
|
||||
dataCount = math.floor(decomp_data_size / bytesPerDataItem)
|
||||
|
||||
print("Progress:")
|
||||
print(f"\tCode sections: {decomp_code_size} / {dol_code_size}\tbytes in src ({codeCompletionPcnt:%})")
|
||||
print(f"\tData sections: {decomp_data_size} / {dol_data_size}\tbytes in src ({dataCompletionPcnt:%})")
|
||||
print("\nYou have collected {} out of {} {} and completed {} out of {} {}.".format(codeCount, codeFrac, codeItem, dataCount, dataFrac, dataItem))
|
120
tools/deincbin.py
Normal file
120
tools/deincbin.py
Normal 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
497
tools/elf2dol.c
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue