diff options
| author | John "Lameguy" Wilbert Villamor <lameguy64@gmail.com> | 2021-08-31 13:23:20 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-08-31 13:23:20 +0800 |
| commit | ffa679d4d24b891cb59aba10946368f2ec00c391 (patch) | |
| tree | 0cf6061915ebf48acdedf6d77b0c1b76eec5b8c3 /libpsn00b | |
| parent | 317dc2b91d3afcdbaddb035f38611d12af161970 (diff) | |
| parent | f2fc18f82dd7900465d6ab3ae2080726d5589d39 (diff) | |
| download | psn00bsdk-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.h | 176 | ||||
| -rw-r--r-- | libpsn00b/include/elf.h | 138 | ||||
| -rw-r--r-- | libpsn00b/include/psxapi.h | 1 | ||||
| -rw-r--r-- | libpsn00b/include/psxgpu.h | 2 | ||||
| -rw-r--r-- | libpsn00b/include/stdio.h | 3 | ||||
| -rw-r--r-- | libpsn00b/ldscripts/dll.ld | 122 | ||||
| -rw-r--r-- | libpsn00b/ldscripts/exe.ld | 114 | ||||
| -rw-r--r-- | libpsn00b/libc/_mem_init.s | 20 | ||||
| -rw-r--r-- | libpsn00b/libc/makefile | 59 | ||||
| -rw-r--r-- | libpsn00b/libc/malloc.s | 11 | ||||
| -rw-r--r-- | libpsn00b/libc/start.c | 181 | ||||
| -rw-r--r-- | libpsn00b/lzp/makefile | 61 | ||||
| -rw-r--r-- | libpsn00b/psxapi/makefile | 62 | ||||
| -rw-r--r-- | libpsn00b/psxapi/sys/flushcache.s | 10 | ||||
| -rw-r--r-- | libpsn00b/psxcd/makefile | 57 | ||||
| -rw-r--r-- | libpsn00b/psxetc/_dl_resolve_wrapper.s | 54 | ||||
| -rw-r--r-- | libpsn00b/psxetc/dl.c | 665 | ||||
| -rw-r--r-- | libpsn00b/psxetc/makefile | 59 | ||||
| -rw-r--r-- | libpsn00b/psxetc/readme.txt | 16 | ||||
| -rw-r--r-- | libpsn00b/psxgpu/makefile | 57 | ||||
| -rw-r--r-- | libpsn00b/psxgte/makefile | 57 | ||||
| -rw-r--r-- | libpsn00b/psxsio/makefile | 57 | ||||
| -rw-r--r-- | libpsn00b/psxspu/makefile | 61 | ||||
| -rw-r--r-- | libpsn00b/psxspu/transfer.s | 3 |
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 |
