aboutsummaryrefslogtreecommitdiff
path: root/libpsn00b/include/dlfcn.h
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/dlfcn.h
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/dlfcn.h')
-rw-r--r--libpsn00b/include/dlfcn.h176
1 files changed, 176 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