aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b
diff options
context:
space:
mode:
authorJohn "Lameguy" Wilbert Villamor <lameguy64@gmail.com>2021-08-31 13:23:20 +0800
committerGitHub <noreply@github.com>2021-08-31 13:23:20 +0800
commitffa679d4d24b891cb59aba10946368f2ec00c391 (patch)
tree0cf6061915ebf48acdedf6d77b0c1b76eec5b8c3 /libpsn00b
parent317dc2b91d3afcdbaddb035f38611d12af161970 (diff)
parentf2fc18f82dd7900465d6ab3ae2080726d5589d39 (diff)
downloadpsn00bsdk-ffa679d4d24b891cb59aba10946368f2ec00c391.tar.gz
Merge pull request #36 from spicyjpeg/dynlink
Dynamic linker, gp-relative addressing, ldscripts and more
Diffstat (limited to 'libpsn00b')
-rw-r--r--libpsn00b/include/dlfcn.h176
-rw-r--r--libpsn00b/include/elf.h138
-rw-r--r--libpsn00b/include/psxapi.h1
-rw-r--r--libpsn00b/include/psxgpu.h2
-rw-r--r--libpsn00b/include/stdio.h3
-rw-r--r--libpsn00b/ldscripts/dll.ld122
-rw-r--r--libpsn00b/ldscripts/exe.ld114
-rw-r--r--libpsn00b/libc/_mem_init.s20
-rw-r--r--libpsn00b/libc/makefile59
-rw-r--r--libpsn00b/libc/malloc.s11
-rw-r--r--libpsn00b/libc/start.c181
-rw-r--r--libpsn00b/lzp/makefile61
-rw-r--r--libpsn00b/psxapi/makefile62
-rw-r--r--libpsn00b/psxapi/sys/flushcache.s10
-rw-r--r--libpsn00b/psxcd/makefile57
-rw-r--r--libpsn00b/psxetc/_dl_resolve_wrapper.s54
-rw-r--r--libpsn00b/psxetc/dl.c665
-rw-r--r--libpsn00b/psxetc/makefile59
-rw-r--r--libpsn00b/psxetc/readme.txt16
-rw-r--r--libpsn00b/psxgpu/makefile57
-rw-r--r--libpsn00b/psxgte/makefile57
-rw-r--r--libpsn00b/psxsio/makefile57
-rw-r--r--libpsn00b/psxspu/makefile61
-rw-r--r--libpsn00b/psxspu/transfer.s3
24 files changed, 1726 insertions, 320 deletions
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 <sys/types.h>
+#include <elf.h>
+
+/* 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:
+ *
+ * <SYMBOL_NAME> <T|R|D|B> <HEX_ADDRESS> <HEX_SIZE> [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 <sys/types.h>
+
+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 = """<paste #defines here>"""
+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/include/psxapi.h b/libpsn00b/include/psxapi.h
index c366702..9e92568 100644
--- a/libpsn00b/include/psxapi.h
+++ b/libpsn00b/include/psxapi.h
@@ -180,6 +180,7 @@ void ChangeClearRCnt(int t, int m);
// Executable functions
int Exec(struct EXEC *exec, int argc, char **argv);
+void FlushCache(void);
void _boot(void);
diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h
index ddb4522..f50b841 100644
--- a/libpsn00b/include/psxgpu.h
+++ b/libpsn00b/include/psxgpu.h
@@ -1,6 +1,8 @@
#ifndef __PSXGPU_H
#define __PSXGPU_H
+#include <sys/types.h>
+
// Low-level display parameters for DISPENV_RAW. A leftover from prototyping
#define DISP_WIDTH_256 0
#define DISP_WIDTH_320 1
diff --git a/libpsn00b/include/stdio.h b/libpsn00b/include/stdio.h
index cf1909d..97a2f5a 100644
--- a/libpsn00b/include/stdio.h
+++ b/libpsn00b/include/stdio.h
@@ -56,6 +56,9 @@ int vsprintf(char *string, const char *fmt, va_list ap);
int sprintf(char *string, const char *fmt, ...);
int snprintf(char *string, unsigned int size, const char *fmt, ...);
+int vsscanf(const char *str, const char *format, va_list ap);
+int sscanf(const char *str, const char *fmt, ...);
+
#ifdef __cplusplus
}
#endif
diff --git a/libpsn00b/ldscripts/dll.ld b/libpsn00b/ldscripts/dll.ld
new file mode 100644
index 0000000..59cbb53
--- /dev/null
+++ b/libpsn00b/ldscripts/dll.ld
@@ -0,0 +1,122 @@
+/*
+ * PSn00bSDK linker script for dynamically-loaded libraries
+ * (C) 2021 spicyjpeg - MPL licensed
+ *
+ * This script is similar to the one for executables. The main differences are
+ * the header at the beginning of the file, the .sdata/.sbss sections being
+ * replaced by the global offset table (GOT) and .bss being merged with .data.
+ */
+
+OUTPUT_FORMAT(elf32-littlemips)
+/*ENTRY(_start)
+STARTUP(start.o)*/
+
+MEMORY {
+ /* Code is position-independent, so we just set zero as address */
+ RELOC_RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 1M
+}
+
+SECTIONS {
+ /*
+ * DLL "header", containing the following sections:
+ *
+ * .dynamic: key-value pairs describing various section offsets, flags and
+ * other stuff
+ * .dynsym: dynamic symbol table, listing all functions to relocate as well
+ * as exported symbols
+ * .hash: pre-generated hash table to quickly look up symbol table
+ * entries by their names
+ * .dynstr: string blob referenced by symbol table entries
+ */
+
+ .dynamic : { *(.dynamic) } > RELOC_RAM
+ .dynsym : { *(.dynsym) } > RELOC_RAM
+ .hash : { *(.hash) } > RELOC_RAM
+ .dynstr : { *(.dynstr) } > RELOC_RAM
+
+ /* Text section, i.e. code and constants */
+
+ .text : ALIGN(16) {
+ __text_start = .;
+
+ *(.text .text.* .gnu.linkonce.t.*)
+ *(.plt .MIPS.stubs)
+ } > RELOC_RAM
+ .rodata : {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ } > RELOC_RAM
+
+ /* Global constructor and destructor arrays (length-prefixed) */
+
+ /*
+ * https://sourceware.org/binutils/docs/ld/Output-Section-Keywords.html
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770
+ */
+ .ctors : ALIGN(16) {
+ __CTOR_LIST__ = .;
+
+ LONG(((__CTOR_END__ - __CTOR_LIST__) / 4) - 2)
+ KEEP(*(SORT(.ctors.*)))
+ KEEP(*(.ctors))
+ LONG(0)
+
+ __CTOR_END__ = .;
+ } > RELOC_RAM
+ .dtors : ALIGN(16) {
+ __DTOR_LIST__ = .;
+
+ LONG(((__DTOR_END__ - __DTOR_LIST__) / 4) - 2)
+ KEEP(*(SORT(.dtors.*)))
+ KEEP(*(.dtors))
+ LONG(0)
+
+ __DTOR_END__ = .;
+ } > RELOC_RAM
+
+ /* Data and BSS sections, i.e. variables */
+
+ .data : {
+ *(.data .data.* .gnu.linkonce.d.*)
+
+ /*
+ * Merge the .bss section into the .data section, so uninitialized
+ * variables are treated as if they were initialized and preallocated.
+ * This makes DLLs unnecessarily larger (BSS values shouldn't be stored
+ * as they are always zero) but greatly simplifies the dynamic linker,
+ * as we don't have to worry about managing .bss separately from the
+ * main DLL blob.
+ */
+ __bss_start = .;
+
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+
+ . = ALIGN((. != 0) ? 4 : 1);
+ } > RELOC_RAM
+
+ /*
+ * Set _gp to point to the beginning of the GOT plus 0x7ff0, so anything
+ * within the GOT can be accessed using the $gp register as base plus a
+ * signed 16-bit immediate. Note that $gp is set to _gp in all exported
+ * functions (GCC adds the relevant code automatically).
+ */
+ HIDDEN(_gp = ALIGN(16) + 0x7ff0);
+
+ .got : {
+ *(.got)
+ } > RELOC_RAM
+
+ _end = .;
+
+ /* Dummy section */
+
+ .dummy (NOLOAD) : {
+ KEEP(*(.dummy))
+ } > RELOC_RAM
+
+ /* Remove anything flagged as link-time optimized */
+
+ /DISCARD/ : {
+ *(.gnu.lto_*)
+ }
+}
diff --git a/libpsn00b/ldscripts/exe.ld b/libpsn00b/ldscripts/exe.ld
new file mode 100644
index 0000000..3033636
--- /dev/null
+++ b/libpsn00b/ldscripts/exe.ld
@@ -0,0 +1,114 @@
+/*
+ * PSn00bSDK linker script for executables
+ * (C) 2021 spicyjpeg - MPL licensed
+ *
+ * GP-relative addressing (i.e. placing small variables in a 64 KB block and
+ * using $gp to reference them) is fully supported; the block is made up of
+ * sections .sdata and .sbss. Note that GP-relative addressing is not
+ * compatible with dynamic linking, as DLLs require GP to be unused.
+ */
+
+OUTPUT_FORMAT(elf32-littlemips)
+ENTRY(_start)
+/*STARTUP(start.o)*/
+
+MEMORY {
+ /* Mapped into KSEG0 */
+ KERNEL_RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x010000
+ APP_RAM (rwx) : ORIGIN = 0x80010000, LENGTH = 0x1f0000
+}
+
+SECTIONS {
+ /* Text section, i.e. code and constants */
+
+ .text : {
+ __text_start = .;
+
+ *(.text .text.* .gnu.linkonce.t.*)
+ *(.plt .MIPS.stubs)
+ } > APP_RAM
+ .rodata : {
+ *(.rodata .rodata.* .gnu.linkonce.r.*)
+ } > APP_RAM
+
+ /* Global constructor and destructor arrays (length-prefixed) */
+
+ /*
+ * TODO: replace this crap with .init_array and .fini_array, which are the
+ * "modern" way of doing it without reversed arrays and weird length
+ * prefixes. That would require even more patching though.
+ *
+ * https://sourceware.org/binutils/docs/ld/Output-Section-Keywords.html
+ * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770
+ */
+ .ctors : ALIGN(16) {
+ __CTOR_LIST__ = .;
+
+ LONG(((__CTOR_END__ - __CTOR_LIST__) / 4) - 2)
+ KEEP(*(SORT(.ctors.*)))
+ KEEP(*(.ctors))
+ LONG(0)
+
+ __CTOR_END__ = .;
+ } > APP_RAM
+ .dtors : ALIGN(16) {
+ __DTOR_LIST__ = .;
+
+ LONG(((__DTOR_END__ - __DTOR_LIST__) / 4) - 2)
+ KEEP(*(SORT(.dtors.*)))
+ KEEP(*(.dtors))
+ LONG(0)
+
+ __DTOR_END__ = .;
+ } > APP_RAM
+
+ /* Data sections, i.e. variables with default values */
+
+ .data : {
+ *(.data .data.* .gnu.linkonce.d.*)
+ } > APP_RAM
+
+ /*
+ * Set _gp to point to the beginning of .sdata plus 0x7ff0, so anything
+ * within .sdata (and .sbss) can be accessed using the $gp register as
+ * base plus a signed 16-bit immediate. Note that $gp is set to _gp on
+ * boot by _start() in the SDK.
+ */
+ HIDDEN(_gp = ALIGN(16) + 0x7ff0);
+
+ .sdata : {
+ *(.sdata .sdata.* .gnu.linkonce.s.*)
+ } > APP_RAM
+
+ /* BSS sections, i.e. uninitialized variables */
+
+ __bss_start = .;
+
+ .sbss (NOLOAD) : {
+ *(.sbss .sbss.* .gnu.linkonce.sb.*)
+ *(.scommon)
+ } > APP_RAM
+ .bss (NOLOAD) : {
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+
+ /*
+ * This crap was in the stock GCC linker script.
+ */
+ . = ALIGN((. != 0) ? 4 : 1);
+ } > APP_RAM
+
+ _end = .;
+
+ /* Dummy section */
+
+ .dummy (NOLOAD) : {
+ KEEP(*(.dummy))
+ } > APP_RAM
+
+ /* Remove anything flagged as link-time optimized */
+
+ /DISCARD/ : {
+ *(.gnu.lto_*)
+ }
+}
diff --git a/libpsn00b/libc/_mem_init.s b/libpsn00b/libc/_mem_init.s
deleted file mode 100644
index 672ac2f..0000000
--- a/libpsn00b/libc/_mem_init.s
+++ /dev/null
@@ -1,20 +0,0 @@
-.set noreorder
-
-.global _mem_init
-.type _mem_init, @function
-_mem_init:
-
-.section .text
-
-_mem_init:
- la $a0, __bss_start
- la $a1, _end
-.Lclear_bss:
- sb $0 , 0($a0)
- blt $a0, $a1, .Lclear_bss
- addiu $a0, 1
- la $a0, _end+4 # Initialize heap for malloc (does not use BIOS maalloc)
- li $a1, 1572864 # Allocate 1.5MB at end of bss
- j InitHeap
- nop
- \ No newline at end of file
diff --git a/libpsn00b/libc/makefile b/libpsn00b/libc/makefile
index a515ad5..bb3a687 100644
--- a/libpsn00b/libc/makefile
+++ b/libpsn00b/libc/makefile
@@ -1,54 +1,61 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libc.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-CFLAGS = -g -O2 -msoft-float -fno-builtin -fdata-sections \
- -ffunction-sections -Wa,--strip-local-absolute
-AFLAGS = -g -msoft-float -Wa,-strip-local-absolute
+# Project target name
+TARGET = libc.a
-CFILES = $(notdir $(wildcard ./*.c))
-CXXFILES = $(notdir $(wildcard ./*.cxx))
-AFILES = $(notdir $(wildcard ./*.s))
+## Files
-OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CXXFILES:.cxx=.o) \
- $(AFILES:.s=.o))
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-ifndef PSN00BSDK_LIBS
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-PSN00BSDK_LIBS = ..
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
-endif
+## Build rules
-all: $(TARGET)
+all: build/$(TARGET)
-$(TARGET): $(OFILES)
- cp $(GCC_BASE)/lib/gcc/$(PREFIX)/$(GCC_VERSION)/libgcc.a ./$(TARGET)
- $(AR) r $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ # "Import" libgcc's contents
+ cp $(GCC_BASE)/lib/gcc/$(PREFIX)/$(GCC_VERSION)/libgcc.a ./$@
+ $(AR) rs $@ $(OFILES)
build/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.cxx
@mkdir -p $(dir $@)
- $(CXX) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
diff --git a/libpsn00b/libc/malloc.s b/libpsn00b/libc/malloc.s
index 90f9bd4..e441bbe 100644
--- a/libpsn00b/libc/malloc.s
+++ b/libpsn00b/libc/malloc.s
@@ -1,5 +1,10 @@
# Custom first-fit malloc routines by Lameguy64
# Part of the PSn00bSDK Project
+#
+# NOTE: there reportedly is a GCC bug which messes up .weak functions written
+# in assembly if LTO is enabled. I haven't tested but, according to the
+# internet, this bug has never been fixed.
+# https://gcc.gnu.org/legacy-ml/gcc-help/2019-10/msg00092.html
.set noreorder
@@ -10,7 +15,6 @@
.section .text
-
# Stupid small function just to get bss end
# due to GCC insisting externs to be gp relative
.global GetBSSend
@@ -27,6 +31,7 @@ GetBSSend:
#
.global InitHeap
.type InitHeap, @function
+.weak InitHeap
InitHeap:
la $v0, _malloc_addr
sw $a0, 0($v0)
@@ -43,6 +48,7 @@ InitHeap:
# a0 - Size of memory heap in bytes
.global SetHeapSize
.type SetHeapSize, @function
+.weak SetHeapSize
SetHeapSize:
la $v1, _malloc_size
lw $v0, 0($v1)
@@ -55,6 +61,7 @@ SetHeapSize:
#
.global malloc
.type malloc, @function
+.weak malloc
malloc:
addiu $a0, 3 # Round size to a multiple of 4
srl $a0, 2
@@ -170,6 +177,7 @@ malloc:
#
.global calloc
.type calloc, @function
+.weak calloc
calloc:
mult $a0, $a1
addiu $sp, -4
@@ -199,6 +207,7 @@ calloc:
#
.global free
.type free, @function
+.weak free
free:
addiu $a0, -ND_HSIZ
diff --git a/libpsn00b/libc/start.c b/libpsn00b/libc/start.c
index 354ebb9..c5872df 100644
--- a/libpsn00b/libc/start.c
+++ b/libpsn00b/libc/start.c
@@ -1,110 +1,115 @@
-#include <stdio.h>
+/*
+ * PSn00bSDK startup code
+ * (C) 2021 Lameguy64, spicyjpeg - MPL licensed
+ */
+
+#include <sys/types.h>
#include <string.h>
#include <malloc.h>
-#define load_gp() __asm__ volatile ( \
- "la $gp, _gp;" )
-
-extern int _end;
-extern int main(int argc, const char* argv[]);
+#define KERNEL_ARG_STRING ((const char *) 0x80000180)
+#define KERNEL_RETURN_VALUE ((volatile int *) 0x8000dffc)
-void _mem_init(void);
+/* Argument parsing */
-int __argc;
+int32_t __argc;
const char **__argv;
-static const char *_arg_ptrs_int[8];
-static char _arg_buff[132];
+#define ARGC_MAX 16
-static void _call_global_ctors(void)
-{
- extern void (*__CTOR_LIST__[])(void);
+static const char *_argv_buffer[ARGC_MAX];
+static char _arg_string_buffer[132];
- // Constructors are called in reverse order of the list
- int i;
- for (i = (int)__CTOR_LIST__[0]; i >= 1; i--) {
- // Each function handles one or more destructor (within
- // file scope)
- __CTOR_LIST__[i]();
+static void _parse_kernel_args() {
+ // Copy the argument string from kernel memory into a private buffer (which
+ // won't be cleared or deallocated) and trim it at the first newline.
+ memset(_arg_string_buffer, 0, 132);
+ strncpy(_arg_string_buffer, KERNEL_ARG_STRING, 128);
+
+ for (char *ptr = _arg_string_buffer; *ptr; ptr++) {
+ if ((*ptr == '\r') || (*ptr == '\n')) {
+ *ptr = 0;
+ break;
+ }
}
-}
-static void _call_global_dtors(void)
-{
- extern void (*__DTOR_LIST__[])(void);
+ __argv = _argv_buffer;
+ for (__argc = 0; __argc < ARGC_MAX; __argc++) {
+ const char *ptr;
+ if (!__argc)
+ ptr = strtok(_arg_string_buffer, " ");
+ else
+ ptr = strtok(0, " ");
- /* Destructors in forward order */
- int i;
- for (i = 0; i < (int)__DTOR_LIST__[0]; i++) {
- /* Each function handles one or more destructor (within
- * file scope) */
- __DTOR_LIST__[i + 1]();
+ _argv_buffer[__argc] = ptr;
+ if (!ptr)
+ break;
}
}
-void _parse_args( int argc, const char *args[] )
-{
- int i;
- char *c,*s;
-
- memset( _arg_buff, 0, 132 );
-
- if( !args )
- {
- // Use arguments from kernel if args is NULL
- strncpy( _arg_buff, (char*)0x180, 128 );
-
- // Clean-up args froom stray line-ends
- while( ( c = strrchr( _arg_buff, '\r' ) ) ||
- ( c = strrchr( _arg_buff, '\n' ) ) )
- *c = 0;
- }
- else
- {
- __argc = argc;
- __argv = args;
- return;
- }
-
- __argc = 0;
- for( i=0; i<8; i++ )
- _arg_ptrs_int[i] = 0;
-
- s = _arg_buff;
- while( c = strtok( s, " " ) )
- {
- _arg_ptrs_int[__argc] = c;
- __argc++;
- s = NULL;
- if( __argc >= 8 )
- break;
- }
-
- __argv = _arg_ptrs_int;
-
-} /* parse_args */
+/* Main */
+
+// How much space at the end of RAM to leave for the stack (instead of using it
+// as heap). By default 128 KB are reserved for the stack, but this constant
+// can be overridden in main.c (or anywhere else) simply by redeclaring it
+// without the weak attribute.
+const int32_t __attribute__((weak)) STACK_MAX_SIZE = 0x20000;
-void _start( int argc, const char *args[] )
-{
- // Load GP address
- load_gp();
+// These are defined by the linker script. Note that these are *NOT* pointers,
+// they are virtual symbols whose location matches their value. The simplest
+// way to turn them into pointers is to declare them as arrays, so here we go.
+extern uint8_t __text_start[];
+extern uint8_t __bss_start[];
+extern uint8_t _end[];
+//extern uint8_t _gp[];
+
+extern void (*__CTOR_LIST__[])(void);
+extern void (*__DTOR_LIST__[])(void);
+
+extern int32_t main(int32_t argc, const char* argv[]);
+
+// Even though _start() usually takes no arguments, this implementation allows
+// parent executables to pass args directly to child executables without having
+// to overwrite the arg strings in kernel RAM.
+void _start(int32_t override_argc, const char **override_argv) {
+ __asm__ volatile("la $gp, _gp;");
// Mem init assembly function (clears BSS and InitHeap to _end which is
// not possible to do purely in C because the linker complains about
// relocation truncated to fit: R_MIPS_GPREL16 against `_end'
// Workaround is to do it in assembly because la pseudo-op doesn't use
// stupid gp relative addressing
- _mem_init();
-
- // process command line arguments
- _parse_args( argc, args );
-
- _call_global_ctors();
-
- *((int*)0x8000DFFC) = main( __argc, __argv );
-
- _call_global_dtors();
-
- // Set return value to kernel return value area
-
-} /* _start */ \ No newline at end of file
+ //_mem_init();
+
+ // Clear BSS 4 bytes at a time. BSS is always aligned to 4 bytes by the
+ // linker script.
+ for (uint32_t *i = (uint32_t *) __bss_start; i < (uint32_t *) _end; i++)
+ *i = 0;
+
+ // Calculate how much RAM is available after the loaded executable and
+ // initialize heap accordingly.
+ void *exe_end = _end + 4;
+ unsigned int exe_size = (unsigned int) exe_end - (unsigned int) __text_start;
+ InitHeap(exe_end, 0x200000 - (exe_size + STACK_MAX_SIZE));
+
+ if (override_argv) {
+ __argc = override_argc;
+ __argv = override_argv;
+ } else {
+ _parse_kernel_args();
+ }
+
+ // Call the global constructors (if any) to initialize global objects
+ // before calling main(). Constructors are put by the linker script in a
+ // length-prefixed array in reverse order.
+ for (uint32_t i = (uint32_t) __CTOR_LIST__[0]; i >= 1; i--)
+ __CTOR_LIST__[i]();
+
+ // Store main()'s return value into the kernel return value area (for child
+ // executables).
+ *KERNEL_RETURN_VALUE = main(__argc, __argv);
+
+ // Call global destructors (in forward order).
+ for (uint32_t i = 0; i < (uint32_t) __DTOR_LIST__[0]; i++)
+ __DTOR_LIST__[i + 1]();
+}
diff --git a/libpsn00b/lzp/makefile b/libpsn00b/lzp/makefile
index 729a500..02654ed 100644
--- a/libpsn00b/lzp/makefile
+++ b/libpsn00b/lzp/makefile
@@ -1,40 +1,59 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = liblzp.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-CFILES = $(notdir $(wildcard ./*.c))
-OFILES = $(addprefix build/,$(CFILES:.c=.o))
+# Project target name
+TARGET = liblzp.a
-CFLAGS = -g -O2 -msoft-float -fno-builtin -nostdlib -fdata-sections \
- -ffunction-sections -Wa,--strip-local-absolute
+## Files
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
-all: $(TARGET)
+## Build rules
+
+all: build/$(TARGET)
+
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
-
build/%.o: %.c
- @mkdir -p build
- $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.s
+ @mkdir -p $(dir $@)
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
clean:
- rm -Rf build $(OFILES) $(TARGET)
+ rm -rf build
diff --git a/libpsn00b/psxapi/makefile b/libpsn00b/psxapi/makefile
index 0b0d603..f300e5f 100644
--- a/libpsn00b/psxapi/makefile
+++ b/libpsn00b/psxapi/makefile
@@ -1,41 +1,61 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libpsxapi.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-SOURCES = stdio fs sys
+# Project target name
+TARGET = libpsxapi.a
-AFILES = $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.s))
-OFILES = $(addprefix build/,$(AFILES:.s=.o))
+## Files
-AFLAGS = -g -msoft-float -Wa,--strip-local-absolute
+SOURCES = stdio fs sys
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.c))
+CXXFILES= $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.cxx))
+AFILES = $(foreach dir,$(SOURCES),$(wildcard $(dir)/*.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
-all: $(TARGET)
-
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+## Build rules
+
+all: build/$(TARGET)
+
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
+
+build/%.o: %.c
+ @mkdir -p $(dir $@)
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
-
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
diff --git a/libpsn00b/psxapi/sys/flushcache.s b/libpsn00b/psxapi/sys/flushcache.s
new file mode 100644
index 0000000..7eeb510
--- /dev/null
+++ b/libpsn00b/psxapi/sys/flushcache.s
@@ -0,0 +1,10 @@
+.set noreorder
+
+.section .text
+
+.global FlushCache
+.type FlushCache, @function
+FlushCache:
+ addiu $t2, $0, 0xa0
+ jr $t2
+ addiu $t1, $0, 0x44
diff --git a/libpsn00b/psxcd/makefile b/libpsn00b/psxcd/makefile
index cf1eda5..ee9b958 100644
--- a/libpsn00b/psxcd/makefile
+++ b/libpsn00b/psxcd/makefile
@@ -1,46 +1,59 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libpsxcd.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-CFILES = $(notdir $(wildcard ./*.c))
-AFILES = $(notdir $(wildcard ./*.s))
-OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o))
+# Project target name
+TARGET = libpsxcd.a
-CFLAGS = -g -msoft-float -fno-builtin -fdata-sections \
- -ffunction-sections -Wa,--strip-local-absolute
-AFLAGS = -g -msoft-float -Wa,--strip-local-absolute
+## Files
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
+
+## Build rules
-all: $(TARGET)
+all: build/$(TARGET)
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
build/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) -O2 $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
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 <sys/types.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <elf.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <psxapi.h>
+
+/* 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/makefile b/libpsn00b/psxetc/makefile
index cc12944..84bb64b 100644
--- a/libpsn00b/psxetc/makefile
+++ b/libpsn00b/psxetc/makefile
@@ -1,46 +1,59 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libpsxetc.a
+PSN00BSDK_LIBS ?= ..
-CFILES = $(notdir $(wildcard ./*.c))
-AFILES = $(notdir $(wildcard ./*.s))
-OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o))
+include ../../psn00bsdk-setup.mk
-CFLAGS = -g -O2 -msoft-float -fno-builtin -nostdlib -fdata-sections \
- -ffunction-sections -Wa,--strip-local-absolute
-AFLAGS = -g -msoft-float -strip-local-absolute
+# Project target name
+TARGET = libpsxetc.a
-INCLUDE = -I../include
+## Files
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
-all: $(TARGET)
+## Build rules
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+all: build/$(TARGET)
+
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
build/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(AS) $(AFLAGS) $(INCLUDE) $< -o $@
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
-
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
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)
diff --git a/libpsn00b/psxgpu/makefile b/libpsn00b/psxgpu/makefile
index 5442958..adcb7fa 100644
--- a/libpsn00b/psxgpu/makefile
+++ b/libpsn00b/psxgpu/makefile
@@ -1,46 +1,59 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libpsxgpu.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-CFILES = $(notdir $(wildcard ./*.c))
-AFILES = $(notdir $(wildcard ./*.s))
-OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o))
+# Project target name
+TARGET = libpsxgpu.a
-CFLAGS = -g -O2 -msoft-float -fno-builtin -fdata-sections \
- -ffunction-sections -Wa,--strip-local-absolute
-AFLAGS = -g -msoft-float -Wa,--strip-local-absolute
+## Files
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
-all: $(TARGET)
+## Build rules
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+all: build/$(TARGET)
+
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
build/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
diff --git a/libpsn00b/psxgte/makefile b/libpsn00b/psxgte/makefile
index 7c1683f..e60ff1e 100644
--- a/libpsn00b/psxgte/makefile
+++ b/libpsn00b/psxgte/makefile
@@ -1,46 +1,59 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libpsxgte.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-CFILES = $(notdir $(wildcard ./*.c))
-AFILES = $(notdir $(wildcard ./*.s))
-OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o))
+# Project target name
+TARGET = libpsxgte.a
-CFLAGS = -g -O2 -msoft-float -fno-builtin -fdata-sections \
- -ffunction-sections -Wa,--strip-local-absolute
-AFLAGS = -g -msoft-float -strip-local-absolute
+## Files
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
+
+## Build rules
-all: $(TARGET)
+all: build/$(TARGET)
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
build/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(AS) $(AFLAGS) $(INCLUDE) $< -o $@
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
diff --git a/libpsn00b/psxsio/makefile b/libpsn00b/psxsio/makefile
index 06971ba..c9dcade 100644
--- a/libpsn00b/psxsio/makefile
+++ b/libpsn00b/psxsio/makefile
@@ -1,46 +1,59 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libpsxsio.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-CFILES = $(notdir $(wildcard ./*.c))
-AFILES = $(notdir $(wildcard ./*.s))
-OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o))
+# Project target name
+TARGET = libpsxsio.a
-CFLAGS = -g -O2 -msoft-float -fno-builtin -fdata-sections \
- -ffunction-sections -Wa,--strip-local-absolute
-AFLAGS = -g -msoft-float -Wa,--strip-local-absolute
+## Files
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
+
+## Build rules
-all: $(TARGET)
+all: build/$(TARGET)
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
build/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
diff --git a/libpsn00b/psxspu/makefile b/libpsn00b/psxspu/makefile
index 7948f34..5710f39 100644
--- a/libpsn00b/psxspu/makefile
+++ b/libpsn00b/psxspu/makefile
@@ -1,46 +1,59 @@
-# Run using make (Linux) or gmake (BSD)
+# PSn00bSDK library makefile
# Part of the PSn00bSDK Project
-# 2019 - 2020 Lameguy64 / Meido-Tek Productions
+# 2019 - 2021 Lameguy64 / Meido-Tek Productions
-include ../../template/psn00bsdk-setup.mk
+## Settings
-TARGET = libpsxspu.a
+PSN00BSDK_LIBS ?= ..
-INCLUDE = -I../include
+include ../../psn00bsdk-setup.mk
-CFILES = $(notdir $(wildcard ./*.c))
-AFILES = $(notdir $(wildcard ./*.s))
-OFILES = $(addprefix build/,$(CFILES:.c=.o) $(AFILES:.s=.o))
+# Project target name
+TARGET = libpsxspu.a
-CFLAGS = -g -O2 -msoft-float -fdata-sections -ffunction-sections \
- -Wa,--strip-local-absolute
-AFLAGS = -g -msoft-float -strip-local-absolute
+## Files
-ifndef PSN00BSDK_LIBS
+# Searches for C, C++ and S (assembler) files in local directory
+CFILES = $(notdir $(wildcard *.c))
+CXXFILES= $(notdir $(wildcard *.cxx))
+AFILES = $(notdir $(wildcard *.s))
-PSN00BSDK_LIBS = ..
+# Create names for object files
+OFILES = $(addprefix build/,$(CFILES:.c=.o)) \
+ $(addprefix build/,$(CXXFILES:.cxx=.o)) \
+ $(addprefix build/,$(AFILES:.s=.o))
-endif
+# Project specific includes and libraries
+# (use -I for include dirs, -L for library dirs, -l for static libraries)
+INCLUDE +=
+LIBDIRS +=
+LIBS +=
+
+## Build rules
-all: $(TARGET)
+all: build/$(TARGET)
-$(TARGET): $(OFILES)
- $(AR) cr $(TARGET) $(OFILES)
- $(RANLIB) $(TARGET)
+build/$(TARGET): $(OFILES)
+ @mkdir -p $(dir $@)
+ $(AR) crs $@ $(OFILES)
build/%.o: %.c
@mkdir -p $(dir $@)
- $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
+ $(CC) $(CFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
+build/%.o: %.cxx
+ @mkdir -p $(dir $@)
+ $(CXX) $(CPPFLAGS_LIB) $(INCLUDE) -c $< -o $@
build/%.o: %.s
@mkdir -p $(dir $@)
- $(AS) $(AFLAGS) $(INCLUDE) $< -o $@
-
+ $(CC) $(AFLAGS_LIB) $(INCLUDE) -c $< -o $@
+
install:
ifneq ($(PSN00BSDK_LIBS), "..")
@mkdir -p $(PSN00BSDK_LIBS)
endif
- cp $(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
-
+ cp build/$(TARGET) $(PSN00BSDK_LIBS)/$(TARGET)
+
clean:
- rm -Rf build $(TARGET)
+ rm -rf build
diff --git a/libpsn00b/psxspu/transfer.s b/libpsn00b/psxspu/transfer.s
index 086bb3b..5b62c28 100644
--- a/libpsn00b/psxspu/transfer.s
+++ b/libpsn00b/psxspu/transfer.s
@@ -20,7 +20,8 @@ SpuSetTransferStartAddr:
li $v0, 0x1000 # Check if value is valid
blt $a0, $v0, .Lbad_value
nop
- li $v0, 0xffff
+ lui $v0, 8 # 0x7ffff = (8<<16)-1
+ addiu $v0, -1
bgt $a0, $v0, .Lbad_value
nop