aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/include
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/include
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/include')
-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
5 files changed, 320 insertions, 0 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