From 89751f29467b359339a8c8b57c218cc3328bae43 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:36:56 +0000 Subject: Rewritten all Makefiles, set up proper GCC options, added iso.xml to template --- examples/beginner/cppdemo/makefile | 81 ++++---- examples/beginner/hello/makefile | 81 ++++---- examples/cdrom/cdbrowse/iso.xml | 33 ++-- examples/cdrom/cdbrowse/makefile | 80 +++++--- examples/cdrom/cdxa/iso.xml | 29 +-- examples/cdrom/cdxa/makefile | 80 +++++--- examples/demos/n00bdemo/data.s | 2 +- examples/demos/n00bdemo/data.xml | 6 +- examples/demos/n00bdemo/makefile | 82 +++++--- examples/examples-setup.mk | 64 ------- examples/graphics/balls/makefile | 81 ++++---- examples/graphics/billboard/makefile | 76 +++++--- examples/graphics/fpscam/makefile | 76 +++++--- examples/graphics/gte/makefile | 76 +++++--- examples/graphics/hdtv/makefile | 77 +++++--- examples/graphics/render2tex/makefile | 76 +++++--- examples/graphics/rgb24/makefile | 76 +++++--- examples/makefile | 4 +- examples/system/childexec/child.c | 326 -------------------------------- examples/system/childexec/child/child.c | 326 ++++++++++++++++++++++++++++++++ examples/system/childexec/child_exe.s | 2 +- examples/system/childexec/makefile | 105 +++++++--- examples/system/console/makefile | 81 ++++---- examples/system/timer/makefile | 76 +++++--- examples/system/tty/makefile | 81 ++++---- 25 files changed, 1200 insertions(+), 877 deletions(-) delete mode 100644 examples/examples-setup.mk delete mode 100644 examples/system/childexec/child.c create mode 100644 examples/system/childexec/child/child.c (limited to 'examples') diff --git a/examples/beginner/cppdemo/makefile b/examples/beginner/cppdemo/makefile index 3e122ab..630a280 100644 --- a/examples/beginner/cppdemo/makefile +++ b/examples/beginner/cppdemo/makefile @@ -1,54 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions + +## Settings + +PSN00BSDK_LIBS ?= ../../../libpsn00b + +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk # Project target name -TARGET = cppdemo.elf +TARGET = cppdemo + +## Files # Searches for C, C++ and S (assembler) files in local directory -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) -# Determine object files -OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ - $(addprefix build/,$(CPPFILES:.cpp=.o)) \ - $(addprefix build/,$(AFILES:.s=.o)) +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) -# Project specific include and library directories -# (use -I for include dirs, -L for library dirs) -INCLUDE += -LIBDIRS += +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += -# Libraries to link -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Build rules -# C compiler flags -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections +#all: iso +all: build/$(TARGET) -# C++ compiler flags -CPPFLAGS = $(CFLAGS) -fno-builtin -fno-rtti -fno-exceptions +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml -# Assembler flags -AFLAGS = -g -msoft-float +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml -# Linker flags -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.cpp @mkdir -p $(dir $@) - $(CXX) $(CPPFLAGS) $(INCLUDE) -c $< -o $@ - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/beginner/hello/makefile b/examples/beginner/hello/makefile index 3fce1ae..e4bed20 100644 --- a/examples/beginner/hello/makefile +++ b/examples/beginner/hello/makefile @@ -1,54 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions + +## Settings + +PSN00BSDK_LIBS ?= ../../../libpsn00b + +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk # Project target name -TARGET = hello.elf +TARGET = hello + +## Files # Searches for C, C++ and S (assembler) files in local directory -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) -# Determine object files -OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ - $(addprefix build/,$(CPPFILES:.cpp=.o)) \ - $(addprefix build/,$(AFILES:.s=.o)) +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) -# Project specific include and library directories -# (use -I for include dirs, -L for library dirs) -INCLUDE += -LIBDIRS += +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += -# Libraries to link -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Build rules -# C compiler flags -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections +#all: iso +all: build/$(TARGET) -# C++ compiler flags -CPPFLAGS = $(CFLAGS) -fno-exceptions +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml -# Assembler flags -AFLAGS = -g -msoft-float +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml -# Linker flags -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.cpp @mkdir -p $(dir $@) - $(CXX) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/cdrom/cdbrowse/iso.xml b/examples/cdrom/cdbrowse/iso.xml index a828df1..5d8963d 100644 --- a/examples/cdrom/cdbrowse/iso.xml +++ b/examples/cdrom/cdbrowse/iso.xml @@ -1,33 +1,34 @@ - - - - + + - + + + - - - - - + + - + - - + diff --git a/examples/cdrom/cdbrowse/makefile b/examples/cdrom/cdbrowse/makefile index ee925d2..954408b 100644 --- a/examples/cdrom/cdbrowse/makefile +++ b/examples/cdrom/cdbrowse/makefile @@ -1,45 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = cdbrowse.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -LIBS = -lpsxcd -lpsxgpu -lpsxgte -lpsxspu -lpsxsio -lpsxetc -lpsxapi -lc +# Project target name +TARGET = cdbrowse -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) \ - -fno-exceptions \ - -fno-rtti \ - -fno-unwind-tables \ - -fno-threadsafe-statics \ - -fno-use-cxa-atexit +## Files -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +all: iso +#all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - -iso: - mkpsxiso -y -q iso.xml - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ build/%.o: %.cpp @mkdir -p $(dir $@) - $(CXX) $(CPPFLAGS) $(INCLUDE) -c $< -o $@ - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/cdrom/cdxa/iso.xml b/examples/cdrom/cdxa/iso.xml index 840b414..9a6a206 100644 --- a/examples/cdrom/cdxa/iso.xml +++ b/examples/cdrom/cdxa/iso.xml @@ -1,29 +1,30 @@ - - - - + + - + + + - - - - + - - + diff --git a/examples/cdrom/cdxa/makefile b/examples/cdrom/cdxa/makefile index b95efa7..b1627f7 100644 --- a/examples/cdrom/cdxa/makefile +++ b/examples/cdrom/cdxa/makefile @@ -1,45 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = cdxa.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -LIBS = -lpsxcd -lpsxgpu -lpsxgte -lpsxspu -lpsxsio -lpsxetc -lpsxapi -lc +# Project target name +TARGET = cdxa -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) \ - -fno-exceptions \ - -fno-rtti \ - -fno-unwind-tables \ - -fno-threadsafe-statics \ - -fno-use-cxa-atexit +## Files -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - -iso: - mkpsxiso -y -q iso.xml - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ build/%.o: %.cpp @mkdir -p $(dir $@) - $(CXX) $(CPPFLAGS) $(INCLUDE) -c $< -o $@ - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/demos/n00bdemo/data.s b/examples/demos/n00bdemo/data.s index 3ca33cb..f3b2a83 100644 --- a/examples/demos/n00bdemo/data.s +++ b/examples/demos/n00bdemo/data.s @@ -3,7 +3,7 @@ .global lz_resources .type lz_resources, @object lz_resources: - .incbin "data.lzp" + .incbin "build/data.lzp" #.global smd_mtekdisk #.type smd_mtekdisk, @object diff --git a/examples/demos/n00bdemo/data.xml b/examples/demos/n00bdemo/data.xml index 292a325..6761f16 100644 --- a/examples/demos/n00bdemo/data.xml +++ b/examples/demos/n00bdemo/data.xml @@ -1,6 +1,6 @@ - + data/petscum16c.tim data/bungirl.tim @@ -19,7 +19,7 @@ - + data/mtekdisk.smd @@ -42,7 +42,7 @@ data/hatkid.smd - textures.qlp + build/textures.qlp diff --git a/examples/demos/n00bdemo/makefile b/examples/demos/n00bdemo/makefile index 7e3bd8e..9206e09 100644 --- a/examples/demos/n00bdemo/makefile +++ b/examples/demos/n00bdemo/makefile @@ -1,39 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = demo.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -I../../../libpsn00b/lzp +# Project target name +TARGET = demo -LIBS = -llzp -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += -llzp + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) + $(MKPSXISO) -y -q iso.xml + +resources: + $(LZPACK) data.xml + touch data.s + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: resources $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - -build/%.o: %.s + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - -resources: - lzpack data.xml - touch data.s - -iso: - mkpsxiso -y -q -o demo.iso iso.xml - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.s resources + @mkdir -p $(dir $@) + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build *.lzp *.qlp $(TARGET) $(TARGET:.elf=.exe) $(TARGET:.elf=.iso) + rm -rf build diff --git a/examples/examples-setup.mk b/examples/examples-setup.mk deleted file mode 100644 index 1fadd84..0000000 --- a/examples/examples-setup.mk +++ /dev/null @@ -1,64 +0,0 @@ -# PSn00bSDK examples setup file -# Part of the PSn00bSDK Project -# 2019 - 2020 Lameguy64 / Meido-Tek Productions -# -# This is only for the PSn00bSDK example programs, not recommended -# for use with user projects - -PREFIX = mipsel-unknown-elf - -ifndef GCC_VERSION - -GCC_VERSION = 7.4.0 - -endif # GCC_VERSION - -# PSn00bSDK library/include path setup -ifndef PSN00BSDK_LIBS - -# Default assumes libpsn00b is just in the parent dir of the examples dir - -LIBDIRS = -L../../../libpsn00b -INCLUDE = -I../../../libpsn00b/include - -else - -LIBDIRS = -L$(PSN00BSDK_LIBS) -INCLUDE = -I$(PSN00BSDK_LIBS)/include - -endif # PSN00BSDK_LIBS - -# PSn00bSDK toolchain path setup -ifndef GCC_BASE - -ifndef PSN00BSDK_TC - -# Default assumes GCC toolchain is in root of C drive or /usr/local - -ifeq "$(OS)" "Windows_NT" - -GCC_BASE = /c/mipsel-unknown-elf -GCC_BIN = - -else - -GCC_BASE = /usr/local/mipsel-unknown-elf -GCC_BIN = - -endif - -else - -GCC_BASE = $(PSN00BSDK_TC) -GCC_BIN = $(PSN00BSDK_TC)/bin/ - -endif # PSN00BSDK_TC - -endif # GCC_BASE - -CC = $(GCC_BIN)$(PREFIX)-gcc -CXX = $(GCC_BIN)$(PREFIX)-g++ -AS = $(GCC_BIN)$(PREFIX)-as -AR = $(GCC_BIN)$(PREFIX)-ar -LD = $(GCC_BIN)$(PREFIX)-ld -RANLIB = $(GCC_BIN)$(PREFIX)-ranlib \ No newline at end of file diff --git a/examples/graphics/balls/makefile b/examples/graphics/balls/makefile index 70f41bf..44d3d71 100644 --- a/examples/graphics/balls/makefile +++ b/examples/graphics/balls/makefile @@ -1,54 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions + +## Settings + +PSN00BSDK_LIBS ?= ../../../libpsn00b + +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk # Project target name -TARGET = balls.elf +TARGET = balls + +## Files # Searches for C, C++ and S (assembler) files in local directory -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) -# Determine object files -OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ - $(addprefix build/,$(CPPFILES:.cpp=.o)) \ - $(addprefix build/,$(AFILES:.s=.o)) +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) -# Project specific include and library directories -# (use -I for include dirs, -L for library dirs) -INCLUDE += -LIBDIRS += +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += -# Libraries to link -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Build rules -# C compiler flags -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections +#all: iso +all: build/$(TARGET) -# C++ compiler flags -CPPFLAGS = $(CFLAGS) -fno-exceptions +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml -# Assembler flags -AFLAGS = -g -msoft-float +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml -# Linker flags -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.cpp @mkdir -p $(dir $@) - $(CXX) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/graphics/billboard/makefile b/examples/graphics/billboard/makefile index 4f0fcf4..462e73c 100644 --- a/examples/graphics/billboard/makefile +++ b/examples/graphics/billboard/makefile @@ -1,35 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = billboard.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -LIBDIRS += +# Project target name +TARGET = billboard -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/graphics/fpscam/makefile b/examples/graphics/fpscam/makefile index dac1d43..2ddb7fd 100644 --- a/examples/graphics/fpscam/makefile +++ b/examples/graphics/fpscam/makefile @@ -1,35 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = fpscam.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -LIBDIRS += +# Project target name +TARGET = fpscam -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/graphics/gte/makefile b/examples/graphics/gte/makefile index 43b7c5b..f44e72b 100644 --- a/examples/graphics/gte/makefile +++ b/examples/graphics/gte/makefile @@ -1,35 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = gte.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -LIBDIRS += +# Project target name +TARGET = gte -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/graphics/hdtv/makefile b/examples/graphics/hdtv/makefile index 2c7cb71..6659c98 100644 --- a/examples/graphics/hdtv/makefile +++ b/examples/graphics/hdtv/makefile @@ -1,36 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = hdtv.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -LIBDIRS += +# Project target name +TARGET = hdtv -LIBS = -lpsxetc -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/graphics/render2tex/makefile b/examples/graphics/render2tex/makefile index 52d650b..bb0ef3c 100644 --- a/examples/graphics/render2tex/makefile +++ b/examples/graphics/render2tex/makefile @@ -1,35 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = render2tex.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -LIBDIRS += +# Project target name +TARGET = render2tex -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/graphics/rgb24/makefile b/examples/graphics/rgb24/makefile index 3ec9cfe..9fa35ec 100644 --- a/examples/graphics/rgb24/makefile +++ b/examples/graphics/rgb24/makefile @@ -1,37 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = rgb24.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -LIBDIRS += +# Project target name +TARGET = rgb24 -LIBS = -lpsxgpu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - -build/%.o: %.tim + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/makefile b/examples/makefile index e02f7ba..bdc62ce 100644 --- a/examples/makefile +++ b/examples/makefile @@ -13,8 +13,8 @@ DIRS += graphics/balls graphics/billboard graphics/fpscam \ graphics/rgb24 # System related examples -DIRS += system/childexec system/console system/timer \ - system/tty +DIRS += system/childexec system/console system/dynlink \ + system/timer system/tty # Low-level examples DIRS += diff --git a/examples/system/childexec/child.c b/examples/system/childexec/child.c deleted file mode 100644 index 2ddfa73..0000000 --- a/examples/system/childexec/child.c +++ /dev/null @@ -1,326 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -/* OT and Packet Buffer sizes */ -#define OT_LEN 256 -#define PACKET_LEN 1024 - -/* Screen resolution */ -#define SCREEN_XRES 320 -#define SCREEN_YRES 240 - -/* Screen center position */ -#define CENTERX SCREEN_XRES>>1 -#define CENTERY SCREEN_YRES>>1 - - -/* Double buffer structure */ -typedef struct { - DISPENV disp; /* Display environment */ - DRAWENV draw; /* Drawing environment */ - u_long ot[OT_LEN]; /* Ordering table */ - char p[PACKET_LEN]; /* Packet buffer */ -} DB; - -/* Double buffer variables */ -DB db[2]; -int db_active = 0; -char *db_nextpri; - - -/* For easier handling of vertex indices */ -typedef struct { - short v0,v1,v2,v3; -} INDEX; - -/* Cube vertices */ -SVECTOR cube_verts[] = { - { -100, -100, -100, 0 }, - { 100, -100, -100, 0 }, - { -100, 100, -100, 0 }, - { 100, 100, -100, 0 }, - { 100, -100, 100, 0 }, - { -100, -100, 100, 0 }, - { 100, 100, 100, 0 }, - { -100, 100, 100, 0 } -}; - -/* Cube face normals */ -SVECTOR cube_norms[] = { - { 0, 0, -ONE, 0 }, - { 0, 0, ONE, 0 }, - { 0, -ONE, 0, 0 }, - { 0, ONE, 0, 0 }, - { -ONE, 0, 0, 0 }, - { ONE, 0, 0, 0 } -}; - -/* Cube vertex indices */ -INDEX cube_indices[] = { - { 0, 1, 2, 3 }, - { 4, 5, 6, 7 }, - { 5, 4, 0, 1 }, - { 6, 7, 3, 2 }, - { 0, 2, 5, 7 }, - { 3, 1, 6, 4 } -}; - -/* Number of faces of cube */ -#define CUBE_FACES 6 - - -/* Light color matrix */ -/* Each column represents the color matrix of each light source and is */ -/* used as material color when using gte_ncs() or multiplied by a */ -/* source color when using gte_nccs(). 4096 is 1.0 in this matrix */ -/* A column of zeroes disables the light source. */ -MATRIX color_mtx = { - ONE, 0, 0, /* Red */ - ONE, 0, 0, /* Green */ - ONE, 0, 0 /* Blue */ -}; - -/* Light matrix */ -/* Each row represents a vector direction of each light source. */ -/* An entire row of zeroes disables the light source. */ -MATRIX light_mtx = { - /* X, Y, Z */ - -2048 , -2048 , -2048, - 0 , 0 , 0, - 0 , 0 , 0 -}; - -char pad_buff[2][34]; - -/* Function declarations */ -void init(); -void display(); - - -/* Main function */ -int main(int argc, const char *argv[]) { - - int i,p,xy_temp; - - SVECTOR rot = { 0 }; /* Rotation vector for Rotmatrix */ - VECTOR pos = { 0, 0, 400 }; /* Translation vector for TransMatrix */ - MATRIX mtx,lmtx; /* Rotation matrices for geometry and lighting */ - - POLY_F4 *pol4; /* Flat shaded quad primitive pointer */ - - - printf( "Arguments passed: %d\n", argc ); - for( i=0; istat == 0 ) { - - // For digital pad, dual-analog and dual-shock - if( ( pad->type == 0x4 ) || ( pad->type == 0x5 ) || ( pad->type == 0x7 ) ) { - - if( !(pad->btn&PAD_SELECT) ) { - break; - } - - } - - } - - /* Set rotation and translation to the matrix */ - RotMatrix( &rot, &mtx ); - TransMatrix( &mtx, &pos ); - - /* Multiply light matrix by rotation matrix so light source */ - /* won't appear relative to the model's rotation */ - MulMatrix0( &light_mtx, &mtx, &lmtx ); - - /* Set rotation and translation matrix */ - gte_SetRotMatrix( &mtx ); - gte_SetTransMatrix( &mtx ); - - /* Set light matrix */ - gte_SetLightMatrix( &lmtx ); - - /* Make the cube SPEEN */ - rot.vx += 16; - rot.vz += 16; - - - /* Draw the cube */ - pol4 = (POLY_F4*)db_nextpri; - - for( i=0; i>2) > OT_LEN ) - continue; - - /* Initialize a quad primitive */ - setPolyF4( pol4 ); - - /* Set the projected vertices to the primitive */ - gte_stsxy0( &pol4->x0 ); - gte_stsxy1( &pol4->x1 ); - gte_stsxy2( &pol4->x2 ); - - /* Compute the last vertex and set the result */ - gte_ldv0( &cube_verts[cube_indices[i].v3] ); - gte_rtps(); - gte_stsxy( &pol4->x3 ); - - /* Load primitive color even though gte_ncs() doesn't use it. */ - /* This is so the GTE will output a color result with the */ - /* correct primitive code. */ - gte_ldrgb( &pol4->r0 ); - - /* Load the face normal */ - gte_ldv0( &cube_norms[i] ); - - /* Normal Color Single */ - gte_ncs(); - - /* Store result to the primitive */ - gte_strgb( &pol4->r0 ); - - /* Sort primitive to the ordering table */ - addPrim( db[db_active].ot+(p>>2), pol4 ); - - /* Advance to make another primitive */ - pol4++; - - } - - /* Update nextpri variable */ - /* (IMPORTANT if you plan to sort more primitives after this) */ - db_nextpri = (char*)pol4; - - /* Swap buffers and draw the primitives */ - display(); - - } - - StopPAD(); - - return 0; - -} - -void init() { - - /* Reset the GPU, also installs a VSync event handler */ - ResetGraph( 0 ); - - /* Set display and draw environment areas */ - /* (display and draw areas must be separate, otherwise hello flicker) */ - SetDefDispEnv( &db[0].disp, 0, 0, SCREEN_XRES, SCREEN_YRES ); - SetDefDrawEnv( &db[0].draw, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES ); - - /* Enable draw area clear and dither processing */ - setRGB0( &db[0].draw, 63, 0, 127 ); - db[0].draw.isbg = 1; - db[0].draw.dtd = 1; - - - /* Define the second set of display/draw environments */ - SetDefDispEnv( &db[1].disp, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES ); - SetDefDrawEnv( &db[1].draw, 0, 0, SCREEN_XRES, SCREEN_YRES ); - - setRGB0( &db[1].draw, 63, 0, 127 ); - db[1].draw.isbg = 1; - db[1].draw.dtd = 1; - - - /* Apply the drawing environment of the first double buffer */ - PutDrawEnv( &db[0].draw ); - - - /* Clear both ordering tables to make sure they are clean at the start */ - ClearOTagR( db[0].ot, OT_LEN ); - ClearOTagR( db[1].ot, OT_LEN ); - - /* Set primitive pointer address */ - db_nextpri = db[0].p; - - /* Initialize the GTE */ - InitGeom(); - - /* Set GTE offset (recommended method of centering) */ - gte_SetGeomOffset( CENTERX, CENTERY ); - - /* Set screen depth (basically FOV control, W/2 works best) */ - gte_SetGeomScreen( CENTERX ); - - /* Set light ambient color and light color matrix */ - gte_SetBackColor( 63, 63, 63 ); - gte_SetColorMatrix( &color_mtx ); - - InitPAD(pad_buff[0], 34, pad_buff[1], 34); - StartPAD(); - ChangeClearPAD(0); - -} - -void display() { - - /* Wait for GPU to finish drawing and vertical retrace */ - DrawSync( 0 ); - VSync( 0 ); - - /* Swap buffers */ - db_active ^= 1; - db_nextpri = db[db_active].p; - - /* Clear the OT of the next frame */ - ClearOTagR( db[db_active].ot, OT_LEN ); - - /* Apply display/drawing environments */ - PutDrawEnv( &db[db_active].draw ); - PutDispEnv( &db[db_active].disp ); - - /* Enable display */ - SetDispMask( 1 ); - - /* Start drawing the OT of the last buffer */ - DrawOTag( db[1-db_active].ot+(OT_LEN-1) ); - -} \ No newline at end of file diff --git a/examples/system/childexec/child/child.c b/examples/system/childexec/child/child.c new file mode 100644 index 0000000..2ddfa73 --- /dev/null +++ b/examples/system/childexec/child/child.c @@ -0,0 +1,326 @@ +#include +#include +#include +#include +#include +#include +#include + +/* OT and Packet Buffer sizes */ +#define OT_LEN 256 +#define PACKET_LEN 1024 + +/* Screen resolution */ +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +/* Screen center position */ +#define CENTERX SCREEN_XRES>>1 +#define CENTERY SCREEN_YRES>>1 + + +/* Double buffer structure */ +typedef struct { + DISPENV disp; /* Display environment */ + DRAWENV draw; /* Drawing environment */ + u_long ot[OT_LEN]; /* Ordering table */ + char p[PACKET_LEN]; /* Packet buffer */ +} DB; + +/* Double buffer variables */ +DB db[2]; +int db_active = 0; +char *db_nextpri; + + +/* For easier handling of vertex indices */ +typedef struct { + short v0,v1,v2,v3; +} INDEX; + +/* Cube vertices */ +SVECTOR cube_verts[] = { + { -100, -100, -100, 0 }, + { 100, -100, -100, 0 }, + { -100, 100, -100, 0 }, + { 100, 100, -100, 0 }, + { 100, -100, 100, 0 }, + { -100, -100, 100, 0 }, + { 100, 100, 100, 0 }, + { -100, 100, 100, 0 } +}; + +/* Cube face normals */ +SVECTOR cube_norms[] = { + { 0, 0, -ONE, 0 }, + { 0, 0, ONE, 0 }, + { 0, -ONE, 0, 0 }, + { 0, ONE, 0, 0 }, + { -ONE, 0, 0, 0 }, + { ONE, 0, 0, 0 } +}; + +/* Cube vertex indices */ +INDEX cube_indices[] = { + { 0, 1, 2, 3 }, + { 4, 5, 6, 7 }, + { 5, 4, 0, 1 }, + { 6, 7, 3, 2 }, + { 0, 2, 5, 7 }, + { 3, 1, 6, 4 } +}; + +/* Number of faces of cube */ +#define CUBE_FACES 6 + + +/* Light color matrix */ +/* Each column represents the color matrix of each light source and is */ +/* used as material color when using gte_ncs() or multiplied by a */ +/* source color when using gte_nccs(). 4096 is 1.0 in this matrix */ +/* A column of zeroes disables the light source. */ +MATRIX color_mtx = { + ONE, 0, 0, /* Red */ + ONE, 0, 0, /* Green */ + ONE, 0, 0 /* Blue */ +}; + +/* Light matrix */ +/* Each row represents a vector direction of each light source. */ +/* An entire row of zeroes disables the light source. */ +MATRIX light_mtx = { + /* X, Y, Z */ + -2048 , -2048 , -2048, + 0 , 0 , 0, + 0 , 0 , 0 +}; + +char pad_buff[2][34]; + +/* Function declarations */ +void init(); +void display(); + + +/* Main function */ +int main(int argc, const char *argv[]) { + + int i,p,xy_temp; + + SVECTOR rot = { 0 }; /* Rotation vector for Rotmatrix */ + VECTOR pos = { 0, 0, 400 }; /* Translation vector for TransMatrix */ + MATRIX mtx,lmtx; /* Rotation matrices for geometry and lighting */ + + POLY_F4 *pol4; /* Flat shaded quad primitive pointer */ + + + printf( "Arguments passed: %d\n", argc ); + for( i=0; istat == 0 ) { + + // For digital pad, dual-analog and dual-shock + if( ( pad->type == 0x4 ) || ( pad->type == 0x5 ) || ( pad->type == 0x7 ) ) { + + if( !(pad->btn&PAD_SELECT) ) { + break; + } + + } + + } + + /* Set rotation and translation to the matrix */ + RotMatrix( &rot, &mtx ); + TransMatrix( &mtx, &pos ); + + /* Multiply light matrix by rotation matrix so light source */ + /* won't appear relative to the model's rotation */ + MulMatrix0( &light_mtx, &mtx, &lmtx ); + + /* Set rotation and translation matrix */ + gte_SetRotMatrix( &mtx ); + gte_SetTransMatrix( &mtx ); + + /* Set light matrix */ + gte_SetLightMatrix( &lmtx ); + + /* Make the cube SPEEN */ + rot.vx += 16; + rot.vz += 16; + + + /* Draw the cube */ + pol4 = (POLY_F4*)db_nextpri; + + for( i=0; i>2) > OT_LEN ) + continue; + + /* Initialize a quad primitive */ + setPolyF4( pol4 ); + + /* Set the projected vertices to the primitive */ + gte_stsxy0( &pol4->x0 ); + gte_stsxy1( &pol4->x1 ); + gte_stsxy2( &pol4->x2 ); + + /* Compute the last vertex and set the result */ + gte_ldv0( &cube_verts[cube_indices[i].v3] ); + gte_rtps(); + gte_stsxy( &pol4->x3 ); + + /* Load primitive color even though gte_ncs() doesn't use it. */ + /* This is so the GTE will output a color result with the */ + /* correct primitive code. */ + gte_ldrgb( &pol4->r0 ); + + /* Load the face normal */ + gte_ldv0( &cube_norms[i] ); + + /* Normal Color Single */ + gte_ncs(); + + /* Store result to the primitive */ + gte_strgb( &pol4->r0 ); + + /* Sort primitive to the ordering table */ + addPrim( db[db_active].ot+(p>>2), pol4 ); + + /* Advance to make another primitive */ + pol4++; + + } + + /* Update nextpri variable */ + /* (IMPORTANT if you plan to sort more primitives after this) */ + db_nextpri = (char*)pol4; + + /* Swap buffers and draw the primitives */ + display(); + + } + + StopPAD(); + + return 0; + +} + +void init() { + + /* Reset the GPU, also installs a VSync event handler */ + ResetGraph( 0 ); + + /* Set display and draw environment areas */ + /* (display and draw areas must be separate, otherwise hello flicker) */ + SetDefDispEnv( &db[0].disp, 0, 0, SCREEN_XRES, SCREEN_YRES ); + SetDefDrawEnv( &db[0].draw, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES ); + + /* Enable draw area clear and dither processing */ + setRGB0( &db[0].draw, 63, 0, 127 ); + db[0].draw.isbg = 1; + db[0].draw.dtd = 1; + + + /* Define the second set of display/draw environments */ + SetDefDispEnv( &db[1].disp, SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES ); + SetDefDrawEnv( &db[1].draw, 0, 0, SCREEN_XRES, SCREEN_YRES ); + + setRGB0( &db[1].draw, 63, 0, 127 ); + db[1].draw.isbg = 1; + db[1].draw.dtd = 1; + + + /* Apply the drawing environment of the first double buffer */ + PutDrawEnv( &db[0].draw ); + + + /* Clear both ordering tables to make sure they are clean at the start */ + ClearOTagR( db[0].ot, OT_LEN ); + ClearOTagR( db[1].ot, OT_LEN ); + + /* Set primitive pointer address */ + db_nextpri = db[0].p; + + /* Initialize the GTE */ + InitGeom(); + + /* Set GTE offset (recommended method of centering) */ + gte_SetGeomOffset( CENTERX, CENTERY ); + + /* Set screen depth (basically FOV control, W/2 works best) */ + gte_SetGeomScreen( CENTERX ); + + /* Set light ambient color and light color matrix */ + gte_SetBackColor( 63, 63, 63 ); + gte_SetColorMatrix( &color_mtx ); + + InitPAD(pad_buff[0], 34, pad_buff[1], 34); + StartPAD(); + ChangeClearPAD(0); + +} + +void display() { + + /* Wait for GPU to finish drawing and vertical retrace */ + DrawSync( 0 ); + VSync( 0 ); + + /* Swap buffers */ + db_active ^= 1; + db_nextpri = db[db_active].p; + + /* Clear the OT of the next frame */ + ClearOTagR( db[db_active].ot, OT_LEN ); + + /* Apply display/drawing environments */ + PutDrawEnv( &db[db_active].draw ); + PutDispEnv( &db[db_active].disp ); + + /* Enable display */ + SetDispMask( 1 ); + + /* Start drawing the OT of the last buffer */ + DrawOTag( db[1-db_active].ot+(OT_LEN-1) ); + +} \ No newline at end of file diff --git a/examples/system/childexec/child_exe.s b/examples/system/childexec/child_exe.s index 842ac88..66bd0e2 100644 --- a/examples/system/childexec/child_exe.s +++ b/examples/system/childexec/child_exe.s @@ -3,4 +3,4 @@ .global child_exe # Insert spoopypasta .type child_exe, @object child_exe: - .incbin "child.exe" \ No newline at end of file + .incbin "build/child/child.exe" \ No newline at end of file diff --git a/examples/system/childexec/makefile b/examples/system/childexec/makefile index 30229ae..e801739 100644 --- a/examples/system/childexec/makefile +++ b/examples/system/childexec/makefile @@ -1,36 +1,93 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -INCLUDE += -LIBDIRS += +## Settings -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +PSN00BSDK_LIBS ?= ../../../libpsn00b -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -LDFLAGS_P = $(LDFLAGS) -Ttext=0x80010000 -LDFLAGS_C = $(LDFLAGS) -Ttext=0x80030000 +# Project target name +#TARGET = childexec -all: child parent +## Files -child: build/child.o - $(LD) $(LDFLAGS_C) $(LIBDIRS) build/child.o $(LIBS) -o child.elf - elf2x -q child.elf +# Searches for C, C++ and S (assembler) files in local directory +CFILES_PARENT = $(notdir $(wildcard *.c)) +CPPFILES_PARENT = $(notdir $(wildcard *.cpp)) +AFILES_PARENT = $(notdir $(wildcard *.s)) + +CFILES_CHILD = $(notdir $(wildcard child/*.c)) +CPPFILES_CHILD = $(notdir $(wildcard child/*.cpp)) +AFILES_CHILD = $(notdir $(wildcard child/*.s)) + +# Create names for object files +OFILES_PARENT = $(addprefix build/,$(CFILES_PARENT:.c=.o)) \ + $(addprefix build/,$(CPPFILES_PARENT:.cpp=.o)) \ + $(addprefix build/,$(AFILES_PARENT:.s=.o)) +OFILES_CHILD = $(addprefix build/child/,$(CFILES_CHILD:.c=.o)) \ + $(addprefix build/child/,$(CPPFILES_CHILD:.cpp=.o)) \ + $(addprefix build/child/,$(AFILES_CHILD:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +# Relocate the child executable to 0x30000 +LDFLAGS_CHILD = -Ttext=0x80030000 + +## Build rules + +#all: iso +all: build/child/child build/parent + +iso: build/child/child build/parent resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/child/child: $(OFILES_CHILD) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LDFLAGS_CHILD) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe + +build/parent: $(OFILES_PARENT) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe + +build/child/%.o: child/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/child/%.o: child/%.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/child/%.o: child/%.s + @mkdir -p $(dir $@) + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ -parent: build/parent.o build/child_exe.o - $(LD) $(LDFLAGS_P) $(LIBDIRS) build/parent.o build/child_exe.o $(LIBS) -o parent.elf - elf2x -q parent.elf - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build parent.elf parent.exe child.elf child.exe + rm -rf build diff --git a/examples/system/console/makefile b/examples/system/console/makefile index 1ee638f..addf02a 100644 --- a/examples/system/console/makefile +++ b/examples/system/console/makefile @@ -1,54 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions + +## Settings + +PSN00BSDK_LIBS ?= ../../../libpsn00b + +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk # Project target name -TARGET = console.elf +TARGET = console + +## Files # Searches for C, C++ and S (assembler) files in local directory -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) -# Determine object files -OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ - $(addprefix build/,$(CPPFILES:.cpp=.o)) \ - $(addprefix build/,$(AFILES:.s=.o)) +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) -# Project specific include and library directories -# (use -I for include dirs, -L for library dirs) -INCLUDE += -LIBDIRS += +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += -# Libraries to link -LIBS = -lpsxsio -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Build rules -# C compiler flags -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections +#all: iso +all: build/$(TARGET) -# C++ compiler flags -CPPFLAGS = $(CFLAGS) -fno-exceptions +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml -# Assembler flags -AFLAGS = -g -msoft-float +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml -# Linker flags -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.cpp @mkdir -p $(dir $@) - $(CXX) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/system/timer/makefile b/examples/system/timer/makefile index a8defe7..c418af4 100644 --- a/examples/system/timer/makefile +++ b/examples/system/timer/makefile @@ -1,35 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions -TARGET = timer.elf +## Settings -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +PSN00BSDK_LIBS ?= ../../../libpsn00b -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk -INCLUDE += -LIBDIRS += +# Project target name +TARGET = timer -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Files -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +# Searches for C, C++ and S (assembler) files in local directory +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) + +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +#all: iso +all: build/$(TARGET) + +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build diff --git a/examples/system/tty/makefile b/examples/system/tty/makefile index 43893d5..381aa41 100644 --- a/examples/system/tty/makefile +++ b/examples/system/tty/makefile @@ -1,54 +1,65 @@ -include ../../examples-setup.mk +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions + +## Settings + +PSN00BSDK_LIBS ?= ../../../libpsn00b + +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk # Project target name -TARGET = tty.elf +TARGET = tty + +## Files # Searches for C, C++ and S (assembler) files in local directory -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) +CFILES = $(notdir $(wildcard *.c)) +CPPFILES= $(notdir $(wildcard *.cpp)) +AFILES = $(notdir $(wildcard *.s)) -# Determine object files -OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ - $(addprefix build/,$(CPPFILES:.cpp=.o)) \ - $(addprefix build/,$(AFILES:.s=.o)) +# Create names for object files +OFILES = $(addprefix build/,$(CFILES:.c=.o)) \ + $(addprefix build/,$(CPPFILES:.cpp=.o)) \ + $(addprefix build/,$(AFILES:.s=.o)) -# Project specific include and library directories -# (use -I for include dirs, -L for library dirs) -INCLUDE += -LIBDIRS += +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += -# Libraries to link -LIBS = -lpsxgpu -lpsxgte -lpsxspu -lpsxetc -lpsxapi -lc +## Build rules -# C compiler flags -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections +#all: iso +all: build/$(TARGET) -# C++ compiler flags -CPPFLAGS = $(CFLAGS) -fno-exceptions +iso: build/$(TARGET) resources + $(MKPSXISO) -y -q iso.xml -# Assembler flags -AFLAGS = -g -msoft-float +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml -# Linker flags -LDFLAGS = -g -Ttext=0x80010000 -gc-sections \ - -T $(GCC_BASE)/$(PREFIX)/lib/ldscripts/elf32elmip.x +build/$(TARGET): $(OFILES) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXE) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - build/%.o: %.c @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(CFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.cpp @mkdir -p $(dir $@) - $(CXX) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CXX) $(CPPFLAGS_EXE) $(INCLUDE) -c $< -o $@ + build/%.o: %.s @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - + $(CC) $(AFLAGS_EXE) $(INCLUDE) -c $< -o $@ + clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) + rm -rf build -- cgit v1.2.3 From f2fc18f82dd7900465d6ab3ae2080726d5589d39 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:37:03 +0000 Subject: Added dynamic linker API and example, updated README and changelog --- README.md | 35 +- changelog.txt | 29 ++ examples/system/dynlink/display.c | 70 +++ examples/system/dynlink/iso.xml | 30 ++ examples/system/dynlink/library/ball16c.h | 16 + examples/system/dynlink/library/balls.c | 116 +++++ examples/system/dynlink/library/cube.c | 154 +++++++ examples/system/dynlink/library/dll_common.h | 30 ++ examples/system/dynlink/main.c | 225 +++++++++ examples/system/dynlink/makefile | 95 ++++ examples/system/dynlink/system.cnf | 4 + libpsn00b/include/dlfcn.h | 176 +++++++ libpsn00b/include/elf.h | 138 ++++++ libpsn00b/psxetc/_dl_resolve_wrapper.s | 54 +++ libpsn00b/psxetc/dl.c | 665 +++++++++++++++++++++++++++ libpsn00b/psxetc/readme.txt | 16 +- 16 files changed, 1836 insertions(+), 17 deletions(-) create mode 100644 examples/system/dynlink/display.c create mode 100644 examples/system/dynlink/iso.xml create mode 100644 examples/system/dynlink/library/ball16c.h create mode 100644 examples/system/dynlink/library/balls.c create mode 100644 examples/system/dynlink/library/cube.c create mode 100644 examples/system/dynlink/library/dll_common.h create mode 100644 examples/system/dynlink/main.c create mode 100644 examples/system/dynlink/makefile create mode 100644 examples/system/dynlink/system.cnf create mode 100644 libpsn00b/include/dlfcn.h create mode 100644 libpsn00b/include/elf.h create mode 100644 libpsn00b/psxetc/_dl_resolve_wrapper.s create mode 100644 libpsn00b/psxetc/dl.c (limited to 'examples') diff --git a/README.md b/README.md index af9fc75..7b794c7 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ and performance reasons. ## Notable features -As of January 5, 2021 + +As of August 16, 2021 * Extensive GPU support with polygon, line and sprite primitives, high-speed DMA transfers for VRAM data and ordering tables. All video modes for both @@ -62,7 +63,12 @@ As of January 5, 2021 and querying files and directories. Supports directories containing more than 30 files (classic ISO9660 only, no Rock Ridge or Joliet extensions) and also supports reading new sessions on a multi-session disc. - + +* Experimental support for compiling separate sections of an executable into + shared library files (DLLs) and linking them dynamically at runtime, plus + support for function and variable introspection by loading a map file + generated at build time. + * Uses Sony SDK library syntax for familiarity to experienced programmers and to make porting existing homebrew projects to PSn00bSDK easier. @@ -91,15 +97,15 @@ You may set one of the following variables either with set/export or on the make command line, to specify various parameters in building PSn00bSDK and projects made with it as you see fit. -* ``GCC_VERSION`` specifies the GCC version number. This is only required for - building the libc library. If not defined, the default value of 7.4.0 is - used. * ``PSN00BSDK_TC`` specifies the base directory of the GCC toolchain to build PSn00bSDK with, otherwise the makefile assumes you have the path to the toolchain binaries in one of your PATH directories. Alternatively, ``GCC_BASE`` can be specified in place of ``PSN00BSDK_TC``. If not specified psn00bsdk-setup.mk assumes the toolchain is at C:\mipsel-unknown-elf in Win32 or /usr/local/mipsel-unknown-elf in Linux. +* ``GCC_VERSION`` specifies the GCC version number. This is only used for + building the libc library. If not defined, it will be auto-detected by + searching ``PSN00BSDK_TC`` or ``GCC_BASE`` for a valid GCC installation. * ``PSN00BSDK_LIBS`` specifies the target directory you wish to install the compiled libpsn00b libraries to. If not defined, compiled libraries are consolidated to the libpsn00b directory and @@ -158,8 +164,6 @@ programs with PSn00bSDK within the Command Prompt. (see toolchain.txt for details). Export a variable named `PSN00BSDK_TC` containing a path to the installed toolchain's base directory if you've installed the toolchain in a location other than /usr/local. - Also export a `GCC_VERSION` variable if you're using a version of GCC - other than 7.4.0. 3. Clone from PSn00bSDK source with `git clone https://github.com/lameguy64/psn00bsdk` 4. Enter tools directory and run `make`, then `make install` to consolidate @@ -188,14 +192,25 @@ for the PlayStation. The tutorials should still apply to PSn00bSDK. * psxspu: Plenty of work to be done. Hardware timer driven sound/music system may need to be implemented (an equivalent to the Ss* series of functions in libspu basically). Need to figure out the correct frequency - table for playing sounds in musical note notation. - + table for playing sounds in musical note notation. Functions that make use of + the SPU RAM interrupt feature to play or capture streamed audio should also + be added. + * psxcd: Implement a command queue mechanism for the CD-ROM? -* Support for MDEC. +* libc: Improve the memory allocation framework with multiple allocators, GC + and maybe helpers to manage swapping between main RAM and VRAM/SPU RAM. + +* Support for MDEC, and tooling to transcode videos to .STR files (either + reimplementing the container and compression format used by the Sony SDK, or + a custom format with better compression). * Pad and memory card libraries that don't use the BIOS routines. +* Switching to, or adding support for a build system that's easier to use with + IDEs and/or under Windows, which would also make asset conversion pipelines + easier to manage. CMake might be a good option here but more discussion is + needed. ## Usage terms diff --git a/changelog.txt b/changelog.txt index 666e3c5..15bbb85 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,35 @@ PSn00bSDK changelog Items that are lower in the log are more recently implemented. +08-16-2021 by spicyjpeg: + +* psxetc: Added dynamic linking APIs (dl*, DL_*), implemented in dl.c and + _dl_resolve_wrapper.s. The APIs can be accessed by including dlfcn.h, while + another header (elf.h) provides structs and enums to help with manual parsing + of library headers. + +* libc: Removed _mem_init.s, improved heap initialization code. Added support + for setting how much RAM to use for the stack (by defining STACK_MAX_SIZE) + and for overriding/replacing the default memory allocator. + +* psxapi: Added wrapper around BIOS function FlushCache(). + +* psxspu: Fixed wrong bounds check in SpuSetTransferStartAddr(). + +* examples: Added an example showing how to compile dynamically-loaded + libraries (DLLs) and load them at runtime using the new dynamic linker. + +* libpsn00b: Added libpsn00b/ldscripts directory and linker scripts for both + executables and DLLs. Also fixed several broken header files (psxgpu.h not + including sys/types.h, missing scanf() declarations, ...). + +* Rewritten most makefiles (both for libraries and examples) and centralized + all compiler flags into a single psn00bsdk-setup.mk file for consistency and + easier editing. Added flags to build libpsn00b without gp-relative addressing + for compatibility with the dynamic linker. Also added nm commands to generate + symbol maps required by the linker. + + 07-01-2021 by Lameguy64: * Libpsn00b: Added int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, diff --git a/examples/system/dynlink/display.c b/examples/system/dynlink/display.c new file mode 100644 index 0000000..d8ad3ed --- /dev/null +++ b/examples/system/dynlink/display.c @@ -0,0 +1,70 @@ +/* + * PSn00bSDK dynamic linker example (utilities) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#include + +#include "library/dll_common.h" + +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +/* Display/GPU context utilities */ + +void init_context(CONTEXT *ctx) { + DB *db; + + ResetGraph(0); + ctx->xres = SCREEN_XRES; + ctx->yres = SCREEN_YRES; + ctx->db_active = 0; + + db = &(ctx->db[0]); + SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), 63, 0, 127); + db->draw.isbg = 1; + db->draw.dtd = 1; + + db = &(ctx->db[1]); + SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), 63, 0, 127); + db->draw.isbg = 1; + db->draw.dtd = 1; + + // Set up the ordering tables and primitive buffers. + db = &(ctx->db[0]); + ctx->db_nextpri = db->p; + ClearOTagR((u_long *) db->ot, OT_LEN); + + PutDrawEnv(&(db->draw)); + //PutDispEnv(&(db->disp)); + + db = &(ctx->db[1]); + ClearOTagR((u_long *) db->ot, OT_LEN); + + // Create a text stream at the top of the screen. + FntLoad(960, 0); + FntOpen(4, 12, 312, 32, 2, 256); +} + +void display(CONTEXT *ctx) { + DB *db; + + DrawSync(0); + VSync(0); + ctx->db_active ^= 1; + + db = &(ctx->db[ctx->db_active]); + ctx->db_nextpri = db->p; + ClearOTagR((u_long *) db->ot, OT_LEN); + + PutDrawEnv(&(db->draw)); + PutDispEnv(&(db->disp)); + SetDispMask(1); + + db = &(ctx->db[!ctx->db_active]); + DrawOTag((u_long *) &(db->ot[OT_LEN - 1])); +} diff --git a/examples/system/dynlink/iso.xml b/examples/system/dynlink/iso.xml new file mode 100644 index 0000000..76cc8fc --- /dev/null +++ b/examples/system/dynlink/iso.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + diff --git a/examples/system/dynlink/library/ball16c.h b/examples/system/dynlink/library/ball16c.h new file mode 100644 index 0000000..c79f273 --- /dev/null +++ b/examples/system/dynlink/library/ball16c.h @@ -0,0 +1,16 @@ +unsigned int ball16c_size=192; +unsigned char ball16c[] = { +0x10,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0xc0,0x03,0x10, +0x01,0x10,0x00,0x01,0x00,0x00,0x00,0x31,0xc6,0x73,0xce,0x94,0xd2,0x07,0x9d, +0xd6,0xda,0x38,0xe3,0xef,0xbd,0x9b,0xef,0x8c,0xb1,0xc6,0x98,0xde,0xfb,0x4a, +0xa9,0xa4,0x90,0xad,0xb5,0x00,0x00,0x8c,0x00,0x00,0x00,0xc0,0x03,0x00,0x01, +0x04,0x00,0x10,0x00,0x00,0x00,0x10,0x22,0x12,0x02,0x00,0x00,0x00,0x10,0x32, +0x33,0x23,0x11,0x04,0x00,0x00,0x23,0x55,0x66,0x35,0x72,0x47,0x00,0x20,0x52, +0x86,0x68,0x36,0x12,0x97,0x0a,0x20,0x65,0xbb,0x8b,0x36,0x12,0x91,0x04,0x31, +0x85,0xbb,0x68,0x35,0x12,0x97,0xdc,0x32,0x86,0x8b,0x56,0x35,0x73,0x97,0xa4, +0x32,0x66,0x68,0x55,0x23,0x71,0x9e,0xac,0x32,0x65,0x56,0x33,0x13,0x71,0xce, +0xa4,0x21,0x33,0x33,0x23,0x11,0xe7,0xc9,0xd4,0x12,0x22,0x22,0x13,0x71,0xe7, +0xc9,0xda,0x10,0x17,0x11,0x77,0x77,0x9e,0x4c,0x0d,0x40,0x77,0x71,0xe7,0x9e, +0xc9,0xd4,0x0d,0x00,0x94,0x99,0x99,0xcc,0x4c,0xda,0x00,0x00,0xa0,0xc4,0xc4, +0x44,0xda,0x0d,0x00,0x00,0x00,0xd0,0xaa,0xda,0x0d,0x00,0x00 +}; diff --git a/examples/system/dynlink/library/balls.c b/examples/system/dynlink/library/balls.c new file mode 100644 index 0000000..2a4d9f4 --- /dev/null +++ b/examples/system/dynlink/library/balls.c @@ -0,0 +1,116 @@ +/* + * PSn00bSDK dynamic linker example (DLL 2) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "dll_common.h" +#include "ball16c.h" + +/* Balls data */ + +typedef struct { + int16_t x, y; + int16_t xdir, ydir; + uint8_t r, g, b, p; +} BALL_TYPE; + +#define MAX_BALLS 512 + +/* Functions called by the main executable */ + +// NOTE: DLLs have no main(), _start() or other defined entry point. C++ global +// objects are automatically constructed when loading the library, and their +// destructors are called when unloading it via dlclose(). Other than that, the +// main executable can freely call DLL functions in any order, however it's +// still recommended (at least for C code) to have an init() function to e.g. +// initialize variables or hardware. + +static uint32_t frame = 0; +static BALL_TYPE balls[MAX_BALLS]; +static TIM_IMAGE ball_tim; + +void init(CONTEXT *ctx) { + GetTimInfo((u_long *) ball16c, &ball_tim); + + LoadImage(ball_tim.prect, ball_tim.paddr); + if (ball_tim.mode & 8) + LoadImage(ball_tim.crect, ball_tim.caddr); + + // Initialize the balls by giving them a random initial position, velocity + // and color. + for (uint32_t i = 0; i < MAX_BALLS; i++) { + BALL_TYPE *b = &(balls[i]); + + b->x = rand() % (ctx->xres - 16); + b->y = rand() % (ctx->yres - 16); + b->xdir = ((rand() & 1) ? 1 : -1) * ((rand() % 3) + 1); + b->ydir = ((rand() & 1) ? 1 : -1) * ((rand() % 3) + 1); + b->r = rand() & 0xff; + b->g = rand() & 0xff; + b->b = rand() & 0xff; + } +} + +void render(CONTEXT *ctx, uint16_t buttons) { + DB *db = &(ctx->db[ctx->db_active]); + SPRT_16 *sprt = (SPRT_16 *) ctx->db_nextpri; + + for (uint32_t i = 0; i < MAX_BALLS; i++) { + BALL_TYPE *b = &(balls[i]); + + setSprt16(sprt); + + setXY0(sprt, b->x, b->y); + setRGB0(sprt, b->r, b->g, b->b); + setUV0(sprt, 0, 0); + setClut(sprt, ball_tim.crect->x, ball_tim.crect->y); + + addPrim(&(db->ot[OT_LEN - 1]), sprt); + sprt++; + + // Update the ball's velocity and acceleration, moving them slower if + // cross is pressed. + int16_t step = !(buttons & PAD_CROSS) ? 1 : 0; + b->x += b->xdir >> step; + b->y += b->ydir >> step; + + if ( + (b->x < 0) || + ((b->x + 16) > ctx->xres) + ) + b->xdir *= -1; + if ( + (b->y < 0) || + ((b->y + 16) > ctx->yres) + ) + b->ydir *= -1; + } + + ctx->db_nextpri = (uint8_t *) sprt; + + // Add a TPAGE "primitive" to ensure the GPU finds the ball texture. + DR_TPAGE *tpri = (DR_TPAGE * ) ctx->db_nextpri; + + setDrawTPage( + tpri, + 0, + 0, + getTPage(0, 0, ball_tim.prect->x, ball_tim.prect->y) + ); + addPrim(&(db->ot[OT_LEN - 1]), tpri); + tpri++; + + ctx->db_nextpri = (uint8_t *) tpri; + + // Due to our custom resolver, this will actually call dll_printf() in the + // main executable. + printf("DRAWING BALLS, COUNTER=%d\n", frame++); +} diff --git a/examples/system/dynlink/library/cube.c b/examples/system/dynlink/library/cube.c new file mode 100644 index 0000000..57f3e56 --- /dev/null +++ b/examples/system/dynlink/library/cube.c @@ -0,0 +1,154 @@ +/* + * PSn00bSDK dynamic linker example (DLL 1) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#include +#include +#include +#include +#include +#include + +#include "dll_common.h" + +/* Cube model */ + +typedef struct { + int16_t v0, v1, v2, v3; +} INDEX; + +static SVECTOR cube_verts[] = { + { -100, -100, -100, 0 }, + { 100, -100, -100, 0 }, + { -100, 100, -100, 0 }, + { 100, 100, -100, 0 }, + { 100, -100, 100, 0 }, + { -100, -100, 100, 0 }, + { 100, 100, 100, 0 }, + { -100, 100, 100, 0 } +}; +static SVECTOR cube_norms[] = { + { 0, 0, -ONE, 0 }, + { 0, 0, ONE, 0 }, + { 0, -ONE, 0, 0 }, + { 0, ONE, 0, 0 }, + { -ONE, 0, 0, 0 }, + { ONE, 0, 0, 0 } +}; +static INDEX cube_indices[] = { + { 0, 1, 2, 3 }, + { 4, 5, 6, 7 }, + { 5, 4, 0, 1 }, + { 6, 7, 3, 2 }, + { 0, 2, 5, 7 }, + { 3, 1, 6, 4 } +}; + +#define CUBE_FACES 6 + +/* Light matrices */ + +// Each column represents the color matrix of each light source and is used as +// material color when using gte_ncs() or multiplied by a source color when +// using gte_nccs(). 4096 is 1.0 in this matrix. A column of zeroes disables +// the light source. +static MATRIX color_mtx = { + ONE, 0, 0, // R + ONE, 0, 0, // G + ONE, 0, 0 // B +}; + +// Each row represents a vector direction of each light source. An entire row +// of zeroes disables the light source. +static MATRIX light_mtx = { + -2048, -2048, -2048, + 0, 0, 0, + 0, 0, 0 +}; + +/* Functions called by the main executable */ + +// NOTE: DLLs have no main(), _start() or other defined entry point. C++ global +// objects are automatically constructed when loading the library, and their +// destructors are called when unloading it via dlclose(). Other than that, the +// main executable can freely call DLL functions in any order, however it's +// still recommended (at least for C code) to have an init() function to e.g. +// initialize variables or hardware. + +static uint32_t frame = 0; +static SVECTOR rot = { 0 }; +static VECTOR pos = { 0, 0, 400 }; +static MATRIX mtx, lmtx; + +void init(CONTEXT *ctx) { + InitGeom(); + + gte_SetGeomOffset(ctx->xres / 2, ctx->yres / 2); + gte_SetGeomScreen(ctx->xres / 2); + gte_SetBackColor(63, 63, 63); + gte_SetColorMatrix(&color_mtx); +} + +void render(CONTEXT *ctx, uint16_t buttons) { + RotMatrix(&rot, &mtx); + TransMatrix(&mtx, &pos); + MulMatrix0(&light_mtx, &mtx, &lmtx); + + gte_SetRotMatrix(&mtx); + gte_SetTransMatrix(&mtx); + gte_SetLightMatrix(&lmtx); + + // Spin the cube faster is cross is pressed. + int16_t step = !(buttons & PAD_CROSS) ? 32 : 16; + rot.vx += step; + rot.vz += step; + + DB *db = &(ctx->db[ctx->db_active]); + POLY_F4 *pol4 = (POLY_F4 *) ctx->db_nextpri; + + for (uint32_t i = 0; i < CUBE_FACES; i++) { + int32_t p; + + gte_ldv3( + &cube_verts[cube_indices[i].v0], + &cube_verts[cube_indices[i].v1], + &cube_verts[cube_indices[i].v2] + ); + + gte_rtpt(); + gte_nclip(); + gte_stopz(&p); + if (p < 0) + continue; + + gte_avsz4(); + gte_stotz(&p); + if ((p >> 2) > OT_LEN) + continue; + + setPolyF4(pol4); + + gte_stsxy0(&(pol4->x0)); + gte_stsxy1(&(pol4->x1)); + gte_stsxy2(&(pol4->x2)); + + gte_ldv0(&(cube_verts[cube_indices[i].v3])); + gte_rtps(); + gte_stsxy(&(pol4->x3)); + + gte_ldrgb(&(pol4->r0)); + gte_ldv0(&(cube_norms[i])); + gte_ncs(); + gte_strgb(&(pol4->r0)); + + addPrim(&(db->ot[p >> 2]), pol4); + pol4++; + } + + ctx->db_nextpri = (uint8_t *) pol4; + + // Due to our custom resolver, this will actually call dll_printf() in the + // main executable. + printf("DRAWING CUBE, COUNTER=%d\n", frame++); +} diff --git a/examples/system/dynlink/library/dll_common.h b/examples/system/dynlink/library/dll_common.h new file mode 100644 index 0000000..4f9314b --- /dev/null +++ b/examples/system/dynlink/library/dll_common.h @@ -0,0 +1,30 @@ +/* + * PSn00bSDK dynamic linker example (shared header) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#ifndef __DLL_COMMON_H +#define __DLL_COMMON_H + +#include + +/* Common structures shared by the main executable and DLLs */ + +#define OT_LEN 256 +#define PACKET_LEN 16384 + +typedef struct { + DISPENV disp; + DRAWENV draw; + uint32_t ot[OT_LEN]; + uint8_t p[PACKET_LEN]; +} DB; + +typedef struct { + uint16_t xres, yres; + DB db[2]; + uint32_t db_active; + uint8_t *db_nextpri; +} CONTEXT; + +#endif diff --git a/examples/system/dynlink/main.c b/examples/system/dynlink/main.c new file mode 100644 index 0000000..70314da --- /dev/null +++ b/examples/system/dynlink/main.c @@ -0,0 +1,225 @@ +/* + * PSn00bSDK dynamic linker example (main executable) + * (C) 2021 spicyjpeg - MPL licensed + * + * This example shows how to use the psxetc DL_*() APIs to obtain information + * about the executable's symbols at runtime. This is accomplished by parsing a + * symbol map file, which is generated at compile time by GCC's nm command and + * included into the CD image. The symbol map lists all functions/variables in + * the executable and their type, address and size. Currently only searching + * for a symbol's address by its name (DL_GetSymbolByName()) is supported, + * however this may be expanded in the future. + * + * Being able to introspect local symbols at runtime, in turn, allows us to use + * the dl*() set of APIs to load, link and execute code from an external file + * (compiled with the dll.ld linker script). A dynamically-loaded library can + * reference and access any non-static function or variable within the main + * executable (and the libraries the main executable has been compiled with); + * the dynamic linker will automatically patch the DLL's code and resolve these + * references so that they point to the addresses listed in the map file. DLLs + * also have their own symbol tables, and any symbol in a DLL is accessible to + * the main executable through dlsym(). + * + * This example shows how DLLs can be loaded and unloaded at any time. Pressing + * START will unload the current DLL and load an alternate one on-the-fly. A + * custom resolver is also employed to tap into the DLL patching process and + * override the printf() function referenced by the DLLs with a different + * implementation, so the debug output from the DLLs can be redirected to the + * on-screen overlay. + * + * Dynamic linking has plenty of practical applications. It can be e.g. used to + * greatly reduce RAM usage by splitting off a large executable into a "common" + * executable (containing SDK APIs as well as frequently-used symbols such as + * rendering buffers) and many smaller DLLs, which can then be swapped in and + * out depending on which functions are needed. It can also be useful to run + * code that hasn't been compiled at the same time as the main executable, such + * as plugins/mods/patches stored on a memory card. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "library/dll_common.h" + +// List all SDK functions used by the DLLs in a dummy array to ensure GCC won't +// strip them. By default the linker removes all functions unused in the +// executable itself, but we (obviously) need them to be present for a DLL to +// call them. Placing this array in the .dummy section (as defined in the +// PSn00bSDK linker script) ensures it won't be stripped away until all +// functions are in place. +const void *const DO_NOT_STRIP[] __attribute__((section(".dummy"))) = { + &rand, + &InitGeom, + &RotMatrix, + &TransMatrix, + &MulMatrix0 +}; + +static const char *const DLL_FILENAMES[] = { + "cdrom:CUBE.DLL;1", + "cdrom:BALLS.DLL;1" +}; + +#define DLL_COUNT 2 + +void init_context(CONTEXT *ctx); +void display(CONTEXT *ctx); + +/* Symbol overriding example */ + +static volatile uint32_t resolve_counter = 0; + +// This function will override printf(), i.e. DLLs will use this instead of the +// "real" printf() present in the executable, thanks to the custom resolver +// defined below. We'll use this to redirect the DLL's output to the debug text +// window. +int dll_printf(const char *format, ...) { + va_list args; + va_start(args, format); + + char buffer[256]; + int32_t return_value = vsprintf(buffer, format, args); + va_end(args); + + FntPrint(-1, "DLL: %s", buffer); + //FntFlush(-1); + + return return_value; +} + +// This function will be called by the linker for each undefined symbol +// (function or variable) in the DLL, and should return the address of the +// symbol so the dynamic linker can patch it in. The default resolver tries to +// find them in the currently loaded symbol map using DL_GetSymbolByName(). +void *custom_resolver(DLL *dll, const char *name) { + if (!strcmp(name, "printf")) { + printf("Resolving printf() -> dll_printf() (#%d)\n", resolve_counter++); + return &dll_printf; + } + + printf("Resolving %s() (#%d)\n", name, resolve_counter++); + + // Custom resolvers should always fall back to the default behavior. + return DL_GetSymbolByName(name); +} + +/* Global variables and structs */ + +// Define a struct to store pointers to a DLL's functions into. This is not +// strictly required, however looking up symbols is a relatively slow operation +// and the pointers returned by dlsym() should be saved and reused as much as +// possible. +typedef struct { + void (*init)(CONTEXT *); + void (*render)(CONTEXT *, uint16_t buttons); +} DLL_API; + +static DLL *dll = 0; +static DLL_API dll_api; +static CONTEXT ctx; + +/* Main */ + +#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } +#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } + +void load_dll(const char *filename) { + if (dll) + dlclose(dll); + + SHOW_STATUS("LOADING %s\n", filename); + + dll = dlopen(filename, RTLD_LAZY); + if (!dll) + SHOW_ERROR("FAILED TO LOAD %s\n%s\n", filename, dlerror()); + + dll_api.init = dlsym(dll, "init"); + dll_api.render = dlsym(dll, "render"); + + printf("DLL init() @ %08x, render() @ %08x\n", dll_api.init, dll_api.render); + + // Unfortunately, due to how position-independent code works, function + // pointers returned by dlsym() can't be called directly. We have to use + // the DL_CALL() macro instead, which sets up register $t9 to ensure the + // function can locate and reference the DLL's relocation table. + DL_CALL(dll_api.init, &ctx); + +} + +int main(int argc, const char* argv[]) { + // As DL_LoadSymbolMap() and dlopen() rely on BIOS file APIs, the BIOS CD + // driver must be initialized by calling _InitCd() prior to loading the + // symbol map (but after setting up the GPU, for some reason). + init_context(&ctx); + + SHOW_STATUS("INITIALIZING CD\n"); + _InitCd(); + + SHOW_STATUS("LOADING SYMBOL MAP\n"); + + if (!DL_LoadSymbolMap("cdrom:MAIN.MAP;1")) + SHOW_ERROR("FAILED TO LOAD SYMBOL MAP\n%s\n", dlerror()); + + // Try to obtain a reference to a local function. + void (*_display)() = DL_GetSymbolByName("display"); + if (!_display) + SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\n%s\n", dlerror()); + + printf("Symbol map test, display() @ %08x\n", _display); + + // Set up controller polling. + uint8_t pad_buff[2][34]; + InitPAD(pad_buff[0], 34, pad_buff[1], 34); + StartPAD(); + ChangeClearPAD(0); + + // Set up the custom resolver and load the first DLL. + DL_SetResolveCallback(&custom_resolver); + load_dll(DLL_FILENAMES[0]); + + uint32_t dll_active = 0; + uint16_t last_buttons = 0xffff; + + while (1) { + // Use the currently loaded DLL to render a frame. + DL_CALL(dll_api.render, &ctx, last_buttons); + + FntPrint(-1, "MAIN: DLL ADDR=%08x SIZE=%d\n", dll->ptr, dll->size); + FntPrint(-1, "MAIN: %d FUNCTIONS RESOLVED\n", resolve_counter); + FntPrint(-1, "[START] LOAD NEXT DLL\n"); + FntFlush(-1); + display(&ctx); + + // Check if a compatible controller is connected and if START has been + // pressed (i.e. wasn't previously held down, but now is). If so, + // switch the active DLL. + PADTYPE *pad = (PADTYPE *) pad_buff[0]; + if (pad->stat) + continue; + if ((pad->type != 4) && (pad->type != 5) && (pad->type != 7)) + continue; + + if ((last_buttons & PAD_START) && !(pad->btn & PAD_START)) { + dll_active++; + dll_active %= DLL_COUNT; + + load_dll(DLL_FILENAMES[dll_active]); + } + + last_buttons = pad->btn; + } + + //dlclose(dll); + //DL_UnloadSymbolMap(); + return 0; +} diff --git a/examples/system/dynlink/makefile b/examples/system/dynlink/makefile new file mode 100644 index 0000000..b3fb298 --- /dev/null +++ b/examples/system/dynlink/makefile @@ -0,0 +1,95 @@ +# PSn00bSDK makefile template +# Part of the PSn00bSDK Project +# 2019 - 2021 Lameguy64 / Meido-Tek Productions + +## Settings + +PSN00BSDK_LIBS ?= ../../../libpsn00b + +# Edit this to point to psn00bsdk-setup.mk, or copy that over to your project's +# root folder (it only depends on environment variables). +include ../../../psn00bsdk-setup.mk + +# Project target name +#TARGET = dynlink + +## Files + +# Searches for C, C++ and S (assembler) files in local directory +CFILES_MAIN = $(notdir $(wildcard *.c)) +CPPFILES_MAIN = $(notdir $(wildcard *.cpp)) +AFILES_MAIN = $(notdir $(wildcard *.s)) + +CFILES_DLL = $(notdir $(wildcard library/*.c)) +CPPFILES_DLL = $(notdir $(wildcard library/*.cpp)) +AFILES_DLL = $(notdir $(wildcard library/*.s)) + +# Create names for object files +OFILES_MAIN = $(addprefix build/,$(CFILES_MAIN:.c=.o)) \ + $(addprefix build/,$(CPPFILES_MAIN:.cpp=.o)) \ + $(addprefix build/,$(AFILES_MAIN:.s=.o)) +OFILES_CUBE = build/library/cube.o +OFILES_BALLS = build/library/balls.o + +# Project specific includes and libraries +# (use -I for include dirs, -L for library dirs, -l for static libraries) +INCLUDE += +LIBDIRS += +LIBS += + +## Build rules + +all: iso +#all: build/library/cube build/library/balls build/main + +iso: build/library/cube build/library/balls build/main resources + $(MKPSXISO) -y -q iso.xml + +resources: + # Add commands to build/convert your assets here + #$(LZPACK) data.xml + +build/library/cube: $(OFILES_CUBE) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_DLL) $^ -o $@ + #$(NM) -f posix -l -n $@ >$@.map + $(OBJCOPY) -O binary $@ $@.dll + +build/library/balls: $(OFILES_BALLS) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_DLL) $^ -o $@ + #$(NM) -f posix -l -n $@ >$@.map + $(OBJCOPY) -O binary $@ $@.dll + +build/main: $(OFILES_MAIN) + @mkdir -p $(dir $@) + $(LD) $(LDFLAGS_EXEDYN) $(LIBDIRS) $^ $(LIBS) -o $@ + $(NM) -f posix -l -n $@ >$@.map + $(ELF2X) -q $@ $@.exe + +build/library/%.o: library/%.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_DLL) $(INCLUDE) -c $< -o $@ + +build/library/%.o: library/%.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_DLL) $(INCLUDE) -c $< -o $@ + +build/library/%.o: library/%.s + @mkdir -p $(dir $@) + $(CC) $(AFLAGS_DLL) $(INCLUDE) -c $< -o $@ + +build/%.o: %.c + @mkdir -p $(dir $@) + $(CC) $(CFLAGS_EXEDYN) $(INCLUDE) -c $< -o $@ + +build/%.o: %.cpp + @mkdir -p $(dir $@) + $(CXX) $(CPPFLAGS_EXEDYN) $(INCLUDE) -c $< -o $@ + +build/%.o: %.s + @mkdir -p $(dir $@) + $(CC) $(AFLAGS_EXEDYN) $(INCLUDE) -c $< -o $@ + +clean: + rm -rf build diff --git a/examples/system/dynlink/system.cnf b/examples/system/dynlink/system.cnf new file mode 100644 index 0000000..a4a2146 --- /dev/null +++ b/examples/system/dynlink/system.cnf @@ -0,0 +1,4 @@ +BOOT=cdrom:\main.exe;1 +TCB=4 +EVENT=10 +STACK=801FFFF0 diff --git a/libpsn00b/include/dlfcn.h b/libpsn00b/include/dlfcn.h new file mode 100644 index 0000000..5fdd343 --- /dev/null +++ b/libpsn00b/include/dlfcn.h @@ -0,0 +1,176 @@ +/* + * PSn00bSDK dynamic linker + * (C) 2021 spicyjpeg - MPL licensed + */ + +#ifndef __DLFCN_H +#define __DLFCN_H + +#include +#include + +/* Helper macro for setting $t9 before calling a function */ + +#define DL_CALL(func, ...) { \ + __asm__ volatile("move $t9, %0;" :: "r"(func) : "$t9"); \ + func(__VA_ARGS__); \ +} + +/* Types */ + +#define RTLD_DEFAULT ((DLL *) 0) + +typedef enum _DL_ResolveMode { + RTLD_LAZY = 1, // Resolve functions when they are first called (default) + RTLD_NOW = 2 // Resolve all symbols immediately on load +} DL_ResolveMode; + +// Members of this struct should not be accessed directly in most cases, but +// they are intentionally exposed for easier expandability. +typedef struct _DLL { + void *ptr; + void *malloc_ptr; + size_t size; + const uint32_t *hash; + uint32_t *got; + Elf32_Sym *symtab; + const char *strtab; + uint16_t symbol_count; + uint16_t got_length; +} DLL; + +/* API */ + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: rewrite these javadoc comments into proper documentation + +/** + * @brief Reads the symbol table from the provided string buffer (which may or + * may not be null-terminated), parses it and stores the parsed entries into a + * private hash table; the buffer won't be further referenced and can be safely + * deallocated after parsing. Returns the number of entries successfully parsed + * or -1 if an error occurred. + * + * This function expects the string buffer to contain one more lines, each of + * which must follow this format: + * + * [DEBUG_INFO...] + * + * The "nm" tool included in the GCC toolchain can be used to generate a map + * file in the appropriate format after building the executable, by using this + * command: + * + * mipsel-unknown-elf-nm -f posix -l -n executable.elf >executable.map + * + * @param ptr + * @param size + * @return -1 or number of entries parsed + */ +int32_t DL_ParseSymbolMap(const char *ptr, size_t size); +/** + * @brief File wrapper around DL_ParseSymbolMap(). Allocates a temporary buffer + * then loads the specified map file into it (using BIOS APIs) and calls + * DL_ParseSymbolMap() to parse it. The buffer is deallocated immediately after + * parsing. + * + * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1" + * @return -1 or number of entries parsed + */ +int32_t DL_LoadSymbolMap(const char *filename); +/** + * @brief Frees internal buffers containing the currently loaded symbol map. + * This is automatically done before loading a new symbol map so there is no + * need to call this function in most cases, however it can still be useful to + * free up space on the heap once the symbol map is no longer needed. + */ +void DL_UnloadSymbolMap(void); +/** + * @brief Queries the currently loaded symbol map for the symbol with the given + * name and returns a pointer to it, which can then be used to directly access + * the symbol. If the symbol can't be found, null is returned instead. + * + * @param name + * @return NULL or pointer to symbol (any type) + */ +void *DL_GetSymbolByName(const char *name); +/** + * @brief Sets a custom function to be called for resolving symbols in DLLs. + * The function will be given a pointer to the current DLL and the unresolved + * symbol's name, and should return the address of the symbol in the executable + * (the dynamic linker will lock up if it returns null). Passing null instead + * of a function resets the default behavior of calling DL_GetSymbolByName() to + * find the symbol in the currently loaded symbol map. + * + * @param callback NULL or pointer to callback function + */ +void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)); + +/** + * @brief Initializes a buffer holding the contents of a dynamically-loaded + * library file (compiled with the dll.ld linker script and converted to a raw + * binary) *in-place*. A new DLL struct is allocated to store metadata but, + * unlike DL_ParseSymbolMap(), the DLL's actual code, data and tables are + * referenced directly from the provided buffer. The buffer must not be moved + * or deallocated, at least not before calling dlclose() on the DLL struct + * returned by this function. + * + * The third argument specifies when symbols in the DLL should be resolved. + * Setting it to RTLD_LAZY defers resolution of undefined functions to when + * they are first called, while RTLD_DEFAULT forces all symbols to be resolved + * immediately. If a custom resolver has been set via DL_SetResolveCallback(), + * it will be called for each symbol to resolve. + * + * @param ptr + * @param size + * @param mode RTLD_LAZY or RTLD_NOW + * @return NULL or pointer to a new DLL struct + */ +DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode); +/** + * @brief File wrapper around dlinit(). Allocates a new buffer, loads the + * specified file into it (using BIOS APIs) and calls dlinit() on that. When + * calling dlclose() on a DLL loaded from a file, the file buffer is + * automatically destroyed. + * + * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1" + * @param mode RTLD_LAZY or RTLD_NOW + * @return NULL or pointer to a new DLL struct + */ +DLL *dlopen(const char *filename, DL_ResolveMode mode); +/** + * @brief Destroys a loaded DLL by calling its global destructors and freeing + * the buffer it's loaded in. Any pointer passed to dlclose() should no longer + * be used after the call. If the DLL was initialized in-place using dlinit(), + * dlclose() will *NOT* free the buffer initially passed to dlinit(). + * + * @param dll + */ +void dlclose(DLL *dll); +/** + * @brief Returns a pointer to the DLL symbol with the given name, or null if + * it can't be found. If null or RTLD_DEFAULT is passed as first argument, the + * executable itself is searched instead using the symbol map (behaving the + * same as DL_GetSymbolByName()). + * + * @param dll DLL struct or RTLD_DEFAULT + * @param name + * @return NULL or pointer to symbol (any type) + */ +void *dlsym(DLL *dll, const char *name); +/** + * @brief Returns a string describing the last error that occurred, or null if + * no error has occurred since the last call to dlerror() (i.e. calling this + * also resets the internal error flags). + * + * @return NULL or pointer to const string + */ +const char *const dlerror(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libpsn00b/include/elf.h b/libpsn00b/include/elf.h new file mode 100644 index 0000000..b4c4408 --- /dev/null +++ b/libpsn00b/include/elf.h @@ -0,0 +1,138 @@ +/* + * PSn00bSDK dynamic linker + * (C) 2021 spicyjpeg - MPL licensed + * + * This file is used internally by the dynamic linker to parse the "header" + * (.dynamic and .dynsym sections) of dynamically-linked libraries built with + * the provided linker script. Most of it is copied from the standard Unix + * elf.h header, with the only changes being removed typedefs and #defines + * converted to enums. + */ + +#ifndef __ELF_H +#define __ELF_H + +#include + +typedef enum { + DT_NULL = 0, /* Marks end of dynamic section */ + DT_NEEDED = 1, /* Name of needed library */ + DT_PLTRELSZ = 2, /* Size in bytes of PLT relocs */ + DT_PLTGOT = 3, /* Processor defined value */ + DT_HASH = 4, /* Address of symbol hash table */ + DT_STRTAB = 5, /* Address of string table */ + DT_SYMTAB = 6, /* Address of symbol table */ + DT_RELA = 7, /* Address of Rela relocs */ + DT_RELASZ = 8, /* Total size of Rela relocs */ + DT_RELAENT = 9, /* Size of one Rela reloc */ + DT_STRSZ = 10, /* Size of string table */ + DT_SYMENT = 11, /* Size of one symbol table entry */ + DT_INIT = 12, /* Address of init function */ + DT_FINI = 13, /* Address of termination function */ + DT_SONAME = 14, /* Name of shared object */ + DT_RPATH = 15, /* Library search path (deprecated) */ + DT_SYMBOLIC = 16, /* Start symbol search here */ + DT_REL = 17, /* Address of Rel relocs */ + DT_RELSZ = 18, /* Total size of Rel relocs */ + DT_RELENT = 19, /* Size of one Rel reloc */ + DT_PLTREL = 20, /* Type of reloc in PLT */ + DT_DEBUG = 21, /* For debugging; unspecified */ + DT_TEXTREL = 22, /* Reloc might modify .text */ + DT_JMPREL = 23, /* Address of PLT relocs */ + DT_BIND_NOW = 24, /* Process relocations of object */ + DT_INIT_ARRAY = 25, /* Array with addresses of init fct */ + DT_FINI_ARRAY = 26, /* Array with addresses of fini fct */ + DT_INIT_ARRAYSZ = 27, /* Size in bytes of DT_INIT_ARRAY */ + DT_FINI_ARRAYSZ = 28, /* Size in bytes of DT_FINI_ARRAY */ + DT_RUNPATH = 29, /* Library search path */ + DT_FLAGS = 30, /* Flags for the object being loaded */ + DT_ENCODING = 32, /* Start of encoded range */ + DT_PREINIT_ARRAY = 32, /* Array with addresses of preinit fct*/ + DT_PREINIT_ARRAYSZ = 33, /* size in bytes of DT_PREINIT_ARRAY */ + DT_SYMTAB_SHNDX = 34, /* Address of SYMTAB_SHNDX section */ + DT_NUM = 35, /* Number used */ + DT_LOOS = 0x6000000d, /* Start of OS-specific */ + DT_HIOS = 0x6ffff000, /* End of OS-specific */ + DT_LOPROC = 0x70000000, /* Start of processor-specific */ + DT_HIPROC = 0x7fffffff, /* End of processor-specific */ + + DT_MIPS_RLD_VERSION = 0x70000001, + DT_MIPS_FLAGS = 0x70000005, + DT_MIPS_BASE_ADDRESS = 0x70000006, + DT_MIPS_LOCAL_GOTNO = 0x7000000a, + DT_MIPS_SYMTABNO = 0x70000011, + DT_MIPS_UNREFEXTNO = 0x70000012, + DT_MIPS_GOTSYM = 0x70000013, + DT_MIPS_HIPAGENO = 0x70000014 +} Elf32_d_tag; + +typedef enum { + RHF_NONE = 0, /* No flags */ + RHF_QUICKSTART = 1, /* Use quickstart */ + RHF_NOTPOT = 2, /* Hash size not power of 2 */ + RHF_NO_LIBRARY_REPLACEMENT = 4 /* Ignore LD_LIBRARY_PATH */ +} Elf32_d_MIPS_FLAGS; + +typedef struct { + Elf32_d_tag d_tag; /* Dynamic entry type */ + union { + uint32_t d_val; /* Integer value */ + void *d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct { + uint32_t st_name; /* Symbol name (string tbl index) */ + void *st_value; /* Symbol value */ + size_t st_size; /* Symbol size */ + uint8_t st_info; /* Symbol type and binding */ + uint8_t st_other; /* Symbol visibility */ + uint16_t st_shndx; /* Section index */ +} Elf32_Sym; + +#define ELF32_ST_BIND(val) ((Elf32_st_bind) (((uint8_t) (val)) >> 4)) +#define ELF32_ST_TYPE(val) ((Elf32_st_type) ((val) & 0xf)) +#define ELF32_ST_INFO(bind, type) ((((uint8_t) (bind)) << 4) + (((uint8_t) (type)) & 0xf)) + +typedef enum { + STB_LOCAL = 0, /* Local symbol */ + STB_GLOBAL = 1, /* Global symbol */ + STB_WEAK = 2, /* Weak symbol */ + STB_NUM = 3, /* Number of defined types. */ + STB_LOOS = 10, /* Start of OS-specific */ + STB_GNU_UNIQUE = 10, /* Unique symbol. */ + STB_HIOS = 12, /* End of OS-specific */ + STB_LOPROC = 13, /* Start of processor-specific */ + STB_HIPROC = 15 /* End of processor-specific */ +} Elf32_st_bind; + +typedef enum { + STT_NOTYPE = 0, /* Symbol type is unspecified */ + STT_OBJECT = 1, /* Symbol is a data object */ + STT_FUNC = 2, /* Symbol is a code object */ + STT_SECTION = 3, /* Symbol associated with a section */ + STT_FILE = 4, /* Symbol's name is file name */ + STT_COMMON = 5, /* Symbol is a common data object */ + STT_TLS = 6, /* Symbol is thread-local data object*/ + STT_NUM = 7, /* Number of defined types. */ + STT_LOOS = 10, /* Start of OS-specific */ + STT_GNU_IFUNC = 10, /* Symbol is indirect code object */ + STT_HIOS = 12, /* End of OS-specific */ + STT_LOPROC = 13, /* Start of processor-specific */ + STT_HIPROC = 15 /* End of processor-specific */ +} Elf32_st_type; + +// If you need to add more constants, you may use the following Python snippet +// to quickly convert #defines to enums: +/* +import re +t = """""" +t = re.sub( + r"(0x[0-9a-f]+|0b[01]+|[0-9]+)", + lambda m: f"= {m.group(1)},", + t.replace("#define ", "\t").replace("#define\t", "\t") +) +print("typedef enum {\n" + t + "\n} NAME;") +*/ + +#endif diff --git a/libpsn00b/psxetc/_dl_resolve_wrapper.s b/libpsn00b/psxetc/_dl_resolve_wrapper.s new file mode 100644 index 0000000..069ee84 --- /dev/null +++ b/libpsn00b/psxetc/_dl_resolve_wrapper.s @@ -0,0 +1,54 @@ +# PSn00bSDK dynamic linker +# (C) 2021 spicyjpeg - MPL licensed +# +# This function is called by the lazy loader stubs generated by GCC in the +# .plt/.MIPS.stubs section when attempting to call a GOT entry whose address +# hasn't yet been resolved. The generated stubs conform to the MIPS ABI and +# uses the following registers to pass the following parameters: +# - $t7 = address the resolved function should return to (i.e. $ra of the +# caller that triggered the stub) +# - $t8 = index of the function in the .dynsym symbol table +# - $t9 = _dl_resolve_wrapper itself's address + +.set noreorder +.section .text + +.global _dl_resolve_wrapper +.type _dl_resolve_wrapper, @function +_dl_resolve_wrapper: + # Push the registers we're going to use onto the stack. + addiu $sp, -16 + sw $a0, 0($sp) + sw $a1, 4($sp) + sw $v0, 8($sp) + sw $t7, 12($sp) # (will be restored directly to $ra) + + # Figure out where the DLL's struct is. dlinit() places a pointer to the + # struct in the second GOT entry, so it's just a matter of indexing the GOT + # using GP. Then call _dl_resolve_helper with the struct and $t8 as + # arguments, and store the return value into $t0. + lw $a0, -0x7fec($gp) # sizeof(uint32_t) - 0x7ff0 (see dll.ld) + move $a1, $t8 + + jal _dl_resolve_helper + addiu $sp, -8 # (branch delay) + addiu $sp, 8 + + move $t0, $v0 + + # Restore registers from the stack and tail-call the function at the + # address returned by the resolver. This will cause the resolved function + # to run and return directly to the code that called the stub. + lw $a0, 0($sp) + lw $a1, 4($sp) + lw $v0, 8($sp) + lw $ra, 12($sp) + addiu $sp, 16 + + jr $t0 + nop + +.global _dl_credits +.type _dl_credits, @object +_dl_credits: + .asciiz "psxetc runtime dynamic linker by spicyjpeg\n" diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c new file mode 100644 index 0000000..1485183 --- /dev/null +++ b/libpsn00b/psxetc/dl.c @@ -0,0 +1,665 @@ +/* + * PSn00bSDK dynamic linker + * (C) 2021 spicyjpeg - MPL licensed + * + * The bulk of this code is MIPS-specific but not PS1-specific, so the whole + * dynamic linker could be ported to other MIPS platforms that do not have one + * in their OS/SDK (e.g. N64, PIC32 microcontrollers). Note that, despite the + * various ELF references, I did *not* implement a full ELF file parser: this + * implementation just expects all library files to begin with several metadata + * sections (.dynamic, .dynsym, .hash, .dynstr) laid out in a fixed order. Use + * the dll.ld linker script to generate compatible libraries. + * + * Most of the stuff implemented here, despite looking like black magic, is + * actually well-documented across several PDFs scattered around the internet: + * - http://www.sco.com/developers/devspecs/gabi41.pdf + * - http://math-atlas.sourceforge.net/devel/assembly/mipsabi32.pdf + * - http://flint.cs.yale.edu/cs422/doc/ELF_Format.pdf + * + * TODO: + * - Clean up duplicated code, especially hash-table-related loops + * - Fix bugs and improve allocation checks + * - Find a way to predict the smallest ELF hash table size for a certain set + * of entries + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Compile options */ + +// Uncomment before building to enable debug logging (via printf()). +#define DEBUG + +// Comment before building to disable functions that rely on BIOS file APIs, +// i.e. DL_LoadSymbolMap() and dlopen(). +#define USE_FILE_API + +/* Private types */ + +typedef struct { + uint32_t hash; + void *ptr; +} MapEntry; + +typedef enum { + ERR_NONE, + ERR_FILE, + ERR_FILE_MALLOC, + ERR_FILE_READ, + ERR_NO_MAP, + ERR_MAP_MALLOC, + ERR_NO_SYMBOLS, + ERR_DLL_NULL, + ERR_DLL_MALLOC, + ERR_DLL_FORMAT, + ERR_NO_FILE_API, + ERR_MAP_SYMBOL, + ERR_DLL_SYMBOL +} ErrorCode; + +/* Data */ + +static const char *const DL_ERROR_MESSAGES[] = { + "Unable to find file", + "Unable to allocate buffer to load file into", + "Failed to read file", + "No symbol map has been loaded yet", + "Unable to allocate symbol map structures", + "No symbols found in symbol map", + "Unable to initialize DLL from null pointer", + "Unable to allocate DLL metadata structures", + "Unsupported DLL type or format", + "psxetc has been built without file support", + "Symbol not found in symbol map", + "Symbol not found in DLL" +}; + +static ErrorCode _error_code = ERR_NONE; +static uint32_t *_map_hash_table = 0; +static MapEntry *_map_entry_table = 0; + +// Accessed by _dl_resolve_helper, stores the pointer to the current resolver +// function. Can be changed using DL_SetResolveCallback(). +void *(*_dl_resolve_callback)(DLL *, const char *) = 0; + +/* Private utilities */ + +#ifdef DEBUG +#define _LOG(...) printf(__VA_ARGS__) +#define _ERROR(code, ret) { \ + _LOG("psxetc: ERROR! %s\n", DL_ERROR_MESSAGES[code - 1]); \ + _error_code = code; \ + return ret; \ +} +#else +#define _LOG(...) +#define _ERROR(code, ret) { \ + _error_code = code; \ + return ret; \ +} +#endif + +void _dl_resolve_wrapper(void); + +// Called by _dl_resolve_wrapper() (which is in turn called by GCC stubs) to +// resolve a function. +void *_dl_resolve_helper(DLL *dll, uint32_t index) { + Elf32_Sym *sym = &(dll->symtab[index]); + const char *_name = &(dll->strtab[sym->st_name]); + void *address; + + if (_dl_resolve_callback) + address = _dl_resolve_callback(dll, _name); + else + address = DL_GetSymbolByName(_name); + + if (!address) { + _LOG("psxetc: FATAL! Can't resolve %s, locking up\n", _name); + while (1) + __asm__ volatile("nop"); + } + + // Patch the GOT entry to "cache" the resolved address. This can probably + // be implemented in a faster way, but this thing is already too complex. + for (uint32_t i = 0; i < dll->got_length; i++) { + if (dll->got[2 + i] == (uint32_t) sym->st_value) { + dll->got[2 + i] = (uint32_t) address; + break; + } + } + + return address; +} + +// Implementation of the weird obscure hashing function used in the ELF .hash +// section. Not sure how collision-prone this is. +// https://en.wikipedia.org/wiki/PJW_hash_function +static uint32_t _elf_hash(const char *str) { + uint32_t value = 0; + + while (*str) { + value <<= 4; + value += (uint32_t) *(str++); + + uint32_t nibble = value & 0xf0000000; + if (nibble) + value ^= nibble >> 24; + + value &= ~nibble; + } + + return value; +} + +#ifdef USE_FILE_API +static uint8_t *_load_file(const char *filename, size_t *size_output) { + int32_t fd = open(filename, 1); + if (fd < 0) + _ERROR(ERR_FILE, 0); + + // Extract file size from the file's associated control block. + // https://problemkaputt.de/psx-spx.htm#biosmemorymap + FCB *fcb = (FCB *) *((FCB **) 0x80000140); + size_t size = fcb[fd].filesize; + + uint8_t *buffer = malloc(size); + if (!buffer) + _ERROR(ERR_FILE_MALLOC, 0); + + _LOG("psxetc: Loading %s (%d bytes)..", filename, size); + + for (uint32_t offset = 0; offset < size; ) { + int32_t length = read(fd, &(buffer[offset]), 0x800); + + if (length <= 0) { + close(fd); + free(buffer); + _ERROR(ERR_FILE_READ, 0); + } + + _LOG("."); + offset += length; + } + + close(fd); + _LOG(" done\n"); + + if (size_output) + *size_output = size; + return buffer; +} +#endif + +/* Symbol map loading/parsing API */ + +int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { + DL_UnloadSymbolMap(); + + // Perform a quick scan over the entire map text and count the number of + // newlines. This allows us to (over)estimate the number of entries and + // allocate a sufficiently large hash/entry table. + uint32_t entries = 0; + for (uint32_t pos = 0; pos < size; pos++) { + if (ptr[pos] == '\n') + entries++; + } + + // TODO: find a way to calculate the optimal number of hash table "buckets" + // in order to minimize hash table size + uint32_t nbucket = entries; + _LOG("psxetc: Predicted %d entries, %d hash buckets\n", entries, nbucket); + + // Allocate an entry table to store parsed symbols in, and an associated + // hash table (same format as .hash section, with 8-byte header). + _map_hash_table = malloc(sizeof(uint32_t) * (2 + nbucket + entries)); + _map_entry_table = malloc(sizeof(MapEntry) * entries); + if (!_map_hash_table || !_map_entry_table) + _ERROR(ERR_MAP_MALLOC, -1); + + _map_hash_table[0] = nbucket; + _map_hash_table[1] = entries; + for (uint32_t i = 0; i < (nbucket + entries); i++) + _map_hash_table[2 + i] = 0xffffffff; + + // Go again through the symbol map and fill in the hash table. + uint32_t index = 0; + for (uint32_t pos = 0; (pos < size) && ptr[pos]; pos++) { + char name[64]; + char type_string[2]; + uint64_t address64; + size_t _size; + + // e.g. "main T ffffffff80000000 100 ...\n" + int32_t parsed = sscanf( + &(ptr[pos]), + "%63s %1s %Lx %x", + name, + type_string, + &address64, + &_size // Optional, unused (yet) + ); + + if (parsed >= 3) { + // Drop the upper 32 bits of the address (for some reason MIPS nm + // insists on printing 64-bit addresses... wtf) and normalize the + // type letter to upper case, then check if the entry is valid and + // non-null. + void *address = (void *) address64; + char _type = toupper(type_string[0]); + uint32_t hash = _elf_hash(name); + uint32_t hash_mod = hash % nbucket; + + if (address && ( + (_type == 'T') || // .text + (_type == 'R') || // .rodata + (_type == 'D') || // .data + (_type == 'B') // .bss + )) { + _LOG( + "psxetc: Map sym: %08x,%08x [%c %s]\n", + address, + _size, + _type, + name + ); + + MapEntry *entry = &(_map_entry_table[index]); + entry->hash = hash; + entry->ptr = address; + + // Append a reference to the entry to the hash table's chain + // for the current hash_mod. I can't explain this properly. + uint32_t *hash_entry = &(_map_hash_table[2 + hash_mod]); + while (*hash_entry != 0xffffffff) + hash_entry = &(_map_hash_table[2 + nbucket + *hash_entry]); + + *hash_entry = index; + index++; + } + } + + // Skip the line by advancing the pointer until a newline is found. The + // for loop will then skip the newline itself. + while ((pos < size) && (ptr[pos] != '\n')) + pos++; + } + + _LOG("psxetc: Parsed %d symbols from map\n", entries); + if (!entries) + _ERROR(ERR_NO_SYMBOLS, -1); + + return entries; +} + +int32_t DL_LoadSymbolMap(const char *filename) { +#ifdef USE_FILE_API + size_t size; + char *ptr = _load_file(filename, &size); + if (!ptr) + return -1; + + int32_t entries = DL_ParseSymbolMap(ptr, size); + free(ptr); + + return entries; +#else + _ERROR(ERR_NO_FILE_API, -1); +#endif +} + +void DL_UnloadSymbolMap(void) { + if (!_map_hash_table) + return; + + free(_map_hash_table); + free(_map_entry_table); + _map_hash_table = 0; +} + +void *DL_GetSymbolByName(const char *name) { + if (!_map_hash_table) + _ERROR(ERR_NO_MAP, 0); + + // https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-48031.html + uint32_t nbucket = _map_hash_table[0]; + uint32_t hash = _elf_hash(name); + uint32_t hash_mod = hash % nbucket; + + // Go through the hash table's chain until the symbol hash matches the one + // calculated. + for (uint32_t i = _map_hash_table[2 + hash_mod]; i;) { + MapEntry *entry = &(_map_entry_table[i]); + + if (hash == entry->hash) { + _LOG("psxetc: Map lookup [%s = %08x]\n", name, entry->ptr); + return entry->ptr; + } + + i = _map_hash_table[2 + nbucket + i]; + } + + _ERROR(ERR_MAP_SYMBOL, 0); +} + +void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)) { + _dl_resolve_callback = callback; +} + +/* Library loading and linking API */ + +DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { + if (!ptr) + _ERROR(ERR_DLL_NULL, 0); + + DLL *dll = malloc(sizeof(DLL)); + if (!dll) + _ERROR(ERR_DLL_MALLOC, 0); + + dll->ptr = ptr; + dll->malloc_ptr = 0; + dll->size = size; + _LOG("psxetc: Initializing DLL at %08x\n", ptr); + + // Interpret the key-value pairs in the .dynamic section to obtain info + // about all the other sections. The pairs are null-terminated, which makes + // parsing trivial. + uint32_t local_got_len = 0; + uint32_t first_got_sym = 0; + + for (Elf32_Dyn *dyn = (Elf32_Dyn *) ptr; dyn->d_tag; dyn++) { + _LOG("psxetc: .dynamic %08x=%08x ", dyn->d_tag, dyn->d_un.d_val); + + switch (dyn->d_tag) { + // Offset of .got section + case DT_PLTGOT: + _LOG("[PLTGOT]\n"); + + dll->got = (void *) (ptr + dyn->d_un.d_val); + break; + + // Offset of .hash section + case DT_HASH: + _LOG("[HASH]\n"); + + dll->hash = (void *) (ptr + dyn->d_un.d_val); + break; + + // Offset of .dynstr (NOT .strtab) section + case DT_STRTAB: + _LOG("[STRTAB]\n"); + + dll->strtab = (void *) (ptr + dyn->d_un.d_val); + break; + + // Offset of .dynsym (NOT .symtab) section + case DT_SYMTAB: + _LOG("[SYMTAB]\n"); + + dll->symtab = (void *) (ptr + dyn->d_un.d_val); + break; + + // Length of .dynstr section + //case DT_STRSZ: + //_LOG("[STRSZ]\n"); + //break; + + // Length of each .dynsym entry + case DT_SYMENT: + _LOG("[SYMENT]\n"); + + // Only 16-byte symbol table entries are supported. + if (dyn->d_un.d_val != sizeof(Elf32_Sym)) { + free(dll); + _ERROR(ERR_DLL_FORMAT, 0); + } + break; + + // MIPS ABI (?) version + case DT_MIPS_RLD_VERSION: + _LOG("[MIPS_RLD_VERSION]\n"); + + // Versions other than 1 are unsupported (do they even exist?). + if (dyn->d_un.d_val != 1) { + free(dll); + _ERROR(ERR_DLL_FORMAT, 0); + } + break; + + // DLL/ABI flags + case DT_MIPS_FLAGS: + _LOG("[MIPS_FLAGS]\n"); + + // Shortcut pointers (whatever they are) are not supported. + if (dyn->d_un.d_val & RHF_QUICKSTART) { + free(dll); + _ERROR(ERR_DLL_FORMAT, 0); + } + break; + + // Number of local (not to resolve) GOT entries + case DT_MIPS_LOCAL_GOTNO: + _LOG("[MIPS_LOCAL_GOTNO]\n"); + + local_got_len = dyn->d_un.d_val; + break; + + // Base address DLL was compiled for + case DT_MIPS_BASE_ADDRESS: + _LOG("[MIPS_BASE_ADDRESS]\n"); + + // Base addresses other than zero are not supported. It would + // be easy enough to support them, but why? + if (dyn->d_un.d_val) { + free(dll); + _ERROR(ERR_DLL_FORMAT, 0); + } + break; + + // Number of symbol table entries + case DT_MIPS_SYMTABNO: + _LOG("[MIPS_SYMTABNO]\n"); + + dll->symbol_count = dyn->d_un.d_val; + break; + + // Index of first unresolved symbol table entry + //case DT_MIPS_UNREFEXTNO: + //_LOG("[MIPS_UNREFEXTNO]\n"); + //break; + + // Index of first symbol table entry which has a matching GOT entry + case DT_MIPS_GOTSYM: + _LOG("[MIPS_GOTSYM]\n"); + + first_got_sym = dyn->d_un.d_val; + break; + + // Number of pages the GOT is split into (does not apply to PS1) + //case DT_MIPS_HIPAGENO: + //_LOG("[MIPS_HIPAGENO]\n"); + //break; + + default: + _LOG("[ignored]\n"); + } + } + + // Calculate the number of GOT entries (and symbols, if MIPS_SYMTABNO was + // not found in the .dynamic section). + //dll->symbol_count = \ + ((uint32_t) dll->hash - (uint32_t) dll->symtab) / sizeof(Elf32_Sym); + //dll->got_length = \ + ((uint32_t) ptr + size - (uint32_t) dll->got) / sizeof(uint32_t) - 2; + + dll->got_length = local_got_len + (dll->symbol_count - first_got_sym) - 2; + _LOG( + "psxetc: %d symbols, %d GOT entries\n", + dll->symbol_count, + dll->got_length + ); + + // Relocate the DLL by adding its base address to all pointers in the GOT + // except the first two, which are reserved. The first entry in particular + // is a pointer to the lazy resolver, called by auto-generated stubs when a + // function is first used. got[1] is normally unused, but here we'll set it + // to the DLL's metadata struct so we can look that up when resolving + // functions (see _dl_resolve_wrapper()). + dll->got[0] = (uint32_t) &_dl_resolve_wrapper; + dll->got[1] = (uint32_t) dll; + + for (uint32_t i = 0; i < dll->got_length; i++) + dll->got[2 + i] += (uint32_t) ptr; + + // Fix addresses in the symbol table. + // TODO: clean this shit up + uint32_t got_offset = first_got_sym; + + for (uint32_t i = 0; i < dll->symbol_count; i++) { + Elf32_Sym *sym = &(dll->symtab[i]); + const char *_name = &(dll->strtab[sym->st_name]); + + if (!sym->st_value) + continue; + + sym->st_value += (uint32_t) ptr; + _LOG( + "psxetc: DLL sym: %08x,%08x [%s]\n", + sym->st_value, + sym->st_size, + _name + ); + + // If RTLD_NOW was passed, resolve GOT entries ahead of time by + // cross-referencing them with the symbol table. + if (mode != RTLD_NOW) + continue; + + for (uint32_t j = got_offset; j < dll->got_length; j++) { + if (dll->got[2 + j] != (uint32_t) sym->st_value) + continue; + + got_offset = j; + + // If the symbol is undefined (st_shndx = 0) and is either a + // variable or a function, resolve it immediately. + // TODO: linking of external variables needs more testing + if (!(sym->st_shndx) && ( + ELF32_ST_TYPE(sym->st_info) == STT_OBJECT || + ELF32_ST_TYPE(sym->st_info) == STT_FUNC + )) { + dll->got[2 + j] = (uint32_t) _dl_resolve_callback(dll, _name); + + if (!dll->got[2 + j]) { + free(dll); + _ERROR(ERR_MAP_SYMBOL, 0); + } + } + + break; + } + } + + EnterCriticalSection(); + FlushCache(); + ExitCriticalSection(); + + // Call the DLL's global constructors. This is the same thing we'd do in + // _start() for regular executables, but we have to do it outside of the + // DLL as there's no _start() or even a defined entry point within the + // DLL itself. + const uint32_t *ctor_list = dlsym(dll, "__CTOR_LIST__"); + if (ctor_list) { + for (uint32_t i = ((uint32_t) ctor_list[0]); i >= 1; i--) { + void (*ctor)(void) = (void (*)(void)) ctor_list[i]; + DL_CALL(ctor); + } + } + + return dll; +} + +DLL *dlopen(const char *filename, DL_ResolveMode mode) { +#ifdef USE_FILE_API + size_t size; + char *ptr = _load_file(filename, &size); + if (!ptr) + return 0; + + DLL *dll = dlinit(ptr, size, mode); + if (dll) + dll->malloc_ptr = dll->ptr; + else + free(ptr); + + return dll; +#else + _ERROR(ERR_NO_FILE_API, 0); +#endif +} + +void dlclose(DLL *dll) { + if (dll == RTLD_DEFAULT) + return; + + if (dll->ptr) { + // Call the DLL's global destructors. + const uint32_t *dtor_list = dlsym(dll, "__DTOR_LIST__"); + if (dtor_list) { + for (uint32_t i = 0; i < ((uint32_t) dtor_list[0]); i++) { + void (*dtor)(void) = (void (*)(void)) dtor_list[i + 1]; + DL_CALL(dtor); + } + } + } + + // If the DLL is associated to a buffer allocated by dlopen(), free that + // buffer. + if (dll->malloc_ptr) + free(dll->malloc_ptr); + + free(dll); +} + +void *dlsym(DLL *dll, const char *name) { + if (dll == RTLD_DEFAULT) + return DL_GetSymbolByName(name); + //return _dl_resolve_callback(RTLD_DEFAULT, name); + + // https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-48031.html + const uint32_t *hash_table = dll->hash; + uint32_t nbucket = hash_table[0]; + uint32_t hash_mod = _elf_hash(name) % nbucket; + + // Go through the hash table's chain until the symbol name matches the one + // provided. + for (uint32_t i = hash_table[2 + hash_mod]; i;) { + Elf32_Sym *sym = &(dll->symtab[i]); + const char *_name = &(dll->strtab[sym->st_name]); + + if (!strcmp(name, _name)) { + _LOG("psxetc: DLL lookup [%s = %08x]\n", name, sym->st_value); + return sym->st_value; + } + + i = hash_table[2 + nbucket + i]; + } + + _ERROR(ERR_DLL_SYMBOL, 0); +} + +const char *const dlerror(void) { + uint32_t last = _error_code; + _error_code = ERR_NONE; + + if (last) + return DL_ERROR_MESSAGES[last - 1]; + + return 0; +} diff --git a/libpsn00b/psxetc/readme.txt b/libpsn00b/psxetc/readme.txt index f2e7649..24645f7 100644 --- a/libpsn00b/psxetc/readme.txt +++ b/libpsn00b/psxetc/readme.txt @@ -1,18 +1,20 @@ PSX Misc library, part of PSn00bSDK -2019 Lameguy64 / Meido-Tek Productions +2021 Lameguy64 / Meido-Tek Productions Licensed under Mozilla Public License -Open source implementation of the ETC library. Doesn't really provide much -aside from some debug font stuff at the moment. - +Open source implementation of the ETC library. Currently provides the interrupt +and DMA callback dispatchers (used by other libraries) as well as the DL_* and +dl* functions for dynamic library loading (original, not present in the official +SDK but similar to the standard dlopen() API). Library developer(s): Lameguy64 - - + spicyjpeg + Library header(s): psxetc.h - + dlfcn.h + elf.h (used internally) -- cgit v1.2.3